OpenDiablo2/d2core/d2stats/diablo2stats/stat_test.go

420 lines
12 KiB
Go

package diablo2stats
import (
"fmt"
"testing"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2records"
)
const (
errStr string = "incorrect description string format for stat"
errFmt string = "%v:\n\tDescFnID: %v\n\tKey: %v\n\tVal: %+v\n\texpected: %v\n\tgot: %v\n\n"
)
// nolint:gochecknoglobals // just a test
var itemStatCosts = map[string]*d2records.ItemStatCostRecord{
"strength": {
Name: "strength",
DescFnID: 1,
DescVal: int(descValPrefix),
DescStrPos: "to Strength",
DescStrNeg: "to Strength",
},
"dexterity": {
Name: "dexterity",
DescFnID: 1,
DescVal: int(descValPrefix),
DescStrPos: "to Dexterity",
DescStrNeg: "to Dexterity",
},
"vitality": {
Name: "vitality",
DescFnID: 1,
DescVal: int(descValPrefix),
DescStrPos: "to Vitality",
DescStrNeg: "to Vitality",
},
"energy": {
Name: "energy",
DescFnID: 1,
DescVal: int(descValPrefix),
DescStrPos: "to Energy",
DescStrNeg: "to Energy",
},
"hpregen": {
Name: "hpregen",
DescFnID: 1,
DescVal: int(descValPostfix),
DescStrPos: "Replenish Life",
DescStrNeg: "Drain Life",
},
"toblock": {
Name: "toblock",
DescFnID: 2,
DescVal: int(descValPrefix),
DescStrPos: "Increased Chance of Blocking",
DescStrNeg: "Increased Chance of Blocking",
},
"item_absorblight_percent": {
Name: "item_absorblight_percent",
DescFnID: 2,
DescVal: int(descValPostfix),
DescStrPos: "Lightning Absorb",
DescStrNeg: "Lightning Absorb",
},
"item_restinpeace": {
Name: "item_restinpeace",
DescFnID: 3,
DescVal: int(descValHide),
DescStrPos: "Slain Monsters Rest in Peace",
DescStrNeg: "Slain Monsters Rest in Peace",
},
"normal_damage_reduction": {
Name: "normal_damage_reduction",
DescFnID: 3,
DescVal: int(descValPostfix),
DescStrPos: "Damage Reduced by",
DescStrNeg: "Damage Reduced by",
},
"poisonresist": {
Name: "poisonresist",
DescFnID: 4,
DescVal: int(descValPostfix),
DescStrPos: "Poison Resist",
DescStrNeg: "Poison Resist",
},
"item_fastermovevelocity": {
Name: "item_fastermovevelocity",
DescFnID: 4,
DescVal: int(descValPrefix),
DescStrPos: "Faster Run/Walk",
DescStrNeg: "Faster Run/Walk",
},
"item_howl": {
Name: "item_howl",
DescFnID: 5,
DescVal: int(descValPostfix),
DescStrPos: "Hit Causes Monster to Flee",
DescStrNeg: "Hit Causes Monster to Flee",
},
"item_hp_perlevel": {
Name: "item_hp_perlevel",
DescFnID: 6,
DescVal: int(descValPrefix),
DescStrPos: "to Life",
DescStrNeg: "to Life",
DescStr2: "(Based on Character Level)",
},
"item_resist_ltng_perlevel": {
Name: "item_resist_ltng_perlevel",
DescFnID: 7,
DescVal: int(descValPostfix),
DescStrPos: "Lightning Resist",
DescStrNeg: "Lightning Resist",
DescStr2: "(Based on Character Level)",
},
"item_find_magic_perlevel": {
Name: "item_find_magic_perlevel",
DescFnID: 7,
DescVal: int(descValPrefix),
DescStrPos: "Better Chance of Getting Magic Items",
DescStrNeg: "Better Chance of Getting Magic Items",
DescStr2: "(Based on Character Level)",
},
"item_armorpercent_perlevel": {
Name: "item_armorpercent_perlevel",
DescFnID: 8,
DescVal: int(descValPrefix),
DescStrPos: "Enhanced Defense",
DescStrNeg: "Enhanced Defense",
DescStr2: "(Based on Character Level)",
},
"item_regenstamina_perlevel": {
Name: "item_regenstamina_perlevel",
DescFnID: 8,
DescVal: int(descValPostfix),
DescStrPos: "Heal Stamina Plus",
DescStrNeg: "Heal Stamina Plus",
DescStr2: "(Based on Character Level)",
},
"item_thorns_perlevel": {
Name: "item_thorns_perlevel",
DescFnID: 9,
DescVal: int(descValPostfix),
DescStrPos: "Attacker Takes Damage of",
DescStrNeg: "Attacker Takes Damage of",
DescStr2: "(Based on Character Level)",
},
"item_replenish_durability": {
Name: "item_replenish_durability",
DescFnID: 11,
DescVal: int(descValPrefix),
DescStrPos: "Repairs %v durability per second",
DescStrNeg: "Repairs %v durability per second",
DescStr2: "",
},
"item_stupidity": {
Name: "item_stupidity",
DescFnID: 12,
DescVal: int(descValPostfix),
DescStrPos: "Hit Blinds Target",
DescStrNeg: "Hit Blinds Target",
},
"item_addclassskills": {
Name: "item_addclassskills",
DescFnID: 13,
DescVal: int(descValPrefix),
},
"item_addskill_tab": {
Name: "item_addskill_tab",
DescFnID: 14,
DescVal: int(descValPrefix),
},
"item_skillonattack": {
Name: "item_skillonattack",
DescFnID: 15,
DescVal: int(descValPrefix),
DescStrPos: "%d%% Chance to cast level %d %s on attack",
DescStrNeg: "%d%% Chance to cast level %d %s on attack",
},
"item_aura": {
Name: "item_aura",
DescFnID: 16,
DescVal: int(descValPrefix),
DescStrPos: "Level %d %s Aura When Equipped",
DescStrNeg: "Level %d %s Aura When Equipped",
},
"item_fractionaltargetac": {
Name: "item_fractionaltargetac",
DescFnID: 20,
DescVal: int(descValPrefix),
DescStrPos: "Target Defense",
DescStrNeg: "Target Defense",
},
"attack_vs_montype": {
Name: "item_fractionaltargetac",
DescFnID: 22,
DescVal: int(descValPrefix),
DescStrPos: "to Attack Rating versus",
DescStrNeg: "to Attack Rating versus",
},
"item_reanimate": {
Name: "item_reanimate",
DescFnID: 23,
DescVal: int(descValPostfix),
DescStrPos: "Reanimate as:",
DescStrNeg: "Reanimate as:",
},
"item_charged_skill": {
Name: "item_charged_skill",
DescFnID: 24,
DescVal: int(descValPostfix),
DescStrPos: "(%d/%d Charges)",
DescStrNeg: "(%d/%d Charges)",
},
"item_singleskill": {
Name: "item_singleskill",
DescFnID: 27,
DescVal: int(descValPostfix),
DescStrPos: "(%d/%d Charges)",
DescStrNeg: "(%d/%d Charges)",
},
"item_nonclassskill": {
Name: "item_nonclassskill",
DescFnID: 28,
DescVal: int(descValPostfix),
DescStrPos: "(%d/%d Charges)",
DescStrNeg: "(%d/%d Charges)",
},
}
// nolint:gochecknoglobals // just a test
var skillDetails = map[int]*d2records.SkillRecord{
37: {Skill: "Warmth"},
64: {Skill: "Frozen Orb"},
}
// nolint:gochecknoglobals // just a test
var monStats = map[string]*d2records.MonStatRecord{
"Specter": {NameString: "Specter", ID: 40},
}
// nolint:gochecknoglobals // just a test
var charStats = map[d2enum.Hero]*d2records.CharStatRecord{
d2enum.HeroPaladin: {
Class: d2enum.HeroPaladin,
SkillStrAll: "to Paladin Skill Levels",
SkillStrClassOnly: "(Paladin Only)",
SkillStrTab: [3]string{
"+%d to Combat Skills",
"+%d to Offensive Auras",
"+%d to Defensive Auras",
},
},
}
// nolint:gochecknoglobals // just a test
var testAssetManager2 *d2asset.AssetManager
// nolint:gochecknoglobals // just a test
var testStatFactory2 *StatFactory
func TestSetup_Stat(t *testing.T) {
testAssetManager2 = &d2asset.AssetManager{}
testAssetManager2.Records = &d2records.RecordManager{}
testStatFactory2, _ = NewStatFactory(testAssetManager2)
testAssetManager2.Records.Item.Stats = itemStatCosts
testAssetManager2.Records.Character.Stats = charStats
testAssetManager2.Records.Skill.Details = skillDetails
testAssetManager2.Records.Monster.Stats = monStats
}
func TestStat_Clone(t *testing.T) {
s1 := testStatFactory2.NewStat("strength", 5)
s2 := s1.Clone()
// make sure the stats are distinct
if &s1 == &s2 {
t.Errorf("stats share the same pointer %d == %d", &s1, &s2)
}
// make sure the stat values are unique
vs1, vs2 := s1.Values(), s2.Values()
if &vs1 == &vs2 {
t.Errorf("stat values share the same pointer %d == %d", &s1, &s2)
}
s2.Values()[0].SetInt(6)
v1, v2 := s1.Values()[0].Int(), s2.Values()[0].Int()
// make sure the value ranges are distinct
if v1 == v2 {
t.Errorf("clones should not share stat values")
}
}
func TestStat_Descriptions(t *testing.T) {
tests := []struct {
recordKey string
vals []float64
expect string
}{
// DescFn1
{"strength", []float64{31}, "+31 to Strength"},
{"hpregen", []float64{20}, "Replenish Life +20"},
{"hpregen", []float64{-8}, "Drain Life -8"},
// DescFn2
{"toblock", []float64{16}, "+16% Increased Chance of Blocking"},
{"item_absorblight_percent", []float64{10}, "Lightning Absorb +10%"},
// DescFn3
{"normal_damage_reduction", []float64{25}, "Damage Reduced by 25"},
{"item_restinpeace", []float64{25}, "Slain Monsters Rest in Peace"},
// DescFn4
{"poisonresist", []float64{25}, "Poison Resist +25%"},
{"item_fastermovevelocity", []float64{25}, "+25% Faster Run/Walk"},
// DescFn5
{"item_howl", []float64{25}, "Hit Causes Monster to Flee 25%"},
// DescFn6
{"item_hp_perlevel", []float64{25}, "+25 to Life (Based on Character Level)"},
// DescFn7
{"item_resist_ltng_perlevel", []float64{25},
"Lightning Resist +25% (Based on Character Level)"},
{"item_find_magic_perlevel", []float64{25}, "+25% Better Chance of Getting Magic Items (" +
"Based on Character Level)"},
// DescFn8
{"item_armorpercent_perlevel", []float64{25},
"+25% Enhanced Defense (Based on Character Level)"},
{"item_regenstamina_perlevel", []float64{25},
"Heal Stamina Plus +25% (Based on Character Level)"},
// DescFn9
{"item_thorns_perlevel", []float64{25}, "Attacker Takes Damage of 25 (" +
"Based on Character Level)"},
// DescFn11
{"item_replenish_durability", []float64{2}, "Repairs 2 durability per second"},
// DescFn12
{"item_stupidity", []float64{5}, "Hit Blinds Target +5"},
// DescFn13
{"item_addclassskills", []float64{5, 3}, "+5 to Paladin Skill Levels"},
// DescFn14
{"item_addskill_tab", []float64{5, 3, 0}, "+5 to Combat Skills (Paladin Only)"},
{"item_addskill_tab", []float64{5, 3, 1}, "+5 to Offensive Auras (Paladin Only)"},
{"item_addskill_tab", []float64{5, 3, 2}, "+5 to Defensive Auras (Paladin Only)"},
// DescFn15
{"item_skillonattack", []float64{5, 7, 64},
"5% Chance to cast level 7 Frozen Orb on attack"},
// DescFn16
{"item_aura", []float64{3, 37}, "Level 3 Warmth Aura When Equipped"},
// DescFn20
{"item_fractionaltargetac", []float64{-25}, "-25% Target Defense"},
// DescFn22
{"attack_vs_montype", []float64{25, 40}, "25% to Attack Rating versus Specter"},
// DescFn23
{"item_reanimate", []float64{25, 40}, "25% Reanimate as: Specter"},
// DescFn24
{"item_charged_skill", []float64{25, 64, 20, 19}, "Level 25 Frozen Orb (19/20 Charges)"},
// DescFn27
{"item_singleskill", []float64{25, 64, 3}, "+25 to Frozen Orb (Paladin Only)"},
// DescFn28
{"item_nonclassskill", []float64{25, 64}, "+25 to Frozen Orb"},
}
for idx := range tests {
test := tests[idx]
key := test.recordKey
record := itemStatCosts[key]
expect := test.expect
stat := testStatFactory2.NewStat(key, test.vals...)
if got := stat.String(); got != expect {
t.Errorf(errFmt, errStr, record.DescFnID, test.recordKey, test.vals, expect, got)
} else {
success := "[Desc Func %d][%s %+v] %s"
success = fmt.Sprintf(success, record.DescFnID, record.Name, test.vals, got)
fmt.Println(success)
}
}
}
func TestDiablo2Stat_Combine(t *testing.T) {
a := testStatFactory2.NewStat("item_nonclassskill", 25, 64) // "+25 to Frozen Orb"
b := testStatFactory2.NewStat("item_nonclassskill", 5, 64) // "+5 to Frozen Orb"
c, err := a.Combine(b)
if err != nil || c.String() != "+30 to Frozen Orb" {
t.Errorf("stats combination failed\r%s", err)
}
d := testStatFactory2.NewStat("item_nonclassskill", 5, 37) // "+5 to Warmth"
_, err = c.Combine(d)
if err == nil {
t.Error("stats were combined when they should not have been.")
}
}