NIP parser v2 (#17)

This commit is contained in:
Héctor Giménez
2024-05-05 12:15:08 +09:00
committed by GitHub
parent 0d5c108721
commit bb0ec2555d
28 changed files with 56618 additions and 1385 deletions

54705
cmd/d2data/d2data/items.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -15,7 +15,7 @@ package skill
var Desc = map[ID]Description{
{{- range $key, $value := . }}
{{ $key }}: {Page: {{ $value.SkillPage }}, Row: {{ $value.SkillRow }}, Column: {{ $value.SkillColumn }}, ListRow: {{ $value.ListRow }}, IconCel: {{ $value.IconCel }} },
{{ $key }}: {Page: {{ $value.SkillPage }}, Row: {{ $value.SkillRow }}, Column: {{ $value.SkillColumn }}, ListRow: {{ $value.ListRow }}, IconCel: {{ $value.IconCel }}},
{{- end }}
}`
@@ -25,7 +25,7 @@ package skill
var Skills = map[ID]Skill{
{{- range $key, $value := . }}
{{ $key }}: {Name: "{{ $value.Name }}", ID: {{ $key }}, LeftSkill: {{ if eq $value.Leftskill 1 }}true{{ else }}false{{ end }}, RightSkill: {{ if eq $value.Rightskill 1 }}true{{ else }}false{{ end }} },
{{ $key }}: {Name: "{{ $value.Name }}", ID: {{ $key }}, LeftSkill: {{ if eq $value.Leftskill 1 }}true{{ else }}false{{ end }}, RightSkill: {{ if eq $value.Rightskill 1 }}true{{ else }}false{{ end }}},
{{- end }}
}`
@@ -39,6 +39,16 @@ var Areas = map[ID]Area{
{{- end }}
}`
const templateItems = `// Code generated by d2data. DO NOT EDIT.
// source: cmd/d2data/d2data/items.json
package item
var Desc = map[int]Description{
{{- range $key, $value := . }}
{{ $value.ID }}: {Name: "{{ $value.Name }}", ID: {{ $value.ID }}, Code: "{{ $value.Code }}", NormalCode: "{{ $value.NormalCode }}", UberCode: "{{ $value.UberCode }}", UltraCode: "{{ $value.UltraCode }}", InventoryWidth: {{ $value.Width }}, InventoryHeight: {{ $value.Height }}, MinDefense: {{ $value.MinDefense }}, MaxDefense: {{ $value.MaxDefense }}},
{{- end }}
}`
type skillDesc struct {
Skilldesc string `json:"skilldesc"`
SkillPage int `json:"SkillPage"`
@@ -64,6 +74,19 @@ type Level struct {
CanBeTerrorized bool
}
type itemDesc struct {
Name string `json:"name"`
Code string `json:"code"`
NormalCode string `json:"normcode"`
UberCode string `json:"ubercode"`
UltraCode string `json:"ultracode"`
Width int `json:"invwidth"`
Height int `json:"invheight"`
ID int `json:"classid"`
MinDefense int `json:"minac"`
MaxDefense int `json:"maxac"`
}
// Generate static code from https://github.com/blizzhackers/d2data data files.
func main() {
err := generateFromFile(templateSkillDesc, "cmd/d2data/d2data/skilldesc.json", "pkg/data/skill/skilldesc.go", &map[string]skillDesc{})
@@ -80,6 +103,11 @@ func main() {
if err != nil {
log.Fatal(err)
}
err = generateFromFile(templateItems, "cmd/d2data/d2data/items.json", "pkg/data/item/items.go", &map[string]itemDesc{})
if err != nil {
log.Fatal(err)
}
}
func generateFromFile(templateName, filePath, outPath string, unmarshallStruct interface{}) error {

View File

@@ -12,7 +12,6 @@ import (
"github.com/hectorgimenez/d2go/pkg/data"
"github.com/hectorgimenez/d2go/pkg/data/area"
"github.com/hectorgimenez/d2go/pkg/data/item"
"github.com/hectorgimenez/d2go/pkg/itemfilter"
"github.com/hectorgimenez/d2go/pkg/memory"
"github.com/hectorgimenez/d2go/pkg/nip"
)
@@ -55,9 +54,15 @@ func (w *Watcher) Start(ctx context.Context) error {
d := w.gr.GetData()
for _, i := range d.Items.ByLocation(item.LocationGround) {
_, match := itemfilter.Evaluate(i, w.rules)
if !match {
continue
for _, r := range w.rules {
match, err := r.Evaluate(i)
if err != nil {
log.Printf("error evaluating rule: %v", err)
continue
}
if !match {
continue
}
}
found := false

1
go.mod
View File

@@ -3,6 +3,7 @@ module github.com/hectorgimenez/d2go
go 1.22
require (
github.com/expr-lang/expr v1.16.5
github.com/faiface/beep v1.1.0
github.com/stretchr/testify v1.9.0
golang.org/x/sys v0.18.0

2
go.sum
View File

@@ -2,6 +2,8 @@ github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q
github.com/d4l3k/messagediff v1.2.2-0.20190829033028-7e0a312ae40b/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/expr-lang/expr v1.16.5 h1:m2hvtguFeVaVNTHj8L7BoAyt7O0PAIBaSVbjdHgRXMs=
github.com/expr-lang/expr v1.16.5/go.mod h1:uCkhfG+x7fcZ5A5sXHKuQ07jGZRl6J0FCAaf2k4PtVQ=
github.com/faiface/beep v1.1.0 h1:A2gWP6xf5Rh7RG/p9/VAW2jRSDEGQm5sbOb38sf5d4c=
github.com/faiface/beep v1.1.0/go.mod h1:6I8p6kK2q4opL/eWb+kAkk38ehnTunWeToJB+s51sT4=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=

26
pkg/data/item/item.go Normal file
View File

@@ -0,0 +1,26 @@
package item
type Description struct {
ID int
Name string
Code string
NormalCode string // Normal
UberCode string // Exceptional
UltraCode string // Elite
InventoryWidth int
InventoryHeight int
MinDefense int
MaxDefense int
}
func (d Description) Tier() Tier {
if d.Code == d.UltraCode {
return TierElite
}
if d.Code == d.UberCode {
return TierExceptional
}
return TierNormal
}

665
pkg/data/item/items.go Normal file
View File

@@ -0,0 +1,665 @@
// Code generated by d2data. DO NOT EDIT.
// source: cmd/d2data/d2data/items.json
package item
var Desc = map[int]Description{
645: {Name: "Scroll of Knowledge", ID: 645, Code: "0sc", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
2: {Name: "Double Axe", ID: 2, Code: "2ax", NormalCode: "2ax", UberCode: "92a", UltraCode: "72a", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
33: {Name: "Two-Handed Sword", ID: 33, Code: "2hs", NormalCode: "2hs", UberCode: "92h", UltraCode: "72h", InventoryWidth: 1, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
262: {Name: "Shillelagh", ID: 262, Code: "6bs", NormalCode: "bst", UberCode: "8bs", UltraCode: "6bs", InventoryWidth: 1, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
267: {Name: "Great Bow", ID: 267, Code: "6cb", NormalCode: "cbw", UberCode: "8cb", UltraCode: "6cb", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
261: {Name: "Elder Staff", ID: 261, Code: "6cs", NormalCode: "cst", UberCode: "8cs", UltraCode: "6cs", InventoryWidth: 1, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
265: {Name: "Blade Bow", ID: 265, Code: "6hb", NormalCode: "hbw", UberCode: "8hb", UltraCode: "6hb", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
274: {Name: "Colossus Crossbow", ID: 274, Code: "6hx", NormalCode: "hxb", UberCode: "8hx", UltraCode: "6hx", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
269: {Name: "Crusader Bow", ID: 269, Code: "6l7", NormalCode: "lbb", UberCode: "8l8", UltraCode: "6l7", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
266: {Name: "Shadow Bow", ID: 266, Code: "6lb", NormalCode: "lbw", UberCode: "8lb", UltraCode: "6lb", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
260: {Name: "Stalagmite", ID: 260, Code: "6ls", NormalCode: "lst", UberCode: "8ls", UltraCode: "6ls", InventoryWidth: 1, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
271: {Name: "Hydra Bow", ID: 271, Code: "6lw", NormalCode: "lwb", UberCode: "8lw", UltraCode: "6lw", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
272: {Name: "Pellet Bow", ID: 272, Code: "6lx", NormalCode: "lxb", UberCode: "8lx", UltraCode: "6lx", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
273: {Name: "Gorgon Crossbow", ID: 273, Code: "6mx", NormalCode: "mxb", UberCode: "8mx", UltraCode: "6mx", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
275: {Name: "Demon Crossbow", ID: 275, Code: "6rx", NormalCode: "rxb", UberCode: "8rx", UltraCode: "6rx", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
268: {Name: "Diamond Bow", ID: 268, Code: "6s7", NormalCode: "sbb", UberCode: "8s8", UltraCode: "6s7", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
264: {Name: "Spider Bow", ID: 264, Code: "6sb", NormalCode: "sbw", UberCode: "8sb", UltraCode: "6sb", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
259: {Name: "Walking Stick", ID: 259, Code: "6ss", NormalCode: "sst", UberCode: "8ss", UltraCode: "6ss", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
270: {Name: "Ward Bow", ID: 270, Code: "6sw", NormalCode: "swb", UberCode: "8sw", UltraCode: "6sw", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
263: {Name: "Archon Staff", ID: 263, Code: "6ws", NormalCode: "wst", UberCode: "8ws", UltraCode: "6ws", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
198: {Name: "Ettin Axe", ID: 198, Code: "72a", NormalCode: "2ax", UberCode: "92a", UltraCode: "72a", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
229: {Name: "Legend Sword", ID: 229, Code: "72h", NormalCode: "2hs", UberCode: "92h", UltraCode: "72h", InventoryWidth: 1, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
189: {Name: "Suwayyah", ID: 189, Code: "7ar", NormalCode: "ktr", UberCode: "9ar", UltraCode: "7ar", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
197: {Name: "Small Crescent", ID: 197, Code: "7ax", NormalCode: "axe", UberCode: "9ax", UltraCode: "7ax", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
232: {Name: "Champion Sword", ID: 232, Code: "7b7", NormalCode: "bsw", UberCode: "9b9", UltraCode: "7b7", InventoryWidth: 1, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
242: {Name: "Winged Axe", ID: 242, Code: "7b8", NormalCode: "bal", UberCode: "9b8", UltraCode: "7b8", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
202: {Name: "Silver-edged Axe", ID: 202, Code: "7ba", NormalCode: "bax", UberCode: "9ba", UltraCode: "7ba", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
241: {Name: "Winged Knife", ID: 241, Code: "7bk", NormalCode: "bkf", UberCode: "9bk", UltraCode: "7bk", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
238: {Name: "Legend Spike", ID: 238, Code: "7bl", NormalCode: "bld", UberCode: "9bl", UltraCode: "7bl", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
250: {Name: "Mancatcher", ID: 250, Code: "7br", NormalCode: "brn", UberCode: "9br", UltraCode: "7br", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
226: {Name: "Conquest Sword", ID: 226, Code: "7bs", NormalCode: "bsd", UberCode: "9bs", UltraCode: "7bs", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
203: {Name: "Decapitator", ID: 203, Code: "7bt", NormalCode: "btx", UberCode: "9bt", UltraCode: "7bt", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
208: {Name: "Lich Wand", ID: 208, Code: "7bw", NormalCode: "bwn", UberCode: "9bw", UltraCode: "7bw", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
210: {Name: "Truncheon", ID: 210, Code: "7cl", NormalCode: "clb", UberCode: "9cl", UltraCode: "7cl", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
230: {Name: "Highland Blade", ID: 230, Code: "7cm", NormalCode: "clm", UberCode: "9cm", UltraCode: "7cm", InventoryWidth: 1, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
225: {Name: "Phase Blade", ID: 225, Code: "7cr", NormalCode: "crs", UberCode: "9cr", UltraCode: "7cr", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
192: {Name: "Battle Cestus", ID: 192, Code: "7cs", NormalCode: "ces", UberCode: "9cs", UltraCode: "7cs", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
235: {Name: "Bone Knife", ID: 235, Code: "7dg", NormalCode: "dgr", UberCode: "9dg", UltraCode: "7dg", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
236: {Name: "Mithral Point", ID: 236, Code: "7di", NormalCode: "dir", UberCode: "9di", UltraCode: "7di", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
233: {Name: "Colossal Sword", ID: 233, Code: "7fb", NormalCode: "flb", UberCode: "9fb", UltraCode: "7fb", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
224: {Name: "Hydra Edge", ID: 224, Code: "7fc", NormalCode: "flc", UberCode: "9fc", UltraCode: "7fc", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
217: {Name: "Scourge", ID: 217, Code: "7fl", NormalCode: "fla", UberCode: "9fl", UltraCode: "7fl", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
204: {Name: "Champion Axe", ID: 204, Code: "7ga", NormalCode: "gax", UberCode: "9ga", UltraCode: "7ga", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
234: {Name: "Colossus Blade", ID: 234, Code: "7gd", NormalCode: "gsd", UberCode: "9gd", UltraCode: "7gd", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
205: {Name: "Glorious Axe", ID: 205, Code: "7gi", NormalCode: "gix", UberCode: "9gi", UltraCode: "7gi", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
246: {Name: "Ghost Glaive", ID: 246, Code: "7gl", NormalCode: "glv", UberCode: "9gl", UltraCode: "7gl", InventoryWidth: 1, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
220: {Name: "Thunder Maul", ID: 220, Code: "7gm", NormalCode: "gma", UberCode: "9gm", UltraCode: "7gm", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
231: {Name: "Balrog Blade", ID: 231, Code: "7gs", NormalCode: "gis", UberCode: "9gs", UltraCode: "7gs", InventoryWidth: 1, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
209: {Name: "Unearthed Wand", ID: 209, Code: "7gw", NormalCode: "gwn", UberCode: "9gw", UltraCode: "7gw", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
257: {Name: "Great Poleaxe", ID: 257, Code: "7h7", NormalCode: "hal", UberCode: "9h9", UltraCode: "7h7", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
196: {Name: "Tomahawk", ID: 196, Code: "7ha", NormalCode: "hax", UberCode: "9ha", UltraCode: "7ha", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
243: {Name: "Hyperion Javelin", ID: 243, Code: "7ja", NormalCode: "jav", UberCode: "9ja", UltraCode: "7ja", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
237: {Name: "Fanged Knife", ID: 237, Code: "7kr", NormalCode: "kri", UberCode: "9kr", UltraCode: "7kr", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
201: {Name: "Feral Axe", ID: 201, Code: "7la", NormalCode: "lax", UberCode: "9la", UltraCode: "7la", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
227: {Name: "Cryptic Sword", ID: 227, Code: "7ls", NormalCode: "lsd", UberCode: "9ls", UltraCode: "7ls", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
193: {Name: "Feral Claws", ID: 193, Code: "7lw", NormalCode: "clw", UberCode: "9lw", UltraCode: "7lw", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
219: {Name: "Ogre Maul", ID: 219, Code: "7m7", NormalCode: "mau", UberCode: "9m9", UltraCode: "7m7", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
215: {Name: "Reinforced Mace", ID: 215, Code: "7ma", NormalCode: "mac", UberCode: "9ma", UltraCode: "7ma", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
199: {Name: "War Spike", ID: 199, Code: "7mp", NormalCode: "mpi", UberCode: "9mp", UltraCode: "7mp", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
216: {Name: "Devil Star", ID: 216, Code: "7mt", NormalCode: "mst", UberCode: "9mt", UltraCode: "7mt", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
253: {Name: "Ogre Axe", ID: 253, Code: "7o7", NormalCode: "bar", UberCode: "9b7", UltraCode: "7o7", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
252: {Name: "War Pike", ID: 252, Code: "7p7", NormalCode: "pik", UberCode: "9p9", UltraCode: "7p7", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
256: {Name: "Cryptic Axe", ID: 256, Code: "7pa", NormalCode: "pax", UberCode: "9pa", UltraCode: "7pa", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
244: {Name: "Stygian Pilum", ID: 244, Code: "7pi", NormalCode: "pil", UberCode: "9pi", UltraCode: "7pi", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
195: {Name: "Scissors Suwayyah", ID: 195, Code: "7qr", NormalCode: "skr", UberCode: "9qr", UltraCode: "7qr", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
212: {Name: "Seraph Rod", ID: 212, Code: "7qs", NormalCode: "gsc", UberCode: "9qs", UltraCode: "7qs", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
245: {Name: "Balrog Spear", ID: 245, Code: "7s7", NormalCode: "ssp", UberCode: "9s9", UltraCode: "7s7", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
255: {Name: "Thresher", ID: 255, Code: "7s8", NormalCode: "scy", UberCode: "9s8", UltraCode: "7s8", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
223: {Name: "Elegant Blade", ID: 223, Code: "7sb", NormalCode: "sbr", UberCode: "9sb", UltraCode: "7sb", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
211: {Name: "Mighty Scepter", ID: 211, Code: "7sc", NormalCode: "scp", UberCode: "9sc", UltraCode: "7sc", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
222: {Name: "Ataghan", ID: 222, Code: "7sm", NormalCode: "scm", UberCode: "9sm", UltraCode: "7sm", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
214: {Name: "Tyrant Club", ID: 214, Code: "7sp", NormalCode: "spc", UberCode: "9sp", UltraCode: "7sp", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
248: {Name: "Hyperion Spear", ID: 248, Code: "7sr", NormalCode: "spr", UberCode: "9sr", UltraCode: "7sr", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
221: {Name: "Falcata", ID: 221, Code: "7ss", NormalCode: "ssd", UberCode: "9ss", UltraCode: "7ss", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
251: {Name: "Ghost Spear", ID: 251, Code: "7st", NormalCode: "spt", UberCode: "9st", UltraCode: "7st", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
240: {Name: "Flying Axe", ID: 240, Code: "7ta", NormalCode: "tax", UberCode: "9ta", UltraCode: "7ta", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
239: {Name: "Flying Knife", ID: 239, Code: "7tk", NormalCode: "tkf", UberCode: "9tk", UltraCode: "7tk", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
249: {Name: "Stygian Pike", ID: 249, Code: "7tr", NormalCode: "tri", UberCode: "9tr", UltraCode: "7tr", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
247: {Name: "Winged Harpoon", ID: 247, Code: "7ts", NormalCode: "tsp", UberCode: "9ts", UltraCode: "7ts", InventoryWidth: 1, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
194: {Name: "Runic Talons", ID: 194, Code: "7tw", NormalCode: "btl", UberCode: "9tw", UltraCode: "7tw", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
254: {Name: "Colossus Voulge", ID: 254, Code: "7vo", NormalCode: "vou", UberCode: "9vo", UltraCode: "7vo", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
200: {Name: "Berserker Axe", ID: 200, Code: "7wa", NormalCode: "wax", UberCode: "9wa", UltraCode: "7wa", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
190: {Name: "Wrist Sword", ID: 190, Code: "7wb", NormalCode: "wrb", UberCode: "9wb", UltraCode: "7wb", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
258: {Name: "Giant Thresher", ID: 258, Code: "7wc", NormalCode: "wsc", UberCode: "9wc", UltraCode: "7wc", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
228: {Name: "Mythical Sword", ID: 228, Code: "7wd", NormalCode: "wsd", UberCode: "9wd", UltraCode: "7wd", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
218: {Name: "Legendary Mallet", ID: 218, Code: "7wh", NormalCode: "whm", UberCode: "9wh", UltraCode: "7wh", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
206: {Name: "Polished Wand", ID: 206, Code: "7wn", NormalCode: "wnd", UberCode: "9wn", UltraCode: "7wn", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
213: {Name: "Caduceus", ID: 213, Code: "7ws", NormalCode: "wsp", UberCode: "9ws", UltraCode: "7ws", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
191: {Name: "War Fist", ID: 191, Code: "7xf", NormalCode: "axf", UberCode: "9xf", UltraCode: "7xf", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
207: {Name: "Ghost Wand", ID: 207, Code: "7yw", NormalCode: "ywn", UberCode: "9yw", UltraCode: "7yw", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
159: {Name: "Gothic Staff", ID: 159, Code: "8bs", NormalCode: "bst", UberCode: "8bs", UltraCode: "6bs", InventoryWidth: 1, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
164: {Name: "Double Bow", ID: 164, Code: "8cb", NormalCode: "cbw", UberCode: "8cb", UltraCode: "6cb", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
158: {Name: "Cedar Staff", ID: 158, Code: "8cs", NormalCode: "cst", UberCode: "8cs", UltraCode: "6cs", InventoryWidth: 1, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
162: {Name: "Razor Bow", ID: 162, Code: "8hb", NormalCode: "hbw", UberCode: "8hb", UltraCode: "6hb", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
171: {Name: "Ballista", ID: 171, Code: "8hx", NormalCode: "hxb", UberCode: "8hx", UltraCode: "6hx", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
166: {Name: "Long Siege Bow", ID: 166, Code: "8l8", NormalCode: "lbb", UberCode: "8l8", UltraCode: "6l7", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
163: {Name: "Cedar Bow", ID: 163, Code: "8lb", NormalCode: "lbw", UberCode: "8lb", UltraCode: "6lb", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
157: {Name: "Quarterstaff", ID: 157, Code: "8ls", NormalCode: "lst", UberCode: "8ls", UltraCode: "6ls", InventoryWidth: 1, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
168: {Name: "Gothic Bow", ID: 168, Code: "8lw", NormalCode: "lwb", UberCode: "8lw", UltraCode: "6lw", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
169: {Name: "Arbalest", ID: 169, Code: "8lx", NormalCode: "lxb", UberCode: "8lx", UltraCode: "6lx", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
170: {Name: "Siege Crossbow", ID: 170, Code: "8mx", NormalCode: "mxb", UberCode: "8mx", UltraCode: "6mx", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
172: {Name: "Chu-Ko-Nu", ID: 172, Code: "8rx", NormalCode: "rxb", UberCode: "8rx", UltraCode: "6rx", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
165: {Name: "Short Siege Bow", ID: 165, Code: "8s8", NormalCode: "sbb", UberCode: "8s8", UltraCode: "6s7", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
161: {Name: "Edge Bow", ID: 161, Code: "8sb", NormalCode: "sbw", UberCode: "8sb", UltraCode: "6sb", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
156: {Name: "Jo Staff", ID: 156, Code: "8ss", NormalCode: "sst", UberCode: "8ss", UltraCode: "6ss", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
167: {Name: "Rune Bow", ID: 167, Code: "8sw", NormalCode: "swb", UberCode: "8sw", UltraCode: "6sw", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
160: {Name: "Rune Staff", ID: 160, Code: "8ws", NormalCode: "wst", UberCode: "8ws", UltraCode: "6ws", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
95: {Name: "Twin Axe", ID: 95, Code: "92a", NormalCode: "2ax", UberCode: "92a", UltraCode: "72a", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
126: {Name: "Espandon", ID: 126, Code: "92h", NormalCode: "2hs", UberCode: "92h", UltraCode: "72h", InventoryWidth: 1, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
182: {Name: "Quhab", ID: 182, Code: "9ar", NormalCode: "ktr", UberCode: "9ar", UltraCode: "7ar", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
94: {Name: "Cleaver", ID: 94, Code: "9ax", NormalCode: "axe", UberCode: "9ax", UltraCode: "7ax", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
150: {Name: "Lochaber Axe", ID: 150, Code: "9b7", NormalCode: "bar", UberCode: "9b7", UltraCode: "7o7", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
139: {Name: "Hurlbat", ID: 139, Code: "9b8", NormalCode: "bal", UberCode: "9b8", UltraCode: "7b8", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
129: {Name: "Gothic Sword", ID: 129, Code: "9b9", NormalCode: "bsw", UberCode: "9b9", UltraCode: "7b7", InventoryWidth: 1, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
99: {Name: "Bearded Axe", ID: 99, Code: "9ba", NormalCode: "bax", UberCode: "9ba", UltraCode: "7ba", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
138: {Name: "War Dart", ID: 138, Code: "9bk", NormalCode: "bkf", UberCode: "9bk", UltraCode: "7bk", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
135: {Name: "Stilleto", ID: 135, Code: "9bl", NormalCode: "bld", UberCode: "9bl", UltraCode: "7bl", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
147: {Name: "War Fork", ID: 147, Code: "9br", NormalCode: "brn", UberCode: "9br", UltraCode: "7br", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
123: {Name: "Battle Sword", ID: 123, Code: "9bs", NormalCode: "bsd", UberCode: "9bs", UltraCode: "7bs", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
100: {Name: "Tabar", ID: 100, Code: "9bt", NormalCode: "btx", UberCode: "9bt", UltraCode: "7bt", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
105: {Name: "Tomb Wand", ID: 105, Code: "9bw", NormalCode: "bwn", UberCode: "9bw", UltraCode: "7bw", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
107: {Name: "Cudgel", ID: 107, Code: "9cl", NormalCode: "clb", UberCode: "9cl", UltraCode: "7cl", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
127: {Name: "Dacian Falx", ID: 127, Code: "9cm", NormalCode: "clm", UberCode: "9cm", UltraCode: "7cm", InventoryWidth: 1, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
122: {Name: "Dimensional Blade", ID: 122, Code: "9cr", NormalCode: "crs", UberCode: "9cr", UltraCode: "7cr", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
185: {Name: "Hand Scythe", ID: 185, Code: "9cs", NormalCode: "ces", UberCode: "9cs", UltraCode: "7cs", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
132: {Name: "Poignard", ID: 132, Code: "9dg", NormalCode: "dgr", UberCode: "9dg", UltraCode: "7dg", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
133: {Name: "Rondel", ID: 133, Code: "9di", NormalCode: "dir", UberCode: "9di", UltraCode: "7di", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
130: {Name: "Zweihander", ID: 130, Code: "9fb", NormalCode: "flb", UberCode: "9fb", UltraCode: "7fb", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
121: {Name: "Tulwar", ID: 121, Code: "9fc", NormalCode: "flc", UberCode: "9fc", UltraCode: "7fc", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
114: {Name: "Knout", ID: 114, Code: "9fl", NormalCode: "fla", UberCode: "9fl", UltraCode: "7fl", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
101: {Name: "Gothic Axe", ID: 101, Code: "9ga", NormalCode: "gax", UberCode: "9ga", UltraCode: "7ga", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
131: {Name: "Executioner Sword", ID: 131, Code: "9gd", NormalCode: "gsd", UberCode: "9gd", UltraCode: "7gd", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
102: {Name: "Ancient Axe", ID: 102, Code: "9gi", NormalCode: "gix", UberCode: "9gi", UltraCode: "7gi", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
143: {Name: "Spiculum", ID: 143, Code: "9gl", NormalCode: "glv", UberCode: "9gl", UltraCode: "7gl", InventoryWidth: 1, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
117: {Name: "Martel de Fer", ID: 117, Code: "9gm", NormalCode: "gma", UberCode: "9gm", UltraCode: "7gm", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
128: {Name: "Tusk Sword", ID: 128, Code: "9gs", NormalCode: "gis", UberCode: "9gs", UltraCode: "7gs", InventoryWidth: 1, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
106: {Name: "Grave Wand", ID: 106, Code: "9gw", NormalCode: "gwn", UberCode: "9gw", UltraCode: "7gw", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
154: {Name: "Bec-de-Corbin", ID: 154, Code: "9h9", NormalCode: "hal", UberCode: "9h9", UltraCode: "7h7", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
93: {Name: "Hatchet", ID: 93, Code: "9ha", NormalCode: "hax", UberCode: "9ha", UltraCode: "7ha", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
140: {Name: "War Javelin", ID: 140, Code: "9ja", NormalCode: "jav", UberCode: "9ja", UltraCode: "7ja", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
134: {Name: "Cinquedeas", ID: 134, Code: "9kr", NormalCode: "kri", UberCode: "9kr", UltraCode: "7kr", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
98: {Name: "Military Axe", ID: 98, Code: "9la", NormalCode: "lax", UberCode: "9la", UltraCode: "7la", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
124: {Name: "Rune Sword", ID: 124, Code: "9ls", NormalCode: "lsd", UberCode: "9ls", UltraCode: "7ls", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
186: {Name: "Greater Claws", ID: 186, Code: "9lw", NormalCode: "clw", UberCode: "9lw", UltraCode: "7lw", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
116: {Name: "War Club", ID: 116, Code: "9m9", NormalCode: "mau", UberCode: "9m9", UltraCode: "7m7", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
112: {Name: "Flanged Mace", ID: 112, Code: "9ma", NormalCode: "mac", UberCode: "9ma", UltraCode: "7ma", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
96: {Name: "Crowbill", ID: 96, Code: "9mp", NormalCode: "mpi", UberCode: "9mp", UltraCode: "7mp", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
113: {Name: "Jagged Star", ID: 113, Code: "9mt", NormalCode: "mst", UberCode: "9mt", UltraCode: "7mt", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
149: {Name: "Lance", ID: 149, Code: "9p9", NormalCode: "pik", UberCode: "9p9", UltraCode: "7p7", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
153: {Name: "Partizan", ID: 153, Code: "9pa", NormalCode: "pax", UberCode: "9pa", UltraCode: "7pa", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
141: {Name: "Great Pilum", ID: 141, Code: "9pi", NormalCode: "pil", UberCode: "9pi", UltraCode: "7pi", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
188: {Name: "Scissors Quhab", ID: 188, Code: "9qr", NormalCode: "skr", UberCode: "9qr", UltraCode: "7qr", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
109: {Name: "Holy Water Sprinkler", ID: 109, Code: "9qs", NormalCode: "gsc", UberCode: "9qs", UltraCode: "7qs", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
152: {Name: "Battle Scythe", ID: 152, Code: "9s8", NormalCode: "scy", UberCode: "9s8", UltraCode: "7s8", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
142: {Name: "Simbilan", ID: 142, Code: "9s9", NormalCode: "ssp", UberCode: "9s9", UltraCode: "7s7", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
120: {Name: "Shamshir", ID: 120, Code: "9sb", NormalCode: "sbr", UberCode: "9sb", UltraCode: "7sb", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
108: {Name: "Rune Scepter", ID: 108, Code: "9sc", NormalCode: "scp", UberCode: "9sc", UltraCode: "7sc", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
119: {Name: "Cutlass", ID: 119, Code: "9sm", NormalCode: "scm", UberCode: "9sm", UltraCode: "7sm", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
111: {Name: "Barbed Club", ID: 111, Code: "9sp", NormalCode: "spc", UberCode: "9sp", UltraCode: "7sp", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
145: {Name: "War Spear", ID: 145, Code: "9sr", NormalCode: "spr", UberCode: "9sr", UltraCode: "7sr", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
118: {Name: "Gladius", ID: 118, Code: "9ss", NormalCode: "ssd", UberCode: "9ss", UltraCode: "7ss", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
148: {Name: "Yari", ID: 148, Code: "9st", NormalCode: "spt", UberCode: "9st", UltraCode: "7st", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
137: {Name: "Francisca", ID: 137, Code: "9ta", NormalCode: "tax", UberCode: "9ta", UltraCode: "7ta", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
136: {Name: "Battle Dart", ID: 136, Code: "9tk", NormalCode: "tkf", UberCode: "9tk", UltraCode: "7tk", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
146: {Name: "Fuscina", ID: 146, Code: "9tr", NormalCode: "tri", UberCode: "9tr", UltraCode: "7tr", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
144: {Name: "Harpoon", ID: 144, Code: "9ts", NormalCode: "tsp", UberCode: "9ts", UltraCode: "7ts", InventoryWidth: 1, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
187: {Name: "Greater Talons", ID: 187, Code: "9tw", NormalCode: "btl", UberCode: "9tw", UltraCode: "7tw", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
151: {Name: "Bill", ID: 151, Code: "9vo", NormalCode: "vou", UberCode: "9vo", UltraCode: "7vo", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
97: {Name: "Naga", ID: 97, Code: "9wa", NormalCode: "wax", UberCode: "9wa", UltraCode: "7wa", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
183: {Name: "Wrist Spike", ID: 183, Code: "9wb", NormalCode: "wrb", UberCode: "9wb", UltraCode: "7wb", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
155: {Name: "Grim Scythe", ID: 155, Code: "9wc", NormalCode: "wsc", UberCode: "9wc", UltraCode: "7wc", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
125: {Name: "Ancient Sword", ID: 125, Code: "9wd", NormalCode: "wsd", UberCode: "9wd", UltraCode: "7wd", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
115: {Name: "Battle Hammer", ID: 115, Code: "9wh", NormalCode: "whm", UberCode: "9wh", UltraCode: "7wh", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
103: {Name: "Burnt Wand", ID: 103, Code: "9wn", NormalCode: "wnd", UberCode: "9wn", UltraCode: "7wn", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
110: {Name: "Divine Scepter", ID: 110, Code: "9ws", NormalCode: "wsp", UberCode: "9ws", UltraCode: "7ws", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
184: {Name: "Fascia", ID: 184, Code: "9xf", NormalCode: "axf", UberCode: "9xf", UltraCode: "7xf", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
104: {Name: "Petrified Wand", ID: 104, Code: "9yw", NormalCode: "ywn", UberCode: "9yw", UltraCode: "7yw", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
326: {Name: "Ancient Armor", ID: 326, Code: "aar", NormalCode: "aar", UberCode: "xar", UltraCode: "uar", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 218, MaxDefense: 233},
281: {Name: "Stag Bow", ID: 281, Code: "am1", NormalCode: "am1", UberCode: "am6", UltraCode: "amb", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
282: {Name: "Reflex Bow", ID: 282, Code: "am2", NormalCode: "am2", UberCode: "am7", UltraCode: "amc", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
283: {Name: "Maiden Spear", ID: 283, Code: "am3", NormalCode: "am3", UberCode: "am8", UltraCode: "amd", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
284: {Name: "Maiden Pike", ID: 284, Code: "am4", NormalCode: "am4", UberCode: "am9", UltraCode: "ame", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
285: {Name: "Maiden Javelin", ID: 285, Code: "am5", NormalCode: "am5", UberCode: "ama", UltraCode: "amf", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
291: {Name: "Ashwood Bow", ID: 291, Code: "am6", NormalCode: "am1", UberCode: "am6", UltraCode: "amb", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
292: {Name: "Ceremonial Bow", ID: 292, Code: "am7", NormalCode: "am2", UberCode: "am7", UltraCode: "amc", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
293: {Name: "Ceremonial Spear", ID: 293, Code: "am8", NormalCode: "am3", UberCode: "am8", UltraCode: "amd", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
294: {Name: "Ceremonial Pike", ID: 294, Code: "am9", NormalCode: "am4", UberCode: "am9", UltraCode: "ame", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
295: {Name: "Ceremonial Javelin", ID: 295, Code: "ama", NormalCode: "am5", UberCode: "ama", UltraCode: "amf", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
301: {Name: "Matriarchal Bow", ID: 301, Code: "amb", NormalCode: "am1", UberCode: "am6", UltraCode: "amb", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
302: {Name: "Grand Matron Bow", ID: 302, Code: "amc", NormalCode: "am2", UberCode: "am7", UltraCode: "amc", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
303: {Name: "Matriarchal Spear", ID: 303, Code: "amd", NormalCode: "am3", UberCode: "am8", UltraCode: "amd", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
304: {Name: "Matriarchal Pike", ID: 304, Code: "ame", NormalCode: "am4", UberCode: "am9", UltraCode: "ame", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
305: {Name: "Matriarchal Javelin", ID: 305, Code: "amf", NormalCode: "am5", UberCode: "ama", UltraCode: "amf", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
520: {Name: "Amulet", ID: 520, Code: "amu", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
526: {Name: "Arrows", ID: 526, Code: "aqv", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
552: {Name: "Book of Skill", ID: 552, Code: "ass", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
1: {Name: "Axe", ID: 1, Code: "axe", NormalCode: "axe", UberCode: "9ax", UltraCode: "7ax", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
177: {Name: "Hatchet Hands", ID: 177, Code: "axf", NormalCode: "axf", UberCode: "9xf", UltraCode: "7xf", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
403: {Name: "Jawbone Cap", ID: 403, Code: "ba1", NormalCode: "ba1", UberCode: "ba6", UltraCode: "bab", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 10, MaxDefense: 15},
404: {Name: "Fanged Helm", ID: 404, Code: "ba2", NormalCode: "ba2", UberCode: "ba7", UltraCode: "bac", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 15, MaxDefense: 20},
405: {Name: "Horned Helm", ID: 405, Code: "ba3", NormalCode: "ba3", UberCode: "ba8", UltraCode: "bad", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 25, MaxDefense: 30},
406: {Name: "Assault Helmet", ID: 406, Code: "ba4", NormalCode: "ba4", UberCode: "ba9", UltraCode: "bae", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 30, MaxDefense: 35},
407: {Name: "Avenger Guard", ID: 407, Code: "ba5", NormalCode: "ba5", UberCode: "baa", UltraCode: "baf", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 35, MaxDefense: 50},
473: {Name: "Jawbone Visor", ID: 473, Code: "ba6", NormalCode: "ba1", UberCode: "ba6", UltraCode: "bab", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 55, MaxDefense: 68},
474: {Name: "Lion Helm", ID: 474, Code: "ba7", NormalCode: "ba2", UberCode: "ba7", UltraCode: "bac", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 63, MaxDefense: 75},
475: {Name: "Rage Mask", ID: 475, Code: "ba8", NormalCode: "ba3", UberCode: "ba8", UltraCode: "bad", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 78, MaxDefense: 90},
476: {Name: "Savage Helmet", ID: 476, Code: "ba9", NormalCode: "ba4", UberCode: "ba9", UltraCode: "bae", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 85, MaxDefense: 98},
477: {Name: "Slayer Guard", ID: 477, Code: "baa", NormalCode: "ba5", UberCode: "baa", UltraCode: "baf", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 93, MaxDefense: 120},
493: {Name: "Carnage Helm", ID: 493, Code: "bab", NormalCode: "ba1", UberCode: "ba6", UltraCode: "bab", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 102, MaxDefense: 147},
494: {Name: "Fury Visor", ID: 494, Code: "bac", NormalCode: "ba2", UberCode: "ba7", UltraCode: "bac", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 105, MaxDefense: 150},
495: {Name: "Destroyer Helm", ID: 495, Code: "bad", NormalCode: "ba3", UberCode: "ba8", UltraCode: "bad", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 111, MaxDefense: 156},
496: {Name: "Conqueror Crown", ID: 496, Code: "bae", NormalCode: "ba4", UberCode: "ba9", UltraCode: "bae", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 114, MaxDefense: 159},
497: {Name: "Guardian Crown", ID: 497, Code: "baf", NormalCode: "ba5", UberCode: "baa", UltraCode: "baf", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 117, MaxDefense: 168},
46: {Name: "Balanced Axe", ID: 46, Code: "bal", NormalCode: "bal", UberCode: "9b8", UltraCode: "7b8", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
57: {Name: "Bardiche", ID: 57, Code: "bar", NormalCode: "bar", UberCode: "9b7", UltraCode: "7o7", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
6: {Name: "Broad Axe", ID: 6, Code: "bax", NormalCode: "bax", UberCode: "9ba", UltraCode: "7ba", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
548: {Name: "Lam Esen's Tome", ID: 548, Code: "bbb", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
656: {Name: "Burning Essence of Terror", ID: 656, Code: "bet", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
651: {Name: "Baal's Eye", ID: 651, Code: "bey", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
349: {Name: "Bone Helm", ID: 349, Code: "bhm", NormalCode: "bhm", UberCode: "xh9", UltraCode: "uh9", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 33, MaxDefense: 36},
525: {Name: "Key to the Cairn Stones", ID: 525, Code: "bkd", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
45: {Name: "Balanced Knife", ID: 45, Code: "bkf", NormalCode: "bkf", UberCode: "9bk", UltraCode: "7bk", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
524: {Name: "Scroll of Inifuss", ID: 524, Code: "bks", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
42: {Name: "Blade", ID: 42, Code: "bld", NormalCode: "bld", UberCode: "9bl", UltraCode: "7bl", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
549: {Name: "Horadric Cube", ID: 549, Code: "box", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
609: {Name: "Large Blue Potion", ID: 609, Code: "bpl", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
608: {Name: "Small Blue Potion", ID: 608, Code: "bps", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
54: {Name: "Brandistock", ID: 54, Code: "brn", NormalCode: "brn", UberCode: "9br", UltraCode: "7br", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
320: {Name: "Breast Plate", ID: 320, Code: "brs", NormalCode: "brs", UberCode: "xrs", UltraCode: "urs", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 65, MaxDefense: 68},
532: {Name: "Brain", ID: 532, Code: "brz", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
30: {Name: "Broad Sword", ID: 30, Code: "bsd", NormalCode: "bsd", UberCode: "9bs", UltraCode: "7bs", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
350: {Name: "Bone Shield", ID: 350, Code: "bsh", NormalCode: "bsh", UberCode: "xsh", UltraCode: "ush", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 10, MaxDefense: 30},
66: {Name: "Battle Staff", ID: 66, Code: "bst", NormalCode: "bst", UberCode: "8bs", UltraCode: "6bs", InventoryWidth: 1, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
36: {Name: "Bastard Sword", ID: 36, Code: "bsw", NormalCode: "bsw", UberCode: "9b9", UltraCode: "7b7", InventoryWidth: 1, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
180: {Name: "Blade Talons", ID: 180, Code: "btl", NormalCode: "btl", UberCode: "9tw", UltraCode: "7tw", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
7: {Name: "Battle Axe", ID: 7, Code: "btx", NormalCode: "btx", UberCode: "9bt", UltraCode: "7bt", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
328: {Name: "Buckler", ID: 328, Code: "buc", NormalCode: "buc", UberCode: "xuc", UltraCode: "uuc", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 4, MaxDefense: 6},
12: {Name: "Bone Wand", ID: 12, Code: "bwn", NormalCode: "bwn", UberCode: "9bw", UltraCode: "7bw", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
306: {Name: "Cap", ID: 306, Code: "cap", NormalCode: "cap", UberCode: "xap", UltraCode: "uap", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 3, MaxDefense: 5},
71: {Name: "Composite Bow", ID: 71, Code: "cbw", NormalCode: "cbw", UberCode: "8cb", UltraCode: "6cb", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
655: {Name: "Charged Essense of Hatred", ID: 655, Code: "ceh", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
178: {Name: "Cestus", ID: 178, Code: "ces", NormalCode: "ces", UberCode: "9cs", UltraCode: "7cs", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
319: {Name: "Chain Mail", ID: 319, Code: "chn", NormalCode: "chn", UberCode: "xhn", UltraCode: "uhn", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 72, MaxDefense: 75},
418: {Name: "Circlet", ID: 418, Code: "ci0", NormalCode: "ci0", UberCode: "ci2", UltraCode: "ci3", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 20, MaxDefense: 30},
419: {Name: "Coronet", ID: 419, Code: "ci1", NormalCode: "ci1", UberCode: "ci2", UltraCode: "ci3", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 30, MaxDefense: 40},
420: {Name: "Tiara", ID: 420, Code: "ci2", NormalCode: "ci1", UberCode: "ci2", UltraCode: "ci3", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 40, MaxDefense: 50},
421: {Name: "Diadem", ID: 421, Code: "ci3", NormalCode: "ci1", UberCode: "ci2", UltraCode: "ci3", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 50, MaxDefense: 60},
14: {Name: "Club", ID: 14, Code: "clb", NormalCode: "clb", UberCode: "9cl", UltraCode: "7cl", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
34: {Name: "Claymore", ID: 34, Code: "clm", NormalCode: "clm", UberCode: "9cm", UltraCode: "7cm", InventoryWidth: 1, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
179: {Name: "Claws", ID: 179, Code: "clw", NormalCode: "clw", UberCode: "9lw", UltraCode: "7lw", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
603: {Name: "Small Charm", ID: 603, Code: "cm1", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
604: {Name: "Large Charm", ID: 604, Code: "cm2", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
605: {Name: "Grand Charm", ID: 605, Code: "cm3", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
528: {Name: "Bolts", ID: 528, Code: "cqv", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
311: {Name: "Crown", ID: 311, Code: "crn", NormalCode: "crn", UberCode: "xrn", UltraCode: "urn", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 25, MaxDefense: 45},
29: {Name: "Crystal Sword", ID: 29, Code: "crs", NormalCode: "crs", UberCode: "9cr", UltraCode: "7cr", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
65: {Name: "Gnarled Staff", ID: 65, Code: "cst", NormalCode: "cst", UberCode: "8cs", UltraCode: "6cs", InventoryWidth: 1, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
86: {Name: "Decoy Gidbinn", ID: 86, Code: "d33", NormalCode: "d33", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
39: {Name: "Dagger", ID: 39, Code: "dgr", NormalCode: "dgr", UberCode: "9dg", UltraCode: "7dg", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
650: {Name: "Diablo's Horn", ID: 650, Code: "dhn", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
40: {Name: "Dirk", ID: 40, Code: "dir", NormalCode: "dir", UberCode: "9di", UltraCode: "7di", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
398: {Name: "Wolf Head", ID: 398, Code: "dr1", NormalCode: "dr1", UberCode: "dr6", UltraCode: "drb", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 8, MaxDefense: 11},
399: {Name: "Hawk Helm", ID: 399, Code: "dr2", NormalCode: "dr2", UberCode: "dr7", UltraCode: "drc", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 4, MaxDefense: 15},
400: {Name: "Antlers", ID: 400, Code: "dr3", NormalCode: "dr3", UberCode: "dr8", UltraCode: "drd", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 18, MaxDefense: 24},
401: {Name: "Falcon Mask", ID: 401, Code: "dr4", NormalCode: "dr4", UberCode: "dr9", UltraCode: "dre", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 12, MaxDefense: 28},
402: {Name: "Spirit Mask", ID: 402, Code: "dr5", NormalCode: "dr5", UberCode: "dra", UltraCode: "drf", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 22, MaxDefense: 35},
468: {Name: "Alpha Helm", ID: 468, Code: "dr6", NormalCode: "dr1", UberCode: "dr6", UltraCode: "drb", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 52, MaxDefense: 62},
469: {Name: "Griffon Headress", ID: 469, Code: "dr7", NormalCode: "dr2", UberCode: "dr7", UltraCode: "drc", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 46, MaxDefense: 68},
470: {Name: "Hunter's Guise", ID: 470, Code: "dr8", NormalCode: "dr3", UberCode: "dr8", UltraCode: "drd", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 67, MaxDefense: 81},
471: {Name: "Sacred Feathers", ID: 471, Code: "dr9", NormalCode: "dr4", UberCode: "dr9", UltraCode: "dre", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 58, MaxDefense: 87},
472: {Name: "Totemic Mask", ID: 472, Code: "dra", NormalCode: "dr5", UberCode: "dra", UltraCode: "drf", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 73, MaxDefense: 98},
488: {Name: "Blood Spirit", ID: 488, Code: "drb", NormalCode: "dr1", UberCode: "dr6", UltraCode: "drb", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 101, MaxDefense: 145},
489: {Name: "Sun Spirit", ID: 489, Code: "drc", NormalCode: "dr2", UberCode: "dr7", UltraCode: "drc", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 98, MaxDefense: 147},
490: {Name: "Earth Spirit", ID: 490, Code: "drd", NormalCode: "dr3", UberCode: "dr8", UltraCode: "drd", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 107, MaxDefense: 152},
491: {Name: "Sky Spirit", ID: 491, Code: "dre", NormalCode: "dr4", UberCode: "dr9", UltraCode: "dre", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 103, MaxDefense: 155},
492: {Name: "Dream Spirit", ID: 492, Code: "drf", NormalCode: "dr5", UberCode: "dra", UltraCode: "drf", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 109, MaxDefense: 159},
556: {Name: "Ear", ID: 556, Code: "ear", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
508: {Name: "Elixir", ID: 508, Code: "elx", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
534: {Name: "Eye", ID: 534, Code: "eyz", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
657: {Name: "Festering Essence of Destruction", ID: 657, Code: "fed", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
309: {Name: "Full Helm", ID: 309, Code: "fhl", NormalCode: "fhl", UberCode: "xhl", UltraCode: "uhl", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 23, MaxDefense: 26},
21: {Name: "Flail", ID: 21, Code: "fla", NormalCode: "fla", UberCode: "9fl", UltraCode: "7fl", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
37: {Name: "Flamberge", ID: 37, Code: "flb", NormalCode: "flb", UberCode: "9fb", UltraCode: "7fb", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
28: {Name: "Falchion", ID: 28, Code: "flc", NormalCode: "flc", UberCode: "9fc", UltraCode: "7fc", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
323: {Name: "Field Plate", ID: 323, Code: "fld", NormalCode: "fld", UberCode: "xld", UltraCode: "uld", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 101, MaxDefense: 105},
537: {Name: "Flag", ID: 537, Code: "flg", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
538: {Name: "Fang", ID: 538, Code: "fng", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
325: {Name: "Full Plate Mail", ID: 325, Code: "ful", NormalCode: "ful", UberCode: "xul", UltraCode: "uul", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 150, MaxDefense: 161},
87: {Name: "The Gidbinn", ID: 87, Code: "g33", NormalCode: "g33", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
547: {Name: "The Golden Bird", ID: 547, Code: "g34", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
8: {Name: "Great Axe", ID: 8, Code: "gax", NormalCode: "gax", UberCode: "9ga", UltraCode: "7ga", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
567: {Name: "Chipped Sapphire", ID: 567, Code: "gcb", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
572: {Name: "Chipped Emerald", ID: 572, Code: "gcg", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
577: {Name: "Chipped Ruby", ID: 577, Code: "gcr", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
557: {Name: "Chipped Amethyst", ID: 557, Code: "gcv", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
582: {Name: "Chipped Diamond", ID: 582, Code: "gcw", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
562: {Name: "Chipped Topaz", ID: 562, Code: "gcy", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
568: {Name: "Flawed Sapphire", ID: 568, Code: "gfb", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
573: {Name: "Flawed Emerald", ID: 573, Code: "gfg", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
578: {Name: "Flawed Ruby", ID: 578, Code: "gfr", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
558: {Name: "Flawed Amethyst", ID: 558, Code: "gfv", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
583: {Name: "Flawed Diamond", ID: 583, Code: "gfw", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
563: {Name: "Flawed Topaz", ID: 563, Code: "gfy", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
310: {Name: "Great Helm", ID: 310, Code: "ghm", NormalCode: "ghm", UberCode: "xhm", UltraCode: "uhm", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 30, MaxDefense: 35},
35: {Name: "Giant Sword", ID: 35, Code: "gis", NormalCode: "gis", UberCode: "9gs", UltraCode: "7gs", InventoryWidth: 1, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
9: {Name: "Giant Axe", ID: 9, Code: "gix", NormalCode: "gix", UberCode: "9gi", UltraCode: "7gi", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
570: {Name: "Flawless Sapphire", ID: 570, Code: "glb", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
523: {Name: "Gold", ID: 523, Code: "gld", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
575: {Name: "Flawless Emerald", ID: 575, Code: "glg", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
580: {Name: "Flawless Ruby", ID: 580, Code: "glr", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
50: {Name: "Glaive", ID: 50, Code: "glv", NormalCode: "glv", UberCode: "9gl", UltraCode: "7gl", InventoryWidth: 1, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
585: {Name: "Flawless Diamond", ID: 585, Code: "glw", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
565: {Name: "Flawless Topaz", ID: 565, Code: "gly", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
24: {Name: "Great Maul", ID: 24, Code: "gma", NormalCode: "gma", UberCode: "9gm", UltraCode: "7gm", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
571: {Name: "Perfect Sapphire", ID: 571, Code: "gpb", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
576: {Name: "Perfect Emerald", ID: 576, Code: "gpg", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
84: {Name: "Strangling Gas Potion", ID: 84, Code: "gpl", NormalCode: "gpl", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
82: {Name: "Choking Gas Potion", ID: 82, Code: "gpm", NormalCode: "gpm", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
581: {Name: "Perfect Ruby", ID: 581, Code: "gpr", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
80: {Name: "Rancid Gas Potion", ID: 80, Code: "gps", NormalCode: "gps", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
561: {Name: "Perfect Amethyst", ID: 561, Code: "gpv", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
586: {Name: "Perfect Diamond", ID: 586, Code: "gpw", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
566: {Name: "Perfect Topaz", ID: 566, Code: "gpy", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
569: {Name: "Sapphire", ID: 569, Code: "gsb", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
16: {Name: "Grand Scepter", ID: 16, Code: "gsc", NormalCode: "gsc", UberCode: "9qs", UltraCode: "7qs", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
38: {Name: "Great Sword", ID: 38, Code: "gsd", NormalCode: "gsd", UberCode: "9gd", UltraCode: "7gd", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
574: {Name: "Emerald", ID: 574, Code: "gsg", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
579: {Name: "Ruby", ID: 579, Code: "gsr", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
559: {Name: "Amethyst", ID: 559, Code: "gsv", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
584: {Name: "Diamond", ID: 584, Code: "gsw", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
564: {Name: "Topaz", ID: 564, Code: "gsy", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
324: {Name: "Gothic Plate", ID: 324, Code: "gth", NormalCode: "gth", UberCode: "xth", UltraCode: "uth", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 128, MaxDefense: 135},
333: {Name: "Gothic Shield", ID: 333, Code: "gts", NormalCode: "gts", UberCode: "xts", UltraCode: "uts", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 30, MaxDefense: 35},
13: {Name: "Grim Wand", ID: 13, Code: "gwn", NormalCode: "gwn", UberCode: "9gw", UltraCode: "7gw", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
560: {Name: "Flawless Amethyst", ID: 560, Code: "gzv", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
61: {Name: "Halberd", ID: 61, Code: "hal", NormalCode: "hal", UberCode: "9h9", UltraCode: "7h7", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
0: {Name: "Hand Axe", ID: 0, Code: "hax", NormalCode: "hax", UberCode: "9ha", UltraCode: "7ha", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
348: {Name: "Plated Belt", ID: 348, Code: "hbl", NormalCode: "hbl", UberCode: "zhb", UltraCode: "uhc", InventoryWidth: 2, InventoryHeight: 1, MinDefense: 8, MaxDefense: 11},
343: {Name: "Greaves", ID: 343, Code: "hbt", NormalCode: "hbt", UberCode: "xhb", UltraCode: "uhb", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 12, MaxDefense: 15},
69: {Name: "Hunter's Bow", ID: 69, Code: "hbw", NormalCode: "hbw", UberCode: "8hb", UltraCode: "6hb", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
89: {Name: "Horadric Malus", ID: 89, Code: "hdm", NormalCode: "hdm", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
90: {Name: "Hellforge Hammer", ID: 90, Code: "hfh", NormalCode: "hfh", UberCode: "", UltraCode: "", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
338: {Name: "Gauntlets", ID: 338, Code: "hgl", NormalCode: "hgl", UberCode: "xhg", UltraCode: "uhg", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 12, MaxDefense: 15},
315: {Name: "Hard Leather Armor", ID: 315, Code: "hla", NormalCode: "hla", UberCode: "xla", UltraCode: "ula", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 21, MaxDefense: 24},
308: {Name: "Helm", ID: 308, Code: "hlm", NormalCode: "hlm", UberCode: "xlm", UltraCode: "ulm", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 15, MaxDefense: 18},
587: {Name: "Minor Healing Potion", ID: 587, Code: "hp1", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
588: {Name: "Light Healing Potion", ID: 588, Code: "hp2", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
589: {Name: "Healing Potion", ID: 589, Code: "hp3", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
590: {Name: "Greater Healing Potion", ID: 590, Code: "hp4", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
591: {Name: "Super Healing Potion", ID: 591, Code: "hp5", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
511: {Name: "Full Healing Potion", ID: 511, Code: "hpf", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
509: {Name: "Healing Potion", ID: 509, Code: "hpo", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
602: {Name: "Herb", ID: 602, Code: "hrb", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
535: {Name: "Horn", ID: 535, Code: "hrn", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
531: {Name: "Heart", ID: 531, Code: "hrt", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
91: {Name: "Horadric Staff", ID: 91, Code: "hst", NormalCode: "hst", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
78: {Name: "Heavy Crossbow", ID: 78, Code: "hxb", NormalCode: "hxb", UberCode: "8hx", UltraCode: "6hx", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
519: {Name: "Tome of Identify", ID: 519, Code: "ibk", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
644: {Name: "Malah's Potion", ID: 644, Code: "ice", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
530: {Name: "Scroll of Identify", ID: 530, Code: "isc", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
546: {Name: "A Jade Figurine", ID: 546, Code: "j34", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
47: {Name: "Javelin", ID: 47, Code: "jav", NormalCode: "jav", UberCode: "9ja", UltraCode: "7ja", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
533: {Name: "Jawbone", ID: 533, Code: "jaw", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
643: {Name: "Jewel", ID: 643, Code: "jew", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
543: {Name: "Key", ID: 543, Code: "key", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
331: {Name: "Kite Shield", ID: 331, Code: "kit", NormalCode: "kit", UberCode: "xit", UltraCode: "uit", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 16, MaxDefense: 18},
41: {Name: "Kriss", ID: 41, Code: "kri", NormalCode: "kri", UberCode: "9kr", UltraCode: "7kr", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
175: {Name: "Katar", ID: 175, Code: "ktr", NormalCode: "ktr", UberCode: "9ar", UltraCode: "7ar", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
5: {Name: "Large Axe", ID: 5, Code: "lax", NormalCode: "lax", UberCode: "9la", UltraCode: "7la", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
73: {Name: "Long Battle Bow", ID: 73, Code: "lbb", NormalCode: "lbb", UberCode: "8l8", UltraCode: "6l7", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
344: {Name: "Sash", ID: 344, Code: "lbl", NormalCode: "lbl", UberCode: "zlb", UltraCode: "ulc", InventoryWidth: 2, InventoryHeight: 1, MinDefense: 2, MaxDefense: 2},
339: {Name: "Boots", ID: 339, Code: "lbt", NormalCode: "lbt", UberCode: "xlb", UltraCode: "ulb", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 2, MaxDefense: 3},
70: {Name: "Long Bow", ID: 70, Code: "lbw", NormalCode: "lbw", UberCode: "8lb", UltraCode: "6lb", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
314: {Name: "Leather Armor", ID: 314, Code: "lea", NormalCode: "lea", UberCode: "xea", UltraCode: "uea", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 14, MaxDefense: 17},
88: {Name: "Wirt's Leg", ID: 88, Code: "leg", NormalCode: "leg", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
334: {Name: "Leather Gloves", ID: 334, Code: "lgl", NormalCode: "lgl", UberCode: "xlg", UltraCode: "ulg", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 2, MaxDefense: 3},
330: {Name: "Large Shield", ID: 330, Code: "lrg", NormalCode: "lrg", UberCode: "xrg", UltraCode: "urg", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 12, MaxDefense: 14},
31: {Name: "Long Sword", ID: 31, Code: "lsd", NormalCode: "lsd", UberCode: "9ls", UltraCode: "7ls", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
64: {Name: "Long Staff", ID: 64, Code: "lst", NormalCode: "lst", UberCode: "8ls", UltraCode: "6ls", InventoryWidth: 1, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
327: {Name: "Light Plate", ID: 327, Code: "ltp", NormalCode: "ltp", UberCode: "xtp", UltraCode: "utp", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 90, MaxDefense: 107},
544: {Name: "The Black Tower Key", ID: 544, Code: "luv", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
75: {Name: "Long War Bow", ID: 75, Code: "lwb", NormalCode: "lwb", UberCode: "8lw", UltraCode: "6lw", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
76: {Name: "Light Crossbow", ID: 76, Code: "lxb", NormalCode: "lxb", UberCode: "8lx", UltraCode: "6lx", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
19: {Name: "Mace", ID: 19, Code: "mac", NormalCode: "mac", UberCode: "9ma", UltraCode: "7ma", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
23: {Name: "Maul", ID: 23, Code: "mau", NormalCode: "mau", UberCode: "9m9", UltraCode: "7m7", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
346: {Name: "Belt", ID: 346, Code: "mbl", NormalCode: "mbl", UberCode: "zmb", UltraCode: "umc", InventoryWidth: 2, InventoryHeight: 1, MinDefense: 5, MaxDefense: 5},
652: {Name: "Mephisto's Brain", ID: 652, Code: "mbr", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
341: {Name: "Chain Boots", ID: 341, Code: "mbt", NormalCode: "mbt", UberCode: "xmb", UltraCode: "umb", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 8, MaxDefense: 9},
336: {Name: "Chain Gloves", ID: 336, Code: "mgl", NormalCode: "mgl", UberCode: "xmg", UltraCode: "umg", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 8, MaxDefense: 9},
592: {Name: "Minor Mana Potion", ID: 592, Code: "mp1", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
593: {Name: "Light Mana Potion", ID: 593, Code: "mp2", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
594: {Name: "Mana Potion", ID: 594, Code: "mp3", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
595: {Name: "Greater Mana Potion", ID: 595, Code: "mp4", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
596: {Name: "Super Mana Potion", ID: 596, Code: "mp5", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
512: {Name: "Full Mana Potion", ID: 512, Code: "mpf", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
3: {Name: "Military Pick", ID: 3, Code: "mpi", NormalCode: "mpi", UberCode: "9mp", UltraCode: "7mp", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
510: {Name: "Mana Potion", ID: 510, Code: "mpo", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
92: {Name: "Staff of Kings", ID: 92, Code: "msf", NormalCode: "msf", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
312: {Name: "Mask", ID: 312, Code: "msk", NormalCode: "msk", UberCode: "xsk", UltraCode: "usk", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 9, MaxDefense: 27},
551: {Name: "Mephisto's Soulstone", ID: 551, Code: "mss", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
20: {Name: "Morning Star", ID: 20, Code: "mst", NormalCode: "mst", UberCode: "9mt", UltraCode: "7mt", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
77: {Name: "Crossbow", ID: 77, Code: "mxb", NormalCode: "mxb", UberCode: "8mx", UltraCode: "6mx", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
413: {Name: "Preserved Head", ID: 413, Code: "ne1", NormalCode: "ne1", UberCode: "ne6", UltraCode: "neb", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 2, MaxDefense: 5},
414: {Name: "Zombie Head", ID: 414, Code: "ne2", NormalCode: "ne2", UberCode: "ne7", UltraCode: "neg", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 4, MaxDefense: 8},
415: {Name: "Unraveller Head", ID: 415, Code: "ne3", NormalCode: "ne3", UberCode: "ne8", UltraCode: "ned", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 6, MaxDefense: 10},
416: {Name: "Gargoyle Head", ID: 416, Code: "ne4", NormalCode: "ne4", UberCode: "ne9", UltraCode: "nee", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 10, MaxDefense: 16},
417: {Name: "Demon Head", ID: 417, Code: "ne5", NormalCode: "ne5", UberCode: "nea", UltraCode: "nef", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 15, MaxDefense: 20},
483: {Name: "Mummified Trophy", ID: 483, Code: "ne6", NormalCode: "ne1", UberCode: "ne6", UltraCode: "neb", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 38, MaxDefense: 48},
484: {Name: "Fetish Trophy", ID: 484, Code: "ne7", NormalCode: "ne2", UberCode: "ne7", UltraCode: "neg", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 41, MaxDefense: 52},
485: {Name: "Sexton Trophy", ID: 485, Code: "ne8", NormalCode: "ne3", UberCode: "ne8", UltraCode: "ned", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 44, MaxDefense: 55},
486: {Name: "Cantor Trophy", ID: 486, Code: "ne9", NormalCode: "ne4", UberCode: "ne9", UltraCode: "nee", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 50, MaxDefense: 64},
487: {Name: "Heirophant Trophy", ID: 487, Code: "nea", NormalCode: "ne5", UberCode: "nea", UltraCode: "nef", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 58, MaxDefense: 70},
503: {Name: "Minion Skull", ID: 503, Code: "neb", NormalCode: "ne1", UberCode: "ne6", UltraCode: "neb", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 95, MaxDefense: 139},
505: {Name: "Overseer Skull", ID: 505, Code: "ned", NormalCode: "ne3", UberCode: "ne8", UltraCode: "ned", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 98, MaxDefense: 142},
506: {Name: "Succubus Skull", ID: 506, Code: "nee", NormalCode: "ne4", UberCode: "ne9", UltraCode: "nee", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 100, MaxDefense: 146},
507: {Name: "Bloodlord Skull", ID: 507, Code: "nef", NormalCode: "ne5", UberCode: "nea", UltraCode: "nef", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 103, MaxDefense: 148},
504: {Name: "Hellspawn Skull", ID: 504, Code: "neg", NormalCode: "ne2", UberCode: "ne7", UltraCode: "neg", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 96, MaxDefense: 141},
276: {Name: "Eagle Orb", ID: 276, Code: "ob1", NormalCode: "ob1", UberCode: "ob6", UltraCode: "obb", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
277: {Name: "Sacred Globe", ID: 277, Code: "ob2", NormalCode: "ob2", UberCode: "ob7", UltraCode: "obc", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
278: {Name: "Smoked Sphere", ID: 278, Code: "ob3", NormalCode: "ob3", UberCode: "ob8", UltraCode: "obd", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
279: {Name: "Clasped Orb", ID: 279, Code: "ob4", NormalCode: "ob4", UberCode: "ob9", UltraCode: "obe", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
280: {Name: "Jared's Stone", ID: 280, Code: "ob5", NormalCode: "ob5", UberCode: "oba", UltraCode: "obf", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
286: {Name: "Glowing Orb", ID: 286, Code: "ob6", NormalCode: "ob1", UberCode: "ob6", UltraCode: "obb", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
287: {Name: "Crystalline Globe", ID: 287, Code: "ob7", NormalCode: "ob2", UberCode: "ob7", UltraCode: "obc", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
288: {Name: "Cloudy Sphere", ID: 288, Code: "ob8", NormalCode: "ob3", UberCode: "ob8", UltraCode: "obd", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
289: {Name: "Sparkling Ball", ID: 289, Code: "ob9", NormalCode: "ob4", UberCode: "ob9", UltraCode: "obe", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
290: {Name: "Swirling Crystal", ID: 290, Code: "oba", NormalCode: "ob5", UberCode: "oba", UltraCode: "obf", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
296: {Name: "Heavenly Stone", ID: 296, Code: "obb", NormalCode: "ob1", UberCode: "ob6", UltraCode: "obb", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
297: {Name: "Eldritch Orb", ID: 297, Code: "obc", NormalCode: "ob2", UberCode: "ob7", UltraCode: "obc", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
298: {Name: "Demon Heart", ID: 298, Code: "obd", NormalCode: "ob3", UberCode: "ob8", UltraCode: "obd", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
299: {Name: "Vortex Orb", ID: 299, Code: "obe", NormalCode: "ob4", UberCode: "ob9", UltraCode: "obe", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
300: {Name: "Dimensional Shard", ID: 300, Code: "obf", NormalCode: "ob5", UberCode: "oba", UltraCode: "obf", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
85: {Name: "Fulminating Potion", ID: 85, Code: "opl", NormalCode: "opl", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
83: {Name: "Exploding Potion", ID: 83, Code: "opm", NormalCode: "opm", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
81: {Name: "Oil Potion", ID: 81, Code: "ops", NormalCode: "ops", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
408: {Name: "Targe", ID: 408, Code: "pa1", NormalCode: "pa1", UberCode: "pa6", UltraCode: "pab", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 8, MaxDefense: 12},
409: {Name: "Rondache", ID: 409, Code: "pa2", NormalCode: "pa2", UberCode: "pa7", UltraCode: "pac", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 10, MaxDefense: 18},
410: {Name: "Heraldic Shield", ID: 410, Code: "pa3", NormalCode: "pa3", UberCode: "pa8", UltraCode: "pad", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 16, MaxDefense: 26},
411: {Name: "Aerin Shield", ID: 411, Code: "pa4", NormalCode: "pa4", UberCode: "pa9", UltraCode: "pae", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 26, MaxDefense: 36},
412: {Name: "Crown Shield", ID: 412, Code: "pa5", NormalCode: "pa5", UberCode: "paa", UltraCode: "paf", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 30, MaxDefense: 40},
478: {Name: "Akaran Targe", ID: 478, Code: "pa6", NormalCode: "pa1", UberCode: "pa6", UltraCode: "pab", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 101, MaxDefense: 125},
479: {Name: "Akaran Rondache", ID: 479, Code: "pa7", NormalCode: "pa2", UberCode: "pa7", UltraCode: "pac", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 113, MaxDefense: 137},
480: {Name: "Protector Shield", ID: 480, Code: "pa8", NormalCode: "pa3", UberCode: "pa8", UltraCode: "pad", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 129, MaxDefense: 153},
481: {Name: "Gilded Shield", ID: 481, Code: "pa9", NormalCode: "pa4", UberCode: "pa9", UltraCode: "pae", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 144, MaxDefense: 168},
482: {Name: "Royal Shield", ID: 482, Code: "paa", NormalCode: "pa5", UberCode: "paa", UltraCode: "paf", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 156, MaxDefense: 181},
498: {Name: "Sacred Targe", ID: 498, Code: "pab", NormalCode: "pa1", UberCode: "pa6", UltraCode: "pab", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 126, MaxDefense: 158},
499: {Name: "Sacred Rondache", ID: 499, Code: "pac", NormalCode: "pa2", UberCode: "pa7", UltraCode: "pac", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 138, MaxDefense: 164},
500: {Name: "Ancient Shield", ID: 500, Code: "pad", NormalCode: "pa3", UberCode: "pa8", UltraCode: "pad", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 154, MaxDefense: 172},
501: {Name: "Zakarum Shield", ID: 501, Code: "pae", NormalCode: "pa4", UberCode: "pa9", UltraCode: "pae", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 169, MaxDefense: 193},
502: {Name: "Vortex Shield", ID: 502, Code: "paf", NormalCode: "pa5", UberCode: "paa", UltraCode: "paf", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 182, MaxDefense: 225},
60: {Name: "Poleaxe", ID: 60, Code: "pax", NormalCode: "pax", UberCode: "9pa", UltraCode: "7pa", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
56: {Name: "Pike", ID: 56, Code: "pik", NormalCode: "pik", UberCode: "9p9", UltraCode: "7p7", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
48: {Name: "Pilum", ID: 48, Code: "pil", NormalCode: "pil", UberCode: "9pi", UltraCode: "7pi", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
647: {Name: "Key of Terror", ID: 647, Code: "pk1", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
648: {Name: "Key of Hate", ID: 648, Code: "pk2", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
649: {Name: "Key of Destruction", ID: 649, Code: "pk3", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
322: {Name: "Plate Mail", ID: 322, Code: "plt", NormalCode: "plt", UberCode: "xlt", UltraCode: "ult", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 108, MaxDefense: 116},
555: {Name: "Khalim's Brain", ID: 555, Code: "qbr", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
553: {Name: "Khalim's Eye", ID: 553, Code: "qey", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
173: {Name: "Khalim's Flail", ID: 173, Code: "qf1", NormalCode: "fla", UberCode: "", UltraCode: "", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
174: {Name: "Khalim's Will", ID: 174, Code: "qf2", NormalCode: "fla", UberCode: "", UltraCode: "", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
554: {Name: "Khalim's Heart", ID: 554, Code: "qhr", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
539: {Name: "Quill", ID: 539, Code: "qll", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
313: {Name: "Quilted Armor", ID: 313, Code: "qui", NormalCode: "qui", UberCode: "xui", UltraCode: "uui", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 8, MaxDefense: 11},
610: {Name: "El Rune", ID: 610, Code: "r01", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
611: {Name: "Eld Rune", ID: 611, Code: "r02", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
612: {Name: "Tir Rune", ID: 612, Code: "r03", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
613: {Name: "Nef Rune", ID: 613, Code: "r04", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
614: {Name: "Eth Rune", ID: 614, Code: "r05", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
615: {Name: "Ith Rune", ID: 615, Code: "r06", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
616: {Name: "Tal Rune", ID: 616, Code: "r07", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
617: {Name: "Ral Rune", ID: 617, Code: "r08", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
618: {Name: "Ort Rune", ID: 618, Code: "r09", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
619: {Name: "Thul Rune", ID: 619, Code: "r10", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
620: {Name: "Amn Rune", ID: 620, Code: "r11", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
621: {Name: "Sol Rune", ID: 621, Code: "r12", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
622: {Name: "Shael Rune", ID: 622, Code: "r13", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
623: {Name: "Dol Rune", ID: 623, Code: "r14", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
624: {Name: "Hel Rune", ID: 624, Code: "r15", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
625: {Name: "Io Rune", ID: 625, Code: "r16", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
626: {Name: "Lum Rune", ID: 626, Code: "r17", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
627: {Name: "Ko Rune", ID: 627, Code: "r18", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
628: {Name: "Fal Rune", ID: 628, Code: "r19", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
629: {Name: "Lem Rune", ID: 629, Code: "r20", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
630: {Name: "Pul Rune", ID: 630, Code: "r21", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
631: {Name: "Um Rune", ID: 631, Code: "r22", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
632: {Name: "Mal Rune", ID: 632, Code: "r23", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
633: {Name: "Ist Rune", ID: 633, Code: "r24", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
634: {Name: "Gul Rune", ID: 634, Code: "r25", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
635: {Name: "Vex Rune", ID: 635, Code: "r26", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
636: {Name: "Ohm Rune", ID: 636, Code: "r27", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
637: {Name: "Lo Rune", ID: 637, Code: "r28", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
638: {Name: "Sur Rune", ID: 638, Code: "r29", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
639: {Name: "Ber Rune", ID: 639, Code: "r30", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
640: {Name: "Jah Rune", ID: 640, Code: "r31", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
641: {Name: "Cham Rune", ID: 641, Code: "r32", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
642: {Name: "Zod Rune", ID: 642, Code: "r33", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
522: {Name: "Ring", ID: 522, Code: "rin", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
317: {Name: "Ring Mail", ID: 317, Code: "rng", NormalCode: "rng", UberCode: "xng", UltraCode: "ung", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 45, MaxDefense: 48},
607: {Name: "Large Red Potion", ID: 607, Code: "rpl", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
606: {Name: "Small Red Potion", ID: 606, Code: "rps", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
516: {Name: "Full Rejuvenation Potion", ID: 516, Code: "rvl", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
515: {Name: "Rejuvenation Potion", ID: 515, Code: "rvs", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
79: {Name: "Repeating Crossbow", ID: 79, Code: "rxb", NormalCode: "rxb", UberCode: "8rx", UltraCode: "6rx", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
72: {Name: "Short Battle Bow", ID: 72, Code: "sbb", NormalCode: "sbb", UberCode: "8s8", UltraCode: "6s7", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
27: {Name: "Saber", ID: 27, Code: "sbr", NormalCode: "sbr", UberCode: "9sb", UltraCode: "7sb", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
68: {Name: "Short Bow", ID: 68, Code: "sbw", NormalCode: "sbw", UberCode: "8sb", UltraCode: "6sb", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
318: {Name: "Scale Mail", ID: 318, Code: "scl", NormalCode: "scl", UberCode: "xcl", UltraCode: "ucl", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 57, MaxDefense: 60},
26: {Name: "Scimitar", ID: 26, Code: "scm", NormalCode: "scm", UberCode: "9sm", UltraCode: "7sm", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
15: {Name: "Scepter", ID: 15, Code: "scp", NormalCode: "scp", UberCode: "9sc", UltraCode: "7sc", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
59: {Name: "Scythe", ID: 59, Code: "scy", NormalCode: "scy", UberCode: "9s8", UltraCode: "7s8", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
541: {Name: "Scalp", ID: 541, Code: "scz", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
597: {Name: "Chipped Skull", ID: 597, Code: "skc", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
598: {Name: "Flawed Skull", ID: 598, Code: "skf", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
600: {Name: "Flawless Skull", ID: 600, Code: "skl", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
307: {Name: "Skull Cap", ID: 307, Code: "skp", NormalCode: "skp", UberCode: "xkp", UltraCode: "ukp", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 8, MaxDefense: 11},
181: {Name: "Scissors Katar", ID: 181, Code: "skr", NormalCode: "skr", UberCode: "9qr", UltraCode: "7qr", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
599: {Name: "Skull", ID: 599, Code: "sku", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
601: {Name: "Perfect Skull", ID: 601, Code: "skz", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
329: {Name: "Small Shield", ID: 329, Code: "sml", NormalCode: "sml", UberCode: "xml", UltraCode: "uml", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 8, MaxDefense: 10},
540: {Name: "Soul", ID: 540, Code: "sol", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
18: {Name: "Spiked Club", ID: 18, Code: "spc", NormalCode: "spc", UberCode: "9sp", UltraCode: "7sp", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
542: {Name: "Spleen", ID: 542, Code: "spe", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
351: {Name: "Spiked Shield", ID: 351, Code: "spk", NormalCode: "spk", UberCode: "xpk", UltraCode: "upk", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 15, MaxDefense: 25},
321: {Name: "Splint Mail", ID: 321, Code: "spl", NormalCode: "spl", UberCode: "xpl", UltraCode: "upl", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 90, MaxDefense: 95},
52: {Name: "Spear", ID: 52, Code: "spr", NormalCode: "spr", UberCode: "9sr", UltraCode: "7sr", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
55: {Name: "Spetum", ID: 55, Code: "spt", NormalCode: "spt", UberCode: "9st", UltraCode: "7st", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
25: {Name: "Short Sword", ID: 25, Code: "ssd", NormalCode: "ssd", UberCode: "9ss", UltraCode: "7ss", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
49: {Name: "Short Spear", ID: 49, Code: "ssp", NormalCode: "ssp", UberCode: "9s9", UltraCode: "7s7", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
63: {Name: "Short Staff", ID: 63, Code: "sst", NormalCode: "sst", UberCode: "8ss", UltraCode: "6ss", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
658: {Name: "Standard of Heroes", ID: 658, Code: "std", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
316: {Name: "Studded Leather", ID: 316, Code: "stu", NormalCode: "stu", UberCode: "xtu", UltraCode: "utu", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 32, MaxDefense: 35},
74: {Name: "Short War Bow", ID: 74, Code: "swb", NormalCode: "swb", UberCode: "8sw", UltraCode: "6sw", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
536: {Name: "Tail", ID: 536, Code: "tal", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
44: {Name: "Throwing Axe", ID: 44, Code: "tax", NormalCode: "tax", UberCode: "9ta", UltraCode: "7ta", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
518: {Name: "Tome of Town Portal", ID: 518, Code: "tbk", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
347: {Name: "Heavy Belt", ID: 347, Code: "tbl", NormalCode: "tbl", UberCode: "ztb", UltraCode: "utc", InventoryWidth: 2, InventoryHeight: 1, MinDefense: 6, MaxDefense: 6},
342: {Name: "Light Plated Boots", ID: 342, Code: "tbt", NormalCode: "tbt", UberCode: "xtb", UltraCode: "utb", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 9, MaxDefense: 11},
527: {Name: "Torch", ID: 527, Code: "tch", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
654: {Name: "Twisted Essence of Suffering", ID: 654, Code: "tes", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
337: {Name: "Light Gauntlets", ID: 337, Code: "tgl", NormalCode: "tgl", UberCode: "xtg", UltraCode: "utg", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 9, MaxDefense: 11},
43: {Name: "Throwing Knife", ID: 43, Code: "tkf", NormalCode: "tkf", UberCode: "9tk", UltraCode: "7tk", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
653: {Name: "Token of Absolution", ID: 653, Code: "toa", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
332: {Name: "Tower Shield", ID: 332, Code: "tow", NormalCode: "tow", UberCode: "xow", UltraCode: "uow", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 22, MaxDefense: 25},
550: {Name: "Horadric Scroll", ID: 550, Code: "tr1", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
646: {Name: "Scroll of Resistance", ID: 646, Code: "tr2", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
53: {Name: "Trident", ID: 53, Code: "tri", NormalCode: "tri", UberCode: "9tr", UltraCode: "7tr", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
529: {Name: "Scroll of Town Portal", ID: 529, Code: "tsc", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
51: {Name: "Throwing Spear", ID: 51, Code: "tsp", NormalCode: "tsp", UberCode: "9ts", UltraCode: "7ts", InventoryWidth: 1, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
422: {Name: "Shako", ID: 422, Code: "uap", NormalCode: "cap", UberCode: "xap", UltraCode: "uap", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 98, MaxDefense: 141},
442: {Name: "Sacred Armor", ID: 442, Code: "uar", NormalCode: "aar", UberCode: "xar", UltraCode: "uar", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 487, MaxDefense: 600},
434: {Name: "Loricated Mail", ID: 434, Code: "ucl", NormalCode: "scl", UberCode: "xcl", UltraCode: "ucl", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 390, MaxDefense: 496},
430: {Name: "Wyrmhide", ID: 430, Code: "uea", NormalCode: "lea", UberCode: "xea", UltraCode: "uea", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 364, MaxDefense: 470},
465: {Name: "Bone Visage", ID: 465, Code: "uh9", NormalCode: "bhm", UberCode: "xh9", UltraCode: "uh9", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 100, MaxDefense: 157},
459: {Name: "Myrmidon Greaves", ID: 459, Code: "uhb", NormalCode: "hbt", UberCode: "xhb", UltraCode: "uhb", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 62, MaxDefense: 71},
464: {Name: "Colossus Girdle", ID: 464, Code: "uhc", NormalCode: "hbl", UberCode: "zhb", UltraCode: "uhc", InventoryWidth: 2, InventoryHeight: 1, MinDefense: 61, MaxDefense: 71},
454: {Name: "Ogre Gauntlets", ID: 454, Code: "uhg", NormalCode: "hgl", UberCode: "xhg", UltraCode: "uhg", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 62, MaxDefense: 71},
425: {Name: "Giant Conch", ID: 425, Code: "uhl", NormalCode: "fhl", UberCode: "xhl", UltraCode: "uhl", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 110, MaxDefense: 154},
426: {Name: "Spired Helm", ID: 426, Code: "uhm", NormalCode: "ghm", UberCode: "xhm", UltraCode: "uhm", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 114, MaxDefense: 159},
435: {Name: "Boneweave", ID: 435, Code: "uhn", NormalCode: "chn", UberCode: "xhn", UltraCode: "uhn", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 399, MaxDefense: 505},
447: {Name: "Monarch", ID: 447, Code: "uit", NormalCode: "kit", UberCode: "xit", UltraCode: "uit", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 133, MaxDefense: 148},
423: {Name: "Hydraskull", ID: 423, Code: "ukp", NormalCode: "skp", UberCode: "xkp", UltraCode: "ukp", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 101, MaxDefense: 145},
431: {Name: "Scarab Husk", ID: 431, Code: "ula", NormalCode: "hla", UberCode: "xla", UltraCode: "ula", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 369, MaxDefense: 474},
455: {Name: "Wyrmhide Boots", ID: 455, Code: "ulb", NormalCode: "lbt", UberCode: "xlb", UltraCode: "ulb", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 54, MaxDefense: 62},
460: {Name: "Spiderweb Sash", ID: 460, Code: "ulc", NormalCode: "lbl", UberCode: "zlb", UltraCode: "ulc", InventoryWidth: 2, InventoryHeight: 1, MinDefense: 55, MaxDefense: 62},
439: {Name: "Kraken Shell", ID: 439, Code: "uld", NormalCode: "fld", UberCode: "xld", UltraCode: "uld", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 417, MaxDefense: 523},
450: {Name: "Bramble Mitts", ID: 450, Code: "ulg", NormalCode: "lgl", UberCode: "xlg", UltraCode: "ulg", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 54, MaxDefense: 62},
424: {Name: "Armet", ID: 424, Code: "ulm", NormalCode: "hlm", UberCode: "xlm", UltraCode: "ulm", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 105, MaxDefense: 149},
438: {Name: "Hellforge Plate", ID: 438, Code: "ult", NormalCode: "plt", UberCode: "xlt", UltraCode: "ult", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 421, MaxDefense: 530},
457: {Name: "Boneweave Boots", ID: 457, Code: "umb", NormalCode: "mbt", UberCode: "xmb", UltraCode: "umb", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 59, MaxDefense: 67},
462: {Name: "Mithril Coil", ID: 462, Code: "umc", NormalCode: "mbl", UberCode: "zmb", UltraCode: "umc", InventoryWidth: 2, InventoryHeight: 1, MinDefense: 58, MaxDefense: 65},
452: {Name: "Vambraces", ID: 452, Code: "umg", NormalCode: "mgl", UberCode: "xmg", UltraCode: "umg", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 59, MaxDefense: 67},
445: {Name: "Luna", ID: 445, Code: "uml", NormalCode: "sml", UberCode: "xml", UltraCode: "uml", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 108, MaxDefense: 123},
433: {Name: "Diamond Mail", ID: 433, Code: "ung", NormalCode: "rng", UberCode: "xng", UltraCode: "ung", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 383, MaxDefense: 489},
448: {Name: "Aegis", ID: 448, Code: "uow", NormalCode: "tow", UberCode: "xow", UltraCode: "uow", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 145, MaxDefense: 161},
467: {Name: "Blade Barrier", ID: 467, Code: "upk", NormalCode: "spk", UberCode: "xpk", UltraCode: "upk", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 147, MaxDefense: 163},
437: {Name: "Balrog Skin", ID: 437, Code: "upl", NormalCode: "spl", UberCode: "xpl", UltraCode: "upl", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 410, MaxDefense: 517},
446: {Name: "Hyperion", ID: 446, Code: "urg", NormalCode: "lrg", UberCode: "xrg", UltraCode: "urg", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 119, MaxDefense: 135},
427: {Name: "Corona", ID: 427, Code: "urn", NormalCode: "crn", UberCode: "xrn", UltraCode: "urn", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 111, MaxDefense: 165},
436: {Name: "Great Hauberk", ID: 436, Code: "urs", NormalCode: "brs", UberCode: "xrs", UltraCode: "urs", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 395, MaxDefense: 501},
466: {Name: "Troll Nest", ID: 466, Code: "ush", NormalCode: "bsh", UberCode: "xsh", UltraCode: "ush", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 158, MaxDefense: 173},
428: {Name: "Demonhead", ID: 428, Code: "usk", NormalCode: "msk", UberCode: "xsk", UltraCode: "usk", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 101, MaxDefense: 154},
458: {Name: "Mirrored Boots", ID: 458, Code: "utb", NormalCode: "tbt", UberCode: "xtb", UltraCode: "utb", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 59, MaxDefense: 68},
463: {Name: "Troll Belt", ID: 463, Code: "utc", NormalCode: "tbl", UberCode: "ztb", UltraCode: "utc", InventoryWidth: 2, InventoryHeight: 1, MinDefense: 59, MaxDefense: 66},
453: {Name: "Crusader Gauntlets", ID: 453, Code: "utg", NormalCode: "tgl", UberCode: "xtg", UltraCode: "utg", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 59, MaxDefense: 68},
440: {Name: "Lacquered Plate", ID: 440, Code: "uth", NormalCode: "gth", UberCode: "xth", UltraCode: "uth", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 433, MaxDefense: 541},
443: {Name: "Archon Plate", ID: 443, Code: "utp", NormalCode: "ltp", UberCode: "xtp", UltraCode: "utp", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 410, MaxDefense: 524},
449: {Name: "Ward", ID: 449, Code: "uts", NormalCode: "gts", UberCode: "xts", UltraCode: "uts", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 153, MaxDefense: 170},
432: {Name: "Wire Fleece", ID: 432, Code: "utu", NormalCode: "stu", UberCode: "xtu", UltraCode: "utu", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 375, MaxDefense: 481},
444: {Name: "Heater", ID: 444, Code: "uuc", NormalCode: "buc", UberCode: "xuc", UltraCode: "uuc", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 95, MaxDefense: 110},
429: {Name: "Dusk Shroud", ID: 429, Code: "uui", NormalCode: "qui", UberCode: "xui", UltraCode: "uui", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 361, MaxDefense: 467},
441: {Name: "Shadow Plate", ID: 441, Code: "uul", NormalCode: "ful", UberCode: "xul", UltraCode: "uul", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 446, MaxDefense: 557},
456: {Name: "Scarabshell Boots", ID: 456, Code: "uvb", NormalCode: "vbt", UberCode: "xvb", UltraCode: "uvb", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 56, MaxDefense: 65},
461: {Name: "Vampirefang Belt", ID: 461, Code: "uvc", NormalCode: "vbl", UberCode: "zvb", UltraCode: "uvc", InventoryWidth: 2, InventoryHeight: 1, MinDefense: 56, MaxDefense: 63},
451: {Name: "Vampirebone Gloves", ID: 451, Code: "uvg", NormalCode: "vgl", UberCode: "xvg", UltraCode: "uvg", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 56, MaxDefense: 65},
345: {Name: "Light Belt", ID: 345, Code: "vbl", NormalCode: "vbl", UberCode: "zvb", UltraCode: "uvc", InventoryWidth: 2, InventoryHeight: 1, MinDefense: 3, MaxDefense: 3},
340: {Name: "Heavy Boots", ID: 340, Code: "vbt", NormalCode: "vbt", UberCode: "xvb", UltraCode: "uvb", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 5, MaxDefense: 6},
335: {Name: "Heavy Gloves", ID: 335, Code: "vgl", NormalCode: "vgl", UberCode: "xvg", UltraCode: "uvg", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 5, MaxDefense: 6},
521: {Name: "Amulet of the Viper", ID: 521, Code: "vip", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
58: {Name: "Voulge", ID: 58, Code: "vou", NormalCode: "vou", UberCode: "9vo", UltraCode: "7vo", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
513: {Name: "Stamina Potion", ID: 513, Code: "vps", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
4: {Name: "War Axe", ID: 4, Code: "wax", NormalCode: "wax", UberCode: "9wa", UltraCode: "7wa", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
22: {Name: "War Hammer", ID: 22, Code: "whm", NormalCode: "whm", UberCode: "9wh", UltraCode: "7wh", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
517: {Name: "Thawing Potion", ID: 517, Code: "wms", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
10: {Name: "Wand", ID: 10, Code: "wnd", NormalCode: "wnd", UberCode: "9wn", UltraCode: "7wn", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
176: {Name: "Wrist Blade", ID: 176, Code: "wrb", NormalCode: "wrb", UberCode: "9wb", UltraCode: "7wb", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
62: {Name: "War Scythe", ID: 62, Code: "wsc", NormalCode: "wsc", UberCode: "9wc", UltraCode: "7wc", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
32: {Name: "War Sword", ID: 32, Code: "wsd", NormalCode: "wsd", UberCode: "9wd", UltraCode: "7wd", InventoryWidth: 1, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
17: {Name: "War Scepter", ID: 17, Code: "wsp", NormalCode: "wsp", UberCode: "9ws", UltraCode: "7ws", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 0, MaxDefense: 0},
67: {Name: "War Staff", ID: 67, Code: "wst", NormalCode: "wst", UberCode: "8ws", UltraCode: "6ws", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 0, MaxDefense: 0},
352: {Name: "War Hat", ID: 352, Code: "xap", NormalCode: "cap", UberCode: "xap", UltraCode: "uap", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 45, MaxDefense: 53},
372: {Name: "Ornate Armor", ID: 372, Code: "xar", NormalCode: "aar", UberCode: "xar", UltraCode: "uar", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 417, MaxDefense: 450},
364: {Name: "Tigulated Mail", ID: 364, Code: "xcl", NormalCode: "scl", UberCode: "xcl", UltraCode: "ucl", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 176, MaxDefense: 190},
360: {Name: "Serpentskin Armor", ID: 360, Code: "xea", NormalCode: "lea", UberCode: "xea", UltraCode: "uea", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 111, MaxDefense: 126},
395: {Name: "Grim Helm", ID: 395, Code: "xh9", NormalCode: "bhm", UberCode: "xh9", UltraCode: "uh9", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 60, MaxDefense: 125},
389: {Name: "War Boots", ID: 389, Code: "xhb", NormalCode: "hbt", UberCode: "xhb", UltraCode: "uhb", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 43, MaxDefense: 53},
384: {Name: "War Gauntlets", ID: 384, Code: "xhg", NormalCode: "hgl", UberCode: "xhg", UltraCode: "uhg", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 43, MaxDefense: 53},
355: {Name: "Basinet", ID: 355, Code: "xhl", NormalCode: "fhl", UberCode: "xhl", UltraCode: "uhl", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 75, MaxDefense: 84},
356: {Name: "Winged Helm", ID: 356, Code: "xhm", NormalCode: "ghm", UberCode: "xhm", UltraCode: "uhm", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 85, MaxDefense: 98},
365: {Name: "Mesh Armor", ID: 365, Code: "xhn", NormalCode: "chn", UberCode: "xhn", UltraCode: "uhn", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 198, MaxDefense: 213},
377: {Name: "Dragon Shield", ID: 377, Code: "xit", NormalCode: "kit", UberCode: "xit", UltraCode: "uit", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 59, MaxDefense: 67},
353: {Name: "Sallet", ID: 353, Code: "xkp", NormalCode: "skp", UberCode: "xkp", UltraCode: "ukp", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 52, MaxDefense: 62},
361: {Name: "Demonhide Armor", ID: 361, Code: "xla", NormalCode: "hla", UberCode: "xla", UltraCode: "ula", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 122, MaxDefense: 136},
385: {Name: "Demonhide Boots", ID: 385, Code: "xlb", NormalCode: "lbt", UberCode: "xlb", UltraCode: "ulb", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 28, MaxDefense: 35},
369: {Name: "Sharktooth Armor", ID: 369, Code: "xld", NormalCode: "fld", UberCode: "xld", UltraCode: "uld", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 242, MaxDefense: 258},
380: {Name: "Demonhide Gloves", ID: 380, Code: "xlg", NormalCode: "lgl", UberCode: "xlg", UltraCode: "ulg", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 28, MaxDefense: 35},
354: {Name: "Casque", ID: 354, Code: "xlm", NormalCode: "hlm", UberCode: "xlm", UltraCode: "ulm", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 63, MaxDefense: 72},
368: {Name: "Templar Coat", ID: 368, Code: "xlt", NormalCode: "plt", UberCode: "xlt", UltraCode: "ult", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 252, MaxDefense: 274},
387: {Name: "Mesh Boots", ID: 387, Code: "xmb", NormalCode: "mbt", UberCode: "xmb", UltraCode: "umb", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 37, MaxDefense: 44},
382: {Name: "Heavy Bracers", ID: 382, Code: "xmg", NormalCode: "mgl", UberCode: "xmg", UltraCode: "umg", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 37, MaxDefense: 44},
375: {Name: "Round Shield", ID: 375, Code: "xml", NormalCode: "sml", UberCode: "xml", UltraCode: "uml", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 47, MaxDefense: 55},
363: {Name: "Linked Mail", ID: 363, Code: "xng", NormalCode: "rng", UberCode: "xng", UltraCode: "ung", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 158, MaxDefense: 172},
378: {Name: "Pavise", ID: 378, Code: "xow", NormalCode: "tow", UberCode: "xow", UltraCode: "uow", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 68, MaxDefense: 78},
397: {Name: "Barbed Shield", ID: 397, Code: "xpk", NormalCode: "spk", UberCode: "xpk", UltraCode: "upk", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 58, MaxDefense: 78},
367: {Name: "Russet Armor", ID: 367, Code: "xpl", NormalCode: "spl", UberCode: "xpl", UltraCode: "upl", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 225, MaxDefense: 243},
376: {Name: "Scutum", ID: 376, Code: "xrg", NormalCode: "lrg", UberCode: "xrg", UltraCode: "urg", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 53, MaxDefense: 61},
357: {Name: "Grand Crown", ID: 357, Code: "xrn", NormalCode: "crn", UberCode: "xrn", UltraCode: "urn", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 78, MaxDefense: 113},
366: {Name: "Cuirass", ID: 366, Code: "xrs", NormalCode: "brs", UberCode: "xrs", UltraCode: "urs", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 188, MaxDefense: 202},
396: {Name: "Grim Shield", ID: 396, Code: "xsh", NormalCode: "bsh", UberCode: "xsh", UltraCode: "ush", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 50, MaxDefense: 150},
358: {Name: "Death Mask", ID: 358, Code: "xsk", NormalCode: "msk", UberCode: "xsk", UltraCode: "usk", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 54, MaxDefense: 86},
388: {Name: "Battle Boots", ID: 388, Code: "xtb", NormalCode: "tbt", UberCode: "xtb", UltraCode: "utb", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 39, MaxDefense: 47},
383: {Name: "Battle Gauntlets", ID: 383, Code: "xtg", NormalCode: "tgl", UberCode: "xtg", UltraCode: "utg", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 39, MaxDefense: 47},
370: {Name: "Embossed Plate", ID: 370, Code: "xth", NormalCode: "gth", UberCode: "xth", UltraCode: "uth", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 282, MaxDefense: 303},
373: {Name: "Mage Plate", ID: 373, Code: "xtp", NormalCode: "ltp", UberCode: "xtp", UltraCode: "utp", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 225, MaxDefense: 261},
379: {Name: "Ancient Shield", ID: 379, Code: "xts", NormalCode: "gts", UberCode: "xts", UltraCode: "uts", InventoryWidth: 2, InventoryHeight: 4, MinDefense: 80, MaxDefense: 93},
362: {Name: "Trellised Armor", ID: 362, Code: "xtu", NormalCode: "stu", UberCode: "xtu", UltraCode: "utu", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 138, MaxDefense: 153},
374: {Name: "Defender", ID: 374, Code: "xuc", NormalCode: "buc", UberCode: "xuc", UltraCode: "uuc", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 41, MaxDefense: 49},
359: {Name: "Ghost Armor", ID: 359, Code: "xui", NormalCode: "qui", UberCode: "xui", UltraCode: "uui", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 102, MaxDefense: 117},
371: {Name: "Chaos Armor", ID: 371, Code: "xul", NormalCode: "ful", UberCode: "xul", UltraCode: "uul", InventoryWidth: 2, InventoryHeight: 3, MinDefense: 315, MaxDefense: 342},
386: {Name: "Sharkskin Boots", ID: 386, Code: "xvb", NormalCode: "vbt", UberCode: "xvb", UltraCode: "uvb", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 33, MaxDefense: 39},
381: {Name: "Sharkskin Gloves", ID: 381, Code: "xvg", NormalCode: "vgl", UberCode: "xvg", UltraCode: "uvg", InventoryWidth: 2, InventoryHeight: 2, MinDefense: 33, MaxDefense: 39},
545: {Name: "Potion of Life", ID: 545, Code: "xyz", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
514: {Name: "Antidote Potion", ID: 514, Code: "yps", NormalCode: "", UberCode: "", UltraCode: "", InventoryWidth: 1, InventoryHeight: 1, MinDefense: 0, MaxDefense: 0},
11: {Name: "Yew Wand", ID: 11, Code: "ywn", NormalCode: "ywn", UberCode: "9yw", UltraCode: "7yw", InventoryWidth: 1, InventoryHeight: 2, MinDefense: 0, MaxDefense: 0},
394: {Name: "War Belt", ID: 394, Code: "zhb", NormalCode: "hbl", UberCode: "zhb", UltraCode: "uhc", InventoryWidth: 2, InventoryHeight: 1, MinDefense: 41, MaxDefense: 52},
390: {Name: "Demonhide Sash", ID: 390, Code: "zlb", NormalCode: "lbl", UberCode: "zlb", UltraCode: "ulc", InventoryWidth: 2, InventoryHeight: 1, MinDefense: 29, MaxDefense: 34},
392: {Name: "Mesh Belt", ID: 392, Code: "zmb", NormalCode: "mbl", UberCode: "zmb", UltraCode: "umc", InventoryWidth: 2, InventoryHeight: 1, MinDefense: 35, MaxDefense: 40},
393: {Name: "Battle Belt", ID: 393, Code: "ztb", NormalCode: "tbl", UberCode: "ztb", UltraCode: "utc", InventoryWidth: 2, InventoryHeight: 1, MinDefense: 37, MaxDefense: 42},
391: {Name: "Sharkskin Belt", ID: 391, Code: "zvb", NormalCode: "vbl", UberCode: "zvb", UltraCode: "uvc", InventoryWidth: 2, InventoryHeight: 1, MinDefense: 31, MaxDefense: 36},
}

View File

@@ -1,5 +1,7 @@
package item
import "strings"
const (
ScrollOfTownPortal = "ScrollOfTownPortal"
ScrollOfIdentify = "ScrollOfIdentify"
@@ -9,12 +11,22 @@ const (
)
func GetNameByEnum(itemNumber uint) Name {
return Name(available[itemNumber])
return Name(Names[itemNumber])
}
func GetIDByName(itemName string) int {
for i, name := range Names {
if strings.EqualFold(name, itemName) {
return i
}
}
return -1
}
type Name string
var available = []string{
var Names = []string{
"HandAxe",
"Axe",
"DoubleAxe",

9
pkg/data/item/tier.go Normal file
View File

@@ -0,0 +1,9 @@
package item
type Tier int
const (
TierNormal Tier = iota
TierExceptional
TierElite
)

View File

@@ -48,6 +48,7 @@ func (i Items) ByLocation(locations ...item.Location) []Item {
type UnitID int
type Item struct {
ID int
UnitID
Name item.Name
Quality item.Quality
@@ -55,11 +56,17 @@ type Item struct {
Location item.Location
Ethereal bool
IsHovered bool
Stats map[stat.ID]stat.Data
Stats []stat.Data
BaseStats []stat.Data
Identified bool
Type int
}
func (i Item) Type() string {
func (i Item) Desc() item.Description {
return item.Desc[i.ID]
}
func (i Item) TypeAsString() string {
t, _ := item.TypeForItemName(string(i.Name))
return t
@@ -90,3 +97,14 @@ func (i Item) IsFromQuest() bool {
return false
}
func (i Item) FindStat(id stat.ID, layer int) (stat.Data, bool) {
totalStats := append(i.Stats, i.BaseStats...)
for _, s := range totalStats {
if s.ID == id && s.Layer == layer {
return s, true
}
}
return stat.Data{}, false
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,49 +0,0 @@
package itemfilter
import "github.com/hectorgimenez/d2go/pkg/nip"
type evaluationChain struct {
links []link
}
type link struct {
Result bool
Operand nip.Operand
}
func (ch *evaluationChain) Add(result bool, operand nip.Operand) {
ch.links = append(ch.links, link{
Result: result,
Operand: operand,
})
}
func (ch *evaluationChain) Evaluate() bool {
if len(ch.links) == 0 {
return true
}
if len(ch.links) == 1 {
return ch.links[0].Result
}
result := ch.links[0].Result
operand := ch.links[0].Operand
for i, eval := range ch.links {
if i == 0 {
continue
}
if i < len(ch.links) {
switch operand {
case nip.OperandAnd:
result = result && eval.Result
case nip.OperandOr:
result = result || eval.Result
}
}
operand = eval.Operand
}
return result
}

View File

@@ -1,64 +0,0 @@
package itemfilter
import (
"testing"
"github.com/hectorgimenez/d2go/pkg/nip"
)
func Test_evaluationChain_Evaluate(t *testing.T) {
tests := []struct {
name string
links []link
want bool
}{
{
name: "Given one single link with true value should return true",
links: []link{
{true, nip.OperandNone},
},
want: true,
},
{
name: "Given one single link with false value should return false",
links: []link{
{false, nip.OperandNone},
},
want: false,
},
{
name: "Given different links using OR and one valid value should be true",
links: []link{
{false, nip.OperandOr},
{true, nip.OperandNone},
},
want: true,
},
{
name: "Given different links using OR and no valid value should be false",
links: []link{
{false, nip.OperandOr},
{false, nip.OperandNone},
},
want: false,
},
{
name: "Given different links using AND and one valid value should be false",
links: []link{
{true, nip.OperandAnd},
{false, nip.OperandNone},
},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ch := &evaluationChain{
links: tt.links,
}
if got := ch.Evaluate(); got != tt.want {
t.Errorf("Evaluate() = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -1,117 +0,0 @@
package itemfilter
import (
"github.com/hectorgimenez/d2go/pkg/data/stat"
"strings"
"github.com/hectorgimenez/d2go/pkg/data"
"github.com/hectorgimenez/d2go/pkg/nip"
)
func Evaluate(i data.Item, rules []nip.Rule) (nip.Rule, bool) {
for _, r := range rules {
if !evaluateGroups(i, r.Properties, checkProperty) {
// Properties not matching, skipping
continue
}
// We can not check stats, item is not identified, but properties matching
if !i.Identified {
return nip.Rule{}, true
}
if evaluateGroups(i, r.Stats, checkStat) {
return r, true
}
}
return nip.Rule{}, false
}
func evaluateGroups(i data.Item, groups []nip.Group, evalFunc func(i data.Item, prop nip.Comparable) bool) bool {
groupChain := evaluationChain{}
for _, group := range groups {
propChain := evaluationChain{}
for _, st := range group.Comparable {
propChain.Add(evalFunc(i, st), st.Operand)
}
groupChain.Add(propChain.Evaluate(), group.Operand)
}
return groupChain.Evaluate()
}
func checkStat(i data.Item, cmp nip.Comparable) bool {
st, found := stats[cmp.Keyword]
if !found {
// pass it, just in case...
return true
}
itemStat, found := i.Stats[stat.ID(st[0])]
if !found {
return false
}
if !compare(itemStat.Value, cmp.ValueInt, cmp.Comparison) {
return false
}
if len(st) == 1 {
return true
}
return st[1] == itemStat.Layer
}
func checkProperty(i data.Item, prop nip.Comparable) bool {
switch prop.Keyword {
case nip.PropertyType:
return strings.EqualFold(i.Type(), prop.ValueString)
case nip.PropertyName:
return strings.EqualFold(string(i.Name), prop.ValueString)
case nip.PropertyClass:
// TODO: Implement
case nip.PropertyQuality:
quality, found := qualities[prop.ValueString]
if !found {
return false
}
return compare(int(i.Quality), int(quality), prop.Comparison)
case nip.PropertyFlag:
if prop.Comparison == nip.OperandEqual && !i.Ethereal {
return false
}
if prop.Comparison == nip.OperandNotEqualTo && i.Ethereal {
return false
}
case nip.PropertyLevel:
// TODO: Implement
case nip.PropertyPrefix:
// TODO: Implement
case nip.PropertySuffix:
// TODO: Implement
}
return true
}
func compare(val1, val2 int, operand nip.Operand) bool {
switch operand {
case nip.OperandEqual:
return val1 == val2
case nip.OperandGreaterThan:
return val1 > val2
case nip.OperandGreaterOrEqualTo:
return val1 >= val2
case nip.OperandLessThan:
return val1 < val2
case nip.OperandLessThanOrEqualTo:
return val1 <= val2
case nip.OperandNotEqualTo:
return val1 != val2
}
return false
}

View File

@@ -1,45 +0,0 @@
package itemfilter
import (
"testing"
"github.com/hectorgimenez/d2go/pkg/data"
"github.com/hectorgimenez/d2go/pkg/data/item"
"github.com/hectorgimenez/d2go/pkg/nip"
"github.com/stretchr/testify/require"
)
func TestEvaluate(t *testing.T) {
type args struct {
i data.Item
nipRule string // not the best test but too lazy to write rules manually
}
tests := []struct {
name string
args args
want bool
}{
{
name: "",
args: args{
i: data.Item{
Name: item.Name("lightplatedboots"),
Quality: item.QualityUnique,
Ethereal: false,
},
nipRule: "[name] == lightplatedboots && [quality] == unique && [flag] != ethereal # [enhanceddefense] == 60 // goblin toe",
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
rule, err := nip.ParseLine(tt.args.nipRule, "dummy", 0)
require.NoError(t, err)
if _, got := Evaluate(tt.args.i, []nip.Rule{rule}); got != tt.want {
t.Errorf("Evaluate() = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -108,6 +108,10 @@ func (gd *GameReader) hoveredData() data.HoverData {
}
func (gd *GameReader) getStatsData(statCount uint, statPtr uintptr) []stat.Data {
if statCount == 0 {
return []stat.Data{}
}
var stats = make([]stat.Data, 0)
statBuffer := gd.Process.ReadBytesFromMemory(statPtr, statCount*10)
for i := 0; i < int(statCount); i++ {

View File

@@ -46,13 +46,8 @@ func (gd *GameReader) Items(pu data.PlayerUnit, hover data.HoverData) data.Items
// Item Stats
statsListExPtr := uintptr(ReadUIntFromBuffer(itemDataBuffer, 0x88, Uint64))
statsListExBuffer := gd.Process.ReadBytesFromMemory(statsListExPtr, 180)
statPtr := uintptr(ReadUIntFromBuffer(statsListExBuffer, 0x30, Uint64))
statCount := ReadUIntFromBuffer(statsListExBuffer, 0x38, Uint32)
statExPtr := uintptr(ReadUIntFromBuffer(statsListExBuffer, 0x88, Uint64))
statExCount := ReadUIntFromBuffer(statsListExBuffer, 0x90, Uint32)
stats := gd.getItemStats(statCount, statPtr, statExCount, statExPtr)
baseStats, stats := gd.getItemStats(statsListExPtr)
name := item.GetNameByEnum(txtFileNo)
itemHovered := false
@@ -61,6 +56,7 @@ func (gd *GameReader) Items(pu data.PlayerUnit, hover data.HoverData) data.Items
}
itm := data.Item{
ID: int(txtFileNo),
UnitID: data.UnitID(unitID),
Name: name,
Quality: item.Quality(itemQuality),
@@ -70,6 +66,8 @@ func (gd *GameReader) Items(pu data.PlayerUnit, hover data.HoverData) data.Items
},
IsHovered: itemHovered,
Stats: stats,
BaseStats: baseStats,
Type: int(itemType),
}
setProperties(&itm, uint32(flags))
@@ -103,7 +101,7 @@ func (gd *GameReader) Items(pu data.PlayerUnit, hover data.HoverData) data.Items
}
case 1:
location = item.LocationEquipped
if itm.Type() == "belt" {
if itm.TypeAsString() == "belt" {
belt.Name = itm.Name
}
case 2:
@@ -140,23 +138,16 @@ func (gd *GameReader) Items(pu data.PlayerUnit, hover data.HoverData) data.Items
return items
}
func (gd *GameReader) getItemStats(statCount uint, statPtr uintptr, statExCount uint, statExPtr uintptr) map[stat.ID]stat.Data {
stats := make(map[stat.ID]stat.Data, 0)
if statCount < 20 && statCount > 0 {
stats1 := gd.getStatsData(statCount, statPtr)
for _, v := range stats1 {
stats[v.ID] = v
}
}
func (gd *GameReader) getItemStats(statsListExPtr uintptr) ([]stat.Data, []stat.Data) {
baseStatPtr := gd.Process.ReadUInt(statsListExPtr+0x30, Uint64)
baseStatsCount := gd.Process.ReadUInt(statsListExPtr+0x38, Uint64)
statPtr := gd.Process.ReadUInt(statsListExPtr+0x88, Uint64)
statCount := gd.Process.ReadUInt(statsListExPtr+0x90, Uint64)
if statExCount < 20 && statExCount > 0 {
stats2 := gd.getStatsData(statExCount, statExPtr)
for _, v := range stats2 {
stats[v.ID] = v
}
}
stats := gd.getStatsData(statCount, uintptr(statPtr))
baseStats := gd.getStatsData(baseStatsCount, uintptr(baseStatPtr))
return stats
return baseStats, stats
}
func setProperties(item *data.Item, flags uint32) {

View File

@@ -1,23 +1,127 @@
package itemfilter
package nip
import (
"github.com/hectorgimenez/d2go/pkg/data/item"
"github.com/hectorgimenez/d2go/pkg/nip"
)
var qualities = map[string]item.Quality{
nip.QualityLowQuality: item.QualityLowQuality,
nip.QualityNormal: item.QualityNormal,
nip.QualitySuperior: item.QualitySuperior,
nip.QualityMagic: item.QualityMagic,
nip.QualitySet: item.QualitySet,
nip.QualityRare: item.QualityRare,
nip.QualityUnique: item.QualityUnique,
nip.QualityCrafted: item.QualityCrafted,
var typeAliases = map[string]int{
"shield": 2,
"armor": 3,
"gold": 4,
"bowquiver": 5,
"crossbowquiver": 6,
"playerbodypart": 7,
"herb": 8,
"potion": 9,
"ring": 10,
"elixir": 11,
"amulet": 12,
"charm": 13,
"notused1": 14,
"boots": 15,
"gloves": 16,
"notused2": 17,
"book": 18,
"belt": 19,
"gem": 20,
"torch": 21,
"scroll": 22,
"notused3": 23,
"scepter": 24,
"wand": 25,
"staff": 26,
"bow": 27,
"axe": 28,
"club": 29,
"sword": 30,
"hammer": 31,
"knife": 32,
"spear": 33,
"polearm": 34,
"crossbow": 35,
"mace": 36,
"helm": 37,
"missilepotion": 38,
"quest": 39,
"bodypart": 40,
"key": 41,
"throwingknife": 42,
"throwingaxe": 43,
"javelin": 44,
"weapon": 45,
"meleeweapon": 46,
"missileweapon": 47,
"thrownweapon": 48,
"comboweapon": 49,
"anyarmor": 50,
"anyshield": 51,
"miscellaneous": 52,
"socketfiller": 53,
"secondhand": 54,
"stavesandrods": 55,
"missile": 56,
"blunt": 57,
"jewel": 58,
"classspecific": 59,
"amazonitem": 60,
"barbarianitem": 61,
"necromanceritem": 62,
"paladinitem": 63,
"sorceressitem": 64,
"assassinitem": 65,
"druiditem": 66,
"handtohand": 67,
"orb": 68,
"voodooheads": 69,
"auricshields": 70,
"primalhelm": 71,
"pelt": 72,
"cloak": 73,
"rune": 74,
"circlet": 75,
"healingpotion": 76,
"manapotion": 77,
"rejuvpotion": 78,
"staminapotion": 79,
"antidotepotion": 80,
"thawingpotion": 81,
"smallcharm": 82,
"mediumcharm": 83,
"largecharm": 84,
"amazonbow": 85,
"amazonspear": 86,
"amazonjavelin": 87,
"assassinclaw": 88,
"magicbowquiv": 89,
"magicxbowquiv": 90,
"chippedgem": 91,
"flawedgem": 92,
"standardgem": 93,
"flawlessgem": 94,
"perfectgem": 95,
"amethyst": 96,
"diamond": 97,
"emerald": 98,
"ruby": 99,
"sapphire": 100,
"topaz": 101,
"skull": 102,
}
// Source: https://github.com/blizzhackers/kolbot/blob/mainline/d2bs/kolbot/libs/NTItemAlias.dbl
var stats = map[string][]int{
var qualityAliases = map[string]int{
"lowquality": 1,
"normal": 2,
"superior": 3,
"magic": 4,
"set": 5,
"rare": 6,
"unique": 7,
"crafted": 8,
}
var classAliases = map[string]int{
"normal": 0,
"exceptional": 1,
"elite": 2,
}
var statAliases = map[string][]int{
"strength": {0},
"energy": {1},
"dexterity": {2},
@@ -421,20 +525,21 @@ var stats = map[string][]int{
"itemabsorbcold": {149},
"itemslow": {150},
"itemaura": {151},
"mightaura": {151, 98},
"holyfireaura": {151, 102},
"thornsaura": {151, 103},
"defianceaura": {151, 104},
"concentrationaura": {151, 113},
"holyfreezeaura": {151, 114},
"vigoraura": {151, 115},
"holyshockaura": {151, 118},
"sanctuaryaura": {151, 119},
"meditationaura": {151, 120},
"fanaticismaura": {151, 122},
"convictionaura": {151, 123},
"redemptionaura": {151, 124},
"itemaura": {151},
"mightaura": {151, 98},
"holyfireaura": {151, 102},
"thornsaura": {151, 103},
"defianceaura": {151, 104},
"concentrationaura": {151, 113},
"holyfreezeaura": {151, 114},
"vigoraura": {151, 115},
"holyshockaura": {151, 118},
"sanctuaryaura": {151, 119},
"meditationaura": {151, 120},
"fanaticismaura": {151, 122},
"convictionaura": {151, 123},
"redemptionaura": {151, 124},
"itemindestructible": {152},
"itemcannotbefrozen": {153},
"itemstaminadrainpct": {154},
@@ -678,4 +783,7 @@ var stats = map[string][]int{
"questitemdifficulty": {356},
"passivemagmastery": {357},
"passivemagpierce": {358},
// Doesnt really exists, but is calculated in getStatEx
"allres": {555},
}

8
pkg/nip/errors.go Normal file
View File

@@ -0,0 +1,8 @@
package nip
import "errors"
var (
// ErrEmptyRule is returned when a rule is empty
ErrEmptyRule = errors.New("empty rule")
)

View File

@@ -6,9 +6,12 @@ import (
"fmt"
"os"
"strings"
"github.com/hectorgimenez/d2go/pkg/data"
"github.com/hectorgimenez/d2go/pkg/data/item"
)
func ReadDir(path string) ([]Rule, error) {
func ReadDir(path string) (Rules, error) {
files, err := os.ReadDir(path)
if err != nil {
return nil, err
@@ -31,7 +34,7 @@ func ReadDir(path string) ([]Rule, error) {
return rules, nil
}
func ParseNIPFile(filePath string) ([]Rule, error) {
func ParseNIPFile(filePath string) (Rules, error) {
fileReader, err := os.Open(filePath)
if err != nil {
return nil, err
@@ -43,18 +46,44 @@ func ParseNIPFile(filePath string) ([]Rule, error) {
rules := make([]Rule, 0)
lineNumber := 0
dummyItem := data.Item{
ID: 516,
Name: "healingpotion",
Quality: item.QualityNormal,
}
for fileScanner.Scan() {
lineNumber++
rule, err := ParseLine(fileScanner.Text(), filePath, lineNumber)
if errors.Is(err, errEmptyLine) {
rule, err := New(fileScanner.Text(), filePath, lineNumber)
if errors.Is(err, ErrEmptyRule) {
continue
}
if err != nil {
return nil, fmt.Errorf("error reading %s file at line %d: %w", filePath, lineNumber, err)
}
// We evaluate all the rules at startup to ensure no format errors, if there is a format error we will throw it now instead of during runtime
_, err = rule.Evaluate(dummyItem)
if err != nil {
return nil, fmt.Errorf("error testing rule on [%s:%d]: %w", filePath, lineNumber, err)
}
rules = append(rules, rule)
}
return rules, nil
}
func sanitizeLine(rawLine string) string {
l := strings.Split(rawLine, "//")
line := strings.TrimSpace(l[0])
line = strings.Join(strings.Fields(line), " ")
line = strings.ReplaceAll(line, "'", "")
// Fix possible wrong formatted lines
line = strings.ReplaceAll(line, "=>", ">=")
line = strings.ReplaceAll(line, "=<", "<=")
line = strings.TrimSpace(strings.Trim(line, "&&"))
return strings.ToLower(line)
}

View File

@@ -0,0 +1,15 @@
package nip
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestParseNIPFile(t *testing.T) {
path := "mock/rare.nip"
rules, err := ParseNIPFile(path)
assert.NoError(t, err)
assert.NotEmpty(t, rules)
}

View File

@@ -0,0 +1,21 @@
package nip
// These stats are currently NOT supported, rules can not contain them otherwise it will return an error
var notSupportedStats = []string{
"plusmindamage",
"mindamage",
"plusmaxdamage",
"maxdamage",
"enhanceddamage",
"itemarmorpercent",
"itemmindamagepercent",
"itemslashdamage",
"itemslashdamagepercent",
"itemcrushdamage",
"itemcrushdamagepercent",
"itemthrustdamage",
"itemthrustdamagepercent",
"secondarymindamage",
"secondarymaxdamage",
"damagepercent",
}

103
pkg/nip/mock/rare.nip Normal file
View File

@@ -0,0 +1,103 @@
// rare jewels
[type] == jewel
// rare rings
[type] == ring && [quality] == rare # [fcr] >= 10 && [strength] + [dexterity] + [maxhp] + [maxmana] + [fireresist] + [lightresist] + [coldresist] + [poisonresist] >= 50
// rare amulets
[type] == amulet && [quality] == rare # [itemaddclassskills] >= 2
// rare circlets
[type] == circlet && [quality] == rare && [flag] != ethereal # [itemaddclassskills] >= 2 && [dexterity]+[strength] >= 10 && ([itemtohitpercentperlevel] >= 1 || [fhr]+[frw] >= 10 || [maxhp]+[maxmana] >= 10 ||[coldresist]+[fireresist]+[lightresist]+[poisonresist] >= 10)
[type] == circlet && [quality] == rare && [flag] == ethereal # [itemreplenishdurability] >=1 && [itemaddclassskills] >= 2 && [dexterity]+[strength] >= 10 && ([itemtohitpercentperlevel] >= 1 || [fhr]+[frw] >= 10 || [maxhp]+[maxmana] >= 10 ||[coldresist]+[fireresist]+[lightresist]+[poisonresist] >= 10)
[type] == circlet && [quality] == rare && [flag] != ethereal # [amazonskills] >= 2 && [frw] >= 10 && ([dexterity]+[strength] >= 10 || [maxhp] >= 20) && (([itemtohitpercentperlevel] >= 1) || ([dexterity]+[strength] >= 20) || ([maxhp] >= 10) || ([fhr] >= 10) || ([fcr] >= 20) || ([coldresist]+[fireresist]+[lightresist]+[poisonresist] >= 10))
[type] == circlet && [quality] == rare && [flag] == ethereal # [itemreplenishdurability] >=1 && [amazonskills] >= 2 && [frw] >= 10 && ([dexterity]+[strength] >= 10 || [maxhp] >= 20) && (([itemtohitpercentperlevel] >= 1) || ([dexterity]+[strength] >= 20) || ([maxhp] >= 10) || ([fhr] >= 10) || ([fcr] >= 10) || ([coldresist]+[fireresist]+[lightresist]+[poisonresist] >= 10))
[type] == circlet && [quality] == rare && [flag] != ethereal # [paladinskills] >= 2
[type] == circlet && [quality] == rare && [flag] == ethereal # [itemreplenishdurability] >=1 && [paladinskills] >= 2
[type] == circlet && [quality] == rare && [flag] != ethereal # [necromancerskills] >= 2
[type] == circlet && [quality] == rare && [flag] == ethereal # [itemreplenishdurability] >=1 && [necromancerskills] >= 2
[type] == circlet && [quality] == rare && [flag] != ethereal # [itemgoldbonus] >= 40 && [sockets] >= 2
[type] == circlet && [quality] == rare && [flag] == ethereal # [itemreplenishdurability] >=1 && [itemgoldbonus] >= 40 && [sockets] >= 2
// rare sorceress orbs
[type] == orb && [quality] == rare # [fcr] >= 20 && [sorceressskills]+[skilllightning] >= 5
[type] == orb && [quality] == rare # [fcr] >= 20 && [sorceressskills]+[skillfrozenorb]+[skillblizzard] >= 5
[type] == orb && [quality] == rare # [fcr] >= 20 && [sorceressskills]+[skillfireball]+[skillhydra] >= 5
// rare barb helms
[type] == primalhelm && [quality] == rare # [barbarianskills]+[skillbattleorders] >= 5
[type] == primalhelm && [quality] == rare # [itemtohitpercentperlevel] >= 1 || [sockets] == 2
[type] == primalhelm && [quality] == rare && [flag] == ethereal # [itemreplenishdurability] >=1 && ([itemtohitpercentperlevel] >= 1 || [sockets] == 2)
// rare wands
[type] == wand && [quality] == rare # [necromancerskills]+[poisonandboneskilltab]+[skillbonespear] +[skillpoisonnova] + [skillrevive] >= 4
// rare necro shields
[type] == voodooheads && [quality] == rare # [necromancerskills] == 2 && ([skillbonespear] >= 3 || [skillpoisonnova] >= 3) && ([sockets] >= 2 || ([fbr] >= 30 && ([maxhp] >= 20 || [fhr] >= 17)))
[type] == voodooheads && [quality] == rare # [necromancerskills]+[skillbonespear]+[skillpoisonnova]+[skillrevive]+[skilllowerresist]+[skilldecrepify]+[poisonandboneskilltab]+[necromancersummoningskilltab]+[skillbonespirit] >= 5
// rare helm
[type] == helm && [quality] == rare && [flag] != ethereal # [itemtohitpercentperlevel] >= 1 && [maxhp] >= 39 && [sockets] == 2 && [fhr] >= 10
[type] == helm && [quality] == rare && [flag] == ethereal # [itemtohitpercentperlevel] >= 1 && [maxhp] >= 35 && [sockets] == 2 && ([fhr] >= 10 || [itemtohitpercent] >= 5 ) && [itemreplenishdurability] >=1
[type] == helm && [quality] == rare && [flag] == ethereal # [itemreplenishdurability] >=1 && ([itemtohitpercentperlevel] >= 1 || [sockets] == 2)
// rare armor
//([name] == archonplate || [name] == mageplate || [name] == lightplate || [name] == gothicplate || [name] == ancientarmor || [name] == wirefleece)
//[type] == armor && [quality] == rare && [flag] != ethereal # [sockets] == 2 && [maxhp] >= 55 && ([dexterity] >= 9 || [strength] >= 17) && (([coldresist]+[fireresist]+[lightresist] >= 20) || [fhr] >= 17 || [hpregen] >= 4)
[type] == armor && [quality] == rare && [flag] == ethereal # [sockets] == 2 && [maxhp] >= 50 && [dexterity]+[strength] >= 7 && [itemreplenishdurability] >= 1
[type] == armor && [quality] == rare && [flag] == ethereal # [sockets] == 2 && [itemreplenishdurability] >=1
[type] == armor && [quality] == rare && [flag] == ethereal # [sockets] == 2 && [itemreplenishdurability] >=1 && ([maxhp] >= 20 || [dexterity]+[strength] >= 7 || [hpregen] >= 4)
// rare shields
[type] == shield && [quality] == rare && [flag] == ethereal # [sockets] == 2 && [itemreplenishdurability] >=1
[type] == shield && [quality] == rare && [flag] != ethereal # [fhr] >= 17 && [sockets] == 2 && [fbr] >= 30 && ([maxhp] >= 30 || [coldresist]+[fireresist]+[lightresist]+[poisonresist] >= 40)
[type] == shield && [quality] == rare && [flag] == ethereal # [fhr] >= 17 && [sockets] == 2 && [fbr] >= 30 && ([maxhp] >= 30 || [coldresist]+[fireresist]+[lightresist]+[poisonresist] >= 40) && [itemreplenishdurability] >= 1
[type] == shield && [quality] == rare && [flag] != ethereal # [maxhp] >= 30 && [coldresist]+[fireresist]+[lightresist]+[poisonresist] >= 40 && [sockets] == 2 && [fbr] >= 30
[type] == shield && [quality] == rare && [flag] == ethereal # [maxhp] >= 30 && [coldresist]+[fireresist]+[lightresist]+[poisonresist] >= 40 && [sockets] == 2 && [fbr] >= 30 && [itemreplenishdurability] >= 1
// rare gloves
[type] == gloves && [quality] == rare && [flag] == ethereal # [ias] >= 20 && ([bowandcrossbowskilltab] == 2 || [javelinandspearskilltab] == 2) && [itemreplenishdurability] >=1
[type] == gloves && [quality] == rare && [flag] != ethereal # [ias] >= 20 && ([bowandcrossbowskilltab] == 2 || [javelinandspearskilltab] == 2)
[type] == gloves && [quality] == rare && [flag] == ethereal # [itemreplenishdurability] >=1 && ([enhanceddefense] >= 200 || [ias] >= 20 || ([dexterity]+[strength] >= 10) || [hpregen] >= 5 || [passiveandmagicskilltab] == 2 || [javelinandspearskilltab] == 2 || ([coldresist]+[fireresist]+[lightresist] >= 20))
[type] == gloves && [quality] == rare && [flag] != ethereal # [dexterity]+[strength] >= 25 && [coldresist]+[fireresist]+[lightresist] >= 55
[type] == gloves && [quality] == rare && [flag] != ethereal # [ias] == 20 && ([dexterity] >= 15 || [strength] >= 15) && ([bowandcrossbowskilltab] == 2 || [javelinandspearskilltab] == 2) && [coldresist]+[fireresist]+[lightresist] >= 25
//[type] == gloves && [quality] == rare && [flag] != ethereal # [ias] == 20 && [dexterity]+[strength] >= 20 && (([coldresist]+[fireresist]+[lightresist] >= 35) || ([coldresist]+[fireresist] >= 40) || ([coldresist]+[lightresist] >= 40) || ([fireresist]+[lightresist] >= 40))
[type] == gloves && [quality] == rare && [flag] != ethereal # [ias] == 20 && [dexterity]+[strength] >= 20 && ([bowandcrossbowskilltab] == 2 || [javelinandspearskilltab] == 2)
[type] == gloves && [quality] == rare && [flag] == ethereal # [dexterity]+[strength] >= 25 && [coldresist]+[fireresist]+[lightresist] >= 60 && [itemreplenishdurability] >= 1
[type] == gloves && [quality] == rare && [flag] == ethereal # [ias] == 20 && ([dexterity] >= 15 || [strength] >= 15) && ([bowandcrossbowskilltab] == 2 || [javelinandspearskilltab] == 2) && [coldresist]+[fireresist]+[lightresist] >= 25 && [itemreplenishdurability] >= 1
//[type] == gloves && [quality] == rare && [flag] == ethereal # [ias] == 20 && [dexterity]+[strength] >= 20 && (([coldresist]+[fireresist]+[lightresist] >= 35) || ([coldresist]+[fireresist] >= 40) || ([coldresist]+[lightresist] >= 40) || ([fireresist]+[lightresist] >= 40)) && [itemreplenishdurability] >= 1
[type] == gloves && [quality] == rare && [flag] == ethereal # [ias] == 20 && [dexterity]+[strength] >= 20 && ([bowandcrossbowskilltab] == 2 || [javelinandspearskilltab] == 2) && [itemreplenishdurability] >= 1
// rare boots
[type] == boots && [quality] == rare && [flag] == ethereal # [itemreplenishdurability] >=1 && ([enhanceddefense] >= 200 || [fhr] >= 10 || [dexterity] >= 7 || [hpregen] >= 5 || [frw] >= 30 || ([coldresist]+[fireresist]+[lightresist]+[poisonresist] >= 60))
[type] == boots && [quality] == rare && [flag] != ethereal # [coldresist]+[fireresist]+[lightresist]+[poisonresist] >= 120
[type] == boots && [quality] == rare && [flag] != ethereal # [coldresist]+[fireresist]+[lightresist]+[poisonresist] >= 105 && ([fhr] >= 10 || [frw] >= 30 || [dexterity] >= 7 || [hpregen] >= 5)
//[type] == boots && [quality] == rare && [flag] != ethereal # [dexterity] >= 7 && ([frw] >= 30 || [hpregen] >= 5) && (([coldresist]+[fireresist]+[lightresist]+[poisonresist] >= 90) || ([coldresist]+[fireresist] >= 79) || ([coldresist]+[lightresist] >= 79) || ([lightresist]+[fireresist] >= 79))
//[type] == boots && [quality] == rare && [flag] != ethereal # [frw] >= 30 && ([fhr] >= 10 || [hpregen] >= 5) && (([coldresist]+[fireresist]+[lightresist]+[poisonresist] >= 90) || ([coldresist]+[fireresist] >= 79) || ([coldresist]+[lightresist] >= 79) || ([lightresist]+[fireresist] >= 79))
//[type] == boots && [quality] == rare && [flag] != ethereal # [fhr] >= 10 && ([dexterity] >= 7 || [hpregen] >= 5) && (([coldresist]+[fireresist]+[lightresist]+[poisonresist] >= 90) || ([coldresist]+[fireresist] >= 79) || ([coldresist]+[lightresist] >= 79) || ([lightresist]+[fireresist] >= 79))
[type] == boots && [quality] == rare && [flag] == ethereal # [itemreplenishdurability] >=1 && [coldresist]+[fireresist]+[lightresist]+[poisonresist] >= 120
[type] == boots && [quality] == rare && [flag] == ethereal # [itemreplenishdurability] >=1 && [coldresist]+[fireresist]+[lightresist]+[poisonresist] >= 105 && ([fhr] >= 10 || [frw] >= 30 || [dexterity] >= 7 || [hpregen] >= 5)
//[type] == boots && [quality] == rare && [flag] == ethereal # [itemreplenishdurability] >=1 && [dexterity] >= 7 && ([frw] >= 30 || [hpregen] >= 5) && (([coldresist]+[fireresist]+[lightresist]+[poisonresist] >= 90) || ([coldresist]+[fireresist] >= 79) || ([coldresist]+[lightresist] >= 79) || ([lightresist]+[fireresist] >= 79))
//[type] == boots && [quality] == rare && [flag] == ethereal # [itemreplenishdurability] >=1 && [frw] >= 30 && ([fhr] >= 10 || [hpregen] >= 5) && (([coldresist]+[fireresist]+[lightresist]+[poisonresist] >= 90) || ([coldresist]+[fireresist] >= 79) || ([coldresist]+[lightresist] >= 79) || ([lightresist]+[fireresist] >= 79))
//[type] == boots && [quality] == rare && [flag] == ethereal # [itemreplenishdurability] >=1 && [fhr] >= 10 && ([dexterity] >= 7 || [hpregen] >= 5) && (([coldresist]+[fireresist]+[lightresist]+[poisonresist] >= 90) || ([coldresist]+[fireresist] >= 79) || ([coldresist]+[lightresist] >= 79) || ([lightresist]+[fireresist] >= 79))
// rare belts
[type] == belt && [quality] == rare && [flag] == ethereal # [itemreplenishdurability] >=1 && [fhr] >= 1 && [coldresist]+[fireresist]+[lightresist] >= 25
[type] == belt && [quality] == rare && [flag] != ethereal # [fhr] == 24 && [strength] >= 25 && [maxhp] >= 45 && [coldresist]+[fireresist]+[lightresist]+[poisonresist] >= 22
[type] == belt && [quality] == rare && [flag] != ethereal # [fhr] == 24 && [strength] >= 30 && [maxhp] >= 60
[type] == belt && [quality] == rare && [flag] == ethereal # [fhr] == 24 && [strength] >= 25 && [maxhp] >= 45 && [coldresist]+[fireresist]+[lightresist]+[poisonresist] >= 22 && [itemreplenishdurability] >= 1
[type] == belt && [quality] == rare && [flag] == ethereal # [fhr] == 24 && [strength] >= 29 && [maxhp] >= 55 && [itemreplenishdurability] >= 1
// scepters
[type] == scepter && [quality] == rare && [flag] == ethereal # [itemreplenishdurability] >= 1 && [skillfistoftheheavens] + [paladinskills] + [palicombatskilltab] + [sockets] + [skillblessedhammer] + [skillconcentration]+[skillholyshield] >= 5
[type] == scepter && [quality] == rare && [flag] != ethereal # [skillfistoftheheavens] + [paladinskills] + [palicombatskilltab] + [sockets] + [skillblessedhammer] + [skillconcentration]+[skillholyshield] >= 5
// polearms
[type] == polearm && [quality] == rare && [flag] == ethereal # [itemtohitperlevel] >= 1

View File

@@ -1,175 +0,0 @@
package nip
import (
"errors"
"regexp"
"strconv"
"strings"
)
var (
errEmptyLine = errors.New("empty line")
operandRegex = regexp.MustCompile(`(\|{2}|\&{2})`)
comparableRegex = regexp.MustCompile(`(\={2}|\<\=|\>\=|\>|\<|\!\=)`)
propertyNameRegex = regexp.MustCompile(`\[(.*)\]`)
)
func ParseLine(line string, filename string, linenum int) (Rule, error) {
line = lineCleanup(line)
if line == "" {
return Rule{}, errEmptyLine
}
lineSplit := strings.Split(line, "#")
properties, err := buildGroups(lineSplit[0])
if err != nil {
return Rule{}, err
}
var stats []Group
if len(lineSplit) > 1 && strings.TrimSpace(lineSplit[1]) != "" {
stats, err = buildGroups(lineSplit[1])
if err != nil {
return Rule{}, err
}
}
var maxQuantity []Group
if len(lineSplit) > 2 && strings.TrimSpace(lineSplit[2]) != "" {
maxQuantity, err = buildGroups(lineSplit[2])
if err != nil {
return Rule{}, err
}
}
return Rule{
Properties: properties,
Stats: stats,
MaxQuantity: maxQuantity,
Filename: filename,
LineNumber: linenum,
Enabled: true,
}, nil
}
func lineCleanup(line string) string {
l := strings.Split(line, "//")
line = strings.TrimSpace(l[0])
return strings.ToLower(line)
}
func buildGroups(block string) ([]Group, error) {
operands := operandRegex.FindAllString(block, -1)
groupLines := operandRegex.Split(block, -1)
operands, groupLines = reGroupParentheses(operands, groupLines)
if len(operands) != len(groupLines)-1 {
return nil, errors.New("syntax error")
}
groups := make([]Group, 0)
for i, groupLine := range groupLines {
cmp, err := buildGroupComparables(groupLine)
if err != nil {
return nil, err
}
group := Group{Comparable: cmp}
if i < len(operands) {
group.Operand = Operand(operands[i])
}
groups = append(groups, group)
}
return groups, nil
}
func reGroupParentheses(operands, groups []string) ([]string, []string) {
cleanOperands := make([]string, 0)
cleanGroups := make([]string, 0)
inGroup := false
currentGroupContent := ""
for i, group := range groups {
group = strings.TrimSpace(group)
if strings.Contains(group, "(") && strings.Contains(group, ")") {
group = strings.ReplaceAll(group, "(", "")
group = strings.ReplaceAll(group, ")", "")
cleanGroups = append(cleanGroups, group)
if i < len(operands) {
cleanOperands = append(cleanOperands, operands[i])
}
continue
}
if strings.Contains(group, "(") || (inGroup && !strings.Contains(group, ")")) {
inGroup = true
currentGroupContent += strings.ReplaceAll(group, "(", "") + operands[i]
continue
}
if strings.Contains(group, ")") {
inGroup = false
currentGroupContent += strings.ReplaceAll(group, ")", "")
cleanGroups = append(cleanGroups, currentGroupContent)
currentGroupContent = ""
if i < len(operands) {
cleanOperands = append(cleanOperands, operands[i])
}
continue
}
if !inGroup {
cleanGroups = append(cleanGroups, group)
if i < len(operands) {
cleanOperands = append(cleanOperands, operands[i])
}
}
}
return cleanOperands, cleanGroups
}
func buildGroupComparables(group string) ([]Comparable, error) {
operands := operandRegex.FindAllString(group, -1)
properties := operandRegex.Split(group, -1)
if len(properties)-1 != len(operands) {
return nil, errors.New("invalid NIP block")
}
comparables := make([]Comparable, 0)
for i, prop := range properties {
comparisonSymbol := comparableRegex.FindString(prop)
values := comparableRegex.Split(prop, -1)
if len(values) != 2 {
return nil, errors.New("invalid NIP line")
}
aggregatedProperties := strings.Split(values[0], "+")
for propNum, prop := range aggregatedProperties {
propertyNameGr := propertyNameRegex.FindStringSubmatch(prop)
propertyName := strings.TrimSpace(propertyNameGr[1])
cmp := Comparable{
Keyword: propertyName,
Comparison: Operand(strings.TrimSpace(comparisonSymbol)),
}
if len(aggregatedProperties) > 0 && propNum < len(aggregatedProperties)-1 {
cmp.Operand = OperandOr
} else if i < len(operands) {
cmp.Operand = Operand(operands[i])
}
value := strings.TrimSpace(values[1])
if val, err := strconv.Atoi(value); err == nil {
cmp.ValueInt = val
} else {
cmp.ValueString = value
}
comparables = append(comparables, cmp)
}
}
return comparables, nil
}

View File

@@ -1,96 +0,0 @@
package nip
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
const nipLine = "[type] == boots && [quality] == rare # [frw] >= 10 && [fireresist] >= 10 && ([lightresist]+[coldresist] >= 10 && [dexterity] >= 1 && [fireresist]+[poisonresist] >= 10) // this is a comment"
func Test_parseLine(t *testing.T) {
rules, err := ParseLine(nipLine, "dummy", 0)
require.NoError(t, err)
expected := Rule{
Properties: []Group{
{
Comparable: []Comparable{
{
Keyword: "type",
Comparison: OperandEqual,
ValueString: "boots",
},
},
Operand: OperandAnd,
},
{
Comparable: []Comparable{
{
Keyword: "quality",
Comparison: OperandEqual,
ValueString: "rare",
},
},
},
},
Stats: []Group{
{
Comparable: []Comparable{
{
Keyword: "frw",
Comparison: OperandGreaterOrEqualTo,
ValueInt: 10,
},
},
Operand: OperandAnd,
},
{
Comparable: []Comparable{
{
Keyword: "fireresist",
Comparison: OperandGreaterOrEqualTo,
ValueInt: 10,
},
},
Operand: OperandAnd,
},
{
Comparable: []Comparable{
{
Keyword: "lightresist",
Comparison: OperandGreaterOrEqualTo,
ValueInt: 10,
Operand: OperandOr,
},
{
Keyword: "coldresist",
Comparison: OperandGreaterOrEqualTo,
ValueInt: 10,
Operand: OperandAnd,
},
{
Keyword: "dexterity",
Comparison: OperandGreaterOrEqualTo,
ValueInt: 1,
Operand: OperandAnd,
},
{
Keyword: "fireresist",
Comparison: OperandGreaterOrEqualTo,
ValueInt: 10,
Operand: OperandOr,
},
{
Keyword: "poisonresist",
Comparison: OperandGreaterOrEqualTo,
ValueInt: 10,
},
},
},
},
}
assert.Equal(t, expected, rules)
}

View File

@@ -1,28 +1,14 @@
package nip
const (
// Properties
PropertyType = "type"
PropertyName = "name"
PropertyClass = "class"
PropertyQuality = "quality"
PropertyFlag = "flag"
PropertyLevel = "level"
PropertyPrefix = "prefix"
PropertySuffix = "suffix"
import "github.com/hectorgimenez/d2go/pkg/data/item"
// Types
TypeNormal = "normal"
TypeExceptional = "exceptional"
TypeElite = "elite"
// Quality
QualityLowQuality = "lowquality"
QualityNormal = "normal"
QualitySuperior = "superior"
QualityMagic = "magic"
QualitySet = "set"
QualityRare = "rare"
QualityUnique = "unique"
QualityCrafted = "crafted"
)
var quality = map[string]item.Quality{
"LowQuality": item.QualityLowQuality,
"Normal": item.QualityNormal,
"Superior": item.QualitySuperior,
"Magic": item.QualityMagic,
"Set": item.QualitySet,
"Rare": item.QualityRare,
"Unique": item.QualityUnique,
"Crafted": item.QualityCrafted,
}

View File

@@ -1,38 +1,223 @@
package nip
const (
OperandEqual Operand = "=="
OperandGreaterThan Operand = ">"
OperandGreaterOrEqualTo Operand = ">="
OperandLessThan Operand = "<"
OperandLessThanOrEqualTo Operand = "<="
OperandNotEqualTo Operand = "!="
OperandAnd Operand = "&&"
OperandOr Operand = "||"
OperandNone Operand = ""
import (
"fmt"
"math"
"regexp"
"slices"
"strconv"
"strings"
"github.com/expr-lang/expr"
"github.com/hectorgimenez/d2go/pkg/data"
"github.com/hectorgimenez/d2go/pkg/data/item"
"github.com/hectorgimenez/d2go/pkg/data/stat"
)
var (
fixedPropsRegexp = regexp.MustCompile(`(\[(type|quality|class|name|flag|color)]\s*(<=|<|>|>=|!=|==)\s*([a-zA-Z]+))`)
statsRegexp = regexp.MustCompile(`\[(.*?)]`)
maxQtyRegexp = regexp.MustCompile(`(\[maxquantity]\s*(<=|<|>|>=|!=|==)\s*([0-9]+))`)
)
type Rule struct {
Properties []Group
Stats []Group
MaxQuantity []Group
RawLine string // Original line, don't use it for evaluation
Stages [3]string
Filename string
LineNumber int
Enabled bool
maxQuantity int
}
type Keyword string
type Operand string
type Rules []Rule
type Comparable struct {
Keyword string
Comparison Operand
ValueInt int
ValueString string
Operand
type RuleResult int
var (
RuleResultFullMatch RuleResult = 1
RuleResultPartial RuleResult = 2
RuleResultNoMatch RuleResult = 3
)
func (r Rules) EvaluateAll(it data.Item) (Rule, RuleResult) {
bestMatch := RuleResultNoMatch
bestMatchingRule := Rule{}
for _, rule := range r {
if rule.Enabled {
result, err := rule.Evaluate(it)
if err != nil {
continue
}
if result == RuleResultFullMatch {
return rule, result
}
if result == RuleResultPartial {
bestMatch = result
bestMatchingRule = rule
}
}
}
return bestMatchingRule, bestMatch
}
type Group struct {
Comparable []Comparable
Operand
func New(rawRule string, filename string, lineNumber int) (Rule, error) {
rule := sanitizeLine(rawRule)
// Check for not supported stats
for _, prop := range statsRegexp.FindAllStringSubmatch(rule, -1) {
if slices.Contains(notSupportedStats, prop[1]) {
return Rule{}, fmt.Errorf("property %s is not supported, please remove it", prop[1])
}
}
// Try to get the maxquantity value and purge it from the rule, we can not evaluate it
maxQuantity := 0
for _, prop := range maxQtyRegexp.FindAllStringSubmatch(rule, -1) {
mxQty, err := strconv.Atoi(prop[3])
if err != nil {
return Rule{}, fmt.Errorf("error parsing maxquantity value %s: %w", prop[3], err)
}
maxQuantity = mxQty
rule = strings.ReplaceAll(rule, prop[0], "")
}
// Sanitize again, just in case we messed up the rule while parsing maxquantity
rule = sanitizeLine(rule)
if rule == "" {
return Rule{}, ErrEmptyRule
}
stages := [3]string{}
for i, stg := range strings.Split(rule, "#") {
stages[i] = strings.TrimSpace(stg)
}
return Rule{
RawLine: rawRule,
Stages: stages,
Filename: filename,
LineNumber: lineNumber,
Enabled: true,
maxQuantity: maxQuantity,
}, nil
}
func (r Rule) Evaluate(it data.Item) (RuleResult, error) {
stage1 := r.Stages[0]
baseProperties := fixedPropsRegexp.FindAllStringSubmatch(stage1, -1)
for _, prop := range baseProperties {
switch prop[2] {
case "type":
partialReplace := strings.ReplaceAll(prop[0], fmt.Sprintf("[%s]", prop[2]), fmt.Sprintf("%d", typeAliases[it.TypeAsString()]))
partialReplace = strings.ReplaceAll(partialReplace, prop[4], fmt.Sprintf("%d", typeAliases[prop[4]]))
stage1 = strings.ReplaceAll(stage1, prop[0], partialReplace)
case "quality":
partialReplace := strings.ReplaceAll(prop[0], fmt.Sprintf("[%s]", prop[2]), fmt.Sprintf("%d", it.Quality))
partialReplace = strings.ReplaceAll(partialReplace, prop[4], fmt.Sprintf("%d", qualityAliases[prop[4]]))
stage1 = strings.ReplaceAll(stage1, prop[0], partialReplace)
case "class":
partialReplace := strings.ReplaceAll(prop[0], fmt.Sprintf("[%s]", prop[2]), fmt.Sprintf("%d", it.Desc().Tier()))
partialReplace = strings.ReplaceAll(partialReplace, prop[4], fmt.Sprintf("%d", classAliases[prop[4]]))
stage1 = strings.ReplaceAll(stage1, prop[0], partialReplace)
case "name":
partialReplace := strings.ReplaceAll(prop[0], fmt.Sprintf("[%s]", prop[2]), fmt.Sprintf("%d", item.GetIDByName(string(it.Name))))
partialReplace = strings.ReplaceAll(partialReplace, prop[4], fmt.Sprintf("%d", item.GetIDByName(prop[4])))
stage1 = strings.ReplaceAll(stage1, prop[0], partialReplace)
case "flag":
partialReplace := strings.ReplaceAll(prop[0], fmt.Sprintf("[%s]", prop[2]), fmt.Sprintf("%d", map[bool]int{true: 1, false: 0}[it.Ethereal]))
partialReplace = strings.ReplaceAll(partialReplace, prop[4], fmt.Sprintf("%d", 1))
stage1 = strings.ReplaceAll(stage1, prop[0], partialReplace)
case "color":
// TODO: Not supported yet
}
}
// Let's evaluate first stage
stage1Result, err := expr.Eval(stage1, nil)
if err != nil {
return RuleResultNoMatch, fmt.Errorf("error evaluating rule: %w", err)
}
// Let's go with other stats now
// TODO: properties are missing (enhanceddefense, enhanceddamage, etc)
stage2 := r.Stages[1]
stage2Result := true
if stage2 != "" {
for _, statName := range statsRegexp.FindAllStringSubmatch(stage2, -1) {
statData, found := statAliases[statName[1]]
if !found {
return RuleResultNoMatch, fmt.Errorf("property %s is not valid or not supported", statName[1])
}
// Exception for enhanceddefense, is not accurate
if statName[0] == "[enhanceddefense]" {
enhancedDefense := r.calculateEnhancedDefense(it)
stage2 = strings.ReplaceAll(stage2, statName[0], fmt.Sprintf("%d", enhancedDefense))
continue
}
layer := 0
if len(statData) > 1 {
layer = statData[1]
}
itemStat, _ := it.FindStat(stat.ID(statData[0]), layer)
// By default, value will be 0 is stat is not found, it's okay for evaluation purposes.
stage2 = strings.ReplaceAll(stage2, statName[0], fmt.Sprintf("%d", itemStat.Value))
}
res, err := expr.Eval(stage2, nil)
if err != nil {
return RuleResultNoMatch, fmt.Errorf("error evaluating rule: %w", err)
}
stage2Result = res.(bool)
}
// 100% rule match, we can return here
if stage1Result.(bool) && stage2Result {
return RuleResultFullMatch, nil
}
// If Stage 1 matches and the item is NOT identified, let's return a partial match, we can not be 100% sure if all the stats are matching
if stage1Result.(bool) && !it.Identified {
return RuleResultPartial, nil
}
return RuleResultNoMatch, nil
}
// MaxQuantity returns the maximum quantity of items that character can have, 0 means no limit
func (r Rule) MaxQuantity() int {
return r.maxQuantity
}
func (r Rule) calculateEnhancedDefense(i data.Item) int {
defenseStat, found := i.FindStat(stat.Defense, 0)
// This is special case for weapons and other stuff without base Defense and with EnhancedDefense, we can return it directly
// since EnhancedDefense it's a stat.
if !found {
if edStat, edStatFound := i.FindStat(stat.EnhancedDefense, 0); edStatFound {
return edStat.Value
}
}
// Make it only work for white base items
if i.Quality != item.QualitySuperior {
return 0
}
if i.TypeAsString() != "armor" {
return 0
}
itemTypeMaxDefense := i.Desc().MaxDefense
if defenseStat.Value <= itemTypeMaxDefense {
return 0
}
res := float64(defenseStat.Value-itemTypeMaxDefense) / float64(itemTypeMaxDefense) * 100
return int(math.Ceil(res))
}

213
pkg/nip/rule_test.go Normal file
View File

@@ -0,0 +1,213 @@
package nip
import (
"testing"
"github.com/hectorgimenez/d2go/pkg/data"
"github.com/hectorgimenez/d2go/pkg/data/item"
"github.com/hectorgimenez/d2go/pkg/data/stat"
"github.com/stretchr/testify/require"
)
func TestRule_Evaluate(t *testing.T) {
type fields struct {
RawLine string
Filename string
LineNumber int
Enabled bool
}
type args struct {
item data.Item
}
tests := []struct {
name string
fields fields
args args
want RuleResult
wantErr bool
}{
{
name: "Basic rule with posion dmg, ethereal is not specified as a condition so it should be ignored",
fields: fields{
RawLine: "[name] == smallcharm && [quality] == magic # (([poisonlength]*25)*[poisonmaxdam])/256 >= 123",
Filename: "test.nip",
LineNumber: 1,
Enabled: true,
},
args: args{
item: data.Item{
Name: "SmAlLCharM",
Quality: item.QualityMagic,
Ethereal: true,
Stats: []stat.Data{
{ID: stat.PoisonLength, Value: 20},
{ID: stat.PoisonMaxDamage, Value: 100},
},
},
},
want: RuleResultFullMatch,
},
{
name: "Complex rule with flags",
fields: fields{
RawLine: "[type] == armor && [quality] <= superior && [flag] != ethereal # ([itemmaxdurabilitypercent] == 0 || [itemmaxdurabilitypercent] == 15) && ([sockets] == 0 || [sockets] == 3 || [sockets] == 4)",
Filename: "test.nip",
LineNumber: 1,
Enabled: true,
},
args: args{
item: data.Item{
Name: "mageplate",
Quality: item.QualitySuperior,
Ethereal: false,
Stats: []stat.Data{
{ID: stat.MaxDurabilityPercent, Value: 15},
{ID: stat.NumSockets, Value: 4},
},
},
},
want: RuleResultFullMatch,
},
{
name: "Armor with +3 Sorc skills",
fields: fields{
RawLine: "[type] == armor # [sorceressskills] >= 3",
Enabled: true,
},
args: args{
item: data.Item{
Name: "mageplate",
Stats: []stat.Data{
{ID: stat.AddClassSkills, Value: 3, Layer: 1},
},
},
},
want: RuleResultFullMatch,
},
{
name: "Armor with +3 Glacial Spike",
fields: fields{
RawLine: "[type] == armor # [skillglacialspike] >= 3",
Enabled: true,
},
args: args{
item: data.Item{
Name: "mageplate",
Stats: []stat.Data{
{ID: stat.SingleSkill, Value: 3, Layer: 55},
},
},
},
want: RuleResultFullMatch,
},
{
name: "Ensure [color] returns error, not supported yet",
fields: fields{
RawLine: "[type] == armor && [color] == 1000 && [quality] == magic",
Enabled: true,
},
args: args{
item: data.Item{
Name: "mageplate",
Quality: item.QualityMagic,
},
},
wantErr: true,
},
{
name: "Unid item matching base stats should return partial match",
fields: fields{
RawLine: "[type] == armor && [quality] == magic # [defense] == 200",
Enabled: true,
},
args: args{
item: data.Item{
Identified: false,
Name: "mageplate",
Quality: item.QualityMagic,
},
},
want: RuleResultPartial,
},
{
name: "Basic rule without stats or maxquantity",
fields: fields{
RawLine: "[type] == armor && [quality] == magic # #",
Enabled: true,
},
args: args{
item: data.Item{
Identified: false,
Name: "mageplate",
Quality: item.QualityMagic,
},
},
want: RuleResultFullMatch,
},
{
name: "Basic rule for a white superior item with enhanceddefense",
fields: fields{
RawLine: "[type] == armor && [quality] == superior # [enhanceddefense] >= 15 #",
Enabled: true,
},
args: args{
item: data.Item{
Identified: true,
ID: 373,
Name: "mageplate",
Quality: item.QualitySuperior,
Stats: []stat.Data{
{ID: stat.Defense, Value: 301},
},
},
},
want: RuleResultFullMatch,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r, err := New(tt.fields.RawLine, tt.fields.Filename, tt.fields.LineNumber)
require.NoError(t, err)
got, err := r.Evaluate(tt.args.item)
if !tt.wantErr {
require.NoError(t, err)
require.Equal(t, tt.want, got)
} else {
require.Error(t, err)
}
})
}
}
func TestNew(t *testing.T) {
type args struct {
rawRule string
filename string
lineNumber int
}
tests := []struct {
name string
args args
want Rule
wantErr bool
}{
{
name: "Enhanced Damage should throw an error",
args: args{
rawRule: "[type] == armor # [enhanceddamage] >= 15",
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := New(tt.args.rawRule, tt.args.filename, tt.args.lineNumber)
if tt.wantErr {
require.Error(t, err)
} else {
require.NoError(t, err)
require.NotNil(t, got)
}
})
}
}