From 99b10b2599120041a2e513b4f810149e20e855f3 Mon Sep 17 00:00:00 2001 From: danhale-git <36298392+danhale-git@users.noreply.github.com> Date: Sun, 21 Jun 2020 00:53:29 +0100 Subject: [PATCH] Feature/automap (#364) * Added automap.go stub * Handle errors in original AutoMap.txt file * Completed AutoMapRecord struct and comments * AutoMap loader implemented --- d2common/d2data/d2datadict/automap.go | 124 ++++++++++++++++++++++++++ d2common/d2resource/resource_paths.go | 1 + main.go | 1 + 3 files changed, 126 insertions(+) create mode 100644 d2common/d2data/d2datadict/automap.go diff --git a/d2common/d2data/d2datadict/automap.go b/d2common/d2data/d2datadict/automap.go new file mode 100644 index 00000000..3906dcfe --- /dev/null +++ b/d2common/d2data/d2datadict/automap.go @@ -0,0 +1,124 @@ +package d2datadict + +import ( + "log" + "strings" + + "github.com/OpenDiablo2/OpenDiablo2/d2common" +) + +// AutoMapRecord represents one row from d2data.mpq/AutoMap.txt. +// Based on the information here https://d2mods.info/forum/kb/viewarticle?a=419 +type AutoMapRecord struct { + // LevelName is a string with an act number followed + // by a level type, separated by a space. For example: + // '1 Barracks' is the barracks level in act 1. + LevelName string + + // TileName refers to a certain tile orientation. + // See https://d2mods.info/forum/kb/viewarticle?a=468 + TileName string + + // Style is the top index in a 2D tile array. + Style int // tiles[autoMapRecord.Style][] + + // StartSequence and EndSequence are sub indices the + // same 2D array as Style. They describe a range of + // tiles for which covered by this AutoMapRecord. + // + // In some rows you can find a value of -1. This means + // the game will only look at Style and TileName to + // determine which tiles are addressed. + StartSequence int // tiles[][autoMapRecord.StartSequence] + EndSequence int // tiles[][autoMapRecord.EndSequence] + + // Type values are described as: + // "...just comment fields, as far as I know. Put in + // whatever you like..." + // The values seem functional but naming conventions + // vary between LevelNames. + Type1 string + Type2 string + Type3 string + Type4 string + + // Cel values contain numbers which determine the frame + // of the MaxiMap(s).dc6 that will be applied to the + // specified tiles. + // + // Multiple values exist for Cel (and Type) to enable + // variation. The game will choose randomly between any + // of the 4 values which are not set to -1. + Cel1 int + Cel2 int + Cel3 int + Cel4 int +} + +// AutoMaps contains all data in AutoMap.txt. +var AutoMaps []*AutoMapRecord + +// LoadAutoMaps populates AutoMaps with the data from AutoMap.txt. +// It also amends a duplicate field (column) name in that data. +func LoadAutoMaps(file []byte) { + // Fix the error in the original file + fileString := fixDuplicateFieldName(string(file)) + + // Split file by newlines and tabs + d := d2common.LoadDataDictionary(fileString) + + // Construct records + AutoMaps = make([]*AutoMapRecord, len(d.Data)) + for idx := range d.Data { + // Row 2603 is a separator with all empty field values + if idx == 2603 { + continue + } + + AutoMaps[idx] = &AutoMapRecord{ + LevelName: d.GetString("LevelName", idx), + TileName: d.GetString("TileName", idx), + + Style: d.GetNumber("Style", idx), + StartSequence: d.GetNumber("StartSequence", idx), + EndSequence: d.GetNumber("EndSequence", idx), + + Type1: d.GetString("Type1", idx), + Type2: d.GetString("Type2", idx), + Type3: d.GetString("Type3", idx), + Type4: d.GetString("Type4", idx), + + Cel1: d.GetNumber("Cel1", idx), + Cel2: d.GetNumber("Cel2", idx), + Cel3: d.GetNumber("Cel3", idx), + Cel4: d.GetNumber("Cel4", idx), + } + } + + log.Printf("Loaded %d AutoMapRecord records", len(AutoMaps)) +} + +// fixDuplicateFieldName changes one of the two 'Type2' fields +// in AutoMap.txt to 'Type3'. An error in the file can be seen +// by looking at the lists of 'Type' and 'Cel' fields: +// +// Type1 Type2 Type2* Type4 +// Cel1 Cel2 Cel3 Cel4 +// +// LoadDataDictionary uses a set of field names. The duplicate +// is omitted resulting in all rows being skipped because their +// counts are different from the field names count. +func fixDuplicateFieldName(fileString string) string { + // Split rows + rows := strings.Split(fileString, "\r\n") + + // Split the field names row and correct the duplicate + fieldNames := strings.Split(rows[0], "\t") + fieldNames[9] = "Type3" + + // Join the field names back up and assign to the first row + rows[0] = strings.Join(fieldNames, "\t") + + // Return the rows, joined back into one string + return strings.Join(rows, "\r\n") +} diff --git a/d2common/d2resource/resource_paths.go b/d2common/d2resource/resource_paths.go index 977dd272..e35feab5 100644 --- a/d2common/d2resource/resource_paths.go +++ b/d2common/d2resource/resource_paths.go @@ -178,6 +178,7 @@ const ( ItemStatCost = "/data/global/excel/ItemStatCost.txt" Hireling = "/data/global/excel/hireling.txt" DifficultyLevels = "/data/global/excel/difficultylevels.txt" + AutoMap = "/data/global/excel/AutoMap.txt" // --- Animations --- diff --git a/main.go b/main.go index 342e3fa6..016ba62d 100644 --- a/main.go +++ b/main.go @@ -402,6 +402,7 @@ func loadDataDict() error { {d2resource.Experience, d2datadict.LoadExperienceBreakpoints}, {d2resource.Gems, d2datadict.LoadGems}, {d2resource.DifficultyLevels, d2datadict.LoadDifficultyLevels}, + {d2resource.AutoMap, d2datadict.LoadAutoMaps}, } for _, entry := range entries {