Monstat2 loading and a bunch of lint issues (#491)
* MonStat2 loader * Fix a bunch of lint issues in d2datadict
This commit is contained in:
parent
b29e7c8fdd
commit
aae565d528
|
@ -39,10 +39,10 @@ type AutoMapRecord struct {
|
||||||
// whatever you like..."
|
// whatever you like..."
|
||||||
// The values seem functional but naming conventions
|
// The values seem functional but naming conventions
|
||||||
// vary between LevelNames.
|
// vary between LevelNames.
|
||||||
//Type1 string
|
// Type1 string
|
||||||
//Type2 string
|
// Type2 string
|
||||||
//Type3 string
|
// Type3 string
|
||||||
//Type4 string // Note: I commented these out for now because they supposedly aren't useful see the LoadAutoMaps function.
|
// Type4 string // Note: I commented these out for now because they supposedly aren't useful see the LoadAutoMaps function.
|
||||||
|
|
||||||
// Frames determine the frame of the MaxiMap(s).dc6 that
|
// Frames determine the frame of the MaxiMap(s).dc6 that
|
||||||
// will be applied to the specified tiles. The frames
|
// will be applied to the specified tiles. The frames
|
||||||
|
@ -58,6 +58,7 @@ type AutoMapRecord struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// AutoMaps contains all data in AutoMap.txt.
|
// AutoMaps contains all data in AutoMap.txt.
|
||||||
|
//nolint:gochecknoglobals // Current design is to have these global
|
||||||
var AutoMaps []*AutoMapRecord
|
var AutoMaps []*AutoMapRecord
|
||||||
|
|
||||||
// LoadAutoMaps populates AutoMaps with the data from AutoMap.txt.
|
// LoadAutoMaps populates AutoMaps with the data from AutoMap.txt.
|
||||||
|
@ -71,9 +72,9 @@ func LoadAutoMaps(file []byte) {
|
||||||
|
|
||||||
// Construct records
|
// Construct records
|
||||||
AutoMaps = make([]*AutoMapRecord, len(d.Data))
|
AutoMaps = make([]*AutoMapRecord, len(d.Data))
|
||||||
|
|
||||||
for idx := range d.Data {
|
for idx := range d.Data {
|
||||||
// Row 2603 is a separator with all empty field values
|
if d.GetString("LevelName", idx) == "Expansion" {
|
||||||
if idx == 2603 {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +89,9 @@ func LoadAutoMaps(file []byte) {
|
||||||
//Type1: d.GetString("Type1", idx),
|
//Type1: d.GetString("Type1", idx),
|
||||||
//Type2: d.GetString("Type2", idx),
|
//Type2: d.GetString("Type2", idx),
|
||||||
//Type3: d.GetString("Type3", idx),
|
//Type3: d.GetString("Type3", idx),
|
||||||
//Type4: d.GetString("Type4", idx), // Note: I commented these out for now because they supposedly aren't useful see the AutoMapRecord struct.
|
//Type4: d.GetString("Type4", idx),
|
||||||
|
// Note: I commented these out for now because they supposedly
|
||||||
|
// aren't useful see the AutoMapRecord struct.
|
||||||
}
|
}
|
||||||
|
|
||||||
AutoMaps[idx].Frames = make([]int, len(frameFields))
|
AutoMaps[idx].Frames = make([]int, len(frameFields))
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Charecter stats
|
||||||
type CharStatsRecord struct {
|
type CharStatsRecord struct {
|
||||||
Class d2enum.Hero
|
Class d2enum.Hero
|
||||||
|
|
||||||
|
@ -59,8 +60,8 @@ var CharStats map[d2enum.Hero]*CharStatsRecord
|
||||||
var charStringMap map[string]d2enum.Hero
|
var charStringMap map[string]d2enum.Hero
|
||||||
var weaponTokenMap map[string]d2enum.WeaponClass
|
var weaponTokenMap map[string]d2enum.WeaponClass
|
||||||
|
|
||||||
|
//nolint:funlen // Makes no sense to split
|
||||||
func LoadCharStats(file []byte) {
|
func LoadCharStats(file []byte) {
|
||||||
|
|
||||||
charStringMap = map[string]d2enum.Hero{
|
charStringMap = map[string]d2enum.Hero{
|
||||||
"Amazon": d2enum.HeroAmazon,
|
"Amazon": d2enum.HeroAmazon,
|
||||||
"Barbarian": d2enum.HeroBarbarian,
|
"Barbarian": d2enum.HeroBarbarian,
|
||||||
|
@ -186,5 +187,6 @@ func LoadCharStats(file []byte) {
|
||||||
}
|
}
|
||||||
CharStats[record.Class] = record
|
CharStats[record.Class] = record
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Loaded %d CharStats records", len(CharStats))
|
log.Printf("Loaded %d CharStats records", len(CharStats))
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,7 +130,7 @@ type CubeRecipeItemProperty struct {
|
||||||
// string or an integer.
|
// string or an integer.
|
||||||
//
|
//
|
||||||
// See: https://d2mods.info/forum/kb/viewarticle?a=345
|
// See: https://d2mods.info/forum/kb/viewarticle?a=345
|
||||||
//"the parameter passed on to the associated property, this is used to pass skill IDs,
|
// "the parameter passed on to the associated property, this is used to pass skill IDs,
|
||||||
// state IDs, monster IDs, montype IDs and the like on to the properties that require
|
// state IDs, monster IDs, montype IDs and the like on to the properties that require
|
||||||
// them, these fields support calculations."
|
// them, these fields support calculations."
|
||||||
Param int // for properties that use parameters
|
Param int // for properties that use parameters
|
||||||
|
@ -153,7 +153,6 @@ var inputFields = []string{"input 1", "input 2", "input 3", "input 4", "input 5"
|
||||||
// LoadCubeRecipes populates CubeRecipes with
|
// LoadCubeRecipes populates CubeRecipes with
|
||||||
// the data from CubeMain.txt.
|
// the data from CubeMain.txt.
|
||||||
func LoadCubeRecipes(file []byte) {
|
func LoadCubeRecipes(file []byte) {
|
||||||
|
|
||||||
// Load data
|
// Load data
|
||||||
d := d2common.LoadDataDictionary(string(file))
|
d := d2common.LoadDataDictionary(string(file))
|
||||||
|
|
||||||
|
@ -188,7 +187,6 @@ func LoadCubeRecipes(file []byte) {
|
||||||
// Create outputs - output "", b, c
|
// Create outputs - output "", b, c
|
||||||
CubeRecipes[idx].Outputs = make([]CubeRecipeResult, 3)
|
CubeRecipes[idx].Outputs = make([]CubeRecipeResult, 3)
|
||||||
for o, outLabel := range outputLabels {
|
for o, outLabel := range outputLabels {
|
||||||
|
|
||||||
CubeRecipes[idx].Outputs[o] = CubeRecipeResult{
|
CubeRecipes[idx].Outputs[o] = CubeRecipeResult{
|
||||||
Item: newCubeRecipeItem(
|
Item: newCubeRecipeItem(
|
||||||
d.GetString(outputFields[o], idx)),
|
d.GetString(outputFields[o], idx)),
|
||||||
|
@ -201,7 +199,6 @@ func LoadCubeRecipes(file []byte) {
|
||||||
// Create properties - mod 1-5
|
// Create properties - mod 1-5
|
||||||
properties := make([]CubeRecipeItemProperty, 5)
|
properties := make([]CubeRecipeItemProperty, 5)
|
||||||
for p, prop := range propLabels {
|
for p, prop := range propLabels {
|
||||||
|
|
||||||
properties[p] = CubeRecipeItemProperty{
|
properties[p] = CubeRecipeItemProperty{
|
||||||
Code: d.GetString(outLabel+prop, idx),
|
Code: d.GetString(outLabel+prop, idx),
|
||||||
Chance: d.GetNumber(outLabel+prop+" chance", idx),
|
Chance: d.GetNumber(outLabel+prop+" chance", idx),
|
||||||
|
@ -213,7 +210,6 @@ func LoadCubeRecipes(file []byte) {
|
||||||
|
|
||||||
CubeRecipes[idx].Outputs[o].Properties = properties
|
CubeRecipes[idx].Outputs[o].Properties = properties
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Loaded %d CubeMainRecord records", len(CubeRecipes))
|
log.Printf("Loaded %d CubeMainRecord records", len(CubeRecipes))
|
||||||
|
@ -237,21 +233,26 @@ func newCubeRecipeItem(f string) CubeRecipeItem {
|
||||||
// Find the qty parameter if it was provided,
|
// Find the qty parameter if it was provided,
|
||||||
// convert to int and assign to item.Count
|
// convert to int and assign to item.Count
|
||||||
for idx, arg := range args {
|
for idx, arg := range args {
|
||||||
if strings.HasPrefix(arg, "qty") {
|
if !strings.HasPrefix(arg, "qty") {
|
||||||
count, err := strconv.Atoi(strings.Split(arg, "=")[1])
|
continue
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Error parsing item count:", err)
|
|
||||||
}
|
|
||||||
item.Count = count
|
|
||||||
|
|
||||||
// Remove the qty parameter
|
|
||||||
if idx != len(args)-1 {
|
|
||||||
args[idx] = args[len(args)-1]
|
|
||||||
}
|
|
||||||
args = args[:len(args)-1]
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
count, err := strconv.Atoi(strings.Split(arg, "=")[1])
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error parsing item count:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
item.Count = count
|
||||||
|
|
||||||
|
// Remove the qty parameter
|
||||||
|
if idx != len(args)-1 {
|
||||||
|
args[idx] = args[len(args)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
args = args[:len(args)-1]
|
||||||
|
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// No other arguments were provided
|
// No other arguments were provided
|
||||||
|
@ -272,10 +273,12 @@ func newCubeRecipeItem(f string) CubeRecipeItem {
|
||||||
func classFieldToEnum(f string) []d2enum.Hero {
|
func classFieldToEnum(f string) []d2enum.Hero {
|
||||||
split := splitFieldValue(f)
|
split := splitFieldValue(f)
|
||||||
enums := make([]d2enum.Hero, len(split))
|
enums := make([]d2enum.Hero, len(split))
|
||||||
|
|
||||||
for idx, class := range split {
|
for idx, class := range split {
|
||||||
if class == "" {
|
if class == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
switch class {
|
switch class {
|
||||||
case "bar":
|
case "bar":
|
||||||
enums[idx] = d2enum.HeroBarbarian
|
enums[idx] = d2enum.HeroBarbarian
|
||||||
|
@ -295,6 +298,7 @@ func classFieldToEnum(f string) []d2enum.Hero {
|
||||||
log.Fatalf("Unknown hero token: '%s'", class)
|
log.Fatalf("Unknown hero token: '%s'", class)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return enums
|
return enums
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,11 @@ import (
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DifficultyLevels contain the difficulty records for each difficulty
|
||||||
|
//nolint:gochecknoglobals // Current design is to have these global
|
||||||
var DifficultyLevels map[string]*DifficultyLevelRecord
|
var DifficultyLevels map[string]*DifficultyLevelRecord
|
||||||
|
|
||||||
|
// DifficultyLevelRecord contain the parameters that change for different difficultios
|
||||||
type DifficultyLevelRecord struct {
|
type DifficultyLevelRecord struct {
|
||||||
// Difficulty name. it is hardcoded and you cannot add new ones unless you do
|
// Difficulty name. it is hardcoded and you cannot add new ones unless you do
|
||||||
// some Code Edits
|
// some Code Edits
|
||||||
|
@ -30,7 +33,7 @@ type DifficultyLevelRecord struct {
|
||||||
// txt file...
|
// txt file...
|
||||||
|
|
||||||
// Not used. Pre 1.07 it was the percentage of magic, rare, set and unique
|
// Not used. Pre 1.07 it was the percentage of magic, rare, set and unique
|
||||||
// exceptional items dropped on this difficuly.
|
// exceptional items dropped on this difficulty.
|
||||||
DropChanceMagic int // UberCodeOddsGood
|
DropChanceMagic int // UberCodeOddsGood
|
||||||
DropChanceRare int // UberCodeOddsGood
|
DropChanceRare int // UberCodeOddsGood
|
||||||
DropChanceSet int // UberCodeOddsGood
|
DropChanceSet int // UberCodeOddsGood
|
||||||
|
@ -89,6 +92,7 @@ type DifficultyLevelRecord struct {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadDifficultyLevels is a loader for difficultylevels.txt
|
||||||
func LoadDifficultyLevels(file []byte) {
|
func LoadDifficultyLevels(file []byte) {
|
||||||
dict := d2common.LoadDataDictionary(string(file))
|
dict := d2common.LoadDataDictionary(string(file))
|
||||||
numRows := len(dict.Data)
|
numRows := len(dict.Data)
|
||||||
|
@ -119,5 +123,4 @@ func LoadDifficultyLevels(file []byte) {
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Loaded %d DifficultyLevel records", len(DifficultyLevels))
|
log.Printf("Loaded %d DifficultyLevel records", len(DifficultyLevels))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,6 +89,7 @@ func LoadExperienceBreakpoints(file []byte) {
|
||||||
d2enum.HeroPaladin: d.GetNumber("Paladin", idx),
|
d2enum.HeroPaladin: d.GetNumber("Paladin", idx),
|
||||||
d2enum.HeroSorceress: d.GetNumber("Sorceress", idx),
|
d2enum.HeroSorceress: d.GetNumber("Sorceress", idx),
|
||||||
}
|
}
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,5 +109,6 @@ func LoadExperienceBreakpoints(file []byte) {
|
||||||
|
|
||||||
ExperienceBreakpoints[record.Level] = record
|
ExperienceBreakpoints[record.Level] = record
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Loaded %d ExperienceBreakpoint records", len(ExperienceBreakpoints))
|
log.Printf("Loaded %d ExperienceBreakpoint records", len(ExperienceBreakpoints))
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,9 @@ type GemsRecord struct {
|
||||||
|
|
||||||
func LoadGems(file []byte) {
|
func LoadGems(file []byte) {
|
||||||
d := d2common.LoadDataDictionary(string(file))
|
d := d2common.LoadDataDictionary(string(file))
|
||||||
|
|
||||||
var Gems []*GemsRecord
|
var Gems []*GemsRecord
|
||||||
|
|
||||||
for idx := range d.Data {
|
for idx := range d.Data {
|
||||||
if d.GetString("name", idx) != "Expansion" {
|
if d.GetString("name", idx) != "Expansion" {
|
||||||
/*
|
/*
|
||||||
|
@ -105,5 +107,6 @@ func LoadGems(file []byte) {
|
||||||
Gems = append(Gems, gem)
|
Gems = append(Gems, gem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Loaded %d Gems records", len(Gems))
|
log.Printf("Loaded %d Gems records", len(Gems))
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
type HirelingRecord struct {
|
type HirelingRecord struct {
|
||||||
Hireling string
|
Hireling string
|
||||||
SubType string
|
SubType string
|
||||||
Id int
|
ID int
|
||||||
Class int
|
Class int
|
||||||
Act int
|
Act int
|
||||||
Difficulty int
|
Difficulty int
|
||||||
|
@ -84,11 +84,12 @@ type HirelingRecord struct {
|
||||||
func LoadHireling(file []byte) {
|
func LoadHireling(file []byte) {
|
||||||
d := d2common.LoadDataDictionary(string(file))
|
d := d2common.LoadDataDictionary(string(file))
|
||||||
var Hirelings []*HirelingRecord
|
var Hirelings []*HirelingRecord
|
||||||
|
|
||||||
for idx := range d.Data {
|
for idx := range d.Data {
|
||||||
hireling := &HirelingRecord{
|
hireling := &HirelingRecord{
|
||||||
Hireling: d.GetString("Hireling", idx),
|
Hireling: d.GetString("Hireling", idx),
|
||||||
SubType: d.GetString("SubType", idx),
|
SubType: d.GetString("SubType", idx),
|
||||||
Id: d.GetNumber("Id", idx),
|
ID: d.GetNumber("Id", idx),
|
||||||
Class: d.GetNumber("Class", idx),
|
Class: d.GetNumber("Class", idx),
|
||||||
Act: d.GetNumber("Act", idx),
|
Act: d.GetNumber("Act", idx),
|
||||||
Difficulty: d.GetNumber("Difficulty", idx),
|
Difficulty: d.GetNumber("Difficulty", idx),
|
||||||
|
@ -161,5 +162,6 @@ func LoadHireling(file []byte) {
|
||||||
}
|
}
|
||||||
Hirelings = append(Hirelings, hireling)
|
Hirelings = append(Hirelings, hireling)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Loaded %d Hireling records", len(Hirelings))
|
log.Printf("Loaded %d Hireling records", len(Hirelings))
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,26 +16,28 @@ var MagicSuffixRecords []*ItemAffixCommonRecord
|
||||||
|
|
||||||
var AffixMagicGroups []*ItemAffixCommonGroup
|
var AffixMagicGroups []*ItemAffixCommonGroup
|
||||||
|
|
||||||
var superType d2enum.ItemAffixSuperType
|
// LoadMagicPrefix loads MagicPrefix.txt
|
||||||
var subType d2enum.ItemAffixSubType
|
|
||||||
|
|
||||||
func LoadMagicPrefix(file []byte) {
|
func LoadMagicPrefix(file []byte) {
|
||||||
superType = d2enum.ItemAffixPrefix
|
superType := d2enum.ItemAffixPrefix
|
||||||
subType = d2enum.ItemAffixMagic
|
|
||||||
|
subType := d2enum.ItemAffixMagic
|
||||||
|
|
||||||
MagicPrefixDictionary, MagicPrefixRecords = loadDictionary(file, superType, subType)
|
MagicPrefixDictionary, MagicPrefixRecords = loadDictionary(file, superType, subType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadMagicSuffix loads MagicSuffix.txt
|
||||||
func LoadMagicSuffix(file []byte) {
|
func LoadMagicSuffix(file []byte) {
|
||||||
superType = d2enum.ItemAffixSuffix
|
superType := d2enum.ItemAffixSuffix
|
||||||
subType = d2enum.ItemAffixMagic
|
|
||||||
|
subType := d2enum.ItemAffixMagic
|
||||||
|
|
||||||
MagicSuffixDictionary, MagicSuffixRecords = loadDictionary(file, superType, subType)
|
MagicSuffixDictionary, MagicSuffixRecords = loadDictionary(file, superType, subType)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAffixString(t1 d2enum.ItemAffixSuperType, t2 d2enum.ItemAffixSubType) string {
|
func getAffixString(t1 d2enum.ItemAffixSuperType, t2 d2enum.ItemAffixSubType) string {
|
||||||
var name string = ""
|
var name string = ""
|
||||||
|
|
||||||
switch t2 {
|
if t2 == d2enum.ItemAffixMagic {
|
||||||
case d2enum.ItemAffixMagic:
|
|
||||||
name = "Magic"
|
name = "Magic"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +49,6 @@ func getAffixString(t1 d2enum.ItemAffixSuperType, t2 d2enum.ItemAffixSubType) st
|
||||||
}
|
}
|
||||||
|
|
||||||
return name
|
return name
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadDictionary(
|
func loadDictionary(
|
||||||
|
@ -59,6 +60,7 @@ func loadDictionary(
|
||||||
records := createItemAffixRecords(dict, superType, subType)
|
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(dict.Data), name)
|
||||||
|
|
||||||
return dict, records
|
return dict, records
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,8 +113,8 @@ func createItemAffixRecords(
|
||||||
subType d2enum.ItemAffixSubType,
|
subType d2enum.ItemAffixSubType,
|
||||||
) []*ItemAffixCommonRecord {
|
) []*ItemAffixCommonRecord {
|
||||||
records := make([]*ItemAffixCommonRecord, 0)
|
records := make([]*ItemAffixCommonRecord, 0)
|
||||||
for index := range d.Data {
|
|
||||||
|
|
||||||
|
for index := range d.Data {
|
||||||
affix := &ItemAffixCommonRecord{
|
affix := &ItemAffixCommonRecord{
|
||||||
Name: d.GetString("Name", index),
|
Name: d.GetString("Name", index),
|
||||||
Version: d.GetNumber("version", index),
|
Version: d.GetNumber("version", index),
|
||||||
|
@ -179,6 +181,7 @@ func createItemAffixRecords(
|
||||||
|
|
||||||
records = append(records, affix)
|
records = append(records, affix)
|
||||||
}
|
}
|
||||||
|
|
||||||
return records
|
return records
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,14 +196,17 @@ func (g *ItemAffixCommonGroup) AddMember(a *ItemAffixCommonRecord) {
|
||||||
if g.Members == nil {
|
if g.Members == nil {
|
||||||
g.Members = make(map[string]*ItemAffixCommonRecord)
|
g.Members = make(map[string]*ItemAffixCommonRecord)
|
||||||
}
|
}
|
||||||
|
|
||||||
g.Members[a.Name] = a
|
g.Members[a.Name] = a
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *ItemAffixCommonGroup) GetTotalFrequency() int {
|
func (g *ItemAffixCommonGroup) GetTotalFrequency() int {
|
||||||
total := 0
|
total := 0
|
||||||
|
|
||||||
for _, affix := range g.Members {
|
for _, affix := range g.Members {
|
||||||
total += affix.Frequency
|
total += affix.Frequency
|
||||||
}
|
}
|
||||||
|
|
||||||
return total
|
return total
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,6 +255,8 @@ func (a *ItemAffixCommonRecord) ProbabilityToSpawn(qlvl int) float64 {
|
||||||
if (qlvl > a.MaxLevel) || (qlvl < a.Level) {
|
if (qlvl > a.MaxLevel) || (qlvl < a.Level) {
|
||||||
return 0.0
|
return 0.0
|
||||||
}
|
}
|
||||||
p := (float64)(a.Frequency) / (float64)(a.Group.GetTotalFrequency())
|
|
||||||
|
p := float64(a.Frequency) / float64(a.Group.GetTotalFrequency())
|
||||||
|
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,30 +181,35 @@ type ItemVendorParams struct {
|
||||||
MagicLevel uint8
|
MagicLevel uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loading Functions
|
|
||||||
var CommonItems map[string]*ItemCommonRecord
|
var CommonItems map[string]*ItemCommonRecord
|
||||||
|
|
||||||
func LoadCommonItems(file []byte, source d2enum.InventoryItemType) *map[string]*ItemCommonRecord {
|
func LoadCommonItems(file []byte, source d2enum.InventoryItemType) *map[string]*ItemCommonRecord {
|
||||||
if CommonItems == nil {
|
if CommonItems == nil {
|
||||||
CommonItems = make(map[string]*ItemCommonRecord)
|
CommonItems = make(map[string]*ItemCommonRecord)
|
||||||
}
|
}
|
||||||
|
|
||||||
items := make(map[string]*ItemCommonRecord)
|
items := make(map[string]*ItemCommonRecord)
|
||||||
data := strings.Split(string(file), "\r\n")
|
data := strings.Split(string(file), "\r\n")
|
||||||
mapping := MapHeaders(data[0])
|
mapping := MapHeaders(data[0])
|
||||||
|
|
||||||
for lineno, line := range data {
|
for lineno, line := range data {
|
||||||
if lineno == 0 {
|
if lineno == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if len(line) == 0 {
|
|
||||||
|
if line == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
rec := createCommonItemRecord(line, &mapping, source)
|
rec := createCommonItemRecord(line, &mapping, source)
|
||||||
items[rec.Code] = &rec
|
items[rec.Code] = &rec
|
||||||
CommonItems[rec.Code] = &rec
|
CommonItems[rec.Code] = &rec
|
||||||
}
|
}
|
||||||
|
|
||||||
return &items
|
return &items
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:funlen // Makes no sens to split
|
||||||
func createCommonItemRecord(line string, mapping *map[string]int, source d2enum.InventoryItemType) ItemCommonRecord {
|
func createCommonItemRecord(line string, mapping *map[string]int, source d2enum.InventoryItemType) ItemCommonRecord {
|
||||||
r := strings.Split(line, "\t")
|
r := strings.Split(line, "\t")
|
||||||
result := ItemCommonRecord{
|
result := ItemCommonRecord{
|
||||||
|
@ -363,6 +368,7 @@ func createCommonItemRecord(line string, mapping *map[string]int, source d2enum.
|
||||||
|
|
||||||
Multibuy: MapLoadBool(&r, mapping, "multibuy"),
|
Multibuy: MapLoadBool(&r, mapping, "multibuy"),
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -398,6 +404,7 @@ func createItemVendorParams(r *[]string, mapping *map[string]int) map[string]*It
|
||||||
}
|
}
|
||||||
result[name] = &wvp
|
result[name] = &wvp
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -407,5 +414,6 @@ func createItemUsageStats(r *[]string, mapping *map[string]int) [3]ItemUsageStat
|
||||||
result[i].Stat = MapLoadString(r, mapping, "stat"+strconv.Itoa(i))
|
result[i].Stat = MapLoadString(r, mapping, "stat"+strconv.Itoa(i))
|
||||||
result[i].Calc = d2common.CalcString(MapLoadString(r, mapping, "calc"+strconv.Itoa(i)))
|
result[i].Calc = d2common.CalcString(MapLoadString(r, mapping, "calc"+strconv.Itoa(i)))
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,10 +115,10 @@ const (
|
||||||
// just adds the stat to the unit directly
|
// just adds the stat to the unit directly
|
||||||
OpDefault = OperatorType(iota)
|
OpDefault = OperatorType(iota)
|
||||||
|
|
||||||
// adds opstat.base * statvalue / 100 to the opstat.
|
// Op1 adds opstat.base * statvalue / 100 to the opstat.
|
||||||
Op1
|
Op1
|
||||||
|
|
||||||
// adds (statvalue * basevalue) / (2 ^ param) to the opstat
|
// Op2 adds (statvalue * basevalue) / (2 ^ param) to the opstat
|
||||||
// this does not work properly with any stat other then level because of the
|
// this does not work properly with any stat other then level because of the
|
||||||
// way this is updated, it is only refreshed when you re-equip the item,
|
// way this is updated, it is only refreshed when you re-equip the item,
|
||||||
// your character is saved or you level up, similar to passive skills, just
|
// your character is saved or you level up, similar to passive skills, just
|
||||||
|
@ -127,56 +127,56 @@ const (
|
||||||
// description every frame, while the values remain unchanged serverside.
|
// description every frame, while the values remain unchanged serverside.
|
||||||
Op2
|
Op2
|
||||||
|
|
||||||
// this is a percentage based version of op #2
|
// Op3 is a percentage based version of op #2
|
||||||
// look at op #2 for information about the formula behind it, just
|
// look at op #2 for information about the formula behind it, just
|
||||||
// remember the stat is increased by a percentage rather then by adding
|
// remember the stat is increased by a percentage rather then by adding
|
||||||
// an integer.
|
// an integer.
|
||||||
Op3
|
Op3
|
||||||
|
|
||||||
// this works the same way op #2 works, however the stat bonus is
|
// Op4 works the same way op #2 works, however the stat bonus is
|
||||||
// added to the item and not to the player (so that +defense per level
|
// added to the item and not to the player (so that +defense per level
|
||||||
// properly adds the defense to the armor and not to the character
|
// properly adds the defense to the armor and not to the character
|
||||||
// directly!)
|
// directly!)
|
||||||
Op4
|
Op4
|
||||||
|
|
||||||
// this works like op #4 but is percentage based, it is used for percentage
|
// Op5 works like op #4 but is percentage based, it is used for percentage
|
||||||
// based increase of stats that are found on the item itself, and not stats
|
// based increase of stats that are found on the item itself, and not stats
|
||||||
// that are found on the character.
|
// that are found on the character.
|
||||||
Op5
|
Op5
|
||||||
|
|
||||||
// like for op #7, however this adds a plain bonus to the stat, and just
|
// Op6 works like for op #7, however this adds a plain bonus to the stat, and just
|
||||||
// like #7 it also doesn't work so I won't bother to explain the arithmetic
|
// like #7 it also doesn't work so I won't bother to explain the arithmetic
|
||||||
// behind it either.
|
// behind it either.
|
||||||
Op6
|
Op6
|
||||||
|
|
||||||
// this is used to increase a stat based on the current daytime of the game
|
// Op7 is used to increase a stat based on the current daytime of the game
|
||||||
// world by a percentage, there is no need to explain the arithmetics
|
// world by a percentage, there is no need to explain the arithmetics
|
||||||
// behind it because frankly enough it just doesn't work serverside, it
|
// behind it because frankly enough it just doesn't work serverside, it
|
||||||
// only updates clientside so this op is essentially useless.
|
// only updates clientside so this op is essentially useless.
|
||||||
Op7
|
Op7
|
||||||
|
|
||||||
// hardcoded to work only with maxmana, this will apply the proper amount
|
// Op8 hardcoded to work only with maxmana, this will apply the proper amount
|
||||||
// of mana to your character based on CharStats.txt for the amount of energy
|
// of mana to your character based on CharStats.txt for the amount of energy
|
||||||
// the stat added (doesn't work for non characters)
|
// the stat added (doesn't work for non characters)
|
||||||
Op8
|
Op8
|
||||||
|
|
||||||
// hardcoded to work only with maxhp and maxstamina, this will apply the
|
// Op9 hardcoded to work only with maxhp and maxstamina, this will apply the
|
||||||
// proper amount of maxhp and maxstamina to your character based on
|
// proper amount of maxhp and maxstamina to your character based on
|
||||||
// CharStats.txt for the amount of vitality the stat added (doesn't work
|
// CharStats.txt for the amount of vitality the stat added (doesn't work
|
||||||
// for non characters)
|
// for non characters)
|
||||||
Op9
|
Op9
|
||||||
|
|
||||||
// doesn't do anything, this has no switch case in the op function.
|
// Op10 doesn't do anything, this has no switch case in the op function.
|
||||||
Op10
|
Op10
|
||||||
|
|
||||||
// adds opstat.base * statvalue / 100 similar to 1 and 13, the code just
|
// Op11 adds opstat.base * statvalue / 100 similar to 1 and 13, the code just
|
||||||
// does a few more checks
|
// does a few more checks
|
||||||
Op11
|
Op11
|
||||||
|
|
||||||
// doesn't do anything, this has no switch case in the op function.
|
// Op12 doesn't do anything, this has no switch case in the op function.
|
||||||
Op12
|
Op12
|
||||||
|
|
||||||
// adds opstat.base * statvalue / 100 to the value of opstat, this is
|
// Op13 adds opstat.base * statvalue / 100 to the value of opstat, this is
|
||||||
// useable only on items it will not apply the bonus to other unit types
|
// useable only on items it will not apply the bonus to other unit types
|
||||||
// (this is why it is used for +% durability, +% level requirement,
|
// (this is why it is used for +% durability, +% level requirement,
|
||||||
// +% damage, +% defense ).
|
// +% damage, +% defense ).
|
||||||
|
@ -241,6 +241,7 @@ stuff
|
||||||
|
|
||||||
var ItemStatCosts map[string]*ItemStatCostRecord
|
var ItemStatCosts map[string]*ItemStatCostRecord
|
||||||
|
|
||||||
|
// LoadItemStatCosts loads ItemStatCostRecord's from text
|
||||||
func LoadItemStatCosts(file []byte) {
|
func LoadItemStatCosts(file []byte) {
|
||||||
d := d2common.LoadDataDictionary(string(file))
|
d := d2common.LoadDataDictionary(string(file))
|
||||||
numRecords := len(d.Data)
|
numRecords := len(d.Data)
|
||||||
|
@ -317,5 +318,6 @@ func LoadItemStatCosts(file []byte) {
|
||||||
|
|
||||||
ItemStatCosts[record.Name] = record
|
ItemStatCosts[record.Name] = record
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Loaded %d ItemStatCost records", len(ItemStatCosts))
|
log.Printf("Loaded %d ItemStatCost records", len(ItemStatCosts))
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ type LevelMazeDetailsRecord struct {
|
||||||
// ID from Levels.txt
|
// ID from Levels.txt
|
||||||
// NOTE: Cave 1 is the Den of Evil, its associated treasure level is quest
|
// NOTE: Cave 1 is the Den of Evil, its associated treasure level is quest
|
||||||
// only.
|
// only.
|
||||||
LevelId int // Level
|
LevelID int // Level
|
||||||
|
|
||||||
// the minimum number of .ds1 map sections that will make up the maze in
|
// the minimum number of .ds1 map sections that will make up the maze in
|
||||||
// Normal, Nightmare and Hell difficulties.
|
// Normal, Nightmare and Hell difficulties.
|
||||||
|
@ -36,21 +36,24 @@ type LevelMazeDetailsRecord struct {
|
||||||
|
|
||||||
var LevelMazeDetails map[int]*LevelMazeDetailsRecord
|
var LevelMazeDetails map[int]*LevelMazeDetailsRecord
|
||||||
|
|
||||||
|
// LoadLevelMazeDetails loads LevelMazeDetailsRecords from text file
|
||||||
func LoadLevelMazeDetails(file []byte) {
|
func LoadLevelMazeDetails(file []byte) {
|
||||||
dict := d2common.LoadDataDictionary(string(file))
|
dict := d2common.LoadDataDictionary(string(file))
|
||||||
numRecords := len(dict.Data)
|
numRecords := len(dict.Data)
|
||||||
LevelMazeDetails = make(map[int]*LevelMazeDetailsRecord, numRecords)
|
LevelMazeDetails = make(map[int]*LevelMazeDetailsRecord, numRecords)
|
||||||
|
|
||||||
for idx := range dict.Data {
|
for idx := range dict.Data {
|
||||||
record := &LevelMazeDetailsRecord{
|
record := &LevelMazeDetailsRecord{
|
||||||
Name: dict.GetString("Name", idx),
|
Name: dict.GetString("Name", idx),
|
||||||
LevelId: dict.GetNumber("Level", idx),
|
LevelID: dict.GetNumber("Level", idx),
|
||||||
NumRoomsNormal: dict.GetNumber("Rooms", idx),
|
NumRoomsNormal: dict.GetNumber("Rooms", idx),
|
||||||
NumRoomsNightmare: dict.GetNumber("Rooms(N)", idx),
|
NumRoomsNightmare: dict.GetNumber("Rooms(N)", idx),
|
||||||
NumRoomsHell: dict.GetNumber("Rooms(H)", idx),
|
NumRoomsHell: dict.GetNumber("Rooms(H)", idx),
|
||||||
SizeX: dict.GetNumber("SizeX", idx),
|
SizeX: dict.GetNumber("SizeX", idx),
|
||||||
SizeY: dict.GetNumber("SizeY", idx),
|
SizeY: dict.GetNumber("SizeY", idx),
|
||||||
}
|
}
|
||||||
LevelMazeDetails[record.LevelId] = record
|
LevelMazeDetails[record.LevelID] = record
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Loaded %d LevelMazeDetails records", len(LevelMazeDetails))
|
log.Printf("Loaded %d LevelMazeDetails records", len(LevelMazeDetails))
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,8 @@ import (
|
||||||
|
|
||||||
type LevelPresetRecord struct {
|
type LevelPresetRecord struct {
|
||||||
Name string
|
Name string
|
||||||
DefinitionId int
|
DefinitionID int
|
||||||
LevelId int
|
LevelID int
|
||||||
Populate bool
|
Populate bool
|
||||||
Logicals bool
|
Logicals bool
|
||||||
Outdoors bool
|
Outdoors bool
|
||||||
|
@ -39,8 +39,8 @@ func createLevelPresetRecord(props []string) LevelPresetRecord {
|
||||||
}
|
}
|
||||||
result := LevelPresetRecord{
|
result := LevelPresetRecord{
|
||||||
Name: props[inc()],
|
Name: props[inc()],
|
||||||
DefinitionId: d2common.StringToInt(props[inc()]),
|
DefinitionID: d2common.StringToInt(props[inc()]),
|
||||||
LevelId: d2common.StringToInt(props[inc()]),
|
LevelID: d2common.StringToInt(props[inc()]),
|
||||||
Populate: d2common.StringToUint8(props[inc()]) == 1,
|
Populate: d2common.StringToUint8(props[inc()]) == 1,
|
||||||
Logicals: d2common.StringToUint8(props[inc()]) == 1,
|
Logicals: d2common.StringToUint8(props[inc()]) == 1,
|
||||||
Outdoors: d2common.StringToUint8(props[inc()]) == 1,
|
Outdoors: d2common.StringToUint8(props[inc()]) == 1,
|
||||||
|
@ -66,31 +66,39 @@ func createLevelPresetRecord(props []string) LevelPresetRecord {
|
||||||
Beta: d2common.StringToUint8(props[inc()]) == 1,
|
Beta: d2common.StringToUint8(props[inc()]) == 1,
|
||||||
Expansion: d2common.StringToUint8(props[inc()]) == 1,
|
Expansion: d2common.StringToUint8(props[inc()]) == 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
var LevelPresets map[int]LevelPresetRecord
|
var LevelPresets map[int]LevelPresetRecord
|
||||||
|
|
||||||
|
// LoadLevelPresets loads level presets from text file
|
||||||
func LoadLevelPresets(file []byte) {
|
func LoadLevelPresets(file []byte) {
|
||||||
LevelPresets = make(map[int]LevelPresetRecord)
|
LevelPresets = make(map[int]LevelPresetRecord)
|
||||||
data := strings.Split(string(file), "\r\n")[1:]
|
data := strings.Split(string(file), "\r\n")[1:]
|
||||||
|
|
||||||
for _, line := range data {
|
for _, line := range data {
|
||||||
if len(line) == 0 {
|
if line == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
props := strings.Split(line, "\t")
|
props := strings.Split(line, "\t")
|
||||||
|
|
||||||
if props[1] == "" {
|
if props[1] == "" {
|
||||||
continue // any line without a definition id is skipped (e.g. the "Expansion" line)
|
continue // any line without a definition id is skipped (e.g. the "Expansion" line)
|
||||||
}
|
}
|
||||||
|
|
||||||
rec := createLevelPresetRecord(props)
|
rec := createLevelPresetRecord(props)
|
||||||
LevelPresets[rec.DefinitionId] = rec
|
LevelPresets[rec.DefinitionID] = rec
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Loaded %d level presets", len(LevelPresets))
|
log.Printf("Loaded %d level presets", len(LevelPresets))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LevelPreset looks up a LevelPresetRecord by ID
|
||||||
func LevelPreset(id int) LevelPresetRecord {
|
func LevelPreset(id int) LevelPresetRecord {
|
||||||
for i := 0; i < len(LevelPresets); i++ {
|
for i := 0; i < len(LevelPresets); i++ {
|
||||||
if LevelPresets[i].DefinitionId == id {
|
if LevelPresets[i].DefinitionID == id {
|
||||||
return LevelPresets[i]
|
return LevelPresets[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ type LevelSubstitutionRecord struct {
|
||||||
// groups. If you count each row of a group starting from 0, then you'll
|
// groups. If you count each row of a group starting from 0, then you'll
|
||||||
// obtain what is written in Levels.txt, columns 'SubTheme', 'SubWaypoint'
|
// obtain what is written in Levels.txt, columns 'SubTheme', 'SubWaypoint'
|
||||||
// and 'SubShrine'. (added by Paul Siramy)
|
// and 'SubShrine'. (added by Paul Siramy)
|
||||||
Id int // Type
|
ID int // Type
|
||||||
|
|
||||||
// What .ds1 is being used.
|
// What .ds1 is being used.
|
||||||
File string // File
|
File string // File
|
||||||
|
@ -69,10 +69,9 @@ func LoadLevelSubstitutions(file []byte) {
|
||||||
LevelSubstitutions = make(map[int]*LevelSubstitutionRecord, numRecords)
|
LevelSubstitutions = make(map[int]*LevelSubstitutionRecord, numRecords)
|
||||||
|
|
||||||
for idx := range dict.Data {
|
for idx := range dict.Data {
|
||||||
|
|
||||||
record := &LevelSubstitutionRecord{
|
record := &LevelSubstitutionRecord{
|
||||||
Name: dict.GetString("Name", idx),
|
Name: dict.GetString("Name", idx),
|
||||||
Id: dict.GetNumber("Type", idx),
|
ID: dict.GetNumber("Type", idx),
|
||||||
File: dict.GetString("File", idx),
|
File: dict.GetString("File", idx),
|
||||||
IsExpansion: dict.GetNumber("Expansion", idx) > 0,
|
IsExpansion: dict.GetNumber("Expansion", idx) > 0,
|
||||||
BorderType: dict.GetNumber("BordType", idx),
|
BorderType: dict.GetNumber("BordType", idx),
|
||||||
|
@ -95,7 +94,8 @@ func LoadLevelSubstitutions(file []byte) {
|
||||||
GridMax4: dict.GetNumber("Max4", idx),
|
GridMax4: dict.GetNumber("Max4", idx),
|
||||||
}
|
}
|
||||||
|
|
||||||
LevelSubstitutions[record.Id] = record
|
LevelSubstitutions[record.ID] = record
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Loaded %d LevelSubstitution records", len(LevelSubstitutions))
|
log.Printf("Loaded %d LevelSubstitution records", len(LevelSubstitutions))
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
|
|
||||||
type LevelTypeRecord struct {
|
type LevelTypeRecord struct {
|
||||||
Name string
|
Name string
|
||||||
Id int
|
ID int
|
||||||
Files [32]string
|
Files [32]string
|
||||||
Beta bool
|
Beta bool
|
||||||
Act int
|
Act int
|
||||||
|
@ -21,29 +21,35 @@ var LevelTypes []LevelTypeRecord
|
||||||
func LoadLevelTypes(file []byte) {
|
func LoadLevelTypes(file []byte) {
|
||||||
data := strings.Split(string(file), "\r\n")[1:]
|
data := strings.Split(string(file), "\r\n")[1:]
|
||||||
LevelTypes = make([]LevelTypeRecord, len(data))
|
LevelTypes = make([]LevelTypeRecord, len(data))
|
||||||
|
|
||||||
for i, j := 0, 0; i < len(data); i, j = i+1, j+1 {
|
for i, j := 0, 0; i < len(data); i, j = i+1, j+1 {
|
||||||
idx := -1
|
idx := -1
|
||||||
inc := func() int {
|
inc := func() int {
|
||||||
idx++
|
idx++
|
||||||
return idx
|
return idx
|
||||||
}
|
}
|
||||||
if len(data[i]) == 0 {
|
|
||||||
|
if data[i] == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
parts := strings.Split(data[i], "\t")
|
parts := strings.Split(data[i], "\t")
|
||||||
|
|
||||||
if parts[0] == "Expansion" {
|
if parts[0] == "Expansion" {
|
||||||
j--
|
j--
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
LevelTypes[j].Name = parts[inc()]
|
LevelTypes[j].Name = parts[inc()]
|
||||||
LevelTypes[j].Id = d2common.StringToInt(parts[inc()])
|
LevelTypes[j].ID = d2common.StringToInt(parts[inc()])
|
||||||
|
|
||||||
for fileIdx := range LevelTypes[i].Files {
|
for fileIdx := range LevelTypes[i].Files {
|
||||||
LevelTypes[j].Files[fileIdx] = parts[inc()]
|
LevelTypes[j].Files[fileIdx] = parts[inc()]
|
||||||
if LevelTypes[j].Files[fileIdx] == "0" {
|
if LevelTypes[j].Files[fileIdx] == "0" {
|
||||||
LevelTypes[j].Files[fileIdx] = ""
|
LevelTypes[j].Files[fileIdx] = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LevelTypes[j].Beta = parts[inc()] != "1"
|
LevelTypes[j].Beta = parts[inc()] != "1"
|
||||||
LevelTypes[j].Act = d2common.StringToInt(parts[inc()])
|
LevelTypes[j].Act = d2common.StringToInt(parts[inc()])
|
||||||
LevelTypes[j].Expansion = parts[inc()] != "1"
|
LevelTypes[j].Expansion = parts[inc()] != "1"
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type LevelWarpRecord struct {
|
type LevelWarpRecord struct {
|
||||||
Id int32
|
ID int32
|
||||||
SelectX int32
|
SelectX int32
|
||||||
SelectY int32
|
SelectY int32
|
||||||
SelectDX int32
|
SelectDX int32
|
||||||
|
@ -21,16 +21,20 @@ type LevelWarpRecord struct {
|
||||||
Direction string
|
Direction string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:gochecknoglobals // Currently global by design, only written once
|
||||||
|
// LevelWarps loaded from txt records
|
||||||
var LevelWarps map[int]*LevelWarpRecord
|
var LevelWarps map[int]*LevelWarpRecord
|
||||||
|
|
||||||
|
// LoadLevelWarps loads LevelWarpRecord's from text file data
|
||||||
func LoadLevelWarps(levelWarpData []byte) {
|
func LoadLevelWarps(levelWarpData []byte) {
|
||||||
LevelWarps = make(map[int]*LevelWarpRecord)
|
LevelWarps = make(map[int]*LevelWarpRecord)
|
||||||
streamReader := d2common.CreateStreamReader(levelWarpData)
|
streamReader := d2common.CreateStreamReader(levelWarpData)
|
||||||
numRecords := int(streamReader.GetInt32())
|
numRecords := int(streamReader.GetInt32())
|
||||||
|
|
||||||
for i := 0; i < numRecords; i++ {
|
for i := 0; i < numRecords; i++ {
|
||||||
id := int(streamReader.GetInt32())
|
id := int(streamReader.GetInt32())
|
||||||
LevelWarps[id] = &LevelWarpRecord{}
|
LevelWarps[id] = &LevelWarpRecord{}
|
||||||
LevelWarps[id].Id = int32(id)
|
LevelWarps[id].ID = int32(id)
|
||||||
LevelWarps[id].SelectX = streamReader.GetInt32()
|
LevelWarps[id].SelectX = streamReader.GetInt32()
|
||||||
LevelWarps[id].SelectY = streamReader.GetInt32()
|
LevelWarps[id].SelectY = streamReader.GetInt32()
|
||||||
LevelWarps[id].SelectDX = streamReader.GetInt32()
|
LevelWarps[id].SelectDX = streamReader.GetInt32()
|
||||||
|
@ -44,5 +48,6 @@ func LoadLevelWarps(levelWarpData []byte) {
|
||||||
LevelWarps[id].Direction = string(streamReader.GetByte())
|
LevelWarps[id].Direction = string(streamReader.GetByte())
|
||||||
streamReader.SkipBytes(3)
|
streamReader.SkipBytes(3)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Loaded %d level warps", len(LevelWarps))
|
log.Printf("Loaded %d level warps", len(LevelWarps))
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ type LevelDetailsRecord struct {
|
||||||
// additional layers.
|
// additional layers.
|
||||||
AutomapIndex int // Layer
|
AutomapIndex int // Layer
|
||||||
|
|
||||||
// sizeX - SizeY in each difficuly. If this is a preset area this sets the
|
// sizeX - SizeY in each difficulty. If this is a preset area this sets the
|
||||||
// X size for the area. Othervise use the same value here that are used in
|
// X size for the area. Othervise use the same value here that are used in
|
||||||
// lvlprest.txt to set the size for the .ds1 file.
|
// lvlprest.txt to set the size for the .ds1 file.
|
||||||
SizeXNormal int // SizeX
|
SizeXNormal int // SizeX
|
||||||
|
@ -102,7 +102,7 @@ type LevelDetailsRecord struct {
|
||||||
// cycles, because they always use the light values specified in Intensity,
|
// cycles, because they always use the light values specified in Intensity,
|
||||||
// Red, Green, Blue. this field also controls whenever sounds will echo if
|
// Red, Green, Blue. this field also controls whenever sounds will echo if
|
||||||
// you're running the game with a sound card capable of it and have
|
// you're running the game with a sound card capable of it and have
|
||||||
// enviroment sound effects set to true.
|
// environment sound effects set to true.
|
||||||
IsInside bool // IsInside
|
IsInside bool // IsInside
|
||||||
|
|
||||||
// Setting for Level Generation: You have 3 possibilities here:
|
// Setting for Level Generation: You have 3 possibilities here:
|
||||||
|
@ -141,28 +141,28 @@ type LevelDetailsRecord struct {
|
||||||
// linked with, but the actuall number of Vis ( 0 - 7 ) is determined by
|
// linked with, but the actuall number of Vis ( 0 - 7 ) is determined by
|
||||||
// your actual map (the .ds1 fle).
|
// your actual map (the .ds1 fle).
|
||||||
// Example: Normally Cave levels are only using vis 0-3 and wilderness areas 4-7 .
|
// Example: Normally Cave levels are only using vis 0-3 and wilderness areas 4-7 .
|
||||||
LevelLinkId0 int // Vis0
|
LevelLinkID0 int // Vis0
|
||||||
LevelLinkId1 int // Vis1
|
LevelLinkID1 int // Vis1
|
||||||
LevelLinkId2 int // Vis2
|
LevelLinkID2 int // Vis2
|
||||||
LevelLinkId3 int // Vis3
|
LevelLinkID3 int // Vis3
|
||||||
LevelLinkId4 int // Vis4
|
LevelLinkID4 int // Vis4
|
||||||
LevelLinkId5 int // Vis5
|
LevelLinkID5 int // Vis5
|
||||||
LevelLinkId6 int // Vis6
|
LevelLinkID6 int // Vis6
|
||||||
LevelLinkId7 int // Vis7
|
LevelLinkID7 int // Vis7
|
||||||
|
|
||||||
// This controls the visual graphics then you move the mouse pointer over
|
// This controls the visual graphics then you move the mouse pointer over
|
||||||
// an entrance. To show the graphics you use an ID from lvlwarp.txt and the
|
// an entrance. To show the graphics you use an ID from lvlwarp.txt and the
|
||||||
// behavior on the graphics is controlled by lvlwarp.txt. Your Warps must
|
// behavior on the graphics is controlled by lvlwarp.txt. Your Warps must
|
||||||
// match your Vis.
|
// match your Vis.
|
||||||
// Example: If your level uses Vis 3,5,7 then you must also use Warp 3,5,7 .
|
// Example: If your level uses Vis 3,5,7 then you must also use Warp 3,5,7 .
|
||||||
WarpGraphicsId0 int // Warp0
|
WarpGraphicsID0 int // Warp0
|
||||||
WarpGraphicsId1 int // Warp1
|
WarpGraphicsID1 int // Warp1
|
||||||
WarpGraphicsId2 int // Warp2
|
WarpGraphicsID2 int // Warp2
|
||||||
WarpGraphicsId3 int // Warp3
|
WarpGraphicsID3 int // Warp3
|
||||||
WarpGraphicsId4 int // Warp4
|
WarpGraphicsID4 int // Warp4
|
||||||
WarpGraphicsId5 int // Warp5
|
WarpGraphicsID5 int // Warp5
|
||||||
WarpGraphicsId6 int // Warp6
|
WarpGraphicsID6 int // Warp6
|
||||||
WarpGraphicsId7 int // Warp7
|
WarpGraphicsID7 int // Warp7
|
||||||
|
|
||||||
// These settings handle the light intensity as well as its RGB components
|
// These settings handle the light intensity as well as its RGB components
|
||||||
LightIntensity int // Intensity
|
LightIntensity int // Intensity
|
||||||
|
@ -195,7 +195,7 @@ type LevelDetailsRecord struct {
|
||||||
|
|
||||||
// What quest is this level related to. This is the quest id (as example the
|
// What quest is this level related to. This is the quest id (as example the
|
||||||
// first quest Den of Evil are set to 1, since its the first quest).
|
// first quest Den of Evil are set to 1, since its the first quest).
|
||||||
QuestId int // Quest
|
QuestID int // Quest
|
||||||
|
|
||||||
// This sets the minimum distance from a VisX or WarpX location that a
|
// This sets the minimum distance from a VisX or WarpX location that a
|
||||||
// monster, object or tile can be spawned at. (also applies to waypoints and
|
// monster, object or tile can be spawned at. (also applies to waypoints and
|
||||||
|
@ -247,40 +247,40 @@ type LevelDetailsRecord struct {
|
||||||
// Hell. They tell the game which monster ID taken from MonStats.txt.
|
// Hell. They tell the game which monster ID taken from MonStats.txt.
|
||||||
// NOTE: you need to manually add from mon11 to mon25 and from nmon11 to
|
// NOTE: you need to manually add from mon11 to mon25 and from nmon11 to
|
||||||
// nmon25 !
|
// nmon25 !
|
||||||
MonsterId1Normal string // mon1
|
MonsterID1Normal string // mon1
|
||||||
MonsterId2Normal string // mon2
|
MonsterID2Normal string // mon2
|
||||||
MonsterId3Normal string // mon3
|
MonsterID3Normal string // mon3
|
||||||
MonsterId4Normal string // mon4
|
MonsterID4Normal string // mon4
|
||||||
MonsterId5Normal string // mon5
|
MonsterID5Normal string // mon5
|
||||||
MonsterId6Normal string // mon6
|
MonsterID6Normal string // mon6
|
||||||
MonsterId7Normal string // mon7
|
MonsterID7Normal string // mon7
|
||||||
MonsterId8Normal string // mon8
|
MonsterID8Normal string // mon8
|
||||||
MonsterId9Normal string // mon9
|
MonsterID9Normal string // mon9
|
||||||
MonsterId10Normal string // mon10
|
MonsterID10Normal string // mon10
|
||||||
|
|
||||||
MonsterId1Nightmare string // nmon1
|
MonsterID1Nightmare string // nmon1
|
||||||
MonsterId2Nightmare string // nmon2
|
MonsterID2Nightmare string // nmon2
|
||||||
MonsterId3Nightmare string // nmon3
|
MonsterID3Nightmare string // nmon3
|
||||||
MonsterId4Nightmare string // nmon4
|
MonsterID4Nightmare string // nmon4
|
||||||
MonsterId5Nightmare string // nmon5
|
MonsterID5Nightmare string // nmon5
|
||||||
MonsterId6Nightmare string // nmon6
|
MonsterID6Nightmare string // nmon6
|
||||||
MonsterId7Nightmare string // nmon7
|
MonsterID7Nightmare string // nmon7
|
||||||
MonsterId8Nightmare string // nmon8
|
MonsterID8Nightmare string // nmon8
|
||||||
MonsterId9Nightmare string // nmon9
|
MonsterID9Nightmare string // nmon9
|
||||||
MonsterId10Nightmare string // nmon10
|
MonsterID10Nightmare string // nmon10
|
||||||
|
|
||||||
// Gravestench - adding additional fields for Hell, original txt combined
|
// Gravestench - adding additional fields for Hell, original txt combined
|
||||||
// the nighmare and hell ID's stringo the same field
|
// the nighmare and hell ID's stringo the same field
|
||||||
MonsterId1Hell string // nmon1
|
MonsterID1Hell string // nmon1
|
||||||
MonsterId2Hell string // nmon2
|
MonsterID2Hell string // nmon2
|
||||||
MonsterId3Hell string // nmon3
|
MonsterID3Hell string // nmon3
|
||||||
MonsterId4Hell string // nmon4
|
MonsterID4Hell string // nmon4
|
||||||
MonsterId5Hell string // nmon5
|
MonsterID5Hell string // nmon5
|
||||||
MonsterId6Hell string // nmon6
|
MonsterID6Hell string // nmon6
|
||||||
MonsterId7Hell string // nmon7
|
MonsterID7Hell string // nmon7
|
||||||
MonsterId8Hell string // nmon8
|
MonsterID8Hell string // nmon8
|
||||||
MonsterId9Hell string // nmon9
|
MonsterID9Hell string // nmon9
|
||||||
MonsterId10Hell string // nmon10
|
MonsterID10Hell string // nmon10
|
||||||
|
|
||||||
// Give preference to monsters set to ranged=1 in MonStats.txt on Nightmare
|
// Give preference to monsters set to ranged=1 in MonStats.txt on Nightmare
|
||||||
// and Hell difficulties when picking something to spawn.
|
// and Hell difficulties when picking something to spawn.
|
||||||
|
@ -293,24 +293,24 @@ type LevelDetailsRecord struct {
|
||||||
// NOTE: you can allow umon1-25 to also work in Nightmare and Hell by
|
// NOTE: you can allow umon1-25 to also work in Nightmare and Hell by
|
||||||
// following this simple ASM edit
|
// following this simple ASM edit
|
||||||
// (https://d2mods.info/forum/viewtopic.php?f=8&t=53969&p=425179&hilit=umon#p425179)
|
// (https://d2mods.info/forum/viewtopic.php?f=8&t=53969&p=425179&hilit=umon#p425179)
|
||||||
MonsterUniqueId1 string // umon1
|
MonsterUniqueID1 string // umon1
|
||||||
MonsterUniqueId2 string // umon2
|
MonsterUniqueID2 string // umon2
|
||||||
MonsterUniqueId3 string // umon3
|
MonsterUniqueID3 string // umon3
|
||||||
MonsterUniqueId4 string // umon4
|
MonsterUniqueID4 string // umon4
|
||||||
MonsterUniqueId5 string // umon5
|
MonsterUniqueID5 string // umon5
|
||||||
MonsterUniqueId6 string // umon6
|
MonsterUniqueID6 string // umon6
|
||||||
MonsterUniqueId7 string // umon7
|
MonsterUniqueID7 string // umon7
|
||||||
MonsterUniqueId8 string // umon8
|
MonsterUniqueID8 string // umon8
|
||||||
MonsterUniqueId9 string // umon9
|
MonsterUniqueID9 string // umon9
|
||||||
MonsterUniqueId10 string // umon10
|
MonsterUniqueID10 string // umon10
|
||||||
|
|
||||||
// Critter Species 1-4. Uses the Id from monstats2.txt and only monsters
|
// Critter Species 1-4. Uses the Id from monstats2.txt and only monsters
|
||||||
// with critter column set to 1 can spawn here. critter column is also found
|
// with critter column set to 1 can spawn here. critter column is also found
|
||||||
// in monstats2.txt. Critters are in reality only present clientside.
|
// in monstats2.txt. Critters are in reality only present clientside.
|
||||||
MonsterCritterId1 string // cmon1
|
MonsterCritterID1 string // cmon1
|
||||||
MonsterCritterId2 string // cmon2
|
MonsterCritterID2 string // cmon2
|
||||||
MonsterCritterId3 string // cmon3
|
MonsterCritterID3 string // cmon3
|
||||||
MonsterCritterId4 string // cmon4
|
MonsterCritterID4 string // cmon4
|
||||||
|
|
||||||
// Controls the chance for a critter to spawn.
|
// Controls the chance for a critter to spawn.
|
||||||
MonsterCritter1SpawnChance int // cpct1
|
MonsterCritter1SpawnChance int // cpct1
|
||||||
|
@ -330,13 +330,13 @@ type LevelDetailsRecord struct {
|
||||||
// Themes
|
// Themes
|
||||||
|
|
||||||
// Referes to a entry in SoundEnviron.txt (for the Levels Music)
|
// Referes to a entry in SoundEnviron.txt (for the Levels Music)
|
||||||
SoundEnvironmentId int // SoundEnv
|
SoundEnvironmentID int // SoundEnv
|
||||||
|
|
||||||
// 255 means no Waipoint for this level, while others state the Waypoint' ID
|
// 255 means no Waipoint for this level, while others state the Waypoint' ID
|
||||||
// for the level
|
// for the level
|
||||||
// NOTE: you can switch waypoint destinations between areas this way, not
|
// NOTE: you can switch waypoint destinations between areas this way, not
|
||||||
// between acts however so don't even bother to try.
|
// between acts however so don't even bother to try.
|
||||||
WaypointId int // Waypoint
|
WaypointID int // Waypoint
|
||||||
|
|
||||||
// String Code for the Display name of the Level
|
// String Code for the Display name of the Level
|
||||||
LevelDisplayName string // LevelName
|
LevelDisplayName string // LevelName
|
||||||
|
@ -351,14 +351,14 @@ type LevelDetailsRecord struct {
|
||||||
|
|
||||||
// this field uses the ID of the ObjectGroup you want to Spawn in this Area,
|
// this field uses the ID of the ObjectGroup you want to Spawn in this Area,
|
||||||
// taken from Objgroup.txt.
|
// taken from Objgroup.txt.
|
||||||
ObjectGroupId0 int // ObjGrp0
|
ObjectGroupID0 int // ObjGrp0
|
||||||
ObjectGroupId1 int // ObjGrp1
|
ObjectGroupID1 int // ObjGrp1
|
||||||
ObjectGroupId2 int // ObjGrp2
|
ObjectGroupID2 int // ObjGrp2
|
||||||
ObjectGroupId3 int // ObjGrp3
|
ObjectGroupID3 int // ObjGrp3
|
||||||
ObjectGroupId4 int // ObjGrp4
|
ObjectGroupID4 int // ObjGrp4
|
||||||
ObjectGroupId5 int // ObjGrp5
|
ObjectGroupID5 int // ObjGrp5
|
||||||
ObjectGroupId6 int // ObjGrp6
|
ObjectGroupID6 int // ObjGrp6
|
||||||
ObjectGroupId7 int // ObjGrp7
|
ObjectGroupID7 int // ObjGrp7
|
||||||
|
|
||||||
// These fields indicates the chance for each object group to spawn (if you
|
// These fields indicates the chance for each object group to spawn (if you
|
||||||
// use ObjGrp0 then set ObjPrb0 to a value below 100)
|
// use ObjGrp0 then set ObjPrb0 to a value below 100)
|
||||||
|
@ -387,6 +387,7 @@ func GetLevelDetails(id int) *LevelDetailsRecord {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:funlen // Txt loader, makes no sense to split
|
||||||
func LoadLevelDetails(file []byte) {
|
func LoadLevelDetails(file []byte) {
|
||||||
dict := d2common.LoadDataDictionary(string(file))
|
dict := d2common.LoadDataDictionary(string(file))
|
||||||
numRecords := len(dict.Data)
|
numRecords := len(dict.Data)
|
||||||
|
@ -425,22 +426,22 @@ func LoadLevelDetails(file []byte) {
|
||||||
SubTheme: dict.GetNumber("SubTheme", idx),
|
SubTheme: dict.GetNumber("SubTheme", idx),
|
||||||
SubWaypoint: dict.GetNumber("SubWaypoint", idx),
|
SubWaypoint: dict.GetNumber("SubWaypoint", idx),
|
||||||
SubShrine: dict.GetNumber("SubShrine", idx),
|
SubShrine: dict.GetNumber("SubShrine", idx),
|
||||||
LevelLinkId0: dict.GetNumber("Vis0", idx),
|
LevelLinkID0: dict.GetNumber("Vis0", idx),
|
||||||
LevelLinkId1: dict.GetNumber("Vis1", idx),
|
LevelLinkID1: dict.GetNumber("Vis1", idx),
|
||||||
LevelLinkId2: dict.GetNumber("Vis2", idx),
|
LevelLinkID2: dict.GetNumber("Vis2", idx),
|
||||||
LevelLinkId3: dict.GetNumber("Vis3", idx),
|
LevelLinkID3: dict.GetNumber("Vis3", idx),
|
||||||
LevelLinkId4: dict.GetNumber("Vis4", idx),
|
LevelLinkID4: dict.GetNumber("Vis4", idx),
|
||||||
LevelLinkId5: dict.GetNumber("Vis5", idx),
|
LevelLinkID5: dict.GetNumber("Vis5", idx),
|
||||||
LevelLinkId6: dict.GetNumber("Vis6", idx),
|
LevelLinkID6: dict.GetNumber("Vis6", idx),
|
||||||
LevelLinkId7: dict.GetNumber("Vis7", idx),
|
LevelLinkID7: dict.GetNumber("Vis7", idx),
|
||||||
WarpGraphicsId0: dict.GetNumber("Warp0", idx),
|
WarpGraphicsID0: dict.GetNumber("Warp0", idx),
|
||||||
WarpGraphicsId1: dict.GetNumber("Warp1", idx),
|
WarpGraphicsID1: dict.GetNumber("Warp1", idx),
|
||||||
WarpGraphicsId2: dict.GetNumber("Warp2", idx),
|
WarpGraphicsID2: dict.GetNumber("Warp2", idx),
|
||||||
WarpGraphicsId3: dict.GetNumber("Warp3", idx),
|
WarpGraphicsID3: dict.GetNumber("Warp3", idx),
|
||||||
WarpGraphicsId4: dict.GetNumber("Warp4", idx),
|
WarpGraphicsID4: dict.GetNumber("Warp4", idx),
|
||||||
WarpGraphicsId5: dict.GetNumber("Warp5", idx),
|
WarpGraphicsID5: dict.GetNumber("Warp5", idx),
|
||||||
WarpGraphicsId6: dict.GetNumber("Warp6", idx),
|
WarpGraphicsID6: dict.GetNumber("Warp6", idx),
|
||||||
WarpGraphicsId7: dict.GetNumber("Warp7", idx),
|
WarpGraphicsID7: dict.GetNumber("Warp7", idx),
|
||||||
LightIntensity: dict.GetNumber("Intensity", idx),
|
LightIntensity: dict.GetNumber("Intensity", idx),
|
||||||
Red: dict.GetNumber("Red", idx),
|
Red: dict.GetNumber("Red", idx),
|
||||||
Green: dict.GetNumber("Green", idx),
|
Green: dict.GetNumber("Green", idx),
|
||||||
|
@ -449,7 +450,7 @@ func LoadLevelDetails(file []byte) {
|
||||||
PortalRepositionEnable: dict.GetNumber("Position", idx) > 0,
|
PortalRepositionEnable: dict.GetNumber("Position", idx) > 0,
|
||||||
SaveMonsterStates: dict.GetNumber("SaveMonsters", idx) > 0,
|
SaveMonsterStates: dict.GetNumber("SaveMonsters", idx) > 0,
|
||||||
SaveMerchantStates: dict.GetNumber("SaveMonsters", idx) > 0,
|
SaveMerchantStates: dict.GetNumber("SaveMonsters", idx) > 0,
|
||||||
QuestId: dict.GetNumber("Quest", idx),
|
QuestID: dict.GetNumber("Quest", idx),
|
||||||
WarpClearanceDistance: dict.GetNumber("WarpDist", idx),
|
WarpClearanceDistance: dict.GetNumber("WarpDist", idx),
|
||||||
MonsterLevelNormal: dict.GetNumber("MonLvl1", idx),
|
MonsterLevelNormal: dict.GetNumber("MonLvl1", idx),
|
||||||
MonsterLevelNightmare: dict.GetNumber("MonLvl2", idx),
|
MonsterLevelNightmare: dict.GetNumber("MonLvl2", idx),
|
||||||
|
@ -469,68 +470,68 @@ func LoadLevelDetails(file []byte) {
|
||||||
MonsterWanderEnable: dict.GetNumber("MonWndr", idx) > 0,
|
MonsterWanderEnable: dict.GetNumber("MonWndr", idx) > 0,
|
||||||
MonsterSpecialWalk: dict.GetNumber("MonSpcWalk", idx) > 0,
|
MonsterSpecialWalk: dict.GetNumber("MonSpcWalk", idx) > 0,
|
||||||
NumMonsterTypes: dict.GetNumber("NumMon", idx),
|
NumMonsterTypes: dict.GetNumber("NumMon", idx),
|
||||||
MonsterId1Normal: dict.GetString("mon1", idx),
|
MonsterID1Normal: dict.GetString("mon1", idx),
|
||||||
MonsterId2Normal: dict.GetString("mon2", idx),
|
MonsterID2Normal: dict.GetString("mon2", idx),
|
||||||
MonsterId3Normal: dict.GetString("mon3", idx),
|
MonsterID3Normal: dict.GetString("mon3", idx),
|
||||||
MonsterId4Normal: dict.GetString("mon4", idx),
|
MonsterID4Normal: dict.GetString("mon4", idx),
|
||||||
MonsterId5Normal: dict.GetString("mon5", idx),
|
MonsterID5Normal: dict.GetString("mon5", idx),
|
||||||
MonsterId6Normal: dict.GetString("mon6", idx),
|
MonsterID6Normal: dict.GetString("mon6", idx),
|
||||||
MonsterId7Normal: dict.GetString("mon7", idx),
|
MonsterID7Normal: dict.GetString("mon7", idx),
|
||||||
MonsterId8Normal: dict.GetString("mon8", idx),
|
MonsterID8Normal: dict.GetString("mon8", idx),
|
||||||
MonsterId9Normal: dict.GetString("mon9", idx),
|
MonsterID9Normal: dict.GetString("mon9", idx),
|
||||||
MonsterId10Normal: dict.GetString("mon10", idx),
|
MonsterID10Normal: dict.GetString("mon10", idx),
|
||||||
MonsterId1Nightmare: dict.GetString("nmon1", idx),
|
MonsterID1Nightmare: dict.GetString("nmon1", idx),
|
||||||
MonsterId2Nightmare: dict.GetString("nmon2", idx),
|
MonsterID2Nightmare: dict.GetString("nmon2", idx),
|
||||||
MonsterId3Nightmare: dict.GetString("nmon3", idx),
|
MonsterID3Nightmare: dict.GetString("nmon3", idx),
|
||||||
MonsterId4Nightmare: dict.GetString("nmon4", idx),
|
MonsterID4Nightmare: dict.GetString("nmon4", idx),
|
||||||
MonsterId5Nightmare: dict.GetString("nmon5", idx),
|
MonsterID5Nightmare: dict.GetString("nmon5", idx),
|
||||||
MonsterId6Nightmare: dict.GetString("nmon6", idx),
|
MonsterID6Nightmare: dict.GetString("nmon6", idx),
|
||||||
MonsterId7Nightmare: dict.GetString("nmon7", idx),
|
MonsterID7Nightmare: dict.GetString("nmon7", idx),
|
||||||
MonsterId8Nightmare: dict.GetString("nmon8", idx),
|
MonsterID8Nightmare: dict.GetString("nmon8", idx),
|
||||||
MonsterId9Nightmare: dict.GetString("nmon9", idx),
|
MonsterID9Nightmare: dict.GetString("nmon9", idx),
|
||||||
MonsterId10Nightmare: dict.GetString("nmon10", idx),
|
MonsterID10Nightmare: dict.GetString("nmon10", idx),
|
||||||
MonsterId1Hell: dict.GetString("nmon1", idx),
|
MonsterID1Hell: dict.GetString("nmon1", idx),
|
||||||
MonsterId2Hell: dict.GetString("nmon2", idx),
|
MonsterID2Hell: dict.GetString("nmon2", idx),
|
||||||
MonsterId3Hell: dict.GetString("nmon3", idx),
|
MonsterID3Hell: dict.GetString("nmon3", idx),
|
||||||
MonsterId4Hell: dict.GetString("nmon4", idx),
|
MonsterID4Hell: dict.GetString("nmon4", idx),
|
||||||
MonsterId5Hell: dict.GetString("nmon5", idx),
|
MonsterID5Hell: dict.GetString("nmon5", idx),
|
||||||
MonsterId6Hell: dict.GetString("nmon6", idx),
|
MonsterID6Hell: dict.GetString("nmon6", idx),
|
||||||
MonsterId7Hell: dict.GetString("nmon7", idx),
|
MonsterID7Hell: dict.GetString("nmon7", idx),
|
||||||
MonsterId8Hell: dict.GetString("nmon8", idx),
|
MonsterID8Hell: dict.GetString("nmon8", idx),
|
||||||
MonsterId9Hell: dict.GetString("nmon9", idx),
|
MonsterID9Hell: dict.GetString("nmon9", idx),
|
||||||
MonsterId10Hell: dict.GetString("nmon10", idx),
|
MonsterID10Hell: dict.GetString("nmon10", idx),
|
||||||
MonsterPreferRanged: dict.GetNumber("rangedspawn", idx) > 0,
|
MonsterPreferRanged: dict.GetNumber("rangedspawn", idx) > 0,
|
||||||
MonsterUniqueId1: dict.GetString("umon1", idx),
|
MonsterUniqueID1: dict.GetString("umon1", idx),
|
||||||
MonsterUniqueId2: dict.GetString("umon2", idx),
|
MonsterUniqueID2: dict.GetString("umon2", idx),
|
||||||
MonsterUniqueId3: dict.GetString("umon3", idx),
|
MonsterUniqueID3: dict.GetString("umon3", idx),
|
||||||
MonsterUniqueId4: dict.GetString("umon4", idx),
|
MonsterUniqueID4: dict.GetString("umon4", idx),
|
||||||
MonsterUniqueId5: dict.GetString("umon5", idx),
|
MonsterUniqueID5: dict.GetString("umon5", idx),
|
||||||
MonsterUniqueId6: dict.GetString("umon6", idx),
|
MonsterUniqueID6: dict.GetString("umon6", idx),
|
||||||
MonsterUniqueId7: dict.GetString("umon7", idx),
|
MonsterUniqueID7: dict.GetString("umon7", idx),
|
||||||
MonsterUniqueId8: dict.GetString("umon8", idx),
|
MonsterUniqueID8: dict.GetString("umon8", idx),
|
||||||
MonsterUniqueId9: dict.GetString("umon9", idx),
|
MonsterUniqueID9: dict.GetString("umon9", idx),
|
||||||
MonsterUniqueId10: dict.GetString("umon10", idx),
|
MonsterUniqueID10: dict.GetString("umon10", idx),
|
||||||
MonsterCritterId1: dict.GetString("cmon1", idx),
|
MonsterCritterID1: dict.GetString("cmon1", idx),
|
||||||
MonsterCritterId2: dict.GetString("cmon2", idx),
|
MonsterCritterID2: dict.GetString("cmon2", idx),
|
||||||
MonsterCritterId3: dict.GetString("cmon3", idx),
|
MonsterCritterID3: dict.GetString("cmon3", idx),
|
||||||
MonsterCritterId4: dict.GetString("cmon4", idx),
|
MonsterCritterID4: dict.GetString("cmon4", idx),
|
||||||
MonsterCritter1SpawnChance: dict.GetNumber("cpct1", idx),
|
MonsterCritter1SpawnChance: dict.GetNumber("cpct1", idx),
|
||||||
MonsterCritter2SpawnChance: dict.GetNumber("cpct2", idx),
|
MonsterCritter2SpawnChance: dict.GetNumber("cpct2", idx),
|
||||||
MonsterCritter3SpawnChance: dict.GetNumber("cpct3", idx),
|
MonsterCritter3SpawnChance: dict.GetNumber("cpct3", idx),
|
||||||
MonsterCritter4SpawnChance: dict.GetNumber("cpct4", idx),
|
MonsterCritter4SpawnChance: dict.GetNumber("cpct4", idx),
|
||||||
SoundEnvironmentId: dict.GetNumber("SoundEnv", idx),
|
SoundEnvironmentID: dict.GetNumber("SoundEnv", idx),
|
||||||
WaypointId: dict.GetNumber("Waypoint", idx),
|
WaypointID: dict.GetNumber("Waypoint", idx),
|
||||||
LevelDisplayName: dict.GetString("LevelName", idx),
|
LevelDisplayName: dict.GetString("LevelName", idx),
|
||||||
LevelWarpName: dict.GetString("LevelWarp", idx),
|
LevelWarpName: dict.GetString("LevelWarp", idx),
|
||||||
TitleImageName: dict.GetString("EntryFile", idx),
|
TitleImageName: dict.GetString("EntryFile", idx),
|
||||||
ObjectGroupId0: dict.GetNumber("ObjGrp0", idx),
|
ObjectGroupID0: dict.GetNumber("ObjGrp0", idx),
|
||||||
ObjectGroupId1: dict.GetNumber("ObjGrp1", idx),
|
ObjectGroupID1: dict.GetNumber("ObjGrp1", idx),
|
||||||
ObjectGroupId2: dict.GetNumber("ObjGrp2", idx),
|
ObjectGroupID2: dict.GetNumber("ObjGrp2", idx),
|
||||||
ObjectGroupId3: dict.GetNumber("ObjGrp3", idx),
|
ObjectGroupID3: dict.GetNumber("ObjGrp3", idx),
|
||||||
ObjectGroupId4: dict.GetNumber("ObjGrp4", idx),
|
ObjectGroupID4: dict.GetNumber("ObjGrp4", idx),
|
||||||
ObjectGroupId5: dict.GetNumber("ObjGrp5", idx),
|
ObjectGroupID5: dict.GetNumber("ObjGrp5", idx),
|
||||||
ObjectGroupId6: dict.GetNumber("ObjGrp6", idx),
|
ObjectGroupID6: dict.GetNumber("ObjGrp6", idx),
|
||||||
ObjectGroupId7: dict.GetNumber("ObjGrp7", idx),
|
ObjectGroupID7: dict.GetNumber("ObjGrp7", idx),
|
||||||
ObjectGroupSpawnChance0: dict.GetNumber("ObjPrb0", idx),
|
ObjectGroupSpawnChance0: dict.GetNumber("ObjPrb0", idx),
|
||||||
ObjectGroupSpawnChance1: dict.GetNumber("ObjPrb1", idx),
|
ObjectGroupSpawnChance1: dict.GetNumber("ObjPrb1", idx),
|
||||||
ObjectGroupSpawnChance2: dict.GetNumber("ObjPrb2", idx),
|
ObjectGroupSpawnChance2: dict.GetNumber("ObjPrb2", idx),
|
||||||
|
@ -542,5 +543,6 @@ func LoadLevelDetails(file []byte) {
|
||||||
}
|
}
|
||||||
LevelDetails[idx] = record
|
LevelDetails[idx] = record
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Loaded %d LevelDetails records", len(LevelDetails))
|
log.Printf("Loaded %d LevelDetails records", len(LevelDetails))
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,11 @@ import (
|
||||||
func MapHeaders(line string) map[string]int {
|
func MapHeaders(line string) map[string]int {
|
||||||
m := make(map[string]int)
|
m := make(map[string]int)
|
||||||
r := strings.Split(line, "\t")
|
r := strings.Split(line, "\t")
|
||||||
|
|
||||||
for index, header := range r {
|
for index, header := range r {
|
||||||
m[header] = index
|
m[header] = index
|
||||||
}
|
}
|
||||||
|
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +22,7 @@ func MapLoadInt(r *[]string, mapping *map[string]int, field string) int {
|
||||||
if ok {
|
if ok {
|
||||||
return d2common.StringToInt(d2common.EmptyToZero(d2common.AsterToEmpty((*r)[index])))
|
return d2common.StringToInt(d2common.EmptyToZero(d2common.AsterToEmpty((*r)[index])))
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +31,7 @@ func MapLoadString(r *[]string, mapping *map[string]int, field string) string {
|
||||||
if ok {
|
if ok {
|
||||||
return d2common.AsterToEmpty((*r)[index])
|
return d2common.AsterToEmpty((*r)[index])
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,5 +44,6 @@ func MapLoadUint8(r *[]string, mapping *map[string]int, field string) uint8 {
|
||||||
if ok {
|
if ok {
|
||||||
return d2common.StringToUint8(d2common.EmptyToZero(d2common.AsterToEmpty((*r)[index])))
|
return d2common.StringToUint8(d2common.EmptyToZero(d2common.AsterToEmpty((*r)[index])))
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,8 +109,10 @@ type MissileRecord struct {
|
||||||
|
|
||||||
UseAttackRating bool // if true, uses 'attack rating' to determine if it hits or misses
|
UseAttackRating bool // if true, uses 'attack rating' to determine if it hits or misses
|
||||||
// if false, has a 95% chance to hit.
|
// if false, has a 95% chance to hit.
|
||||||
AlwaysExplode bool // if true, always calls its collision function when it is destroyed, even if it doesn't hit anything
|
AlwaysExplode bool // if true, always calls its collision function when it is destroyed,
|
||||||
// note that some collision functions (lightning fury) seem to ignore this and always explode regardless of setting (requires investigation)
|
// even if it doesn't hit anything
|
||||||
|
// note that some collision functions (lightning fury)
|
||||||
|
// seem to ignore this and always explode regardless of setting (requires investigation)
|
||||||
|
|
||||||
ClientExplosion bool // if true, does not really exist
|
ClientExplosion bool // if true, does not really exist
|
||||||
// is only aesthetic / client side
|
// is only aesthetic / client side
|
||||||
|
@ -286,21 +288,26 @@ func createMissileRecord(line string) MissileRecord {
|
||||||
ClientSubMissile: [3]string{r[inc()], r[inc()], r[inc()]},
|
ClientSubMissile: [3]string{r[inc()], r[inc()], r[inc()]},
|
||||||
ClientHitSubMissile: [4]string{r[inc()], r[inc()], r[inc()], r[inc()]},
|
ClientHitSubMissile: [4]string{r[inc()], r[inc()], r[inc()], r[inc()]},
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:gochecknoglobals // Currently global by design, only written once
|
||||||
var Missiles map[int]*MissileRecord
|
var Missiles map[int]*MissileRecord
|
||||||
|
|
||||||
func LoadMissiles(file []byte) {
|
func LoadMissiles(file []byte) {
|
||||||
Missiles = make(map[int]*MissileRecord)
|
Missiles = make(map[int]*MissileRecord)
|
||||||
data := strings.Split(string(file), "\r\n")[1:]
|
data := strings.Split(string(file), "\r\n")[1:]
|
||||||
|
|
||||||
for _, line := range data {
|
for _, line := range data {
|
||||||
if len(line) == 0 {
|
if line == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
rec := createMissileRecord(line)
|
rec := createMissileRecord(line)
|
||||||
Missiles[rec.Id] = &rec
|
Missiles[rec.Id] = &rec
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Loaded %d missiles", len(Missiles))
|
log.Printf("Loaded %d missiles", len(Missiles))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,6 +316,7 @@ func loadMissileCalcParam(r *[]string, inc func() int) MissileCalcParam {
|
||||||
Param: d2common.StringToInt(d2common.EmptyToZero((*r)[inc()])),
|
Param: d2common.StringToInt(d2common.EmptyToZero((*r)[inc()])),
|
||||||
Desc: (*r)[inc()],
|
Desc: (*r)[inc()],
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,9 +326,11 @@ func loadMissileCalc(r *[]string, inc func() int, params int) MissileCalc {
|
||||||
Desc: (*r)[inc()],
|
Desc: (*r)[inc()],
|
||||||
}
|
}
|
||||||
result.Params = make([]MissileCalcParam, params)
|
result.Params = make([]MissileCalcParam, params)
|
||||||
|
|
||||||
for p := 0; p < params; p++ {
|
for p := 0; p < params; p++ {
|
||||||
result.Params[p] = loadMissileCalcParam(r, inc)
|
result.Params[p] = loadMissileCalcParam(r, inc)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,6 +342,7 @@ func loadMissileLight(r *[]string, inc func() int) MissileLight {
|
||||||
Green: d2common.StringToUint8(d2common.EmptyToZero((*r)[inc()])),
|
Green: d2common.StringToUint8(d2common.EmptyToZero((*r)[inc()])),
|
||||||
Blue: d2common.StringToUint8(d2common.EmptyToZero((*r)[inc()])),
|
Blue: d2common.StringToUint8(d2common.EmptyToZero((*r)[inc()])),
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -349,6 +360,7 @@ func loadMissileAnimation(r *[]string, inc func() int) MissileAnimation {
|
||||||
SubStartingFrame: d2common.StringToInt(d2common.EmptyToZero((*r)[inc()])),
|
SubStartingFrame: d2common.StringToInt(d2common.EmptyToZero((*r)[inc()])),
|
||||||
SubEndingFrame: d2common.StringToInt(d2common.EmptyToZero((*r)[inc()])),
|
SubEndingFrame: d2common.StringToInt(d2common.EmptyToZero((*r)[inc()])),
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -364,6 +376,7 @@ func loadMissileCollision(r *[]string, inc func() int) MissileCollision {
|
||||||
UseCollisionTimer: d2common.StringToInt(d2common.EmptyToZero((*r)[inc()])) == 1,
|
UseCollisionTimer: d2common.StringToInt(d2common.EmptyToZero((*r)[inc()])) == 1,
|
||||||
TimerFrames: d2common.StringToInt(d2common.EmptyToZero((*r)[inc()])),
|
TimerFrames: d2common.StringToInt(d2common.EmptyToZero((*r)[inc()])),
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -387,6 +400,7 @@ func loadMissileDamage(r *[]string, inc func() int) MissileDamage {
|
||||||
},
|
},
|
||||||
DamageSynergyPerCalc: d2common.CalcString((*r)[inc()]),
|
DamageSynergyPerCalc: d2common.CalcString((*r)[inc()]),
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,5 +415,6 @@ func loadMissileElementalDamage(r *[]string, inc func() int) MissileElementalDam
|
||||||
d2common.StringToInt(d2common.EmptyToZero((*r)[inc()])),
|
d2common.StringToInt(d2common.EmptyToZero((*r)[inc()])),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,26 +6,32 @@ import (
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//nolint:gochecknoglobals // Currently global by design, only written once
|
||||||
var MonPresets [][]string
|
var MonPresets [][]string
|
||||||
|
|
||||||
func LoadMonPresets(file []byte) {
|
func LoadMonPresets(file []byte) {
|
||||||
dict := d2common.LoadDataDictionary(string(file))
|
dict := d2common.LoadDataDictionary(string(file))
|
||||||
numRecords := len(dict.Data)
|
numRecords := len(dict.Data)
|
||||||
MonPresets = make([][]string, numRecords)
|
MonPresets = make([][]string, numRecords)
|
||||||
|
|
||||||
for idx := range MonPresets {
|
for idx := range MonPresets {
|
||||||
MonPresets[idx] = make([]string, numRecords)
|
MonPresets[idx] = make([]string, numRecords)
|
||||||
}
|
}
|
||||||
|
|
||||||
lastAct := 0
|
lastAct := 0
|
||||||
placeIdx := 0
|
placeIdx := 0
|
||||||
|
|
||||||
for dictIdx := range dict.Data {
|
for dictIdx := range dict.Data {
|
||||||
act := dict.GetNumber("Act", dictIdx)
|
act := dict.GetNumber("Act", dictIdx)
|
||||||
if act != lastAct {
|
if act != lastAct {
|
||||||
placeIdx = 0
|
placeIdx = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
MonPresets[act][placeIdx] = dict.GetString("Place", dictIdx)
|
MonPresets[act][placeIdx] = dict.GetString("Place", dictIdx)
|
||||||
placeIdx++
|
|
||||||
lastAct = act
|
lastAct = act
|
||||||
|
|
||||||
|
placeIdx++
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Loaded %d MonPreset records", len(MonPresets))
|
log.Printf("Loaded %d MonPreset records", len(MonPresets))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
// d2datadict contains loaders for the txt file data
|
||||||
package d2datadict
|
package d2datadict
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -37,7 +38,7 @@ type MonStatsRecord struct {
|
||||||
|
|
||||||
// this is the actual internal ID of the unit (this is what the ID pointer
|
// this is the actual internal ID of the unit (this is what the ID pointer
|
||||||
// actually points at) remember that no two units can have the same ID,
|
// actually points at) remember that no two units can have the same ID,
|
||||||
// this will result in lots of unpredictable behaviour and crashes so please
|
// this will result in lots of unpredictable behavior and crashes so please
|
||||||
// don’t do it. This 'HarcCodedInDeX' is used for several things, such as
|
// don’t do it. This 'HarcCodedInDeX' is used for several things, such as
|
||||||
// determining whenever the unit uses DCC or DC6 graphics (like mephisto
|
// determining whenever the unit uses DCC or DC6 graphics (like mephisto
|
||||||
// and the death animations of Diablo, the Maggoc Queen etc.), the hcIdx
|
// and the death animations of Diablo, the Maggoc Queen etc.), the hcIdx
|
||||||
|
@ -49,8 +50,8 @@ type MonStatsRecord struct {
|
||||||
// this column contains the ID pointer of the “base” unit for this specific
|
// this column contains the ID pointer of the “base” unit for this specific
|
||||||
// monster type (ex. There are five types of “Fallen”; all of them have
|
// monster type (ex. There are five types of “Fallen”; all of them have
|
||||||
// fallen1 as their “base” unit). The baseID is responsible for some
|
// fallen1 as their “base” unit). The baseID is responsible for some
|
||||||
// hardcoded behaviours, for example moving thru walls (ghosts), knowing
|
// hardcoded behaviors, for example moving thru walls (ghosts), knowing
|
||||||
// what units to ressurect, create etc (putrid defilers, shamans etc), the
|
// what units to resurrect, create etc (putrid defilers, shamans etc), the
|
||||||
// explosion appended to suicide minions (either cold, fire or ice). Thanks
|
// explosion appended to suicide minions (either cold, fire or ice). Thanks
|
||||||
// to Kingpin for additional info on this column.
|
// to Kingpin for additional info on this column.
|
||||||
BaseKey string // BaseId
|
BaseKey string // BaseId
|
||||||
|
@ -312,7 +313,7 @@ type MonStatsRecord struct {
|
||||||
// these columns control “non-skill-related” missiles used by the monster.
|
// these columns control “non-skill-related” missiles used by the monster.
|
||||||
// For example if you enter a missile ID pointer (from Missiles.txt) in
|
// For example if you enter a missile ID pointer (from Missiles.txt) in
|
||||||
// MissA1 then, whenever the monster uses its A1 mode, it will shoot a
|
// MissA1 then, whenever the monster uses its A1 mode, it will shoot a
|
||||||
// missile, this however will succesfully prevent it from dealing any damage
|
// missile, this however will successfully prevent it from dealing any damage
|
||||||
// with the swing of A1.
|
// with the swing of A1.
|
||||||
// NOTE: for the beginners, A1=Attack1, A2=Attack2, S1=Skill1, S2=Skill2,
|
// NOTE: for the beginners, A1=Attack1, A2=Attack2, S1=Skill1, S2=Skill2,
|
||||||
// S3=Skill3, S4=Skill4, C=Cast, SQ=Sequence.
|
// S3=Skill3, S4=Skill4, C=Cast, SQ=Sequence.
|
||||||
|
@ -389,7 +390,7 @@ type MonStatsRecord struct {
|
||||||
// Boolean, 1=I can open doors, 0=I’m too damn retarded to open doors. Ever
|
// Boolean, 1=I can open doors, 0=I’m too damn retarded to open doors. Ever
|
||||||
// wanted to make the game more like D1 (where closing doors could actually
|
// wanted to make the game more like D1 (where closing doors could actually
|
||||||
// protect you), then this column is all you need. By setting this to 0 you
|
// protect you), then this column is all you need. By setting this to 0 you
|
||||||
// will succesfully lobotomize the monster, thus he will not be able to open
|
// will successfully lobotomize the monster, thus he will not be able to open
|
||||||
// doors any more.
|
// doors any more.
|
||||||
CanOpenDoors bool // opendoors
|
CanOpenDoors bool // opendoors
|
||||||
|
|
||||||
|
@ -793,10 +794,12 @@ type MonStatsRecord struct {
|
||||||
|
|
||||||
var MonStats map[string]*MonStatsRecord
|
var MonStats map[string]*MonStatsRecord
|
||||||
|
|
||||||
|
//nolint:funlen // Makes no sense to split
|
||||||
func LoadMonStats(file []byte) {
|
func LoadMonStats(file []byte) {
|
||||||
dict := d2common.LoadDataDictionary(string(file))
|
dict := d2common.LoadDataDictionary(string(file))
|
||||||
numRecords := len(dict.Data)
|
numRecords := len(dict.Data)
|
||||||
MonStats = make(map[string]*MonStatsRecord, numRecords)
|
MonStats = make(map[string]*MonStatsRecord, numRecords)
|
||||||
|
|
||||||
for idx := range dict.Data {
|
for idx := range dict.Data {
|
||||||
record := &MonStatsRecord{
|
record := &MonStatsRecord{
|
||||||
Key: dict.GetString("Id", idx),
|
Key: dict.GetString("Id", idx),
|
||||||
|
@ -1054,5 +1057,6 @@ func LoadMonStats(file []byte) {
|
||||||
}
|
}
|
||||||
MonStats[record.Key] = record
|
MonStats[record.Key] = record
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Loaded %d MonStats records", len(MonStats))
|
log.Printf("Loaded %d MonStats records", len(MonStats))
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,369 @@
|
||||||
|
package d2datadict
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||||
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MonStats2Record struct {
|
||||||
|
// Key, the object ID MonStatEx feild from MonStat
|
||||||
|
Key string
|
||||||
|
|
||||||
|
// These follow three are apparently unused
|
||||||
|
Height int
|
||||||
|
OverlayHeight int
|
||||||
|
PixelHeight int
|
||||||
|
|
||||||
|
// Diameter in subtiles
|
||||||
|
SizeX int
|
||||||
|
SizeY int
|
||||||
|
|
||||||
|
// This specifies if the size values get used for collision detection
|
||||||
|
NoGfxHitTest bool
|
||||||
|
|
||||||
|
// Bounding box
|
||||||
|
BoxTop int
|
||||||
|
BoxLeft int
|
||||||
|
BoxWidth int
|
||||||
|
BoxHeight int
|
||||||
|
|
||||||
|
// Spawn method used
|
||||||
|
SpawnMethod int
|
||||||
|
|
||||||
|
// Melee radius
|
||||||
|
MeleeRng int
|
||||||
|
|
||||||
|
// base weaponclass?
|
||||||
|
BaseWeaponClass string
|
||||||
|
HitClass int
|
||||||
|
|
||||||
|
// Available options for equipment
|
||||||
|
// randomly selected from
|
||||||
|
HDv []string
|
||||||
|
TRv []string
|
||||||
|
LGv []string
|
||||||
|
Rav []string
|
||||||
|
Lav []string
|
||||||
|
RHv []string
|
||||||
|
LHv []string
|
||||||
|
SHv []string
|
||||||
|
S1v []string
|
||||||
|
S2v []string
|
||||||
|
S3v []string
|
||||||
|
S4v []string
|
||||||
|
S5v []string
|
||||||
|
S6v []string
|
||||||
|
S7v []string
|
||||||
|
S8v []string
|
||||||
|
|
||||||
|
// Does the unit have this component
|
||||||
|
HD bool
|
||||||
|
TR bool
|
||||||
|
LG bool
|
||||||
|
RA bool
|
||||||
|
LA bool
|
||||||
|
RH bool
|
||||||
|
LH bool
|
||||||
|
SH bool
|
||||||
|
S1 bool
|
||||||
|
S2 bool
|
||||||
|
S3 bool
|
||||||
|
S4 bool
|
||||||
|
S5 bool
|
||||||
|
S6 bool
|
||||||
|
S7 bool
|
||||||
|
S8 bool
|
||||||
|
|
||||||
|
// Sum of available components
|
||||||
|
TotalPieces int
|
||||||
|
|
||||||
|
// Available animation modes
|
||||||
|
mDT bool
|
||||||
|
mNU bool
|
||||||
|
mWL bool
|
||||||
|
mGH bool
|
||||||
|
mA1 bool
|
||||||
|
mA2 bool
|
||||||
|
mBL bool
|
||||||
|
mSC bool
|
||||||
|
mS1 bool
|
||||||
|
mS2 bool
|
||||||
|
mS3 bool
|
||||||
|
mS4 bool
|
||||||
|
mDD bool
|
||||||
|
mKB bool
|
||||||
|
mSQ bool
|
||||||
|
mRN bool
|
||||||
|
|
||||||
|
// Number of directions for each mode
|
||||||
|
dDT int
|
||||||
|
dNU int
|
||||||
|
dWL int
|
||||||
|
dGH int
|
||||||
|
dA1 int
|
||||||
|
dA2 int
|
||||||
|
dBL int
|
||||||
|
dSC int
|
||||||
|
dS1 int
|
||||||
|
dS2 int
|
||||||
|
dS3 int
|
||||||
|
dS4 int
|
||||||
|
dDD int
|
||||||
|
dKB int
|
||||||
|
dSQ int
|
||||||
|
dRN int
|
||||||
|
|
||||||
|
// Available modes while moving aside from WL and RN
|
||||||
|
A1mv bool
|
||||||
|
A2mv bool
|
||||||
|
SCmv bool
|
||||||
|
S1mv bool
|
||||||
|
S2mv bool
|
||||||
|
S3mv bool
|
||||||
|
S4mv bool
|
||||||
|
|
||||||
|
// If the units is restored on map reload
|
||||||
|
Restore int
|
||||||
|
|
||||||
|
// What maximap index is used for the automap
|
||||||
|
AutomapCel int
|
||||||
|
|
||||||
|
// true of unit uses an automap entry
|
||||||
|
NoMap bool
|
||||||
|
|
||||||
|
// If the units can use overlays
|
||||||
|
NoOvly bool
|
||||||
|
|
||||||
|
// If unit is selectable
|
||||||
|
IsSelectable bool
|
||||||
|
|
||||||
|
// If unit is selectable by allies
|
||||||
|
AllySelectable bool
|
||||||
|
|
||||||
|
// If unit is not selectable
|
||||||
|
NotSelectable bool
|
||||||
|
|
||||||
|
// Kinda unk, used for bonewalls etc that are not properly selectable
|
||||||
|
shiftSel bool
|
||||||
|
|
||||||
|
// if the units corpse is selectable
|
||||||
|
IsCorpseSelectable bool
|
||||||
|
|
||||||
|
// If the unit is attackable
|
||||||
|
IsAttackable bool
|
||||||
|
|
||||||
|
// If the unit is revivable
|
||||||
|
IsRevivable bool
|
||||||
|
|
||||||
|
// If the unit is a critter
|
||||||
|
IsCritter bool
|
||||||
|
|
||||||
|
// If the unit is Small, Small units can be knocked back with 100% efficiency
|
||||||
|
IsSmall bool
|
||||||
|
|
||||||
|
// Large units can be knocked back at 25% efficincy
|
||||||
|
IsLarge bool
|
||||||
|
|
||||||
|
// Possibly to do with sound, usually set for creatures without flesh
|
||||||
|
IsSoft bool
|
||||||
|
|
||||||
|
// Aggressive or harmless, usually NPC's
|
||||||
|
IsInert bool
|
||||||
|
|
||||||
|
// Unknown
|
||||||
|
objCol bool
|
||||||
|
|
||||||
|
// Enables collision on corpse for units
|
||||||
|
IsCorpseCollidable bool
|
||||||
|
|
||||||
|
// Can the corpse be walked through
|
||||||
|
IsCorpseWalkable bool
|
||||||
|
|
||||||
|
// If the unit casts a shadow
|
||||||
|
HasShadow bool
|
||||||
|
|
||||||
|
// If unique palettes should not be used
|
||||||
|
NoUniqueShift bool
|
||||||
|
|
||||||
|
// If multiple layers should be used on death (otherwise only TR)
|
||||||
|
CompositeDeath bool
|
||||||
|
|
||||||
|
// Blood offset?
|
||||||
|
LocalBlood int
|
||||||
|
|
||||||
|
// 0 = don't bleed, 1 = small blood missile, 2 = small and large, > 3 other missiles?
|
||||||
|
Bleed int
|
||||||
|
|
||||||
|
// If the unit is lights up the area
|
||||||
|
Light int
|
||||||
|
|
||||||
|
// Light color
|
||||||
|
LightR int
|
||||||
|
LightG int
|
||||||
|
lightB int
|
||||||
|
|
||||||
|
// Palettes per difficulty
|
||||||
|
NormalPalette int
|
||||||
|
NightmarePalette int
|
||||||
|
HellPalatte int
|
||||||
|
|
||||||
|
// These two are useless as of 1.07
|
||||||
|
Heart string
|
||||||
|
BodyPart string
|
||||||
|
|
||||||
|
// Inferno animation stuff
|
||||||
|
InfernoLen int
|
||||||
|
InfernoAnim int
|
||||||
|
InfernoRollback int
|
||||||
|
|
||||||
|
// Which mode is used after resurrection
|
||||||
|
ResurrectMode d2enum.MonsterAnimationMode
|
||||||
|
|
||||||
|
// Which skill is used for resurrection
|
||||||
|
ResurrectSkill string
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:gochecknoglobals // Current design issue
|
||||||
|
var MonStats2 map[string]MonStats2Record
|
||||||
|
|
||||||
|
//nolint:funlen //just a big data loader
|
||||||
|
func LoadMonStats2(file []byte) {
|
||||||
|
dict := d2common.LoadDataDictionary(string(file))
|
||||||
|
numRecords := len(dict.Data)
|
||||||
|
MonStats2 = make(map[string]MonStats2Record, numRecords)
|
||||||
|
|
||||||
|
for idx := range dict.Data {
|
||||||
|
record := MonStats2Record{
|
||||||
|
Key: dict.GetString("Id", idx),
|
||||||
|
Height: dict.GetNumber("Height", idx),
|
||||||
|
OverlayHeight: dict.GetNumber("OverlayHeight", idx),
|
||||||
|
PixelHeight: dict.GetNumber("pixHeight", idx),
|
||||||
|
SizeX: dict.GetNumber("SizeX", idx),
|
||||||
|
SizeY: dict.GetNumber("SizeY", idx),
|
||||||
|
SpawnMethod: dict.GetNumber("spawnCol", idx),
|
||||||
|
MeleeRng: dict.GetNumber("MeleeRng", idx),
|
||||||
|
BaseWeaponClass: dict.GetString("BaseW", idx),
|
||||||
|
HitClass: dict.GetNumber("HitClass", idx),
|
||||||
|
HDv: dict.GetDelimitedList("HDv", idx),
|
||||||
|
TRv: dict.GetDelimitedList("TRv", idx),
|
||||||
|
LGv: dict.GetDelimitedList("LGv", idx),
|
||||||
|
Rav: dict.GetDelimitedList("Rav", idx),
|
||||||
|
Lav: dict.GetDelimitedList("Lav", idx),
|
||||||
|
RHv: dict.GetDelimitedList("RDv", idx),
|
||||||
|
LHv: dict.GetDelimitedList("LHv", idx),
|
||||||
|
SHv: dict.GetDelimitedList("SHv", idx),
|
||||||
|
S1v: dict.GetDelimitedList("S1v", idx),
|
||||||
|
S2v: dict.GetDelimitedList("S2v", idx),
|
||||||
|
S3v: dict.GetDelimitedList("S3v", idx),
|
||||||
|
S4v: dict.GetDelimitedList("S4v", idx),
|
||||||
|
S5v: dict.GetDelimitedList("S5v", idx),
|
||||||
|
S6v: dict.GetDelimitedList("S6v", idx),
|
||||||
|
S7v: dict.GetDelimitedList("S7v", idx),
|
||||||
|
S8v: dict.GetDelimitedList("S8v", idx),
|
||||||
|
HD: dict.GetBool("HD", idx),
|
||||||
|
TR: dict.GetBool("TR", idx),
|
||||||
|
LG: dict.GetBool("LG", idx),
|
||||||
|
RA: dict.GetBool("RA", idx),
|
||||||
|
LA: dict.GetBool("LA", idx),
|
||||||
|
RH: dict.GetBool("RH", idx),
|
||||||
|
LH: dict.GetBool("LH", idx),
|
||||||
|
SH: dict.GetBool("SH", idx),
|
||||||
|
S1: dict.GetBool("S1", idx),
|
||||||
|
S2: dict.GetBool("S2", idx),
|
||||||
|
S3: dict.GetBool("S3", idx),
|
||||||
|
S4: dict.GetBool("S4", idx),
|
||||||
|
S5: dict.GetBool("S5", idx),
|
||||||
|
S6: dict.GetBool("S6", idx),
|
||||||
|
S7: dict.GetBool("S7", idx),
|
||||||
|
S8: dict.GetBool("S8", idx),
|
||||||
|
TotalPieces: dict.GetNumber("TotalPieces", idx),
|
||||||
|
mDT: dict.GetBool("mDT", idx),
|
||||||
|
mNU: dict.GetBool("mNU", idx),
|
||||||
|
mWL: dict.GetBool("mWL", idx),
|
||||||
|
mGH: dict.GetBool("mGH", idx),
|
||||||
|
mA1: dict.GetBool("mA1", idx),
|
||||||
|
mA2: dict.GetBool("mA2", idx),
|
||||||
|
mBL: dict.GetBool("mBL", idx),
|
||||||
|
mSC: dict.GetBool("mSC", idx),
|
||||||
|
mS1: dict.GetBool("mS1", idx),
|
||||||
|
mS2: dict.GetBool("mS2", idx),
|
||||||
|
mS3: dict.GetBool("mS3", idx),
|
||||||
|
mS4: dict.GetBool("mS4", idx),
|
||||||
|
mDD: dict.GetBool("mDD", idx),
|
||||||
|
mKB: dict.GetBool("mKB", idx),
|
||||||
|
mSQ: dict.GetBool("mSQ", idx),
|
||||||
|
mRN: dict.GetBool("mRN", idx),
|
||||||
|
dDT: dict.GetNumber("mDT", idx),
|
||||||
|
dNU: dict.GetNumber("mNU", idx),
|
||||||
|
dWL: dict.GetNumber("mWL", idx),
|
||||||
|
dGH: dict.GetNumber("mGH", idx),
|
||||||
|
dA1: dict.GetNumber("mA1", idx),
|
||||||
|
dA2: dict.GetNumber("mA2", idx),
|
||||||
|
dBL: dict.GetNumber("mBL", idx),
|
||||||
|
dSC: dict.GetNumber("mSC", idx),
|
||||||
|
dS1: dict.GetNumber("mS1", idx),
|
||||||
|
dS2: dict.GetNumber("mS2", idx),
|
||||||
|
dS3: dict.GetNumber("mS3", idx),
|
||||||
|
dS4: dict.GetNumber("mS4", idx),
|
||||||
|
dDD: dict.GetNumber("mDD", idx),
|
||||||
|
dKB: dict.GetNumber("mKB", idx),
|
||||||
|
dSQ: dict.GetNumber("mSQ", idx),
|
||||||
|
dRN: dict.GetNumber("mRN", idx),
|
||||||
|
A1mv: dict.GetBool("A1mv", idx),
|
||||||
|
A2mv: dict.GetBool("A2mv", idx),
|
||||||
|
SCmv: dict.GetBool("SCmv", idx),
|
||||||
|
S1mv: dict.GetBool("S1mv", idx),
|
||||||
|
S2mv: dict.GetBool("S2mv", idx),
|
||||||
|
S3mv: dict.GetBool("S3mv", idx),
|
||||||
|
S4mv: dict.GetBool("S4mv", idx),
|
||||||
|
NoGfxHitTest: dict.GetBool("noGfxHitTest", idx),
|
||||||
|
BoxTop: dict.GetNumber("htTop", idx),
|
||||||
|
BoxLeft: dict.GetNumber("htLeft", idx),
|
||||||
|
BoxWidth: dict.GetNumber("htWidth", idx),
|
||||||
|
BoxHeight: dict.GetNumber("htHeight", idx),
|
||||||
|
Restore: dict.GetNumber("restore", idx),
|
||||||
|
AutomapCel: dict.GetNumber("automapCel", idx),
|
||||||
|
NoMap: dict.GetBool("noMap", idx),
|
||||||
|
NoOvly: dict.GetBool("noOvly", idx),
|
||||||
|
IsSelectable: dict.GetBool("isSel", idx),
|
||||||
|
AllySelectable: dict.GetBool("alSel", idx),
|
||||||
|
shiftSel: dict.GetBool("shiftSel", idx),
|
||||||
|
NotSelectable: dict.GetBool("noSel", idx),
|
||||||
|
IsCorpseSelectable: dict.GetBool("corpseSel", idx),
|
||||||
|
IsAttackable: dict.GetBool("isAtt", idx),
|
||||||
|
IsRevivable: dict.GetBool("revive", idx),
|
||||||
|
IsCritter: dict.GetBool("critter", idx),
|
||||||
|
IsSmall: dict.GetBool("small", idx),
|
||||||
|
IsLarge: dict.GetBool("large", idx),
|
||||||
|
IsSoft: dict.GetBool("soft", idx),
|
||||||
|
IsInert: dict.GetBool("inert", idx),
|
||||||
|
objCol: dict.GetBool("objCol", idx),
|
||||||
|
IsCorpseCollidable: dict.GetBool("deadCol", idx),
|
||||||
|
IsCorpseWalkable: dict.GetBool("unflatDead", idx),
|
||||||
|
HasShadow: dict.GetBool("Shadow", idx),
|
||||||
|
NoUniqueShift: dict.GetBool("noUniqueShift", idx),
|
||||||
|
CompositeDeath: dict.GetBool("compositeDeath", idx),
|
||||||
|
LocalBlood: dict.GetNumber("localBlood", idx),
|
||||||
|
Bleed: dict.GetNumber("Bleed", idx),
|
||||||
|
Light: dict.GetNumber("Light", idx),
|
||||||
|
LightR: dict.GetNumber("light-r", idx),
|
||||||
|
LightG: dict.GetNumber("light-g", idx),
|
||||||
|
lightB: dict.GetNumber("light-b", idx),
|
||||||
|
NormalPalette: dict.GetNumber("Utrans", idx),
|
||||||
|
NightmarePalette: dict.GetNumber("Utrans(N)", idx),
|
||||||
|
HellPalatte: dict.GetNumber("Utrans(H)", idx),
|
||||||
|
Heart: dict.GetString("Heart", idx),
|
||||||
|
BodyPart: dict.GetString("BodyPart", idx),
|
||||||
|
InfernoLen: dict.GetNumber("InfernoLen", idx),
|
||||||
|
InfernoAnim: dict.GetNumber("InfernoAnim", idx),
|
||||||
|
InfernoRollback: dict.GetNumber("InfernoRollback", idx),
|
||||||
|
ResurrectMode: d2enum.MonsterAnimationModeFromString(dict.GetString("ResurrectMode", idx)),
|
||||||
|
ResurrectSkill: dict.GetString("ResurrectSkill", idx),
|
||||||
|
}
|
||||||
|
MonStats2[record.Key] = record
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Loaded %d MonStats2 records", len(MonStats2))
|
||||||
|
}
|
|
@ -49,6 +49,7 @@ func LookupObject(act, typ, id int) *ObjectLookupRecord {
|
||||||
if object == nil {
|
if object == nil {
|
||||||
log.Panicf("Failed to look up object Act: %d, Type: %d, Id: %d", act, typ, id)
|
log.Panicf("Failed to look up object Act: %d, Type: %d, Id: %d", act, typ, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
return object
|
return object
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,6 +57,7 @@ func lookupObject(act, typ, id int, objects [][][]*ObjectLookupRecord) *ObjectLo
|
||||||
if objects[act] != nil && objects[act][typ] != nil && objects[act][typ][id] != nil {
|
if objects[act] != nil && objects[act][typ] != nil && objects[act][typ][id] != nil {
|
||||||
return objects[act][typ][id]
|
return objects[act][typ][id]
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,6 +68,7 @@ func init() {
|
||||||
func indexObjects(objects []ObjectLookupRecord) [][][]*ObjectLookupRecord {
|
func indexObjects(objects []ObjectLookupRecord) [][][]*ObjectLookupRecord {
|
||||||
// Allocating 6 to allow Acts 1-5 without requiring a -1 at every read.
|
// Allocating 6 to allow Acts 1-5 without requiring a -1 at every read.
|
||||||
indexedObjects = make([][][]*ObjectLookupRecord, 6)
|
indexedObjects = make([][][]*ObjectLookupRecord, 6)
|
||||||
|
|
||||||
for i := range objects {
|
for i := range objects {
|
||||||
record := &objects[i]
|
record := &objects[i]
|
||||||
if indexedObjects[record.Act] == nil {
|
if indexedObjects[record.Act] == nil {
|
||||||
|
|
|
@ -12,12 +12,15 @@ type ObjectTypeRecord struct {
|
||||||
Token string
|
Token string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:gochecknoglobals // Currently global by design, only written once
|
||||||
|
// ObjectTypes contains the name and token for objects
|
||||||
var ObjectTypes []ObjectTypeRecord
|
var ObjectTypes []ObjectTypeRecord
|
||||||
|
|
||||||
func LoadObjectTypes(objectTypeData []byte) {
|
func LoadObjectTypes(objectTypeData []byte) {
|
||||||
streamReader := d2common.CreateStreamReader(objectTypeData)
|
streamReader := d2common.CreateStreamReader(objectTypeData)
|
||||||
count := streamReader.GetInt32()
|
count := streamReader.GetInt32()
|
||||||
ObjectTypes = make([]ObjectTypeRecord, count)
|
ObjectTypes = make([]ObjectTypeRecord, count)
|
||||||
|
|
||||||
for i := range ObjectTypes {
|
for i := range ObjectTypes {
|
||||||
nameBytes := streamReader.ReadBytes(32)
|
nameBytes := streamReader.ReadBytes(32)
|
||||||
tokenBytes := streamReader.ReadBytes(20)
|
tokenBytes := streamReader.ReadBytes(20)
|
||||||
|
@ -26,5 +29,6 @@ func LoadObjectTypes(objectTypeData []byte) {
|
||||||
Token: strings.TrimSpace(strings.ReplaceAll(string(tokenBytes), string(0), "")),
|
Token: strings.TrimSpace(strings.ReplaceAll(string(tokenBytes), string(0), "")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Loaded %d object types", len(ObjectTypes))
|
log.Printf("Loaded %d object types", len(ObjectTypes))
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,6 +119,7 @@ type ObjectRecord struct {
|
||||||
// 0 = it doesn't, rest of modes need to be analyzed
|
// 0 = it doesn't, rest of modes need to be analyzed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:funlen // Makes no sense to split
|
||||||
// CreateObjectRecord parses a row from objects.txt into an object record
|
// CreateObjectRecord parses a row from objects.txt into an object record
|
||||||
func createObjectRecord(props []string) ObjectRecord {
|
func createObjectRecord(props []string) ObjectRecord {
|
||||||
i := -1
|
i := -1
|
||||||
|
@ -330,24 +331,31 @@ func createObjectRecord(props []string) ObjectRecord {
|
||||||
|
|
||||||
AutoMap: d2common.StringToInt(props[inc()]),
|
AutoMap: d2common.StringToInt(props[inc()]),
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:gochecknoglobals // Currently global by design, only written once
|
||||||
var Objects map[int]*ObjectRecord
|
var Objects map[int]*ObjectRecord
|
||||||
|
|
||||||
func LoadObjects(file []byte) {
|
func LoadObjects(file []byte) {
|
||||||
Objects = make(map[int]*ObjectRecord)
|
Objects = make(map[int]*ObjectRecord)
|
||||||
data := strings.Split(string(file), "\r\n")[1:]
|
data := strings.Split(string(file), "\r\n")[1:]
|
||||||
|
|
||||||
for _, line := range data {
|
for _, line := range data {
|
||||||
if len(line) == 0 {
|
if line == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
props := strings.Split(line, "\t")
|
props := strings.Split(line, "\t")
|
||||||
|
|
||||||
if props[2] == "" {
|
if props[2] == "" {
|
||||||
continue // skip a line that doesn't have an id
|
continue // skip a line that doesn't have an id
|
||||||
}
|
}
|
||||||
|
|
||||||
rec := createObjectRecord(props)
|
rec := createObjectRecord(props)
|
||||||
Objects[rec.Id] = &rec
|
Objects[rec.Id] = &rec
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Loaded %d objects", len(Objects))
|
log.Printf("Loaded %d objects", len(Objects))
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,21 +71,27 @@ func createSoundEntry(soundLine string) SoundEntry {
|
||||||
Block2: d2common.StringToInt(props[inc()]),
|
Block2: d2common.StringToInt(props[inc()]),
|
||||||
Block3: d2common.StringToInt(props[inc()]),
|
Block3: d2common.StringToInt(props[inc()]),
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:gochecknoglobals // Currently global by design, only written once
|
||||||
var Sounds map[string]SoundEntry
|
var Sounds map[string]SoundEntry
|
||||||
|
|
||||||
func LoadSounds(file []byte) {
|
func LoadSounds(file []byte) {
|
||||||
Sounds = make(map[string]SoundEntry)
|
Sounds = make(map[string]SoundEntry)
|
||||||
soundData := strings.Split(string(file), "\r\n")[1:]
|
soundData := strings.Split(string(file), "\r\n")[1:]
|
||||||
|
|
||||||
for _, line := range soundData {
|
for _, line := range soundData {
|
||||||
if len(line) == 0 {
|
if line == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
soundEntry := createSoundEntry(line)
|
soundEntry := createSoundEntry(line)
|
||||||
soundEntry.FileName = "/data/global/sfx/" + strings.ReplaceAll(soundEntry.FileName, `\`, "/")
|
soundEntry.FileName = "/data/global/sfx/" + strings.ReplaceAll(soundEntry.FileName, `\`, "/")
|
||||||
Sounds[soundEntry.Handle] = soundEntry
|
Sounds[soundEntry.Handle] = soundEntry
|
||||||
|
|
||||||
|
//nolint:gocritic // Debug util code
|
||||||
/*
|
/*
|
||||||
// Use the following code to write out the values
|
// Use the following code to write out the values
|
||||||
f, err := os.OpenFile(`C:\Users\lunat\Desktop\D2\sounds.txt`,
|
f, err := os.OpenFile(`C:\Users\lunat\Desktop\D2\sounds.txt`,
|
||||||
|
@ -98,6 +104,7 @@ func LoadSounds(file []byte) {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
}
|
} //nolint:wsl // Debug util code
|
||||||
|
|
||||||
log.Printf("Loaded %d sound definitions", len(Sounds))
|
log.Printf("Loaded %d sound definitions", len(Sounds))
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,8 +93,10 @@ type SuperUniqueRecord struct {
|
||||||
// Boolean indicates if the game is expansion or classic
|
// Boolean indicates if the game is expansion or classic
|
||||||
IsExpansion bool // named as "EClass" in the SuperUniques.txt
|
IsExpansion bool // named as "EClass" in the SuperUniques.txt
|
||||||
|
|
||||||
// This field states whether the SuperUnique will be placed within a radius from his original position(defined by the .ds1 map file), or not.
|
// This field states whether the SuperUnique will be placed within a radius from his original
|
||||||
// false means that the boss will spawn in a random position within a large radius from its actual position in the .ds1 file,
|
// position(defined by the .ds1 map file), or not.
|
||||||
|
// false means that the boss will spawn in a random position within a large radius from its actual
|
||||||
|
// position in the .ds1 file,
|
||||||
// true means it will spawn exactly where expected.
|
// true means it will spawn exactly where expected.
|
||||||
AutoPosition bool
|
AutoPosition bool
|
||||||
|
|
||||||
|
@ -104,7 +106,8 @@ type SuperUniqueRecord struct {
|
||||||
|
|
||||||
// Treasure Classes for the 3 Difficulties.
|
// Treasure Classes for the 3 Difficulties.
|
||||||
// These columns list the treasureclass that is valid if this boss is killed and drops something.
|
// These columns list the treasureclass that is valid if this boss is killed and drops something.
|
||||||
// These fields must contain the values taken from the "TreasureClass" column in TreasureClassEx.txt (Expansion) or TreasureClass (Classic).
|
// These fields must contain the values taken from the "TreasureClass" column in TreasureClassEx.txt (Expansion)
|
||||||
|
// or TreasureClass (Classic).
|
||||||
TreasureClassNormal string
|
TreasureClassNormal string
|
||||||
TreasureClassNightmare string
|
TreasureClassNightmare string
|
||||||
TreasureClassHell string
|
TreasureClassHell string
|
||||||
|
@ -120,6 +123,7 @@ var SuperUniques map[string]*SuperUniqueRecord
|
||||||
func LoadSuperUniques(file []byte) {
|
func LoadSuperUniques(file []byte) {
|
||||||
dictionary := d2common.LoadDataDictionary(string(file))
|
dictionary := d2common.LoadDataDictionary(string(file))
|
||||||
SuperUniques = make(map[string]*SuperUniqueRecord, len(dictionary.Data))
|
SuperUniques = make(map[string]*SuperUniqueRecord, len(dictionary.Data))
|
||||||
|
|
||||||
for idx := range dictionary.Data {
|
for idx := range dictionary.Data {
|
||||||
record := &SuperUniqueRecord{
|
record := &SuperUniqueRecord{
|
||||||
Key: dictionary.GetString("Superunique", idx),
|
Key: dictionary.GetString("Superunique", idx),
|
||||||
|
@ -146,5 +150,6 @@ func LoadSuperUniques(file []byte) {
|
||||||
}
|
}
|
||||||
SuperUniques[record.Key] = record
|
SuperUniques[record.Key] = record
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Loaded %d SuperUnique records", len(SuperUniques))
|
log.Printf("Loaded %d SuperUnique records", len(SuperUniques))
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,6 +99,7 @@ func createUniqueItemRecord(r []string) UniqueItemRecord {
|
||||||
createUniqueItemProperty(&r, inc),
|
createUniqueItemProperty(&r, inc),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,6 +110,7 @@ func createUniqueItemProperty(r *[]string, inc func() int) UniqueItemProperty {
|
||||||
Min: d2common.StringToInt(d2common.EmptyToZero((*r)[inc()])),
|
Min: d2common.StringToInt(d2common.EmptyToZero((*r)[inc()])),
|
||||||
Max: d2common.StringToInt(d2common.EmptyToZero((*r)[inc()])),
|
Max: d2common.StringToInt(d2common.EmptyToZero((*r)[inc()])),
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,17 +119,21 @@ var UniqueItems map[string]*UniqueItemRecord
|
||||||
func LoadUniqueItems(file []byte) {
|
func LoadUniqueItems(file []byte) {
|
||||||
UniqueItems = make(map[string]*UniqueItemRecord)
|
UniqueItems = make(map[string]*UniqueItemRecord)
|
||||||
data := strings.Split(string(file), "\r\n")[1:]
|
data := strings.Split(string(file), "\r\n")[1:]
|
||||||
|
|
||||||
for _, line := range data {
|
for _, line := range data {
|
||||||
if len(line) == 0 {
|
if line == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
r := strings.Split(line, "\t")
|
r := strings.Split(line, "\t")
|
||||||
// skip rows that are not enabled
|
// skip rows that are not enabled
|
||||||
if r[2] != "1" {
|
if r[2] != "1" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
rec := createUniqueItemRecord(r)
|
rec := createUniqueItemRecord(r)
|
||||||
UniqueItems[rec.Code] = &rec
|
UniqueItems[rec.Code] = &rec
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Loaded %d unique items", len(UniqueItems))
|
log.Printf("Loaded %d unique items", len(UniqueItems))
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
// Manually edited to remove duplicate
|
||||||
|
// If you generate it again you need fix it up
|
||||||
|
|
||||||
|
// Code generated by "string2enum -samepkg -linecomment -type MonsterAnimationMode ."; DO NOT EDIT.
|
||||||
|
|
||||||
|
package d2enum
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// MonsterAnimationModeFromString returns the MonsterAnimationMode enum corresponding to s.
|
||||||
|
func MonsterAnimationModeFromString(s string) MonsterAnimationMode {
|
||||||
|
if len(s) == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
for i := range _MonsterAnimationMode_index[:len(_MonsterAnimationMode_index)-1] {
|
||||||
|
if s == _MonsterAnimationMode_name[_MonsterAnimationMode_index[i]:_MonsterAnimationMode_index[i+1]] {
|
||||||
|
return MonsterAnimationMode(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic(fmt.Errorf("unable to locate MonsterAnimationMode enum corresponding to %q", s))
|
||||||
|
}
|
||||||
|
|
||||||
|
func _(s string) {
|
||||||
|
// Check for duplicate string values in type "MonsterAnimationMode".
|
||||||
|
switch s {
|
||||||
|
// 0
|
||||||
|
case "DT":
|
||||||
|
// 1
|
||||||
|
case "NU":
|
||||||
|
// 2
|
||||||
|
case "WL":
|
||||||
|
// 3
|
||||||
|
case "GH":
|
||||||
|
// 4
|
||||||
|
case "A1":
|
||||||
|
// 5
|
||||||
|
case "A2":
|
||||||
|
// 6
|
||||||
|
case "BL":
|
||||||
|
// 7
|
||||||
|
case "SC":
|
||||||
|
// 8
|
||||||
|
case "S1":
|
||||||
|
// 9
|
||||||
|
case "S2":
|
||||||
|
// 10
|
||||||
|
case "S3":
|
||||||
|
// 11
|
||||||
|
case "S4":
|
||||||
|
// 12
|
||||||
|
case "DD":
|
||||||
|
// 14
|
||||||
|
case "xx":
|
||||||
|
// 15
|
||||||
|
case "RN":
|
||||||
|
}
|
||||||
|
}
|
|
@ -279,6 +279,7 @@ const (
|
||||||
// --- Enemy Data ---
|
// --- Enemy Data ---
|
||||||
|
|
||||||
MonStats = "/data/global/excel/monstats.txt"
|
MonStats = "/data/global/excel/monstats.txt"
|
||||||
|
MonStats2 = "/data/global/excel/monstats2.txt"
|
||||||
MonPreset = "/data/global/excel/monpreset.txt"
|
MonPreset = "/data/global/excel/monpreset.txt"
|
||||||
SuperUniques = "/data/global/excel/SuperUniques.txt"
|
SuperUniques = "/data/global/excel/SuperUniques.txt"
|
||||||
|
|
||||||
|
|
|
@ -47,3 +47,15 @@ func (v *DataDictionary) GetNumber(fieldName string, index int) int {
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *DataDictionary) GetDelimitedList(fieldName string, index int) []string {
|
||||||
|
return strings.Split(v.GetString(fieldName, index), ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *DataDictionary) GetBool(fieldName string, index int) bool {
|
||||||
|
n := v.GetNumber(fieldName, index)
|
||||||
|
if n > 1 {
|
||||||
|
log.Panic("GetBool on non-bool field")
|
||||||
|
}
|
||||||
|
return n == 1
|
||||||
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ func CreateMapRenderer(mapEngine *d2mapengine.MapEngine, term d2interface.Termin
|
||||||
result.debugVisLevel = level
|
result.debugVisLevel = level
|
||||||
})
|
})
|
||||||
|
|
||||||
if mapEngine.LevelType().Id != 0 {
|
if mapEngine.LevelType().ID != 0 {
|
||||||
result.generateTileCache()
|
result.generateTileCache()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (mr *MapRenderer) generateTileCache() {
|
func (mr *MapRenderer) generateTileCache() {
|
||||||
mr.palette, _ = loadPaletteForAct(d2enum.RegionIdType(mr.mapEngine.LevelType().Id))
|
mr.palette, _ = loadPaletteForAct(d2enum.RegionIdType(mr.mapEngine.LevelType().ID))
|
||||||
mapEngineSize := mr.mapEngine.Size()
|
mapEngineSize := mr.mapEngine.Size()
|
||||||
|
|
||||||
for idx, tile := range *mr.mapEngine.Tiles() {
|
for idx, tile := range *mr.mapEngine.Tiles() {
|
||||||
|
@ -197,7 +197,7 @@ func (mr *MapRenderer) getRandomTile(tiles []d2dt1.Tile, x, y int, seed int64) b
|
||||||
|
|
||||||
var tileSeed uint64
|
var tileSeed uint64
|
||||||
tileSeed = uint64(seed) + uint64(x)
|
tileSeed = uint64(seed) + uint64(x)
|
||||||
tileSeed *= uint64(y) + uint64(mr.mapEngine.LevelType().Id)
|
tileSeed *= uint64(y) + uint64(mr.mapEngine.LevelType().ID)
|
||||||
|
|
||||||
tileSeed ^= tileSeed << 13
|
tileSeed ^= tileSeed << 13
|
||||||
tileSeed ^= tileSeed >> 17
|
tileSeed ^= tileSeed >> 17
|
||||||
|
|
1
main.go
1
main.go
|
@ -431,6 +431,7 @@ func loadDataDict() error {
|
||||||
{d2resource.SoundSettings, d2datadict.LoadSounds},
|
{d2resource.SoundSettings, d2datadict.LoadSounds},
|
||||||
{d2resource.AnimationData, d2data.LoadAnimationData},
|
{d2resource.AnimationData, d2data.LoadAnimationData},
|
||||||
{d2resource.MonStats, d2datadict.LoadMonStats},
|
{d2resource.MonStats, d2datadict.LoadMonStats},
|
||||||
|
{d2resource.MonStats2, d2datadict.LoadMonStats2},
|
||||||
{d2resource.MonPreset, d2datadict.LoadMonPresets},
|
{d2resource.MonPreset, d2datadict.LoadMonPresets},
|
||||||
{d2resource.MagicPrefix, d2datadict.LoadMagicPrefix},
|
{d2resource.MagicPrefix, d2datadict.LoadMagicPrefix},
|
||||||
{d2resource.MagicSuffix, d2datadict.LoadMagicSuffix},
|
{d2resource.MagicSuffix, d2datadict.LoadMagicSuffix},
|
||||||
|
|
Loading…
Reference in New Issue