85 lines
3.2 KiB
Python
85 lines
3.2 KiB
Python
import pytest
|
||
|
||
from tft.model import statsheet
|
||
|
||
|
||
def unit(spell=None, scaling="ap", **overrides):
|
||
stats = {"hp": 700, "armor": 30, "magicResist": 30, "damage": 50,
|
||
"attackSpeed": 0.7, "mana": 60, "initialMana": 10,
|
||
"critChance": 0.25, "critMultiplier": 1.4}
|
||
stats.update(overrides)
|
||
return {"stats": stats, "spell_damage": spell, "spell_scaling": scaling}
|
||
|
||
|
||
ITEMS = {
|
||
"sword": {"effects": {"AD": 0.35, "CritChance": 35.0}},
|
||
"vest": {"effects": {"Armor": 20.0}},
|
||
"rod": {"effects": {"AP": 10.0}},
|
||
"unmodeled": {"effects": {"BurnPercent": 1.0}},
|
||
}
|
||
|
||
|
||
def test_star_scaling():
|
||
one = statsheet.unit_stats(unit(), 1, [], ITEMS, None, None)
|
||
two = statsheet.unit_stats(unit(), 2, [], ITEMS, None, None)
|
||
assert two["ehp"] == pytest.approx(one["ehp"] * 1.8)
|
||
assert two["dps"] == pytest.approx(one["dps"] * 1.5)
|
||
|
||
|
||
def test_spell_damage_uses_star_index_and_ap():
|
||
spell = [0, 200, 300, 450, 0, 0, 0]
|
||
bare = statsheet.unit_stats(unit(spell=spell), 1, [], ITEMS, None, None)
|
||
with_rod = statsheet.unit_stats(unit(spell=spell), 1, ["rod"], ITEMS, None, None)
|
||
no_spell = statsheet.unit_stats(unit(), 1, [], ITEMS, None, None)
|
||
assert bare["dps"] > no_spell["dps"]
|
||
assert with_rod["dps"] > bare["dps"] # +10 AP skaliert den Spell
|
||
|
||
|
||
def test_items_change_profile():
|
||
base = statsheet.unit_stats(unit(), 1, [], ITEMS, None, None)
|
||
sword = statsheet.unit_stats(unit(), 1, ["sword"], ITEMS, None, None)
|
||
vest = statsheet.unit_stats(unit(), 1, ["vest"], ITEMS, None, None)
|
||
assert sword["dps"] > base["dps"]
|
||
assert vest["ehp"] > base["ehp"]
|
||
assert statsheet.item_is_modeled(ITEMS["sword"])
|
||
assert not statsheet.item_is_modeled(ITEMS["unmodeled"])
|
||
|
||
|
||
def test_on_attack_spell_scales_with_attack_speed():
|
||
# Pro-Attacke-Passive: DPS = Schaden × AS, unabhängig vom Mana-Zyklus.
|
||
spell = [0, 50, 75, 110, 0, 0, 0]
|
||
u = unit(spell=spell)
|
||
u["spell_on_attack"] = True
|
||
on_attack = statsheet.unit_stats(u, 1, [], ITEMS, None, None)
|
||
assert on_attack["spell_dps"] == pytest.approx(50 * 0.7)
|
||
cast = statsheet.unit_stats(unit(spell=spell), 1, [], ITEMS, None, None)
|
||
assert on_attack["spell_dps"] > cast["spell_dps"]
|
||
|
||
|
||
def test_frontline_casts_more():
|
||
spell = [0, 300, 450, 700, 0, 0, 0]
|
||
tank = statsheet.unit_stats(unit(spell=spell), 1, [], ITEMS, None, "frontline")
|
||
carry = statsheet.unit_stats(unit(spell=spell), 1, [], ITEMS, None, "ad_carry")
|
||
assert tank["dps"] > carry["dps"]
|
||
|
||
|
||
def test_trait_buffs_keyword_mapping():
|
||
traits = {
|
||
"T_AD": {"breakpoints": [{"min_units": 2, "style": 1,
|
||
"variables": {"BonusAD": 0.15}}]},
|
||
"T_Mech": {"breakpoints": [{"min_units": 2, "style": 1,
|
||
"variables": {"Wolf_Gold": 1.0}}]},
|
||
"T_Skip": {"breakpoints": [{"min_units": 2, "style": 1,
|
||
"variables": {"StunDuration": 1.5}}]},
|
||
}
|
||
buffs, recognized = statsheet.trait_buffs(
|
||
{"T_AD": 1, "T_Mech": 1, "T_Skip": 1}, traits
|
||
)
|
||
assert recognized == {"T_AD"}
|
||
assert buffs["ad_pct"] == pytest.approx(0.15)
|
||
|
||
|
||
def test_fraction_normalization():
|
||
assert statsheet._fraction(0.15) == 0.15
|
||
assert statsheet._fraction(15.0) == 0.15
|