Monstat2 loading and a bunch of lint issues (#491)

* MonStat2 loader

* Fix a bunch of lint issues in d2datadict
This commit is contained in:
Ziemas 2020-06-29 18:37:11 +02:00 committed by GitHub
parent b29e7c8fdd
commit aae565d528
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 805 additions and 241 deletions

View File

@ -39,10 +39,10 @@ type AutoMapRecord struct {
// whatever you like..."
// The values seem functional but naming conventions
// vary between LevelNames.
//Type1 string
//Type2 string
//Type3 string
//Type4 string // Note: I commented these out for now because they supposedly aren't useful see the LoadAutoMaps function.
// Type1 string
// Type2 string
// Type3 string
// Type4 string // Note: I commented these out for now because they supposedly aren't useful see the LoadAutoMaps function.
// Frames determine the frame of the MaxiMap(s).dc6 that
// will be applied to the specified tiles. The frames
@ -58,6 +58,7 @@ type AutoMapRecord struct {
}
// AutoMaps contains all data in AutoMap.txt.
//nolint:gochecknoglobals // Current design is to have these global
var AutoMaps []*AutoMapRecord
// LoadAutoMaps populates AutoMaps with the data from AutoMap.txt.
@ -71,9 +72,9 @@ func LoadAutoMaps(file []byte) {
// Construct records
AutoMaps = make([]*AutoMapRecord, len(d.Data))
for idx := range d.Data {
// Row 2603 is a separator with all empty field values
if idx == 2603 {
if d.GetString("LevelName", idx) == "Expansion" {
continue
}
@ -88,7 +89,9 @@ func LoadAutoMaps(file []byte) {
//Type1: d.GetString("Type1", idx),
//Type2: d.GetString("Type2", idx),
//Type3: d.GetString("Type3", idx),
//Type4: d.GetString("Type4", idx), // Note: I commented these out for now because they supposedly aren't useful see the AutoMapRecord struct.
//Type4: d.GetString("Type4", idx),
// Note: I commented these out for now because they supposedly
// aren't useful see the AutoMapRecord struct.
}
AutoMaps[idx].Frames = make([]int, len(frameFields))

View File

@ -7,6 +7,7 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
)
// Charecter stats
type CharStatsRecord struct {
Class d2enum.Hero
@ -59,8 +60,8 @@ var CharStats map[d2enum.Hero]*CharStatsRecord
var charStringMap map[string]d2enum.Hero
var weaponTokenMap map[string]d2enum.WeaponClass
//nolint:funlen // Makes no sense to split
func LoadCharStats(file []byte) {
charStringMap = map[string]d2enum.Hero{
"Amazon": d2enum.HeroAmazon,
"Barbarian": d2enum.HeroBarbarian,
@ -186,5 +187,6 @@ func LoadCharStats(file []byte) {
}
CharStats[record.Class] = record
}
log.Printf("Loaded %d CharStats records", len(CharStats))
}

View File

@ -130,7 +130,7 @@ type CubeRecipeItemProperty struct {
// string or an integer.
//
// See: https://d2mods.info/forum/kb/viewarticle?a=345
//"the parameter passed on to the associated property, this is used to pass skill IDs,
// "the parameter passed on to the associated property, this is used to pass skill IDs,
// state IDs, monster IDs, montype IDs and the like on to the properties that require
// them, these fields support calculations."
Param int // for properties that use parameters
@ -153,7 +153,6 @@ var inputFields = []string{"input 1", "input 2", "input 3", "input 4", "input 5"
// LoadCubeRecipes populates CubeRecipes with
// the data from CubeMain.txt.
func LoadCubeRecipes(file []byte) {
// Load data
d := d2common.LoadDataDictionary(string(file))
@ -188,7 +187,6 @@ func LoadCubeRecipes(file []byte) {
// Create outputs - output "", b, c
CubeRecipes[idx].Outputs = make([]CubeRecipeResult, 3)
for o, outLabel := range outputLabels {
CubeRecipes[idx].Outputs[o] = CubeRecipeResult{
Item: newCubeRecipeItem(
d.GetString(outputFields[o], idx)),
@ -201,7 +199,6 @@ func LoadCubeRecipes(file []byte) {
// Create properties - mod 1-5
properties := make([]CubeRecipeItemProperty, 5)
for p, prop := range propLabels {
properties[p] = CubeRecipeItemProperty{
Code: d.GetString(outLabel+prop, idx),
Chance: d.GetNumber(outLabel+prop+" chance", idx),
@ -213,7 +210,6 @@ func LoadCubeRecipes(file []byte) {
CubeRecipes[idx].Outputs[o].Properties = properties
}
}
log.Printf("Loaded %d CubeMainRecord records", len(CubeRecipes))
@ -237,21 +233,26 @@ func newCubeRecipeItem(f string) CubeRecipeItem {
// Find the qty parameter if it was provided,
// convert to int and assign to item.Count
for idx, arg := range args {
if strings.HasPrefix(arg, "qty") {
count, err := strconv.Atoi(strings.Split(arg, "=")[1])
if err != nil {
log.Fatal("Error parsing item count:", err)
}
item.Count = count
// Remove the qty parameter
if idx != len(args)-1 {
args[idx] = args[len(args)-1]
}
args = args[:len(args)-1]
break
if !strings.HasPrefix(arg, "qty") {
continue
}
count, err := strconv.Atoi(strings.Split(arg, "=")[1])
if err != nil {
log.Fatal("Error parsing item count:", err)
}
item.Count = count
// Remove the qty parameter
if idx != len(args)-1 {
args[idx] = args[len(args)-1]
}
args = args[:len(args)-1]
break
}
// No other arguments were provided
@ -272,10 +273,12 @@ func newCubeRecipeItem(f string) CubeRecipeItem {
func classFieldToEnum(f string) []d2enum.Hero {
split := splitFieldValue(f)
enums := make([]d2enum.Hero, len(split))
for idx, class := range split {
if class == "" {
continue
}
switch class {
case "bar":
enums[idx] = d2enum.HeroBarbarian
@ -295,6 +298,7 @@ func classFieldToEnum(f string) []d2enum.Hero {
log.Fatalf("Unknown hero token: '%s'", class)
}
}
return enums
}

View File

@ -6,8 +6,11 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
// DifficultyLevels contain the difficulty records for each difficulty
//nolint:gochecknoglobals // Current design is to have these global
var DifficultyLevels map[string]*DifficultyLevelRecord
// DifficultyLevelRecord contain the parameters that change for different difficultios
type DifficultyLevelRecord struct {
// Difficulty name. it is hardcoded and you cannot add new ones unless you do
// some Code Edits
@ -30,7 +33,7 @@ type DifficultyLevelRecord struct {
// txt file...
// Not used. Pre 1.07 it was the percentage of magic, rare, set and unique
// exceptional items dropped on this difficuly.
// exceptional items dropped on this difficulty.
DropChanceMagic int // UberCodeOddsGood
DropChanceRare int // UberCodeOddsGood
DropChanceSet int // UberCodeOddsGood
@ -89,6 +92,7 @@ type DifficultyLevelRecord struct {
}
// LoadDifficultyLevels is a loader for difficultylevels.txt
func LoadDifficultyLevels(file []byte) {
dict := d2common.LoadDataDictionary(string(file))
numRows := len(dict.Data)
@ -119,5 +123,4 @@ func LoadDifficultyLevels(file []byte) {
}
log.Printf("Loaded %d DifficultyLevel records", len(DifficultyLevels))
}

View File

@ -89,6 +89,7 @@ func LoadExperienceBreakpoints(file []byte) {
d2enum.HeroPaladin: d.GetNumber("Paladin", idx),
d2enum.HeroSorceress: d.GetNumber("Sorceress", idx),
}
continue
}
@ -108,5 +109,6 @@ func LoadExperienceBreakpoints(file []byte) {
ExperienceBreakpoints[record.Level] = record
}
log.Printf("Loaded %d ExperienceBreakpoint records", len(ExperienceBreakpoints))
}

View File

@ -52,7 +52,9 @@ type GemsRecord struct {
func LoadGems(file []byte) {
d := d2common.LoadDataDictionary(string(file))
var Gems []*GemsRecord
for idx := range d.Data {
if d.GetString("name", idx) != "Expansion" {
/*
@ -105,5 +107,6 @@ func LoadGems(file []byte) {
Gems = append(Gems, gem)
}
}
log.Printf("Loaded %d Gems records", len(Gems))
}

View File

@ -9,7 +9,7 @@ import (
type HirelingRecord struct {
Hireling string
SubType string
Id int
ID int
Class int
Act int
Difficulty int
@ -84,11 +84,12 @@ type HirelingRecord struct {
func LoadHireling(file []byte) {
d := d2common.LoadDataDictionary(string(file))
var Hirelings []*HirelingRecord
for idx := range d.Data {
hireling := &HirelingRecord{
Hireling: d.GetString("Hireling", idx),
SubType: d.GetString("SubType", idx),
Id: d.GetNumber("Id", idx),
ID: d.GetNumber("Id", idx),
Class: d.GetNumber("Class", idx),
Act: d.GetNumber("Act", idx),
Difficulty: d.GetNumber("Difficulty", idx),
@ -161,5 +162,6 @@ func LoadHireling(file []byte) {
}
Hirelings = append(Hirelings, hireling)
}
log.Printf("Loaded %d Hireling records", len(Hirelings))
}

View File

@ -16,26 +16,28 @@ var MagicSuffixRecords []*ItemAffixCommonRecord
var AffixMagicGroups []*ItemAffixCommonGroup
var superType d2enum.ItemAffixSuperType
var subType d2enum.ItemAffixSubType
// LoadMagicPrefix loads MagicPrefix.txt
func LoadMagicPrefix(file []byte) {
superType = d2enum.ItemAffixPrefix
subType = d2enum.ItemAffixMagic
superType := d2enum.ItemAffixPrefix
subType := d2enum.ItemAffixMagic
MagicPrefixDictionary, MagicPrefixRecords = loadDictionary(file, superType, subType)
}
// LoadMagicSuffix loads MagicSuffix.txt
func LoadMagicSuffix(file []byte) {
superType = d2enum.ItemAffixSuffix
subType = d2enum.ItemAffixMagic
superType := d2enum.ItemAffixSuffix
subType := d2enum.ItemAffixMagic
MagicSuffixDictionary, MagicSuffixRecords = loadDictionary(file, superType, subType)
}
func getAffixString(t1 d2enum.ItemAffixSuperType, t2 d2enum.ItemAffixSubType) string {
var name string = ""
switch t2 {
case d2enum.ItemAffixMagic:
if t2 == d2enum.ItemAffixMagic {
name = "Magic"
}
@ -47,7 +49,6 @@ func getAffixString(t1 d2enum.ItemAffixSuperType, t2 d2enum.ItemAffixSubType) st
}
return name
}
func loadDictionary(
@ -59,6 +60,7 @@ func loadDictionary(
records := createItemAffixRecords(dict, superType, subType)
name := getAffixString(superType, subType)
log.Printf("Loaded %d %s records", len(dict.Data), name)
return dict, records
}
@ -111,8 +113,8 @@ func createItemAffixRecords(
subType d2enum.ItemAffixSubType,
) []*ItemAffixCommonRecord {
records := make([]*ItemAffixCommonRecord, 0)
for index := range d.Data {
for index := range d.Data {
affix := &ItemAffixCommonRecord{
Name: d.GetString("Name", index),
Version: d.GetNumber("version", index),
@ -179,6 +181,7 @@ func createItemAffixRecords(
records = append(records, affix)
}
return records
}
@ -193,14 +196,17 @@ func (g *ItemAffixCommonGroup) AddMember(a *ItemAffixCommonRecord) {
if g.Members == nil {
g.Members = make(map[string]*ItemAffixCommonRecord)
}
g.Members[a.Name] = a
}
func (g *ItemAffixCommonGroup) GetTotalFrequency() int {
total := 0
for _, affix := range g.Members {
total += affix.Frequency
}
return total
}
@ -249,6 +255,8 @@ func (a *ItemAffixCommonRecord) ProbabilityToSpawn(qlvl int) float64 {
if (qlvl > a.MaxLevel) || (qlvl < a.Level) {
return 0.0
}
p := (float64)(a.Frequency) / (float64)(a.Group.GetTotalFrequency())
p := float64(a.Frequency) / float64(a.Group.GetTotalFrequency())
return p
}

View File

@ -181,30 +181,35 @@ type ItemVendorParams struct {
MagicLevel uint8
}
// Loading Functions
var CommonItems map[string]*ItemCommonRecord
func LoadCommonItems(file []byte, source d2enum.InventoryItemType) *map[string]*ItemCommonRecord {
if CommonItems == nil {
CommonItems = make(map[string]*ItemCommonRecord)
}
items := make(map[string]*ItemCommonRecord)
data := strings.Split(string(file), "\r\n")
mapping := MapHeaders(data[0])
for lineno, line := range data {
if lineno == 0 {
continue
}
if len(line) == 0 {
if line == "" {
continue
}
rec := createCommonItemRecord(line, &mapping, source)
items[rec.Code] = &rec
CommonItems[rec.Code] = &rec
}
return &items
}
//nolint:funlen // Makes no sens to split
func createCommonItemRecord(line string, mapping *map[string]int, source d2enum.InventoryItemType) ItemCommonRecord {
r := strings.Split(line, "\t")
result := ItemCommonRecord{
@ -363,6 +368,7 @@ func createCommonItemRecord(line string, mapping *map[string]int, source d2enum.
Multibuy: MapLoadBool(&r, mapping, "multibuy"),
}
return result
}
@ -398,6 +404,7 @@ func createItemVendorParams(r *[]string, mapping *map[string]int) map[string]*It
}
result[name] = &wvp
}
return result
}
@ -407,5 +414,6 @@ func createItemUsageStats(r *[]string, mapping *map[string]int) [3]ItemUsageStat
result[i].Stat = MapLoadString(r, mapping, "stat"+strconv.Itoa(i))
result[i].Calc = d2common.CalcString(MapLoadString(r, mapping, "calc"+strconv.Itoa(i)))
}
return result
}

View File

@ -115,10 +115,10 @@ const (
// just adds the stat to the unit directly
OpDefault = OperatorType(iota)
// adds opstat.base * statvalue / 100 to the opstat.
// Op1 adds opstat.base * statvalue / 100 to the opstat.
Op1
// adds (statvalue * basevalue) / (2 ^ param) to the opstat
// Op2 adds (statvalue * basevalue) / (2 ^ param) to the opstat
// this does not work properly with any stat other then level because of the
// way this is updated, it is only refreshed when you re-equip the item,
// your character is saved or you level up, similar to passive skills, just
@ -127,56 +127,56 @@ const (
// description every frame, while the values remain unchanged serverside.
Op2
// this is a percentage based version of op #2
// Op3 is a percentage based version of op #2
// look at op #2 for information about the formula behind it, just
// remember the stat is increased by a percentage rather then by adding
// an integer.
Op3
// this works the same way op #2 works, however the stat bonus is
// Op4 works the same way op #2 works, however the stat bonus is
// added to the item and not to the player (so that +defense per level
// properly adds the defense to the armor and not to the character
// directly!)
Op4
// this works like op #4 but is percentage based, it is used for percentage
// Op5 works like op #4 but is percentage based, it is used for percentage
// based increase of stats that are found on the item itself, and not stats
// that are found on the character.
Op5
// like for op #7, however this adds a plain bonus to the stat, and just
// Op6 works like for op #7, however this adds a plain bonus to the stat, and just
// like #7 it also doesn't work so I won't bother to explain the arithmetic
// behind it either.
Op6
// this is used to increase a stat based on the current daytime of the game
// Op7 is used to increase a stat based on the current daytime of the game
// world by a percentage, there is no need to explain the arithmetics
// behind it because frankly enough it just doesn't work serverside, it
// only updates clientside so this op is essentially useless.
Op7
// hardcoded to work only with maxmana, this will apply the proper amount
// Op8 hardcoded to work only with maxmana, this will apply the proper amount
// of mana to your character based on CharStats.txt for the amount of energy
// the stat added (doesn't work for non characters)
Op8
// hardcoded to work only with maxhp and maxstamina, this will apply the
// Op9 hardcoded to work only with maxhp and maxstamina, this will apply the
// proper amount of maxhp and maxstamina to your character based on
// CharStats.txt for the amount of vitality the stat added (doesn't work
// for non characters)
Op9
// doesn't do anything, this has no switch case in the op function.
// Op10 doesn't do anything, this has no switch case in the op function.
Op10
// adds opstat.base * statvalue / 100 similar to 1 and 13, the code just
// Op11 adds opstat.base * statvalue / 100 similar to 1 and 13, the code just
// does a few more checks
Op11
// doesn't do anything, this has no switch case in the op function.
// Op12 doesn't do anything, this has no switch case in the op function.
Op12
// adds opstat.base * statvalue / 100 to the value of opstat, this is
// Op13 adds opstat.base * statvalue / 100 to the value of opstat, this is
// useable only on items it will not apply the bonus to other unit types
// (this is why it is used for +% durability, +% level requirement,
// +% damage, +% defense ).
@ -241,6 +241,7 @@ stuff
var ItemStatCosts map[string]*ItemStatCostRecord
// LoadItemStatCosts loads ItemStatCostRecord's from text
func LoadItemStatCosts(file []byte) {
d := d2common.LoadDataDictionary(string(file))
numRecords := len(d.Data)
@ -317,5 +318,6 @@ func LoadItemStatCosts(file []byte) {
ItemStatCosts[record.Name] = record
}
log.Printf("Loaded %d ItemStatCost records", len(ItemStatCosts))
}

View File

@ -14,7 +14,7 @@ type LevelMazeDetailsRecord struct {
// ID from Levels.txt
// NOTE: Cave 1 is the Den of Evil, its associated treasure level is quest
// only.
LevelId int // Level
LevelID int // Level
// the minimum number of .ds1 map sections that will make up the maze in
// Normal, Nightmare and Hell difficulties.
@ -36,21 +36,24 @@ type LevelMazeDetailsRecord struct {
var LevelMazeDetails map[int]*LevelMazeDetailsRecord
// LoadLevelMazeDetails loads LevelMazeDetailsRecords from text file
func LoadLevelMazeDetails(file []byte) {
dict := d2common.LoadDataDictionary(string(file))
numRecords := len(dict.Data)
LevelMazeDetails = make(map[int]*LevelMazeDetailsRecord, numRecords)
for idx := range dict.Data {
record := &LevelMazeDetailsRecord{
Name: dict.GetString("Name", idx),
LevelId: dict.GetNumber("Level", idx),
LevelID: dict.GetNumber("Level", idx),
NumRoomsNormal: dict.GetNumber("Rooms", idx),
NumRoomsNightmare: dict.GetNumber("Rooms(N)", idx),
NumRoomsHell: dict.GetNumber("Rooms(H)", idx),
SizeX: dict.GetNumber("SizeX", idx),
SizeY: dict.GetNumber("SizeY", idx),
}
LevelMazeDetails[record.LevelId] = record
LevelMazeDetails[record.LevelID] = record
}
log.Printf("Loaded %d LevelMazeDetails records", len(LevelMazeDetails))
}

View File

@ -9,8 +9,8 @@ import (
type LevelPresetRecord struct {
Name string
DefinitionId int
LevelId int
DefinitionID int
LevelID int
Populate bool
Logicals bool
Outdoors bool
@ -39,8 +39,8 @@ func createLevelPresetRecord(props []string) LevelPresetRecord {
}
result := LevelPresetRecord{
Name: props[inc()],
DefinitionId: d2common.StringToInt(props[inc()]),
LevelId: d2common.StringToInt(props[inc()]),
DefinitionID: d2common.StringToInt(props[inc()]),
LevelID: d2common.StringToInt(props[inc()]),
Populate: d2common.StringToUint8(props[inc()]) == 1,
Logicals: d2common.StringToUint8(props[inc()]) == 1,
Outdoors: d2common.StringToUint8(props[inc()]) == 1,
@ -66,31 +66,39 @@ func createLevelPresetRecord(props []string) LevelPresetRecord {
Beta: d2common.StringToUint8(props[inc()]) == 1,
Expansion: d2common.StringToUint8(props[inc()]) == 1,
}
return result
}
var LevelPresets map[int]LevelPresetRecord
// LoadLevelPresets loads level presets from text file
func LoadLevelPresets(file []byte) {
LevelPresets = make(map[int]LevelPresetRecord)
data := strings.Split(string(file), "\r\n")[1:]
for _, line := range data {
if len(line) == 0 {
if line == "" {
continue
}
props := strings.Split(line, "\t")
if props[1] == "" {
continue // any line without a definition id is skipped (e.g. the "Expansion" line)
}
rec := createLevelPresetRecord(props)
LevelPresets[rec.DefinitionId] = rec
LevelPresets[rec.DefinitionID] = rec
}
log.Printf("Loaded %d level presets", len(LevelPresets))
}
// LevelPreset looks up a LevelPresetRecord by ID
func LevelPreset(id int) LevelPresetRecord {
for i := 0; i < len(LevelPresets); i++ {
if LevelPresets[i].DefinitionId == id {
if LevelPresets[i].DefinitionID == id {
return LevelPresets[i]
}
}

View File

@ -14,7 +14,7 @@ type LevelSubstitutionRecord struct {
// groups. If you count each row of a group starting from 0, then you'll
// obtain what is written in Levels.txt, columns 'SubTheme', 'SubWaypoint'
// and 'SubShrine'. (added by Paul Siramy)
Id int // Type
ID int // Type
// What .ds1 is being used.
File string // File
@ -69,10 +69,9 @@ func LoadLevelSubstitutions(file []byte) {
LevelSubstitutions = make(map[int]*LevelSubstitutionRecord, numRecords)
for idx := range dict.Data {
record := &LevelSubstitutionRecord{
Name: dict.GetString("Name", idx),
Id: dict.GetNumber("Type", idx),
ID: dict.GetNumber("Type", idx),
File: dict.GetString("File", idx),
IsExpansion: dict.GetNumber("Expansion", idx) > 0,
BorderType: dict.GetNumber("BordType", idx),
@ -95,7 +94,8 @@ func LoadLevelSubstitutions(file []byte) {
GridMax4: dict.GetNumber("Max4", idx),
}
LevelSubstitutions[record.Id] = record
LevelSubstitutions[record.ID] = record
}
log.Printf("Loaded %d LevelSubstitution records", len(LevelSubstitutions))
}

View File

@ -9,7 +9,7 @@ import (
type LevelTypeRecord struct {
Name string
Id int
ID int
Files [32]string
Beta bool
Act int
@ -21,29 +21,35 @@ var LevelTypes []LevelTypeRecord
func LoadLevelTypes(file []byte) {
data := strings.Split(string(file), "\r\n")[1:]
LevelTypes = make([]LevelTypeRecord, len(data))
for i, j := 0, 0; i < len(data); i, j = i+1, j+1 {
idx := -1
inc := func() int {
idx++
return idx
}
if len(data[i]) == 0 {
if data[i] == "" {
continue
}
parts := strings.Split(data[i], "\t")
if parts[0] == "Expansion" {
j--
continue
}
LevelTypes[j].Name = parts[inc()]
LevelTypes[j].Id = d2common.StringToInt(parts[inc()])
LevelTypes[j].ID = d2common.StringToInt(parts[inc()])
for fileIdx := range LevelTypes[i].Files {
LevelTypes[j].Files[fileIdx] = parts[inc()]
if LevelTypes[j].Files[fileIdx] == "0" {
LevelTypes[j].Files[fileIdx] = ""
}
}
LevelTypes[j].Beta = parts[inc()] != "1"
LevelTypes[j].Act = d2common.StringToInt(parts[inc()])
LevelTypes[j].Expansion = parts[inc()] != "1"

View File

@ -7,7 +7,7 @@ import (
)
type LevelWarpRecord struct {
Id int32
ID int32
SelectX int32
SelectY int32
SelectDX int32
@ -21,16 +21,20 @@ type LevelWarpRecord struct {
Direction string
}
//nolint:gochecknoglobals // Currently global by design, only written once
// LevelWarps loaded from txt records
var LevelWarps map[int]*LevelWarpRecord
// LoadLevelWarps loads LevelWarpRecord's from text file data
func LoadLevelWarps(levelWarpData []byte) {
LevelWarps = make(map[int]*LevelWarpRecord)
streamReader := d2common.CreateStreamReader(levelWarpData)
numRecords := int(streamReader.GetInt32())
for i := 0; i < numRecords; i++ {
id := int(streamReader.GetInt32())
LevelWarps[id] = &LevelWarpRecord{}
LevelWarps[id].Id = int32(id)
LevelWarps[id].ID = int32(id)
LevelWarps[id].SelectX = streamReader.GetInt32()
LevelWarps[id].SelectY = streamReader.GetInt32()
LevelWarps[id].SelectDX = streamReader.GetInt32()
@ -44,5 +48,6 @@ func LoadLevelWarps(levelWarpData []byte) {
LevelWarps[id].Direction = string(streamReader.GetByte())
streamReader.SkipBytes(3)
}
log.Printf("Loaded %d level warps", len(LevelWarps))
}

View File

@ -37,7 +37,7 @@ type LevelDetailsRecord struct {
// additional layers.
AutomapIndex int // Layer
// sizeX - SizeY in each difficuly. If this is a preset area this sets the
// sizeX - SizeY in each difficulty. If this is a preset area this sets the
// X size for the area. Othervise use the same value here that are used in
// lvlprest.txt to set the size for the .ds1 file.
SizeXNormal int // SizeX
@ -102,7 +102,7 @@ type LevelDetailsRecord struct {
// cycles, because they always use the light values specified in Intensity,
// Red, Green, Blue. this field also controls whenever sounds will echo if
// you're running the game with a sound card capable of it and have
// enviroment sound effects set to true.
// environment sound effects set to true.
IsInside bool // IsInside
// Setting for Level Generation: You have 3 possibilities here:
@ -141,28 +141,28 @@ type LevelDetailsRecord struct {
// linked with, but the actuall number of Vis ( 0 - 7 ) is determined by
// your actual map (the .ds1 fle).
// Example: Normally Cave levels are only using vis 0-3 and wilderness areas 4-7 .
LevelLinkId0 int // Vis0
LevelLinkId1 int // Vis1
LevelLinkId2 int // Vis2
LevelLinkId3 int // Vis3
LevelLinkId4 int // Vis4
LevelLinkId5 int // Vis5
LevelLinkId6 int // Vis6
LevelLinkId7 int // Vis7
LevelLinkID0 int // Vis0
LevelLinkID1 int // Vis1
LevelLinkID2 int // Vis2
LevelLinkID3 int // Vis3
LevelLinkID4 int // Vis4
LevelLinkID5 int // Vis5
LevelLinkID6 int // Vis6
LevelLinkID7 int // Vis7
// This controls the visual graphics then you move the mouse pointer over
// an entrance. To show the graphics you use an ID from lvlwarp.txt and the
// behavior on the graphics is controlled by lvlwarp.txt. Your Warps must
// match your Vis.
// Example: If your level uses Vis 3,5,7 then you must also use Warp 3,5,7 .
WarpGraphicsId0 int // Warp0
WarpGraphicsId1 int // Warp1
WarpGraphicsId2 int // Warp2
WarpGraphicsId3 int // Warp3
WarpGraphicsId4 int // Warp4
WarpGraphicsId5 int // Warp5
WarpGraphicsId6 int // Warp6
WarpGraphicsId7 int // Warp7
WarpGraphicsID0 int // Warp0
WarpGraphicsID1 int // Warp1
WarpGraphicsID2 int // Warp2
WarpGraphicsID3 int // Warp3
WarpGraphicsID4 int // Warp4
WarpGraphicsID5 int // Warp5
WarpGraphicsID6 int // Warp6
WarpGraphicsID7 int // Warp7
// These settings handle the light intensity as well as its RGB components
LightIntensity int // Intensity
@ -195,7 +195,7 @@ type LevelDetailsRecord struct {
// What quest is this level related to. This is the quest id (as example the
// first quest Den of Evil are set to 1, since its the first quest).
QuestId int // Quest
QuestID int // Quest
// This sets the minimum distance from a VisX or WarpX location that a
// monster, object or tile can be spawned at. (also applies to waypoints and
@ -247,40 +247,40 @@ type LevelDetailsRecord struct {
// Hell. They tell the game which monster ID taken from MonStats.txt.
// NOTE: you need to manually add from mon11 to mon25 and from nmon11 to
// nmon25 !
MonsterId1Normal string // mon1
MonsterId2Normal string // mon2
MonsterId3Normal string // mon3
MonsterId4Normal string // mon4
MonsterId5Normal string // mon5
MonsterId6Normal string // mon6
MonsterId7Normal string // mon7
MonsterId8Normal string // mon8
MonsterId9Normal string // mon9
MonsterId10Normal string // mon10
MonsterID1Normal string // mon1
MonsterID2Normal string // mon2
MonsterID3Normal string // mon3
MonsterID4Normal string // mon4
MonsterID5Normal string // mon5
MonsterID6Normal string // mon6
MonsterID7Normal string // mon7
MonsterID8Normal string // mon8
MonsterID9Normal string // mon9
MonsterID10Normal string // mon10
MonsterId1Nightmare string // nmon1
MonsterId2Nightmare string // nmon2
MonsterId3Nightmare string // nmon3
MonsterId4Nightmare string // nmon4
MonsterId5Nightmare string // nmon5
MonsterId6Nightmare string // nmon6
MonsterId7Nightmare string // nmon7
MonsterId8Nightmare string // nmon8
MonsterId9Nightmare string // nmon9
MonsterId10Nightmare string // nmon10
MonsterID1Nightmare string // nmon1
MonsterID2Nightmare string // nmon2
MonsterID3Nightmare string // nmon3
MonsterID4Nightmare string // nmon4
MonsterID5Nightmare string // nmon5
MonsterID6Nightmare string // nmon6
MonsterID7Nightmare string // nmon7
MonsterID8Nightmare string // nmon8
MonsterID9Nightmare string // nmon9
MonsterID10Nightmare string // nmon10
// Gravestench - adding additional fields for Hell, original txt combined
// the nighmare and hell ID's stringo the same field
MonsterId1Hell string // nmon1
MonsterId2Hell string // nmon2
MonsterId3Hell string // nmon3
MonsterId4Hell string // nmon4
MonsterId5Hell string // nmon5
MonsterId6Hell string // nmon6
MonsterId7Hell string // nmon7
MonsterId8Hell string // nmon8
MonsterId9Hell string // nmon9
MonsterId10Hell string // nmon10
MonsterID1Hell string // nmon1
MonsterID2Hell string // nmon2
MonsterID3Hell string // nmon3
MonsterID4Hell string // nmon4
MonsterID5Hell string // nmon5
MonsterID6Hell string // nmon6
MonsterID7Hell string // nmon7
MonsterID8Hell string // nmon8
MonsterID9Hell string // nmon9
MonsterID10Hell string // nmon10
// Give preference to monsters set to ranged=1 in MonStats.txt on Nightmare
// and Hell difficulties when picking something to spawn.
@ -293,24 +293,24 @@ type LevelDetailsRecord struct {
// NOTE: you can allow umon1-25 to also work in Nightmare and Hell by
// following this simple ASM edit
// (https://d2mods.info/forum/viewtopic.php?f=8&t=53969&p=425179&hilit=umon#p425179)
MonsterUniqueId1 string // umon1
MonsterUniqueId2 string // umon2
MonsterUniqueId3 string // umon3
MonsterUniqueId4 string // umon4
MonsterUniqueId5 string // umon5
MonsterUniqueId6 string // umon6
MonsterUniqueId7 string // umon7
MonsterUniqueId8 string // umon8
MonsterUniqueId9 string // umon9
MonsterUniqueId10 string // umon10
MonsterUniqueID1 string // umon1
MonsterUniqueID2 string // umon2
MonsterUniqueID3 string // umon3
MonsterUniqueID4 string // umon4
MonsterUniqueID5 string // umon5
MonsterUniqueID6 string // umon6
MonsterUniqueID7 string // umon7
MonsterUniqueID8 string // umon8
MonsterUniqueID9 string // umon9
MonsterUniqueID10 string // umon10
// Critter Species 1-4. Uses the Id from monstats2.txt and only monsters
// with critter column set to 1 can spawn here. critter column is also found
// in monstats2.txt. Critters are in reality only present clientside.
MonsterCritterId1 string // cmon1
MonsterCritterId2 string // cmon2
MonsterCritterId3 string // cmon3
MonsterCritterId4 string // cmon4
MonsterCritterID1 string // cmon1
MonsterCritterID2 string // cmon2
MonsterCritterID3 string // cmon3
MonsterCritterID4 string // cmon4
// Controls the chance for a critter to spawn.
MonsterCritter1SpawnChance int // cpct1
@ -330,13 +330,13 @@ type LevelDetailsRecord struct {
// Themes
// Referes to a entry in SoundEnviron.txt (for the Levels Music)
SoundEnvironmentId int // SoundEnv
SoundEnvironmentID int // SoundEnv
// 255 means no Waipoint for this level, while others state the Waypoint' ID
// for the level
// NOTE: you can switch waypoint destinations between areas this way, not
// between acts however so don't even bother to try.
WaypointId int // Waypoint
WaypointID int // Waypoint
// String Code for the Display name of the Level
LevelDisplayName string // LevelName
@ -351,14 +351,14 @@ type LevelDetailsRecord struct {
// this field uses the ID of the ObjectGroup you want to Spawn in this Area,
// taken from Objgroup.txt.
ObjectGroupId0 int // ObjGrp0
ObjectGroupId1 int // ObjGrp1
ObjectGroupId2 int // ObjGrp2
ObjectGroupId3 int // ObjGrp3
ObjectGroupId4 int // ObjGrp4
ObjectGroupId5 int // ObjGrp5
ObjectGroupId6 int // ObjGrp6
ObjectGroupId7 int // ObjGrp7
ObjectGroupID0 int // ObjGrp0
ObjectGroupID1 int // ObjGrp1
ObjectGroupID2 int // ObjGrp2
ObjectGroupID3 int // ObjGrp3
ObjectGroupID4 int // ObjGrp4
ObjectGroupID5 int // ObjGrp5
ObjectGroupID6 int // ObjGrp6
ObjectGroupID7 int // ObjGrp7
// These fields indicates the chance for each object group to spawn (if you
// use ObjGrp0 then set ObjPrb0 to a value below 100)
@ -387,6 +387,7 @@ func GetLevelDetails(id int) *LevelDetailsRecord {
return nil
}
//nolint:funlen // Txt loader, makes no sense to split
func LoadLevelDetails(file []byte) {
dict := d2common.LoadDataDictionary(string(file))
numRecords := len(dict.Data)
@ -425,22 +426,22 @@ func LoadLevelDetails(file []byte) {
SubTheme: dict.GetNumber("SubTheme", idx),
SubWaypoint: dict.GetNumber("SubWaypoint", idx),
SubShrine: dict.GetNumber("SubShrine", idx),
LevelLinkId0: dict.GetNumber("Vis0", idx),
LevelLinkId1: dict.GetNumber("Vis1", idx),
LevelLinkId2: dict.GetNumber("Vis2", idx),
LevelLinkId3: dict.GetNumber("Vis3", idx),
LevelLinkId4: dict.GetNumber("Vis4", idx),
LevelLinkId5: dict.GetNumber("Vis5", idx),
LevelLinkId6: dict.GetNumber("Vis6", idx),
LevelLinkId7: dict.GetNumber("Vis7", idx),
WarpGraphicsId0: dict.GetNumber("Warp0", idx),
WarpGraphicsId1: dict.GetNumber("Warp1", idx),
WarpGraphicsId2: dict.GetNumber("Warp2", idx),
WarpGraphicsId3: dict.GetNumber("Warp3", idx),
WarpGraphicsId4: dict.GetNumber("Warp4", idx),
WarpGraphicsId5: dict.GetNumber("Warp5", idx),
WarpGraphicsId6: dict.GetNumber("Warp6", idx),
WarpGraphicsId7: dict.GetNumber("Warp7", idx),
LevelLinkID0: dict.GetNumber("Vis0", idx),
LevelLinkID1: dict.GetNumber("Vis1", idx),
LevelLinkID2: dict.GetNumber("Vis2", idx),
LevelLinkID3: dict.GetNumber("Vis3", idx),
LevelLinkID4: dict.GetNumber("Vis4", idx),
LevelLinkID5: dict.GetNumber("Vis5", idx),
LevelLinkID6: dict.GetNumber("Vis6", idx),
LevelLinkID7: dict.GetNumber("Vis7", idx),
WarpGraphicsID0: dict.GetNumber("Warp0", idx),
WarpGraphicsID1: dict.GetNumber("Warp1", idx),
WarpGraphicsID2: dict.GetNumber("Warp2", idx),
WarpGraphicsID3: dict.GetNumber("Warp3", idx),
WarpGraphicsID4: dict.GetNumber("Warp4", idx),
WarpGraphicsID5: dict.GetNumber("Warp5", idx),
WarpGraphicsID6: dict.GetNumber("Warp6", idx),
WarpGraphicsID7: dict.GetNumber("Warp7", idx),
LightIntensity: dict.GetNumber("Intensity", idx),
Red: dict.GetNumber("Red", idx),
Green: dict.GetNumber("Green", idx),
@ -449,7 +450,7 @@ func LoadLevelDetails(file []byte) {
PortalRepositionEnable: dict.GetNumber("Position", idx) > 0,
SaveMonsterStates: dict.GetNumber("SaveMonsters", idx) > 0,
SaveMerchantStates: dict.GetNumber("SaveMonsters", idx) > 0,
QuestId: dict.GetNumber("Quest", idx),
QuestID: dict.GetNumber("Quest", idx),
WarpClearanceDistance: dict.GetNumber("WarpDist", idx),
MonsterLevelNormal: dict.GetNumber("MonLvl1", idx),
MonsterLevelNightmare: dict.GetNumber("MonLvl2", idx),
@ -469,68 +470,68 @@ func LoadLevelDetails(file []byte) {
MonsterWanderEnable: dict.GetNumber("MonWndr", idx) > 0,
MonsterSpecialWalk: dict.GetNumber("MonSpcWalk", idx) > 0,
NumMonsterTypes: dict.GetNumber("NumMon", idx),
MonsterId1Normal: dict.GetString("mon1", idx),
MonsterId2Normal: dict.GetString("mon2", idx),
MonsterId3Normal: dict.GetString("mon3", idx),
MonsterId4Normal: dict.GetString("mon4", idx),
MonsterId5Normal: dict.GetString("mon5", idx),
MonsterId6Normal: dict.GetString("mon6", idx),
MonsterId7Normal: dict.GetString("mon7", idx),
MonsterId8Normal: dict.GetString("mon8", idx),
MonsterId9Normal: dict.GetString("mon9", idx),
MonsterId10Normal: dict.GetString("mon10", idx),
MonsterId1Nightmare: dict.GetString("nmon1", idx),
MonsterId2Nightmare: dict.GetString("nmon2", idx),
MonsterId3Nightmare: dict.GetString("nmon3", idx),
MonsterId4Nightmare: dict.GetString("nmon4", idx),
MonsterId5Nightmare: dict.GetString("nmon5", idx),
MonsterId6Nightmare: dict.GetString("nmon6", idx),
MonsterId7Nightmare: dict.GetString("nmon7", idx),
MonsterId8Nightmare: dict.GetString("nmon8", idx),
MonsterId9Nightmare: dict.GetString("nmon9", idx),
MonsterId10Nightmare: dict.GetString("nmon10", idx),
MonsterId1Hell: dict.GetString("nmon1", idx),
MonsterId2Hell: dict.GetString("nmon2", idx),
MonsterId3Hell: dict.GetString("nmon3", idx),
MonsterId4Hell: dict.GetString("nmon4", idx),
MonsterId5Hell: dict.GetString("nmon5", idx),
MonsterId6Hell: dict.GetString("nmon6", idx),
MonsterId7Hell: dict.GetString("nmon7", idx),
MonsterId8Hell: dict.GetString("nmon8", idx),
MonsterId9Hell: dict.GetString("nmon9", idx),
MonsterId10Hell: dict.GetString("nmon10", idx),
MonsterID1Normal: dict.GetString("mon1", idx),
MonsterID2Normal: dict.GetString("mon2", idx),
MonsterID3Normal: dict.GetString("mon3", idx),
MonsterID4Normal: dict.GetString("mon4", idx),
MonsterID5Normal: dict.GetString("mon5", idx),
MonsterID6Normal: dict.GetString("mon6", idx),
MonsterID7Normal: dict.GetString("mon7", idx),
MonsterID8Normal: dict.GetString("mon8", idx),
MonsterID9Normal: dict.GetString("mon9", idx),
MonsterID10Normal: dict.GetString("mon10", idx),
MonsterID1Nightmare: dict.GetString("nmon1", idx),
MonsterID2Nightmare: dict.GetString("nmon2", idx),
MonsterID3Nightmare: dict.GetString("nmon3", idx),
MonsterID4Nightmare: dict.GetString("nmon4", idx),
MonsterID5Nightmare: dict.GetString("nmon5", idx),
MonsterID6Nightmare: dict.GetString("nmon6", idx),
MonsterID7Nightmare: dict.GetString("nmon7", idx),
MonsterID8Nightmare: dict.GetString("nmon8", idx),
MonsterID9Nightmare: dict.GetString("nmon9", idx),
MonsterID10Nightmare: dict.GetString("nmon10", idx),
MonsterID1Hell: dict.GetString("nmon1", idx),
MonsterID2Hell: dict.GetString("nmon2", idx),
MonsterID3Hell: dict.GetString("nmon3", idx),
MonsterID4Hell: dict.GetString("nmon4", idx),
MonsterID5Hell: dict.GetString("nmon5", idx),
MonsterID6Hell: dict.GetString("nmon6", idx),
MonsterID7Hell: dict.GetString("nmon7", idx),
MonsterID8Hell: dict.GetString("nmon8", idx),
MonsterID9Hell: dict.GetString("nmon9", idx),
MonsterID10Hell: dict.GetString("nmon10", idx),
MonsterPreferRanged: dict.GetNumber("rangedspawn", idx) > 0,
MonsterUniqueId1: dict.GetString("umon1", idx),
MonsterUniqueId2: dict.GetString("umon2", idx),
MonsterUniqueId3: dict.GetString("umon3", idx),
MonsterUniqueId4: dict.GetString("umon4", idx),
MonsterUniqueId5: dict.GetString("umon5", idx),
MonsterUniqueId6: dict.GetString("umon6", idx),
MonsterUniqueId7: dict.GetString("umon7", idx),
MonsterUniqueId8: dict.GetString("umon8", idx),
MonsterUniqueId9: dict.GetString("umon9", idx),
MonsterUniqueId10: dict.GetString("umon10", idx),
MonsterCritterId1: dict.GetString("cmon1", idx),
MonsterCritterId2: dict.GetString("cmon2", idx),
MonsterCritterId3: dict.GetString("cmon3", idx),
MonsterCritterId4: dict.GetString("cmon4", idx),
MonsterUniqueID1: dict.GetString("umon1", idx),
MonsterUniqueID2: dict.GetString("umon2", idx),
MonsterUniqueID3: dict.GetString("umon3", idx),
MonsterUniqueID4: dict.GetString("umon4", idx),
MonsterUniqueID5: dict.GetString("umon5", idx),
MonsterUniqueID6: dict.GetString("umon6", idx),
MonsterUniqueID7: dict.GetString("umon7", idx),
MonsterUniqueID8: dict.GetString("umon8", idx),
MonsterUniqueID9: dict.GetString("umon9", idx),
MonsterUniqueID10: dict.GetString("umon10", idx),
MonsterCritterID1: dict.GetString("cmon1", idx),
MonsterCritterID2: dict.GetString("cmon2", idx),
MonsterCritterID3: dict.GetString("cmon3", idx),
MonsterCritterID4: dict.GetString("cmon4", idx),
MonsterCritter1SpawnChance: dict.GetNumber("cpct1", idx),
MonsterCritter2SpawnChance: dict.GetNumber("cpct2", idx),
MonsterCritter3SpawnChance: dict.GetNumber("cpct3", idx),
MonsterCritter4SpawnChance: dict.GetNumber("cpct4", idx),
SoundEnvironmentId: dict.GetNumber("SoundEnv", idx),
WaypointId: dict.GetNumber("Waypoint", idx),
SoundEnvironmentID: dict.GetNumber("SoundEnv", idx),
WaypointID: dict.GetNumber("Waypoint", idx),
LevelDisplayName: dict.GetString("LevelName", idx),
LevelWarpName: dict.GetString("LevelWarp", idx),
TitleImageName: dict.GetString("EntryFile", idx),
ObjectGroupId0: dict.GetNumber("ObjGrp0", idx),
ObjectGroupId1: dict.GetNumber("ObjGrp1", idx),
ObjectGroupId2: dict.GetNumber("ObjGrp2", idx),
ObjectGroupId3: dict.GetNumber("ObjGrp3", idx),
ObjectGroupId4: dict.GetNumber("ObjGrp4", idx),
ObjectGroupId5: dict.GetNumber("ObjGrp5", idx),
ObjectGroupId6: dict.GetNumber("ObjGrp6", idx),
ObjectGroupId7: dict.GetNumber("ObjGrp7", idx),
ObjectGroupID0: dict.GetNumber("ObjGrp0", idx),
ObjectGroupID1: dict.GetNumber("ObjGrp1", idx),
ObjectGroupID2: dict.GetNumber("ObjGrp2", idx),
ObjectGroupID3: dict.GetNumber("ObjGrp3", idx),
ObjectGroupID4: dict.GetNumber("ObjGrp4", idx),
ObjectGroupID5: dict.GetNumber("ObjGrp5", idx),
ObjectGroupID6: dict.GetNumber("ObjGrp6", idx),
ObjectGroupID7: dict.GetNumber("ObjGrp7", idx),
ObjectGroupSpawnChance0: dict.GetNumber("ObjPrb0", idx),
ObjectGroupSpawnChance1: dict.GetNumber("ObjPrb1", idx),
ObjectGroupSpawnChance2: dict.GetNumber("ObjPrb2", idx),
@ -542,5 +543,6 @@ func LoadLevelDetails(file []byte) {
}
LevelDetails[idx] = record
}
log.Printf("Loaded %d LevelDetails records", len(LevelDetails))
}

View File

@ -9,9 +9,11 @@ import (
func MapHeaders(line string) map[string]int {
m := make(map[string]int)
r := strings.Split(line, "\t")
for index, header := range r {
m[header] = index
}
return m
}
@ -20,6 +22,7 @@ func MapLoadInt(r *[]string, mapping *map[string]int, field string) int {
if ok {
return d2common.StringToInt(d2common.EmptyToZero(d2common.AsterToEmpty((*r)[index])))
}
return 0
}
@ -28,6 +31,7 @@ func MapLoadString(r *[]string, mapping *map[string]int, field string) string {
if ok {
return d2common.AsterToEmpty((*r)[index])
}
return ""
}
@ -40,5 +44,6 @@ func MapLoadUint8(r *[]string, mapping *map[string]int, field string) uint8 {
if ok {
return d2common.StringToUint8(d2common.EmptyToZero(d2common.AsterToEmpty((*r)[index])))
}
return 0
}

View File

@ -109,8 +109,10 @@ type MissileRecord struct {
UseAttackRating bool // if true, uses 'attack rating' to determine if it hits or misses
// if false, has a 95% chance to hit.
AlwaysExplode bool // if true, always calls its collision function when it is destroyed, even if it doesn't hit anything
// note that some collision functions (lightning fury) seem to ignore this and always explode regardless of setting (requires investigation)
AlwaysExplode bool // if true, always calls its collision function when it is destroyed,
// even if it doesn't hit anything
// note that some collision functions (lightning fury)
// seem to ignore this and always explode regardless of setting (requires investigation)
ClientExplosion bool // if true, does not really exist
// is only aesthetic / client side
@ -286,21 +288,26 @@ func createMissileRecord(line string) MissileRecord {
ClientSubMissile: [3]string{r[inc()], r[inc()], r[inc()]},
ClientHitSubMissile: [4]string{r[inc()], r[inc()], r[inc()], r[inc()]},
}
return result
}
//nolint:gochecknoglobals // Currently global by design, only written once
var Missiles map[int]*MissileRecord
func LoadMissiles(file []byte) {
Missiles = make(map[int]*MissileRecord)
data := strings.Split(string(file), "\r\n")[1:]
for _, line := range data {
if len(line) == 0 {
if line == "" {
continue
}
rec := createMissileRecord(line)
Missiles[rec.Id] = &rec
}
log.Printf("Loaded %d missiles", len(Missiles))
}
@ -309,6 +316,7 @@ func loadMissileCalcParam(r *[]string, inc func() int) MissileCalcParam {
Param: d2common.StringToInt(d2common.EmptyToZero((*r)[inc()])),
Desc: (*r)[inc()],
}
return result
}
@ -318,9 +326,11 @@ func loadMissileCalc(r *[]string, inc func() int, params int) MissileCalc {
Desc: (*r)[inc()],
}
result.Params = make([]MissileCalcParam, params)
for p := 0; p < params; p++ {
result.Params[p] = loadMissileCalcParam(r, inc)
}
return result
}
@ -332,6 +342,7 @@ func loadMissileLight(r *[]string, inc func() int) MissileLight {
Green: d2common.StringToUint8(d2common.EmptyToZero((*r)[inc()])),
Blue: d2common.StringToUint8(d2common.EmptyToZero((*r)[inc()])),
}
return result
}
@ -349,6 +360,7 @@ func loadMissileAnimation(r *[]string, inc func() int) MissileAnimation {
SubStartingFrame: d2common.StringToInt(d2common.EmptyToZero((*r)[inc()])),
SubEndingFrame: d2common.StringToInt(d2common.EmptyToZero((*r)[inc()])),
}
return result
}
@ -364,6 +376,7 @@ func loadMissileCollision(r *[]string, inc func() int) MissileCollision {
UseCollisionTimer: d2common.StringToInt(d2common.EmptyToZero((*r)[inc()])) == 1,
TimerFrames: d2common.StringToInt(d2common.EmptyToZero((*r)[inc()])),
}
return result
}
@ -387,6 +400,7 @@ func loadMissileDamage(r *[]string, inc func() int) MissileDamage {
},
DamageSynergyPerCalc: d2common.CalcString((*r)[inc()]),
}
return result
}
@ -401,5 +415,6 @@ func loadMissileElementalDamage(r *[]string, inc func() int) MissileElementalDam
d2common.StringToInt(d2common.EmptyToZero((*r)[inc()])),
},
}
return result
}

View File

@ -6,26 +6,32 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
//nolint:gochecknoglobals // Currently global by design, only written once
var MonPresets [][]string
func LoadMonPresets(file []byte) {
dict := d2common.LoadDataDictionary(string(file))
numRecords := len(dict.Data)
MonPresets = make([][]string, numRecords)
for idx := range MonPresets {
MonPresets[idx] = make([]string, numRecords)
}
lastAct := 0
placeIdx := 0
for dictIdx := range dict.Data {
act := dict.GetNumber("Act", dictIdx)
if act != lastAct {
placeIdx = 0
}
MonPresets[act][placeIdx] = dict.GetString("Place", dictIdx)
placeIdx++
lastAct = act
placeIdx++
}
log.Printf("Loaded %d MonPreset records", len(MonPresets))
}

View File

@ -1,3 +1,4 @@
// d2datadict contains loaders for the txt file data
package d2datadict
import (
@ -37,7 +38,7 @@ type MonStatsRecord struct {
// this is the actual internal ID of the unit (this is what the ID pointer
// actually points at) remember that no two units can have the same ID,
// this will result in lots of unpredictable behaviour and crashes so please
// this will result in lots of unpredictable behavior and crashes so please
// dont do it. This 'HarcCodedInDeX' is used for several things, such as
// determining whenever the unit uses DCC or DC6 graphics (like mephisto
// and the death animations of Diablo, the Maggoc Queen etc.), the hcIdx
@ -49,8 +50,8 @@ type MonStatsRecord struct {
// this column contains the ID pointer of the “base” unit for this specific
// monster type (ex. There are five types of “Fallen”; all of them have
// fallen1 as their “base” unit). The baseID is responsible for some
// hardcoded behaviours, for example moving thru walls (ghosts), knowing
// what units to ressurect, create etc (putrid defilers, shamans etc), the
// hardcoded behaviors, for example moving thru walls (ghosts), knowing
// what units to resurrect, create etc (putrid defilers, shamans etc), the
// explosion appended to suicide minions (either cold, fire or ice). Thanks
// to Kingpin for additional info on this column.
BaseKey string // BaseId
@ -312,7 +313,7 @@ type MonStatsRecord struct {
// these columns control “non-skill-related” missiles used by the monster.
// For example if you enter a missile ID pointer (from Missiles.txt) in
// MissA1 then, whenever the monster uses its A1 mode, it will shoot a
// missile, this however will succesfully prevent it from dealing any damage
// missile, this however will successfully prevent it from dealing any damage
// with the swing of A1.
// NOTE: for the beginners, A1=Attack1, A2=Attack2, S1=Skill1, S2=Skill2,
// S3=Skill3, S4=Skill4, C=Cast, SQ=Sequence.
@ -389,7 +390,7 @@ type MonStatsRecord struct {
// Boolean, 1=I can open doors, 0=Im too damn retarded to open doors. Ever
// wanted to make the game more like D1 (where closing doors could actually
// protect you), then this column is all you need. By setting this to 0 you
// will succesfully lobotomize the monster, thus he will not be able to open
// will successfully lobotomize the monster, thus he will not be able to open
// doors any more.
CanOpenDoors bool // opendoors
@ -793,10 +794,12 @@ type MonStatsRecord struct {
var MonStats map[string]*MonStatsRecord
//nolint:funlen // Makes no sense to split
func LoadMonStats(file []byte) {
dict := d2common.LoadDataDictionary(string(file))
numRecords := len(dict.Data)
MonStats = make(map[string]*MonStatsRecord, numRecords)
for idx := range dict.Data {
record := &MonStatsRecord{
Key: dict.GetString("Id", idx),
@ -1054,5 +1057,6 @@ func LoadMonStats(file []byte) {
}
MonStats[record.Key] = record
}
log.Printf("Loaded %d MonStats records", len(MonStats))
}

View File

@ -0,0 +1,369 @@
package d2datadict
import (
"log"
"github.com/OpenDiablo2/OpenDiablo2/d2common"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
)
type MonStats2Record struct {
// Key, the object ID MonStatEx feild from MonStat
Key string
// These follow three are apparently unused
Height int
OverlayHeight int
PixelHeight int
// Diameter in subtiles
SizeX int
SizeY int
// This specifies if the size values get used for collision detection
NoGfxHitTest bool
// Bounding box
BoxTop int
BoxLeft int
BoxWidth int
BoxHeight int
// Spawn method used
SpawnMethod int
// Melee radius
MeleeRng int
// base weaponclass?
BaseWeaponClass string
HitClass int
// Available options for equipment
// randomly selected from
HDv []string
TRv []string
LGv []string
Rav []string
Lav []string
RHv []string
LHv []string
SHv []string
S1v []string
S2v []string
S3v []string
S4v []string
S5v []string
S6v []string
S7v []string
S8v []string
// Does the unit have this component
HD bool
TR bool
LG bool
RA bool
LA bool
RH bool
LH bool
SH bool
S1 bool
S2 bool
S3 bool
S4 bool
S5 bool
S6 bool
S7 bool
S8 bool
// Sum of available components
TotalPieces int
// Available animation modes
mDT bool
mNU bool
mWL bool
mGH bool
mA1 bool
mA2 bool
mBL bool
mSC bool
mS1 bool
mS2 bool
mS3 bool
mS4 bool
mDD bool
mKB bool
mSQ bool
mRN bool
// Number of directions for each mode
dDT int
dNU int
dWL int
dGH int
dA1 int
dA2 int
dBL int
dSC int
dS1 int
dS2 int
dS3 int
dS4 int
dDD int
dKB int
dSQ int
dRN int
// Available modes while moving aside from WL and RN
A1mv bool
A2mv bool
SCmv bool
S1mv bool
S2mv bool
S3mv bool
S4mv bool
// If the units is restored on map reload
Restore int
// What maximap index is used for the automap
AutomapCel int
// true of unit uses an automap entry
NoMap bool
// If the units can use overlays
NoOvly bool
// If unit is selectable
IsSelectable bool
// If unit is selectable by allies
AllySelectable bool
// If unit is not selectable
NotSelectable bool
// Kinda unk, used for bonewalls etc that are not properly selectable
shiftSel bool
// if the units corpse is selectable
IsCorpseSelectable bool
// If the unit is attackable
IsAttackable bool
// If the unit is revivable
IsRevivable bool
// If the unit is a critter
IsCritter bool
// If the unit is Small, Small units can be knocked back with 100% efficiency
IsSmall bool
// Large units can be knocked back at 25% efficincy
IsLarge bool
// Possibly to do with sound, usually set for creatures without flesh
IsSoft bool
// Aggressive or harmless, usually NPC's
IsInert bool
// Unknown
objCol bool
// Enables collision on corpse for units
IsCorpseCollidable bool
// Can the corpse be walked through
IsCorpseWalkable bool
// If the unit casts a shadow
HasShadow bool
// If unique palettes should not be used
NoUniqueShift bool
// If multiple layers should be used on death (otherwise only TR)
CompositeDeath bool
// Blood offset?
LocalBlood int
// 0 = don't bleed, 1 = small blood missile, 2 = small and large, > 3 other missiles?
Bleed int
// If the unit is lights up the area
Light int
// Light color
LightR int
LightG int
lightB int
// Palettes per difficulty
NormalPalette int
NightmarePalette int
HellPalatte int
// These two are useless as of 1.07
Heart string
BodyPart string
// Inferno animation stuff
InfernoLen int
InfernoAnim int
InfernoRollback int
// Which mode is used after resurrection
ResurrectMode d2enum.MonsterAnimationMode
// Which skill is used for resurrection
ResurrectSkill string
}
//nolint:gochecknoglobals // Current design issue
var MonStats2 map[string]MonStats2Record
//nolint:funlen //just a big data loader
func LoadMonStats2(file []byte) {
dict := d2common.LoadDataDictionary(string(file))
numRecords := len(dict.Data)
MonStats2 = make(map[string]MonStats2Record, numRecords)
for idx := range dict.Data {
record := MonStats2Record{
Key: dict.GetString("Id", idx),
Height: dict.GetNumber("Height", idx),
OverlayHeight: dict.GetNumber("OverlayHeight", idx),
PixelHeight: dict.GetNumber("pixHeight", idx),
SizeX: dict.GetNumber("SizeX", idx),
SizeY: dict.GetNumber("SizeY", idx),
SpawnMethod: dict.GetNumber("spawnCol", idx),
MeleeRng: dict.GetNumber("MeleeRng", idx),
BaseWeaponClass: dict.GetString("BaseW", idx),
HitClass: dict.GetNumber("HitClass", idx),
HDv: dict.GetDelimitedList("HDv", idx),
TRv: dict.GetDelimitedList("TRv", idx),
LGv: dict.GetDelimitedList("LGv", idx),
Rav: dict.GetDelimitedList("Rav", idx),
Lav: dict.GetDelimitedList("Lav", idx),
RHv: dict.GetDelimitedList("RDv", idx),
LHv: dict.GetDelimitedList("LHv", idx),
SHv: dict.GetDelimitedList("SHv", idx),
S1v: dict.GetDelimitedList("S1v", idx),
S2v: dict.GetDelimitedList("S2v", idx),
S3v: dict.GetDelimitedList("S3v", idx),
S4v: dict.GetDelimitedList("S4v", idx),
S5v: dict.GetDelimitedList("S5v", idx),
S6v: dict.GetDelimitedList("S6v", idx),
S7v: dict.GetDelimitedList("S7v", idx),
S8v: dict.GetDelimitedList("S8v", idx),
HD: dict.GetBool("HD", idx),
TR: dict.GetBool("TR", idx),
LG: dict.GetBool("LG", idx),
RA: dict.GetBool("RA", idx),
LA: dict.GetBool("LA", idx),
RH: dict.GetBool("RH", idx),
LH: dict.GetBool("LH", idx),
SH: dict.GetBool("SH", idx),
S1: dict.GetBool("S1", idx),
S2: dict.GetBool("S2", idx),
S3: dict.GetBool("S3", idx),
S4: dict.GetBool("S4", idx),
S5: dict.GetBool("S5", idx),
S6: dict.GetBool("S6", idx),
S7: dict.GetBool("S7", idx),
S8: dict.GetBool("S8", idx),
TotalPieces: dict.GetNumber("TotalPieces", idx),
mDT: dict.GetBool("mDT", idx),
mNU: dict.GetBool("mNU", idx),
mWL: dict.GetBool("mWL", idx),
mGH: dict.GetBool("mGH", idx),
mA1: dict.GetBool("mA1", idx),
mA2: dict.GetBool("mA2", idx),
mBL: dict.GetBool("mBL", idx),
mSC: dict.GetBool("mSC", idx),
mS1: dict.GetBool("mS1", idx),
mS2: dict.GetBool("mS2", idx),
mS3: dict.GetBool("mS3", idx),
mS4: dict.GetBool("mS4", idx),
mDD: dict.GetBool("mDD", idx),
mKB: dict.GetBool("mKB", idx),
mSQ: dict.GetBool("mSQ", idx),
mRN: dict.GetBool("mRN", idx),
dDT: dict.GetNumber("mDT", idx),
dNU: dict.GetNumber("mNU", idx),
dWL: dict.GetNumber("mWL", idx),
dGH: dict.GetNumber("mGH", idx),
dA1: dict.GetNumber("mA1", idx),
dA2: dict.GetNumber("mA2", idx),
dBL: dict.GetNumber("mBL", idx),
dSC: dict.GetNumber("mSC", idx),
dS1: dict.GetNumber("mS1", idx),
dS2: dict.GetNumber("mS2", idx),
dS3: dict.GetNumber("mS3", idx),
dS4: dict.GetNumber("mS4", idx),
dDD: dict.GetNumber("mDD", idx),
dKB: dict.GetNumber("mKB", idx),
dSQ: dict.GetNumber("mSQ", idx),
dRN: dict.GetNumber("mRN", idx),
A1mv: dict.GetBool("A1mv", idx),
A2mv: dict.GetBool("A2mv", idx),
SCmv: dict.GetBool("SCmv", idx),
S1mv: dict.GetBool("S1mv", idx),
S2mv: dict.GetBool("S2mv", idx),
S3mv: dict.GetBool("S3mv", idx),
S4mv: dict.GetBool("S4mv", idx),
NoGfxHitTest: dict.GetBool("noGfxHitTest", idx),
BoxTop: dict.GetNumber("htTop", idx),
BoxLeft: dict.GetNumber("htLeft", idx),
BoxWidth: dict.GetNumber("htWidth", idx),
BoxHeight: dict.GetNumber("htHeight", idx),
Restore: dict.GetNumber("restore", idx),
AutomapCel: dict.GetNumber("automapCel", idx),
NoMap: dict.GetBool("noMap", idx),
NoOvly: dict.GetBool("noOvly", idx),
IsSelectable: dict.GetBool("isSel", idx),
AllySelectable: dict.GetBool("alSel", idx),
shiftSel: dict.GetBool("shiftSel", idx),
NotSelectable: dict.GetBool("noSel", idx),
IsCorpseSelectable: dict.GetBool("corpseSel", idx),
IsAttackable: dict.GetBool("isAtt", idx),
IsRevivable: dict.GetBool("revive", idx),
IsCritter: dict.GetBool("critter", idx),
IsSmall: dict.GetBool("small", idx),
IsLarge: dict.GetBool("large", idx),
IsSoft: dict.GetBool("soft", idx),
IsInert: dict.GetBool("inert", idx),
objCol: dict.GetBool("objCol", idx),
IsCorpseCollidable: dict.GetBool("deadCol", idx),
IsCorpseWalkable: dict.GetBool("unflatDead", idx),
HasShadow: dict.GetBool("Shadow", idx),
NoUniqueShift: dict.GetBool("noUniqueShift", idx),
CompositeDeath: dict.GetBool("compositeDeath", idx),
LocalBlood: dict.GetNumber("localBlood", idx),
Bleed: dict.GetNumber("Bleed", idx),
Light: dict.GetNumber("Light", idx),
LightR: dict.GetNumber("light-r", idx),
LightG: dict.GetNumber("light-g", idx),
lightB: dict.GetNumber("light-b", idx),
NormalPalette: dict.GetNumber("Utrans", idx),
NightmarePalette: dict.GetNumber("Utrans(N)", idx),
HellPalatte: dict.GetNumber("Utrans(H)", idx),
Heart: dict.GetString("Heart", idx),
BodyPart: dict.GetString("BodyPart", idx),
InfernoLen: dict.GetNumber("InfernoLen", idx),
InfernoAnim: dict.GetNumber("InfernoAnim", idx),
InfernoRollback: dict.GetNumber("InfernoRollback", idx),
ResurrectMode: d2enum.MonsterAnimationModeFromString(dict.GetString("ResurrectMode", idx)),
ResurrectSkill: dict.GetString("ResurrectSkill", idx),
}
MonStats2[record.Key] = record
}
log.Printf("Loaded %d MonStats2 records", len(MonStats2))
}

View File

@ -49,6 +49,7 @@ func LookupObject(act, typ, id int) *ObjectLookupRecord {
if object == nil {
log.Panicf("Failed to look up object Act: %d, Type: %d, Id: %d", act, typ, id)
}
return object
}
@ -56,6 +57,7 @@ func lookupObject(act, typ, id int, objects [][][]*ObjectLookupRecord) *ObjectLo
if objects[act] != nil && objects[act][typ] != nil && objects[act][typ][id] != nil {
return objects[act][typ][id]
}
return nil
}
@ -66,6 +68,7 @@ func init() {
func indexObjects(objects []ObjectLookupRecord) [][][]*ObjectLookupRecord {
// Allocating 6 to allow Acts 1-5 without requiring a -1 at every read.
indexedObjects = make([][][]*ObjectLookupRecord, 6)
for i := range objects {
record := &objects[i]
if indexedObjects[record.Act] == nil {

View File

@ -12,12 +12,15 @@ type ObjectTypeRecord struct {
Token string
}
//nolint:gochecknoglobals // Currently global by design, only written once
// ObjectTypes contains the name and token for objects
var ObjectTypes []ObjectTypeRecord
func LoadObjectTypes(objectTypeData []byte) {
streamReader := d2common.CreateStreamReader(objectTypeData)
count := streamReader.GetInt32()
ObjectTypes = make([]ObjectTypeRecord, count)
for i := range ObjectTypes {
nameBytes := streamReader.ReadBytes(32)
tokenBytes := streamReader.ReadBytes(20)
@ -26,5 +29,6 @@ func LoadObjectTypes(objectTypeData []byte) {
Token: strings.TrimSpace(strings.ReplaceAll(string(tokenBytes), string(0), "")),
}
}
log.Printf("Loaded %d object types", len(ObjectTypes))
}

View File

@ -119,6 +119,7 @@ type ObjectRecord struct {
// 0 = it doesn't, rest of modes need to be analyzed
}
//nolint:funlen // Makes no sense to split
// CreateObjectRecord parses a row from objects.txt into an object record
func createObjectRecord(props []string) ObjectRecord {
i := -1
@ -330,24 +331,31 @@ func createObjectRecord(props []string) ObjectRecord {
AutoMap: d2common.StringToInt(props[inc()]),
}
return result
}
//nolint:gochecknoglobals // Currently global by design, only written once
var Objects map[int]*ObjectRecord
func LoadObjects(file []byte) {
Objects = make(map[int]*ObjectRecord)
data := strings.Split(string(file), "\r\n")[1:]
for _, line := range data {
if len(line) == 0 {
if line == "" {
continue
}
props := strings.Split(line, "\t")
if props[2] == "" {
continue // skip a line that doesn't have an id
}
rec := createObjectRecord(props)
Objects[rec.Id] = &rec
}
log.Printf("Loaded %d objects", len(Objects))
}

View File

@ -71,21 +71,27 @@ func createSoundEntry(soundLine string) SoundEntry {
Block2: d2common.StringToInt(props[inc()]),
Block3: d2common.StringToInt(props[inc()]),
}
return result
}
//nolint:gochecknoglobals // Currently global by design, only written once
var Sounds map[string]SoundEntry
func LoadSounds(file []byte) {
Sounds = make(map[string]SoundEntry)
soundData := strings.Split(string(file), "\r\n")[1:]
for _, line := range soundData {
if len(line) == 0 {
if line == "" {
continue
}
soundEntry := createSoundEntry(line)
soundEntry.FileName = "/data/global/sfx/" + strings.ReplaceAll(soundEntry.FileName, `\`, "/")
Sounds[soundEntry.Handle] = soundEntry
//nolint:gocritic // Debug util code
/*
// Use the following code to write out the values
f, err := os.OpenFile(`C:\Users\lunat\Desktop\D2\sounds.txt`,
@ -98,6 +104,7 @@ func LoadSounds(file []byte) {
log.Println(err)
}
*/
}
} //nolint:wsl // Debug util code
log.Printf("Loaded %d sound definitions", len(Sounds))
}

View File

@ -93,8 +93,10 @@ type SuperUniqueRecord struct {
// Boolean indicates if the game is expansion or classic
IsExpansion bool // named as "EClass" in the SuperUniques.txt
// This field states whether the SuperUnique will be placed within a radius from his original position(defined by the .ds1 map file), or not.
// false means that the boss will spawn in a random position within a large radius from its actual position in the .ds1 file,
// This field states whether the SuperUnique will be placed within a radius from his original
// position(defined by the .ds1 map file), or not.
// false means that the boss will spawn in a random position within a large radius from its actual
// position in the .ds1 file,
// true means it will spawn exactly where expected.
AutoPosition bool
@ -104,7 +106,8 @@ type SuperUniqueRecord struct {
// Treasure Classes for the 3 Difficulties.
// These columns list the treasureclass that is valid if this boss is killed and drops something.
// These fields must contain the values taken from the "TreasureClass" column in TreasureClassEx.txt (Expansion) or TreasureClass (Classic).
// These fields must contain the values taken from the "TreasureClass" column in TreasureClassEx.txt (Expansion)
// or TreasureClass (Classic).
TreasureClassNormal string
TreasureClassNightmare string
TreasureClassHell string
@ -120,6 +123,7 @@ var SuperUniques map[string]*SuperUniqueRecord
func LoadSuperUniques(file []byte) {
dictionary := d2common.LoadDataDictionary(string(file))
SuperUniques = make(map[string]*SuperUniqueRecord, len(dictionary.Data))
for idx := range dictionary.Data {
record := &SuperUniqueRecord{
Key: dictionary.GetString("Superunique", idx),
@ -146,5 +150,6 @@ func LoadSuperUniques(file []byte) {
}
SuperUniques[record.Key] = record
}
log.Printf("Loaded %d SuperUnique records", len(SuperUniques))
}

View File

@ -99,6 +99,7 @@ func createUniqueItemRecord(r []string) UniqueItemRecord {
createUniqueItemProperty(&r, inc),
},
}
return result
}
@ -109,6 +110,7 @@ func createUniqueItemProperty(r *[]string, inc func() int) UniqueItemProperty {
Min: d2common.StringToInt(d2common.EmptyToZero((*r)[inc()])),
Max: d2common.StringToInt(d2common.EmptyToZero((*r)[inc()])),
}
return result
}
@ -117,17 +119,21 @@ var UniqueItems map[string]*UniqueItemRecord
func LoadUniqueItems(file []byte) {
UniqueItems = make(map[string]*UniqueItemRecord)
data := strings.Split(string(file), "\r\n")[1:]
for _, line := range data {
if len(line) == 0 {
if line == "" {
continue
}
r := strings.Split(line, "\t")
// skip rows that are not enabled
if r[2] != "1" {
continue
}
rec := createUniqueItemRecord(r)
UniqueItems[rec.Code] = &rec
}
log.Printf("Loaded %d unique items", len(UniqueItems))
}

View File

@ -0,0 +1,57 @@
// Manually edited to remove duplicate
// If you generate it again you need fix it up
// Code generated by "string2enum -samepkg -linecomment -type MonsterAnimationMode ."; DO NOT EDIT.
package d2enum
import "fmt"
// MonsterAnimationModeFromString returns the MonsterAnimationMode enum corresponding to s.
func MonsterAnimationModeFromString(s string) MonsterAnimationMode {
if len(s) == 0 {
return 0
}
for i := range _MonsterAnimationMode_index[:len(_MonsterAnimationMode_index)-1] {
if s == _MonsterAnimationMode_name[_MonsterAnimationMode_index[i]:_MonsterAnimationMode_index[i+1]] {
return MonsterAnimationMode(i)
}
}
panic(fmt.Errorf("unable to locate MonsterAnimationMode enum corresponding to %q", s))
}
func _(s string) {
// Check for duplicate string values in type "MonsterAnimationMode".
switch s {
// 0
case "DT":
// 1
case "NU":
// 2
case "WL":
// 3
case "GH":
// 4
case "A1":
// 5
case "A2":
// 6
case "BL":
// 7
case "SC":
// 8
case "S1":
// 9
case "S2":
// 10
case "S3":
// 11
case "S4":
// 12
case "DD":
// 14
case "xx":
// 15
case "RN":
}
}

View File

@ -279,6 +279,7 @@ const (
// --- Enemy Data ---
MonStats = "/data/global/excel/monstats.txt"
MonStats2 = "/data/global/excel/monstats2.txt"
MonPreset = "/data/global/excel/monpreset.txt"
SuperUniques = "/data/global/excel/SuperUniques.txt"

View File

@ -47,3 +47,15 @@ func (v *DataDictionary) GetNumber(fieldName string, index int) int {
}
return result
}
func (v *DataDictionary) GetDelimitedList(fieldName string, index int) []string {
return strings.Split(v.GetString(fieldName, index), ",")
}
func (v *DataDictionary) GetBool(fieldName string, index int) bool {
n := v.GetNumber(fieldName, index)
if n > 1 {
log.Panic("GetBool on non-bool field")
}
return n == 1
}

View File

@ -41,7 +41,7 @@ func CreateMapRenderer(mapEngine *d2mapengine.MapEngine, term d2interface.Termin
result.debugVisLevel = level
})
if mapEngine.LevelType().Id != 0 {
if mapEngine.LevelType().ID != 0 {
result.generateTileCache()
}

View File

@ -14,7 +14,7 @@ import (
)
func (mr *MapRenderer) generateTileCache() {
mr.palette, _ = loadPaletteForAct(d2enum.RegionIdType(mr.mapEngine.LevelType().Id))
mr.palette, _ = loadPaletteForAct(d2enum.RegionIdType(mr.mapEngine.LevelType().ID))
mapEngineSize := mr.mapEngine.Size()
for idx, tile := range *mr.mapEngine.Tiles() {
@ -197,7 +197,7 @@ func (mr *MapRenderer) getRandomTile(tiles []d2dt1.Tile, x, y int, seed int64) b
var tileSeed uint64
tileSeed = uint64(seed) + uint64(x)
tileSeed *= uint64(y) + uint64(mr.mapEngine.LevelType().Id)
tileSeed *= uint64(y) + uint64(mr.mapEngine.LevelType().ID)
tileSeed ^= tileSeed << 13
tileSeed ^= tileSeed >> 17

View File

@ -431,6 +431,7 @@ func loadDataDict() error {
{d2resource.SoundSettings, d2datadict.LoadSounds},
{d2resource.AnimationData, d2data.LoadAnimationData},
{d2resource.MonStats, d2datadict.LoadMonStats},
{d2resource.MonStats2, d2datadict.LoadMonStats2},
{d2resource.MonPreset, d2datadict.LoadMonPresets},
{d2resource.MagicPrefix, d2datadict.LoadMagicPrefix},
{d2resource.MagicSuffix, d2datadict.LoadMagicSuffix},