1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-06-11 18:20:42 +00:00

Switch items to dynamic load with a common struct, add misc.txt loading (#185)

This commit is contained in:
ndechiara 2019-11-15 22:31:10 -05:00 committed by Tim Sarbin
parent 0e0ad09e70
commit f13433f299
9 changed files with 505 additions and 549 deletions

9
d2common/calcstring.go Normal file
View File

@ -0,0 +1,9 @@
package d2common
// a calcstring is a type of string often used in datafiles to specify
// a value that is calculated dynamically based on the stats of the relevant
// source, for instance a missile might have a movement speed of lvl*2
type CalcString string
// todo: the logic for parsing these should exist here

View File

@ -73,6 +73,7 @@ func CreateEngine() Engine {
d2datadict.LoadObjects(&result)
d2datadict.LoadWeapons(&result)
d2datadict.LoadArmors(&result)
d2datadict.LoadMiscItems(&result)
d2datadict.LoadUniqueItems(&result)
d2datadict.LoadMissiles(&result)
d2datadict.LoadSounds(&result)

View File

@ -2,281 +2,17 @@ package d2datadict
import (
"log"
"strings"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
dh "github.com/OpenDiablo2/OpenDiablo2/d2helper"
)
type ArmorRecord struct {
Name string
Version int // 0 = classic, 100 = expansion
CompactSave bool // if true, doesn't store any stats upon saving
Rarity int // higher, the rarer
Spawnable bool // if 0, cannot spawn in shops
MinAC int
MaxAC int
Absorbs int // unused?
Speed int // affects movement speed of wielder, >0 = you move slower, <0 = you move faster
RequiredStrength int
Block int // chance to block, capped at 75%
Durability int // base durability 0-255
NoDurability bool // if true, item has no durability
Level int // base item level (controls monster drops, for instance a lv20 monster cannot drop a lv30 item)
RequiredLevel int // required level to wield
Cost int // base cost
GambleCost int // for reference only, not used
Code string // identifies the item
NameString string // seems to be identical to code?
MagicLevel int // additional magic level (for gambling?)
AutoPrefix int // prefix automatically assigned to this item on spawn, maps to group column of Automagic.txt
AlternateGfx string // code of the DCC used when equipped
OpenBetaGfx string // unknown
NormalCode string
UberCode string
UltraCode string
SpellOffset int // unknown
Component int // corresponds to Composit.txt, player animation layer used by this
InventoryWidth int
InventoryHeight int
HasInventory bool // if true, item can store gems or runes
GemSockets int // number of gems to store
GemApplyType int // what kind of gem effect is applied
// 0 = weapon, 1= armor or helmet, 2 = shield
FlippyFile string // DC6 file animation to play when item drops on the ground
InventoryFile string // DC6 file used in your inventory
UniqueInventoryFile string // DC6 file used by the unique version of this item
SetInventoryFile string // DC6 file used by the set version of this item
// these represent how player animations and graphics change upon wearing this
// these come from ArmType.txt
AnimRightArm int
AnimLeftArm int
AnimTorso int
AnimLegs int
AnimRightShoulderPad int
AnimLeftShoulderPad int
Useable bool // can be used via right click if true
// game knows what to do if used by item code
Throwable bool
Stackable bool // can be stacked in inventory
MinStack int // min size of stack when item is spawned, used if stackable
MaxStack int // max size of stack when item is spawned
Type string // base type in ItemTypes.txt
Type2 string
DropSound string // sfx for dropping
DropSfxFrame int // what frame of drop animation the sfx triggers on
UseSound string // sfx for using
Unique bool // if true, only spawns as unique
Transparent bool // unused
TransTable int // unknown, related to blending mode?
Quivered bool // if true, requires ammo to use
LightRadius int // apparently unused
Belt bool // tells what kind of belt this item is
Quest int // indicates that this item belongs to a given quest?
MissileType int // missile gfx for throwing
DurabilityWarning int // controls what warning icon appears when durability is low
QuantityWarning int // controls at what quantity the low quantity warning appears
MinDamage int
MaxDamage int
StrengthBonus int
DexterityBonus int
// final mindam = min * str / strbonus + min * dex / dexbonus
// same for maxdam
GemOffset int // unknown
BitField1 int // 1 = leather item, 3 = metal
Vendors map[string]*ItemVendorParams // controls vendor settings
SourceArt string // unused?
GameArt string // unused?
ColorTransform int // colormap to use for player's gfx
InventoryColorTransform int // colormap to use for inventory's gfx
SkipName bool // if true, don't include the base name in the item description
NightmareUpgrade string // upgraded in higher difficulties
HellUpgrade string
UnusedMinDamage int
UnusedMaxDamage int
Nameable bool // if true, item can be personalized
}
type ArmorVendorParams struct {
Min int // minimum of this item they can stock
Max int // max they can stock
MagicMin int
MagicMax int
MagicLevel uint8
}
func createArmorRecord(line string) ArmorRecord {
r := strings.Split(line, "\t")
i := -1
inc := func() int {
i++
return i
}
result := ArmorRecord{
Name: r[inc()],
Version: dh.StringToInt(dh.EmptyToZero(r[inc()])),
CompactSave: dh.StringToInt(dh.EmptyToZero(r[inc()])) == 1,
Rarity: dh.StringToInt(dh.EmptyToZero(r[inc()])),
Spawnable: dh.StringToInt(dh.EmptyToZero(r[inc()])) == 1,
MinAC: dh.StringToInt(dh.EmptyToZero(r[inc()])),
MaxAC: dh.StringToInt(dh.EmptyToZero(r[inc()])),
Absorbs: dh.StringToInt(dh.EmptyToZero(r[inc()])),
Speed: dh.StringToInt(dh.EmptyToZero(r[inc()])),
RequiredStrength: dh.StringToInt(dh.EmptyToZero(r[inc()])),
Block: dh.StringToInt(dh.EmptyToZero(r[inc()])),
Durability: dh.StringToInt(dh.EmptyToZero(r[inc()])),
NoDurability: dh.StringToInt(dh.EmptyToZero(r[inc()])) == 1,
Level: dh.StringToInt(dh.EmptyToZero(r[inc()])),
RequiredLevel: dh.StringToInt(dh.EmptyToZero(r[inc()])),
Cost: dh.StringToInt(dh.EmptyToZero(r[inc()])),
GambleCost: dh.StringToInt(dh.EmptyToZero(r[inc()])),
Code: r[inc()],
NameString: r[inc()],
MagicLevel: dh.StringToInt(dh.EmptyToZero(r[inc()])),
AutoPrefix: dh.StringToInt(dh.EmptyToZero(r[inc()])),
AlternateGfx: r[inc()],
OpenBetaGfx: r[inc()],
NormalCode: r[inc()],
UberCode: r[inc()],
UltraCode: r[inc()],
SpellOffset: dh.StringToInt(dh.EmptyToZero(r[inc()])),
Component: dh.StringToInt(dh.EmptyToZero(r[inc()])),
InventoryWidth: dh.StringToInt(dh.EmptyToZero(r[inc()])),
InventoryHeight: dh.StringToInt(dh.EmptyToZero(r[inc()])),
HasInventory: dh.StringToInt(dh.EmptyToZero(r[inc()])) == 1,
GemSockets: dh.StringToInt(dh.EmptyToZero(r[inc()])),
GemApplyType: dh.StringToInt(dh.EmptyToZero(r[inc()])),
FlippyFile: r[inc()],
InventoryFile: r[inc()],
UniqueInventoryFile: r[inc()],
SetInventoryFile: r[inc()],
AnimRightArm: dh.StringToInt(dh.EmptyToZero(r[inc()])),
AnimLeftArm: dh.StringToInt(dh.EmptyToZero(r[inc()])),
AnimTorso: dh.StringToInt(dh.EmptyToZero(r[inc()])),
AnimLegs: dh.StringToInt(dh.EmptyToZero(r[inc()])),
AnimRightShoulderPad: dh.StringToInt(dh.EmptyToZero(r[inc()])),
AnimLeftShoulderPad: dh.StringToInt(dh.EmptyToZero(r[inc()])),
Useable: dh.StringToInt(dh.EmptyToZero(r[inc()])) == 1,
Throwable: dh.StringToInt(dh.EmptyToZero(r[inc()])) == 1,
Stackable: dh.StringToInt(dh.EmptyToZero(r[inc()])) == 1,
MinStack: dh.StringToInt(dh.EmptyToZero(r[inc()])),
MaxStack: dh.StringToInt(dh.EmptyToZero(r[inc()])),
Type: r[inc()],
Type2: r[inc()],
DropSound: r[inc()],
DropSfxFrame: dh.StringToInt(dh.EmptyToZero(r[inc()])),
UseSound: r[inc()],
Unique: dh.StringToInt(dh.EmptyToZero(r[inc()])) == 1,
Transparent: dh.StringToInt(dh.EmptyToZero(r[inc()])) == 1,
TransTable: dh.StringToInt(dh.EmptyToZero(r[inc()])),
Quivered: dh.StringToInt(dh.EmptyToZero(r[inc()])) == 1,
LightRadius: dh.StringToInt(dh.EmptyToZero(r[inc()])),
Belt: dh.StringToInt(dh.EmptyToZero(r[inc()])) == 1,
Quest: dh.StringToInt(dh.EmptyToZero(r[inc()])),
MissileType: dh.StringToInt(dh.EmptyToZero(r[inc()])),
DurabilityWarning: dh.StringToInt(dh.EmptyToZero(r[inc()])),
QuantityWarning: dh.StringToInt(dh.EmptyToZero(r[inc()])),
MinDamage: dh.StringToInt(dh.EmptyToZero(r[inc()])),
MaxDamage: dh.StringToInt(dh.EmptyToZero(r[inc()])),
StrengthBonus: dh.StringToInt(dh.EmptyToZero(r[inc()])),
DexterityBonus: dh.StringToInt(dh.EmptyToZero(r[inc()])),
GemOffset: dh.StringToInt(dh.EmptyToZero(r[inc()])),
BitField1: dh.StringToInt(dh.EmptyToZero(r[inc()])),
Vendors: createArmorVendorParams(&r, inc),
SourceArt: r[inc()],
GameArt: r[inc()],
ColorTransform: dh.StringToInt(dh.EmptyToZero(r[inc()])),
InventoryColorTransform: dh.StringToInt(dh.EmptyToZero(r[inc()])),
SkipName: dh.StringToInt(dh.EmptyToZero(r[inc()])) == 1,
NightmareUpgrade: r[inc()],
HellUpgrade: r[inc()],
UnusedMinDamage: dh.StringToInt(dh.EmptyToZero(r[inc()])),
UnusedMaxDamage: dh.StringToInt(dh.EmptyToZero(r[inc()])),
Nameable: dh.StringToInt(dh.EmptyToZero(r[inc()])) == 1,
}
return result
}
func createArmorVendorParams(r *[]string, inc func() int) map[string]*ItemVendorParams {
vs := make([]string, 17)
vs[0] = "Charsi"
vs[1] = "Gheed"
vs[2] = "Akara"
vs[3] = "Fara"
vs[4] = "Lysander"
vs[5] = "Drognan"
vs[6] = "Hralti"
vs[7] = "Alkor"
vs[8] = "Ormus"
vs[9] = "Elzix"
vs[10] = "Asheara"
vs[11] = "Cain"
vs[12] = "Halbu"
vs[13] = "Jamella"
vs[14] = "Larzuk"
vs[15] = "Malah"
vs[16] = "Drehya"
return CreateItemVendorParams(r, inc, vs)
}
var Armors map[string]*ArmorRecord
var Armors map[string]*ItemCommonRecord
func LoadArmors(fileProvider d2interface.FileProvider) {
Armors = make(map[string]*ArmorRecord)
data := strings.Split(string(fileProvider.LoadFile(d2resource.Armor)), "\r\n")[1:]
for _, line := range data {
if len(line) == 0 {
continue
}
rec := createArmorRecord(line)
Armors[rec.Code] = &rec
}
Armors = *LoadCommonItems(fileProvider, d2resource.Armor, d2enum.InventoryItemTypeArmor)
log.Printf("Loaded %d armors", len(Armors))
}

View File

@ -0,0 +1,416 @@
package d2datadict
import (
"strings"
"strconv"
"github.com/OpenDiablo2/OpenDiablo2/d2common"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
)
type ItemCommonRecord struct {
Source d2enum.InventoryItemType
Name string
Version int // 0 = classic, 100 = expansion
CompactSave bool // if true, doesn't store any stats upon saving
Rarity int // higher, the rarer
Spawnable bool // if 0, cannot spawn in shops
MinAC int
MaxAC int
Absorbs int // unused?
Speed int // affects movement speed of wielder, >0 = you move slower, <0 = you move faster
RequiredStrength int
Block int // chance to block, capped at 75%
Durability int // base durability 0-255
NoDurability bool // if true, item has no durability
Level int // base item level (controls monster drops, for instance a lv20 monster cannot drop a lv30 item)
RequiredLevel int // required level to wield
Cost int // base cost
GambleCost int // for reference only, not used
Code string // identifies the item
NameString string // seems to be identical to code?
MagicLevel int // additional magic level (for gambling?)
AutoPrefix int // prefix automatically assigned to this item on spawn, maps to group column of Automagic.txt
AlternateGfx string // code of the DCC used when equipped
OpenBetaGfx string // unknown
NormalCode string
UberCode string
UltraCode string
SpellOffset int // unknown
Component int // corresponds to Composit.txt, player animation layer used by this
InventoryWidth int
InventoryHeight int
HasInventory bool // if true, item can store gems or runes
GemSockets int // number of gems to store
GemApplyType int // what kind of gem effect is applied
// 0 = weapon, 1= armor or helmet, 2 = shield
FlippyFile string // DC6 file animation to play when item drops on the ground
InventoryFile string // DC6 file used in your inventory
UniqueInventoryFile string // DC6 file used by the unique version of this item
SetInventoryFile string // DC6 file used by the set version of this item
// these represent how player animations and graphics change upon wearing this
// these come from ArmType.txt
AnimRightArm int
AnimLeftArm int
AnimTorso int
AnimLegs int
AnimRightShoulderPad int
AnimLeftShoulderPad int
Useable bool // can be used via right click if true
// game knows what to do if used by item code
Throwable bool
Stackable bool // can be stacked in inventory
MinStack int // min size of stack when item is spawned, used if stackable
MaxStack int // max size of stack when item is spawned
Type string // base type in ItemTypes.txt
Type2 string
DropSound string // sfx for dropping
DropSfxFrame int // what frame of drop animation the sfx triggers on
UseSound string // sfx for using
Unique bool // if true, only spawns as unique
Transparent bool // unused
TransTable int // unknown, related to blending mode?
Quivered bool // if true, requires ammo to use
LightRadius int // apparently unused
Belt bool // tells what kind of belt this item is
Quest int // indicates that this item belongs to a given quest?
MissileType int // missile gfx for throwing
DurabilityWarning int // controls what warning icon appears when durability is low
QuantityWarning int // controls at what quantity the low quantity warning appears
MinDamage int
MaxDamage int
StrengthBonus int
DexterityBonus int
// final mindam = min * str / strbonus + min * dex / dexbonus
// same for maxdam
GemOffset int // unknown
BitField1 int // 1 = leather item, 3 = metal
Vendors map[string]*ItemVendorParams // controls vendor settings
SourceArt string // unused?
GameArt string // unused?
ColorTransform int // colormap to use for player's gfx
InventoryColorTransform int // colormap to use for inventory's gfx
SkipName bool // if true, don't include the base name in the item description
NightmareUpgrade string // upgraded in higher difficulties
HellUpgrade string
Nameable bool // if true, item can be personalized
// weapon params
BarbOneOrTwoHanded bool // if true, barb can wield this in one or two hands
UsesTwoHands bool // if true, it's a 2handed weapon
Min2HandDamage int
Max2HandDamage int
MinMissileDamage int // ranged damage stats
MaxMissileDamage int
MissileSpeed int // unknown, affects movement speed of wielder during ranged attacks?
ExtraRange int // base range = 1, if this is non-zero add this to the range
// final mindam = min * str / strbonus + min * dex / dexbonus
// same for maxdam
RequiredDexterity int
WeaponClass string // what kind of attack does this weapon have (i.e. determines attack animations)
WeaponClass2Hand string // what kind of attack when wielded with two hands
HitClass string // determines sounds/graphic effects when attacking
SpawnStack int // unknown, something to do with stack size when spawned (sold maybe?)
SpecialFeature string // Just a comment
QuestDifficultyCheck bool // if true, item only works in the difficulty it was found in
PermStoreItem bool // if true, vendor will always sell this
// misc params
FlavorText string // unknown, probably just for reference
Transmogrify bool // if true, can be turned into another item via right click
TransmogCode string // the 3 char code representing the item this becomes via transmog
TransmogMin int // min amount of the transmog item to create
TransmogMax int // max ''
AutoBelt bool // if true, item is put into your belt when picked up
SpellIcon int // which icon to display when used? Is this always -1?
SpellType int // determines what kind of function is used when you use this item
OverlayState string // name of the overlay state to be applied upon use of this item
CureOverlayStates [2]string // name of the overlay states that are removed upon use of this item
EffectLength int // timer for timed usage effects
UsageStats [3]ItemUsageStat // stat boosts applied upon usage
SpellDescriptionType int // specifies how to format the usage description
// 0 = none, 1 = use desc string, 2 = use desc string + calc value
SpellDescriptionString string // points to a string containing the description
SpellDescriptionCalc d2common.CalcString // a calc string what value to display
BetterGem string // 3 char code pointing to the gem this upgrades to (non if not applicable)
Multibuy bool // if true, when you buy via right click + shift it will fill your belt automatically
}
type ItemUsageStat struct {
Stat string // name of the stat to add to
Calc d2common.CalcString // calc string representing the amount to add
}
type ItemVendorParams struct {
Min int // minimum of this item they can stock
Max int // max they can stock
MagicMin int
MagicMax int
MagicLevel uint8
}
// Loading Functions
var CommonItems map[string]*ItemCommonRecord
func LoadCommonItems(fileProvider d2interface.FileProvider, filepath string, source d2enum.InventoryItemType) *map[string]*ItemCommonRecord {
if CommonItems == nil {
CommonItems = make(map[string]*ItemCommonRecord)
}
items := make(map[string]*ItemCommonRecord)
data := strings.Split(string(fileProvider.LoadFile(filepath)), "\r\n")
mapping := MapHeaders(data[0])
for lineno, line := range data {
if lineno == 0 {
continue
}
if len(line) == 0 {
continue
}
rec := createCommonItemRecord(line, &mapping, source)
items[rec.Code] = &rec
CommonItems[rec.Code] = &rec
}
return &items
}
func createCommonItemRecord(line string, mapping *map[string]int, source d2enum.InventoryItemType) ItemCommonRecord {
r := strings.Split(line, "\t")
result := ItemCommonRecord{
Source: source,
Name: MapLoadString(&r, mapping, "name"),
Version: MapLoadInt(&r, mapping, "version"),
CompactSave: MapLoadBool(&r, mapping, "compactsave"),
Rarity: MapLoadInt(&r, mapping, "rarity"),
Spawnable: MapLoadBool(&r, mapping, "spawnable"),
MinAC: MapLoadInt(&r, mapping, "minac"),
MaxAC: MapLoadInt(&r, mapping, "maxac"),
Absorbs: MapLoadInt(&r, mapping, "absorbs"),
Speed: MapLoadInt(&r, mapping, "speed"),
RequiredStrength: MapLoadInt(&r, mapping, "reqstr"),
Block: MapLoadInt(&r, mapping, "block"),
Durability: MapLoadInt(&r, mapping, "durability"),
NoDurability: MapLoadBool(&r, mapping, "nodurability"),
Level: MapLoadInt(&r, mapping, "level"),
RequiredLevel: MapLoadInt(&r, mapping, "levelreq"),
Cost: MapLoadInt(&r, mapping, "cost"),
GambleCost: MapLoadInt(&r, mapping, "gamble cost"),
Code: MapLoadString(&r, mapping, "code"),
NameString: MapLoadString(&r, mapping, "namestr"),
MagicLevel: MapLoadInt(&r, mapping, "magic lvl"),
AutoPrefix: MapLoadInt(&r, mapping, "auto prefix"),
AlternateGfx: MapLoadString(&r, mapping, "alternategfx"),
OpenBetaGfx: MapLoadString(&r, mapping, "OpenBetaGfx"),
NormalCode: MapLoadString(&r, mapping, "normcode"),
UberCode: MapLoadString(&r, mapping, "ubercode"),
UltraCode: MapLoadString(&r, mapping, "ultracode"),
SpellOffset: MapLoadInt(&r, mapping, "spelloffset"),
Component: MapLoadInt(&r, mapping, "component"),
InventoryWidth: MapLoadInt(&r, mapping, "invwidth"),
InventoryHeight: MapLoadInt(&r, mapping, "invheight"),
HasInventory: MapLoadBool(&r, mapping, "hasinv"),
GemSockets: MapLoadInt(&r, mapping, "gemsockets"),
GemApplyType: MapLoadInt(&r, mapping, "gemapplytype"),
FlippyFile: MapLoadString(&r, mapping, "flippyfile"),
InventoryFile: MapLoadString(&r, mapping, "invfile"),
UniqueInventoryFile: MapLoadString(&r, mapping, "uniqueinvfile"),
SetInventoryFile: MapLoadString(&r, mapping, "setinvfile"),
AnimRightArm: MapLoadInt(&r, mapping, "rArm"),
AnimLeftArm: MapLoadInt(&r, mapping, "lArm"),
AnimTorso: MapLoadInt(&r, mapping, "Torso"),
AnimLegs: MapLoadInt(&r, mapping, "Legs"),
AnimRightShoulderPad: MapLoadInt(&r, mapping, "rSPad"),
AnimLeftShoulderPad: MapLoadInt(&r, mapping, "lSPad"),
Useable: MapLoadBool(&r, mapping, "useable"),
Throwable: MapLoadBool(&r, mapping, "throwable"),
Stackable: MapLoadBool(&r, mapping, "stackable"),
MinStack: MapLoadInt(&r, mapping, "minstack"),
MaxStack: MapLoadInt(&r, mapping, "maxstack"),
Type: MapLoadString(&r, mapping, "type"),
Type2: MapLoadString(&r, mapping, "type2"),
DropSound: MapLoadString(&r, mapping, "dropsound"),
DropSfxFrame: MapLoadInt(&r, mapping, "dropsfxframe"),
UseSound: MapLoadString(&r, mapping, "usesound"),
Unique: MapLoadBool(&r, mapping, "unique"),
Transparent: MapLoadBool(&r, mapping, "transparent"),
TransTable: MapLoadInt(&r, mapping, "transtbl"),
Quivered: MapLoadBool(&r, mapping, "quivered"),
LightRadius: MapLoadInt(&r, mapping, "lightradius"),
Belt: MapLoadBool(&r, mapping, "belt"),
Quest: MapLoadInt(&r, mapping, "quest"),
MissileType: MapLoadInt(&r, mapping, "missiletype"),
DurabilityWarning: MapLoadInt(&r, mapping, "durwarning"),
QuantityWarning: MapLoadInt(&r, mapping, "qntwarning"),
MinDamage: MapLoadInt(&r, mapping, "mindam"),
MaxDamage: MapLoadInt(&r, mapping, "maxdam"),
StrengthBonus: MapLoadInt(&r, mapping, "StrBonus"),
DexterityBonus: MapLoadInt(&r, mapping, "DexBonus"),
GemOffset: MapLoadInt(&r, mapping, "gemoffset"),
BitField1: MapLoadInt(&r, mapping, "bitfield1"),
Vendors: createItemVendorParams(&r, mapping),
SourceArt: MapLoadString(&r, mapping, "Source Art"),
GameArt: MapLoadString(&r, mapping, "Game Art"),
ColorTransform: MapLoadInt(&r, mapping, "Transform"),
InventoryColorTransform: MapLoadInt(&r, mapping, "InvTrans"),
SkipName: MapLoadBool(&r, mapping, "SkipName"),
NightmareUpgrade: MapLoadString(&r, mapping, "NightmareUpgrade"),
HellUpgrade: MapLoadString(&r, mapping, "HellUpgrade"),
Nameable: MapLoadBool(&r, mapping, "Nameable"),
// weapon params
BarbOneOrTwoHanded: MapLoadBool(&r, mapping, "1or2handed"),
UsesTwoHands: MapLoadBool(&r, mapping, "2handed"),
Min2HandDamage: MapLoadInt(&r, mapping, "2handmindam"),
Max2HandDamage: MapLoadInt(&r, mapping, "2handmaxdam"),
MinMissileDamage: MapLoadInt(&r, mapping, "minmisdam"),
MaxMissileDamage: MapLoadInt(&r, mapping, "maxmisdam"),
MissileSpeed: MapLoadInt(&r, mapping, "misspeed"),
ExtraRange: MapLoadInt(&r, mapping, "rangeadder"),
RequiredDexterity: MapLoadInt(&r, mapping, "reqdex"),
WeaponClass: MapLoadString(&r, mapping, "wclass"),
WeaponClass2Hand: MapLoadString(&r, mapping, "2handedwclass"),
HitClass: MapLoadString(&r, mapping, "hit class"),
SpawnStack: MapLoadInt(&r, mapping, "spawnstack"),
SpecialFeature: MapLoadString(&r, mapping, "special"),
QuestDifficultyCheck: MapLoadBool(&r, mapping, "questdiffcheck"),
PermStoreItem: MapLoadBool(&r, mapping, "PermStoreItem"),
// misc params
FlavorText: MapLoadString(&r, mapping, "szFlavorText"),
Transmogrify: MapLoadBool(&r, mapping, "Transmogrify"),
TransmogCode: MapLoadString(&r, mapping, "TMogType"),
TransmogMin: MapLoadInt(&r, mapping, "TMogMin"),
TransmogMax: MapLoadInt(&r, mapping, "TMogMax"),
AutoBelt: MapLoadBool(&r, mapping, "autobelt"),
SpellIcon: MapLoadInt(&r, mapping, "spellicon"),
SpellType: MapLoadInt(&r, mapping, "pSpell"),
OverlayState: MapLoadString(&r, mapping, "state"),
CureOverlayStates: [2]string{
MapLoadString(&r, mapping, "cstate1"),
MapLoadString(&r, mapping, "cstate2"),
},
EffectLength: MapLoadInt(&r, mapping, "len"),
UsageStats: createItemUsageStats(&r, mapping),
SpellDescriptionType: MapLoadInt(&r, mapping, "spelldesc"),
// 0 = none, 1 = use desc string, 2 = use desc string + calc value
SpellDescriptionString: MapLoadString(&r, mapping, "spelldescstr"),
SpellDescriptionCalc: d2common.CalcString(MapLoadString(&r, mapping, "spelldesccalc")),
BetterGem: MapLoadString(&r, mapping, "BetterGem"),
Multibuy: MapLoadBool(&r, mapping, "multibuy"),
}
return result
}
func createItemVendorParams(r *[]string, mapping *map[string]int) map[string]*ItemVendorParams {
vs := make([]string, 17)
vs[0] = "Charsi"
vs[1] = "Gheed"
vs[2] = "Akara"
vs[3] = "Fara"
vs[4] = "Lysander"
vs[5] = "Drognan"
vs[6] = "Hralti"
vs[7] = "Alkor"
vs[8] = "Ormus"
vs[9] = "Elzix"
vs[10] = "Asheara"
vs[11] = "Cain"
vs[12] = "Halbu"
vs[13] = "Jamella"
vs[14] = "Larzuk"
vs[15] = "Malah"
vs[16] = "Drehya"
result := make(map[string]*ItemVendorParams)
for _, name := range vs {
wvp := ItemVendorParams{
Min: MapLoadInt(r, mapping, name + "Min"),
Max: MapLoadInt(r, mapping, name + "Max"),
MagicMin: MapLoadInt(r, mapping, name + "MagicMin"),
MagicMax: MapLoadInt(r, mapping, name + "MagicMax"),
MagicLevel: MapLoadUint8(r, mapping, name + "MagicLvl"),
}
result[name] = &wvp
}
return result
}
func createItemUsageStats(r *[]string, mapping *map[string]int) [3]ItemUsageStat {
result := [3]ItemUsageStat{}
for i := 0; i < 3; i++ {
result[i].Stat = MapLoadString(r, mapping, "stat" + strconv.Itoa(i))
result[i].Calc = d2common.CalcString(MapLoadString(r, mapping, "calc" + strconv.Itoa(i)))
}
return result
}

View File

@ -0,0 +1,43 @@
package d2datadict
import (
"strings"
dh "github.com/OpenDiablo2/OpenDiablo2/d2helper"
)
func MapHeaders(line string) map[string]int {
m := make(map[string]int)
r := strings.Split(line, "\t")
for index, header := range r {
m[header] = index
}
return m
}
func MapLoadInt(r *[]string, mapping *map[string]int, field string) int {
index, ok := (*mapping)[field]
if ok {
return dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty((*r)[index])))
}
return 0
}
func MapLoadString(r *[]string, mapping *map[string]int, field string) string {
index, ok := (*mapping)[field]
if ok {
return dh.AsterToEmpty((*r)[index])
}
return ""
}
func MapLoadBool(r *[]string, mapping *map[string]int, field string) bool {
return MapLoadInt(r, mapping, field) == 1
}
func MapLoadUint8(r *[]string, mapping *map[string]int, field string) uint8 {
index, ok := (*mapping)[field]
if ok {
return dh.StringToUint8(dh.EmptyToZero(dh.AsterToEmpty((*r)[index])))
}
return 0
}

18
d2data/d2datadict/misc.go Normal file
View File

@ -0,0 +1,18 @@
package d2datadict
import (
"log"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
)
var MiscItems map[string]*ItemCommonRecord
func LoadMiscItems(fileProvider d2interface.FileProvider) {
MiscItems = *LoadCommonItems(fileProvider, d2resource.Misc, d2enum.InventoryItemTypeItem)
log.Printf("Loaded %d misc items", len(MiscItems))
}

View File

@ -4,6 +4,8 @@ import (
"log"
"strings"
"github.com/OpenDiablo2/OpenDiablo2/d2common"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
@ -17,7 +19,7 @@ type MissileCalcParam struct {
}
type MissileCalc struct {
Calc string
Calc d2common.CalcString
Desc string
Params []MissileCalcParam
}
@ -64,7 +66,7 @@ type MissileDamage struct {
MinLevelDamage [5]int // additional damage per missile level
// [0]: lvs 2-8, [1]: lvs 9-16, [2]: lvs 17-22, [3]: lvs 23-28, [4]: lv 29+
MaxLevelDamage [5]int // see above
DamageSynergyPerCalc string // works like synergy in skills.txt, not clear
DamageSynergyPerCalc d2common.CalcString // works like synergy in skills.txt, not clear
}
type MissileElementalDamage struct {
@ -318,7 +320,7 @@ func loadMissileCalcParam(r *[]string, inc func() int) MissileCalcParam {
func loadMissileCalc(r *[]string, inc func() int, params int) MissileCalc {
result := MissileCalc{
Calc: (*r)[inc()],
Calc: d2common.CalcString((*r)[inc()]),
Desc: (*r)[inc()],
}
result.Params = make([]MissileCalcParam, params)
@ -389,7 +391,7 @@ func loadMissileDamage(r *[]string, inc func() int) MissileDamage {
dh.StringToInt(dh.EmptyToZero((*r)[inc()])),
dh.StringToInt(dh.EmptyToZero((*r)[inc()])),
},
DamageSynergyPerCalc: (*r)[inc()],
DamageSynergyPerCalc: d2common.CalcString((*r)[inc()]),
}
return result
}

View File

@ -4,6 +4,8 @@ import (
"log"
"strings"
"github.com/OpenDiablo2/OpenDiablo2/d2common"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
@ -47,7 +49,7 @@ type UniqueItemRecord struct {
type UniqueItemProperty struct {
Property string
Parameter string // depending on the property, this may be an int (usually), or a string
Parameter d2common.CalcString // depending on the property, this may be an int (usually), or a string
Min int
Max int
}
@ -109,7 +111,7 @@ func createUniqueItemRecord(r []string) UniqueItemRecord {
func createUniqueItemProperty(r *[]string, inc func() int) UniqueItemProperty {
result := UniqueItemProperty{
Property: (*r)[inc()],
Parameter: (*r)[inc()],
Parameter: d2common.CalcString((*r)[inc()]),
Min: dh.StringToInt(dh.EmptyToZero((*r)[inc()])),
Max: dh.StringToInt(dh.EmptyToZero((*r)[inc()])),
}

View File

@ -2,288 +2,17 @@ package d2datadict
import (
"log"
"strings"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
dh "github.com/OpenDiablo2/OpenDiablo2/d2helper"
)
type WeaponRecord struct {
Name string
Type string // base type in ItemTypes.txt
Type2 string
Code string // identifies the item
AlternateGfx string // code of the DCC used when equipped
NameString string // seems to be identical to code?
Version int // 0 = classic, 100 = expansion
CompactSave bool // if true, doesn't store any stats upon saving
Rarity int // higher, the rarer
Spawnable bool // if 0, cannot spawn in shops
MinDamage int
MaxDamage int
BarbOneOrTwoHanded bool // if true, barb can wield this in one or two hands
UsesTwoHands bool // if true, it's a 2handed weapon
Min2HandDamage int
Max2HandDamage int
MinMissileDamage int // ranged damage stats
MaxMissileDamage int
MissileSpeed int // unknown, affects movement speed of wielder during ranged attacks?
ExtraRange int // base range = 1, if this is non-zero add this to the range
Speed int // affects movement speed of wielder, >0 = you move slower, <0 = you move faster
StrengthBonus int
DexterityBonus int
// final mindam = min * str / strbonus + min * dex / dexbonus
// same for maxdam
RequiredStrength int
RequiredDexterity int
Durability int // base durability 0-255
NoDurability bool // if true, item has no durability
Level int // base item level (controls monster drops, for instance a lv20 monster cannot drop a lv30 item)
RequiredLevel int // required level to wield
Cost int // base cost
GambleCost int // for reference only, not used
MagicLevel int // additional magic level (for gambling?)
AutoPrefix int // prefix automatically assigned to this item on spawn, maps to group column of Automagic.txt
OpenBetaGfx string // unknown
NormalCode string
UberCode string
UltraCode string
WeaponClass string // what kind of attack does this weapon have (i.e. determines attack animations)
WeaponClass2Hand string // what kind of attack when wielded with two hands
Component int // corresponds to Composit.txt, player animation layer used by this
HitClass string // determines sounds/graphic effects when attacking
InventoryWidth int
InventoryHeight int
Stackable bool // can be stacked in inventory
MinStack int // min size of stack when item is spawned, used if stackable
MaxStack int // max size of stack when item is spawned
SpawnStack int // unknown, something to do with stack size when spawned (sold maybe?)
FlippyFile string // DC6 file animation to play when item drops on the ground
InventoryFile string // DC6 file used in your inventory
UniqueInventoryFile string // DC6 file used by the unique version of this item
SetInventoryFile string // DC6 file used by the set version of this item
HasInventory bool // if true, item can store gems or runes
GemSockets int // number of gems to store
GemApplyType int // what kind of gem effect is applied
// 0 = weapon, 1= armor or helmet, 2 = shield
SpecialFeature string // Just a comment
Useable bool // can be used via right click if true
// game knows what to do if used by item code
DropSound string // sfx for dropping
DropSfxFrame int // what frame of drop animation the sfx triggers on
UseSound string // sfx for using
Unique bool // if true, only spawns as unique
Transparent bool // unused
TransTable int // unknown, related to blending mode?
Quivered bool // if true, requires ammo to use
LightRadius int // apparently unused
Belt bool // seems to be unused? supposed to be whether this can go in your quick access belt
Quest int // indicates that this item belongs to a given quest?
QuestDifficultyCheck bool // if true, item only works in the difficulty it was found in
MissileType int // missile gfx for throwing
DurabilityWarning int // controls what warning icon appears when durability is low
QuantityWarning int // controls at what quantity the low quantity warning appears
GemOffset int // unknown
BitField1 int // 1 = leather item, 3 = metal
Vendors map[string]*ItemVendorParams // controls vendor settings
SourceArt string // unused?
GameArt string // unused?
ColorTransform int // colormap to use for player's gfx
InventoryColorTransform int // colormap to use for inventory's gfx
SkipName bool // if true, don't include the base name in the item description
NightmareUpgrade string // upgraded in higher difficulties
HellUpgrade string
Nameable bool // if true, item can be personalized
PermStoreItem bool // if true, vendor will always sell this
}
type ItemVendorParams struct {
Min int // minimum of this item they can stock
Max int // max they can stock
MagicMin int
MagicMax int
MagicLevel uint8
}
func createWeaponRecord(line string) WeaponRecord {
r := strings.Split(line, "\t")
i := -1
inc := func() int {
i++
return i
}
result := WeaponRecord{
Name: r[inc()],
Type: r[inc()],
Type2: r[inc()],
Code: r[inc()],
AlternateGfx: r[inc()],
NameString: r[inc()],
Version: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
CompactSave: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))) == 1,
Rarity: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
Spawnable: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))) == 1,
MinDamage: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
MaxDamage: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
BarbOneOrTwoHanded: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))) == 1,
UsesTwoHands: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))) == 1,
Min2HandDamage: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
Max2HandDamage: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
MinMissileDamage: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
MaxMissileDamage: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
MissileSpeed: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
ExtraRange: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
Speed: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
StrengthBonus: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
DexterityBonus: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
RequiredStrength: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
RequiredDexterity: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
Durability: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
NoDurability: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))) == 1,
Level: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
RequiredLevel: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
Cost: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
GambleCost: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
MagicLevel: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
AutoPrefix: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
OpenBetaGfx: r[inc()],
NormalCode: r[inc()],
UberCode: r[inc()],
UltraCode: r[inc()],
WeaponClass: r[inc()],
WeaponClass2Hand: r[inc()],
Component: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
HitClass: r[inc()],
InventoryWidth: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
InventoryHeight: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
Stackable: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))) == 1,
MinStack: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
MaxStack: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
SpawnStack: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
FlippyFile: r[inc()],
InventoryFile: r[inc()],
UniqueInventoryFile: r[inc()],
SetInventoryFile: r[inc()],
HasInventory: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))) == 1,
GemSockets: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
GemApplyType: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
SpecialFeature: r[inc()],
Useable: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))) == 1,
DropSound: r[inc()],
DropSfxFrame: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
UseSound: r[inc()],
Unique: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))) == 1,
Transparent: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))) == 1,
TransTable: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
Quivered: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))) == 1,
LightRadius: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
Belt: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))) == 1,
Quest: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
QuestDifficultyCheck: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))) == 1,
MissileType: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
DurabilityWarning: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
QuantityWarning: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
GemOffset: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
BitField1: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
Vendors: createWeaponVendorParams(&r, inc),
SourceArt: r[inc()],
GameArt: r[inc()],
ColorTransform: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
InventoryColorTransform: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))),
SkipName: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))) == 1,
NightmareUpgrade: r[inc()],
HellUpgrade: r[inc()],
Nameable: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))) == 1,
PermStoreItem: dh.StringToInt(dh.EmptyToZero(dh.AsterToEmpty(r[inc()]))) == 1,
}
return result
}
func createWeaponVendorParams(r *[]string, inc func() int) map[string]*ItemVendorParams {
vs := make([]string, 17)
vs[0] = "Charsi"
vs[1] = "Gheed"
vs[2] = "Akara"
vs[3] = "Fara"
vs[4] = "Lysander"
vs[5] = "Drognan"
vs[6] = "Hralti"
vs[7] = "Alkor"
vs[8] = "Ormus"
vs[9] = "Elzix"
vs[10] = "Asheara"
vs[11] = "Cain"
vs[12] = "Halbu"
vs[13] = "Jamella"
vs[14] = "Larzuk"
vs[15] = "Drehya"
vs[16] = "Malah"
return CreateItemVendorParams(r, inc, vs)
}
func CreateItemVendorParams(r *[]string, inc func() int, vs []string) map[string]*ItemVendorParams {
result := make(map[string]*ItemVendorParams)
for _, name := range vs {
wvp := ItemVendorParams{
Min: dh.StringToInt(dh.EmptyToZero((*r)[inc()])),
Max: dh.StringToInt(dh.EmptyToZero((*r)[inc()])),
MagicMin: dh.StringToInt(dh.EmptyToZero((*r)[inc()])),
MagicMax: dh.StringToInt(dh.EmptyToZero((*r)[inc()])),
MagicLevel: dh.StringToUint8(dh.EmptyToZero((*r)[inc()])),
}
result[name] = &wvp
}
return result
}
var Weapons map[string]*WeaponRecord
var Weapons map[string]*ItemCommonRecord
func LoadWeapons(fileProvider d2interface.FileProvider) {
Weapons = make(map[string]*WeaponRecord)
data := strings.Split(string(fileProvider.LoadFile(d2resource.Weapons)), "\r\n")[1:]
for _, line := range data {
if len(line) == 0 {
continue
}
rec := createWeaponRecord(line)
Weapons[rec.Code] = &rec
}
Weapons = *LoadCommonItems(fileProvider, d2resource.Weapons, d2enum.InventoryItemTypeWeapon)
log.Printf("Loaded %d weapons", len(Weapons))
}