OpenDiablo2/common/Weapons.go

281 lines
11 KiB
Go

package common
import (
"log"
"strings"
"github.com/OpenDiablo2/OpenDiablo2/resourcepaths"
)
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]*WeaponVendorParams // 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 WeaponVendorParams 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: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
CompactSave: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))) == 1,
Rarity: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
Spawnable: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))) == 1,
MinDamage: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
MaxDamage: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
BarbOneOrTwoHanded: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))) == 1,
UsesTwoHands: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))) == 1,
Min2HandDamage: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
Max2HandDamage: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
MinMissileDamage: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
MaxMissileDamage: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
MissileSpeed: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
ExtraRange: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
Speed: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
StrengthBonus: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
DexterityBonus: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
RequiredStrength: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
RequiredDexterity: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
Durability: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
NoDurability: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))) == 1,
Level: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
RequiredLevel: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
Cost: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
GambleCost: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
MagicLevel: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
AutoPrefix: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
OpenBetaGfx: r[inc()],
NormalCode: r[inc()],
UberCode: r[inc()],
UltraCode: r[inc()],
WeaponClass: r[inc()],
WeaponClass2Hand: r[inc()],
Component: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
HitClass: r[inc()],
InventoryWidth: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
InventoryHeight: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
Stackable: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))) == 1,
MinStack: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
MaxStack: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
SpawnStack: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
FlippyFile: r[inc()],
InventoryFile: r[inc()],
UniqueInventoryFile: r[inc()],
SetInventoryFile: r[inc()],
HasInventory: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))) == 1,
GemSockets: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
GemApplyType: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
SpecialFeature: r[inc()],
Useable: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))) == 1,
DropSound: r[inc()],
DropSfxFrame: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
UseSound: r[inc()],
Unique: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))) == 1,
Transparent: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))) == 1,
TransTable: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
Quivered: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))) == 1,
LightRadius: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
Belt: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))) == 1,
Quest: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
QuestDifficultyCheck: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))) == 1,
MissileType: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
DurabilityWarning: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
QuantityWarning: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
GemOffset: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
BitField1: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
Vendors: createWeaponVendorParams(&r, inc),
SourceArt: r[inc()],
GameArt: r[inc()],
ColorTransform: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
InventoryColorTransform: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))),
SkipName: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))) == 1,
NightmareUpgrade: r[inc()],
HellUpgrade: r[inc()],
Nameable: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))) == 1,
PermStoreItem: StringToInt(EmptyToZero(AsterToEmpty(r[inc()]))) == 1,
}
return result
}
func createWeaponVendorParams(r *[]string, inc func() int) map[string]*WeaponVendorParams {
result := make(map[string]*WeaponVendorParams)
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"
for _, name := range vs {
wvp := WeaponVendorParams{
Min: StringToInt(EmptyToZero((*r)[inc()])),
Max: StringToInt(EmptyToZero((*r)[inc()])),
MagicMin: StringToInt(EmptyToZero((*r)[inc()])),
MagicMax: StringToInt(EmptyToZero((*r)[inc()])),
MagicLevel: StringToUint8(EmptyToZero((*r)[inc()])),
}
result[name] = &wvp
}
return result
}
var Weapons map[string]*WeaponRecord
func LoadWeapons(fileProvider FileProvider) {
Weapons = make(map[string]*WeaponRecord)
data := strings.Split(string(fileProvider.LoadFile(resourcepaths.Weapons)), "\r\n")[1:]
for _, line := range data {
if len(line) == 0 {
continue
}
rec := createWeaponRecord(line)
Weapons[rec.Code] = &rec
}
log.Printf("Loaded %d weapons", len(Weapons))
}