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:
parent
c216ddab53
commit
466855e5f5
204
d2app/app.go
204
d2app/app.go
@ -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))
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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))
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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))
|
||||
|
||||
}
|
||||
|
@ -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"),
|
||||
},
|
||||
|
@ -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())
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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"),
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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"))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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)),
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 ---
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user