1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-09-16 00:08:29 -04:00

d2datadict reading changes (#553)

* Read excel files with csv.Reader

* Read LvlWarp from txt file

* Fix lint issues in d2datadict

* changed ID back to Id
This commit is contained in:
Intyre 2020-07-07 14:56:31 +02:00 committed by GitHub
parent af0fb11a54
commit 04c7ff543a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 1281 additions and 1285 deletions

View File

@ -6,6 +6,7 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
) )
// Armors stores all of the ArmorRecords
//nolint:gochecknoglobals // Currently global by design, only written once //nolint:gochecknoglobals // Currently global by design, only written once
var Armors map[string]*ItemCommonRecord var Armors map[string]*ItemCommonRecord

View File

@ -2,18 +2,10 @@ package d2datadict
import ( import (
"log" "log"
"strings"
"github.com/OpenDiablo2/OpenDiablo2/d2common" "github.com/OpenDiablo2/OpenDiablo2/d2common"
) )
const (
expansion = "Expansion" // blizzard put this in the txt where expansion data starts
)
//nolint:gochecknoglobals // Currently global by design, only written once
var frameFields = []string{"Cel1", "Cel2", "Cel3", "Cel4"}
// AutoMapRecord represents one row from d2data.mpq/AutoMap.txt. // AutoMapRecord represents one row from d2data.mpq/AutoMap.txt.
// Based on the information here https://d2mods.info/forum/kb/viewarticle?a=419 // Based on the information here https://d2mods.info/forum/kb/viewarticle?a=419
type AutoMapRecord struct { type AutoMapRecord struct {
@ -67,66 +59,40 @@ var AutoMaps []*AutoMapRecord
// LoadAutoMaps populates AutoMaps with the data from AutoMap.txt. // LoadAutoMaps populates AutoMaps with the data from AutoMap.txt.
// It also amends a duplicate field (column) name in that data. // It also amends a duplicate field (column) name in that data.
func LoadAutoMaps(file []byte) { func LoadAutoMaps(file []byte) {
// Fix the error in the original file AutoMaps = make([]*AutoMapRecord, 0)
fileString := fixDuplicateFieldName(string(file))
var frameFields = []string{"Cel1", "Cel2", "Cel3", "Cel4"}
// Split file by newlines and tabs // Split file by newlines and tabs
d := d2common.LoadDataDictionary(fileString) d := d2common.LoadDataDictionary(file)
for d.Next() {
record := &AutoMapRecord{
LevelName: d.String("LevelName"),
TileName: d.String("TileName"),
// Construct records Style: d.Number("Style"),
AutoMaps = make([]*AutoMapRecord, len(d.Data)) StartSequence: d.Number("StartSequence"),
EndSequence: d.Number("EndSequence"),
for idx := range d.Data { //Type1: d.String("Type1"),
if d.GetString("LevelName", idx) == expansion { //Type2: d.String("Type2"),
continue //Type3: d.String("Type3"),
} //Type4: d.String("Type4"),
AutoMaps[idx] = &AutoMapRecord{
LevelName: d.GetString("LevelName", idx),
TileName: d.GetString("TileName", idx),
Style: d.GetNumber("Style", idx),
StartSequence: d.GetNumber("StartSequence", idx),
EndSequence: d.GetNumber("EndSequence", idx),
//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 // Note: I commented these out for now because they supposedly
// aren't useful see the AutoMapRecord struct. // aren't useful see the AutoMapRecord struct.
} }
record.Frames = make([]int, len(frameFields))
AutoMaps[idx].Frames = make([]int, len(frameFields))
for i := range frameFields { for i := range frameFields {
AutoMaps[idx].Frames[i] = d.GetNumber(frameFields[i], idx) record.Frames[i] = d.Number(frameFields[i])
} }
AutoMaps = append(AutoMaps, record)
}
if d.Err != nil {
panic(d.Err)
} }
log.Printf("Loaded %d AutoMapRecord records", len(AutoMaps)) log.Printf("Loaded %d AutoMapRecord records", len(AutoMaps))
} }
// fixDuplicateFieldName changes one of the two 'Type2' fields
// in AutoMap.txt to 'Type3'. An error in the file can be seen
// by looking at the lists of 'Type' and 'Cel' fields:
//
// Type1 Type2 Type2* Type4
// Cel1 Cel2 Cel3 Cel4
//
// LoadDataDictionary uses a set of field names. The duplicate
// is omitted resulting in all rows being skipped because their
// counts are different from the field names count.
func fixDuplicateFieldName(fileString string) string {
// Split rows
rows := strings.Split(fileString, "\r\n")
// Split the field names row and correct the duplicate
fieldNames := strings.Split(rows[0], "\t")
fieldNames[9] = "Type3"
// Join the field names back up and assign to the first row
rows[0] = strings.Join(fieldNames, "\t")
// Return the rows, joined back into one string
return strings.Join(rows, "\r\n")
}

View File

@ -67,6 +67,8 @@ var weaponTokenMap map[string]d2enum.WeaponClass //nolint:gochecknoglobals // Cu
//nolint:funlen // Makes no sense to split //nolint:funlen // Makes no sense to split
// LoadCharStats loads charstats.txt file contents into map[d2enum.Hero]*CharStatsRecord // LoadCharStats loads charstats.txt file contents into map[d2enum.Hero]*CharStatsRecord
func LoadCharStats(file []byte) { func LoadCharStats(file []byte) {
CharStats = make(map[d2enum.Hero]*CharStatsRecord)
charStringMap = map[string]d2enum.Hero{ charStringMap = map[string]d2enum.Hero{
"Amazon": d2enum.HeroAmazon, "Amazon": d2enum.HeroAmazon,
"Barbarian": d2enum.HeroBarbarian, "Barbarian": d2enum.HeroBarbarian,
@ -95,103 +97,105 @@ func LoadCharStats(file []byte) {
"ht2": d2enum.WeaponClassTwoHandToHand, "ht2": d2enum.WeaponClassTwoHandToHand,
} }
d := d2common.LoadDataDictionary(string(file)) d := d2common.LoadDataDictionary(file)
CharStats = make(map[d2enum.Hero]*CharStatsRecord, len(d.Data)) for d.Next() {
for idx := range d.Data {
record := &CharStatsRecord{ record := &CharStatsRecord{
Class: charStringMap[d.GetString("class", idx)], Class: charStringMap[d.String("class")],
InitStr: d.GetNumber("str", idx), InitStr: d.Number("str"),
InitDex: d.GetNumber("dex", idx), InitDex: d.Number("dex"),
InitVit: d.GetNumber("vit", idx), InitVit: d.Number("vit"),
InitEne: d.GetNumber("int", idx), InitEne: d.Number("int"),
InitStamina: d.GetNumber("stamina", idx), InitStamina: d.Number("stamina"),
ManaRegen: d.GetNumber("ManaRegen", idx), ManaRegen: d.Number("ManaRegen"),
ToHitFactor: d.GetNumber("ToHitFactor", idx), ToHitFactor: d.Number("ToHitFactor"),
VelocityWalk: d.GetNumber("WalkVelocity", idx), VelocityWalk: d.Number("WalkVelocity"),
VelocityRun: d.GetNumber("RunVelocity", idx), VelocityRun: d.Number("RunVelocity"),
StaminaRunDrain: d.GetNumber("RunDrain", idx), StaminaRunDrain: d.Number("RunDrain"),
LifePerLevel: d.GetNumber("LifePerLevel", idx), LifePerLevel: d.Number("LifePerLevel"),
ManaPerLevel: d.GetNumber("ManaPerLevel", idx), ManaPerLevel: d.Number("ManaPerLevel"),
StaminaPerLevel: d.GetNumber("StaminaPerLevel", idx), StaminaPerLevel: d.Number("StaminaPerLevel"),
LifePerVit: d.GetNumber("LifePerVitality", idx), LifePerVit: d.Number("LifePerVitality"),
ManaPerEne: d.GetNumber("ManaPerMagic", idx), ManaPerEne: d.Number("ManaPerMagic"),
StaminaPerVit: d.GetNumber("StaminaPerVitality", idx), StaminaPerVit: d.Number("StaminaPerVitality"),
StatPerLevel: d.GetNumber("StatPerLevel", idx), StatPerLevel: d.Number("StatPerLevel"),
BlockFactor: d.GetNumber("BlockFactor", idx), BlockFactor: d.Number("BlockFactor"),
StartSkillBonus: d.GetString("StartSkill", idx), StartSkillBonus: d.String("StartSkill"),
SkillStrAll: d.GetString("StrAllSkills", idx), SkillStrAll: d.String("StrAllSkills"),
SkillStrClassOnly: d.GetString("StrClassOnly", idx), SkillStrClassOnly: d.String("StrClassOnly"),
BaseSkill: [10]string{ BaseSkill: [10]string{
d.GetString("Skill 1", idx), d.String("Skill 1"),
d.GetString("Skill 2", idx), d.String("Skill 2"),
d.GetString("Skill 3", idx), d.String("Skill 3"),
d.GetString("Skill 4", idx), d.String("Skill 4"),
d.GetString("Skill 5", idx), d.String("Skill 5"),
d.GetString("Skill 6", idx), d.String("Skill 6"),
d.GetString("Skill 7", idx), d.String("Skill 7"),
d.GetString("Skill 8", idx), d.String("Skill 8"),
d.GetString("Skill 9", idx), d.String("Skill 9"),
d.GetString("Skill 10", idx), d.String("Skill 10"),
}, },
SkillStrTab: [3]string{ SkillStrTab: [3]string{
d.GetString("StrSkillTab1", idx), d.String("StrSkillTab1"),
d.GetString("StrSkillTab2", idx), d.String("StrSkillTab2"),
d.GetString("StrSkillTab3", idx), d.String("StrSkillTab3"),
}, },
BaseWeaponClass: weaponTokenMap[d.GetString("baseWClass", idx)], BaseWeaponClass: weaponTokenMap[d.String("baseWClass")],
StartItem: [10]string{ StartItem: [10]string{
d.GetString("item1", idx), d.String("item1"),
d.GetString("item2", idx), d.String("item2"),
d.GetString("item3", idx), d.String("item3"),
d.GetString("item4", idx), d.String("item4"),
d.GetString("item5", idx), d.String("item5"),
d.GetString("item6", idx), d.String("item6"),
d.GetString("item7", idx), d.String("item7"),
d.GetString("item8", idx), d.String("item8"),
d.GetString("item9", idx), d.String("item9"),
d.GetString("item10", idx), d.String("item10"),
}, },
StartItemLocation: [10]string{ StartItemLocation: [10]string{
d.GetString("item1loc", idx), d.String("item1loc"),
d.GetString("item2loc", idx), d.String("item2loc"),
d.GetString("item3loc", idx), d.String("item3loc"),
d.GetString("item4loc", idx), d.String("item4loc"),
d.GetString("item5loc", idx), d.String("item5loc"),
d.GetString("item6loc", idx), d.String("item6loc"),
d.GetString("item7loc", idx), d.String("item7loc"),
d.GetString("item8loc", idx), d.String("item8loc"),
d.GetString("item9loc", idx), d.String("item9loc"),
d.GetString("item10loc", idx), d.String("item10loc"),
}, },
StartItemCount: [10]int{ StartItemCount: [10]int{
d.GetNumber("item1count", idx), d.Number("item1count"),
d.GetNumber("item2count", idx), d.Number("item2count"),
d.GetNumber("item3count", idx), d.Number("item3count"),
d.GetNumber("item4count", idx), d.Number("item4count"),
d.GetNumber("item5count", idx), d.Number("item5count"),
d.GetNumber("item6count", idx), d.Number("item6count"),
d.GetNumber("item7count", idx), d.Number("item7count"),
d.GetNumber("item8count", idx), d.Number("item8count"),
d.GetNumber("item9count", idx), d.Number("item9count"),
d.GetNumber("item10count", idx), d.Number("item10count"),
}, },
} }
CharStats[record.Class] = record CharStats[record.Class] = record
} }
if d.Err != nil {
panic(d.Err)
}
log.Printf("Loaded %d CharStats records", len(CharStats)) log.Printf("Loaded %d CharStats records", len(CharStats))
} }

View File

@ -146,8 +146,7 @@ var CubeRecipes []*CubeRecipeRecord
// LoadCubeRecipes populates CubeRecipes with // LoadCubeRecipes populates CubeRecipes with
// the data from CubeMain.txt. // the data from CubeMain.txt.
func LoadCubeRecipes(file []byte) { func LoadCubeRecipes(file []byte) {
// Load data CubeRecipes = make([]*CubeRecipeRecord, 0)
d := d2common.LoadDataDictionary(string(file))
// There are repeated fields and sections in this file, some // There are repeated fields and sections in this file, some
// of which have inconsistent naming conventions. These slices // of which have inconsistent naming conventions. These slices
@ -160,60 +159,65 @@ func LoadCubeRecipes(file []byte) {
var inputFields = []string{"input 1", "input 2", "input 3", "input 4", "input 5", "input 6", "input 7"} var inputFields = []string{"input 1", "input 2", "input 3", "input 4", "input 5", "input 6", "input 7"}
// Create records d := d2common.LoadDataDictionary(file)
CubeRecipes = make([]*CubeRecipeRecord, len(d.Data)) for d.Next() {
for idx := range d.Data { record := &CubeRecipeRecord{
CubeRecipes[idx] = &CubeRecipeRecord{ Description: d.String("description"),
Description: d.GetString("description", idx),
Enabled: d.GetNumber("enabled", idx) == 1, Enabled: d.Bool("enabled"),
Ladder: d.GetNumber("ladder", idx) == 1, Ladder: d.Bool("ladder"),
MinDiff: d.GetNumber("min diff", idx), MinDiff: d.Number("min diff"),
Version: d.GetNumber("version", idx), Version: d.Number("version"),
ReqStatID: d.GetNumber("param", idx), ReqStatID: d.Number("param"),
ReqOperation: d.GetNumber("op", idx), ReqOperation: d.Number("op"),
ReqValue: d.GetNumber("value", idx), ReqValue: d.Number("value"),
Class: classFieldToEnum(d.GetString("class", idx)), Class: classFieldToEnum(d.String("class")),
NumInputs: d.GetNumber("numinputs", idx), NumInputs: d.Number("numinputs"),
} }
// Create inputs - input 1-7 // Create inputs - input 1-7
CubeRecipes[idx].Inputs = make([]CubeRecipeItem, 7) record.Inputs = make([]CubeRecipeItem, len(inputFields))
for i := range inputFields { for i := range inputFields {
CubeRecipes[idx].Inputs[i] = newCubeRecipeItem( record.Inputs[i] = newCubeRecipeItem(
d.GetString(inputFields[i], idx)) d.String(inputFields[i]))
} }
// Create outputs - output "", b, c // Create outputs - output "", b, c
CubeRecipes[idx].Outputs = make([]CubeRecipeResult, 3) record.Outputs = make([]CubeRecipeResult, len(outputLabels))
for o, outLabel := range outputLabels { for o, outLabel := range outputLabels {
CubeRecipes[idx].Outputs[o] = CubeRecipeResult{ record.Outputs[o] = CubeRecipeResult{
Item: newCubeRecipeItem( Item: newCubeRecipeItem(
d.GetString(outputFields[o], idx)), d.String(outputFields[o])),
Level: d.GetNumber(outLabel+"lvl", idx), Level: d.Number(outLabel + "lvl"),
ILevel: d.GetNumber(outLabel+"plvl", idx), ILevel: d.Number(outLabel + "plvl"),
PLevel: d.GetNumber(outLabel+"ilvl", idx), PLevel: d.Number(outLabel + "ilvl"),
} }
// Create properties - mod 1-5 // Create properties - mod 1-5
properties := make([]CubeRecipeItemProperty, 5) properties := make([]CubeRecipeItemProperty, len(propLabels))
for p, prop := range propLabels { for p, prop := range propLabels {
properties[p] = CubeRecipeItemProperty{ properties[p] = CubeRecipeItemProperty{
Code: d.GetString(outLabel+prop, idx), Code: d.String(outLabel + prop),
Chance: d.GetNumber(outLabel+prop+" chance", idx), Chance: d.Number(outLabel + prop + " chance"),
Param: d.GetNumber(outLabel+prop+" param", idx), Param: d.Number(outLabel + prop + " param"),
Min: d.GetNumber(outLabel+prop+" min", idx), Min: d.Number(outLabel + prop + " min"),
Max: d.GetNumber(outLabel+prop+" max", idx), Max: d.Number(outLabel + prop + " max"),
} }
} }
CubeRecipes[idx].Outputs[o].Properties = properties record.Outputs[o].Properties = properties
} }
CubeRecipes = append(CubeRecipes, record)
}
if d.Err != nil {
panic(d.Err)
} }
log.Printf("Loaded %d CubeMainRecord records", len(CubeRecipes)) log.Printf("Loaded %d CubeMainRecord records", len(CubeRecipes))

View File

@ -94,33 +94,35 @@ type DifficultyLevelRecord struct {
// LoadDifficultyLevels is a loader for difficultylevels.txt // LoadDifficultyLevels is a loader for difficultylevels.txt
func LoadDifficultyLevels(file []byte) { func LoadDifficultyLevels(file []byte) {
dict := d2common.LoadDataDictionary(string(file)) DifficultyLevels = make(map[string]*DifficultyLevelRecord)
numRows := len(dict.Data)
DifficultyLevels = make(map[string]*DifficultyLevelRecord, numRows) d := d2common.LoadDataDictionary(file)
for d.Next() {
for idx := range dict.Data {
record := &DifficultyLevelRecord{ record := &DifficultyLevelRecord{
Name: dict.GetString("Name", idx), Name: d.String("Name"),
ResistancePenalty: dict.GetNumber("ResistPenalty", idx), ResistancePenalty: d.Number("ResistPenalty"),
DeathExperiencePenalty: dict.GetNumber("DeathExpPenalty", idx), DeathExperiencePenalty: d.Number("DeathExpPenalty"),
DropChanceLow: dict.GetNumber("UberCodeOddsNormal", idx), DropChanceLow: d.Number("UberCodeOddsNormal"),
DropChanceNormal: dict.GetNumber("UberCodeOddsNormal", idx), DropChanceNormal: d.Number("UberCodeOddsNormal"),
DropChanceSuperior: dict.GetNumber("UberCodeOddsNormal", idx), DropChanceSuperior: d.Number("UberCodeOddsNormal"),
DropChanceExceptional: dict.GetNumber("UberCodeOddsNormal", idx), DropChanceExceptional: d.Number("UberCodeOddsNormal"),
DropChanceMagic: dict.GetNumber("UberCodeOddsGood", idx), DropChanceMagic: d.Number("UberCodeOddsGood"),
DropChanceRare: dict.GetNumber("UberCodeOddsGood", idx), DropChanceRare: d.Number("UberCodeOddsGood"),
DropChanceSet: dict.GetNumber("UberCodeOddsGood", idx), DropChanceSet: d.Number("UberCodeOddsGood"),
DropChanceUnique: dict.GetNumber("UberCodeOddsGood", idx), DropChanceUnique: d.Number("UberCodeOddsGood"),
MonsterSkillBonus: dict.GetNumber("MonsterSkillBonus", idx), MonsterSkillBonus: d.Number("MonsterSkillBonus"),
MonsterColdDivisor: dict.GetNumber("MonsterColdDivisor", idx), MonsterColdDivisor: d.Number("MonsterColdDivisor"),
MonsterFreezeDivisor: dict.GetNumber("MonsterFreezeDivisor", idx), MonsterFreezeDivisor: d.Number("MonsterFreezeDivisor"),
AiCurseDivisor: dict.GetNumber("AiCurseDivisor", idx), AiCurseDivisor: d.Number("AiCurseDivisor"),
LifeStealDivisor: dict.GetNumber("LifeStealDivisor", idx), LifeStealDivisor: d.Number("LifeStealDivisor"),
ManaStealDivisor: dict.GetNumber("ManaStealDivisor", idx), ManaStealDivisor: d.Number("ManaStealDivisor"),
} }
DifficultyLevels[record.Name] = record DifficultyLevels[record.Name] = record
} }
if d.Err != nil {
panic(d.Err)
}
log.Printf("Loaded %d DifficultyLevel records", len(DifficultyLevels)) log.Printf("Loaded %d DifficultyLevel records", len(DifficultyLevels))
} }

View File

@ -42,7 +42,7 @@ type ExperienceBreakpointsRecord struct {
// ExperienceBreakpoints describes the required experience // ExperienceBreakpoints describes the required experience
// for each level for each character class // for each level for each character class
//nolint:gochecknoglobals // Currently global by design, only written once //nolint:gochecknoglobals // Currently global by design, only written once
var ExperienceBreakpoints []*ExperienceBreakpointsRecord var ExperienceBreakpoints map[int]*ExperienceBreakpointsRecord
//nolint:gochecknoglobals // Currently global by design //nolint:gochecknoglobals // Currently global by design
var maxLevels map[d2enum.Hero]int var maxLevels map[d2enum.Hero]int
@ -60,43 +60,42 @@ func GetExperienceBreakpoint(heroType d2enum.Hero, level int) int {
// LoadExperienceBreakpoints loads experience.txt into a map // LoadExperienceBreakpoints loads experience.txt into a map
// ExperienceBreakpoints []*ExperienceBreakpointsRecord // ExperienceBreakpoints []*ExperienceBreakpointsRecord
func LoadExperienceBreakpoints(file []byte) { func LoadExperienceBreakpoints(file []byte) {
d := d2common.LoadDataDictionary(string(file)) ExperienceBreakpoints = make(map[int]*ExperienceBreakpointsRecord)
// we skip the second row because that describes max level of char classes d := d2common.LoadDataDictionary(file)
ExperienceBreakpoints = make([]*ExperienceBreakpointsRecord, len(d.Data)-1) d.Next()
for idx := range d.Data { // the first row describes the max level of char classes
if idx == 0 { maxLevels = map[d2enum.Hero]int{
// max levels are a special case d2enum.HeroAmazon: d.Number("Amazon"),
maxLevels = map[d2enum.Hero]int{ d2enum.HeroBarbarian: d.Number("Barbarian"),
d2enum.HeroAmazon: d.GetNumber("Amazon", idx), d2enum.HeroDruid: d.Number("Druid"),
d2enum.HeroBarbarian: d.GetNumber("Barbarian", idx), d2enum.HeroAssassin: d.Number("Assassin"),
d2enum.HeroDruid: d.GetNumber("Druid", idx), d2enum.HeroNecromancer: d.Number("Necromancer"),
d2enum.HeroAssassin: d.GetNumber("Assassin", idx), d2enum.HeroPaladin: d.Number("Paladin"),
d2enum.HeroNecromancer: d.GetNumber("Necromancer", idx), d2enum.HeroSorceress: d.Number("Sorceress"),
d2enum.HeroPaladin: d.GetNumber("Paladin", idx), }
d2enum.HeroSorceress: d.GetNumber("Sorceress", idx),
}
continue
}
for d.Next() {
record := &ExperienceBreakpointsRecord{ record := &ExperienceBreakpointsRecord{
Level: d.GetNumber("Level", idx), Level: d.Number("Level"),
HeroBreakpoints: map[d2enum.Hero]int{ HeroBreakpoints: map[d2enum.Hero]int{
d2enum.HeroAmazon: d.GetNumber("Amazon", idx), d2enum.HeroAmazon: d.Number("Amazon"),
d2enum.HeroBarbarian: d.GetNumber("Barbarian", idx), d2enum.HeroBarbarian: d.Number("Barbarian"),
d2enum.HeroDruid: d.GetNumber("Druid", idx), d2enum.HeroDruid: d.Number("Druid"),
d2enum.HeroAssassin: d.GetNumber("Assassin", idx), d2enum.HeroAssassin: d.Number("Assassin"),
d2enum.HeroNecromancer: d.GetNumber("Necromancer", idx), d2enum.HeroNecromancer: d.Number("Necromancer"),
d2enum.HeroPaladin: d.GetNumber("Paladin", idx), d2enum.HeroPaladin: d.Number("Paladin"),
d2enum.HeroSorceress: d.GetNumber("Sorceress", idx), d2enum.HeroSorceress: d.Number("Sorceress"),
}, },
Ratio: d.GetNumber("ExpRatio", idx), Ratio: d.Number("ExpRatio"),
} }
ExperienceBreakpoints[record.Level] = record ExperienceBreakpoints[record.Level] = record
} }
if d.Err != nil {
panic(d.Err)
}
log.Printf("Loaded %d ExperienceBreakpoint records", len(ExperienceBreakpoints)) log.Printf("Loaded %d ExperienceBreakpoint records", len(ExperienceBreakpoints))
} }

View File

@ -57,61 +57,58 @@ var Gems map[string]*GemsRecord //nolint:gochecknoglobals // Currently global by
// LoadGems loads gem records into a map[string]*GemsRecord // LoadGems loads gem records into a map[string]*GemsRecord
func LoadGems(file []byte) { func LoadGems(file []byte) {
d := d2common.LoadDataDictionary(string(file)) Gems = make(map[string]*GemsRecord)
Gems = make(map[string]*GemsRecord, len(d.Data)) d := d2common.LoadDataDictionary(file)
for d.Next() {
for idx := range d.Data { gem := &GemsRecord{
if d.GetString("name", idx) != expansion { Name: d.String("name"),
/* Letter: d.String("letter"),
"Expansion" is the only field in line 36 of /data/global/excel/gems.txt and is only used to visually Transform: d.Number("transform"),
separate base-game gems and expansion runes. Code: d.String("code"),
*/ Nummods: d.Number("nummods"),
gem := &GemsRecord{ WeaponMod1Code: d.String("weaponMod1Code"),
Name: d.GetString("name", idx), WeaponMod1Param: d.Number("weaponMod1Param"),
Letter: d.GetString("letter", idx), WeaponMod1Min: d.Number("weaponMod1Min"),
Transform: d.GetNumber("transform", idx), WeaponMod1Max: d.Number("weaponMod1Max"),
Code: d.GetString("code", idx), WeaponMod2Code: d.String("weaponMod2Code"),
Nummods: d.GetNumber("nummods", idx), WeaponMod2Param: d.Number("weaponMod2Param"),
WeaponMod1Code: d.GetString("weaponMod1Code", idx), WeaponMod2Min: d.Number("weaponMod2Min"),
WeaponMod1Param: d.GetNumber("weaponMod1Param", idx), WeaponMod2Max: d.Number("weaponMod2Max"),
WeaponMod1Min: d.GetNumber("weaponMod1Min", idx), WeaponMod3Code: d.String("weaponMod3Code"),
WeaponMod1Max: d.GetNumber("weaponMod1Max", idx), WeaponMod3Param: d.Number("weaponMod3Param"),
WeaponMod2Code: d.GetString("weaponMod2Code", idx), WeaponMod3Min: d.Number("weaponMod3Min"),
WeaponMod2Param: d.GetNumber("weaponMod2Param", idx), WeaponMod3Max: d.Number("weaponMod3Max"),
WeaponMod2Min: d.GetNumber("weaponMod2Min", idx), HelmMod1Code: d.String("helmMod1Code"),
WeaponMod2Max: d.GetNumber("weaponMod2Max", idx), HelmMod1Param: d.Number("helmMod1Param"),
WeaponMod3Code: d.GetString("weaponMod3Code", idx), HelmMod1Min: d.Number("helmMod1Min"),
WeaponMod3Param: d.GetNumber("weaponMod3Param", idx), HelmMod1Max: d.Number("helmMod1Max"),
WeaponMod3Min: d.GetNumber("weaponMod3Min", idx), HelmMod2Code: d.String("helmMod2Code"),
WeaponMod3Max: d.GetNumber("weaponMod3Max", idx), HelmMod2Param: d.Number("helmMod2Param"),
HelmMod1Code: d.GetString("helmMod1Code", idx), HelmMod2Min: d.Number("helmMod2Min"),
HelmMod1Param: d.GetNumber("helmMod1Param", idx), HelmMod2Max: d.Number("helmMod2Max"),
HelmMod1Min: d.GetNumber("helmMod1Min", idx), HelmMod3Code: d.String("helmMod3Code"),
HelmMod1Max: d.GetNumber("helmMod1Max", idx), HelmMod3Param: d.Number("helmMod3Param"),
HelmMod2Code: d.GetString("helmMod2Code", idx), HelmMod3Min: d.Number("helmMod3Min"),
HelmMod2Param: d.GetNumber("helmMod2Param", idx), HelmMod3Max: d.Number("helmMod3Max"),
HelmMod2Min: d.GetNumber("helmMod2Min", idx), ShieldMod1Code: d.String("shieldMod1Code"),
HelmMod2Max: d.GetNumber("helmMod2Max", idx), ShieldMod1Param: d.Number("shieldMod1Param"),
HelmMod3Code: d.GetString("helmMod3Code", idx), ShieldMod1Min: d.Number("shieldMod1Min"),
HelmMod3Param: d.GetNumber("helmMod3Param", idx), ShieldMod1Max: d.Number("shieldMod1Max"),
HelmMod3Min: d.GetNumber("helmMod3Min", idx), ShieldMod2Code: d.String("shieldMod2Code"),
HelmMod3Max: d.GetNumber("helmMod3Max", idx), ShieldMod2Param: d.Number("shieldMod2Param"),
ShieldMod1Code: d.GetString("shieldMod1Code", idx), ShieldMod2Min: d.Number("shieldMod2Min"),
ShieldMod1Param: d.GetNumber("shieldMod1Param", idx), ShieldMod2Max: d.Number("shieldMod2Max"),
ShieldMod1Min: d.GetNumber("shieldMod1Min", idx), ShieldMod3Code: d.String("shieldMod3Code"),
ShieldMod1Max: d.GetNumber("shieldMod1Max", idx), ShieldMod3Param: d.Number("shieldMod3Param"),
ShieldMod2Code: d.GetString("shieldMod2Code", idx), ShieldMod3Min: d.Number("shieldMod3Min"),
ShieldMod2Param: d.GetNumber("shieldMod2Param", idx), ShieldMod3Max: d.Number("shieldMod3Max"),
ShieldMod2Min: d.GetNumber("shieldMod2Min", idx),
ShieldMod2Max: d.GetNumber("shieldMod2Max", idx),
ShieldMod3Code: d.GetString("shieldMod3Code", idx),
ShieldMod3Param: d.GetNumber("shieldMod3Param", idx),
ShieldMod3Min: d.GetNumber("shieldMod3Min", idx),
ShieldMod3Max: d.GetNumber("shieldMod3Max", idx),
}
Gems[gem.Name] = gem
} }
Gems[gem.Name] = gem
}
if d.Err != nil {
panic(d.Err)
} }
log.Printf("Loaded %d Gems records", len(Gems)) log.Printf("Loaded %d Gems records", len(Gems))

View File

@ -89,87 +89,90 @@ var Hirelings []*HirelingRecord
// LoadHireling loads hireling data into []*HirelingRecord // LoadHireling loads hireling data into []*HirelingRecord
func LoadHireling(file []byte) { func LoadHireling(file []byte) {
d := d2common.LoadDataDictionary(string(file)) Hirelings = make([]*HirelingRecord, 0)
Hirelings = make([]*HirelingRecord, len(d.Data)) d := d2common.LoadDataDictionary(file)
for d.Next() {
for idx := range d.Data {
hireling := &HirelingRecord{ hireling := &HirelingRecord{
Hireling: d.GetString("Hireling", idx), Hireling: d.String("Hireling"),
SubType: d.GetString("SubType", idx), SubType: d.String("SubType"),
ID: d.GetNumber("Id", idx), ID: d.Number("Id"),
Class: d.GetNumber("Class", idx), Class: d.Number("Class"),
Act: d.GetNumber("Act", idx), Act: d.Number("Act"),
Difficulty: d.GetNumber("Difficulty", idx), Difficulty: d.Number("Difficulty"),
Level: d.GetNumber("Level", idx), Level: d.Number("Level"),
Seller: d.GetNumber("Seller", idx), Seller: d.Number("Seller"),
NameFirst: d.GetString("NameFirst", idx), NameFirst: d.String("NameFirst"),
NameLast: d.GetString("NameLast", idx), NameLast: d.String("NameLast"),
Gold: d.GetNumber("Gold", idx), Gold: d.Number("Gold"),
ExpPerLvl: d.GetNumber("Exp/Lvl", idx), ExpPerLvl: d.Number("Exp/Lvl"),
HP: d.GetNumber("HP", idx), HP: d.Number("HP"),
HPPerLvl: d.GetNumber("HP/Lvl", idx), HPPerLvl: d.Number("HP/Lvl"),
Defense: d.GetNumber("Defense", idx), Defense: d.Number("Defense"),
DefPerLvl: d.GetNumber("Id", idx), DefPerLvl: d.Number("Id"),
Str: d.GetNumber("Str", idx), Str: d.Number("Str"),
StrPerLvl: d.GetNumber("Str/Lvl", idx), StrPerLvl: d.Number("Str/Lvl"),
Dex: d.GetNumber("Dex", idx), Dex: d.Number("Dex"),
DexPerLvl: d.GetNumber("Dex/Lvl", idx), DexPerLvl: d.Number("Dex/Lvl"),
AR: d.GetNumber("AR", idx), AR: d.Number("AR"),
ARPerLvl: d.GetNumber("AR/Lvl", idx), ARPerLvl: d.Number("AR/Lvl"),
Share: d.GetNumber("Share", idx), Share: d.Number("Share"),
DmgMin: d.GetNumber("Dmg-Min", idx), DmgMin: d.Number("Dmg-Min"),
DmgMax: d.GetNumber("Dmg-Max", idx), DmgMax: d.Number("Dmg-Max"),
DmgPerLvl: d.GetNumber("Dmg/Lvl", idx), DmgPerLvl: d.Number("Dmg/Lvl"),
Resist: d.GetNumber("Resist", idx), Resist: d.Number("Resist"),
ResistPerLvl: d.GetNumber("Resist/Lvl", idx), ResistPerLvl: d.Number("Resist/Lvl"),
WType1: d.GetString("WType1", idx), WType1: d.String("WType1"),
WType2: d.GetString("WType2", idx), WType2: d.String("WType2"),
HireDesc: d.GetString("HireDesc", idx), HireDesc: d.String("HireDesc"),
DefaultChance: d.GetNumber("DefaultChance", idx), DefaultChance: d.Number("DefaultChance"),
Skill1: d.GetString("Skill1", idx), Skill1: d.String("Skill1"),
Mode1: d.GetNumber("Mode1", idx), Mode1: d.Number("Mode1"),
Chance1: d.GetNumber("Chance1", idx), Chance1: d.Number("Chance1"),
ChancePerLevel1: d.GetNumber("ChancePerLvl1", idx), ChancePerLevel1: d.Number("ChancePerLvl1"),
Level1: d.GetNumber("Level1", idx), Level1: d.Number("Level1"),
LvlPerLvl1: d.GetNumber("LvlPerLvl1", idx), LvlPerLvl1: d.Number("LvlPerLvl1"),
Skill2: d.GetString("Skill2", idx), Skill2: d.String("Skill2"),
Mode2: d.GetNumber("Mode2", idx), Mode2: d.Number("Mode2"),
Chance2: d.GetNumber("Chance2", idx), Chance2: d.Number("Chance2"),
ChancePerLevel2: d.GetNumber("ChancePerLvl2", idx), ChancePerLevel2: d.Number("ChancePerLvl2"),
Level2: d.GetNumber("Level2", idx), Level2: d.Number("Level2"),
LvlPerLvl2: d.GetNumber("LvlPerLvl2", idx), LvlPerLvl2: d.Number("LvlPerLvl2"),
Skill3: d.GetString("Skill3", idx), Skill3: d.String("Skill3"),
Mode3: d.GetNumber("Mode3", idx), Mode3: d.Number("Mode3"),
Chance3: d.GetNumber("Chance3", idx), Chance3: d.Number("Chance3"),
ChancePerLevel3: d.GetNumber("ChancePerLvl3", idx), ChancePerLevel3: d.Number("ChancePerLvl3"),
Level3: d.GetNumber("Level3", idx), Level3: d.Number("Level3"),
LvlPerLvl3: d.GetNumber("LvlPerLvl3", idx), LvlPerLvl3: d.Number("LvlPerLvl3"),
Skill4: d.GetString("Skill4", idx), Skill4: d.String("Skill4"),
Mode4: d.GetNumber("Mode4", idx), Mode4: d.Number("Mode4"),
Chance4: d.GetNumber("Chance4", idx), Chance4: d.Number("Chance4"),
ChancePerLevel4: d.GetNumber("ChancePerLvl4", idx), ChancePerLevel4: d.Number("ChancePerLvl4"),
Level4: d.GetNumber("Level4", idx), Level4: d.Number("Level4"),
LvlPerLvl4: d.GetNumber("LvlPerLvl4", idx), LvlPerLvl4: d.Number("LvlPerLvl4"),
Skill5: d.GetString("Skill5", idx), Skill5: d.String("Skill5"),
Mode5: d.GetNumber("Mode5", idx), Mode5: d.Number("Mode5"),
Chance5: d.GetNumber("Chance5", idx), Chance5: d.Number("Chance5"),
ChancePerLevel5: d.GetNumber("ChancePerLvl5", idx), ChancePerLevel5: d.Number("ChancePerLvl5"),
Level5: d.GetNumber("Level5", idx), Level5: d.Number("Level5"),
LvlPerLvl5: d.GetNumber("LvlPerLvl5", idx), LvlPerLvl5: d.Number("LvlPerLvl5"),
Skill6: d.GetString("Skill6", idx), Skill6: d.String("Skill6"),
Mode6: d.GetNumber("Mode6", idx), Mode6: d.Number("Mode6"),
Chance6: d.GetNumber("Chance6", idx), Chance6: d.Number("Chance6"),
ChancePerLevel6: d.GetNumber("ChancePerLvl6", idx), ChancePerLevel6: d.Number("ChancePerLvl6"),
Level6: d.GetNumber("Level6", idx), Level6: d.Number("Level6"),
LvlPerLvl6: d.GetNumber("LvlPerLvl6", idx), LvlPerLvl6: d.Number("LvlPerLvl6"),
Head: d.GetNumber("Head", idx), Head: d.Number("Head"),
Torso: d.GetNumber("Torso", idx), Torso: d.Number("Torso"),
Weapon: d.GetNumber("Weapon", idx), Weapon: d.Number("Weapon"),
Shield: d.GetNumber("Shield", idx), Shield: d.Number("Shield"),
} }
Hirelings = append(Hirelings, hireling) Hirelings = append(Hirelings, hireling)
} }
if d.Err != nil {
panic(d.Err)
}
log.Printf("Loaded %d Hireling records", len(Hirelings)) log.Printf("Loaded %d Hireling records", len(Hirelings))
} }

View File

@ -8,8 +8,9 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
) )
// MagicPrefix + MagicSuffix store item affix records // MagicPrefix stores all of the magic prefix records
var MagicPrefix []*ItemAffixCommonRecord //nolint:gochecknoglobals // Currently global by design var MagicPrefix []*ItemAffixCommonRecord //nolint:gochecknoglobals // Currently global by design
// MagicSuffix stores all of the magic suffix records
var MagicSuffix []*ItemAffixCommonRecord //nolint:gochecknoglobals // Currently global by design var MagicSuffix []*ItemAffixCommonRecord //nolint:gochecknoglobals // Currently global by design
// LoadMagicPrefix loads MagicPrefix.txt // LoadMagicPrefix loads MagicPrefix.txt
@ -47,46 +48,38 @@ func getAffixString(t1 d2enum.ItemAffixSuperType, t2 d2enum.ItemAffixSubType) st
return name return name
} }
func loadDictionary( func loadDictionary(file []byte, superType d2enum.ItemAffixSuperType, subType d2enum.ItemAffixSubType) []*ItemAffixCommonRecord {
file []byte, d := d2common.LoadDataDictionary(file)
superType d2enum.ItemAffixSuperType, records := createItemAffixRecords(d, superType, subType)
subType d2enum.ItemAffixSubType,
) []*ItemAffixCommonRecord {
dict := d2common.LoadDataDictionary(string(file))
records := createItemAffixRecords(dict, superType, subType)
name := getAffixString(superType, subType) name := getAffixString(superType, subType)
log.Printf("Loaded %d %s records", len(dict.Data), name) log.Printf("Loaded %d %s records", len(records), name)
return records return records
} }
func createItemAffixRecords( func createItemAffixRecords(d *d2common.DataDictionary, superType d2enum.ItemAffixSuperType, subType d2enum.ItemAffixSubType) []*ItemAffixCommonRecord {
d *d2common.DataDictionary,
superType d2enum.ItemAffixSuperType,
subType d2enum.ItemAffixSubType,
) []*ItemAffixCommonRecord {
records := make([]*ItemAffixCommonRecord, 0) records := make([]*ItemAffixCommonRecord, 0)
for index := range d.Data { for d.Next() {
affix := &ItemAffixCommonRecord{ affix := &ItemAffixCommonRecord{
Name: d.GetString("Name", index), Name: d.String("Name"),
Version: d.GetNumber("version", index), Version: d.Number("version"),
Type: subType, Type: subType,
IsPrefix: superType == d2enum.ItemAffixPrefix, IsPrefix: superType == d2enum.ItemAffixPrefix,
IsSuffix: superType == d2enum.ItemAffixSuffix, IsSuffix: superType == d2enum.ItemAffixSuffix,
Spawnable: d.GetNumber("spawnable", index) == 1, Spawnable: d.Bool("spawnable"),
Rare: d.GetNumber("rare", index) == 1, Rare: d.Bool("rare"),
Level: d.GetNumber("level", index), Level: d.Number("level"),
MaxLevel: d.GetNumber("maxlevel", index), MaxLevel: d.Number("maxlevel"),
LevelReq: d.GetNumber("levelreq", index), LevelReq: d.Number("levelreq"),
Class: d.GetString("classspecific", index), Class: d.String("classspecific"),
ClassLevelReq: d.GetNumber("classlevelreq", index), ClassLevelReq: d.Number("classlevelreq"),
Frequency: d.GetNumber("frequency", index), Frequency: d.Number("frequency"),
GroupID: d.GetNumber("group", index), GroupID: d.Number("group"),
Transform: d.GetNumber("transform", index) == 1, Transform: d.Bool("transform"),
TransformColor: d.GetString("transformcolor", index), TransformColor: d.String("transformcolor"),
PriceAdd: d.GetNumber("add", index), PriceAdd: d.Number("add"),
PriceScale: d.GetNumber("multiply", index), PriceScale: d.Number("multiply"),
} }
// modifiers (Property references with parameters to be eval'd) // modifiers (Property references with parameters to be eval'd)
@ -96,10 +89,10 @@ func createItemAffixRecords(
minKey := fmt.Sprintf("mod%dmin", i) minKey := fmt.Sprintf("mod%dmin", i)
maxKey := fmt.Sprintf("mod%dmax", i) maxKey := fmt.Sprintf("mod%dmax", i)
modifier := &ItemAffixCommonModifier{ modifier := &ItemAffixCommonModifier{
Code: d.GetString(codeKey, index), Code: d.String(codeKey),
Parameter: d.GetNumber(paramKey, index), Parameter: d.Number(paramKey),
Min: d.GetNumber(minKey, index), Min: d.Number(minKey),
Max: d.GetNumber(maxKey, index), Max: d.Number(maxKey),
} }
affix.Modifiers = append(affix.Modifiers, modifier) affix.Modifiers = append(affix.Modifiers, modifier)
} }
@ -107,14 +100,14 @@ func createItemAffixRecords(
// items to include for spawning // items to include for spawning
for i := 1; i <= 7; i++ { for i := 1; i <= 7; i++ {
itemKey := fmt.Sprintf("itype%d", i) itemKey := fmt.Sprintf("itype%d", i)
itemToken := d.GetString(itemKey, index) itemToken := d.String(itemKey)
affix.ItemInclude = append(affix.ItemInclude, itemToken) affix.ItemInclude = append(affix.ItemInclude, itemToken)
} }
// items to exclude for spawning // items to exclude for spawning
for i := 1; i <= 7; i++ { for i := 1; i <= 7; i++ {
itemKey := fmt.Sprintf("etype%d", i) itemKey := fmt.Sprintf("etype%d", i)
itemToken := d.GetString(itemKey, index) itemToken := d.String(itemKey)
affix.ItemExclude = append(affix.ItemExclude, itemToken) affix.ItemExclude = append(affix.ItemExclude, itemToken)
} }
@ -134,6 +127,9 @@ func createItemAffixRecords(
records = append(records, affix) records = append(records, affix)
} }
if d.Err != nil {
panic(d.Err)
}
return records return records
} }

View File

@ -109,81 +109,83 @@ var ItemStatCosts map[string]*ItemStatCostRecord
// LoadItemStatCosts loads ItemStatCostRecord's from text // LoadItemStatCosts loads ItemStatCostRecord's from text
func LoadItemStatCosts(file []byte) { func LoadItemStatCosts(file []byte) {
d := d2common.LoadDataDictionary(string(file)) ItemStatCosts = make(map[string]*ItemStatCostRecord)
numRecords := len(d.Data)
ItemStatCosts = make(map[string]*ItemStatCostRecord, numRecords)
for idx := range d.Data { d := d2common.LoadDataDictionary(file)
for d.Next() {
record := &ItemStatCostRecord{ record := &ItemStatCostRecord{
Name: d.GetString("Stat", idx), Name: d.String("Stat"),
Index: d.GetNumber("ID", idx), Index: d.Number("ID"),
Signed: d.GetNumber("Signed", idx) > 0, Signed: d.Number("Signed") > 0,
KeepZero: d.GetNumber("keepzero", idx) > 0, KeepZero: d.Number("keepzero") > 0,
// Ranged: d.GetNumber("Ranged", idx) > 0, // Ranged: d.Number("Ranged") > 0,
MinAccr: d.GetNumber("MinAccr", idx), MinAccr: d.Number("MinAccr"),
UpdateAnimRate: d.GetNumber("UpdateAnimRate", idx) > 0, UpdateAnimRate: d.Number("UpdateAnimRate") > 0,
SendOther: d.GetNumber("Send Other", idx) > 0, SendOther: d.Number("Send Other") > 0,
SendBits: d.GetNumber("Send Bits", idx), SendBits: d.Number("Send Bits"),
SendParam: d.GetNumber("Send Param Bits", idx), SendParam: d.Number("Send Param Bits"),
Saved: d.GetNumber("CSvBits", idx) > 0, Saved: d.Number("CSvBits") > 0,
SavedSigned: d.GetNumber("CSvSigned", idx) > 0, SavedSigned: d.Number("CSvSigned") > 0,
SavedBits: d.GetNumber("CSvBits", idx), SavedBits: d.Number("CSvBits"),
SaveBits: d.GetNumber("Save Bits", idx), SaveBits: d.Number("Save Bits"),
SaveAdd: d.GetNumber("Save Add", idx), SaveAdd: d.Number("Save Add"),
SaveParamBits: d.GetNumber("Save Param Bits", idx), SaveParamBits: d.Number("Save Param Bits"),
Encode: d2enum.EncodingType(d.GetNumber("Encode", idx)), Encode: d2enum.EncodingType(d.Number("Encode")),
CallbackEnabled: d.GetNumber("fCallback", idx) > 0, CallbackEnabled: d.Number("fCallback") > 0,
CostAdd: d.GetNumber("Add", idx), CostAdd: d.Number("Add"),
CostMultiply: d.GetNumber("Multiply", idx), CostMultiply: d.Number("Multiply"),
ValShift: d.GetNumber("ValShift", idx), ValShift: d.Number("ValShift"),
OperatorType: d2enum.OperatorType(d.GetNumber("op", idx)), OperatorType: d2enum.OperatorType(d.Number("op")),
OpParam: d.GetNumber("op param", idx), OpParam: d.Number("op param"),
OpBase: d.GetString("op base", idx), OpBase: d.String("op base"),
OpStat1: d.GetString("op stat1", idx), OpStat1: d.String("op stat1"),
OpStat2: d.GetString("op stat2", idx), OpStat2: d.String("op stat2"),
OpStat3: d.GetString("op stat3", idx), OpStat3: d.String("op stat3"),
Direct: d.GetNumber("direct", idx) > 0, Direct: d.Number("direct") > 0,
MaxStat: d.GetString("maxstat", idx), MaxStat: d.String("maxstat"),
ItemSpecific: d.GetNumber("itemspecific", idx) > 0, ItemSpecific: d.Number("itemspecific") > 0,
DamageRelated: d.GetNumber("damagerelated", idx) > 0, DamageRelated: d.Number("damagerelated") > 0,
EventID1: d2enum.GetItemEventType(d.GetString("itemevent1", idx)), EventID1: d2enum.GetItemEventType(d.String("itemevent1")),
EventID2: d2enum.GetItemEventType(d.GetString("itemevent2", idx)), EventID2: d2enum.GetItemEventType(d.String("itemevent2")),
EventFuncID1: d2enum.GetItemEventFuncID(d.GetNumber("itemeventfunc1", idx)), EventFuncID1: d2enum.GetItemEventFuncID(d.Number("itemeventfunc1")),
EventFuncID2: d2enum.GetItemEventFuncID(d.GetNumber("itemeventfunc2", idx)), EventFuncID2: d2enum.GetItemEventFuncID(d.Number("itemeventfunc2")),
DescPriority: d.GetNumber("descpriority", idx), DescPriority: d.Number("descpriority"),
DescFnID: d2enum.DescFuncID(d.GetNumber("descfunc", idx)), DescFnID: d2enum.DescFuncID(d.Number("descfunc")),
DescFn: d2enum.GetDescFunction(d2enum.DescFuncID(d.GetNumber("descfunc", idx))), DescFn: d2enum.GetDescFunction(d2enum.DescFuncID(d.Number("descfunc"))),
DescVal: d.GetNumber("descval", idx), DescVal: d.Number("descval"),
DescStrPos: d.GetString("descstrpos", idx), DescStrPos: d.String("descstrpos"),
DescStrNeg: d.GetString("descstrneg", idx), DescStrNeg: d.String("descstrneg"),
DescStr2: d.GetString("descstr2", idx), DescStr2: d.String("descstr2"),
DescGroup: d.GetNumber("dgrp", idx), DescGroup: d.Number("dgrp"),
DescGroupFuncID: d2enum.DescFuncID(d.GetNumber("dgrpfunc", idx)), DescGroupFuncID: d2enum.DescFuncID(d.Number("dgrpfunc")),
DescGroupFn: d2enum.GetDescFunction(d2enum.DescFuncID(d.GetNumber("dgrpfunc", idx))), DescGroupFn: d2enum.GetDescFunction(d2enum.DescFuncID(d.Number("dgrpfunc"))),
DescGroupVal: d.GetNumber("dgrpval", idx), DescGroupVal: d.Number("dgrpval"),
DescGroupStrPos: d.GetString("dgrpstrpos", idx), DescGroupStrPos: d.String("dgrpstrpos"),
DescGroupStrNeg: d.GetString("dgrpstrneg", idx), DescGroupStrNeg: d.String("dgrpstrneg"),
DescGroupStr2: d.GetString("dgrpstr2", idx), DescGroupStr2: d.String("dgrpstr2"),
Stuff: d.GetString("stuff", idx), Stuff: d.String("stuff"),
} }
ItemStatCosts[record.Name] = record ItemStatCosts[record.Name] = record
} }
if d.Err != nil {
panic(d.Err)
}
log.Printf("Loaded %d ItemStatCost records", len(ItemStatCosts)) log.Printf("Loaded %d ItemStatCost records", len(ItemStatCosts))
} }

View File

@ -41,22 +41,25 @@ var LevelMazeDetails map[int]*LevelMazeDetailsRecord //nolint:gochecknoglobals /
// LoadLevelMazeDetails loads LevelMazeDetailsRecords from text file // LoadLevelMazeDetails loads LevelMazeDetailsRecords from text file
func LoadLevelMazeDetails(file []byte) { func LoadLevelMazeDetails(file []byte) {
dict := d2common.LoadDataDictionary(string(file)) LevelMazeDetails = make(map[int]*LevelMazeDetailsRecord)
numRecords := len(dict.Data)
LevelMazeDetails = make(map[int]*LevelMazeDetailsRecord, numRecords)
for idx := range dict.Data { d := d2common.LoadDataDictionary(file)
for d.Next() {
record := &LevelMazeDetailsRecord{ record := &LevelMazeDetailsRecord{
Name: dict.GetString("Name", idx), Name: d.String("Name"),
LevelID: dict.GetNumber("Level", idx), LevelID: d.Number("Level"),
NumRoomsNormal: dict.GetNumber("Rooms", idx), NumRoomsNormal: d.Number("Rooms"),
NumRoomsNightmare: dict.GetNumber("Rooms(N)", idx), NumRoomsNightmare: d.Number("Rooms(N)"),
NumRoomsHell: dict.GetNumber("Rooms(H)", idx), NumRoomsHell: d.Number("Rooms(H)"),
SizeX: dict.GetNumber("SizeX", idx), SizeX: d.Number("SizeX"),
SizeY: dict.GetNumber("SizeY", idx), SizeY: d.Number("SizeY"),
} }
LevelMazeDetails[record.LevelID] = record LevelMazeDetails[record.LevelID] = record
} }
if d.Err != nil {
panic(d.Err)
}
log.Printf("Loaded %d LevelMazeDetails records", len(LevelMazeDetails)) log.Printf("Loaded %d LevelMazeDetails records", len(LevelMazeDetails))
} }

View File

@ -70,38 +70,40 @@ var LevelSubstitutions map[int]*LevelSubstitutionRecord
// LoadLevelSubstitutions loads lvlsub.txt and parses into records // LoadLevelSubstitutions loads lvlsub.txt and parses into records
func LoadLevelSubstitutions(file []byte) { func LoadLevelSubstitutions(file []byte) {
dict := d2common.LoadDataDictionary(string(file)) LevelSubstitutions = make(map[int]*LevelSubstitutionRecord)
numRecords := len(dict.Data)
LevelSubstitutions = make(map[int]*LevelSubstitutionRecord, numRecords)
for idx := range dict.Data { d := d2common.LoadDataDictionary(file)
for d.Next() {
record := &LevelSubstitutionRecord{ record := &LevelSubstitutionRecord{
Name: dict.GetString("Name", idx), Name: d.String("Name"),
ID: dict.GetNumber("Type", idx), ID: d.Number("Type"),
File: dict.GetString("File", idx), File: d.String("File"),
IsExpansion: dict.GetNumber("Expansion", idx) > 0, IsExpansion: d.Number("Expansion") > 0,
BorderType: dict.GetNumber("BordType", idx), BorderType: d.Number("BordType"),
GridSize: dict.GetNumber("GridSize", idx), GridSize: d.Number("GridSize"),
Mask: dict.GetNumber("Dt1Mask", idx), Mask: d.Number("Dt1Mask"),
ChanceSpawn0: dict.GetNumber("Prob0", idx), ChanceSpawn0: d.Number("Prob0"),
ChanceSpawn1: dict.GetNumber("Prob1", idx), ChanceSpawn1: d.Number("Prob1"),
ChanceSpawn2: dict.GetNumber("Prob2", idx), ChanceSpawn2: d.Number("Prob2"),
ChanceSpawn3: dict.GetNumber("Prob3", idx), ChanceSpawn3: d.Number("Prob3"),
ChanceSpawn4: dict.GetNumber("Prob4", idx), ChanceSpawn4: d.Number("Prob4"),
ChanceFloor0: dict.GetNumber("Trials0", idx), ChanceFloor0: d.Number("Trials0"),
ChanceFloor1: dict.GetNumber("Trials1", idx), ChanceFloor1: d.Number("Trials1"),
ChanceFloor2: dict.GetNumber("Trials2", idx), ChanceFloor2: d.Number("Trials2"),
ChanceFloor3: dict.GetNumber("Trials3", idx), ChanceFloor3: d.Number("Trials3"),
ChanceFloor4: dict.GetNumber("Trials4", idx), ChanceFloor4: d.Number("Trials4"),
GridMax0: dict.GetNumber("Max0", idx), GridMax0: d.Number("Max0"),
GridMax1: dict.GetNumber("Max1", idx), GridMax1: d.Number("Max1"),
GridMax2: dict.GetNumber("Max2", idx), GridMax2: d.Number("Max2"),
GridMax3: dict.GetNumber("Max3", idx), GridMax3: d.Number("Max3"),
GridMax4: dict.GetNumber("Max4", idx), GridMax4: d.Number("Max4"),
} }
LevelSubstitutions[record.ID] = record LevelSubstitutions[record.ID] = record
} }
if d.Err != nil {
panic(d.Err)
}
log.Printf("Loaded %d LevelSubstitution records", len(LevelSubstitutions)) log.Printf("Loaded %d LevelSubstitution records", len(LevelSubstitutions))
} }

View File

@ -9,17 +9,18 @@ import (
// LevelWarpRecord is a representation of a row from lvlwarp.txt // LevelWarpRecord is a representation of a row from lvlwarp.txt
// it describes the warp graphics offsets and dimensions for levels // it describes the warp graphics offsets and dimensions for levels
type LevelWarpRecord struct { type LevelWarpRecord struct {
ID int32 Name string
SelectX int32 ID int
SelectY int32 SelectX int
SelectDX int32 SelectY int
SelectDY int32 SelectDX int
ExitWalkX int32 SelectDY int
ExitWalkY int32 ExitWalkX int
OffsetX int32 ExitWalkY int
OffsetY int32 OffsetX int
OffsetY int
LitVersion bool LitVersion bool
Tiles int32 Tiles int
Direction string Direction string
} }
@ -28,27 +29,27 @@ type LevelWarpRecord struct {
var LevelWarps map[int]*LevelWarpRecord var LevelWarps map[int]*LevelWarpRecord
// LoadLevelWarps loads LevelWarpRecord's from text file data // LoadLevelWarps loads LevelWarpRecord's from text file data
func LoadLevelWarps(levelWarpData []byte) { func LoadLevelWarps(file []byte) {
LevelWarps = make(map[int]*LevelWarpRecord) LevelWarps = make(map[int]*LevelWarpRecord)
streamReader := d2common.CreateStreamReader(levelWarpData)
numRecords := int(streamReader.GetInt32())
for i := 0; i < numRecords; i++ { d := d2common.LoadDataDictionary(file)
id := int(streamReader.GetInt32()) for d.Next() {
LevelWarps[id] = &LevelWarpRecord{} record := &LevelWarpRecord{
LevelWarps[id].ID = int32(id) Name: d.String("Name"),
LevelWarps[id].SelectX = streamReader.GetInt32() ID: d.Number("Id"),
LevelWarps[id].SelectY = streamReader.GetInt32() SelectX: d.Number("SelectX"),
LevelWarps[id].SelectDX = streamReader.GetInt32() SelectY: d.Number("SelectY"),
LevelWarps[id].SelectDY = streamReader.GetInt32() SelectDX: d.Number("SelectDX"),
LevelWarps[id].ExitWalkX = streamReader.GetInt32() SelectDY: d.Number("SelectDY"),
LevelWarps[id].ExitWalkY = streamReader.GetInt32() ExitWalkX: d.Number("ExitWalkX"),
LevelWarps[id].OffsetX = streamReader.GetInt32() ExitWalkY: d.Number("ExitWalkY"),
LevelWarps[id].OffsetY = streamReader.GetInt32() OffsetX: d.Number("OffsetX"),
LevelWarps[id].LitVersion = streamReader.GetInt32() == 1 OffsetY: d.Number("OffsetY"),
LevelWarps[id].Tiles = streamReader.GetInt32() LitVersion: d.Bool("LitVersion"),
LevelWarps[id].Direction = string(streamReader.GetByte()) Tiles: d.Number("Tiles"),
streamReader.SkipBytes(3) Direction: d.String("Direction"),
}
LevelWarps[record.ID] = record
} }
log.Printf("Loaded %d level warps", len(LevelWarps)) log.Printf("Loaded %d level warps", len(LevelWarps))

View File

@ -95,7 +95,7 @@ type LevelDetailsRecord struct {
// Id // Id
// Level ID (used in columns like VIS0-7) // Level ID (used in columns like VIS0-7)
Id int //nolint:golint Id is the right key Id int //nolint:golint,stylecheck // Id is the right key
// Palette is the Act Palette . Reference only // Palette is the Act Palette . Reference only
Palette int // Pal Palette int // Pal
@ -381,159 +381,162 @@ func GetLevelDetails(id int) *LevelDetailsRecord {
// LoadLevelDetails loads level details records from levels.txt // LoadLevelDetails loads level details records from levels.txt
//nolint:funlen // Txt loader, makes no sense to split //nolint:funlen // Txt loader, makes no sense to split
func LoadLevelDetails(file []byte) { func LoadLevelDetails(file []byte) {
dict := d2common.LoadDataDictionary(string(file)) LevelDetails = make(map[int]*LevelDetailsRecord)
numRecords := len(dict.Data)
LevelDetails = make(map[int]*LevelDetailsRecord, numRecords)
for idx := range dict.Data { d := d2common.LoadDataDictionary(file)
for d.Next() {
record := &LevelDetailsRecord{ record := &LevelDetailsRecord{
Name: dict.GetString("Name ", idx), Name: d.String("Name "),
Id: dict.GetNumber("Id", idx), Id: d.Number("Id"),
Palette: dict.GetNumber("Pal", idx), Palette: d.Number("Pal"),
Act: dict.GetNumber("Act", idx), Act: d.Number("Act"),
QuestFlag: dict.GetNumber("QuestFlag", idx), QuestFlag: d.Number("QuestFlag"),
QuestFlagExpansion: dict.GetNumber("QuestFlagEx", idx), QuestFlagExpansion: d.Number("QuestFlagEx"),
AutomapIndex: dict.GetNumber("Layer", idx), AutomapIndex: d.Number("Layer"),
SizeXNormal: dict.GetNumber("SizeX", idx), SizeXNormal: d.Number("SizeX"),
SizeYNormal: dict.GetNumber("SizeY", idx), SizeYNormal: d.Number("SizeY"),
SizeXNightmare: dict.GetNumber("SizeX(N)", idx), SizeXNightmare: d.Number("SizeX(N)"),
SizeYNightmare: dict.GetNumber("SizeY(N)", idx), SizeYNightmare: d.Number("SizeY(N)"),
SizeXHell: dict.GetNumber("SizeX(H)", idx), SizeXHell: d.Number("SizeX(H)"),
SizeYHell: dict.GetNumber("SizeY(H)", idx), SizeYHell: d.Number("SizeY(H)"),
WorldOffsetX: dict.GetNumber("OffsetX", idx), WorldOffsetX: d.Number("OffsetX"),
WorldOffsetY: dict.GetNumber("OffsetY", idx), WorldOffsetY: d.Number("OffsetY"),
DependantLevelID: dict.GetNumber("Depend", idx), DependantLevelID: d.Number("Depend"),
TeleportFlag: d2enum.TeleportFlag(dict.GetNumber("Teleport", idx)), TeleportFlag: d2enum.TeleportFlag(d.Number("Teleport")),
EnableRain: dict.GetNumber("Rain", idx) > 0, EnableRain: d.Number("Rain") > 0,
EnableMud: dict.GetNumber("Mud", idx) > 0, EnableMud: d.Number("Mud") > 0,
EnablePerspective: dict.GetNumber("NoPer", idx) > 0, EnablePerspective: d.Number("NoPer") > 0,
EnableLineOfSightDraw: dict.GetNumber("LOSDraw", idx) > 0, EnableLineOfSightDraw: d.Number("LOSDraw") > 0,
EnableFloorFliter: dict.GetNumber("FloorFilter", idx) > 0, EnableFloorFliter: d.Number("FloorFilter") > 0,
EnableBlankScreen: dict.GetNumber("BlankScreen", idx) > 0, EnableBlankScreen: d.Number("BlankScreen") > 0,
EnableDrawEdges: dict.GetNumber("DrawEdges", idx) > 0, EnableDrawEdges: d.Number("DrawEdges") > 0,
IsInside: dict.GetNumber("IsInside", idx) > 0, IsInside: d.Number("IsInside") > 0,
LevelGenerationType: d2enum.LevelGenerationType(dict.GetNumber("DrlgType", idx)), LevelGenerationType: d2enum.LevelGenerationType(d.Number("DrlgType")),
LevelType: dict.GetNumber("LevelType", idx), LevelType: d.Number("LevelType"),
SubType: dict.GetNumber("SubType", idx), SubType: d.Number("SubType"),
SubTheme: dict.GetNumber("SubTheme", idx), SubTheme: d.Number("SubTheme"),
SubWaypoint: dict.GetNumber("SubWaypoint", idx), SubWaypoint: d.Number("SubWaypoint"),
SubShrine: dict.GetNumber("SubShrine", idx), SubShrine: d.Number("SubShrine"),
LevelLinkID0: dict.GetNumber("Vis0", idx), LevelLinkID0: d.Number("Vis0"),
LevelLinkID1: dict.GetNumber("Vis1", idx), LevelLinkID1: d.Number("Vis1"),
LevelLinkID2: dict.GetNumber("Vis2", idx), LevelLinkID2: d.Number("Vis2"),
LevelLinkID3: dict.GetNumber("Vis3", idx), LevelLinkID3: d.Number("Vis3"),
LevelLinkID4: dict.GetNumber("Vis4", idx), LevelLinkID4: d.Number("Vis4"),
LevelLinkID5: dict.GetNumber("Vis5", idx), LevelLinkID5: d.Number("Vis5"),
LevelLinkID6: dict.GetNumber("Vis6", idx), LevelLinkID6: d.Number("Vis6"),
LevelLinkID7: dict.GetNumber("Vis7", idx), LevelLinkID7: d.Number("Vis7"),
WarpGraphicsID0: dict.GetNumber("Warp0", idx), WarpGraphicsID0: d.Number("Warp0"),
WarpGraphicsID1: dict.GetNumber("Warp1", idx), WarpGraphicsID1: d.Number("Warp1"),
WarpGraphicsID2: dict.GetNumber("Warp2", idx), WarpGraphicsID2: d.Number("Warp2"),
WarpGraphicsID3: dict.GetNumber("Warp3", idx), WarpGraphicsID3: d.Number("Warp3"),
WarpGraphicsID4: dict.GetNumber("Warp4", idx), WarpGraphicsID4: d.Number("Warp4"),
WarpGraphicsID5: dict.GetNumber("Warp5", idx), WarpGraphicsID5: d.Number("Warp5"),
WarpGraphicsID6: dict.GetNumber("Warp6", idx), WarpGraphicsID6: d.Number("Warp6"),
WarpGraphicsID7: dict.GetNumber("Warp7", idx), WarpGraphicsID7: d.Number("Warp7"),
LightIntensity: dict.GetNumber("Intensity", idx), LightIntensity: d.Number("Intensity"),
Red: dict.GetNumber("Red", idx), Red: d.Number("Red"),
Green: dict.GetNumber("Green", idx), Green: d.Number("Green"),
Blue: dict.GetNumber("Blue", idx), Blue: d.Number("Blue"),
PortalEnable: dict.GetNumber("Portal", idx) > 0, PortalEnable: d.Number("Portal") > 0,
PortalRepositionEnable: dict.GetNumber("Position", idx) > 0, PortalRepositionEnable: d.Number("Position") > 0,
SaveMonsterStates: dict.GetNumber("SaveMonsters", idx) > 0, SaveMonsterStates: d.Number("SaveMonsters") > 0,
SaveMerchantStates: dict.GetNumber("SaveMonsters", idx) > 0, SaveMerchantStates: d.Number("SaveMonsters") > 0,
QuestID: dict.GetNumber("Quest", idx), QuestID: d.Number("Quest"),
WarpClearanceDistance: dict.GetNumber("WarpDist", idx), WarpClearanceDistance: d.Number("WarpDist"),
MonsterLevelNormal: dict.GetNumber("MonLvl1", idx), MonsterLevelNormal: d.Number("MonLvl1"),
MonsterLevelNightmare: dict.GetNumber("MonLvl2", idx), MonsterLevelNightmare: d.Number("MonLvl2"),
MonsterLevelHell: dict.GetNumber("MonLvl3", idx), MonsterLevelHell: d.Number("MonLvl3"),
MonsterLevelNormalEx: dict.GetNumber("MonLvl1Ex", idx), MonsterLevelNormalEx: d.Number("MonLvl1Ex"),
MonsterLevelNightmareEx: dict.GetNumber("MonLvl2Ex", idx), MonsterLevelNightmareEx: d.Number("MonLvl2Ex"),
MonsterLevelHellEx: dict.GetNumber("MonLvl3Ex", idx), MonsterLevelHellEx: d.Number("MonLvl3Ex"),
MonsterDensityNormal: dict.GetNumber("MonDen", idx), MonsterDensityNormal: d.Number("MonDen"),
MonsterDensityNightmare: dict.GetNumber("MonDen(N)", idx), MonsterDensityNightmare: d.Number("MonDen(N)"),
MonsterDensityHell: dict.GetNumber("MonDen(H)", idx), MonsterDensityHell: d.Number("MonDen(H)"),
MonsterUniqueMinNormal: dict.GetNumber("MonUMin", idx), MonsterUniqueMinNormal: d.Number("MonUMin"),
MonsterUniqueMinNightmare: dict.GetNumber("MonUMin(N)", idx), MonsterUniqueMinNightmare: d.Number("MonUMin(N)"),
MonsterUniqueMinHell: dict.GetNumber("MonUMin(H)", idx), MonsterUniqueMinHell: d.Number("MonUMin(H)"),
MonsterUniqueMaxNormal: dict.GetNumber("MonUMax", idx), MonsterUniqueMaxNormal: d.Number("MonUMax"),
MonsterUniqueMaxNightmare: dict.GetNumber("MonUMax(N)", idx), MonsterUniqueMaxNightmare: d.Number("MonUMax(N)"),
MonsterUniqueMaxHell: dict.GetNumber("MonUMax(H)", idx), MonsterUniqueMaxHell: d.Number("MonUMax(H)"),
MonsterWanderEnable: dict.GetNumber("MonWndr", idx) > 0, MonsterWanderEnable: d.Number("MonWndr") > 0,
MonsterSpecialWalk: dict.GetNumber("MonSpcWalk", idx) > 0, MonsterSpecialWalk: d.Number("MonSpcWalk") > 0,
NumMonsterTypes: dict.GetNumber("NumMon", idx), NumMonsterTypes: d.Number("NumMon"),
MonsterID1Normal: dict.GetString("mon1", idx), MonsterID1Normal: d.String("mon1"),
MonsterID2Normal: dict.GetString("mon2", idx), MonsterID2Normal: d.String("mon2"),
MonsterID3Normal: dict.GetString("mon3", idx), MonsterID3Normal: d.String("mon3"),
MonsterID4Normal: dict.GetString("mon4", idx), MonsterID4Normal: d.String("mon4"),
MonsterID5Normal: dict.GetString("mon5", idx), MonsterID5Normal: d.String("mon5"),
MonsterID6Normal: dict.GetString("mon6", idx), MonsterID6Normal: d.String("mon6"),
MonsterID7Normal: dict.GetString("mon7", idx), MonsterID7Normal: d.String("mon7"),
MonsterID8Normal: dict.GetString("mon8", idx), MonsterID8Normal: d.String("mon8"),
MonsterID9Normal: dict.GetString("mon9", idx), MonsterID9Normal: d.String("mon9"),
MonsterID10Normal: dict.GetString("mon10", idx), MonsterID10Normal: d.String("mon10"),
MonsterID1Nightmare: dict.GetString("nmon1", idx), MonsterID1Nightmare: d.String("nmon1"),
MonsterID2Nightmare: dict.GetString("nmon2", idx), MonsterID2Nightmare: d.String("nmon2"),
MonsterID3Nightmare: dict.GetString("nmon3", idx), MonsterID3Nightmare: d.String("nmon3"),
MonsterID4Nightmare: dict.GetString("nmon4", idx), MonsterID4Nightmare: d.String("nmon4"),
MonsterID5Nightmare: dict.GetString("nmon5", idx), MonsterID5Nightmare: d.String("nmon5"),
MonsterID6Nightmare: dict.GetString("nmon6", idx), MonsterID6Nightmare: d.String("nmon6"),
MonsterID7Nightmare: dict.GetString("nmon7", idx), MonsterID7Nightmare: d.String("nmon7"),
MonsterID8Nightmare: dict.GetString("nmon8", idx), MonsterID8Nightmare: d.String("nmon8"),
MonsterID9Nightmare: dict.GetString("nmon9", idx), MonsterID9Nightmare: d.String("nmon9"),
MonsterID10Nightmare: dict.GetString("nmon10", idx), MonsterID10Nightmare: d.String("nmon10"),
MonsterID1Hell: dict.GetString("nmon1", idx), MonsterID1Hell: d.String("nmon1"),
MonsterID2Hell: dict.GetString("nmon2", idx), MonsterID2Hell: d.String("nmon2"),
MonsterID3Hell: dict.GetString("nmon3", idx), MonsterID3Hell: d.String("nmon3"),
MonsterID4Hell: dict.GetString("nmon4", idx), MonsterID4Hell: d.String("nmon4"),
MonsterID5Hell: dict.GetString("nmon5", idx), MonsterID5Hell: d.String("nmon5"),
MonsterID6Hell: dict.GetString("nmon6", idx), MonsterID6Hell: d.String("nmon6"),
MonsterID7Hell: dict.GetString("nmon7", idx), MonsterID7Hell: d.String("nmon7"),
MonsterID8Hell: dict.GetString("nmon8", idx), MonsterID8Hell: d.String("nmon8"),
MonsterID9Hell: dict.GetString("nmon9", idx), MonsterID9Hell: d.String("nmon9"),
MonsterID10Hell: dict.GetString("nmon10", idx), MonsterID10Hell: d.String("nmon10"),
MonsterPreferRanged: dict.GetNumber("rangedspawn", idx) > 0, MonsterPreferRanged: d.Number("rangedspawn") > 0,
MonsterUniqueID1: dict.GetString("umon1", idx), MonsterUniqueID1: d.String("umon1"),
MonsterUniqueID2: dict.GetString("umon2", idx), MonsterUniqueID2: d.String("umon2"),
MonsterUniqueID3: dict.GetString("umon3", idx), MonsterUniqueID3: d.String("umon3"),
MonsterUniqueID4: dict.GetString("umon4", idx), MonsterUniqueID4: d.String("umon4"),
MonsterUniqueID5: dict.GetString("umon5", idx), MonsterUniqueID5: d.String("umon5"),
MonsterUniqueID6: dict.GetString("umon6", idx), MonsterUniqueID6: d.String("umon6"),
MonsterUniqueID7: dict.GetString("umon7", idx), MonsterUniqueID7: d.String("umon7"),
MonsterUniqueID8: dict.GetString("umon8", idx), MonsterUniqueID8: d.String("umon8"),
MonsterUniqueID9: dict.GetString("umon9", idx), MonsterUniqueID9: d.String("umon9"),
MonsterUniqueID10: dict.GetString("umon10", idx), MonsterUniqueID10: d.String("umon10"),
MonsterCritterID1: dict.GetString("cmon1", idx), MonsterCritterID1: d.String("cmon1"),
MonsterCritterID2: dict.GetString("cmon2", idx), MonsterCritterID2: d.String("cmon2"),
MonsterCritterID3: dict.GetString("cmon3", idx), MonsterCritterID3: d.String("cmon3"),
MonsterCritterID4: dict.GetString("cmon4", idx), MonsterCritterID4: d.String("cmon4"),
MonsterCritter1SpawnChance: dict.GetNumber("cpct1", idx), MonsterCritter1SpawnChance: d.Number("cpct1"),
MonsterCritter2SpawnChance: dict.GetNumber("cpct2", idx), MonsterCritter2SpawnChance: d.Number("cpct2"),
MonsterCritter3SpawnChance: dict.GetNumber("cpct3", idx), MonsterCritter3SpawnChance: d.Number("cpct3"),
MonsterCritter4SpawnChance: dict.GetNumber("cpct4", idx), MonsterCritter4SpawnChance: d.Number("cpct4"),
SoundEnvironmentID: dict.GetNumber("SoundEnv", idx), SoundEnvironmentID: d.Number("SoundEnv"),
WaypointID: dict.GetNumber("Waypoint", idx), WaypointID: d.Number("Waypoint"),
LevelDisplayName: dict.GetString("LevelName", idx), LevelDisplayName: d.String("LevelName"),
LevelWarpName: dict.GetString("LevelWarp", idx), LevelWarpName: d.String("LevelWarp"),
TitleImageName: dict.GetString("EntryFile", idx), TitleImageName: d.String("EntryFile"),
ObjectGroupID0: dict.GetNumber("ObjGrp0", idx), ObjectGroupID0: d.Number("ObjGrp0"),
ObjectGroupID1: dict.GetNumber("ObjGrp1", idx), ObjectGroupID1: d.Number("ObjGrp1"),
ObjectGroupID2: dict.GetNumber("ObjGrp2", idx), ObjectGroupID2: d.Number("ObjGrp2"),
ObjectGroupID3: dict.GetNumber("ObjGrp3", idx), ObjectGroupID3: d.Number("ObjGrp3"),
ObjectGroupID4: dict.GetNumber("ObjGrp4", idx), ObjectGroupID4: d.Number("ObjGrp4"),
ObjectGroupID5: dict.GetNumber("ObjGrp5", idx), ObjectGroupID5: d.Number("ObjGrp5"),
ObjectGroupID6: dict.GetNumber("ObjGrp6", idx), ObjectGroupID6: d.Number("ObjGrp6"),
ObjectGroupID7: dict.GetNumber("ObjGrp7", idx), ObjectGroupID7: d.Number("ObjGrp7"),
ObjectGroupSpawnChance0: dict.GetNumber("ObjPrb0", idx), ObjectGroupSpawnChance0: d.Number("ObjPrb0"),
ObjectGroupSpawnChance1: dict.GetNumber("ObjPrb1", idx), ObjectGroupSpawnChance1: d.Number("ObjPrb1"),
ObjectGroupSpawnChance2: dict.GetNumber("ObjPrb2", idx), ObjectGroupSpawnChance2: d.Number("ObjPrb2"),
ObjectGroupSpawnChance3: dict.GetNumber("ObjPrb3", idx), ObjectGroupSpawnChance3: d.Number("ObjPrb3"),
ObjectGroupSpawnChance4: dict.GetNumber("ObjPrb4", idx), ObjectGroupSpawnChance4: d.Number("ObjPrb4"),
ObjectGroupSpawnChance5: dict.GetNumber("ObjPrb5", idx), ObjectGroupSpawnChance5: d.Number("ObjPrb5"),
ObjectGroupSpawnChance6: dict.GetNumber("ObjPrb6", idx), ObjectGroupSpawnChance6: d.Number("ObjPrb6"),
ObjectGroupSpawnChance7: dict.GetNumber("ObjPrb7", idx), ObjectGroupSpawnChance7: d.Number("ObjPrb7"),
} }
LevelDetails[idx] = record LevelDetails[record.Id] = record
}
if d.Err != nil {
panic(d.Err)
} }
log.Printf("Loaded %d LevelDetails records", len(LevelDetails)) log.Printf("Loaded %d LevelDetails records", len(LevelDetails))

View File

@ -110,7 +110,7 @@ type MissileRecord struct {
ExplosionMissile string // name of a missile from missiles.txt that is created upon collision ExplosionMissile string // name of a missile from missiles.txt that is created upon collision
// or anytime it is destroyed if AlwaysExplode is true // or anytime it is destroyed if AlwaysExplode is true
Id int //nolint:golint Id is the correct key Id int //nolint:golint,stylecheck // Id is the correct key
ClientMovementFunc int ClientMovementFunc int
ClientCollisionFunc int ClientCollisionFunc int

View File

@ -8,31 +8,24 @@ import (
// MonPresets stores monster presets // MonPresets stores monster presets
//nolint:gochecknoglobals // Currently global by design, only written once //nolint:gochecknoglobals // Currently global by design, only written once
var MonPresets [][]string var MonPresets map[int32][]string
// LoadMonPresets loads monster presets from monpresets.txt // LoadMonPresets loads monster presets from monpresets.txt
func LoadMonPresets(file []byte) { func LoadMonPresets(file []byte) {
dict := d2common.LoadDataDictionary(string(file)) MonPresets = make(map[int32][]string)
numRecords := len(dict.Data)
MonPresets = make([][]string, numRecords)
for idx := range MonPresets { d := d2common.LoadDataDictionary(file)
MonPresets[idx] = make([]string, numRecords) for d.Next() {
} act := int32(d.Number("Act"))
if _, ok := MonPresets[act]; !ok {
lastAct := 0 MonPresets[act] = make([]string, 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) MonPresets[act] = append(MonPresets[act], d.String("Place"))
lastAct = act }
placeIdx++ if d.Err != nil {
panic(d.Err)
} }
log.Printf("Loaded %d MonPreset records", len(MonPresets)) log.Printf("Loaded %d MonPreset records", len(MonPresets))

View File

@ -28,7 +28,7 @@ type (
// column also links other hardcoded effects to the units, such as the // column also links other hardcoded effects to the units, such as the
// transparency on necro summons and the name-color change on unique boss // transparency on necro summons and the name-color change on unique boss
// units (thanks to Kingpin for the info) // units (thanks to Kingpin for the info)
Id string // called `hcIdx` in monstats.txt //nolint:golint Id is the right key Id string //nolint:golint,stylecheck // called `hcIdx` in monstats.txt
// BaseKey is an ID pointer of the “base” unit for this specific // BaseKey is an ID pointer of the “base” unit for this specific
// monster type (ex. There are five types of “Fallen”; all of them have // monster type (ex. There are five types of “Fallen”; all of them have
@ -81,12 +81,10 @@ type (
SpawnAnimationKey string // called `spawnmode` in monstats.txt SpawnAnimationKey string // called `spawnmode` in monstats.txt
// MinionId1 is an Id of a minion that spawns when this monster is created // MinionId1 is an Id of a minion that spawns when this monster is created
//nolint:golint Id is the right key MinionId1 string //nolint:golint,stylecheck // called `minion1` in monstats.txt
MinionId1 string // called `minion1` in monstats.txt
// MinionId2 is an Id of a minion that spawns when this monster is created // MinionId2 is an Id of a minion that spawns when this monster is created
//nolint:golint Id is the right key MinionId2 string //nolint:golint,stylecheck // called `minion2` in monstats.txt
MinionId2 string // called `minion2` in monstats.txt
// SoundKeyNormal, SoundKeySpecial // SoundKeyNormal, SoundKeySpecial
// specifies the ID pointer to this monsters “Sound Bank” in MonSound.txt // specifies the ID pointer to this monsters “Sound Bank” in MonSound.txt
@ -115,14 +113,14 @@ type (
// the ID Pointer to the skill (from Skills.txt) the monster will cast when // the ID Pointer to the skill (from Skills.txt) the monster will cast when
// this specific slot is accessed by the AI. Which slots are used is // this specific slot is accessed by the AI. Which slots are used is
// determined by the units AI. // determined by the units AI.
SkillId1 string // called `Skill1` in monstats.txt //nolint:golint Id is the right key SkillId1 string //nolint:golint,stylecheck // called `Skill1` in monstats.txt
SkillId2 string // called `Skill2` in monstats.txt //nolint:golint Id is the right key SkillId2 string //nolint:golint,stylecheck // called `Skill2` in monstats.txt
SkillId3 string // called `Skill3` in monstats.txt //nolint:golint Id is the right key SkillId3 string //nolint:golint,stylecheck // called `Skill3` in monstats.txt
SkillId4 string // called `Skill4` in monstats.txt //nolint:golint Id is the right key SkillId4 string //nolint:golint,stylecheck // called `Skill4` in monstats.txt
SkillId5 string // called `Skill5` in monstats.txt //nolint:golint Id is the right key SkillId5 string //nolint:golint,stylecheck // called `Skill5` in monstats.txt
SkillId6 string // called `Skill6` in monstats.txt //nolint:golint Id is the right key SkillId6 string //nolint:golint,stylecheck // called `Skill6` in monstats.txt
SkillId7 string // called `Skill7` in monstats.txt //nolint:golint Id is the right key SkillId7 string //nolint:golint,stylecheck // called `Skill7` in monstats.txt
SkillId8 string // called `Skill8` in monstats.txt //nolint:golint Id is the right key SkillId8 string //nolint:golint,stylecheck // called `Skill8` in monstats.txt
// SkillAnimation1 -- SkillAnimation8 // SkillAnimation1 -- SkillAnimation8
// the graphical MODE (or SEQUENCE) this unit uses when it uses this skill. // the graphical MODE (or SEQUENCE) this unit uses when it uses this skill.
@ -139,8 +137,7 @@ type (
// ID Pointer to the skill that controls this units damage. This is used for // ID Pointer to the skill that controls this units damage. This is used for
// the druids summons. IE their damage is specified solely by Skills.txt and // the druids summons. IE their damage is specified solely by Skills.txt and
// not by MonStats.txt. // not by MonStats.txt.
//nolint:golint Id is the right key DamageSkillId string //nolint:golint,stylecheck // called `SkillDamage` in monstats.txt
DamageSkillId string // called `SkillDamage` in monstats.txt
// ElementAttackMode1 -- ElementAttackMode3 // ElementAttackMode1 -- ElementAttackMode3
// the mode to which the elemental damage is appended. The modes to which // the mode to which the elemental damage is appended. The modes to which
@ -190,12 +187,12 @@ type (
// TreasureClassQuestTriggerId // TreasureClassQuestTriggerId
// the ID of the Quest that triggers the Quest Treasureclass drop. // the ID of the Quest that triggers the Quest Treasureclass drop.
TreasureClassQuestTriggerId string // called `TCQuestId` in monstats.txt TreasureClassQuestTriggerId string //nolint:golint,stylecheck // called `TCQuestId` in monstats.txt
// TreasureClassQuestCompleteId // TreasureClassQuestCompleteId
// the ID of the Quest State that you need to complete to trigger the Quest // the ID of the Quest State that you need to complete to trigger the Quest
// Treasureclass trop. // Treasureclass trop.
TreasureClassQuestCompleteId string // called `TCQuestCP` in monstats.txt TreasureClassQuestCompleteId string //nolint:golint,stylecheck // called `TCQuestCP` in monstats.txt
// PaletteId indicates which palette (color) entry the unit will use, most // PaletteId indicates which palette (color) entry the unit will use, most
// monsters have a palshift.dat file in their COF folder, this file // monsters have a palshift.dat file in their COF folder, this file
@ -208,7 +205,7 @@ type (
// such as FC do not accept their palettes. // such as FC do not accept their palettes.
// NOTE no 2: some monsters got unused palettes, ZM (zombie) for example // NOTE no 2: some monsters got unused palettes, ZM (zombie) for example
// will turn light-rotten-green with palette nr 5 and pink-creamy with 6. // will turn light-rotten-green with palette nr 5 and pink-creamy with 6.
PaletteId int // called `TransLvl` in monstats.txt PaletteId int //nolint:golint,stylecheck // called `TransLvl` in monstats.txt
// SpawnOffsetX, SpawnOffsetY // SpawnOffsetX, SpawnOffsetY
// are the x/y offsets at which spawned monsters are placed. IE this prevents // are the x/y offsets at which spawned monsters are placed. IE this prevents
@ -683,271 +680,275 @@ type (
} }
) )
var MonStats map[string]*MonStatsRecord // MonStats stores all of the MonStat Records
var MonStats map[string]*MonStatsRecord //nolint:gochecknoglobals // Currently global by design, only written once
//nolint:funlen // Makes no sense to split // LoadMonStats loads monstats
func LoadMonStats(file []byte) { func LoadMonStats(file []byte) { // nolint:funlen // Makes no sense to split
dict := d2common.LoadDataDictionary(string(file)) MonStats = make(map[string]*MonStatsRecord)
numRecords := len(dict.Data)
MonStats = make(map[string]*MonStatsRecord, numRecords)
for idx := range dict.Data { d := d2common.LoadDataDictionary(file)
for d.Next() {
record := &MonStatsRecord{ record := &MonStatsRecord{
Key: dict.GetString("Id", idx), Key: d.String("Id"),
Id: dict.GetString("hcIdx", idx), Id: d.String("hcIdx"),
BaseKey: dict.GetString("BaseId", idx), BaseKey: d.String("BaseId"),
NextKey: dict.GetString("NextInClass", idx), NextKey: d.String("NextInClass"),
PaletteId: dict.GetNumber("TransLvl", idx), PaletteId: d.Number("TransLvl"),
NameStringTableKey: dict.GetString("NameStr", idx), NameStringTableKey: d.String("NameStr"),
ExtraDataKey: dict.GetString("MonStatsEx", idx), ExtraDataKey: d.String("MonStatsEx"),
PropertiesKey: dict.GetString("MonProp", idx), PropertiesKey: d.String("MonProp"),
MonsterGroup: dict.GetString("MonType", idx), MonsterGroup: d.String("MonType"),
AiKey: dict.GetString("AI", idx), AiKey: d.String("AI"),
DescriptionStringTableKey: dict.GetString("DescStr", idx), DescriptionStringTableKey: d.String("DescStr"),
AnimationDirectoryToken: dict.GetString("Code", idx), AnimationDirectoryToken: d.String("Code"),
Enabled: dict.GetNumber("enabled", idx) > 0, Enabled: d.Number("enabled") > 0,
IsRanged: dict.GetNumber("rangedtype", idx) > 0, IsRanged: d.Number("rangedtype") > 0,
SpawnsMinions: dict.GetNumber("placespawn", idx) > 0, SpawnsMinions: d.Number("placespawn") > 0,
SpawnKey: dict.GetString("spawn", idx), SpawnKey: d.String("spawn"),
SpawnOffsetX: dict.GetNumber("spawnx", idx), SpawnOffsetX: d.Number("spawnx"),
SpawnOffsetY: dict.GetNumber("spawny", idx), SpawnOffsetY: d.Number("spawny"),
SpawnAnimationKey: dict.GetString("spawnmode", idx), SpawnAnimationKey: d.String("spawnmode"),
MinionId1: dict.GetString("minion1", idx), MinionId1: d.String("minion1"),
MinionId2: dict.GetString("minion2", idx), MinionId2: d.String("minion2"),
IsLeader: dict.GetNumber("SetBoss", idx) > 0, IsLeader: d.Number("SetBoss") > 0,
TransferLeadership: dict.GetNumber("BossXfer", idx) > 0, TransferLeadership: d.Number("BossXfer") > 0,
MinionPartyMin: dict.GetNumber("PartyMin", idx), MinionPartyMin: d.Number("PartyMin"),
MinionPartyMax: dict.GetNumber("PartyMax", idx), MinionPartyMax: d.Number("PartyMax"),
MinionGroupMin: dict.GetNumber("MinGrp", idx), MinionGroupMin: d.Number("MinGrp"),
MinionGroupMax: dict.GetNumber("MaxGrp", idx), MinionGroupMax: d.Number("MaxGrp"),
PopulationReductionPercent: dict.GetNumber("sparsePopulate", idx), PopulationReductionPercent: d.Number("sparsePopulate"),
SpeedBase: dict.GetNumber("Velocity", idx), SpeedBase: d.Number("Velocity"),
SpeedRun: dict.GetNumber("Run", idx), SpeedRun: d.Number("Run"),
Rarity: dict.GetNumber("Rarity", idx), Rarity: d.Number("Rarity"),
LevelNormal: dict.GetNumber("Level", idx), LevelNormal: d.Number("Level"),
LevelNightmare: dict.GetNumber("Level(N)", idx), LevelNightmare: d.Number("Level(N)"),
LevelHell: dict.GetNumber("Level(H)", idx), LevelHell: d.Number("Level(H)"),
SoundKeyNormal: dict.GetString("MonSound", idx), SoundKeyNormal: d.String("MonSound"),
SoundKeySpecial: dict.GetString("UMonSound", idx), SoundKeySpecial: d.String("UMonSound"),
ThreatLevel: dict.GetNumber("threat", idx), ThreatLevel: d.Number("threat"),
AiDelayNormal: dict.GetNumber("aidel", idx), AiDelayNormal: d.Number("aidel"),
AiDelayNightmare: dict.GetNumber("aidel(N)", idx), AiDelayNightmare: d.Number("aidel(N)"),
AiDelayHell: dict.GetNumber("aidel(H)", idx), AiDelayHell: d.Number("aidel(H)"),
AiDistanceNormal: dict.GetNumber("aidist", idx), AiDistanceNormal: d.Number("aidist"),
AiDistanceNightmare: dict.GetNumber("aidist(N)", idx), AiDistanceNightmare: d.Number("aidist(N)"),
AiDistanceHell: dict.GetNumber("aidist(H)", idx), AiDistanceHell: d.Number("aidist(H)"),
AiParameterNormal1: dict.GetNumber("aip1", idx), AiParameterNormal1: d.Number("aip1"),
AiParameterNormal2: dict.GetNumber("aip2", idx), AiParameterNormal2: d.Number("aip2"),
AiParameterNormal3: dict.GetNumber("aip3", idx), AiParameterNormal3: d.Number("aip3"),
AiParameterNormal4: dict.GetNumber("aip4", idx), AiParameterNormal4: d.Number("aip4"),
AiParameterNormal5: dict.GetNumber("aip5", idx), AiParameterNormal5: d.Number("aip5"),
AiParameterNormal6: dict.GetNumber("aip6", idx), AiParameterNormal6: d.Number("aip6"),
AiParameterNormal7: dict.GetNumber("aip7", idx), AiParameterNormal7: d.Number("aip7"),
AiParameterNormal8: dict.GetNumber("aip8", idx), AiParameterNormal8: d.Number("aip8"),
AiParameterNightmare1: dict.GetNumber("aip1(N)", idx), AiParameterNightmare1: d.Number("aip1(N)"),
AiParameterNightmare2: dict.GetNumber("aip2(N)", idx), AiParameterNightmare2: d.Number("aip2(N)"),
AiParameterNightmare3: dict.GetNumber("aip3(N)", idx), AiParameterNightmare3: d.Number("aip3(N)"),
AiParameterNightmare4: dict.GetNumber("aip4(N)", idx), AiParameterNightmare4: d.Number("aip4(N)"),
AiParameterNightmare5: dict.GetNumber("aip5(N)", idx), AiParameterNightmare5: d.Number("aip5(N)"),
AiParameterNightmare6: dict.GetNumber("aip6(N)", idx), AiParameterNightmare6: d.Number("aip6(N)"),
AiParameterNightmare7: dict.GetNumber("aip7(N)", idx), AiParameterNightmare7: d.Number("aip7(N)"),
AiParameterNightmare8: dict.GetNumber("aip8(N)", idx), AiParameterNightmare8: d.Number("aip8(N)"),
AiParameterHell1: dict.GetNumber("aip1(H)", idx), AiParameterHell1: d.Number("aip1(H)"),
AiParameterHell2: dict.GetNumber("aip2(H)", idx), AiParameterHell2: d.Number("aip2(H)"),
AiParameterHell3: dict.GetNumber("aip3(H)", idx), AiParameterHell3: d.Number("aip3(H)"),
AiParameterHell4: dict.GetNumber("aip4(H)", idx), AiParameterHell4: d.Number("aip4(H)"),
AiParameterHell5: dict.GetNumber("aip5(H)", idx), AiParameterHell5: d.Number("aip5(H)"),
AiParameterHell6: dict.GetNumber("aip6(H)", idx), AiParameterHell6: d.Number("aip6(H)"),
AiParameterHell7: dict.GetNumber("aip7(H)", idx), AiParameterHell7: d.Number("aip7(H)"),
AiParameterHell8: dict.GetNumber("aip8(H)", idx), AiParameterHell8: d.Number("aip8(H)"),
MissileA1: dict.GetString("MissA1", idx), MissileA1: d.String("MissA1"),
MissileA2: dict.GetString("MissA2", idx), MissileA2: d.String("MissA2"),
MissileS1: dict.GetString("MissS1", idx), MissileS1: d.String("MissS1"),
MissileS2: dict.GetString("MissS2", idx), MissileS2: d.String("MissS2"),
MissileS3: dict.GetString("MissS3", idx), MissileS3: d.String("MissS3"),
MissileS4: dict.GetString("MissS4", idx), MissileS4: d.String("MissS4"),
MissileC: dict.GetString("MissC", idx), MissileC: d.String("MissC"),
MissileSQ: dict.GetString("MissSQ", idx), MissileSQ: d.String("MissSQ"),
Alignment: d2enum.MonsterAlignmentType(dict.GetNumber("Align", idx)), Alignment: d2enum.MonsterAlignmentType(d.Number("Align")),
IsLevelSpawnable: dict.GetNumber("isSpawn", idx) > 0, IsLevelSpawnable: d.Number("isSpawn") > 0,
IsMelee: dict.GetNumber("isMelee", idx) > 0, IsMelee: d.Number("isMelee") > 0,
IsNpc: dict.GetNumber("npc", idx) > 0, IsNpc: d.Number("npc") > 0,
IsInteractable: dict.GetNumber("interact", idx) > 0, IsInteractable: d.Number("interact") > 0,
HasInventory: dict.GetNumber("inventory", idx) > 0, HasInventory: d.Number("inventory") > 0,
CanEnterTown: dict.GetNumber("inTown", idx) > 0, CanEnterTown: d.Number("inTown") > 0,
IsUndeadLow: dict.GetNumber("lUndead", idx) > 0, IsUndeadLow: d.Number("lUndead") > 0,
IsUndeadHigh: dict.GetNumber("hUndead", idx) > 0, IsUndeadHigh: d.Number("hUndead") > 0,
IsDemon: dict.GetNumber("demon", idx) > 0, IsDemon: d.Number("demon") > 0,
IsFlying: dict.GetNumber("flying", idx) > 0, IsFlying: d.Number("flying") > 0,
CanOpenDoors: dict.GetNumber("opendoors", idx) > 0, CanOpenDoors: d.Number("opendoors") > 0,
IsSpecialBoss: dict.GetNumber("boss", idx) > 0, IsSpecialBoss: d.Number("boss") > 0,
IsActBoss: dict.GetNumber("primeevil", idx) > 0, IsActBoss: d.Number("primeevil") > 0,
IsKillable: dict.GetNumber("killable", idx) > 0, IsKillable: d.Number("killable") > 0,
IsAiSwitchable: dict.GetNumber("switchai", idx) > 0, IsAiSwitchable: d.Number("switchai") > 0,
DisableAura: dict.GetNumber("noAura", idx) > 0, DisableAura: d.Number("noAura") > 0,
DisableMultiShot: dict.GetNumber("nomultishot", idx) > 0, DisableMultiShot: d.Number("nomultishot") > 0,
DisableCounting: dict.GetNumber("neverCount", idx) > 0, DisableCounting: d.Number("neverCount") > 0,
IgnorePets: dict.GetNumber("petIgnore", idx) > 0, IgnorePets: d.Number("petIgnore") > 0,
DealsDamageOnDeath: dict.GetNumber("deathDmg", idx) > 0, DealsDamageOnDeath: d.Number("deathDmg") > 0,
GenericSpawn: dict.GetNumber("genericSpawn", idx) > 0, GenericSpawn: d.Number("genericSpawn") > 0,
SkillId1: dict.GetString("Skill1", idx), SkillId1: d.String("Skill1"),
SkillId2: dict.GetString("Skill2", idx), SkillId2: d.String("Skill2"),
SkillId3: dict.GetString("Skill3", idx), SkillId3: d.String("Skill3"),
SkillId4: dict.GetString("Skill4", idx), SkillId4: d.String("Skill4"),
SkillId5: dict.GetString("Skill5", idx), SkillId5: d.String("Skill5"),
SkillId6: dict.GetString("Skill6", idx), SkillId6: d.String("Skill6"),
SkillId7: dict.GetString("Skill7", idx), SkillId7: d.String("Skill7"),
SkillId8: dict.GetString("Skill8", idx), SkillId8: d.String("Skill8"),
SkillAnimation1: dict.GetString("Sk1mode", idx), SkillAnimation1: d.String("Sk1mode"),
SkillAnimation2: dict.GetString("Sk2mode", idx), SkillAnimation2: d.String("Sk2mode"),
SkillAnimation3: dict.GetString("Sk3mode", idx), SkillAnimation3: d.String("Sk3mode"),
SkillAnimation4: dict.GetString("Sk4mode", idx), SkillAnimation4: d.String("Sk4mode"),
SkillAnimation5: dict.GetString("Sk5mode", idx), SkillAnimation5: d.String("Sk5mode"),
SkillAnimation6: dict.GetString("Sk6mode", idx), SkillAnimation6: d.String("Sk6mode"),
SkillAnimation7: dict.GetString("Sk7mode", idx), SkillAnimation7: d.String("Sk7mode"),
SkillAnimation8: dict.GetString("Sk8mode", idx), SkillAnimation8: d.String("Sk8mode"),
SkillLevel1: dict.GetNumber("Sk1lvl", idx), SkillLevel1: d.Number("Sk1lvl"),
SkillLevel2: dict.GetNumber("Sk2lvl", idx), SkillLevel2: d.Number("Sk2lvl"),
SkillLevel3: dict.GetNumber("Sk3lvl", idx), SkillLevel3: d.Number("Sk3lvl"),
SkillLevel4: dict.GetNumber("Sk4lvl", idx), SkillLevel4: d.Number("Sk4lvl"),
SkillLevel5: dict.GetNumber("Sk5lvl", idx), SkillLevel5: d.Number("Sk5lvl"),
SkillLevel6: dict.GetNumber("Sk6lvl", idx), SkillLevel6: d.Number("Sk6lvl"),
SkillLevel7: dict.GetNumber("Sk7lvl", idx), SkillLevel7: d.Number("Sk7lvl"),
SkillLevel8: dict.GetNumber("Sk8lvl", idx), SkillLevel8: d.Number("Sk8lvl"),
LeechSensitivityNormal: dict.GetNumber("Drain", idx), LeechSensitivityNormal: d.Number("Drain"),
LeechSensitivityNightmare: dict.GetNumber("Drain(N)", idx), LeechSensitivityNightmare: d.Number("Drain(N)"),
LeechSensitivityHell: dict.GetNumber("Drain(H)", idx), LeechSensitivityHell: d.Number("Drain(H)"),
ColdSensitivityNormal: dict.GetNumber("coldeffect", idx), ColdSensitivityNormal: d.Number("coldeffect"),
ColdSensitivityNightmare: dict.GetNumber("coldeffect(N)", idx), ColdSensitivityNightmare: d.Number("coldeffect(N)"),
ColdSensitivityHell: dict.GetNumber("coldeffect(H)", idx), ColdSensitivityHell: d.Number("coldeffect(H)"),
ResistancePhysicalNormal: dict.GetNumber("ResDm", idx), ResistancePhysicalNormal: d.Number("ResDm"),
ResistancePhysicalNightmare: dict.GetNumber("ResDm(N)", idx), ResistancePhysicalNightmare: d.Number("ResDm(N)"),
ResistancePhysicalHell: dict.GetNumber("ResDm(H)", idx), ResistancePhysicalHell: d.Number("ResDm(H)"),
ResistanceMagicNormal: dict.GetNumber("ResMa", idx), ResistanceMagicNormal: d.Number("ResMa"),
ResistanceMagicNightmare: dict.GetNumber("ResMa(N)", idx), ResistanceMagicNightmare: d.Number("ResMa(N)"),
ResistanceMagicHell: dict.GetNumber("ResMa(H)", idx), ResistanceMagicHell: d.Number("ResMa(H)"),
ResistanceFireNormal: dict.GetNumber("ResFi", idx), ResistanceFireNormal: d.Number("ResFi"),
ResistanceFireNightmare: dict.GetNumber("ResFi(N)", idx), ResistanceFireNightmare: d.Number("ResFi(N)"),
ResistanceFireHell: dict.GetNumber("ResFi(H)", idx), ResistanceFireHell: d.Number("ResFi(H)"),
ResistanceLightningNormal: dict.GetNumber("ResLi", idx), ResistanceLightningNormal: d.Number("ResLi"),
ResistanceLightningNightmare: dict.GetNumber("ResLi(N)", idx), ResistanceLightningNightmare: d.Number("ResLi(N)"),
ResistanceLightningHell: dict.GetNumber("ResLi(H)", idx), ResistanceLightningHell: d.Number("ResLi(H)"),
ResistanceColdNormal: dict.GetNumber("ResCo", idx), ResistanceColdNormal: d.Number("ResCo"),
ResistanceColdNightmare: dict.GetNumber("ResCo(N)", idx), ResistanceColdNightmare: d.Number("ResCo(N)"),
ResistanceColdHell: dict.GetNumber("ResCo(H)", idx), ResistanceColdHell: d.Number("ResCo(H)"),
ResistancePoisonNormal: dict.GetNumber("ResPo", idx), ResistancePoisonNormal: d.Number("ResPo"),
ResistancePoisonNightmare: dict.GetNumber("ResPo(N)", idx), ResistancePoisonNightmare: d.Number("ResPo(N)"),
ResistancePoisonHell: dict.GetNumber("ResPo(H)", idx), ResistancePoisonHell: d.Number("ResPo(H)"),
HealthRegenPerFrame: dict.GetNumber("DamageRegen", idx), HealthRegenPerFrame: d.Number("DamageRegen"),
DamageSkillId: dict.GetString("SkillDamage", idx), DamageSkillId: d.String("SkillDamage"),
IgnoreMonLevelTxt: dict.GetNumber("noRatio", idx) > 0, IgnoreMonLevelTxt: d.Number("noRatio") > 0,
CanBlockWithoutShield: dict.GetNumber("NoShldBlock", idx) > 0, CanBlockWithoutShield: d.Number("NoShldBlock") > 0,
ChanceToBlockNormal: dict.GetNumber("ToBlock", idx), ChanceToBlockNormal: d.Number("ToBlock"),
ChanceToBlockNightmare: dict.GetNumber("ToBlock(N)", idx), ChanceToBlockNightmare: d.Number("ToBlock(N)"),
ChanceToBlockHell: dict.GetNumber("ToBlock(H)", idx), ChanceToBlockHell: d.Number("ToBlock(H)"),
ChanceDeadlyStrike: dict.GetNumber("Crit", idx), ChanceDeadlyStrike: d.Number("Crit"),
MinHPNormal: dict.GetNumber("minHP", idx), MinHPNormal: d.Number("minHP"),
MinHPNightmare: dict.GetNumber("MinHP(N)", idx), MinHPNightmare: d.Number("MinHP(N)"),
MinHPHell: dict.GetNumber("MinHP(H)", idx), MinHPHell: d.Number("MinHP(H)"),
MaxHPNormal: dict.GetNumber("maxHP", idx), MaxHPNormal: d.Number("maxHP"),
MaxHPNightmare: dict.GetNumber("MaxHP(N)", idx), MaxHPNightmare: d.Number("MaxHP(N)"),
MaxHPHell: dict.GetNumber("MaxHP(H)", idx), MaxHPHell: d.Number("MaxHP(H)"),
ArmorClassNormal: dict.GetNumber("AC", idx), ArmorClassNormal: d.Number("AC"),
ArmorClassNightmare: dict.GetNumber("AC(N)", idx), ArmorClassNightmare: d.Number("AC(N)"),
ArmorClassHell: dict.GetNumber("AC(H)", idx), ArmorClassHell: d.Number("AC(H)"),
ExperienceNormal: dict.GetNumber("Exp", idx), ExperienceNormal: d.Number("Exp"),
ExperienceNightmare: dict.GetNumber("Exp(N)", idx), ExperienceNightmare: d.Number("Exp(N)"),
ExperienceHell: dict.GetNumber("Exp(H)", idx), ExperienceHell: d.Number("Exp(H)"),
DamageMinA1Normal: dict.GetNumber("A1MinD", idx), DamageMinA1Normal: d.Number("A1MinD"),
DamageMinA1Nightmare: dict.GetNumber("A1MinD(N)", idx), DamageMinA1Nightmare: d.Number("A1MinD(N)"),
DamageMinA1Hell: dict.GetNumber("A1MinD(H)", idx), DamageMinA1Hell: d.Number("A1MinD(H)"),
DamageMaxA1Normal: dict.GetNumber("A1MaxD", idx), DamageMaxA1Normal: d.Number("A1MaxD"),
DamageMaxA1Nightmare: dict.GetNumber("A1MaxD(N)", idx), DamageMaxA1Nightmare: d.Number("A1MaxD(N)"),
DamageMaxA1Hell: dict.GetNumber("A1MaxD(H)", idx), DamageMaxA1Hell: d.Number("A1MaxD(H)"),
DamageMinA2Normal: dict.GetNumber("A2MinD", idx), DamageMinA2Normal: d.Number("A2MinD"),
DamageMinA2Nightmare: dict.GetNumber("A2MinD(N)", idx), DamageMinA2Nightmare: d.Number("A2MinD(N)"),
DamageMinA2Hell: dict.GetNumber("A2MinD(H)", idx), DamageMinA2Hell: d.Number("A2MinD(H)"),
DamageMaxA2Normal: dict.GetNumber("A2MaxD", idx), DamageMaxA2Normal: d.Number("A2MaxD"),
DamageMaxA2Nightmare: dict.GetNumber("A2MaxD(N)", idx), DamageMaxA2Nightmare: d.Number("A2MaxD(N)"),
DamageMaxA2Hell: dict.GetNumber("A2MaxD(H)", idx), DamageMaxA2Hell: d.Number("A2MaxD(H)"),
DamageMinS1Normal: dict.GetNumber("S1MinD", idx), DamageMinS1Normal: d.Number("S1MinD"),
DamageMinS1Nightmare: dict.GetNumber("S1MinD(N)", idx), DamageMinS1Nightmare: d.Number("S1MinD(N)"),
DamageMinS1Hell: dict.GetNumber("S1MinD(H)", idx), DamageMinS1Hell: d.Number("S1MinD(H)"),
DamageMaxS1Normal: dict.GetNumber("S1MaxD", idx), DamageMaxS1Normal: d.Number("S1MaxD"),
DamageMaxS1Nightmare: dict.GetNumber("S1MaxD(N)", idx), DamageMaxS1Nightmare: d.Number("S1MaxD(N)"),
DamageMaxS1Hell: dict.GetNumber("S1MaxD(H)", idx), DamageMaxS1Hell: d.Number("S1MaxD(H)"),
AttackRatingA1Normal: dict.GetNumber("A1TH", idx), AttackRatingA1Normal: d.Number("A1TH"),
AttackRatingA1Nightmare: dict.GetNumber("A1TH(N)", idx), AttackRatingA1Nightmare: d.Number("A1TH(N)"),
AttackRatingA1Hell: dict.GetNumber("A1TH(H)", idx), AttackRatingA1Hell: d.Number("A1TH(H)"),
AttackRatingA2Normal: dict.GetNumber("A2TH", idx), AttackRatingA2Normal: d.Number("A2TH"),
AttackRatingA2Nightmare: dict.GetNumber("A2TH(N)", idx), AttackRatingA2Nightmare: d.Number("A2TH(N)"),
AttackRatingA2Hell: dict.GetNumber("A2TH(H)", idx), AttackRatingA2Hell: d.Number("A2TH(H)"),
AttackRatingS1Normal: dict.GetNumber("S1TH", idx), AttackRatingS1Normal: d.Number("S1TH"),
AttackRatingS1Nightmare: dict.GetNumber("S1TH(N)", idx), AttackRatingS1Nightmare: d.Number("S1TH(N)"),
AttackRatingS1Hell: dict.GetNumber("S1TH(H)", idx), AttackRatingS1Hell: d.Number("S1TH(H)"),
ElementAttackMode1: dict.GetString("El1Mode", idx), ElementAttackMode1: d.String("El1Mode"),
ElementAttackMode2: dict.GetString("El2Mode", idx), ElementAttackMode2: d.String("El2Mode"),
ElementAttackMode3: dict.GetString("El3Mode", idx), ElementAttackMode3: d.String("El3Mode"),
ElementType1: dict.GetString("El1Type", idx), ElementType1: d.String("El1Type"),
ElementType2: dict.GetString("El2Type", idx), ElementType2: d.String("El2Type"),
ElementType3: dict.GetString("El3Type", idx), ElementType3: d.String("El3Type"),
ElementChance1Normal: dict.GetNumber("El1Pct", idx), ElementChance1Normal: d.Number("El1Pct"),
ElementChance1Nightmare: dict.GetNumber("El1Pct(N)", idx), ElementChance1Nightmare: d.Number("El1Pct(N)"),
ElementChance1Hell: dict.GetNumber("El1Pct(H)", idx), ElementChance1Hell: d.Number("El1Pct(H)"),
ElementChance2Normal: dict.GetNumber("El2Pct", idx), ElementChance2Normal: d.Number("El2Pct"),
ElementChance2Nightmare: dict.GetNumber("El2Pct(N)", idx), ElementChance2Nightmare: d.Number("El2Pct(N)"),
ElementChance2Hell: dict.GetNumber("El2Pct(H)", idx), ElementChance2Hell: d.Number("El2Pct(H)"),
ElementChance3Normal: dict.GetNumber("El3Pct", idx), ElementChance3Normal: d.Number("El3Pct"),
ElementChance3Nightmare: dict.GetNumber("El3Pct(N)", idx), ElementChance3Nightmare: d.Number("El3Pct(N)"),
ElementChance3Hell: dict.GetNumber("El3Pct(H)", idx), ElementChance3Hell: d.Number("El3Pct(H)"),
ElementDamageMin1Normal: dict.GetNumber("El1MinD", idx), ElementDamageMin1Normal: d.Number("El1MinD"),
ElementDamageMin1Nightmare: dict.GetNumber("El1MinD(N)", idx), ElementDamageMin1Nightmare: d.Number("El1MinD(N)"),
ElementDamageMin1Hell: dict.GetNumber("El1MinD(H)", idx), ElementDamageMin1Hell: d.Number("El1MinD(H)"),
ElementDamageMin2Normal: dict.GetNumber("El2MinD", idx), ElementDamageMin2Normal: d.Number("El2MinD"),
ElementDamageMin2Nightmare: dict.GetNumber("El2MinD(N)", idx), ElementDamageMin2Nightmare: d.Number("El2MinD(N)"),
ElementDamageMin2Hell: dict.GetNumber("El2MinD(H)", idx), ElementDamageMin2Hell: d.Number("El2MinD(H)"),
ElementDamageMin3Normal: dict.GetNumber("El3MinD", idx), ElementDamageMin3Normal: d.Number("El3MinD"),
ElementDamageMin3Nightmare: dict.GetNumber("El3MinD(N)", idx), ElementDamageMin3Nightmare: d.Number("El3MinD(N)"),
ElementDamageMin3Hell: dict.GetNumber("El3MinD(H)", idx), ElementDamageMin3Hell: d.Number("El3MinD(H)"),
ElementDamageMax1Normal: dict.GetNumber("El1MaxD", idx), ElementDamageMax1Normal: d.Number("El1MaxD"),
ElementDamageMax1Nightmare: dict.GetNumber("El1MaxD(N)", idx), ElementDamageMax1Nightmare: d.Number("El1MaxD(N)"),
ElementDamageMax1Hell: dict.GetNumber("El1MaxD(H)", idx), ElementDamageMax1Hell: d.Number("El1MaxD(H)"),
ElementDamageMax2Normal: dict.GetNumber("El2MaxD", idx), ElementDamageMax2Normal: d.Number("El2MaxD"),
ElementDamageMax2Nightmare: dict.GetNumber("El2MaxD(N)", idx), ElementDamageMax2Nightmare: d.Number("El2MaxD(N)"),
ElementDamageMax2Hell: dict.GetNumber("El2MaxD(H)", idx), ElementDamageMax2Hell: d.Number("El2MaxD(H)"),
ElementDamageMax3Normal: dict.GetNumber("El3MaxD", idx), ElementDamageMax3Normal: d.Number("El3MaxD"),
ElementDamageMax3Nightmare: dict.GetNumber("El3MaxD(N)", idx), ElementDamageMax3Nightmare: d.Number("El3MaxD(N)"),
ElementDamageMax3Hell: dict.GetNumber("El3MaxD(H)", idx), ElementDamageMax3Hell: d.Number("El3MaxD(H)"),
ElementDuration1Normal: dict.GetNumber("El1Dur", idx), ElementDuration1Normal: d.Number("El1Dur"),
ElementDuration1Nightmare: dict.GetNumber("El1Dur(N)", idx), ElementDuration1Nightmare: d.Number("El1Dur(N)"),
ElementDuration1Hell: dict.GetNumber("El1Dur(H)", idx), ElementDuration1Hell: d.Number("El1Dur(H)"),
ElementDuration2Normal: dict.GetNumber("El2Dur", idx), ElementDuration2Normal: d.Number("El2Dur"),
ElementDuration2Nightmare: dict.GetNumber("El2Dur(N)", idx), ElementDuration2Nightmare: d.Number("El2Dur(N)"),
ElementDuration2Hell: dict.GetNumber("El2Dur(H)", idx), ElementDuration2Hell: d.Number("El2Dur(H)"),
ElementDuration3Normal: dict.GetNumber("El3Dur", idx), ElementDuration3Normal: d.Number("El3Dur"),
ElementDuration3Nightmare: dict.GetNumber("El3Dur(N)", idx), ElementDuration3Nightmare: d.Number("El3Dur(N)"),
ElementDuration3Hell: dict.GetNumber("El3Dur(H)", idx), ElementDuration3Hell: d.Number("El3Dur(H)"),
TreasureClassNormal: dict.GetString("TreasureClass1", idx), TreasureClassNormal: d.String("TreasureClass1"),
TreasureClassNightmare: dict.GetString("TreasureClass1(N)", idx), TreasureClassNightmare: d.String("TreasureClass1(N)"),
TreasureClassHell: dict.GetString("TreasureClass1(H)", idx), TreasureClassHell: d.String("TreasureClass1(H)"),
TreasureClassChampionNormal: dict.GetString("TreasureClass2", idx), TreasureClassChampionNormal: d.String("TreasureClass2"),
TreasureClassChampionNightmare: dict.GetString("TreasureClass2(N)", idx), TreasureClassChampionNightmare: d.String("TreasureClass2(N)"),
TreasureClassChampionHell: dict.GetString("TreasureClass2(H)", idx), TreasureClassChampionHell: d.String("TreasureClass2(H)"),
TreasureClass3UniqueNormal: dict.GetString("TreasureClass3", idx), TreasureClass3UniqueNormal: d.String("TreasureClass3"),
TreasureClass3UniqueNightmare: dict.GetString("TreasureClass3(N)", idx), TreasureClass3UniqueNightmare: d.String("TreasureClass3(N)"),
TreasureClass3UniqueHell: dict.GetString("TreasureClass3(H)", idx), TreasureClass3UniqueHell: d.String("TreasureClass3(H)"),
TreasureClassQuestNormal: dict.GetString("TreasureClass4", idx), TreasureClassQuestNormal: d.String("TreasureClass4"),
TreasureClassQuestNightmare: dict.GetString("TreasureClass4(N)", idx), TreasureClassQuestNightmare: d.String("TreasureClass4(N)"),
TreasureClassQuestHell: dict.GetString("TreasureClass4(H)", idx), TreasureClassQuestHell: d.String("TreasureClass4(H)"),
TreasureClassQuestTriggerId: dict.GetString("TCQuestId", idx), TreasureClassQuestTriggerId: d.String("TCQuestId"),
TreasureClassQuestCompleteId: dict.GetString("TCQuestCP", idx), TreasureClassQuestCompleteId: d.String("TCQuestCP"),
SpecialEndDeath: dict.GetNumber("SplEndDeath", idx), SpecialEndDeath: d.Number("SplEndDeath"),
SpecialGetModeChart: dict.GetNumber("SplGetModeChart", idx) > 0, SpecialGetModeChart: d.Number("SplGetModeChart") > 0,
SpecialEndGeneric: dict.GetNumber("SplEndGeneric", idx) > 0, SpecialEndGeneric: d.Number("SplEndGeneric") > 0,
SpecialClientEnd: dict.GetNumber("SplClientEnd", idx) > 0, SpecialClientEnd: d.Number("SplClientEnd") > 0,
} }
MonStats[record.Key] = record MonStats[record.Key] = record
} }
if d.Err != nil {
panic(d.Err)
}
log.Printf("Loaded %d MonStats records", len(MonStats)) log.Printf("Loaded %d MonStats records", len(MonStats))
} }

View File

@ -9,37 +9,6 @@ import (
// MonStats2Record is a representation of a row from monstats2.txt // MonStats2Record is a representation of a row from monstats2.txt
type MonStats2Record struct { 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 // Available options for equipment
// randomly selected from // randomly selected from
HDv []string HDv []string
@ -59,45 +28,40 @@ type MonStats2Record struct {
S7v []string S7v []string
S8v []string S8v []string
// Does the unit have this component Key string // Key, the object ID MonStatEx feild from MonStat
HD bool BaseWeaponClass string
TR bool ResurrectSkill string
LG bool Heart string
RA bool BodyPart string
LA bool
RH bool // These follow three are apparently unused
LH bool Height int
SH bool OverlayHeight int
S1 bool PixelHeight int
S2 bool
S3 bool // Diameter in subtiles
S4 bool SizeX int
S5 bool SizeY int
S6 bool
S7 bool // Bounding box
S8 bool BoxTop int
BoxLeft int
BoxWidth int
BoxHeight int
// Spawn method used
SpawnMethod int
// Melee radius
MeleeRng int
// base weaponclass?
HitClass int
// Sum of available components // Sum of available components
TotalPieces int 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 // Number of directions for each mode
dDT int dDT int
dNU int dNU int
@ -116,6 +80,79 @@ type MonStats2Record struct {
dSQ int dSQ int
dRN int dRN int
// If the units is restored on map reload
Restore int
// What maximap index is used for the automap
AutomapCel int
// 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
// Inferno animation stuff
InfernoLen int
InfernoAnim int
InfernoRollback int
// Which mode is used after resurrection
ResurrectMode d2enum.MonsterAnimationMode
// This specifies if the size values get used for collision detection
NoGfxHitTest bool
// 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
// 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
// Available modes while moving aside from WL and RN // Available modes while moving aside from WL and RN
A1mv bool A1mv bool
A2mv bool A2mv bool
@ -125,12 +162,6 @@ type MonStats2Record struct {
S3mv bool S3mv bool
S4mv 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 // true of unit uses an automap entry
NoMap bool NoMap bool
@ -191,39 +222,8 @@ type MonStats2Record struct {
// If multiple layers should be used on death (otherwise only TR) // If multiple layers should be used on death (otherwise only TR)
CompositeDeath bool 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 // Which skill is used for resurrection
ResurrectSkill string
} }
// MonStats2 stores all of the MonStats2Records // MonStats2 stores all of the MonStats2Records
@ -233,140 +233,143 @@ var MonStats2 map[string]*MonStats2Record
// LoadMonStats2 loads MonStats2Records from monstats2.txt // LoadMonStats2 loads MonStats2Records from monstats2.txt
//nolint:funlen //just a big data loader //nolint:funlen //just a big data loader
func LoadMonStats2(file []byte) { func LoadMonStats2(file []byte) {
dict := d2common.LoadDataDictionary(string(file)) MonStats2 = make(map[string]*MonStats2Record)
numRecords := len(dict.Data)
MonStats2 = make(map[string]*MonStats2Record, numRecords)
for idx := range dict.Data { d := d2common.LoadDataDictionary(file)
for d.Next() {
record := &MonStats2Record{ record := &MonStats2Record{
Key: dict.GetString("Id", idx), Key: d.String("Id"),
Height: dict.GetNumber("Height", idx), Height: d.Number("Height"),
OverlayHeight: dict.GetNumber("OverlayHeight", idx), OverlayHeight: d.Number("OverlayHeight"),
PixelHeight: dict.GetNumber("pixHeight", idx), PixelHeight: d.Number("pixHeight"),
SizeX: dict.GetNumber("SizeX", idx), SizeX: d.Number("SizeX"),
SizeY: dict.GetNumber("SizeY", idx), SizeY: d.Number("SizeY"),
SpawnMethod: dict.GetNumber("spawnCol", idx), SpawnMethod: d.Number("spawnCol"),
MeleeRng: dict.GetNumber("MeleeRng", idx), MeleeRng: d.Number("MeleeRng"),
BaseWeaponClass: dict.GetString("BaseW", idx), BaseWeaponClass: d.String("BaseW"),
HitClass: dict.GetNumber("HitClass", idx), HitClass: d.Number("HitClass"),
HDv: dict.GetDelimitedList("HDv", idx), HDv: d.List("HDv"),
TRv: dict.GetDelimitedList("TRv", idx), TRv: d.List("TRv"),
LGv: dict.GetDelimitedList("LGv", idx), LGv: d.List("LGv"),
Rav: dict.GetDelimitedList("Rav", idx), Rav: d.List("Rav"),
Lav: dict.GetDelimitedList("Lav", idx), Lav: d.List("Lav"),
RHv: dict.GetDelimitedList("RHv", idx), RHv: d.List("RHv"),
LHv: dict.GetDelimitedList("LHv", idx), LHv: d.List("LHv"),
SHv: dict.GetDelimitedList("SHv", idx), SHv: d.List("SHv"),
S1v: dict.GetDelimitedList("S1v", idx), S1v: d.List("S1v"),
S2v: dict.GetDelimitedList("S2v", idx), S2v: d.List("S2v"),
S3v: dict.GetDelimitedList("S3v", idx), S3v: d.List("S3v"),
S4v: dict.GetDelimitedList("S4v", idx), S4v: d.List("S4v"),
S5v: dict.GetDelimitedList("S5v", idx), S5v: d.List("S5v"),
S6v: dict.GetDelimitedList("S6v", idx), S6v: d.List("S6v"),
S7v: dict.GetDelimitedList("S7v", idx), S7v: d.List("S7v"),
S8v: dict.GetDelimitedList("S8v", idx), S8v: d.List("S8v"),
HD: dict.GetBool("HD", idx), HD: d.Bool("HD"),
TR: dict.GetBool("TR", idx), TR: d.Bool("TR"),
LG: dict.GetBool("LG", idx), LG: d.Bool("LG"),
RA: dict.GetBool("RA", idx), RA: d.Bool("RA"),
LA: dict.GetBool("LA", idx), LA: d.Bool("LA"),
RH: dict.GetBool("RH", idx), RH: d.Bool("RH"),
LH: dict.GetBool("LH", idx), LH: d.Bool("LH"),
SH: dict.GetBool("SH", idx), SH: d.Bool("SH"),
S1: dict.GetBool("S1", idx), S1: d.Bool("S1"),
S2: dict.GetBool("S2", idx), S2: d.Bool("S2"),
S3: dict.GetBool("S3", idx), S3: d.Bool("S3"),
S4: dict.GetBool("S4", idx), S4: d.Bool("S4"),
S5: dict.GetBool("S5", idx), S5: d.Bool("S5"),
S6: dict.GetBool("S6", idx), S6: d.Bool("S6"),
S7: dict.GetBool("S7", idx), S7: d.Bool("S7"),
S8: dict.GetBool("S8", idx), S8: d.Bool("S8"),
TotalPieces: dict.GetNumber("TotalPieces", idx), TotalPieces: d.Number("TotalPieces"),
mDT: dict.GetBool("mDT", idx), mDT: d.Bool("mDT"),
mNU: dict.GetBool("mNU", idx), mNU: d.Bool("mNU"),
mWL: dict.GetBool("mWL", idx), mWL: d.Bool("mWL"),
mGH: dict.GetBool("mGH", idx), mGH: d.Bool("mGH"),
mA1: dict.GetBool("mA1", idx), mA1: d.Bool("mA1"),
mA2: dict.GetBool("mA2", idx), mA2: d.Bool("mA2"),
mBL: dict.GetBool("mBL", idx), mBL: d.Bool("mBL"),
mSC: dict.GetBool("mSC", idx), mSC: d.Bool("mSC"),
mS1: dict.GetBool("mS1", idx), mS1: d.Bool("mS1"),
mS2: dict.GetBool("mS2", idx), mS2: d.Bool("mS2"),
mS3: dict.GetBool("mS3", idx), mS3: d.Bool("mS3"),
mS4: dict.GetBool("mS4", idx), mS4: d.Bool("mS4"),
mDD: dict.GetBool("mDD", idx), mDD: d.Bool("mDD"),
mKB: dict.GetBool("mKB", idx), mKB: d.Bool("mKB"),
mSQ: dict.GetBool("mSQ", idx), mSQ: d.Bool("mSQ"),
mRN: dict.GetBool("mRN", idx), mRN: d.Bool("mRN"),
dDT: dict.GetNumber("mDT", idx), dDT: d.Number("mDT"),
dNU: dict.GetNumber("mNU", idx), dNU: d.Number("mNU"),
dWL: dict.GetNumber("mWL", idx), dWL: d.Number("mWL"),
dGH: dict.GetNumber("mGH", idx), dGH: d.Number("mGH"),
dA1: dict.GetNumber("mA1", idx), dA1: d.Number("mA1"),
dA2: dict.GetNumber("mA2", idx), dA2: d.Number("mA2"),
dBL: dict.GetNumber("mBL", idx), dBL: d.Number("mBL"),
dSC: dict.GetNumber("mSC", idx), dSC: d.Number("mSC"),
dS1: dict.GetNumber("mS1", idx), dS1: d.Number("mS1"),
dS2: dict.GetNumber("mS2", idx), dS2: d.Number("mS2"),
dS3: dict.GetNumber("mS3", idx), dS3: d.Number("mS3"),
dS4: dict.GetNumber("mS4", idx), dS4: d.Number("mS4"),
dDD: dict.GetNumber("mDD", idx), dDD: d.Number("mDD"),
dKB: dict.GetNumber("mKB", idx), dKB: d.Number("mKB"),
dSQ: dict.GetNumber("mSQ", idx), dSQ: d.Number("mSQ"),
dRN: dict.GetNumber("mRN", idx), dRN: d.Number("mRN"),
A1mv: dict.GetBool("A1mv", idx), A1mv: d.Bool("A1mv"),
A2mv: dict.GetBool("A2mv", idx), A2mv: d.Bool("A2mv"),
SCmv: dict.GetBool("SCmv", idx), SCmv: d.Bool("SCmv"),
S1mv: dict.GetBool("S1mv", idx), S1mv: d.Bool("S1mv"),
S2mv: dict.GetBool("S2mv", idx), S2mv: d.Bool("S2mv"),
S3mv: dict.GetBool("S3mv", idx), S3mv: d.Bool("S3mv"),
S4mv: dict.GetBool("S4mv", idx), S4mv: d.Bool("S4mv"),
NoGfxHitTest: dict.GetBool("noGfxHitTest", idx), NoGfxHitTest: d.Bool("noGfxHitTest"),
BoxTop: dict.GetNumber("htTop", idx), BoxTop: d.Number("htTop"),
BoxLeft: dict.GetNumber("htLeft", idx), BoxLeft: d.Number("htLeft"),
BoxWidth: dict.GetNumber("htWidth", idx), BoxWidth: d.Number("htWidth"),
BoxHeight: dict.GetNumber("htHeight", idx), BoxHeight: d.Number("htHeight"),
Restore: dict.GetNumber("restore", idx), Restore: d.Number("restore"),
AutomapCel: dict.GetNumber("automapCel", idx), AutomapCel: d.Number("automapCel"),
NoMap: dict.GetBool("noMap", idx), NoMap: d.Bool("noMap"),
NoOvly: dict.GetBool("noOvly", idx), NoOvly: d.Bool("noOvly"),
IsSelectable: dict.GetBool("isSel", idx), IsSelectable: d.Bool("isSel"),
AllySelectable: dict.GetBool("alSel", idx), AllySelectable: d.Bool("alSel"),
shiftSel: dict.GetBool("shiftSel", idx), shiftSel: d.Bool("shiftSel"),
NotSelectable: dict.GetBool("noSel", idx), NotSelectable: d.Bool("noSel"),
IsCorpseSelectable: dict.GetBool("corpseSel", idx), IsCorpseSelectable: d.Bool("corpseSel"),
IsAttackable: dict.GetBool("isAtt", idx), IsAttackable: d.Bool("isAtt"),
IsRevivable: dict.GetBool("revive", idx), IsRevivable: d.Bool("revive"),
IsCritter: dict.GetBool("critter", idx), IsCritter: d.Bool("critter"),
IsSmall: dict.GetBool("small", idx), IsSmall: d.Bool("small"),
IsLarge: dict.GetBool("large", idx), IsLarge: d.Bool("large"),
IsSoft: dict.GetBool("soft", idx), IsSoft: d.Bool("soft"),
IsInert: dict.GetBool("inert", idx), IsInert: d.Bool("inert"),
objCol: dict.GetBool("objCol", idx), objCol: d.Bool("objCol"),
IsCorpseCollidable: dict.GetBool("deadCol", idx), IsCorpseCollidable: d.Bool("deadCol"),
IsCorpseWalkable: dict.GetBool("unflatDead", idx), IsCorpseWalkable: d.Bool("unflatDead"),
HasShadow: dict.GetBool("Shadow", idx), HasShadow: d.Bool("Shadow"),
NoUniqueShift: dict.GetBool("noUniqueShift", idx), NoUniqueShift: d.Bool("noUniqueShift"),
CompositeDeath: dict.GetBool("compositeDeath", idx), CompositeDeath: d.Bool("compositeDeath"),
LocalBlood: dict.GetNumber("localBlood", idx), LocalBlood: d.Number("localBlood"),
Bleed: dict.GetNumber("Bleed", idx), Bleed: d.Number("Bleed"),
Light: dict.GetNumber("Light", idx), Light: d.Number("Light"),
LightR: dict.GetNumber("light-r", idx), LightR: d.Number("light-r"),
LightG: dict.GetNumber("light-g", idx), LightG: d.Number("light-g"),
lightB: dict.GetNumber("light-b", idx), lightB: d.Number("light-b"),
NormalPalette: dict.GetNumber("Utrans", idx), NormalPalette: d.Number("Utrans"),
NightmarePalette: dict.GetNumber("Utrans(N)", idx), NightmarePalette: d.Number("Utrans(N)"),
HellPalatte: dict.GetNumber("Utrans(H)", idx), HellPalatte: d.Number("Utrans(H)"),
Heart: dict.GetString("Heart", idx), Heart: d.String("Heart"),
BodyPart: dict.GetString("BodyPart", idx), BodyPart: d.String("BodyPart"),
InfernoLen: dict.GetNumber("InfernoLen", idx), InfernoLen: d.Number("InfernoLen"),
InfernoAnim: dict.GetNumber("InfernoAnim", idx), InfernoAnim: d.Number("InfernoAnim"),
InfernoRollback: dict.GetNumber("InfernoRollback", idx), InfernoRollback: d.Number("InfernoRollback"),
ResurrectMode: d2enum.MonsterAnimationModeFromString(dict.GetString("ResurrectMode", idx)), ResurrectMode: d2enum.MonsterAnimationModeFromString(d.String("ResurrectMode")),
ResurrectSkill: dict.GetString("ResurrectSkill", idx), ResurrectSkill: d.String("ResurrectSkill"),
} }
MonStats2[record.Key] = record MonStats2[record.Key] = record
} }
if d.Err != nil {
panic(d.Err)
}
log.Printf("Loaded %d MonStats2 records", len(MonStats2)) log.Printf("Loaded %d MonStats2 records", len(MonStats2))
} }

View File

@ -10,11 +10,11 @@ import (
type ObjectLookupRecord struct { type ObjectLookupRecord struct {
Act int Act int
Type d2enum.ObjectType Type d2enum.ObjectType
Id int //nolint:golint Id is the right key Id int //nolint:golint,stylecheck // Id is the right key
Name string Name string
Description string Description string
ObjectsTxtId int //nolint:golint Id is the right key ObjectsTxtId int //nolint:golint,stylecheck // Id is the right key
MonstatsTxtId int //nolint:golint Id is the right key MonstatsTxtId int //nolint:golint,stylecheck // Id is the right key
Direction int Direction int
Base string Base string
Token string Token string

View File

@ -23,9 +23,14 @@ func LoadObjectTypes(objectTypeData []byte) {
count := streamReader.GetInt32() count := streamReader.GetInt32()
ObjectTypes = make([]ObjectTypeRecord, count) ObjectTypes = make([]ObjectTypeRecord, count)
const (
nameSize = 32
tokenSize = 20
)
for i := range ObjectTypes { for i := range ObjectTypes {
nameBytes := streamReader.ReadBytes(32) nameBytes := streamReader.ReadBytes(nameSize)
tokenBytes := streamReader.ReadBytes(20) tokenBytes := streamReader.ReadBytes(tokenSize)
ObjectTypes[i] = ObjectTypeRecord{ ObjectTypes[i] = ObjectTypeRecord{
Name: strings.TrimSpace(strings.ReplaceAll(string(nameBytes), string(0), "")), Name: strings.TrimSpace(strings.ReplaceAll(string(nameBytes), string(0), "")),
Token: strings.TrimSpace(strings.ReplaceAll(string(tokenBytes), string(0), "")), Token: strings.TrimSpace(strings.ReplaceAll(string(tokenBytes), string(0), "")),

View File

@ -25,7 +25,7 @@ type ObjectRecord struct {
token string // refers to what graphics this object uses token string // refers to what graphics this object uses
// Don't use, index by line number // Don't use, index by line number
id int //nolint:golint it's ok that it's called Id, unused indexed by line number instead id int //nolint:golint,stylecheck // unused, indexed by line number instead
SpawnMax int // unused? SpawnMax int // unused?
TrapProbability int // unused TrapProbability int // unused

View File

@ -125,35 +125,39 @@ var SuperUniques map[string]*SuperUniqueRecord
// LoadSuperUniques loads SuperUniqueRecords from superuniques.txt // LoadSuperUniques loads SuperUniqueRecords from superuniques.txt
func LoadSuperUniques(file []byte) { func LoadSuperUniques(file []byte) {
dictionary := d2common.LoadDataDictionary(string(file)) SuperUniques = make(map[string]*SuperUniqueRecord)
SuperUniques = make(map[string]*SuperUniqueRecord, len(dictionary.Data))
for idx := range dictionary.Data { d := d2common.LoadDataDictionary(file)
for d.Next() {
record := &SuperUniqueRecord{ record := &SuperUniqueRecord{
Key: dictionary.GetString("Superunique", idx), Key: d.String("Superunique"),
Name: dictionary.GetString("Name", idx), Name: d.String("Name"),
Class: dictionary.GetString("Class", idx), Class: d.String("Class"),
HcIdx: dictionary.GetString("hcIdx", idx), HcIdx: d.String("hcIdx"),
MonSound: dictionary.GetString("MonSound", idx), MonSound: d.String("MonSound"),
Mod: [3]int{ Mod: [3]int{
dictionary.GetNumber("Mod1", idx), d.Number("Mod1"),
dictionary.GetNumber("Mod2", idx), d.Number("Mod2"),
dictionary.GetNumber("Mod3", idx), d.Number("Mod3"),
}, },
MinGrp: dictionary.GetNumber("MinGrp", idx), MinGrp: d.Number("MinGrp"),
MaxGrp: dictionary.GetNumber("MaxGrp", idx), MaxGrp: d.Number("MaxGrp"),
IsExpansion: dictionary.GetNumber("EClass", idx) == 1, IsExpansion: d.Bool("EClass"),
AutoPosition: dictionary.GetNumber("AutoPos", idx) == 1, AutoPosition: d.Bool("AutoPos"),
Stacks: dictionary.GetNumber("Stacks", idx) == 1, Stacks: d.Bool("Stacks"),
TreasureClassNormal: dictionary.GetString("TC", idx), TreasureClassNormal: d.String("TC"),
TreasureClassNightmare: dictionary.GetString("TC(N)", idx), TreasureClassNightmare: d.String("TC(N)"),
TreasureClassHell: dictionary.GetString("TC(H)", idx), TreasureClassHell: d.String("TC(H)"),
UTransNormal: dictionary.GetString("Utrans", idx), UTransNormal: d.String("Utrans"),
UTransNightmare: dictionary.GetString("Utrans(N)", idx), UTransNightmare: d.String("Utrans(N)"),
UTransHell: dictionary.GetString("Utrans(H)", idx), UTransHell: d.String("Utrans(H)"),
} }
SuperUniques[record.Key] = record SuperUniques[record.Key] = record
} }
if d.Err != nil {
panic(d.Err)
}
log.Printf("Loaded %d SuperUnique records", len(SuperUniques)) log.Printf("Loaded %d SuperUnique records", len(SuperUniques))
} }

View File

@ -9,37 +9,35 @@ import (
// UniqueItemRecord is a representation of a row from uniqueitems.txt // UniqueItemRecord is a representation of a row from uniqueitems.txt
type UniqueItemRecord struct { type UniqueItemRecord struct {
Name string Properties [12]UniqueItemProperty
Version int // 0 = classic pre 1.07, 1 = 1.07-1.11, 100 = expansion
Enabled bool // if false, this record won't be loaded (should always be true...)
Ladder bool // if true, can only be found on ladder and not in single player / tcp/ip
Rarity int // 1-255, higher is more common (ironically...)
NoLimit bool // if true, can drop more than once per game
// (if false, can only drop once per game; if it would drop,
// instead a rare item with enhanced durability drops)
Level int // item's level, can only be dropped by monsters / recipes / vendors / objects of this level or higher
// otherwise they would drop a rare item with enhanced durability
RequiredLevel int // character must have this level to use this item
Code string // three letter code, points to a record in Weapons, Armor, or Misc
TypeDescription string
UberDescription string
SingleCopy bool // if true, player can only hold one of these. can't trade it or pick it up
CostMultiplier int // base price is multiplied by this when sold, repaired or bought
CostAdd int // after multiplied by above, this much is added to the price
Name string
Code string // three letter code, points to a record in Weapons, Armor, or Misc
TypeDescription string
UberDescription string
CharacterGfxTransform string // palette shift applied to this items gfx when held and when CharacterGfxTransform string // palette shift applied to this items gfx when held and when
// on the ground (flippy). Points to a record in Colors.txt // on the ground (flippy). Points to a record in Colors.txt
InventoryGfxTransform string // palette shift applied to the inventory gfx InventoryGfxTransform string // palette shift applied to the inventory gfx
FlippyFile string // if non-empty, overrides the base item's dropped gfx FlippyFile string // if non-empty, overrides the base item's dropped gfx
InventoryFile string // if non-empty, overrides the base item's inventory gfx InventoryFile string // if non-empty, overrides the base item's inventory gfx
DropSound string // if non-empty, overrides the base item's drop sound
UseSound string // if non-empty, overrides the sound played when item is used
DropSound string // if non-empty, overrides the base item's drop sound Version int // 0 = classic pre 1.07, 1 = 1.07-1.11, 100 = expansion
DropSfxFrame int // if non-empty, overrides the base item's frame at which the drop sound plays Rarity int // 1-255, higher is more common (ironically...)
UseSound string // if non-empty, overrides the sound played when item is used Level int // item's level, can only be dropped by monsters / recipes / vendors / objects of this level or higher
// otherwise they would drop a rare item with enhanced durability
RequiredLevel int // character must have this level to use this item
CostMultiplier int // base price is multiplied by this when sold, repaired or bought
CostAdd int // after multiplied by above, this much is added to the price
DropSfxFrame int // if non-empty, overrides the base item's frame at which the drop sound plays
Properties [12]UniqueItemProperty Enabled bool // if false, this record won't be loaded (should always be true...)
Ladder bool // if true, can only be found on ladder and not in single player / tcp/ip
NoLimit bool // if true, can drop more than once per game
// (if false, can only drop once per game; if it would drop,
// instead a rare item with enhanced durability drops)
SingleCopy bool // if true, player can only hold one of these. can't trade it or pick it up
} }
// UniqueItemProperty is describes a property of a unique item // UniqueItemProperty is describes a property of a unique item

View File

@ -6,8 +6,10 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
) )
var Weapons map[string]*ItemCommonRecord // Weapons stores all of the WeaponRecords
var Weapons map[string]*ItemCommonRecord //nolint:gochecknoglobals // Currently global by design, only written once
// LoadWeapons loads weapon records
func LoadWeapons(file []byte) { func LoadWeapons(file []byte) {
Weapons = LoadCommonItems(file, d2enum.InventoryItemTypeWeapon) Weapons = LoadCommonItems(file, d2enum.InventoryItemTypeWeapon)
log.Printf("Loaded %d weapons", len(Weapons)) log.Printf("Loaded %d weapons", len(Weapons))

View File

@ -174,7 +174,7 @@ const (
LevelPreset = "/data/global/excel/LvlPrest.txt" LevelPreset = "/data/global/excel/LvlPrest.txt"
LevelType = "/data/global/excel/LvlTypes.txt" LevelType = "/data/global/excel/LvlTypes.txt"
ObjectType = "/data/global/excel/objtype.bin" ObjectType = "/data/global/excel/objtype.bin"
LevelWarp = "/data/global/excel/LvlWarp.bin" LevelWarp = "/data/global/excel/LvlWarp.txt"
LevelDetails = "/data/global/excel/Levels.txt" LevelDetails = "/data/global/excel/Levels.txt"
LevelMaze = "/data/global/excel/LvlMaze.txt" LevelMaze = "/data/global/excel/LvlMaze.txt"
LevelSubstitutions = "/data/global/excel/LvlSub.txt" LevelSubstitutions = "/data/global/excel/LvlSub.txt"

View File

@ -1,6 +1,9 @@
package d2common package d2common
import ( import (
"bytes"
"encoding/csv"
"io"
"log" "log"
"strconv" "strconv"
"strings" "strings"
@ -8,74 +11,78 @@ import (
// DataDictionary represents a data file (Excel) // DataDictionary represents a data file (Excel)
type DataDictionary struct { type DataDictionary struct {
FieldNameLookup map[string]int lookup map[string]int
Data [][]string r *csv.Reader
record []string
Err error
} }
// LoadDataDictionary loads the contents of a spreadsheet style txt file // LoadDataDictionary loads the contents of a spreadsheet style txt file
func LoadDataDictionary(text string) *DataDictionary { func LoadDataDictionary(buf []byte) *DataDictionary {
result := &DataDictionary{} cr := csv.NewReader(bytes.NewReader(buf))
lines := strings.Split(text, "\r\n") cr.Comma = '\t'
fileNames := strings.Split(lines[0], "\t") cr.ReuseRecord = true
result.FieldNameLookup = make(map[string]int)
for i, fieldName := range fileNames { fieldNames, err := cr.Read()
result.FieldNameLookup[fieldName] = i
}
result.Data = make([][]string, len(lines)-2)
for i, line := range lines[1:] {
if strings.TrimSpace(line) == "" {
continue
}
values := strings.Split(line, "\t")
if len(values) != len(result.FieldNameLookup) {
continue
}
result.Data[i] = values
}
return result
}
// GetString gets a string from the given column and row
func (v *DataDictionary) GetString(fieldName string, index int) string {
return v.Data[index][v.FieldNameLookup[fieldName]]
}
// GetNumber gets a number for the given column and row
func (v *DataDictionary) GetNumber(fieldName string, index int) int {
str := v.GetString(fieldName, index)
str = EmptyToZero(AsterToEmpty(str))
result, err := strconv.Atoi(str)
if err != nil { if err != nil {
log.Panic(err) panic(err)
} }
return result data := &DataDictionary{
lookup: make(map[string]int, len(fieldNames)),
r: cr,
}
for i, name := range fieldNames {
data.lookup[name] = i
}
return data
} }
// GetDelimitedList splits a delimited list from the given column and row // Next reads the next row, skips Expansion lines or
func (v *DataDictionary) GetDelimitedList(fieldName string, index int) []string { // returns false when the end of a file is reached or an error occured
unsplit := v.GetString(fieldName, index) func (d *DataDictionary) Next() bool {
var err error
d.record, err = d.r.Read()
if err == io.EOF {
return false
} else if err != nil {
d.Err = err
return false
}
// Comma delimited fields are quoted, not terribly pretty to fix that here but... if d.record[0] == "Expansion" {
unsplit = strings.TrimRight(unsplit, "\"") return d.Next()
unsplit = strings.TrimLeft(unsplit, "\"") }
return true
return strings.Split(unsplit, ",")
} }
// GetBool gets a bool value for the given column and row // String gets a string from the given column
func (v *DataDictionary) GetBool(fieldName string, index int) bool { func (d *DataDictionary) String(field string) string {
n := v.GetNumber(fieldName, index) return d.record[d.lookup[field]]
}
// Number gets a number for the given column
func (d *DataDictionary) Number(field string) int {
n, err := strconv.Atoi(d.String(field))
if err != nil {
return 0
}
return n
}
// List splits a delimited list from the given column
func (d *DataDictionary) List(field string) []string {
str := d.String(field)
return strings.Split(str, ",")
}
// Bool gets a bool value for the given column
func (d *DataDictionary) Bool(field string) bool {
n := d.Number(field)
if n > 1 { if n > 1 {
log.Panic("GetBool on non-bool field") log.Panic("Bool on non-bool field")
} }
return n == 1 return n == 1
} }