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.") } }