import importlib.util import sys from pathlib import Path from types import SimpleNamespace import pytest from core.basic import skill as skill_base from core.equipment.version_0.jade import jade_5 @pytest.fixture def character(monkeypatch): def get_data(prefix, key): if key == 'custom': return 1.0 if key == 'vps': return [] uuid = prefix.rsplit('/', 1)[-1] if uuid == '0113c8b1306ca76d208f83f2d093dd62' and key == 0: return [0] + [10 * level for level in range(1, 71)] if uuid == '34e1bda921012f6018709f1c1a12b04d': if key == 0: return [0] + [18 + 2 * level for level in range(1, 71)] if key == 1: return [0] + [20] * 70 return [0] + [100 * level for level in range(1, 71)] monkeypatch.setattr(skill_base, 'get_data', get_data) path = Path(__file__).resolve().parents[2] / 'core/character/GF/gunner_female/spitfire_female.py' spec = importlib.util.spec_from_file_location('spitfire_grenade_test', path) module = importlib.util.module_from_spec(spec) monkeypatch.setitem(sys.modules, spec.name, module) spec.loader.exec_module(module) def create(mastery=11, awakening=10, awakening_first=False): char = module.classChange(0) char.charEquipInfo = {'武器': SimpleNamespace(equInfo=None)} char.setInfo = {} char.SetSkills({str(sid): {'lv': 40} for sid in (24, 33, 36, 50)}) levels = [(27, mastery), (49, awakening)] for sid, level in reversed(levels) if awakening_first else levels: char.GetSkillByID(sid).lv = level return char return create @pytest.mark.parametrize('mastery', [0, 11]) @pytest.mark.parametrize('awakening', [0, 10]) @pytest.mark.parametrize('awakening_first', [False, True]) def test_grenade_mastery_applies_awakening_bonus(character, mastery, awakening, awakening_first): char = character(mastery, awakening, awakening_first) common = 1.38 if awakening else 1 mastery_ratio = 1 + mastery * (0.12 if awakening else 0.1) for sid in (24, 33, 36): assert char.GetSkillByID(sid).skillDamage == pytest.approx(common * mastery_ratio) assert char.GetSkillByID(50).skillDamage == pytest.approx(common) @pytest.mark.parametrize('count', [1, 2]) def test_level_jades_update_normal_and_enhanced_grenades(character, count): char = character() before = {(sid, mode): char.GetSkillByID(sid).skillInfo(mode) for sid in (24, 33, 36) for mode in ('普通', '强化')} for _ in range(count): jade_5(char, 1) assert char.GetSkillByID(27).lv == 11 + count for (sid, mode), old in before.items(): skill = char.GetSkillByID(sid) new = skill.skillInfo(mode) expected_level = 40 + count if sid == 33 else 40 assert skill.lv == expected_level expected = (1 + 0.12 * (11 + count)) / 2.32 * expected_level / 40 assert new[0] * new[1] / (old[0] * old[1]) == pytest.approx(expected) def test_changing_awakening_level_does_not_stack_grenade_bonus(character): char = character() awakening = char.GetSkillByID(49) for level, common, mastery_ratio in [(11, 1.4, 2.32), (0, 1, 2.1), (10, 1.38, 2.32)]: awakening.lv = level for sid in (24, 33, 36): assert char.GetSkillByID(sid).skillDamage == pytest.approx(common * mastery_ratio) assert char.GetSkillByID(50).skillDamage == pytest.approx(common) def test_mastery_level_changes_preserve_shared_data(character): char = character() mastery = char.GetSkillByID(27) original = mastery.data0.copy() for requested, actual in [(13, 13), (0, 0), (20, 20), (21, 20), (11, 11)]: mastery.lv = requested assert mastery.lv == actual assert char.GetSkillByID(36).skillDamage == pytest.approx(1.38 * (1 + actual * 0.12)) assert mastery.data0 == original assert character().GetSkillByID(36).skillDamage == pytest.approx(1.38 * 2.32)