Combat rework: packs, abilities, rest, health potions, group bosses
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
472609ac70
commit
57d11e30da
10 changed files with 1562 additions and 380 deletions
|
|
@ -170,6 +170,7 @@ class ActionTest(unittest.TestCase):
|
|||
world = world_with(("wil", "commoner"))
|
||||
p = world["players"]["wil"]
|
||||
p["bank"] = {"bread": 500}
|
||||
p["equip"].update(weapon="copper_sword", helmet="copper_helmet", body="copper_body", legs="copper_legs")
|
||||
game.start_action(world, p, "fight_wolves", T0)
|
||||
summary = game.process_player(world, p, T0 + 3600, random.Random(5))
|
||||
self.assertGreater(summary["done"], 30) # a fight lasts as long as it takes
|
||||
|
|
@ -505,31 +506,58 @@ class CombatTest(unittest.TestCase):
|
|||
self.assertEqual(p["bank"].get("bread", 0), 100 - summary["eaten"]["bread"])
|
||||
self.assertGreaterEqual(p["hp"], 1)
|
||||
|
||||
def test_out_of_food_means_retreat_and_nothing_lost(self):
|
||||
def test_three_knockouts_in_a_row_stop_you_and_nothing_is_lost(self):
|
||||
world, p = self.fighter(level=10, weapon="copper_sword")
|
||||
p["bank"] = {"stone": 10}
|
||||
game.start_action(world, p, "fight_scouts", T0)
|
||||
summary = game.process_player(world, p, T0 + 6 * 3600, random.Random(1))
|
||||
self.assertIsNone(p["action"])
|
||||
self.assertTrue(summary["stopped"].startswith("Retreated"))
|
||||
self.assertEqual(summary["stopped"], "Knocked out 3 times in a row")
|
||||
self.assertEqual(summary["losses"], 3)
|
||||
self.assertEqual(p["equip"]["weapon"], "copper_sword")
|
||||
self.assertEqual(p["bank"]["stone"], 10)
|
||||
self.assertLess(p["hp"], game.max_hp(p) * game.TOO_HURT)
|
||||
self.assertEqual(game.start_action(world, p, "fight_scouts", T0 + 6 * 3600), "Too hurt to fight. Rest, or bring food")
|
||||
self.assertAlmostEqual(p["hp"], game.max_hp(p) * game.KNOCKED_OUT_HP)
|
||||
self.assertIsNone(game.start_action(world, p, "fight_scouts", T0 + 6 * 3600)) # you can always go again
|
||||
|
||||
def test_eating_holds_back_your_swings(self):
|
||||
world, p = self.fighter(level=15)
|
||||
p["bank"] = {"cooked_shrimp": 100}
|
||||
p["hp"] = 1
|
||||
fight = game.simulate_fight(world, p, game.ACTION_BY_ID["fight_clay_golems"], random.Random(3))
|
||||
mine = [event for event in fight["events"] if event[1] == p["id"]]
|
||||
bite = next(i for i, event in enumerate(mine) if event[2] == "eat")
|
||||
swing = next(event for event in mine[bite:] if event[2] != "eat")
|
||||
self.assertGreaterEqual(swing[0] - mine[bite][0], game.EAT_DELAY)
|
||||
scouts = game.ACTION_BY_ID["fight_scouts"]
|
||||
eating = game.fight_odds(p, scouts)["kill_time"]
|
||||
def test_a_win_resets_the_knockouts(self):
|
||||
world, p = self.fighter(level=1, weapon="copper_sword")
|
||||
game.start_action(world, p, "fight_wolves", T0)
|
||||
summary = game.process_player(world, p, T0 + 3600, random.Random(1))
|
||||
self.assertGreater(summary["losses"], 2) # knocked out now and then, never three in a row
|
||||
self.assertEqual(p["action"]["id"], "fight_wolves")
|
||||
self.assertGreater(summary["wins"], 20)
|
||||
|
||||
def test_food_shortens_the_rest_between_fights(self):
|
||||
world, p = self.fighter(level=30)
|
||||
top = game.max_hp(p)
|
||||
p["bank"] = {}
|
||||
self.assertGreater(eating, game.fight_odds(p, scouts)["kill_time"])
|
||||
resting, eaten = game.rest_plan(world, p, top / 2)
|
||||
self.assertEqual(eaten, {})
|
||||
self.assertAlmostEqual(resting, top / 2 / (top * game.REST_RATE))
|
||||
p["bank"] = {"bread": 100}
|
||||
bread, eaten = game.rest_plan(world, p, top / 2)
|
||||
self.assertLess(bread, resting)
|
||||
self.assertEqual(eaten, {"bread": 10}) # 80 HP back in 8 HP bites
|
||||
p["bank"] = {"cooked_salmon": 100}
|
||||
salmon, eaten = game.rest_plan(world, p, top / 2)
|
||||
self.assertLess(salmon, bread) # better food, shorter rest
|
||||
self.assertEqual(eaten, {"cooked_salmon": 3})
|
||||
self.assertEqual(game.rest_plan(world, p, top)[0], game.RESPAWN) # at full: a breather
|
||||
p["xp"]["combat"] = game.XP_TABLE[99]
|
||||
p["bank"] = {"bread": 100}
|
||||
self.assertEqual(game.rest_plan(world, p, 100)[1], {}) # a bite of bread is no faster than resting
|
||||
|
||||
def test_food_is_eaten_resting_not_fighting(self):
|
||||
world, p = self.fighter(level=15, weapon="iron_sword", helmet="iron_helmet", body="iron_body", legs="iron_legs")
|
||||
p["bank"] = {"cooked_trout": 100}
|
||||
p["hp"] = 10
|
||||
fight = game.simulate_fight(world, p, game.ACTION_BY_ID["fight_clay_golems"], random.Random(3))
|
||||
mine = game.member_of(fight, p["id"])
|
||||
self.assertGreater(sum(mine["eaten"].values()), 0)
|
||||
self.assertGreater(fight["rest"], game.RESPAWN)
|
||||
self.assertEqual(mine["hp_in"], game.max_hp(p)) # every fight starts rested
|
||||
self.assertTrue(all(e[0] >= fight["rest"] for e in fight["events"]))
|
||||
self.assertFalse(any(e[3] == "eat" for e in fight["events"]))
|
||||
|
||||
def test_rest_heals(self):
|
||||
world, p = self.fighter()
|
||||
|
|
@ -547,20 +575,24 @@ class CombatTest(unittest.TestCase):
|
|||
self.assertLess(hurt(veteran), hurt(bare))
|
||||
self.assertGreater(game.fight_odds(veteran, scouts)["hit"], game.fight_odds(bare, scouts)["hit"])
|
||||
|
||||
def test_a_fight_is_played_out_swing_by_swing(self):
|
||||
world, p = self.fighter(level=10, weapon="iron_sword")
|
||||
def test_a_fight_is_played_out_slice_by_slice(self):
|
||||
world, p = self.fighter(level=20, weapon="iron_sword", helmet="iron_helmet", body="iron_body", legs="iron_legs")
|
||||
p["bank"] = {"bread": 50}
|
||||
game.start_action(world, p, "fight_scouts", T0)
|
||||
game.process_player(world, p, T0 + 1, random.Random(2))
|
||||
fight = p["action"]["fight"] # simulated, waiting to finish, for the page to replay
|
||||
self.assertEqual(fight["result"], "won")
|
||||
self.assertTrue(all(e[1] in (p["id"], "foe") and e[2] in ("hit", "crit", "miss", "eat", "wind") for e in fight["events"]))
|
||||
self.assertEqual(fight["events"][-1][5], 0) # the last swing finished it
|
||||
kinds = {"hit", "crit", "tick", "miss", "block", "absorb", "heal", "mend", "cast", "use", "stop", "cc", "free",
|
||||
"die", "enrage", "wind", "down", "left"}
|
||||
self.assertTrue(all(e[3] in kinds for e in fight["events"]))
|
||||
self.assertTrue(all(e[1] in (p["id"], None) or e[1].startswith("e") for e in fight["events"]))
|
||||
dead = [e[2] for e in fight["events"] if e[3] == "die"]
|
||||
self.assertEqual(sorted(dead), sorted(f"e{i}" for i in range(len(fight["pack"])))) # the last of them finished it
|
||||
self.assertAlmostEqual(fight["duration"], fight["end"], places=1) # rewards land on the kill
|
||||
summary = game.process_player(world, p, T0 + fight["duration"] + 0.01, random.Random(2))
|
||||
self.assertEqual(summary["wins"], 1)
|
||||
self.assertAlmostEqual(p["hp"], game.member_of(fight, p["id"])["hp"], delta=0.1)
|
||||
self.assertAlmostEqual(p["action"]["start"], T0 + fight["duration"] + game.RESPAWN) # then a short pause
|
||||
self.assertAlmostEqual(p["action"]["start"], T0 + fight["duration"]) # straight on to the next rest
|
||||
|
||||
def test_outclassing_an_enemy_kills_faster(self):
|
||||
kills = {}
|
||||
|
|
@ -570,15 +602,17 @@ class CombatTest(unittest.TestCase):
|
|||
kills[level] = game.process_player(world, p, T0 + 600, random.Random(1))["wins"]
|
||||
self.assertGreater(kills[90], kills[20] * 1.3)
|
||||
|
||||
def test_each_enemy_starts_with_both_swing_timers_empty(self):
|
||||
def test_each_fight_starts_with_every_swing_timer_empty(self):
|
||||
world, p = self.fighter(level=60, weapon="rune_sword")
|
||||
game.start_action(world, p, "fight_wolves", T0)
|
||||
game.process_player(world, p, T0 + 0.1, random.Random(1))
|
||||
first = p["action"]["fight"]
|
||||
game.process_player(world, p, T0 + first["duration"] + 0.01, random.Random(1))
|
||||
self.assertAlmostEqual(p["action"]["start"], T0 + first["duration"] + game.RESPAWN) # the pause starts at the kill
|
||||
self.assertAlmostEqual(p["action"]["start"], T0 + first["duration"]) # the rest starts at the kill
|
||||
second = p["action"]["fight"]
|
||||
self.assertEqual(next(e[0] for e in second["events"] if e[1] == p["id"]), 2.4) # a full swing, from scratch
|
||||
swing = next(e[0] for e in second["events"] if e[1] == p["id"] and e[6] in game.DAMAGE_TYPES)
|
||||
self.assertAlmostEqual(swing, second["rest"] + 2.5) # a full swing from scratch, to the slice
|
||||
|
||||
def test_preferred_food_is_eaten_first(self):
|
||||
world, p = self.fighter()
|
||||
p["bank"] = {"bread": 5, "cooked_salmon": 5}
|
||||
|
|
@ -587,6 +621,99 @@ class CombatTest(unittest.TestCase):
|
|||
self.assertEqual(game.edible(p), ["cooked_salmon", "bread"])
|
||||
|
||||
|
||||
class AbilityTest(unittest.TestCase):
|
||||
def fighter(self, cls, level, tier=1, **equip):
|
||||
world = world_with(("ash", cls))
|
||||
p = world["players"]["ash"]
|
||||
p["xp"]["combat"] = game.XP_TABLE[level]
|
||||
p["equip"].update(equip)
|
||||
p["hp"] = game.max_hp(p)
|
||||
return world, p
|
||||
|
||||
def test_the_weapon_gives_four_abilities_and_the_trade_one(self):
|
||||
known = lambda style, cls, level, shield=False: game.combat.known_abilities(style, cls, level, shield)
|
||||
self.assertEqual(set(known("hammer", "miner", 1)), {"crushing_blow", "find_the_seam"})
|
||||
self.assertEqual(set(known("hammer", "miner", 45)), {"crushing_blow", "concuss", "earthshaker", "unstoppable",
|
||||
"find_the_seam"})
|
||||
self.assertEqual(set(known("bow", "miner", 15)), {"aimed_shot", "pinning_shot", "find_the_seam"}) # any weapon
|
||||
self.assertEqual(set(known("sword", "builder", 45)), {"challenge", "sweeping_cut", "barricade"}) # no shield
|
||||
self.assertEqual(set(known("sword", "builder", 45, True)), {"shield_bash", "challenge", "sweeping_cut",
|
||||
"shield_wall", "barricade"})
|
||||
self.assertEqual(known("staff", "homesteader", 30), ["mend", "ward", "hearty_meal", "bolt"]) # auto's order
|
||||
self.assertEqual(game.combat.role("sword", True), "tank")
|
||||
self.assertEqual(game.combat.role("sword", False), "damage")
|
||||
self.assertEqual(game.combat.role("staff", False), "healer")
|
||||
|
||||
def test_abilities_are_used_in_the_fight(self):
|
||||
world, p = self.fighter("ranger", 30, weapon="willow_bow")
|
||||
fight = game.simulate_fight(world, p, game.ACTION_BY_ID["fight_clay_golems"], random.Random(1))
|
||||
used = {e[6] for e in fight["events"] if e[1] == p["id"] and e[6] in game.combat.ABILITIES}
|
||||
self.assertIn("aimed_shot", used)
|
||||
casts = [e for e in fight["events"] if e[1] == p["id"] and e[3] == "cast"]
|
||||
self.assertTrue(casts)
|
||||
|
||||
def test_casters_get_stopped(self):
|
||||
world, p = self.fighter("ranger", 30, weapon="willow_bow")
|
||||
harpies = game.ACTION_BY_ID["fight_harpies"]
|
||||
stops = 0
|
||||
for seed in range(20):
|
||||
fight = game.simulate_fight(world, p, harpies, random.Random(seed))
|
||||
stops += sum(1 for e in fight["events"] if e[3] == "stop" and e[6] == "hex_bolt")
|
||||
self.assertGreater(stops, 0) # Pinning Shot stops a windcaster's hex
|
||||
|
||||
def test_a_health_potion_once_a_fight_when_low(self):
|
||||
world, p = self.fighter("miner", 10, weapon="copper_warhammer")
|
||||
p["bank"] = {"health_potion_1": 5, "health_potion_2": 5}
|
||||
self.assertEqual(game.health_potion(p), "health_potion_2")
|
||||
game.start_action(world, p, "fight_scouts", T0)
|
||||
summary = game.process_player(world, p, T0 + 3600, random.Random(1))
|
||||
drunk = summary["used"].get("health_potion_2", 0)
|
||||
self.assertGreater(drunk, 0)
|
||||
self.assertLessEqual(drunk, summary["done"])
|
||||
self.assertEqual(p["bank"].get("health_potion_2", 0), 5 - drunk)
|
||||
fight = game.simulate_fight(world, p, game.ACTION_BY_ID["fight_scouts"], random.Random(3))
|
||||
self.assertLessEqual(sum(1 for e in fight["events"] if e[3] == "heal" and e[6] == "potion"), 1)
|
||||
|
||||
def test_health_potions_are_brewed(self):
|
||||
brew = game.ACTION_BY_ID["brew_health_potion_3"]
|
||||
self.assertEqual((brew["skill"], brew["level"]), ("herblore", 30))
|
||||
self.assertEqual(brew["outputs"], {"health_potion_3": 1})
|
||||
self.assertIn("tarromin", brew["inputs"])
|
||||
self.assertIn("cooked_salmon", brew["inputs"])
|
||||
self.assertEqual(game.ITEMS["health_potion_3"]["drink"], game.HEALTH_HEAL[2])
|
||||
self.assertNotIn("health_potion_3", game.edible({"bank": {"health_potion_3": 1}, "food": None}))
|
||||
|
||||
def test_volley_spends_arrows(self):
|
||||
world, p = self.fighter("ranger", 45, weapon="maple_bow")
|
||||
p["bank"] = {"arrows": 100}
|
||||
game.start_action(world, p, "fight_goblins", T0)
|
||||
summary = game.process_player(world, p, T0 + 1800, random.Random(2))
|
||||
shot = summary["used"].get("arrows", 0)
|
||||
self.assertGreater(shot, 0)
|
||||
self.assertEqual(shot % 5, 0)
|
||||
self.assertEqual(p["bank"].get("arrows", 0), 100 - shot)
|
||||
|
||||
def test_a_party_rests_as_long_as_its_slowest(self):
|
||||
world = world_with(("wil", "builder"), ("kay", "builder"))
|
||||
wil, kay = world["players"]["wil"], world["players"]["kay"]
|
||||
for p in (wil, kay):
|
||||
p["xp"]["combat"] = game.XP_TABLE[20]
|
||||
wil["hp"], kay["hp"] = game.max_hp(wil), 10
|
||||
fight = game.draw_fight(world, [wil, kay], game.ACTION_BY_ID["fight_scouts"], random.Random(1))
|
||||
self.assertAlmostEqual(fight["rest"], game.rest_plan(world, kay)[0])
|
||||
|
||||
def test_fights_in_progress_are_drawn_again_after_the_update(self):
|
||||
world = world_with(("wil", "builder"))
|
||||
wil = world["players"]["wil"]
|
||||
game.start_action(world, wil, "fight_wolves", T0)
|
||||
wil["action"]["fight"] = {"old": True}
|
||||
world["parties"]["1"] = {"id": "1", "action": "fight_wolves", "members": [], "start": T0, "fight": {"old": True}}
|
||||
world["version"] = 25
|
||||
game.migrate(world, T0)
|
||||
self.assertNotIn("fight", wil["action"])
|
||||
self.assertIsNone(world["parties"]["1"]["fight"])
|
||||
|
||||
|
||||
class NewSkillsTest(unittest.TestCase):
|
||||
def test_tools_speed_up_gathering(self):
|
||||
world = world_with(("pia", "commoner"))
|
||||
|
|
@ -789,14 +916,18 @@ class TierTest(unittest.TestCase):
|
|||
self.assertEqual(game.build_cost("walls", 5)["steel_bar"], 180)
|
||||
|
||||
def test_dragons_drop_their_areas_reagents(self):
|
||||
world = world_with(("wil", "commoner"))
|
||||
world = world_with(("wil", "commoner"), ("kay", "commoner"))
|
||||
for p in world["players"].values():
|
||||
p["xp"]["combat"] = game.XP_TABLE[80]
|
||||
p["equip"].update(weapon="rune_sword", shield="rune_shield", helmet="rune_helmet", body="rune_body",
|
||||
legs="rune_legs")
|
||||
p["hp"] = game.max_hp(p)
|
||||
p["bank"] = {"cooked_swordfish": 200}
|
||||
p["last_active"] = T0 + 3600 * 2
|
||||
p = world["players"]["wil"]
|
||||
p["xp"]["combat"] = game.XP_TABLE[80]
|
||||
p["equip"].update(weapon="rune_sword", shield="rune_shield", helmet="rune_helmet", body="rune_body", legs="rune_legs")
|
||||
p["hp"] = game.max_hp(p)
|
||||
p["bank"] = {"cooked_swordfish": 200}
|
||||
game.start_action(world, p, "fight_green_dragon", T0)
|
||||
game.process_player(world, p, T0 + 3600 * 2, random.Random(3))
|
||||
game.start_action(world, p, "fight_green_dragon", T0) # a group fight
|
||||
game.join_fight(world, world["players"]["kay"], "wil", T0)
|
||||
game.catch_up(world, T0 + 3600 * 2, random.Random(3))
|
||||
self.assertGreater(sum(p["bank"].get(game.reagent(weight, 3), 0) for weight in game.WEIGHTS), 30)
|
||||
|
||||
|
||||
|
|
@ -843,27 +974,26 @@ class PrayerTest(unittest.TestCase):
|
|||
self.assertTrue(summary["prayer_out"])
|
||||
self.assertEqual(p["xp"]["prayer"], game.XP_TABLE[10] + 4 * game.PRAYER_XP_PER_POINT)
|
||||
|
||||
def test_second_wind_saves_one_retreat(self):
|
||||
def test_second_wind_saves_one_knockout(self):
|
||||
world, p = self.priest(level=40, combat=10)
|
||||
game.set_prayers(p, ["second_wind"])
|
||||
p["pp"] = 1000
|
||||
game.start_action(world, p, "fight_scouts", T0)
|
||||
summary = game.process_player(world, p, T0 + 6 * 3600, random.Random(1))
|
||||
self.assertEqual(summary["second_wind"], 1)
|
||||
self.assertTrue(summary["stopped"].startswith("Retreated"))
|
||||
self.assertEqual(summary["stopped"], "Knocked out 3 times in a row")
|
||||
|
||||
def test_rapid_heal_mends_during_fights(self):
|
||||
world, p = self.priest(level=10, combat=10)
|
||||
scouts = game.ACTION_BY_ID["fight_scouts"]
|
||||
p["pp"], p["hp"] = 1000, 50
|
||||
p["pp"] = 1000
|
||||
p["equip"].update(weapon="copper_sword", helmet="copper_helmet", body="copper_body", legs="copper_legs")
|
||||
plain = game.simulate_fight(world, p, scouts, random.Random(2))
|
||||
game.set_prayers(p, ["rapid_heal"])
|
||||
mended = game.simulate_fight(world, p, scouts, random.Random(2))
|
||||
self.assertEqual(plain["end"], mended["end"]) # the same fight, only the mending differs
|
||||
swings = sum(1 for e in plain["events"] if e[1] == p["id"])
|
||||
top = game.max_hp(p)
|
||||
plain, mended = game.member_of(plain, p["id"]), game.member_of(mended, p["id"])
|
||||
self.assertAlmostEqual(mended["hp"] - plain["hp"], swings * game.MEND * top * plain["interval"], places=6)
|
||||
self.assertEqual([e[:4] for e in plain["events"] if e[1] == p["id"]][:5],
|
||||
[e[:4] for e in mended["events"] if e[1] == p["id"]][:5]) # the same fight
|
||||
self.assertGreater(game.member_of(mended, p["id"])["hp"], game.member_of(plain, p["id"])["hp"])
|
||||
self.assertLess(game.fight_odds(p, scouts)["damage"], game.fight_odds({**p, "prayers": []}, scouts)["damage"])
|
||||
|
||||
def test_offerings_ward_the_city_until_the_raid(self):
|
||||
|
|
@ -1452,11 +1582,27 @@ class WeaponStyleTest(unittest.TestCase):
|
|||
self.assertLess(hurt(cloth, shaper), hurt(plate, shaper))
|
||||
self.assertLess(hurt(plate, brute), hurt(cloth, brute))
|
||||
|
||||
def test_each_kill_is_one_of_the_variants(self):
|
||||
def test_each_fight_is_a_pack_of_the_variants(self):
|
||||
world, p = self.armed("miner", "iron_warhammer", level=30)
|
||||
seen = {game.simulate_fight(world, p, game.ACTION_BY_ID["fight_goblins"], random.Random(seed))["enemy"]
|
||||
for seed in range(60)}
|
||||
self.assertEqual(seen, {"Goblin brute", "Goblin rogue", "Goblin warlock"})
|
||||
goblins = game.ACTION_BY_ID["fight_goblins"]
|
||||
packs = [game.simulate_fight(world, p, goblins, random.Random(seed))["pack"] for seed in range(60)]
|
||||
self.assertEqual({foe["name"] for pack in packs for foe in pack}, {"Goblin brute", "Goblin rogue", "Goblin warlock"})
|
||||
self.assertEqual({len(pack) for pack in packs}, {1, 2})
|
||||
for pack in packs:
|
||||
share = game.OUTNUMBERED[0] if len(pack) == 2 else 1
|
||||
self.assertTrue(all(foe["share"] == share for foe in pack)) # two of them, each weaker
|
||||
scouts = game.ACTION_BY_ID["fight_scouts"]
|
||||
self.assertEqual({len(game.draw_pack(scouts, 3, random.Random(seed))) for seed in range(60)}, {3, 4})
|
||||
|
||||
def test_a_boss_comes_alone(self):
|
||||
ogre = game.ACTION_BY_ID["fight_ruins"]
|
||||
foe = ogre["enemy"]["variants"][0]
|
||||
pair, four = game.draw_pack(ogre, 2, random.Random(1)), game.draw_pack(ogre, 4, random.Random(1))
|
||||
self.assertEqual((len(pair), len(four)), (1, 1))
|
||||
self.assertEqual(pair[0]["hp"], round(foe["hp"] * game.BOSS_HP * 2)) # tougher the more of you there are
|
||||
self.assertEqual(four[0]["hp"], round(foe["hp"] * game.BOSS_HP * 4))
|
||||
self.assertEqual(pair[0]["max_hit"], round(foe["max_hit"] * game.BOSS_HITS[0]))
|
||||
self.assertGreater(four[0]["max_hit"], pair[0]["max_hit"])
|
||||
|
||||
def test_shield_smiths_stop_after_the_move(self):
|
||||
world = world_with(("kay", "miner"))
|
||||
|
|
@ -2059,7 +2205,7 @@ class PartyTest(unittest.TestCase):
|
|||
for p in world["players"].values():
|
||||
p["xp"]["combat"] = game.XP_TABLE[level]
|
||||
p["hp"] = game.max_hp(p)
|
||||
p["equip"]["weapon"] = "iron_sword"
|
||||
p["equip"].update(weapon="iron_sword", helmet="iron_helmet", body="iron_body", legs="iron_legs")
|
||||
p["bank"] = {"bread": 300}
|
||||
return world, world["players"]["wil"], world["players"]["kay"]
|
||||
|
||||
|
|
@ -2096,14 +2242,12 @@ class PartyTest(unittest.TestCase):
|
|||
self.assertEqual(together["done"], kay["away"]["done"])
|
||||
self.assertEqual(together["with"], {"Kay": together["done"]})
|
||||
self.assertEqual(wil["xp"]["combat"], kay["xp"]["combat"]) # the same kills, the full XP each
|
||||
low, high = game.ACTION_BY_ID["fight_scouts"]["gold"]
|
||||
self.assertTrue(together["wins"] * low <= wil["gold"] + kay["gold"] <= together["wins"] * high)
|
||||
self.assertLessEqual(abs(wil["gold"] - kay["gold"]), together["wins"]) # split evenly, odd coins aside
|
||||
self.assertEqual(wil["bank"].get("bones", 0) + kay["bank"].get("bones", 0), together["wins"]) # one each kill
|
||||
self.assertGreater(wil["gold"] + kay["gold"], 0)
|
||||
self.assertLessEqual(abs(wil["gold"] - kay["gold"]), together["wins"] * 4) # split evenly, odd coins aside
|
||||
solo, alone = self.pair()[0:2]
|
||||
game.start_action(solo, alone, "fight_scouts", T0)
|
||||
self.online(solo, T0 + 3600, random.Random(2))
|
||||
self.assertGreater(together["done"], alone["away"]["done"] * 1.5)
|
||||
self.assertGreater(together["xp"]["combat"], alone["away"]["xp"]["combat"] * 1.3)
|
||||
|
||||
def test_leaving_lets_the_other_carry_on(self):
|
||||
world, wil, kay = self.pair()
|
||||
|
|
@ -2117,7 +2261,7 @@ class PartyTest(unittest.TestCase):
|
|||
self.assertNotIn("party", wil["action"])
|
||||
left = game.member_of(wil["action"]["fight"], "kay") # the fight goes on without her
|
||||
self.assertEqual(left["result"], "left")
|
||||
self.online(world, T0 + 600, rng)
|
||||
self.online(world, T0 + 1200, rng)
|
||||
self.assertGreater(wil["away"]["wins"], 3)
|
||||
|
||||
def test_away_too_long_steps_out(self):
|
||||
|
|
@ -2134,17 +2278,42 @@ class PartyTest(unittest.TestCase):
|
|||
self.assertNotIn("party", kay["action"])
|
||||
self.assertGreater(wil["away"]["done"], kay["away"]["done"]) # wil carried on alone for the last hour
|
||||
|
||||
def test_a_retreat_leaves_the_rest_fighting(self):
|
||||
def test_knocked_out_in_a_won_fight_still_shares_the_win(self):
|
||||
world, wil, kay = self.pair()
|
||||
kay["bank"] = {}
|
||||
kay["hp"] = game.max_hp(kay) * 0.35
|
||||
game.start_action(world, wil, "fight_scouts", T0)
|
||||
self.assertIsNone(game.join_fight(world, kay, "wil", T0))
|
||||
self.online(world, T0 + 600, random.Random(5))
|
||||
self.assertIsNone(kay["action"])
|
||||
self.assertTrue(kay["stopped"].startswith("Retreated"))
|
||||
self.assertEqual(wil["action"]["id"], "fight_scouts")
|
||||
self.assertEqual(world["parties"], {})
|
||||
self.online(world, T0 + 0.1, random.Random(0))
|
||||
kay["hp"] = 1 # tags in as she is
|
||||
self.assertIsNone(game.join_fight(world, kay, "wil", T0 + 0.1))
|
||||
fight = game.current_fight(world, wil)
|
||||
mine = game.member_of(fight, "kay")
|
||||
self.assertTrue(mine["down"])
|
||||
self.assertEqual((fight["result"], mine["result"]), ("won", "won"))
|
||||
self.online(world, T0 + fight["duration"] + 0.01, random.Random(5))
|
||||
self.assertEqual(kay["away"]["wins"], 1)
|
||||
self.assertAlmostEqual(kay["hp"], game.max_hp(kay) * game.KNOCKED_OUT_HP)
|
||||
self.assertEqual(kay["action"]["id"], "fight_scouts")
|
||||
|
||||
def test_a_boss_needs_a_group(self):
|
||||
world, wil, kay = self.pair(level=30)
|
||||
for p in (wil, kay):
|
||||
p["equip"].update(weapon="steel_sword", shield="steel_shield", helmet="steel_helmet", body="steel_body",
|
||||
legs="steel_legs")
|
||||
self.assertIsNone(game.start_action(world, wil, "fight_ruins", T0))
|
||||
self.assertIn("Wil is looking for a group", world["ledger"][-1]["text"])
|
||||
self.online(world, T0 + 3600, random.Random(1))
|
||||
self.assertNotIn("fight", wil["action"]) # alone, nothing happens
|
||||
self.assertEqual(wil["away"]["done"], 0) if "away" in wil else None
|
||||
self.assertEqual(wil["action"]["start"], T0 + 3600)
|
||||
self.assertIsNone(game.join_fight(world, kay, "wil", T0 + 3600))
|
||||
self.online(world, T0 + 7200, random.Random(1))
|
||||
self.assertGreater(wil["away"]["wins"], 0)
|
||||
self.assertEqual(wil["away"]["wins"], kay["away"]["wins"])
|
||||
self.assertIsNone(game.start_action(world, kay, "mine_stone", T0 + 7200))
|
||||
self.online(world, T0 + 9000, random.Random(1))
|
||||
wins = wil["away"]["wins"]
|
||||
self.online(world, T0 + 12600, random.Random(1))
|
||||
self.assertEqual(wil["away"]["wins"], wins) # back to waiting
|
||||
self.assertEqual(wil["action"]["id"], "fight_ruins")
|
||||
|
||||
def test_who_can_join(self):
|
||||
world, wil, kay = self.pair()
|
||||
|
|
@ -2162,7 +2331,8 @@ class PartyTest(unittest.TestCase):
|
|||
def test_bosses_give_everyone_their_own_loot(self):
|
||||
world, wil, kay = self.pair(level=30)
|
||||
for p in (wil, kay):
|
||||
p["equip"].update(weapon="steel_sword", helmet="steel_helmet", body="steel_body", legs="steel_legs")
|
||||
p["equip"].update(weapon="steel_sword", shield="steel_shield", helmet="steel_helmet", body="steel_body",
|
||||
legs="steel_legs")
|
||||
p["bank"] = {"cooked_salmon": 5000}
|
||||
game.start_action(world, wil, "fight_ruins", T0)
|
||||
self.assertIsNone(game.join_fight(world, kay, "wil", T0))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue