143 lines
3.8 KiB
Python
143 lines
3.8 KiB
Python
# tests boss : vexaris + morveth + transitions
|
|
|
|
from unittest.mock import AsyncMock
|
|
|
|
from lobby import Lobby, LobbyPlayer
|
|
from game_loop import GameLoop
|
|
from constants import (
|
|
VEXARIS_HP, VEXARIS_HITBOX_RADIUS,
|
|
MORVETH_HP,
|
|
WAVE_BOSSES, SOUL_REWARDS,
|
|
)
|
|
from waves import WaveManager
|
|
from constants import PREPARATION_DURATION, TICK_DURATION
|
|
|
|
|
|
def make_lobby():
|
|
lobby = Lobby("TEST1", "p1")
|
|
lobby.players = [
|
|
LobbyPlayer("p1", "Alice", "kael"),
|
|
LobbyPlayer("p2", "Bob", "seris"),
|
|
LobbyPlayer("p3", "Clara", "aldric"),
|
|
]
|
|
return lobby
|
|
|
|
|
|
def _ticks(wm, n, enemy_count=0):
|
|
events = []
|
|
for _ in range(n):
|
|
events.extend(wm.tick(lambda t: None, enemy_count))
|
|
return events
|
|
|
|
|
|
def _complete_wave_phases(wm):
|
|
for _ in range(200_000):
|
|
wm.tick(lambda t: None, 0)
|
|
if wm.state != "combat":
|
|
return
|
|
raise AssertionError("vague combat infinie")
|
|
|
|
|
|
def _prep_ticks():
|
|
return int(PREPARATION_DURATION / TICK_DURATION) + 1
|
|
|
|
|
|
def _skip_initial_prep(wm):
|
|
while wm.state == "preparation":
|
|
wm.tick(lambda t: None, 0)
|
|
|
|
|
|
def test_wave_bosses():
|
|
assert WAVE_BOSSES[2] == "vexaris"
|
|
assert WAVE_BOSSES[3] == "morveth"
|
|
assert 1 not in WAVE_BOSSES
|
|
|
|
|
|
def test_spawn_vexaris():
|
|
loop = GameLoop(make_lobby(), AsyncMock())
|
|
loop.spawn_enemy("vexaris")
|
|
boss = loop.state.enemies[0]
|
|
assert boss.type == "vexaris"
|
|
assert boss.hp == VEXARIS_HP
|
|
assert boss.is_boss is True
|
|
|
|
|
|
def test_boss_tape_joueur_au_contact():
|
|
loop = GameLoop(make_lobby(), AsyncMock())
|
|
loop.spawn_enemy("vexaris")
|
|
boss = loop.state.enemies[0]
|
|
player = loop.state.players[0]
|
|
boss.x, boss.y = player.x, player.y
|
|
hp_initial = player.hp
|
|
loop._update_enemies()
|
|
assert player.hp < hp_initial
|
|
|
|
|
|
def test_boss_kill_donne_des_ames():
|
|
loop = GameLoop(make_lobby(), AsyncMock())
|
|
loop.spawn_enemy("vexaris")
|
|
boss = loop.state.enemies[0]
|
|
player = loop.state.players[0]
|
|
initial_souls = player.souls
|
|
from game_state import ProjectileState
|
|
loop.state.projectiles.append(ProjectileState(
|
|
id="pr_test", owner_id=player.id,
|
|
x=boss.x, y=boss.y, vx=0, vy=0,
|
|
damage=VEXARIS_HP, radius=VEXARIS_HITBOX_RADIUS + 1, ttl=1.0,
|
|
))
|
|
loop._update_projectiles()
|
|
assert player.souls == initial_souls + SOUL_REWARDS["vexaris"]
|
|
|
|
|
|
def test_spawn_morveth():
|
|
loop = GameLoop(make_lobby(), AsyncMock())
|
|
loop.spawn_enemy("morveth")
|
|
boss = loop.state.enemies[0]
|
|
assert boss.type == "morveth"
|
|
assert boss.hp == MORVETH_HP
|
|
|
|
|
|
def _morveth_at_pct(pct):
|
|
loop = GameLoop(make_lobby(), AsyncMock())
|
|
loop.spawn_enemy("morveth")
|
|
boss = loop.state.enemies[0]
|
|
boss.hp = max(1, int(boss.max_hp * pct))
|
|
boss.data.update({
|
|
"charge_cd": 99.0, "charge_timer": 0.0,
|
|
"charge_tx": 0.0, "charge_ty": 0.0,
|
|
"is_charging": False,
|
|
"burst_cd": 99.0,
|
|
"general_1_spawned": False,
|
|
"general_2_spawned": False,
|
|
"general_3_spawned": False,
|
|
})
|
|
return loop
|
|
|
|
|
|
def test_morveth_invoque_general_a_75():
|
|
loop = _morveth_at_pct(0.74)
|
|
loop._update_enemies()
|
|
generals = [e for e in loop.state.enemies if e.type == "general"]
|
|
assert len(generals) == 1
|
|
|
|
|
|
def test_morveth_pas_de_double_invocation():
|
|
loop = _morveth_at_pct(0.74)
|
|
loop._update_enemies()
|
|
loop._update_enemies()
|
|
generals = [e for e in loop.state.enemies if e.type == "general"]
|
|
assert len(generals) == 1
|
|
|
|
|
|
def test_victory_apres_morveth():
|
|
wm = WaveManager()
|
|
_skip_initial_prep(wm)
|
|
_complete_wave_phases(wm)
|
|
_ticks(wm, _prep_ticks())
|
|
_complete_wave_phases(wm)
|
|
_ticks(wm, 3) # Vexaris spawn + kill
|
|
_ticks(wm, _prep_ticks())
|
|
_complete_wave_phases(wm)
|
|
_ticks(wm, 3) # Morveth spawn + kill
|
|
assert wm.state == "victory"
|