small update
boss prototype, auto planter, visual reworks, bug fixes
This commit is contained in:
+105
-1
@@ -155,6 +155,28 @@ class ShieldWiltDotTest(unittest.TestCase):
|
||||
self.assertEqual(t.hp, 100)
|
||||
self.assertNotIn(S_SHIELD, t.status)
|
||||
|
||||
def test_heal_random_ally_prefers_injured_ally(self):
|
||||
flutter = Skill("Flutter", power=40, target=TARGET_ENEMY, mechanic="heal_random_ally")
|
||||
daisy = make_unit("Daisy", TEAM_PLAYER, "radiant", atk=50, skills=[flutter])
|
||||
full = make_unit("Full", TEAM_PLAYER, "radiant", hp=100)
|
||||
hurt = make_unit("Hurt", TEAM_PLAYER, "radiant", hp=100)
|
||||
hurt.hp = 50
|
||||
enemy = make_unit("E", TEAM_ENEMY, "garden", hp=500)
|
||||
b = Battle([daisy, full, hurt], [enemy])
|
||||
b.start()
|
||||
b.use_skill(daisy, flutter, enemy)
|
||||
self.assertEqual(hurt.hp, 90) # the injured ally got healed
|
||||
self.assertEqual(full.hp, 100) # full-HP ally untouched
|
||||
|
||||
def test_heal_random_ally_noop_when_all_full(self):
|
||||
flutter = Skill("Flutter", power=40, target=TARGET_ENEMY, mechanic="heal_random_ally")
|
||||
daisy = make_unit("Daisy", TEAM_PLAYER, "radiant", atk=50, skills=[flutter])
|
||||
enemy = make_unit("E", TEAM_ENEMY, "garden", hp=500)
|
||||
b = Battle([daisy], [enemy])
|
||||
b.start()
|
||||
b.use_skill(daisy, flutter, enemy)
|
||||
self.assertFalse(any(isinstance(l, dict) and "recovers" in l["text"] for l in b.log)) # nothing healed
|
||||
|
||||
def test_wilt_ticks_over_three_turns(self):
|
||||
a = make_unit("A", TEAM_PLAYER, "radiant", atk=50, skills=[Skill("Hit", 10, target=TARGET_ENEMY)])
|
||||
t = make_unit("T", TEAM_ENEMY, "garden", dfn=20, hp=500, spd=5)
|
||||
@@ -194,6 +216,45 @@ class ShieldWiltDotTest(unittest.TestCase):
|
||||
self.assertEqual(t.hp, 410)
|
||||
self.assertNotIn(S_DOT, t.status)
|
||||
|
||||
def test_shield_decays_after_3_turns(self):
|
||||
t = make_unit("T", TEAM_ENEMY, "garden", hp=500)
|
||||
b = Battle([make_unit("A", TEAM_PLAYER, "radiant")], [t])
|
||||
b.start()
|
||||
b._shield(t, 30)
|
||||
self.assertEqual(b._shield_total(t), 30)
|
||||
b._end_turn_status(t)
|
||||
self.assertEqual(b._shield_total(t), 30) # 3 -> 2
|
||||
b._end_turn_status(t)
|
||||
self.assertEqual(b._shield_total(t), 30) # 2 -> 1
|
||||
b._end_turn_status(t)
|
||||
self.assertEqual(b._shield_total(t), 0) # gone after the 3rd turn
|
||||
self.assertNotIn(S_SHIELD, t.status)
|
||||
|
||||
def test_stacked_shields_decay_independently(self):
|
||||
t = make_unit("T", TEAM_ENEMY, "garden", hp=500)
|
||||
b = Battle([make_unit("A", TEAM_PLAYER, "radiant")], [t])
|
||||
b.start()
|
||||
b._shield(t, 30)
|
||||
b._end_turn_status(t) # first: 3 -> 2
|
||||
b._shield(t, 40) # stack a new one (doesn't refresh the old)
|
||||
b._end_turn_status(t) # first: 2 -> 1, second: 3 -> 2
|
||||
b._end_turn_status(t) # first expires, second: 2 -> 1
|
||||
self.assertEqual(b._shield_total(t), 40) # only the newer 40 remains
|
||||
b._end_turn_status(t) # second expires too
|
||||
self.assertEqual(b._shield_total(t), 0)
|
||||
|
||||
def test_shield_absorption_consumes_applications_in_order(self):
|
||||
t = make_unit("T", TEAM_ENEMY, "garden", hp=500)
|
||||
b = Battle([make_unit("A", TEAM_PLAYER, "radiant")], [t])
|
||||
b.start()
|
||||
b._shield(t, 30)
|
||||
b._shield(t, 40)
|
||||
b._absorb(None, t, 50) # 30 from the old shield, 20 from the new one
|
||||
self.assertEqual(b._shield_total(t), 20)
|
||||
b._absorb(None, t, 20) # finishes the new shield
|
||||
self.assertEqual(b._shield_total(t), 0)
|
||||
self.assertEqual(t.hp, 500)
|
||||
|
||||
|
||||
class MechanicsTest(unittest.TestCase):
|
||||
def test_sleep_skips_until_two_hits(self):
|
||||
@@ -487,7 +548,7 @@ class MechanicsTest(unittest.TestCase):
|
||||
enemy.status["wilt"] = (30, 3)
|
||||
enemy.status["dot"] = [(10, 2)]
|
||||
enemy.status["vulnerable"] = 1
|
||||
zinnia.status["shield"] = 10 # one self effect only
|
||||
zinnia.status["shield"] = [(10, 3)] # one self effect only
|
||||
self.assertIs(b.best_skill(zinnia), glitter)
|
||||
|
||||
def test_zinnia_targets_most_effect_rich_enemy(self):
|
||||
@@ -743,5 +804,48 @@ class TowerInjectionTest(unittest.TestCase):
|
||||
self.assertNotEqual(base.name, inj["skill"]["name"])
|
||||
|
||||
|
||||
class TurnActionLimitTest(unittest.TestCase):
|
||||
"""A unit must act only once per turn (spam-clicking must not multi-cast)."""
|
||||
|
||||
def _battle(self, skills):
|
||||
a = make_unit("A", TEAM_PLAYER, "radiant", atk=50, skills=skills)
|
||||
t = make_unit("T", TEAM_ENEMY, "garden", dfn=20, hp=1000)
|
||||
b = Battle([a], [t])
|
||||
b.start()
|
||||
return b, a, t
|
||||
|
||||
def test_unit_cannot_use_two_skills_in_one_turn(self):
|
||||
hit1 = Skill("Hit1", power=10, target=TARGET_ENEMY)
|
||||
hit2 = Skill("Hit2", power=10, target=TARGET_ENEMY)
|
||||
b, a, t = self._battle([hit1, hit2])
|
||||
self.assertTrue(b.use_skill(a, hit1, t))
|
||||
self.assertFalse(b.use_skill(a, hit2, t)) # already acted
|
||||
self.assertTrue(a.acted)
|
||||
|
||||
def test_same_skill_cannot_be_spammed(self):
|
||||
hit = Skill("Hit", power=10, target=TARGET_ENEMY, cooldown=0)
|
||||
b, a, t = self._battle([hit])
|
||||
self.assertTrue(b.use_skill(a, hit, t))
|
||||
self.assertFalse(b.use_skill(a, hit, t)) # acted flag blocks the re-cast
|
||||
|
||||
def test_extra_action_skill_still_usable_after_acting(self):
|
||||
hit = Skill("Hit", power=10, target=TARGET_ENEMY)
|
||||
extra = Skill("Extra", power=5, target=TARGET_ENEMY, extra_action=True)
|
||||
b, a, t = self._battle([hit, extra])
|
||||
self.assertTrue(b.use_skill(a, hit, t))
|
||||
self.assertTrue(b.use_skill(a, extra, t)) # extra action allowed after acting
|
||||
self.assertTrue(a.acted)
|
||||
|
||||
def test_no_extra_actions_after_act_limit_exhausted(self):
|
||||
hit = Skill("Hit", power=10, target=TARGET_ENEMY)
|
||||
extra = Skill("Extra", power=5, target=TARGET_ENEMY, extra_action=True)
|
||||
extra2 = Skill("Extra2", power=5, target=TARGET_ENEMY, extra_action=True)
|
||||
b, a, t = self._battle([hit, extra, extra2])
|
||||
self.assertTrue(b.use_skill(a, hit, t))
|
||||
self.assertTrue(b.use_skill(a, extra, t))
|
||||
self.assertTrue(b.use_skill(a, extra2, t))
|
||||
self.assertFalse(b.use_skill(a, hit, t)) # acted + cooldown: still blocked
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,139 @@
|
||||
import atexit
|
||||
import os
|
||||
import pathlib
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
# Point the engine at a fresh empty save to keep collection-dependent tests hermetic.
|
||||
import engine.collection as collection
|
||||
from engine import bosses
|
||||
from engine.battle import Battle
|
||||
from engine.plants import make_leveled
|
||||
from engine.unit import TEAM_ENEMY, TEAM_PLAYER
|
||||
|
||||
_fd, _tmp_path = tempfile.mkstemp(suffix=".json")
|
||||
os.close(_fd)
|
||||
_TMP = pathlib.Path(_tmp_path)
|
||||
_ORIG_DATA_FILE = collection._DATA_FILE
|
||||
collection._DATA_FILE = _TMP
|
||||
|
||||
|
||||
@atexit.register
|
||||
def _restore_collection():
|
||||
collection._DATA_FILE = _ORIG_DATA_FILE
|
||||
if _TMP.exists():
|
||||
_TMP.unlink()
|
||||
|
||||
|
||||
class BossSchedulingTest(unittest.TestCase):
|
||||
def test_boss_appears_on_multiples_of_20(self):
|
||||
self.assertEqual(bosses.boss_for_floor(20), "Stones")
|
||||
self.assertEqual(bosses.boss_for_floor(40), "Stones")
|
||||
self.assertEqual(bosses.boss_for_floor(100), "Stones")
|
||||
self.assertIsNone(bosses.boss_for_floor(19))
|
||||
self.assertIsNone(bosses.boss_for_floor(1))
|
||||
|
||||
def test_boss_stats_are_extreme(self):
|
||||
u = bosses.make_boss("Stones", 20)
|
||||
# very low speed, medium defense, high health, medium attack
|
||||
self.assertLessEqual(u.speed, 40)
|
||||
self.assertGreaterEqual(u.max_hp, 900)
|
||||
self.assertGreaterEqual(u.defense, 80)
|
||||
self.assertTrue(60 <= u.attack <= 200)
|
||||
|
||||
|
||||
class BossMechanicTest(unittest.TestCase):
|
||||
def _battle(self):
|
||||
a = make_leveled("Rose", TEAM_PLAYER, 20, 1)
|
||||
a.max_hp = 5000
|
||||
a.hp = 5000
|
||||
u = bosses.make_boss("Stones", 20)
|
||||
b = Battle([a], [u])
|
||||
b.start()
|
||||
return b, a, u
|
||||
|
||||
def test_landslide_blocked_until_three_stacks(self):
|
||||
b, a, u = self._battle()
|
||||
b.current = u
|
||||
pebble, landslide = u.skills
|
||||
u.acted = False
|
||||
self.assertFalse(b.use_skill(u, landslide, a)) # 0 stacks: blocked
|
||||
self.assertEqual(b.valid_skill_targets(u, landslide), [])
|
||||
for _ in range(3):
|
||||
u.acted = False
|
||||
self.assertTrue(b.use_skill(u, pebble, a))
|
||||
self.assertEqual(u.status["boss_stacks"], 3)
|
||||
u.acted = False
|
||||
self.assertTrue(b.use_skill(u, landslide, a)) # now available
|
||||
self.assertEqual(u.status["boss_stacks"], 0) # released
|
||||
|
||||
def test_boss_stacks_cap_at_three(self):
|
||||
b, a, u = self._battle()
|
||||
b.current = u
|
||||
pebble, _ = u.skills
|
||||
for _ in range(6):
|
||||
u.acted = False
|
||||
self.assertTrue(b.use_skill(u, pebble, a))
|
||||
self.assertEqual(u.status["boss_stacks"], 3) # never exceeds 3
|
||||
|
||||
def test_boss_ai_pebbles_then_landslides_then_repeats(self):
|
||||
b, a, u = self._battle()
|
||||
moves = []
|
||||
guard = 0
|
||||
while len(moves) < 7 and guard < 120 and b.winner is None:
|
||||
guard += 1
|
||||
if b.current is u and not u.acted:
|
||||
before = u.status.get("boss_stacks", 0)
|
||||
used = b.enemy_ai(u)
|
||||
after = u.status.get("boss_stacks", 0)
|
||||
if used:
|
||||
moves.append("Pebble" if after > before else
|
||||
("Landslide" if before >= 3 and after == 0 else "?"))
|
||||
b.next_turn()
|
||||
self.assertEqual(moves[:4], ["Pebble", "Pebble", "Pebble", "Landslide"])
|
||||
self.assertEqual(moves[4:], ["Pebble", "Pebble", "Pebble"])
|
||||
|
||||
def test_landslide_hits_harder_than_pebble(self):
|
||||
b, a, u = self._battle()
|
||||
b.current = u
|
||||
pebble, landslide = u.skills
|
||||
for _ in range(3):
|
||||
u.acted = False
|
||||
b.use_skill(u, pebble, a)
|
||||
u.acted = False
|
||||
pebble_dmg = b._hit_damage.get(id(a), 0)
|
||||
a.hp = a.max_hp # reset before landslide
|
||||
u.status["boss_stacks"] = 3
|
||||
b.use_skill(u, landslide, a)
|
||||
landslide_dmg = b._hit_damage.get(id(a), 0)
|
||||
self.assertGreater(landslide_dmg, pebble_dmg * 2)
|
||||
|
||||
|
||||
class BossRewardTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
data = collection.load()
|
||||
data["owned"] = ["Rose"]
|
||||
data["bosses_fought"] = []
|
||||
data["runes"] = []
|
||||
collection.save(data)
|
||||
|
||||
def test_mark_boss_fought(self):
|
||||
self.assertEqual(collection.bosses_fought(), set())
|
||||
collection.mark_boss_fought("Stones")
|
||||
self.assertEqual(collection.bosses_fought(), {"Stones"})
|
||||
|
||||
def test_boss_rune_drop_is_high_chance_and_select(self):
|
||||
colors = set()
|
||||
total = 0
|
||||
for _ in range(300):
|
||||
r = collection.boss_rune_drop("Stones")
|
||||
if r:
|
||||
total += 1
|
||||
self.assertIn(r["color"], ("ruby", "amethyst"))
|
||||
colors.add(r["color"])
|
||||
self.assertGreaterEqual(total, 150) # ~80% chance
|
||||
self.assertEqual(colors, {"ruby", "amethyst"})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -103,7 +103,8 @@ class RelicApplyTest(unittest.TestCase):
|
||||
def test_shield_relic(self):
|
||||
b = _battle("iron_bark")
|
||||
for u in b.players:
|
||||
self.assertGreaterEqual(u.status.get(S_SHIELD, 0), int(u.max_hp * 0.09))
|
||||
total = sum(amount for amount, _ in u.status.get(S_SHIELD, []))
|
||||
self.assertGreaterEqual(total, int(u.max_hp * 0.09))
|
||||
|
||||
def test_pct_hp_relic(self):
|
||||
base = make_leveled("Zinnia", TEAM_PLAYER, 1, 1)
|
||||
@@ -156,7 +157,8 @@ class RelicApplyTest(unittest.TestCase):
|
||||
def test_multiple_relics_stack(self):
|
||||
b = _battle("iron_bark", "morning_dew", "chilling_gust")
|
||||
for u in b.players:
|
||||
self.assertGreaterEqual(u.status.get(S_SHIELD, 0), 1)
|
||||
total = sum(amount for amount, _ in u.status.get(S_SHIELD, []))
|
||||
self.assertGreaterEqual(total, 1)
|
||||
self.assertEqual(u.status[S_ATTACK_UP], (10, 1))
|
||||
for u in b.enemies:
|
||||
self.assertEqual(u.status["speed_down"], (8, 1))
|
||||
|
||||
+44
-15
@@ -131,9 +131,9 @@ class RuneTest(unittest.TestCase):
|
||||
self.assertIn(r2["id"], free)
|
||||
self.assertNotIn(r1["id"], free)
|
||||
|
||||
def _ten_runes(self, data):
|
||||
def _sac_runes(self, data):
|
||||
ids = []
|
||||
for _ in range(10):
|
||||
for _ in range(runes.RUNE_SACRIFICE_COST):
|
||||
ids.append(collection.add_rune("sapphire", data=data, save_now=False)["id"])
|
||||
return ids
|
||||
|
||||
@@ -141,20 +141,20 @@ class RuneTest(unittest.TestCase):
|
||||
r = collection.add_rune("ruby")
|
||||
data = collection.load()
|
||||
data["runes"][0]["pct"] = 25
|
||||
sac = self._ten_runes(data)
|
||||
sac = self._sac_runes(data)
|
||||
collection.save(data)
|
||||
err = collection.reroll_rune(r["id"], sac)
|
||||
self.assertEqual(err, "")
|
||||
new_pct = collection.load()["runes"][0]["pct"]
|
||||
self.assertGreaterEqual(new_pct, 26) # at least 1% better
|
||||
self.assertLessEqual(new_pct, 35)
|
||||
self.assertEqual(len(collection.load()["runes"]), 1) # 10 consumed
|
||||
self.assertEqual(len(collection.load()["runes"]), 1) # cost consumed
|
||||
|
||||
def test_reroll_brown_caps_at_15(self):
|
||||
r = collection.add_rune("brown")
|
||||
data = collection.load()
|
||||
data["runes"][0]["pct"] = 5
|
||||
sac = self._ten_runes(data)
|
||||
sac = self._sac_runes(data)
|
||||
collection.save(data)
|
||||
err = collection.reroll_rune(r["id"], sac)
|
||||
self.assertEqual(err, "")
|
||||
@@ -166,27 +166,27 @@ class RuneTest(unittest.TestCase):
|
||||
r = collection.add_rune("ruby")
|
||||
data = collection.load()
|
||||
data["runes"][0]["pct"] = 35
|
||||
sac = self._ten_runes(data)
|
||||
sac = self._sac_runes(data)
|
||||
collection.save(data)
|
||||
err = collection.reroll_rune(r["id"], sac)
|
||||
self.assertEqual(err, "Already max roll")
|
||||
self.assertEqual(len(collection.load()["runes"]), 11) # nothing consumed
|
||||
self.assertEqual(len(collection.load()["runes"]), 1 + runes.RUNE_SACRIFICE_COST) # nothing consumed
|
||||
|
||||
def test_reroll_requires_exactly_ten(self):
|
||||
def test_reroll_requires_exact_sacrifice_count(self):
|
||||
r = collection.add_rune("ruby")
|
||||
data = collection.load()
|
||||
data["runes"][0]["pct"] = 25
|
||||
sac = self._ten_runes(data)
|
||||
sac = self._sac_runes(data)
|
||||
collection.save(data)
|
||||
self.assertEqual(collection.reroll_rune(r["id"], sac[:9]),
|
||||
"Pick exactly 10 runes to sacrifice")
|
||||
self.assertEqual(collection.reroll_rune(r["id"], sac[:runes.RUNE_SACRIFICE_COST - 1]),
|
||||
f"Pick exactly {runes.RUNE_SACRIFICE_COST} runes to sacrifice")
|
||||
|
||||
def test_reroll_rejects_target_in_sacrifices(self):
|
||||
r = collection.add_rune("ruby")
|
||||
data = collection.load()
|
||||
data["runes"][0]["pct"] = 25
|
||||
sac = self._ten_runes(data)
|
||||
sac[0] = r["id"] # 10 unique runes, but includes the upgrade target
|
||||
sac = self._sac_runes(data)
|
||||
sac[0] = r["id"] # unique runes, but includes the upgrade target
|
||||
collection.save(data)
|
||||
self.assertEqual(collection.reroll_rune(r["id"], sac),
|
||||
"Cannot sacrifice the rune being upgraded")
|
||||
@@ -196,7 +196,7 @@ class RuneTest(unittest.TestCase):
|
||||
r2 = collection.add_rune("sapphire")
|
||||
data = collection.load()
|
||||
data["runes"][0]["pct"] = 25
|
||||
sac = self._ten_runes(data)
|
||||
sac = self._sac_runes(data)
|
||||
sac[0] = r2["id"] # sacrifice the equipped rune
|
||||
collection.save(data)
|
||||
self._evolve_to(self.plant, 1, 10, 2)
|
||||
@@ -338,6 +338,16 @@ class RuneTest(unittest.TestCase):
|
||||
self.assertEqual(len(collection.rune_inventory()), before)
|
||||
self.assertEqual(collection.available_coins(), 50)
|
||||
|
||||
def test_pearl_is_t2_crit_rune(self):
|
||||
self.assertEqual(runes.tier("pearl"), 2)
|
||||
self.assertEqual(runes.effect_for("pearl"), "crit")
|
||||
self.assertEqual(runes.max_pct("pearl"), 40)
|
||||
self.assertEqual(runes.roll_range("pearl"), "20–40%")
|
||||
for _ in range(50):
|
||||
self.assertGreaterEqual(runes.roll_pct("pearl"), 20)
|
||||
self.assertLessEqual(runes.roll_pct("pearl"), 40)
|
||||
self.assertEqual(runes.name("pearl"), "Pearl Rune")
|
||||
|
||||
|
||||
class RuneBattleEffectTest(unittest.TestCase):
|
||||
def _unit(self, name="A", team=TEAM_PLAYER, ptype="radiant", atk=50, spd=5,
|
||||
@@ -378,7 +388,7 @@ class RuneBattleEffectTest(unittest.TestCase):
|
||||
b = Battle([self._unit()], [t])
|
||||
b.start()
|
||||
b._shield(t, 100)
|
||||
self.assertEqual(t.status["shield"], 130)
|
||||
self.assertEqual(sum(a for a, _ in t.status["shield"]), 130)
|
||||
|
||||
def test_yellow_rune_boosts_healing(self):
|
||||
heal = Skill("Mend", power=40, kind="heal", target="ally")
|
||||
@@ -412,6 +422,25 @@ class RuneBattleEffectTest(unittest.TestCase):
|
||||
b.use_skill(a, hit, t)
|
||||
self.assertEqual(t.status["wilt"][1], 3) # lasts 3 turns
|
||||
|
||||
def test_pearl_rune_raises_crit_chance(self):
|
||||
a = self._unit(runes=[("crit", 0.30)])
|
||||
b = Battle([a], [self._unit("T", TEAM_ENEMY, "garden")])
|
||||
b.start()
|
||||
self.assertAlmostEqual(b._crit(a), 0.30)
|
||||
|
||||
def test_pearl_runes_stack_additively(self):
|
||||
a = self._unit(runes=[("crit", 0.20), ("crit", 0.20)])
|
||||
b = Battle([a], [self._unit("T", TEAM_ENEMY, "garden")])
|
||||
b.start()
|
||||
self.assertAlmostEqual(b._crit(a), 0.40)
|
||||
|
||||
def test_pearl_rune_adds_on_top_of_base_crit(self):
|
||||
a = self._unit(runes=[("crit", 0.25)])
|
||||
a.crit = 0.10
|
||||
b = Battle([a], [self._unit("T", TEAM_ENEMY, "garden")])
|
||||
b.start()
|
||||
self.assertAlmostEqual(b._crit(a), 0.35)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -219,6 +219,8 @@ class AutoFarmerTest(unittest.TestCase):
|
||||
data["fertilizer"] = 10
|
||||
data["coins"] = 100
|
||||
data["auto_farmer"] = False
|
||||
data["auto_planter"] = False
|
||||
data["auto_planter_active"] = False
|
||||
collection.save(data)
|
||||
|
||||
def _stalled_plot(self):
|
||||
@@ -308,6 +310,100 @@ class AutoFarmerTest(unittest.TestCase):
|
||||
self.assertEqual(len(state["seeds"]), 1) # seed not planted
|
||||
self.assertEqual(state["plots"][0]["stage"], 1) # stalled plot advanced
|
||||
|
||||
def test_buy_auto_planter(self):
|
||||
data = collection.load()
|
||||
data["coins"] = 300
|
||||
collection.save(data)
|
||||
self.assertEqual(collection.buy_auto_planter(), "")
|
||||
self.assertTrue(collection.auto_planter_owned())
|
||||
self.assertTrue(collection.auto_planter_active())
|
||||
self.assertEqual(collection.available_coins(), 100)
|
||||
self.assertNotEqual(collection.buy_auto_planter(), "") # already owned
|
||||
|
||||
def test_buy_auto_planter_requires_coins(self):
|
||||
data = collection.load()
|
||||
data["coins"] = 50
|
||||
collection.save(data)
|
||||
self.assertNotEqual(collection.buy_auto_planter(), "")
|
||||
self.assertFalse(collection.auto_planter_owned())
|
||||
|
||||
def test_toggle_auto_planter(self):
|
||||
data = collection.load()
|
||||
data["coins"] = 300
|
||||
collection.save(data)
|
||||
collection.buy_auto_planter()
|
||||
self.assertFalse(collection.toggle_auto_planter()) # turned off
|
||||
self.assertFalse(collection.auto_planter_active())
|
||||
self.assertTrue(collection.toggle_auto_planter()) # back on
|
||||
|
||||
def test_auto_plant_plants_seed_into_empty_plot(self):
|
||||
data = collection.load()
|
||||
data["coins"] = 300
|
||||
data["soil"]["seeds"] = [{"plant": "Rose"}, {"plant": "Lavender"}]
|
||||
collection.save(data)
|
||||
self.assertEqual(collection.buy_auto_planter(), "")
|
||||
self.assertEqual(collection.auto_plant(), "")
|
||||
state = collection.soil_state()
|
||||
self.assertEqual(len(state["plots"]), 1)
|
||||
self.assertEqual(state["plots"][0]["plant"], "Rose") # first seed planted
|
||||
self.assertEqual(len(state["seeds"]), 1)
|
||||
|
||||
def test_auto_plant_respects_toggle_and_requirements(self):
|
||||
data = collection.load()
|
||||
data["coins"] = 300
|
||||
data["soil"]["seeds"] = [{"plant": "Rose"}]
|
||||
collection.save(data)
|
||||
self.assertEqual(collection.auto_plant(), "No auto planter") # not owned
|
||||
collection.buy_auto_planter()
|
||||
collection.toggle_auto_planter() # turn off
|
||||
self.assertEqual(collection.auto_plant(), "Auto planter off")
|
||||
collection.toggle_auto_planter()
|
||||
data = collection.load()
|
||||
data["soil"]["seeds"] = []
|
||||
collection.save(data)
|
||||
self.assertEqual(collection.auto_plant(), "") # nothing to plant -> idle
|
||||
self.assertEqual(len(collection.soil_state()["plots"]), 0)
|
||||
|
||||
def test_auto_plant_wont_overfill_plots(self):
|
||||
data = collection.load()
|
||||
data["coins"] = 300
|
||||
data["soil"]["seeds"] = [{"plant": "Rose"}, {"plant": "Lavender"}]
|
||||
data["soil"]["plots"] = [{"plant": "Rose", "stage_started_at": 0, "stage": 0, "water": 0}]
|
||||
collection.save(data)
|
||||
self.assertEqual(collection.buy_auto_planter(), "")
|
||||
self.assertEqual(collection.auto_plant(), "") # no free plot -> idle
|
||||
self.assertEqual(len(collection.soil_state()["plots"]), 1)
|
||||
|
||||
|
||||
class PackShopTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
data = collection.load()
|
||||
data["coins"] = 500
|
||||
data["packs"] = {t: 0 for t in collection.PACK_TYPES}
|
||||
collection.save(data)
|
||||
|
||||
def test_buy_lesser_and_general(self):
|
||||
self.assertEqual(collection.buy_pack("lesser"), "")
|
||||
self.assertEqual(collection.buy_pack("general"), "")
|
||||
self.assertEqual(collection.buy_pack("garden"), "")
|
||||
self.assertEqual(collection.available_coins(), 500 - 50 - 100 - 100)
|
||||
packs = collection.pack_counts()
|
||||
self.assertEqual(packs["lesser"], 1)
|
||||
self.assertEqual(packs["general"], 1)
|
||||
self.assertEqual(packs["garden"], 1)
|
||||
|
||||
def test_tower_pack_cannot_be_bought(self):
|
||||
self.assertEqual(collection.buy_pack("tower"), "That pack can't be bought")
|
||||
self.assertEqual(collection.pack_counts().get("tower", 0), 0)
|
||||
self.assertEqual(collection.available_coins(), 500)
|
||||
|
||||
def test_buy_pack_requires_coins(self):
|
||||
data = collection.load()
|
||||
data["coins"] = 30
|
||||
collection.save(data)
|
||||
self.assertEqual(collection.buy_pack("lesser"), "Not enough coins")
|
||||
self.assertEqual(collection.pack_counts().get("lesser", 0), 0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user