1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2025-02-20 23:47:16 -05:00

remove lint errors from d2datadict (#676)

This commit is contained in:
lord 2020-08-04 08:16:06 -07:00 committed by GitHub
parent c216ddab53
commit 466855e5f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 1151 additions and 1087 deletions

View File

@ -38,6 +38,14 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2script"
)
// these are used for debug print info
const (
fpsX, fpsY = 5, 565
memInfoX, memInfoY = 670, 5
debugLineHeight = 16
errMsgPadding = 20
)
// App represents the main application for the engine
type App struct {
lastTime float64
@ -64,7 +72,6 @@ type bindTerminalEntry struct {
}
const (
defaultFPS = 0.04 // 1/25
bytesToMegabyte = 1024 * 1024
nSamplesTAlloc = 100
debugPopN = 6
@ -97,7 +104,6 @@ func Create(gitBranch, gitCommit string,
// Run executes the application and kicks off the entire game process
func (a *App) Run() error {
profileOption := kingpin.Flag("profile", "Profiles the program, one of (cpu, mem, block, goroutine, trace, thread, mutex)").String()
kingpin.Parse()
@ -138,7 +144,7 @@ func (a *App) initialize() error {
terminalActions := [...]bindTerminalEntry{
{"dumpheap", "dumps the heap to pprof/heap.pprof", a.dumpHeap},
{"fullscreen", "toggles fullscreen", a.toggleFullScreen},
{"capframe", "captures a still frame", a.captureFrame},
{"capframe", "captures a still frame", a.setupCaptureFrame},
{"capgifstart", "captures an animation (start)", a.startAnimationCapture},
{"capgifstop", "captures an animation (stop)", a.stopAnimationCapture},
{"vsync", "toggles vsync", a.toggleVsync},
@ -236,7 +242,7 @@ func (a *App) loadDataDict() error {
{d2resource.Experience, d2datadict.LoadExperienceBreakpoints},
{d2resource.Gems, d2datadict.LoadGems},
{d2resource.QualityItems, d2datadict.LoadQualityItems},
{d2resource.Runes, d2datadict.LoadRunes},
{d2resource.Runes, d2datadict.LoadRunewords},
{d2resource.DifficultyLevels, d2datadict.LoadDifficultyLevels},
{d2resource.AutoMap, d2datadict.LoadAutoMaps},
{d2resource.LevelDetails, d2datadict.LoadLevelDetails},
@ -252,7 +258,7 @@ func (a *App) loadDataDict() error {
{d2resource.SkillDesc, d2datadict.LoadSkillDescriptions},
{d2resource.ItemTypes, d2datadict.LoadItemTypes},
{d2resource.BodyLocations, d2datadict.LoadBodyLocations},
{d2resource.Sets, d2datadict.LoadSets},
{d2resource.Sets, d2datadict.LoadSetRecords},
{d2resource.SetItems, d2datadict.LoadSetItems},
{d2resource.AutoMagic, d2datadict.LoadAutoMagicRecords},
{d2resource.TreasureClass, d2datadict.LoadTreasureClassRecords},
@ -271,6 +277,8 @@ func (a *App) loadDataDict() error {
{d2resource.MonsterSequence, d2datadict.LoadMonsterSequences},
{d2resource.PlayerClass, d2datadict.LoadPlayerClasses},
{d2resource.MonsterPlacement, d2datadict.LoadMonsterPlacements},
{d2resource.CompCode, d2datadict.LoadComponentCodes},
{d2resource.MonsterAI, d2datadict.LoadMonsterAI},
}
d2datadict.InitObjectRecords()
@ -298,24 +306,24 @@ func (a *App) renderDebug(target d2interface.Surface) error {
fps := a.renderer.CurrentFPS()
cx, cy := a.renderer.GetCursorPos()
target.PushTranslation(5, 565)
target.PushTranslation(fpsX, fpsY)
target.DrawTextf("vsync:" + strconv.FormatBool(vsyncEnabled) + "\nFPS:" + strconv.Itoa(int(fps)))
target.Pop()
var m runtime.MemStats
runtime.ReadMemStats(&m)
target.PushTranslation(680, 0)
target.PushTranslation(memInfoX, memInfoY)
target.DrawTextf("Alloc " + strconv.FormatInt(int64(m.Alloc)/bytesToMegabyte, 10))
target.PushTranslation(0, 16)
target.PushTranslation(0, debugLineHeight)
target.DrawTextf("TAlloc/s " + strconv.FormatFloat(a.allocRate(m.TotalAlloc, fps), 'f', 2, 64))
target.PushTranslation(0, 16)
target.PushTranslation(0, debugLineHeight)
target.DrawTextf("Pause " + strconv.FormatInt(int64(m.PauseTotalNs/bytesToMegabyte), 10))
target.PushTranslation(0, 16)
target.PushTranslation(0, debugLineHeight)
target.DrawTextf("HeapSys " + strconv.FormatInt(int64(m.HeapSys/bytesToMegabyte), 10))
target.PushTranslation(0, 16)
target.PushTranslation(0, debugLineHeight)
target.DrawTextf("NumGC " + strconv.FormatInt(int64(m.NumGC), 10))
target.PushTranslation(0, 16)
target.PushTranslation(0, debugLineHeight)
target.DrawTextf("Coords " + strconv.FormatInt(int64(cx), 10) + "," + strconv.FormatInt(int64(cy), 10))
target.PopN(debugPopN)
@ -333,80 +341,18 @@ func (a *App) renderCapture(target d2interface.Surface) error {
case captureStateFrame:
defer cleanupCapture()
fp, err := os.Create(a.capturePath)
if err != nil {
if err := a.doCaptureFrame(target); err != nil {
return err
}
defer func() {
if err := fp.Close(); err != nil {
log.Fatal(err)
}
}()
screenshot := target.Screenshot()
if err := png.Encode(fp, screenshot); err != nil {
return err
}
log.Printf("saved frame to %s", a.capturePath)
case captureStateGif:
screenshot := target.Screenshot()
a.captureFrames = append(a.captureFrames, screenshot)
a.doCaptureGif(target)
case captureStateNone:
if len(a.captureFrames) > 0 {
defer cleanupCapture()
fp, err := os.Create(a.capturePath)
if err != nil {
if err := a.convertFramesToGif(); err != nil {
return err
}
defer func() {
if err := fp.Close(); err != nil {
log.Fatal(err)
}
}()
var (
framesTotal = len(a.captureFrames)
framesPal = make([]*image.Paletted, framesTotal)
frameDelays = make([]int, framesTotal)
framesPerCPU = framesTotal / runtime.NumCPU()
)
var waitGroup sync.WaitGroup
for i := 0; i < framesTotal; i += framesPerCPU {
waitGroup.Add(1)
go func(start, end int) {
defer waitGroup.Done()
for j := start; j < end; j++ {
var buffer bytes.Buffer
if err := gif.Encode(&buffer, a.captureFrames[j], nil); err != nil {
panic(err)
}
framePal, err := gif.Decode(&buffer)
if err != nil {
panic(err)
}
framesPal[j] = framePal.(*image.Paletted)
frameDelays[j] = 5
}
}(i, d2common.MinInt(i+framesPerCPU, framesTotal))
}
waitGroup.Wait()
if err := gif.EncodeAll(fp, &gif.GIF{Image: framesPal, Delay: frameDelays}); err != nil {
return err
}
log.Printf("saved animation to %s", a.capturePath)
}
}
@ -467,7 +413,7 @@ func (a *App) advance(elapsed, elapsedUnscaled, current float64) error {
func (a *App) update(target d2interface.Surface) error {
currentTime := d2common.Now()
elapsedTimeUnscaled := (currentTime - a.lastTime)
elapsedTimeUnscaled := currentTime - a.lastTime
elapsedTime := elapsedTimeUnscaled * a.timeScale
a.lastTime = currentTime
@ -526,12 +472,94 @@ func (a *App) toggleFullScreen() {
a.terminal.OutputInfof("fullscreen is now: %v", fullscreen)
}
func (a *App) captureFrame(path string) {
func (a *App) setupCaptureFrame(path string) {
a.captureState = captureStateFrame
a.capturePath = path
a.captureFrames = nil
}
func (a *App) doCaptureFrame(target d2interface.Surface) error {
fp, err := os.Create(a.capturePath)
if err != nil {
return err
}
defer func() {
if err := fp.Close(); err != nil {
log.Fatal(err)
}
}()
screenshot := target.Screenshot()
if err := png.Encode(fp, screenshot); err != nil {
return err
}
log.Printf("saved frame to %s", a.capturePath)
return nil
}
func (a *App) doCaptureGif(target d2interface.Surface) {
screenshot := target.Screenshot()
a.captureFrames = append(a.captureFrames, screenshot)
}
func (a *App) convertFramesToGif() error {
fp, err := os.Create(a.capturePath)
if err != nil {
return err
}
defer func() {
if err := fp.Close(); err != nil {
log.Fatal(err)
}
}()
var (
framesTotal = len(a.captureFrames)
framesPal = make([]*image.Paletted, framesTotal)
frameDelays = make([]int, framesTotal)
framesPerCPU = framesTotal / runtime.NumCPU()
)
var waitGroup sync.WaitGroup
for i := 0; i < framesTotal; i += framesPerCPU {
waitGroup.Add(1)
go func(start, end int) {
defer waitGroup.Done()
for j := start; j < end; j++ {
var buffer bytes.Buffer
if err := gif.Encode(&buffer, a.captureFrames[j], nil); err != nil {
panic(err)
}
framePal, err := gif.Decode(&buffer)
if err != nil {
panic(err)
}
framesPal[j] = framePal.(*image.Paletted)
frameDelays[j] = 5
}
}(i, d2common.MinInt(i+framesPerCPU, framesTotal))
}
waitGroup.Wait()
if err := gif.EncodeAll(fp, &gif.GIF{Image: framesPal, Delay: frameDelays}); err != nil {
return err
}
log.Printf("saved animation to %s", a.capturePath)
return nil
}
func (a *App) startAnimationCapture(path string) {
a.captureState = captureStateGif
a.capturePath = path
@ -626,26 +654,28 @@ func enableProfiler(profileOption string) interface{ Stop() } {
func updateInitError(target d2interface.Surface) error {
_ = target.Clear(colornames.Darkred)
width, height := target.GetSize()
target.PushTranslation(width/5, height/2)
target.PushTranslation(errMsgPadding, errMsgPadding)
target.DrawTextf(`Could not find the MPQ files in the directory:
%s\nPlease put the files and re-run the game.`, d2config.Config.MpqPath)
return nil
}
// ToMainMenu forces the game to transition to the Main Menu
func (a *App) ToMainMenu() {
mainMenu := d2gamescreen.CreateMainMenu(a, a.renderer, a.inputManager, a.audio, d2gamescreen.BuildInfo{Branch: a.gitBranch, Commit: a.gitCommit})
buildInfo := d2gamescreen.BuildInfo{Branch: a.gitBranch, Commit: a.gitCommit}
mainMenu := d2gamescreen.CreateMainMenu(a, a.renderer, a.inputManager, a.audio, buildInfo)
mainMenu.SetScreenMode(d2gamescreen.ScreenModeMainMenu)
d2screen.SetNextScreen(mainMenu)
}
// ToSelectHero forces the game to transition to the Select Hero (create character) screen
func (a *App) ToSelectHero(connType d2clientconnectiontype.ClientConnectionType, host string) {
selectHero := d2gamescreen.CreateSelectHeroClass(a, a.renderer, a.audio, connType, host)
d2screen.SetNextScreen(selectHero)
}
// ToCreateGame forces the game to transition to the Create Game screen
func (a *App) ToCreateGame(filePath string, connType d2clientconnectiontype.ClientConnectionType, host string) {
gameClient, _ := d2client.Create(connType, a.scriptEngine)
@ -657,16 +687,20 @@ func (a *App) ToCreateGame(filePath string, connType d2clientconnectiontype.Clie
d2screen.SetNextScreen(d2gamescreen.CreateGame(a, a.renderer, a.inputManager, a.audio, gameClient, a.terminal))
}
// ToCharacterSelect forces the game to transition to the Character Select (load character) screen
func (a *App) ToCharacterSelect(connType d2clientconnectiontype.ClientConnectionType, connHost string) {
characterSelect := d2gamescreen.CreateCharacterSelect(a, a.renderer, a.inputManager, a.audio, connType, connHost)
d2screen.SetNextScreen(characterSelect)
}
func (a *App) ToMapEngineTest(region int, level int) {
met := d2gamescreen.CreateMapEngineTest(0, 1, a.terminal, a.renderer, a.inputManager, a.audio)
// ToMapEngineTest forces the game to transition to the map engine test screen
func (a *App) ToMapEngineTest(region, level int) {
met := d2gamescreen.CreateMapEngineTest(region, level, a.terminal, a.renderer, a.inputManager,
a.audio)
d2screen.SetNextScreen(met)
}
// ToCredits forces the game to transition to the credits screen
func (a *App) ToCredits() {
d2screen.SetNextScreen(d2gamescreen.CreateCredits(a, a.renderer))
}

View File

@ -6,16 +6,21 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
// The skillcalc.txt and misscalc.txt files are essentially lookup tables for the Skills.txt and Missiles.txt Calc functions
// To avoid duplication (since they have identical fields) they are both represented by the CalculationRecord type
// CalculationRecord The skillcalc.txt and misscalc.txt files are essentially lookup tables
// for the Skills.txt and Missiles.txt Calc functions To avoid duplication (since they have
// identical fields) they are both represented by the CalculationRecord type
type CalculationRecord struct {
Code string
Description string
}
var SkillCalculations map[string]*CalculationRecord
var MissileCalculations map[string]*CalculationRecord
// SkillCalculations is where calculation records are stored
var SkillCalculations map[string]*CalculationRecord //nolint:gochecknoglobals // Currently global by design
// MissileCalculations is where missile calculations are stored
var MissileCalculations map[string]*CalculationRecord //nolint:gochecknoglobals // Currently global by design
// LoadSkillCalculations loads skill calculation records from skillcalc.txt
func LoadSkillCalculations(file []byte) {
SkillCalculations = make(map[string]*CalculationRecord)
@ -35,6 +40,7 @@ func LoadSkillCalculations(file []byte) {
log.Printf("Loaded %d Skill Calculation records", len(SkillCalculations))
}
// LoadMissileCalculations loads missile calculation records from misscalc.txt
func LoadMissileCalculations(file []byte) {
MissileCalculations = make(map[string]*CalculationRecord)
@ -45,7 +51,6 @@ func LoadMissileCalculations(file []byte) {
Description: d.String("*desc"),
}
MissileCalculations[record.Code] = record
}
if d.Err != nil {

View File

@ -6,13 +6,17 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
// ComponentCodeRecord represents a single row from compcode.txt
type ComponentCodeRecord struct {
Component string
Code string
}
var ComponentCodes map[string]*ComponentCodeRecord
// ComponentCodes is a lookup table for DCC Animation Component Subtype,
// it links hardcoded data with the txt files
var ComponentCodes map[string]*ComponentCodeRecord //nolint:gochecknoglobals // Currently global by design
// LoadComponentCodes loads components code records from compcode.txt
func LoadComponentCodes(file []byte) {
ComponentCodes = make(map[string]*ComponentCodeRecord)
@ -30,5 +34,4 @@ func LoadComponentCodes(file []byte) {
}
log.Printf("Loaded %d ComponentCode records", len(ComponentCodes))
}

View File

@ -1,3 +1,8 @@
// Package d2datadict parses txt files as data dictionaries and exports records arrays
// For the Diablo II MPQ's, data dictionaries are the tab-separated value txt files found in `data/global/excel`
package d2datadict
const (
expansion = "Expansion"
expansionCode = 100
)

View File

@ -6,19 +6,19 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
//ElemTypeRecord represents a single line in ElemType.txt
// ElemTypeRecord represents a single line in ElemType.txt
type ElemTypeRecord struct {
//Elemental damage type name
// ElemType Elemental damage type name
ElemType string
//Elemental damage type code
// Code Elemental damage type code
Code string
}
//ElemTypes stores the ElemTypeRecords
// ElemTypes stores the ElemTypeRecords
var ElemTypes map[string]*ElemTypeRecord //nolint:gochecknoglobals // Currently global by design
//LoadElemTypes loads ElemTypeRecords into ElemTypes
// LoadElemTypes loads ElemTypeRecords into ElemTypes
func LoadElemTypes(file []byte) {
ElemTypes = make(map[string]*ElemTypeRecord)

View File

@ -6,12 +6,15 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
// EventRecord is a representation of a single row from events.txt
type EventRecord struct {
Event string
}
var Events map[string]*EventRecord
// Events holds all of the event records from events.txt
var Events map[string]*EventRecord //nolint:gochecknoglobals // Currently global by design
// LoadEvents loads all of the event records from events.txt
func LoadEvents(file []byte) {
Events = make(map[string]*EventRecord)
@ -28,5 +31,4 @@ func LoadEvents(file []byte) {
}
log.Printf("Loaded %d Event records", len(Events))
}

View File

@ -1,9 +1,10 @@
package d2datadict
import (
"log"
"github.com/OpenDiablo2/OpenDiablo2/d2common"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"log"
)
type box struct {
@ -16,13 +17,15 @@ type box struct {
}
type grid struct {
Box *box
Rows int
Columns int
CellWidth int
Box *box
Rows int
Columns int
CellWidth int
CellHeight int
}
// InventoryRecord represents a single row from inventory.txt, it describes the grid
// layout and positioning of various inventory-related ui panels.
type InventoryRecord struct {
Name string
Panel *box
@ -30,11 +33,14 @@ type InventoryRecord struct {
Slots map[d2enum.EquippedSlot]*box
}
var Inventory map[string]*InventoryRecord
// Inventory holds all of the inventory records from inventory.txt
var Inventory map[string]*InventoryRecord //nolint:gochecknoglobals // Currently global by design
func LoadInventory(file []byte) {
// LoadInventory loads all of the inventory records from inventory.txt
func LoadInventory(file []byte) { //nolint:funlen // doesn't make sense to split
d := d2common.LoadDataDictionary(file)
Inventory = make(map[string]*InventoryRecord, 0)
Inventory = make(map[string]*InventoryRecord)
for d.Next() {
// we need to calc the width/height for the box as it isn't
// specified in the txt file
@ -59,9 +65,9 @@ func LoadInventory(file []byte) {
Name: d.String("class"),
Panel: pBox,
Grid: &grid{
Box: gBox,
Rows: d.Number("gridY"),
Columns: d.Number("gridX"),
Box: gBox,
Rows: d.Number("gridY"),
Columns: d.Number("gridX"),
CellWidth: d.Number("gridBoxWidth"),
CellHeight: d.Number("gridBoxHeight"),
},

View File

@ -9,11 +9,12 @@ import (
)
// MagicPrefix stores all of the magic prefix records
var MagicPrefix map[string]*ItemAffixCommonRecord //nolint:gochecknoglobals // Currently global by
// design
// nolint:gochecknoglobals // Currently global by design
var MagicPrefix map[string]*ItemAffixCommonRecord
// MagicSuffix stores all of the magic suffix records
var MagicSuffix map[string]*ItemAffixCommonRecord //nolint:gochecknoglobals // Currently global by
// design
// nolint:gochecknoglobals // Currently global by design
var MagicSuffix map[string]*ItemAffixCommonRecord
// LoadMagicPrefix loads MagicPrefix.txt
func LoadMagicPrefix(file []byte) {
@ -137,6 +138,7 @@ func createItemAffixRecords(
records[affix.Name] = affix
}
if d.Err != nil {
panic(d.Err)
}
@ -220,7 +222,7 @@ type ItemAffixCommonRecord struct {
// item with a given quality level
func (a *ItemAffixCommonRecord) ProbabilityToSpawn(qlvl int) float64 {
if (qlvl > a.MaxLevel) || (qlvl < a.Level) {
return 0.0
return 0
}
p := float64(a.Frequency) / float64(a.Group.getTotalFrequency())

View File

@ -177,7 +177,8 @@ func LoadCommonItems(file []byte, source d2enum.InventoryItemType) map[string]*I
}
rec := createCommonItemRecord(line, mapping, source)
if rec.Name == "Expansion" {
if rec.Name == expansion {
continue
}
@ -396,4 +397,3 @@ func createItemUsageStats(r *[]string, mapping map[string]int) [3]ItemUsageStat
return result
}

View File

@ -14,8 +14,9 @@ type dropRatioInfo struct {
divisorMin int
}
//ItemRatioRecord encapsulates information found in ItemRatio.txt
//The information has been gathered from [https://d2mods.info/forum/kb/viewarticle?a=387]
// ItemRatioRecord encapsulates information found in ItemRatio.txt, it specifies drop ratios
// for various types of items
// The information has been gathered from [https://d2mods.info/forum/kb/viewarticle?a=387]
type ItemRatioRecord struct {
Function string
// 0 for classic, 1 for LoD
@ -34,8 +35,10 @@ type ItemRatioRecord struct {
NormalDropInfo dropRatioInfo
}
var ItemRatios map[string]*ItemRatioRecord
// ItemRatios holds all of the ItemRatioRecords from ItemRatio.txt
var ItemRatios map[string]*ItemRatioRecord //nolint:gochecknoglobals // Currently global by design
// LoadItemRatios loads all of the ItemRatioRecords from ItemRatio.txt
func LoadItemRatios(file []byte) {
ItemRatios = make(map[string]*ItemRatioRecord)

View File

@ -257,7 +257,7 @@ func LoadItemTypes(file []byte) {
}
// ItemEquivalenciesByTypeCode describes item equivalencies for ItemTypes
var ItemEquivalenciesByTypeCode map[string][]*ItemCommonRecord
var ItemEquivalenciesByTypeCode map[string][]*ItemCommonRecord //nolint:gochecknoglobals // Currently global by design
// LoadItemEquivalencies loads a map of ItemType string codes to slices of ItemCommonRecord pointers
func LoadItemEquivalencies() {
@ -329,8 +329,10 @@ func itemEquivPresent(icr *ItemCommonRecord, list []*ItemCommonRecord) bool {
return false
}
var itemCommonTypeLookup map[*ItemCommonRecord][]string
var itemCommonTypeLookup map[*ItemCommonRecord][]string //nolint:gochecknoglobals // Currently global by design
// FindEquivalentTypesByItemCommonRecord returns itemtype codes that are equivalent
// to the given item common record
func FindEquivalentTypesByItemCommonRecord(icr *ItemCommonRecord) []string {
if itemCommonTypeLookup == nil {
itemCommonTypeLookup = make(map[*ItemCommonRecord][]string)

View File

@ -1,9 +1,10 @@
package d2datadict
import (
"log"
"github.com/OpenDiablo2/OpenDiablo2/d2common"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"log"
)
// ItemStatCostRecord represents a row from itemstatcost.txt

View File

@ -1,113 +1,47 @@
package d2datadict
import (
"fmt"
"log"
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
const (
numMonProps = 6
fmtProp = "prop%d%s"
fmtChance = "chance%d%s"
fmtPar = "par%d%s"
fmtMin = "min%d%s"
fmtMax = "max%d%s"
fmtNormal = ""
fmtNightmare = " (N)"
fmtHell = " (H)"
)
// MonPropRecord is a representation of a single row of monprop.txt
type MonPropRecord struct {
ID string
Prop1 string
Chance1 int
Parameter1 string
Min1 int
Max1 int
Prop2 string
Chance2 int
Parameter2 string
Min2 int
Max2 int
Prop3 string
Chance3 int
Parameter3 string
Min3 int
Max3 int
Prop4 string
Chance4 int
Parameter4 string
Min4 int
Max4 int
Prop5 string
Chance5 int
Parameter5 string
Min5 int
Max5 int
Prop6 string
Chance6 int
Parameter6 string
Min6 int
Max6 int
Prop1N string
Chance1N int
Parameter1N string
Min1N int
Max1N int
Prop2N string
Chance2N int
Parameter2N string
Min2N int
Max2N int
Prop3N string
Chance3N int
Parameter3N string
Min3N int
Max3N int
Prop4N string
Chance4N int
Parameter4N string
Min4N int
Max4N int
Prop5N string
Chance5N int
Parameter5N string
Min5N int
Max5N int
Prop6N string
Chance6N int
Parameter6N string
Min6N int
Max6N int
Prop1H string
Chance1H int
Parameter1H string
Min1H int
Max1H int
Prop2H string
Chance2H int
Parameter2H string
Min2H int
Max2H int
Prop3H string
Chance3H int
Parameter3H string
Min3H int
Max3H int
Prop4H string
Chance4H int
Parameter4H string
Min4H int
Max4H int
Prop5H string
Chance5H int
Parameter5H string
Min5H int
Max5H int
Prop6H string
Chance6H int
Parameter6H string
Min6H int
Max6H int
Properties struct {
Normal [numMonProps]*monProp
Nightmare [numMonProps]*monProp
Hell [numMonProps]*monProp
}
}
// EOL int // unused
type monProp struct {
Code string
Param string
Chance int
Min int
Max int
}
// MonProps stores all of the MonPropRecords
var MonProps map[string]*MonPropRecord //nolint:gochecknoglobals // Currently global by design, only written once
// LoadMonProps loads Monprop records into a map[string]*MonPropRecord
// LoadMonProps loads monster property records into a map[string]*MonPropRecord
func LoadMonProps(file []byte) {
MonProps = make(map[string]*MonPropRecord)
@ -116,97 +50,43 @@ func LoadMonProps(file []byte) {
record := &MonPropRecord{
ID: d.String("Id"),
Prop1: d.String("prop1"),
Chance1: d.Number("chance1"),
Parameter1: d.String("par1"),
Min1: d.Number("min1"),
Max1: d.Number("max1"),
Prop2: d.String("prop2"),
Chance2: d.Number("chance2"),
Parameter2: d.String("par2"),
Min2: d.Number("min2"),
Max2: d.Number("max2"),
Prop3: d.String("prop3"),
Chance3: d.Number("chance3"),
Parameter3: d.String("par3"),
Min3: d.Number("min3"),
Max3: d.Number("max3"),
Prop4: d.String("prop4"),
Chance4: d.Number("chance4"),
Parameter4: d.String("par4"),
Min4: d.Number("min4"),
Max4: d.Number("max4"),
Prop5: d.String("prop5"),
Chance5: d.Number("chance5"),
Parameter5: d.String("par5"),
Min5: d.Number("min5"),
Max5: d.Number("max5"),
Prop6: d.String("prop6"),
Chance6: d.Number("chance6"),
Parameter6: d.String("par6"),
Min6: d.Number("min6"),
Max6: d.Number("max6"),
Prop1N: d.String("prop1 (N)"),
Chance1N: d.Number("chance1 (N)"),
Parameter1N: d.String("par1 (N)"),
Min1N: d.Number("min1 (N)"),
Max1N: d.Number("max1 (N)"),
Prop2N: d.String("prop2 (N)"),
Chance2N: d.Number("chance2 (N)"),
Parameter2N: d.String("par2 (N)"),
Min2N: d.Number("min2 (N)"),
Max2N: d.Number("max2 (N)"),
Prop3N: d.String("prop3 (N)"),
Chance3N: d.Number("chance3 (N)"),
Parameter3N: d.String("par3 (N)"),
Min3N: d.Number("min3 (N)"),
Max3N: d.Number("max3 (N)"),
Prop4N: d.String("prop4 (N)"),
Chance4N: d.Number("chance4 (N)"),
Parameter4N: d.String("par4 (N)"),
Min4N: d.Number("min4 (N)"),
Max4N: d.Number("max4 (N)"),
Prop5N: d.String("prop5 (N)"),
Chance5N: d.Number("chance5 (N)"),
Parameter5N: d.String("par5 (N)"),
Min5N: d.Number("min5 (N)"),
Max5N: d.Number("max5 (N)"),
Prop6N: d.String("prop6 (N)"),
Chance6N: d.Number("chance6 (N)"),
Parameter6N: d.String("par6 (N)"),
Min6N: d.Number("min6 (N)"),
Max6N: d.Number("max6 (N)"),
Prop1H: d.String("prop1 (H)"),
Chance1H: d.Number("chance1 (H)"),
Parameter1H: d.String("par1 (H)"),
Min1H: d.Number("min1 (H)"),
Max1H: d.Number("max1 (H)"),
Prop2H: d.String("prop2 (H)"),
Chance2H: d.Number("chance2 (H)"),
Parameter2H: d.String("par2 (H)"),
Min2H: d.Number("min2 (H)"),
Max2H: d.Number("max2 (H)"),
Prop3H: d.String("prop3 (H)"),
Chance3H: d.Number("chance3 (H)"),
Parameter3H: d.String("par3 (H)"),
Min3H: d.Number("min3 (H)"),
Max3H: d.Number("max3 (H)"),
Prop4H: d.String("prop4 (H)"),
Chance4H: d.Number("chance4 (H)"),
Parameter4H: d.String("par4 (H)"),
Min4H: d.Number("min4 (H)"),
Max4H: d.Number("max4 (H)"),
Prop5H: d.String("prop5 (H)"),
Chance5H: d.Number("chance5 (H)"),
Parameter5H: d.String("par5 (H)"),
Min5H: d.Number("min5 (H)"),
Max5H: d.Number("max5 (H)"),
Prop6H: d.String("prop6 (H)"),
Chance6H: d.Number("chance6 (H)"),
Parameter6H: d.String("par6 (H)"),
Min6H: d.Number("min6 (H)"),
Max6H: d.Number("max6 (H)"),
Properties: struct {
Normal [numMonProps]*monProp
Nightmare [numMonProps]*monProp
Hell [numMonProps]*monProp
}{
[numMonProps]*monProp{},
[numMonProps]*monProp{},
[numMonProps]*monProp{},
},
}
for idx := 1; idx <= numMonProps; idx++ {
record.Properties.Normal[idx-1] = &monProp{
Code: d.String(fmt.Sprintf(fmtProp, idx, fmtNormal)),
Param: d.String(fmt.Sprintf(fmtPar, idx, fmtNormal)),
Chance: d.Number(fmt.Sprintf(fmtChance, idx, fmtNormal)),
Min: d.Number(fmt.Sprintf(fmtMin, idx, fmtNormal)),
Max: d.Number(fmt.Sprintf(fmtMax, idx, fmtNormal)),
}
record.Properties.Nightmare[idx-1] = &monProp{
Code: d.String(fmt.Sprintf(fmtProp, idx, fmtNightmare)),
Param: d.String(fmt.Sprintf(fmtPar, idx, fmtNightmare)),
Chance: d.Number(fmt.Sprintf(fmtChance, idx, fmtNightmare)),
Min: d.Number(fmt.Sprintf(fmtMin, idx, fmtNightmare)),
Max: d.Number(fmt.Sprintf(fmtMax, idx, fmtNightmare)),
}
record.Properties.Hell[idx-1] = &monProp{
Code: d.String(fmt.Sprintf(fmtProp, idx, fmtHell)),
Param: d.String(fmt.Sprintf(fmtPar, idx, fmtHell)),
Chance: d.Number(fmt.Sprintf(fmtChance, idx, fmtHell)),
Min: d.Number(fmt.Sprintf(fmtMin, idx, fmtHell)),
Max: d.Number(fmt.Sprintf(fmtMax, idx, fmtHell)),
}
}
MonProps[record.ID] = record
}

View File

@ -6,13 +6,15 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
// The monai.txt file is a lookup table for unit AI codes
// MonsterAIRecord represents a single row from monai.txt
type MonsterAIRecord struct {
AI string
}
var MonsterAI map[string]*MonsterAIRecord
// MonsterAI holds the MonsterAIRecords, The monai.txt file is a lookup table for unit AI codes
var MonsterAI map[string]*MonsterAIRecord //nolint:gochecknoglobals // Currently global by design
// LoadMonsterAI loads MonsterAIRecords from monai.txt
func LoadMonsterAI(file []byte) {
MonsterAI = make(map[string]*MonsterAIRecord)
@ -29,5 +31,4 @@ func LoadMonsterAI(file []byte) {
}
log.Printf("Loaded %d MonsterAI records", len(MonsterAI))
}

View File

@ -1,71 +1,82 @@
package d2datadict
import (
"fmt"
"log"
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
//MonsterEquipmentRecord represents a single line in monequip.txt
//Information gathered from [https://d2mods.info/forum/kb/viewarticle?a=365]
type MonsterEquipmentRecord struct {
//Name of monster, pointer to MonStats.txt
Monster string
const (
numMonEquippedItems = 3
fmtLocation = "loc%d"
fmtQuality = "mod%d"
fmtCode = "item%d"
)
//If true, monster is created by level, otherwise created by skill
// MonsterEquipmentRecord represents a single line in monequip.txt
// Information gathered from [https://d2mods.info/forum/kb/viewarticle?a=365]
type MonsterEquipmentRecord struct {
// Name of monster, pointer to MonStats.txt
Name string
// If true, monster is created by level, otherwise created by skill
OnInit bool
//Not written in description, only appear on monsters with OnInit false
//Level of skill for which this equipment row can be used?
// Not written in description, only appear on monsters with OnInit false,
// Level of skill for which this equipment row can be used?
Level int
//Code of item
Item1 string
//Slot of equipped item
Location1 string
//Item quality
Mod1 int
//Ditto, 3 items maximum
Item2 string
Location2 string
Mod2 int
Item3 string
Location3 string
Mod3 int
Equipment []*monEquip
}
//MonsterEquipment stores the MonsterEquipmentRecords
type monEquip struct {
// Code of item, probably from ItemCommonRecords
Code string
// Location the body location of the item
Location string
// Quality of the item
Quality int
}
// MonsterEquipment stores the MonsterEquipmentRecords
var MonsterEquipment map[string][]*MonsterEquipmentRecord //nolint:gochecknoglobals // Currently global by design
//LoadMonsterEquipment loads MonsterEquipmentRecords into MonsterEquipment
// LoadMonsterEquipment loads MonsterEquipmentRecords into MonsterEquipment
func LoadMonsterEquipment(file []byte) {
MonsterEquipment = make(map[string][]*MonsterEquipmentRecord)
d := d2common.LoadDataDictionary(file)
for d.Next() {
record := &MonsterEquipmentRecord{
Monster: d.String("monster"),
Name: d.String("monster"),
OnInit: d.Bool("oninit"),
Level: d.Number("level"),
Item1: d.String("item1"),
Location1: d.String("loc1"),
Mod1: d.Number("mod1"),
Item2: d.String("item2"),
Location2: d.String("loc2"),
Mod2: d.Number("mod2"),
Item3: d.String("item3"),
Location3: d.String("loc3"),
Mod3: d.Number("mod3"),
Equipment: make([]*monEquip, 0),
}
if _, ok := MonsterEquipment[record.Monster]; !ok {
MonsterEquipment[record.Monster] = make([]*MonsterEquipmentRecord, 0)
for idx := 0; idx < numMonEquippedItems; idx++ {
num := idx + 1
code := d.String(fmt.Sprintf(fmtCode, num))
location := d.String(fmt.Sprintf(fmtLocation, num))
quality := d.Number(fmt.Sprintf(fmtQuality, num))
if code == "" {
continue
}
equip := &monEquip{code, location, quality}
record.Equipment = append(record.Equipment, equip)
}
MonsterEquipment[record.Monster] = append(MonsterEquipment[record.Monster], record)
if _, ok := MonsterEquipment[record.Name]; !ok {
MonsterEquipment[record.Name] = make([]*MonsterEquipmentRecord, 0)
}
MonsterEquipment[record.Name] = append(MonsterEquipment[record.Name], record)
}
if d.Err != nil {
@ -76,5 +87,6 @@ func LoadMonsterEquipment(file []byte) {
for k := range MonsterEquipment {
length += len(MonsterEquipment[k])
}
log.Printf("Loaded %d MonsterEquipment records", length)
}

View File

@ -6,60 +6,55 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
//MonsterLevelRecord represents a single row in monlvl.txt
// MonsterLevelRecord represents a single row in monlvl.txt
type MonsterLevelRecord struct {
//The level
// The level
Level int
//Values for Battle.net
BattleNet monsterLevelValues
// Values for Battle.net
BattleNet monsterDifficultyLevels
//Values for ladder/single player/lan
Ladder monsterLevelValues
// Values for ladder/single player/lan
Ladder monsterDifficultyLevels
}
type monsterDifficultyLevels struct {
Normal monsterLevelValues
Nightmare monsterLevelValues
Hell monsterLevelValues
}
type monsterLevelValues struct {
//Armor class
//An AC is calcuated as (MonLvl.txt Ac * Monstats.txt AC) / 100)
ArmorClass int
ArmorClassNightmare int
ArmorClassHell int
// DefenseRating AC is calcuated as (MonLvl.txt Ac * Monstats.txt AC) / 100)
DefenseRating int // also known as armor class
//To hit, influences ToHit values for both attacks
// ToHit influences ToHit values for both attacks
// (MonLvl.txt TH * Monstats.txt A1TH
// and MonLvl.txt TH * Monstats.txt A2TH) / 100
ToHit int
ToHitNightmare int
ToHitHell int
AttackRating int
//Hitpoints, influences both minimum and maximum HP
//(MonLvl.txt HP * Monstats.txt minHP) / 100
// Hitpoints, influences both minimum and maximum HP
// (MonLvl.txt HP * Monstats.txt minHP) / 100
// and MonLvl.txt HP * Monstats.txt maxHP) / 100
Hitpoints int
HitpointsNightmare int
HitpointsHell int
Hitpoints int
//Damage, influences minimum and maximum damage for both attacks
//MonLvl.txt DM * Monstats.txt A1MinD) / 100
// Damage, influences minimum and maximum damage for both attacks
// MonLvl.txt DM * Monstats.txt A1MinD) / 100
// and MonLvl.txt DM * Monstats.txt A1MaxD) / 100
// and MonLvl.txt DM * Monstats.txt A2MinD) / 100
// and MonLvl.txt DM * Monstats.txt A2MaxD) / 100
Damage int
DamageNightmare int
DamageHell int
Damage int
//Experience points
//The formula is (MonLvl.txt XP * Monstats.txt Exp) / 100
Experience int
ExperienceNightmare int
ExperienceHell int
// Experience points,
// the formula is (MonLvl.txt XP * Monstats.txt Exp) / 100
Experience int
}
//MonsterLevels stores the MonsterLevelRecords
var MonsterLevels map[int]*MonsterLevelRecord
// MonsterLevels stores the MonsterLevelRecords
var MonsterLevels map[int]*MonsterLevelRecord //nolint:gochecknoglobals // Currently global by design
//LoadMonsterLevels loads LoadMonsterLevelRecords into MonsterLevels
// LoadMonsterLevels loads LoadMonsterLevelRecords into MonsterLevels
func LoadMonsterLevels(file []byte) {
MonsterLevels = make(map[int]*MonsterLevelRecord)
@ -67,39 +62,39 @@ func LoadMonsterLevels(file []byte) {
for d.Next() {
record := &MonsterLevelRecord{
Level: d.Number("Level"),
BattleNet: monsterLevelValues{
ArmorClass: d.Number("AC"),
ArmorClassNightmare: d.Number("AC(N)"),
ArmorClassHell: d.Number("AC(H)"),
ToHit: d.Number("TH"),
ToHitNightmare: d.Number("TH (N)"),
ToHitHell: d.Number("TH (H)"),
Hitpoints: d.Number("HP"),
HitpointsNightmare: d.Number("HP (N)"),
HitpointsHell: d.Number("HP (H)"),
Damage: d.Number("DM"),
DamageNightmare: d.Number("DM (N)"),
DamageHell: d.Number("DM (H)"),
Experience: d.Number("XP"),
ExperienceNightmare: d.Number("XP (N)"),
ExperienceHell: d.Number("XP (H)"),
BattleNet: monsterDifficultyLevels{
Normal: monsterLevelValues{
Hitpoints: d.Number("HP"),
Damage: d.Number("DM"),
Experience: d.Number("XP"),
},
Nightmare: monsterLevelValues{
Hitpoints: d.Number("HP(N)"),
Damage: d.Number("DM(N)"),
Experience: d.Number("XP(N)"),
},
Hell: monsterLevelValues{
Hitpoints: d.Number("HP(H)"),
Damage: d.Number("DM(H)"),
Experience: d.Number("XP(H)"),
},
},
Ladder: monsterLevelValues{
ArmorClass: d.Number("L-AC"),
ArmorClassNightmare: d.Number("L-AC(N)"),
ArmorClassHell: d.Number("L-AC(H)"),
ToHit: d.Number("L-TH"),
ToHitNightmare: d.Number("L-TH (N)"),
ToHitHell: d.Number("L-TH (H)"),
Hitpoints: d.Number("L-HP"),
HitpointsNightmare: d.Number("L-HP (N)"),
HitpointsHell: d.Number("L-HP (H)"),
Damage: d.Number("L-DM"),
DamageNightmare: d.Number("L-DM (N)"),
DamageHell: d.Number("L-DM (H)"),
Experience: d.Number("L-XP"),
ExperienceNightmare: d.Number("L-XP (N)"),
ExperienceHell: d.Number("L-XP (H)"),
Ladder: monsterDifficultyLevels{
Normal: monsterLevelValues{
Hitpoints: d.Number("L-HP"),
Damage: d.Number("L-DM"),
Experience: d.Number("L-XP"),
},
Nightmare: monsterLevelValues{
Hitpoints: d.Number("L-HP(N)"),
Damage: d.Number("L-DM(N)"),
Experience: d.Number("L-XP(N)"),
},
Hell: monsterLevelValues{
Hitpoints: d.Number("L-HP(H)"),
Damage: d.Number("L-DM(H)"),
Experience: d.Number("L-XP(H)"),
},
},
}
MonsterLevels[record.Level] = record

View File

@ -6,44 +6,46 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
//MonsterSequenceRecord contains a record for a monster sequence
//Composed of multiple lines from monseq.txt with the same name in the first column.
//Information gathered from [https://d2mods.info/forum/kb/viewarticle?a=395]
// MonsterSequenceRecord contains a record for a monster sequence
// Composed of multiple lines from monseq.txt with the same name in the first column.
// Information gathered from [https://d2mods.info/forum/kb/viewarticle?a=395]
type MonsterSequenceRecord struct {
//The sequence name, refered to by monstats.txt
// Name of the sequence, referred to by monstats.txt
Name string
//The frames of this sequence
// Frames of this sequence
Frames []*MonsterSequenceFrame
}
//MonsterSequenceFrame represents a single frame of a monster sequence
// MonsterSequenceFrame represents a single frame of a monster sequence
type MonsterSequenceFrame struct {
//The animation mode for this frame (refers to MonMode.txt)
// The animation mode for this frame (refers to MonMode.txt)
Mode string
//The frame of the animation mode used for this frame of the sequence
// The frame of the animation mode used for this frame of the sequence
Frame int
//The direction of the frame, enumerated by d2enum.AnimationFrameDirection
// Direction of the frame, enumerated by d2enum.AnimationFrameDirection
Direction int
//Event trigerred by this frame
// Event triggered by this frame
Event int
}
//MonsterSequences contains the MonsterSequenceRecords
// MonsterSequences contains the MonsterSequenceRecords
// nolint:gochecknoglobals // Currently global by design
var MonsterSequences map[string]*MonsterSequenceRecord
//LoadMonsterSequences loads the MonsterSequenceRecords into MonsterSequences
// LoadMonsterSequences loads the MonsterSequenceRecords into MonsterSequences
func LoadMonsterSequences(file []byte) {
MonsterSequences = make(map[string]*MonsterSequenceRecord)
d := d2common.LoadDataDictionary(file)
for d.Next() {
for d.Next() {
name := d.String("sequence")
if _, ok := MonsterSequences[name]; !ok {
record := &MonsterSequenceRecord{
Name: name,
@ -51,6 +53,7 @@ func LoadMonsterSequences(file []byte) {
}
MonsterSequences[name] = record
}
MonsterSequences[name].Frames = append(MonsterSequences[name].Frames, &MonsterSequenceFrame{
Mode: d.String("mode"),
Frame: d.Number("frame"),

View File

@ -6,31 +6,32 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
//MonsterSoundRecord represents a single line in MonSounds.txt
//Information gathered from [https://d2mods.info/forum/kb/viewarticle?a=418]
// Information gathered from [https://d2mods.info/forum/kb/viewarticle?a=418]
// MonsterSoundRecord represents a single line in MonSounds.txt
type MonsterSoundRecord struct {
//ID is the identifier, used in MonStats.txt to refer to a particular sound record
// ID is the identifier, used in MonStats.txt to refer to a particular sound record
ID string
//Melee attack sound ID, refers to a sound from Sounds.txt
// Melee attack sound ID, refers to a sound from Sounds.txt
Attack1 string
//Weapon attack sound ID, refers to a sound from Sounds.txt
// Weapon attack sound ID, refers to a sound from Sounds.txt
Weapon1 string
//Delay in frames of Attack1 sound
// Delay in frames of Attack1 sound
Attack1Delay int
//Delay in frames of Weapon1 sound
// Delay in frames of Weapon1 sound
Weapon1Delay int
//Probability of playing Attack1 sound instead of Weapon1
// Probability of playing Attack1 sound instead of Weapon1
Attack1Probability int
//Overrides weapon volume from Sounds.txt
// Overrides weapon volume from Sounds.txt
Weapon1Volume int
//Ditto, 2 sets of sounds are possible
// Ditto, 2 sets of sounds are possible
Attack2 string
Weapon2 string
Attack2Delay int
@ -38,60 +39,60 @@ type MonsterSoundRecord struct {
Attack2Probability int
Weapon2Volume int
//Sound when monster takes a hit, refers to a sound from Sounds.txt
// Sound when monster takes a hit, refers to a sound from Sounds.txt
HitSound string
//Sound when monster dies, refers to a sound from Sounds.txt
// Sound when monster dies, refers to a sound from Sounds.txt
DeathSound string
//Delay in frames of HitSound
// Delay in frames of HitSound
HitDelay int
//Delay in frames of DeathSound
// Delay in frames of DeathSound
DeaDelay int
//Sound when monster enters skill mode
// Sound when monster enters skill mode
Skill1 string
Skill2 string
Skill3 string
Skill4 string
//Sound played each loop of the WL animation
// Sound played each loop of the WL animation
Footstep string
//Additional WL animation sound
// Additional WL animation sound
FootstepLayer string
//Number of footstep sounds played (e.g. 2 for two-legged monsters)
// Number of footstep sounds played (e.g. 2 for two-legged monsters)
FootstepCount int
//FsOff, possibly delay between footstep sounds
// FsOff, possibly delay between footstep sounds
FootstepOffset int
//Probability of playing footstep sound, percentage
// Probability of playing footstep sound, percentage
FootstepProbability int
//Sound when monster is neutral (also played when walking)
// Sound when monster is neutral (also played when walking)
Neutral string
//Delay in frames between neutral sounds
// Delay in frames between neutral sounds
NeutralTime int
//Sound when monster is initialized
// Sound when monster is initialized
Init string
//Sound when monster is encountered
// Sound when monster is encountered
Taunt string
//Sound when monster retreats
// Sound when monster retreats
Flee string
//The following are related to skills in some way
//Initial monster animation code (MonMode.txt)
// The following are related to skills in some way
// Initial monster animation code (MonMode.txt)
CvtMo1 string
//ID of skill
// ID of skill
CvtSk1 string
//End monster animation code (MonMode.txt)
// End monster animation code (MonMode.txt)
CvtTgt1 string
CvtMo2 string
@ -103,10 +104,11 @@ type MonsterSoundRecord struct {
CvtTgt3 string
}
//MonsterSounds stores the MonsterSoundRecords
// MonsterSounds stores the MonsterSoundRecords
//nolint:gochecknoglobals // Currently global by design
var MonsterSounds map[string]*MonsterSoundRecord
//LoadMonsterSounds loads MonsterSoundRecords into MonsterSounds
// LoadMonsterSounds loads MonsterSoundRecords into MonsterSounds
func LoadMonsterSounds(file []byte) {
MonsterSounds = make(map[string]*MonsterSoundRecord)

View File

@ -6,87 +6,108 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
//MonsterUniqueModifierRecord represents a single line in monumod.txt
//Information gathered from [https://d2mods.info/forum/kb/viewarticle?a=161]
const (
numModifierConstants = 34
)
// MonsterUniqueModifierRecord represents a single line in monumod.txt
// Information gathered from [https://d2mods.info/forum/kb/viewarticle?a=161]
type MonsterUniqueModifierRecord struct {
//Name of modifer, not used by other files
// Name of modifer, not used by other files
Name string
//ID of the modifier
//The Mod fields of SuperUniqueRecord refer to these ID's
// ID of the modifier,
// the Mod fields of SuperUniqueRecord refer to these ID's
ID int
//If true, this modifier can be applied
// Enabled boolean for whether this modifier can be applied
Enabled bool
//If true, this modifier can only be applied in an expansion game
//In the file, the value 100 represents expansion only
// ExpansionOnly boolean for whether this modifier can only be applied in an expansion game.
// In the file, the value 100 represents expansion only
ExpansionOnly bool
//If true, "Minion" will be displayed below the life bar of minions of the monster with this modifier
// If true, "Minion" will be displayed below the life bar of minions of
// the monster with this modifier
Xfer bool
//If true, only usable by champion monsters
// Champion boolean, only usable by champion monsters
Champion bool
//Unknown
// FPick Unknown
FPick int
//Names of monster types that cannot have this modifier
//Refer to Type in MonType.txt
// Exclude1 monster type code that cannot have this modifier
Exclude1 string
// Exclude2 monster type code that cannot have this modifier
Exclude2 string
//Determines the frequency this modifier appears on champion monsters
//If empty, it will not appear on champions
ChampionPickFrequency int
ChampionPickFrequencyNightmare int
ChampionPickFrequencyHell int
//Determines the frequency this modifier appears on random unique monsters
//If empty, it will not appear on random unique monsters
UniquePickFrequency int
UniquePickFrequencyNightmare int
UniquePickFrequencyHell int
//FInit int: Unused
PickFrequencies struct {
Normal *pickFreq
Nightmare *pickFreq
Hell *pickFreq
}
}
//MonsterUniqueModifiers stores the MonsterUniqueModifierRecords
type pickFreq struct {
// Champion pick frequency
Champion int
// Unique pick frequency
Unique int
}
// MonsterUniqueModifiers stores the MonsterUniqueModifierRecords
var MonsterUniqueModifiers map[string]*MonsterUniqueModifierRecord //nolint:gochecknoglobals // Currently global by design
//MonsterUniqueModifierConstants contains constants from monumod.txt
//See [https://d2mods.info/forum/kb/viewarticle?a=161] for more info
//Can be accessed with indices from d2enum.MonUModConstIndex
var MonsterUniqueModifierConstants []int //nolint:gochecknoglobals
// MonsterUniqueModifierConstants contains constants from monumod.txt,
// can be accessed with indices from d2enum.MonUModConstIndex
var MonsterUniqueModifierConstants []int //nolint:gochecknoglobals // currently global by design
//LoadMonsterUniqueModifiers loads MonsterUniqueModifierRecords into MonsterUniqueModifiers
// See [https://d2mods.info/forum/kb/viewarticle?a=161] for more info
// LoadMonsterUniqueModifiers loads MonsterUniqueModifierRecords into MonsterUniqueModifiers
func LoadMonsterUniqueModifiers(file []byte) {
MonsterUniqueModifiers = make(map[string]*MonsterUniqueModifierRecord)
MonsterUniqueModifierConstants = make([]int, 0, 34)
MonsterUniqueModifierConstants = make([]int, 0, numModifierConstants)
d := d2common.LoadDataDictionary(file)
for d.Next() {
record := &MonsterUniqueModifierRecord{
Name: d.String("uniquemod"),
ID: d.Number("id"),
Enabled: d.Bool("enabled"),
ExpansionOnly: d.Number("version") == 100,
Xfer: d.Bool("xfer"),
Champion: d.Bool("champion"),
FPick: d.Number("fpick"),
Exclude1: d.String("exclude1"),
Exclude2: d.String("exclude2"),
ChampionPickFrequency: d.Number("cpick"),
ChampionPickFrequencyNightmare: d.Number("cpick (N)"),
ChampionPickFrequencyHell: d.Number("cpick (H)"),
UniquePickFrequency: d.Number("upick"),
UniquePickFrequencyNightmare: d.Number("upick (N)"),
UniquePickFrequencyHell: d.Number("upick (H)"),
Name: d.String("uniquemod"),
ID: d.Number("id"),
Enabled: d.Bool("enabled"),
ExpansionOnly: d.Number("version") == expansionCode,
Xfer: d.Bool("xfer"),
Champion: d.Bool("champion"),
FPick: d.Number("fpick"),
Exclude1: d.String("exclude1"),
Exclude2: d.String("exclude2"),
PickFrequencies: struct {
Normal *pickFreq
Nightmare *pickFreq
Hell *pickFreq
}{
Normal: &pickFreq{
Champion: d.Number("cpick"),
Unique: d.Number("upick"),
},
Nightmare: &pickFreq{
Champion: d.Number("cpick (N)"),
Unique: d.Number("upick (N)"),
},
Hell: &pickFreq{
Champion: d.Number("cpick (H)"),
Unique: d.Number("upick (H)"),
},
},
}
MonsterUniqueModifiers[record.Name] = record
if len(MonsterUniqueModifierConstants) < 34 {
MonsterUniqueModifierConstants = append(MonsterUniqueModifierConstants, (d.Number("constants")))
if len(MonsterUniqueModifierConstants) < numModifierConstants {
MonsterUniqueModifierConstants = append(MonsterUniqueModifierConstants, d.Number("constants"))
}
}

View File

@ -17,7 +17,6 @@ type MonTypeRecord struct {
// display code that accesses MonType uses StrPlur.
StrSing string
StrPlural string
// EOL int // unused
}
// MonTypes stores all of the MonTypeRecords

View File

@ -6,90 +6,97 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
//NPCRecord represents a single line in NPC.txt
//The information has been gathered from [https://d2mods.info/forum/kb/viewarticle?a=387]
const (
costDivisor = 1024.
)
// NPCRecord represents a single line in NPC.txt
// The information has been gathered from [https:// d2mods.info/forum/kb/viewarticle?a=387]
type NPCRecord struct {
//ID pointer to row of this npc in monstats.txt
NPC string
// Name is an ID pointer to row of this npc in monstats.txt
Name string
//Percentage of base item price used when an item is bought by NPC
//Actual value in file is fraction of 1024
//Notice: BuyMultiplier used when selling an item to an NPC
BuyMultiplier float64
Multipliers *costMultiplier
//Percentage of base item price used when an item is sold by NPC
//Actual value in file is fraction of 1024
//Notice: SellMultiplier used when buying an item from an NPC
SellMultiplier float64
QuestMultipliers map[int]*costMultiplier
//Percentage of base item price used to calculate the base repair price
//Actual value in file is fraction of 1024
RepairMultiplier float64
//The quest flag that must be checked to activate the corresponding QuestBuyMultiplier
QuestFlagA int
//Percentage of change of multiplier when the corresponding quest flag is checked
//Example: Quest flag A is checked, so the item price will be: (base price) * BuyMultiplier * QuestBuyMultiplierA
//Actual value in file is fraction of 1024
QuestBuyMultiplierA float64
//Percentage of change of multiplier when the corresponding quest flag is checked
//Actual value in file is fraction of 1024
QuestSellMultiplierA float64
//Percentage of change of multiplier when the corresponding quest flag is checked
//Actual value in file is fraction of 1024
QuestRepairMultiplierA float64
//ditto, three quests maximum
QuestFlagB int
QuestBuyMultiplierB float64
QuestSellMultiplierB float64
QuestRepairMultiplierB float64
QuestFlagC int
QuestBuyMultiplierC float64
QuestSellMultiplierC float64
QuestRepairMultiplierC float64
//The maximum amount of gold an NPC will pay for an item for the corresponding difficulty
MaxBuy int
MaxBuyNightmare int
MaxBuyHell int
// MaxBuy is the maximum amount of gold an NPC will pay for an item for the corresponding
// difficulty
MaxBuy struct {
Normal int
Nightmare int
Hell int
}
}
//NPCs stores the NPCRecords
var NPCs map[string]*NPCRecord //nolint:gochecknoglobals // Currently global by design
type costMultiplier struct {
// Buy is a percentage of base item price used when an item is bought by NPC
Buy float64
//LoadNPCs loads NPCRecords into NPCs
// Sell is a percentage of base item price used when an item is sold by NPC
Sell float64
// Repair is a percentage of base item price used to calculate the base repair price
Repair float64
}
// NPCs stores the NPCRecords
var NPCs map[string]*NPCRecord // nolint:gochecknoglobals // Currently global by design
// LoadNPCs loads NPCRecords into NPCs
func LoadNPCs(file []byte) {
NPCs = make(map[string]*NPCRecord)
d := d2common.LoadDataDictionary(file)
for d.Next() {
record := &NPCRecord{
NPC: d.String("npc"),
BuyMultiplier: float64(d.Number("buy mult")) / 1024.,
SellMultiplier: float64(d.Number("sell mult")) / 1024.,
RepairMultiplier: float64(d.Number("rep mult")) / 1024.,
QuestFlagA: d.Number("questflag A"),
QuestBuyMultiplierA: float64(d.Number("questbuymult A")) / 1024.,
QuestSellMultiplierA: float64(d.Number("questsellmult A")) / 1024.,
QuestRepairMultiplierA: float64(d.Number("questrepmult A")) / 1024.,
QuestFlagB: d.Number("questflag B"),
QuestBuyMultiplierB: float64(d.Number("questbuymult B")) / 1024.,
QuestSellMultiplierB: float64(d.Number("questsellmult B")) / 1024.,
QuestRepairMultiplierB: float64(d.Number("questrepmult B")) / 1024.,
QuestFlagC: d.Number("questflag C"),
QuestBuyMultiplierC: float64(d.Number("questbuymult C")) / 1024.,
QuestSellMultiplierC: float64(d.Number("questsellmult C")) / 1024.,
QuestRepairMultiplierC: float64(d.Number("questrepmult C")) / 1024.,
MaxBuy: d.Number("max buy"),
MaxBuyNightmare: d.Number("max buy (N)"),
MaxBuyHell: d.Number("max buy (H)"),
Name: d.String("npc"),
Multipliers: &costMultiplier{
Buy: float64(d.Number("buy mult")) / costDivisor,
Sell: float64(d.Number("sell mult")) / costDivisor,
Repair: float64(d.Number("rep mult")) / costDivisor,
},
MaxBuy: struct {
Normal int
Nightmare int
Hell int
}{
Normal: d.Number("max buy"),
Nightmare: d.Number("max buy (N)"),
Hell: d.Number("max buy (H)"),
},
}
NPCs[record.NPC] = record
record.QuestMultipliers = make(map[int]*costMultiplier)
if flagStr := d.String("questflag A"); flagStr != "" {
flag := d.Number("questflag A")
record.QuestMultipliers[flag] = &costMultiplier{
float64(d.Number("questbuymult A")) / costDivisor,
float64(d.Number("questsellmult A")) / costDivisor,
float64(d.Number("questrepmult A")) / costDivisor,
}
}
if flagStr := d.String("questflag B"); flagStr != "" {
flag := d.Number("questflag B")
record.QuestMultipliers[flag] = &costMultiplier{
float64(d.Number("questbuymult B")) / costDivisor,
float64(d.Number("questsellmult B")) / costDivisor,
float64(d.Number("questrepmult B")) / costDivisor,
}
}
if flagStr := d.String("questflag C"); flagStr != "" {
flag := d.Number("questflag C")
record.QuestMultipliers[flag] = &costMultiplier{
float64(d.Number("questbuymult C")) / costDivisor,
float64(d.Number("questsellmult C")) / costDivisor,
float64(d.Number("questrepmult C")) / costDivisor,
}
}
NPCs[record.Name] = record
}
if d.Err != nil {

View File

@ -71,15 +71,10 @@ type ObjectRecord struct {
Width int
Height int
OperateFn int // what function is called when the player clicks on the object
// (todo: we should enumerate all the functions somewhere, but probably not here
// b/c it's a very long list)
OperateFn int // what function is called when the player clicks on the object
PopulateFn int // what function is used to spawn this object?
// (see above todo)
InitFn int // what function is run when the object is initialized?
// (see above todo)
ClientFn int // controls special audio-visual functions
// (see above todo)
InitFn int // what function is run when the object is initialized?
ClientFn int // controls special audio-visual functions
// 'To ...' or 'trap door' when highlighting, not sure which is T/F
AutoMap int // controls how this object appears on the map

View File

@ -6,30 +6,18 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
// OverlayRecord encapsulates information found in Overlay.txt
// The information has been gathered from [https://d2mods.info/forum/kb/viewarticle?a=465]
// OverlayRecord encapsulates information found in Overlay.txt
type OverlayRecord struct {
// Overlay name
Overlay string
Name string
// .dcc file found in Data/Globals/Overlays
Filename string
Version bool
// Apparently unused, a similar field in the .dcc file is used instead
Frames int
// Unused
Character string
// Controls overlay drawing precedence
PreDraw bool
// Unknown
OneOfN int
// Unknown
Dir bool
Open bool
Beta bool
XOffset int
YOffset int
// XOffset, YOffset the x,y offset of the overlay
XOffset, YOffset int
// These values modify Y-axis placement
Height1 int
@ -37,63 +25,71 @@ type OverlayRecord struct {
Height3 int
Height4 int
// Animation speed control
// AnimRate animation speed control
AnimRate int
// Unused
LoopWaitTime int
// Controls overlay blending mode, check out the link for more info
// Trans controls overlay blending mode, check out the link for more info
// This should probably become an "enum" later on
Trans int
// Maximum light radius
// Radius maximum for light
Radius int
// Light radius increase per frame
// InitRadius Light radius increase per frame
InitRadius int
Red uint8
Green uint8
Blue uint8
// Red, Green, Blue color for light
Red, Green, Blue uint8
// Unknown
NumDirections int
LocalBlood int
// Version is 100 for expansion, 0 for vanilla
Version bool
// PreDraw controls overlay drawing precedence
PreDraw bool
// Unknown fields, commenting out for now
// NumDirections int
// LocalBlood int
// OneOfN int
// Dir bool
// Open bool
// Beta bool
// Apparently unused
// Character string
// LoopWaitTime int
// Frames int
}
var Overlays map[string]*OverlayRecord
// Overlays contains all of the OverlayRecords from Overlay.txt
var Overlays map[string]*OverlayRecord // nolint:gochecknoglobals // Currently global by design
// LoadOverlays loads overlay records from Overlay.txt
func LoadOverlays(file []byte) {
Overlays = make(map[string]*OverlayRecord)
d := d2common.LoadDataDictionary(file)
for d.Next() {
record := &OverlayRecord{
Overlay: d.String("Overlay"),
Filename: d.String("Filename"),
Version: d.Bool("Version"),
Frames: d.Number("Frames"),
Character: d.String("Character"),
PreDraw: d.Bool("PreDraw"),
OneOfN: d.Number("1ofN"),
Dir: d.Bool("Dir"),
Open: d.Bool("Open"),
Beta: d.Bool("Beta"),
XOffset: d.Number("Xoffset"),
YOffset: d.Number("Yoffset"),
Height1: d.Number("Height1"),
Height2: d.Number("Height1"),
Height3: d.Number("Height1"),
Height4: d.Number("Height1"),
AnimRate: d.Number("AnimRate"),
LoopWaitTime: d.Number("LoopWaitTime"),
Trans: d.Number("Trans"),
InitRadius: d.Number("InitRadius"),
Radius: d.Number("Radius"),
Red: uint8(d.Number("Red")),
Green: uint8(d.Number("Green")),
Blue: uint8(d.Number("Blue")),
NumDirections: d.Number("NumDirections"),
LocalBlood: d.Number("LocalBlood"),
Name: d.String("Overlay"),
Filename: d.String("Filename"),
Version: d.Bool("Version"),
PreDraw: d.Bool("PreDraw"),
XOffset: d.Number("Xoffset"),
YOffset: d.Number("Yoffset"),
Height1: d.Number("Height1"),
Height2: d.Number("Height1"),
Height3: d.Number("Height1"),
Height4: d.Number("Height1"),
AnimRate: d.Number("AnimRate"),
Trans: d.Number("Trans"),
InitRadius: d.Number("InitRadius"),
Radius: d.Number("Radius"),
Red: uint8(d.Number("Red")),
Green: uint8(d.Number("Green")),
Blue: uint8(d.Number("Blue")),
}
Overlays[record.Overlay] = record
Overlays[record.Name] = record
}
if d.Err != nil {
@ -101,5 +97,4 @@ func LoadOverlays(file []byte) {
}
log.Printf("Loaded %d Overlay records", len(Overlays))
}

View File

@ -6,85 +6,81 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
//PetTypeRecord represents a single line in PetType.txt
//The information has been gathered from [https://d2mods.info/forum/kb/viewarticle?a=355]
// PetTypeRecord represents a single line in PetType.txt
// The information has been gathered from [https:// d2mods.info/forum/kb/viewarticle?a=355]
type PetTypeRecord struct {
//Name of the pet type, reffered by "pettype" in skills.txt
PetType string
//ID number of the pet type
Idx int
//ID number of the group this pet belongs to
Group int
BaseMax int
//If true, the pet warps with the player, otherwise it dies
Warp bool
//If true, and Warp is false, the pet only die if the distance between the player
//and the pet exceeds 41 sub-tiles.
Range bool
//Unknown
PartySend bool
//If true, can be unsummoned
Unsummon bool
//If true, pet is displayed on the automap
Automap bool
//String file for the text under the pet icon
// Name of the pet type, refferred by "pettype" in skills.txt
Name string
//If true, the pet's HP will be displayed under the icon
DrawHP bool
// Name text under the pet icon
IconName string
//Pet icon type
IconType int
//.dc6 file for the pet's icon, located in /data/global/ui/hireables
// .dc6 file for the pet's icon, located in /data/global/ui/hireables
BaseIcon string
//Alternative pet index from monstats.txt
MClass1 int
//Alternative pet icon .dc6 file
// Alternative pet icon .dc6 file
MIcon1 string
MIcon2 string
MIcon3 string
MIcon4 string
//ditto, there can be four alternatives
// ID number of the pet type
ID int
// GroupID number of the group this pet belongs to
GroupID int
// BaseMax unknown what this does...
BaseMax int
// Pet icon type
IconType int
// Alternative pet index from monstats.txt
MClass1 int
MClass2 int
MIcon2 string
MClass3 int
MIcon3 string
MClass4 int
MIcon4 string
//EOL int, not loaded
// Warp warps with the player, otherwise it dies
Warp bool
// Range the pet only die if the distance between the player and the pet exceeds 41 sub-tiles.
Range bool
// Unknown
PartySend bool
// Unsummon whether the pet can be unsummoned
Unsummon bool
// Automap whether the pet is displayed on the automap
Automap bool
// If true, the pet's HP will be displayed under the icon
DrawHP bool
}
//PetTypes stores the PetTypeRecords
var PetTypes map[string]*PetTypeRecord //nolint:gochecknoglobals // Currently global by design
// PetTypes stores the PetTypeRecords
var PetTypes map[string]*PetTypeRecord // nolint:gochecknoglobals // Currently global by design
//LoadPetTypes loads PetTypeRecords into PetTypes
// LoadPetTypes loads PetTypeRecords into PetTypes
func LoadPetTypes(file []byte) {
PetTypes = make(map[string]*PetTypeRecord)
d := d2common.LoadDataDictionary(file)
for d.Next() {
record := &PetTypeRecord{
PetType: d.String("pet type"),
Idx: d.Number("idx"),
Group: d.Number("group"),
Name: d.String("pet type"),
ID: d.Number("idx"),
GroupID: d.Number("group"),
BaseMax: d.Number("basemax"),
Warp: d.Bool("warp"),
Range: d.Bool("range"),
PartySend: d.Bool("partysend"),
Unsummon: d.Bool("unsummon"),
Automap: d.Bool("automap"),
Name: d.String("name"),
IconName: d.String("name"),
DrawHP: d.Bool("drawhp"),
IconType: d.Number("icontype"),
BaseIcon: d.String("baseicon"),
@ -97,7 +93,7 @@ func LoadPetTypes(file []byte) {
MClass4: d.Number("mclass4"),
MIcon4: d.String("micon4"),
}
PetTypes[record.PetType] = record
PetTypes[record.Name] = record
}
if d.Err != nil {

View File

@ -6,20 +6,20 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
//PlayerClassRecord represents a single line from PlayerClass.txt
//Lookup table for class codes
// PlayerClassRecord represents a single line from PlayerClass.txt
// Lookup table for class codes
type PlayerClassRecord struct {
//Class name
// Name of the player class
Name string
//Class code
// Code for the player class
Code string
}
//PlayerClasses stores the PlayerClassRecords
var PlayerClasses map[string]*PlayerClassRecord
// PlayerClasses stores the PlayerClassRecords
var PlayerClasses map[string]*PlayerClassRecord // nolint:gochecknoglobals // Currently global by design
//LoadPlayerClasses loads the PlayerClassRecords into PlayerClasses
// LoadPlayerClasses loads the PlayerClassRecords into PlayerClasses
func LoadPlayerClasses(file []byte) {
PlayerClasses = make(map[string]*PlayerClassRecord)
@ -29,9 +29,11 @@ func LoadPlayerClasses(file []byte) {
Name: d.String("Player Class"),
Code: d.String("Code"),
}
if record.Name == "Expansion" {
if record.Name == expansion {
continue
}
PlayerClasses[record.Name] = record
}

View File

@ -6,19 +6,19 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
//PlrModeRecord represents a single line in PlrMode.txt
// PlrModeRecord represents a single line in PlrMode.txt
type PlrModeRecord struct {
//Player animation mode name
// Player animation mode name
Name string
//Player animation mode token
// Player animation mode token
Token string
}
//PlrModes stores the PlrModeRecords
// PlrModes stores the PlrModeRecords
var PlrModes map[string]*PlrModeRecord //nolint:gochecknoglobals // Currently global by design
//LoadPlrModes loads PlrModeRecords into PlrModes
// LoadPlrModes loads PlrModeRecords into PlrModes
func LoadPlrModes(file []byte) {
PlrModes = make(map[string]*PlrModeRecord)

View File

@ -6,6 +6,7 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
// PropertyStatRecord contains stat information for a property
type PropertyStatRecord struct {
SetID int
Value int
@ -21,8 +22,7 @@ type PropertyRecord struct {
}
// Properties stores all of the PropertyRecords
var Properties map[string]*PropertyRecord //nolint:gochecknoglobals // Currently global by design,
// only written once
var Properties map[string]*PropertyRecord //nolint:gochecknoglobals // Currently global by design, only written once
// LoadProperties loads gem records into a map[string]*PropertiesRecord
func LoadProperties(file []byte) {

View File

@ -1,11 +1,30 @@
package d2datadict
import (
"fmt"
"log"
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
const (
numRunewordTypeInclude = 6
numRunewordTypeExclude = 3
numRunewordMaxSockets = 6
numRunewordProperties = 7
)
// format strings
const (
fmtTypeInclude = "itype%d"
fmtTypeExclude = "etype%d"
fmtRuneStr = "Rune%d"
fmtRunewordPropCode = "T1Code%d"
fmtRunewordPropParam = "T1Param%d"
fmtRunewordPropMin = "T1Min%d"
fmtRunewordPropMax = "T1Max%d"
)
// RunesRecord is a representation of a single row of runes.txt. It defines
// runewords available in the game.
type RunesRecord struct {
@ -14,74 +33,39 @@ type RunesRecord struct {
Complete bool // An enabled/disabled flag. Only "Complete" runewords work in game.
Server bool // Marks a runeword as only available on ladder, not single player or tcp/ip.
// IType1-6 include item types into the list of item types that this runeword works in.
IType1 string
IType2 string
IType3 string
IType4 string
IType5 string
IType6 string
// The item types for includsion/exclusion for this runeword record
ItemTypes struct {
Include []string
Exclude []string
}
// EType1-3 exclude item types from the list of item types that this runeword works in.
EType1 string
EType2 string
EType3 string
Runes string // The runes as they would appear in the finished runeword. Note field only.
// Rune1-6 are ID pointers from Misc.txt. The fields control what runes are
// Runes slice of ID pointers from Misc.txt, controls what runes are
// required to make the rune word and in what order they are to be socketed.
Rune1 string
Rune2 string
Rune3 string
Rune4 string
Rune5 string
Rune6 string
Runes []string
T1Code1 string
T1Param1 string
T1Min1 int
T1Max1 int
T1Code2 string
T1Param2 string
T1Min2 int
T1Max2 int
T1Code3 string
T1Param3 string
T1Min3 int
T1Max3 int
T1Code4 string
T1Param4 string
T1Min4 int
T1Max4 int
T1Code5 string
T1Param5 string
T1Min5 int
T1Max5 int
T1Code6 string
T1Param6 string
T1Min6 int
T1Max6 int
T1Code7 string
T1Param7 string
T1Min7 int
T1Max7 int
// EOL int // not loaded
Properties []*runewordProperty
}
// Runes stores all of the RunesRecords
var Runes map[string]*RunesRecord //nolint:gochecknoglobals // Currently global by design, only written once
type runewordProperty struct {
// Code is the property code
Code string
// LoadRunes loads runes records into a map[string]*RunesRecord
func LoadRunes(file []byte) {
Runes = make(map[string]*RunesRecord)
// Param is either string or int, parameter for the property
Param string
// Min is the minimum value for the property
Min int
// Max is the maximum value for the property
Max int
}
// Runewords stores all of the RunesRecords
var Runewords map[string]*RunesRecord //nolint:gochecknoglobals // Currently global by design, only written once
// LoadRunewords loads runes records into a map[string]*RunesRecord
func LoadRunewords(file []byte) {
Runewords = make(map[string]*RunesRecord)
d := d2common.LoadDataDictionary(file)
for d.Next() {
@ -90,57 +74,58 @@ func LoadRunes(file []byte) {
RuneName: d.String("Rune Name"),
Complete: d.Bool("complete"),
Server: d.Bool("server"),
IType1: d.String("itype1"),
IType2: d.String("itype2"),
IType3: d.String("itype3"),
IType4: d.String("itype4"),
IType5: d.String("itype5"),
IType6: d.String("itype6"),
EType1: d.String("etype1"),
EType2: d.String("etype2"),
EType3: d.String("etype3"),
Runes: d.String("*runes"),
Rune1: d.String("Rune1"),
Rune2: d.String("Rune2"),
Rune3: d.String("Rune3"),
Rune4: d.String("Rune4"),
Rune5: d.String("Rune5"),
Rune6: d.String("Rune6"),
T1Code1: d.String("T1Code1"),
T1Param1: d.String("T1Param1"),
T1Min1: d.Number("T1Min1"),
T1Max1: d.Number("T1Max1"),
T1Code2: d.String("T1Code2"),
T1Param2: d.String("T1Param2"),
T1Min2: d.Number("T1Min2"),
T1Max2: d.Number("T1Max2"),
T1Code3: d.String("T1Code3"),
T1Param3: d.String("T1Param3"),
T1Min3: d.Number("T1Min3"),
T1Max3: d.Number("T1Max3"),
T1Code4: d.String("T1Code4"),
T1Param4: d.String("T1Param4"),
T1Min4: d.Number("T1Min4"),
T1Max4: d.Number("T1Max4"),
T1Code5: d.String("T1Code5"),
T1Param5: d.String("T1Param5"),
T1Min5: d.Number("T1Min5"),
T1Max5: d.Number("T1Max5"),
T1Code6: d.String("T1Code6"),
T1Param6: d.String("T1Param6"),
T1Min6: d.Number("T1Min6"),
T1Max6: d.Number("T1Max6"),
T1Code7: d.String("T1Code7"),
T1Param7: d.String("T1Param7"),
T1Min7: d.Number("T1Min7"),
T1Max7: d.Number("T1Max7"),
ItemTypes: struct {
Include []string
Exclude []string
}{
Include: make([]string, 0),
Exclude: make([]string, 0),
},
Runes: make([]string, 0),
Properties: make([]*runewordProperty, 0),
}
Runes[record.Name] = record
for idx := 0; idx < numRunewordTypeInclude; idx++ {
column := fmt.Sprintf(fmtTypeInclude, idx+1)
if code := d.String(column); code != "" {
record.ItemTypes.Include = append(record.ItemTypes.Include, code)
}
}
for idx := 0; idx < numRunewordTypeExclude; idx++ {
column := fmt.Sprintf(fmtTypeExclude, idx+1)
if code := d.String(column); code != "" {
record.ItemTypes.Exclude = append(record.ItemTypes.Exclude, code)
}
}
for idx := 0; idx < numRunewordMaxSockets; idx++ {
column := fmt.Sprintf(fmtRuneStr, idx+1)
if code := d.String(column); code != "" {
record.Runes = append(record.Runes, code)
}
}
for idx := 0; idx < numRunewordProperties; idx++ {
codeColumn := fmt.Sprintf(fmtRunewordPropCode, idx+1)
if code := codeColumn; code != "" {
prop := &runewordProperty{
code,
d.String(fmt.Sprintf(fmtRunewordPropParam, idx+1)),
d.Number(fmt.Sprintf(fmtRunewordPropMin, idx+1)),
d.Number(fmt.Sprintf(fmtRunewordPropMax, idx+1)),
}
record.Properties = append(record.Properties, prop)
}
}
Runewords[record.Name] = record
}
if d.Err != nil {
panic(d.Err)
}
log.Printf("Loaded %d Runes records", len(Runes))
log.Printf("Loaded %d Runewords records", len(Runewords))
}

View File

@ -2,23 +2,24 @@ package d2datadict
import (
"fmt"
"github.com/OpenDiablo2/OpenDiablo2/d2common"
"log"
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
const (
numPropertiesOnSetItem = 9
numPropertiesOnSetItem = 9
numBonusPropertiesOnSetItem = 5
bonusToken1 = "a"
bonusToken2 = "b"
propCodeFmt = "prop%d"
propParamFmt = "par%d"
propMinFmt = "min%d"
propMaxFmt = "max%d"
bonusCodeFmt = "aprop%d%s"
bonusParamFmt = "apar%d%s"
bonusMinFmt = "amin%d%s"
bonusMaxFmt = "amax%d%s"
bonusToken1 = "a"
bonusToken2 = "b"
propCodeFmt = "prop%d"
propParamFmt = "par%d"
propMinFmt = "min%d"
propMaxFmt = "max%d"
bonusCodeFmt = "aprop%d%s"
bonusParamFmt = "apar%d%s"
bonusMinFmt = "amin%d%s"
bonusMaxFmt = "amax%d%s"
)
// SetItemRecord represents a set item
@ -152,7 +153,6 @@ func LoadSetItems(file []byte) {
CostMult: d.Number("cost mult"),
CostAdd: d.Number("cost add"),
AddFn: d.Number("add func"),
}
// normal properties
@ -178,14 +178,14 @@ func LoadSetItems(file []byte) {
bonus1[idx] = &SetItemProperty{
d.String(fmt.Sprintf(bonusCodeFmt, num, bonusToken1)),
d.String(fmt.Sprintf(bonusParamFmt, num, bonusToken1)),
d.Number(fmt.Sprintf(bonusMinFmt, num,bonusToken1)),
d.Number(fmt.Sprintf(bonusMinFmt, num, bonusToken1)),
d.Number(fmt.Sprintf(bonusMaxFmt, num, bonusToken1)),
}
bonus2[idx] = &SetItemProperty{
d.String(fmt.Sprintf(bonusCodeFmt, num, bonusToken2)),
d.String(fmt.Sprintf(bonusParamFmt, num, bonusToken2)),
d.Number(fmt.Sprintf(bonusMinFmt, num,bonusToken2)),
d.Number(fmt.Sprintf(bonusMinFmt, num, bonusToken2)),
d.Number(fmt.Sprintf(bonusMaxFmt, num, bonusToken2)),
}
}

View File

@ -1,11 +1,27 @@
package d2datadict
import (
"fmt"
"log"
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
const (
numPartialSetProperties = 4
numFullSetProperties = 8
fmtPropCode = "%sCode%d%s"
fmtPropParam = "%sParam%d%s"
fmtPropMin = "%sMin%d%s"
fmtPropMax = "%sMax%d%s"
partialIdxOffset = 2
setPartialToken = "P"
setPartialTokenA = "a"
setPartialTokenB = "b"
setFullToken = "F"
)
// SetRecord describes the set bonus for a group of set items
type SetRecord struct {
// index
// String key linked to by the set field in SetItems.
@ -24,62 +40,43 @@ type SetRecord struct {
// (reference only, not loaded into game).
Level int
// PCodeA, PCodeB -- PCode2a,PCode2b to PCode5a,PCode5b
// An ID pointer of a property from Properties.txt,
// these columns control each of the five pairs of different partial set modifiers a set item can
// grant you at most.
PCodeA [4]string
PCodeB [4]string
// Properties contains the partial and full set bonus properties.
Properties struct {
PartialA []*setProperty
PartialB []*setProperty
Full []*setProperty
}
}
// PParamA, PParamB -- PParam2[a|b] to PParam5[a|b]
// The parameter passed on to the associated property, this is used to pass skill IDs, state IDs,
// monster IDs, montype IDs and the like on to the properties that require them,
// these fields support calculations.
PParamA [4]int
PParamB [4]int
// PMinA, PMaxA, PMinB, PMaxB -- P[Min|Max]2[a|b] to P[Min|Max]5[a|b]
// Minimum value to assign to the associated property.
// Certain properties have special interpretations based on stat encoding (e.g.
// chance-to-cast and charged skills). See the File Guides for Properties.txt and ItemStatCost.
// txt for further details.
PMinA [4]int
PMaxA [4]int
PMinB [4]int
PMaxB [4]int
// FCode -- FCode1 to FCode8
// An ID pointer of a property from Properties.txt,
type setProperty struct {
// Code is an ID pointer of a property from Properties.txt,
// these columns control each of the eight different full set modifiers a set item can grant you
// at most.
FCode [8]string
Code string
// FParam -- FParam1 to FParam8
// The parameter passed on to the associated property, this is used to pass skill IDs, state IDs,
// Param is the passed on to the associated property, this is used to pass skill IDs, state IDs,
// monster IDs, montype IDs and the like on to the properties that require them,
// these fields support calculations.
FParam [8]int
Param string
// FMin -- FMin1 to FMin8
// Minimum value to assign to the associated property.
// Min value to assign to the associated property.
// Certain properties have special interpretations based on stat encoding (e.g.
// chance-to-cast and charged skills). See the File Guides for Properties.txt and ItemStatCost.
// txt for further details.
FMin [8]int
Min int
// FMax -- FMax1 to FMax8
// Maximum value to assign to the associated property.
// Max value to assign to the associated property.
// Certain properties have special interpretations based on stat encoding (e.g.
// chance-to-cast and charged skills). See the File Guides for Properties.txt and ItemStatCost.
// txt for further details.
FMax [8]int
Max int
}
// SetRecords contain the set records from sets.txt
var SetRecords map[string]*SetRecord
var SetRecords map[string]*SetRecord //nolint:gochecknoglobals // Currently global by design
// LoadSetRecords loads set records from sets.txt
func LoadSets(file []byte) {
func LoadSetRecords(file []byte) { //nolint:funlen // doesn't make sense to split
SetRecords = make(map[string]*SetRecord)
d := d2common.LoadDataDictionary(file)
@ -89,96 +86,73 @@ func LoadSets(file []byte) {
StringTableKey: d.String("name"),
Version: d.Number("version"),
Level: d.Number("level"),
PCodeA: [4]string{
d.String("PCode2a"),
d.String("PCode3a"),
d.String("PCode4a"),
d.String("PCode5a"),
},
PCodeB: [4]string{
d.String("PCode2b"),
d.String("PCode3b"),
d.String("PCode4b"),
d.String("PCode5b"),
},
PParamA: [4]int{
d.Number("PParam2a"),
d.Number("PParam3a"),
d.Number("PParam4a"),
d.Number("PParam5a"),
},
PParamB: [4]int{
d.Number("PParam2b"),
d.Number("PParam3b"),
d.Number("PParam4b"),
d.Number("PParam5b"),
},
PMinA: [4]int{
d.Number("PMin2a"),
d.Number("PMin3a"),
d.Number("PMin4a"),
d.Number("PMin5a"),
},
PMinB: [4]int{
d.Number("PMin2b"),
d.Number("PMin3b"),
d.Number("PMin4b"),
d.Number("PMin5b"),
},
PMaxA: [4]int{
d.Number("PMax2a"),
d.Number("PMax3a"),
d.Number("PMax4a"),
d.Number("PMax5a"),
},
PMaxB: [4]int{
d.Number("PMax2b"),
d.Number("PMax3b"),
d.Number("PMax4b"),
d.Number("PMax5b"),
},
FCode: [8]string{
d.String("FCode1"),
d.String("FCode2"),
d.String("FCode3"),
d.String("FCode4"),
d.String("FCode5"),
d.String("FCode6"),
d.String("FCode7"),
d.String("FCode9"),
},
FParam: [8]int{
d.Number("FParam1"),
d.Number("FParam2"),
d.Number("FParam3"),
d.Number("FParam4"),
d.Number("FParam5"),
d.Number("FParam6"),
d.Number("FParam7"),
d.Number("FParam9"),
},
FMin: [8]int{
d.Number("FMin1"),
d.Number("FMin2"),
d.Number("FMin3"),
d.Number("FMin4"),
d.Number("FMin5"),
d.Number("FMin6"),
d.Number("FMin7"),
d.Number("FMin9"),
},
FMax: [8]int{
d.Number("FMax1"),
d.Number("FMax2"),
d.Number("FMax3"),
d.Number("FMax4"),
d.Number("FMax5"),
d.Number("FMax6"),
d.Number("FMax7"),
d.Number("FMax9"),
Properties: struct {
PartialA []*setProperty
PartialB []*setProperty
Full []*setProperty
}{
PartialA: make([]*setProperty, 0),
PartialB: make([]*setProperty, 0),
Full: make([]*setProperty, 0),
},
}
// for partial properties 2a thru 5b
for idx := 0; idx < numPartialSetProperties; idx++ {
num := idx + partialIdxOffset // needs to be 2,3,4,5
columnA := fmt.Sprintf(fmtPropCode, setPartialToken, num, setPartialTokenA)
columnB := fmt.Sprintf(fmtPropCode, setPartialToken, num, setPartialTokenB)
if codeA := d.String(columnA); codeA != "" {
paramColumn := fmt.Sprintf(fmtPropParam, setPartialToken, num, setPartialTokenA)
minColumn := fmt.Sprintf(fmtPropMin, setPartialToken, num, setPartialTokenA)
maxColumn := fmt.Sprintf(fmtPropMax, setPartialToken, num, setPartialTokenA)
propA := &setProperty{
Code: codeA,
Param: d.String(paramColumn),
Min: d.Number(minColumn),
Max: d.Number(maxColumn),
}
record.Properties.PartialA = append(record.Properties.PartialA, propA)
}
if codeB := d.String(columnB); codeB != "" {
paramColumn := fmt.Sprintf(fmtPropParam, setPartialToken, num, setPartialTokenB)
minColumn := fmt.Sprintf(fmtPropMin, setPartialToken, num, setPartialTokenB)
maxColumn := fmt.Sprintf(fmtPropMax, setPartialToken, num, setPartialTokenB)
propB := &setProperty{
Code: codeB,
Param: d.String(paramColumn),
Min: d.Number(minColumn),
Max: d.Number(maxColumn),
}
record.Properties.PartialB = append(record.Properties.PartialB, propB)
}
}
for idx := 0; idx < numFullSetProperties; idx++ {
num := idx + 1
codeColumn := fmt.Sprintf(fmtPropCode, setFullToken, num, "")
paramColumn := fmt.Sprintf(fmtPropParam, setFullToken, num, "")
minColumn := fmt.Sprintf(fmtPropMin, setFullToken, num, "")
maxColumn := fmt.Sprintf(fmtPropMax, setFullToken, num, "")
if code := d.String(codeColumn); code != "" {
prop := &setProperty{
Code: code,
Param: d.String(paramColumn),
Min: d.Number(minColumn),
Max: d.Number(maxColumn),
}
record.Properties.Full = append(record.Properties.Full, prop)
}
}
SetRecords[record.Key] = record
}

View File

@ -1,10 +1,13 @@
package d2datadict
import (
"github.com/OpenDiablo2/OpenDiablo2/d2common"
"log"
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
// SkillDescriptionRecord is a single row from skilldesc.txt and is used for
// generating text strings for skills.
type SkillDescriptionRecord struct {
Name string // skilldesc
SkillPage string // SkillPage
@ -121,12 +124,12 @@ type SkillDescriptionRecord struct {
Dsc3calcb7 string // dsc3calcb7
}
// ItemStatCosts stores all of the ItemStatCostRecords
// SkillDescriptions stores all of the SkillDescriptionRecords
//nolint:gochecknoglobals // Currently global by design
var SkillDescriptions map[string]*SkillDescriptionRecord
// LoadItemStatCosts loads ItemStatCostRecord's from text
func LoadSkillDescriptions(file []byte) {
// LoadSkillDescriptions loads skill description records from skilldesc.txt
func LoadSkillDescriptions(file []byte) { //nolint:funlen // doesn't make sense to split
SkillDescriptions = make(map[string]*SkillDescriptionRecord)
d := d2common.LoadDataDictionary(file)

View File

@ -9,43 +9,43 @@ import (
// SoundEntry represents a sound entry
type SoundEntry struct {
Handle string
Index int
FileName string
Index int
Volume int
GroupSize int
Loop bool
FadeIn int
FadeOut int
DeferInst bool
StopInst bool
Duration int
Compound int
Reverb int
Falloff int
Priority int
Block1 int
Block2 int
Block3 int
Loop bool
DeferInst bool
StopInst bool
Cache bool
AsyncOnly bool
Priority int
Stream bool
Stereo bool
Tracking bool
Solo bool
MusicVol bool
Block1 int
Block2 int
Block3 int
}
// Sounds stores all of the SoundEntries
//nolint:gochecknoglobals // Currently global by design, only written once
var Sounds map[string]SoundEntry
var Sounds map[string]*SoundEntry
// LoadSounds loads SoundEntries from sounds.txt
func LoadSounds(file []byte) {
Sounds = make(map[string]SoundEntry)
Sounds = make(map[string]*SoundEntry)
d := d2common.LoadDataDictionary(file)
for d.Next() {
entry := SoundEntry{
entry := &SoundEntry{
Handle: d.String("Sound"),
Index: d.Number("Index"),
FileName: d.String("FileName"),
@ -84,9 +84,9 @@ func LoadSounds(file []byte) {
// SelectSoundByIndex selects a sound by its ID
func SelectSoundByIndex(index int) *SoundEntry {
for _, el := range Sounds {
if el.Index == index {
return &el
for idx := range Sounds {
if Sounds[idx].Index == index {
return Sounds[idx]
}
}

View File

@ -8,82 +8,237 @@ import (
// StateRecord describes a body location that items can be equipped to
type StateRecord struct {
State string // Name of status effect (Line # is used for ID purposes)
Group int // States can be grouped together, so they cannot stack
RemHit bool // If a monster gets hit, the state will be dispelled
NoSend bool // Not yet analysed in detail
Transform bool // Whenever a state transforms the appearance of a unit
Aura bool // Aura states will stack on-screen. Aura states are dispelled when a monster is affected by conversion
Cureable bool // Can a heal enabled npc remove this state when you talk to them?
Curse bool // Curse states can't stack. Controls duration reduction from cleansing, and curse resistance. When a new curse is applied, the old one is removed.
Active bool // State has a StateActiveFunc associated with it. The active func is called every frame while the state is active.
Restrict bool // State restricts skill usage (druid shapeshift)
Disguise bool // State makes the game load another sprite (use with Transform)
Blue bool // State applies a color change that overrides all others
// Name of status effect (Line # is used for ID purposes)
State string
AttBlue bool // Control when attack rating is displayed in blue
DmgBlue bool // Control when damage is displayed in blue
ArmBlue bool // Control when armor class is displayed in blue
RfBlue bool // Control when fire resistance is displayed in blue
RlBlue bool // Control when lightning resistance is displayed in blue
RcBlue bool // Control when cold resistance is displayed in blue
RpBlue bool // Control when poison resistance is displayed in blue
AttRed bool // Control when attack rating is displayed in red
DmgRed bool // Control when damage is displayed in red
ArmRed bool // Control when armor class is displayed in red
RfRed bool // Control when fire resistance is displayed in red
RlRed bool // Control when lightning resistance is displayed in red
RcRed bool // Control when cold resistance is displayed in red
RpRed bool // Control when poison resistance is displayed in red
// Exact usage depends on the state and how the code accesses it,
// overlay1 however is the one you should generally be using.
Overlay1 string
Overlay2 string
Overlay3 string
Overlay4 string
StamBarBlue bool // Control when stamina bar color is changed to blue
Exp bool // When a unit effected by this state kills another unit, the summon owner will recieve experience
PlrStayDeath bool // Whenever the state is removed when the player dies
MonStayDeath bool // Whenever the state is removed when the monster dies
BossStayDeath bool // Whenever the state is removed when the boss dies. Prevents bosses from shattering?
Hide bool // When the unit dies, the corpse and death animation will not be drawn
Shatter bool // Whenever the unit shatters or explodes when it dies. This is heavily hardcoded, it will always use the ice shatter for all states other than STATE_UBERMINION
UDead bool // Whenever this state prevents the corpse from being selected by spells and the ai
Life bool // When a state with this is active, it cancels out the native life regen of monsters. (using only the mod part instead of accr).
Green bool // Whenever this state applies a color change that overrides all others (such as from items). (see blue column, which seams to do the same).
Pgsv bool // Whenever this state is associated with progressive spells and will be looked up when the charges are triggered.
NoOverlays bool // Related to assigning overlays to the unit, not extensively researched yet.
NoClear bool // Like the previous column, also only used on states with the previous column enabled.
BossInv bool // whenever this state will use the minion owners inventory clientside (this is what makes the decoy always show up with your own equipment, even when you change what you wear after summoning one).
MeleeOnly bool // Prevents druids that wield a bow or crossbow while shape shifted from firing missiles, and will rather attack in melee.
NotOnDead bool // Not researched yet
// Overlay shown on target of progressive skill when chargeup triggers.
PgOverlay string
Overlay1 string // Exact usage depends on the state and how the code accesses it, overlay1 however is the one you should generally be using.
Overlay2 string
Overlay3 string
Overlay4 string
PgOverlay string // Overlay shown on target of progressive skill when chargeup triggers.
CastOverlay string // Overlay displayed when the state is applied initially (later replaced by overlay1 or whatever applicable by code).
RemOverlay string // Like castoverlay, just this one is displayed when the state expires.
// Overlay displayed when the state is applied initially
// (later replaced by overlay1 or whatever applicable by code).
CastOverlay string
Stat string // Primary stat associated with the state, mostly used for display purposes (you should generally use skills.txt for assigning stats to states).
SetFunc int // Clientside callback function invoked when the state is applied initially.
RemFunc int // Clientside callback function invoked when the state expires or is otherwise removed.
// Like castoverlay, just this one is displayed when the state expires.
RemOverlay string
Missile string // The missile that this state will utilize for certain events, how this is used depends entirely on the functions associated with the state.
Skill string // The skill that will be queried for this state in some sections of code, strangely enough this contradicts the fact states store their assigner skill anyway (via STAT_MODIFIERLIST_SKILL)
// Primary stat associated with the state, mostly used for display purposes
// (you should generally use skills.txt for assigning stats to states).
Stat string
ItemType string // What item type is effected by this states color change.
ItemTrans string // The color being applied to this item (only going to have an effect on alternate gfx, inventory gfx isn't effected).
ColorPri int // The color priority for this states color change, the, this can range from 0 to 255, the state with the highest color priority will always be used should more then one co-exist on the unit. If two states exist with the same priority the one with the lowest id is used (IIRC).
ColorShift int // Index for the color shift palette picked from the *.PL2 files (see Paul Siramy's state color tool for info).
LightR int // Change the color of the light radius to what is indicated here, (only has an effect in D3D and glide of course).
LightG int // Change the color of the light radius to what is indicated here, (only has an effect in D3D and glide of course).
LightB int // Change the color of the light radius to what is indicated here, (only has an effect in D3D and glide of course).
OnSound string // Sound played respectively when the state is initially applied
OffSound string // and when it expires
GfxType int // What unit type is used for the disguise gfx (1 being monsters, 2 being players, contrary to internal game logic).
GfxClass int // The unit class used for disguise gfx, this corresponds with the index from monstats.txt and charstats.txt
// When 'gfxtype' is set to 1, the "class" represents an hcIdx from MonStats.txt. If it's set to 2 then it will indicate a character class the unit with this state will be morphed into.
CltEvent string // Clientside event callback for this state (likely very inconsistent with the server side events, beware).
CltEventFunc int // Callback function invoked when the client event triggers.
CltActiveFunc int // CltDoFunc called every frame the state is active
SrvActiveFunc int // Srvdofunc called every frame the state is active
// The missile that this state will utilize for certain events,
// how this is used depends entirely on the functions associated with the state.
Missile string
// The skill that will be queried for this state in some sections of code,
// strangely enough this contradicts the fact states store their assigner skill anyway
// (via STAT_MODIFIERLIST_SKILL)
Skill string
// What item type is effected by this states color change.
ItemType string
// The color being applied to this item
// (only going to have an effect on alternate gfx, inventory gfx isn't effected).
ItemTrans string
// Sound played respectively when the state is initially applied
OnSound string
// and when it expires
OffSound string
// States can be grouped together, so they cannot stack
Group int
// Clientside callback function invoked when the state is applied initially.
SetFunc int
// Clientside callback function invoked when the state expires or is otherwise removed.
RemFunc int
// The color priority for this states color change, the, this can range from 0 to 255,
// the state with the highest color priority will always be used should more then
// one co-exist on the unit. If two states exist with the same priority the one with the
// lowest id is used (IIRC).
ColorPri int
// Index for the color shift palette picked from the *.PL2 files.
ColorShift int
// Change the color of the light radius to what is indicated here,
// (only has an effect in D3D and glide of course).
LightR int
// Change the color of the light radius to what is indicated here,
// (only has an effect in D3D and glide of course).
LightG int
// Change the color of the light radius to what is indicated here,
// (only has an effect in D3D and glide of course).
LightB int
// What unit type is used for the disguise gfx
// (1 being monsters, 2 being players, contrary to internal game logic).
GfxType int
// The unit class used for disguise gfx, this corresponds with the index
// from monstats.txt and charstats.txt
GfxClass int
// When 'gfxtype' is set to 1, the "class" represents an hcIdx from MonStats.txt.
// If it's set to 2 then it will indicate a character class the unit with this state will be
// morphed into.
// Clientside event callback for this state
// (likely very inconsistent with the server side events, beware).
CltEvent string
// Callback function invoked when the client event triggers.
CltEventFunc int
// CltDoFunc called every frame the state is active
CltActiveFunc int
// Srvdofunc called every frame the state is active
SrvActiveFunc int
// If a monster gets hit, the state will be dispelled
RemHit bool
// Not yet analyzed in detail
NoSend bool
// Whenever a state transforms the appearance of a unit
Transform bool
// Aura states will stack on-screen. Aura states are dispelled when a monster is
// affected by conversion
Aura bool
// Can a heal enabled npc remove this state when you talk to them?
Cureable bool
// Curse states can't stack. Controls duration reduction from cleansing, and curse resistance.
// When a new curse is applied, the old one is removed.
Curse bool
// State has a StateActiveFunc associated with it. The active func is called every frame while
// the state is active.
Active bool
// State restricts skill usage (druid shapeshift)
Restrict bool
// State makes the game load another sprite (use with Transform)
Disguise bool
// State applies a color change that overrides all others
Blue bool
// Control when attack rating is displayed in blue
AttBlue bool
// Control when damage is displayed in blue
DmgBlue bool
// Control when armor class is displayed in blue
ArmBlue bool
// Control when fire resistance is displayed in blue
RfBlue bool
// Control when lightning resistance is displayed in blue
RlBlue bool
// Control when cold resistance is displayed in blue
RcBlue bool
// Control when poison resistance is displayed in blue
RpBlue bool
// Control when attack rating is displayed in red
AttRed bool
// Control when damage is displayed in red
DmgRed bool
// Control when armor class is displayed in red
ArmRed bool
// Control when fire resistance is displayed in red
RfRed bool
// Control when lightning resistance is displayed in red
RlRed bool
// Control when cold resistance is displayed in red
RcRed bool
// Control when poison resistance is displayed in red
RpRed bool
// Control when stamina bar color is changed to blue
StamBarBlue bool
// When a unit effected by this state kills another unit,
// the summon owner will receive experience
Exp bool
// Whenever the state is removed when the player dies
PlrStayDeath bool
// Whenever the state is removed when the monster dies
MonStayDeath bool
// Whenever the state is removed when the boss dies. Prevents bosses from shattering?
BossStayDeath bool
// When the unit dies, the corpse and death animation will not be drawn
Hide bool
// Whenever the unit shatters or explodes when it dies. This is heavily hardcoded,
// it will always use the ice shatter for all states other than STATE_UBERMINION
Shatter bool
// Whenever this state prevents the corpse from being selected by spells and the ai
UDead bool
// When a state with this is active, it cancels out the native life regen of monsters.
// (using only the mod part instead of accr).
Life bool
// Whenever this state applies a color change that overrides all others (such as from items).
// (see blue column, which seams to do the same).
Green bool
// Whenever this state is associated with progressive spells and will be
// looked up when the charges are triggered.
Pgsv bool
// Related to assigning overlays to the unit, not extensively researched yet.
NoOverlays bool
// Like the previous column, also only used on states with the previous column enabled.
NoClear bool
// whenever this state will use the minion owners inventory clientside (this is what makes
// the decoy always show up with your own equipment,
// even when you change what you wear after summoning one).
BossInv bool
// Prevents druids that wield a bow or crossbow while shape shifted from
// firing missiles, and will rather attack in melee.
MeleeOnly bool
// Not researched yet
NotOnDead bool
}
// States contains the state records

View File

@ -2,8 +2,9 @@ package d2datadict
import (
"fmt"
"github.com/OpenDiablo2/OpenDiablo2/d2common"
"log"
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
const (
@ -12,37 +13,6 @@ const (
treasureProbFmt = "Prob%d"
)
// TreasureDropType indicates the drop type of the treasure
type TreasureDropType int
const (
// TreasureNone is default bad case, but nothing should have this
TreasureNone TreasureDropType = iota
// TreasureGold indicates that the treasure drop type is for gold
TreasureGold
// indicates that the drop type resolves directly to an ItemCommonRecord
TreasureWeapon
TreasureArmor
TreasureMisc
// indicates that the code is for a dynamic item record, because the treasure code has
// and item level appended to it. this is for things like `armo63` or `weap24` which does not
// explicitly have an item record that matches this code, but we need to resolve this
TreasureWeaponDynamic
TreasureArmorDynamic
TreasureMiscDynamic
)
const (
GoldMultDropCodeStr string = "gld,mul="
GoldDropCodeStr = "gld"
WeaponDropCodeStr = "weap"
ArmorDropCodeStr = "armo"
MiscDropCodeStr = "misc"
)
// TreasureClassRecord represents a rule for item drops in diablo 2
type TreasureClassRecord struct {
Name string

View File

@ -6,20 +6,24 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common"
)
// UniqueAppellationRecord described the extra suffix of a unique monster name
type UniqueAppellationRecord struct {
// The title
Name string
}
// UniqueAppellations contains all of the UniqueAppellationRecords
//nolint:gochecknoglobals // Currently global by design
var UniqueAppellations map[string]*UniqueAppellationRecord
// LoadUniqueAppellations loads UniqueAppellationRecords from UniqueAppelation.txt
func LoadUniqueAppellations(file []byte) {
UniqueAppellations = make(map[string]*UniqueAppellationRecord)
d := d2common.LoadDataDictionary(file)
for d.Next() {
record := &UniqueAppellationRecord{
Name: d.String("Name"),
Name: d.String("Name"),
}
UniqueAppellations[record.Name] = record
}

View File

@ -214,6 +214,7 @@ const (
MonsterSound = "/data/global/excel/monsounds.txt"
MonsterSequence = "/data/global/excel/monseq.txt"
PlayerClass = "/data/global/excel/PlayerClass.txt"
CompCode = "/data/global/excel/compcode.txt"
// --- Animations ---
@ -316,13 +317,14 @@ const (
// --- Enemy Data ---
MonStats = "/data/global/excel/monstats.txt"
MonStats2 = "/data/global/excel/monstats2.txt"
MonPreset = "/data/global/excel/monpreset.txt"
MonType = "/data/global/excel/Montype.txt"
SuperUniques = "/data/global/excel/SuperUniques.txt"
MonMode = "/data/global/excel/monmode.txt"
MonStats = "/data/global/excel/monstats.txt"
MonStats2 = "/data/global/excel/monstats2.txt"
MonPreset = "/data/global/excel/monpreset.txt"
MonType = "/data/global/excel/Montype.txt"
SuperUniques = "/data/global/excel/SuperUniques.txt"
MonMode = "/data/global/excel/monmode.txt"
MonsterPlacement = "/data/global/excel/MonPlace.txt"
MonsterAI = "/data/global/excel/monai.txt"
// --- Skill Data ---