"""Tests for kill and death tracking in combat.""" import time import pytest from mudlib.combat.engine import ( process_combat, start_encounter, ) from mudlib.combat.moves import CombatMove from mudlib.entity import Mob from mudlib.player import accumulate_play_time @pytest.fixture def punch_move(): """Create a basic punch move for testing.""" return CombatMove( name="punch right", move_type="attack", stamina_cost=5.0, timing_window_ms=800, damage_pct=0.15, countered_by=[], resolve_hit="{attacker} hits {defender}!", resolve_miss="{defender} dodges!", announce="{attacker} punches!", ) @pytest.mark.asyncio async def test_player_kills_mob_increments_stats(player, test_zone, punch_move): """Player kills mob -> kills incremented, mob_kills tracked.""" # Create a goblin mob goblin = Mob(name="goblin", x=0, y=0) goblin.location = test_zone test_zone._contents.append(goblin) # Start encounter encounter = start_encounter(player, goblin) # Execute attack encounter.attack(punch_move) # Advance past telegraph (0.3s) + window (0.8s) encounter.tick(time.monotonic() + 0.31) # -> WINDOW encounter.tick(time.monotonic() + 1.2) # -> RESOLVE # Set defender to very low pl so damage kills them goblin.pl = 1.0 # Process combat (this will resolve and end encounter) await process_combat() # Verify stats assert player.kills == 1 assert player.mob_kills["goblin"] == 1 @pytest.mark.asyncio async def test_player_killed_by_mob_increments_deaths(player, test_zone, punch_move): """Player killed by mob -> deaths incremented.""" # Create a goblin mob goblin = Mob(name="goblin", x=0, y=0) goblin.location = test_zone test_zone._contents.append(goblin) # Start encounter with mob as attacker encounter = start_encounter(goblin, player) # Execute attack encounter.attack(punch_move) # Advance to RESOLVE encounter.tick(time.monotonic() + 0.31) encounter.tick(time.monotonic() + 1.2) # Set player to low pl so they die player.pl = 1.0 # Process combat await process_combat() # Verify deaths incremented assert player.deaths == 1 @pytest.mark.asyncio async def test_multiple_kills_accumulate(player, test_zone, punch_move): """After killing 3 goblins, player.kills == 3, player.mob_kills["goblin"] == 3.""" for _ in range(3): # Create goblin goblin = Mob(name="goblin", x=0, y=0) goblin.location = test_zone test_zone._contents.append(goblin) # Create and resolve encounter encounter = start_encounter(player, goblin) encounter.attack(punch_move) encounter.tick(time.monotonic() + 0.31) encounter.tick(time.monotonic() + 1.2) goblin.pl = 1.0 await process_combat() # Verify accumulated kills assert player.kills == 3 assert player.mob_kills["goblin"] == 3 def test_session_time_tracking(player): """Session time tracking accumulates correctly.""" # Set session start to a known time start_time = time.monotonic() player.session_start = start_time # Simulate 5 seconds passing time.sleep(0.01) # Small real delay to ensure monotonic() advances player.session_start = start_time # Reset for predictable test # Mock time to be 5 seconds later import unittest.mock with unittest.mock.patch("time.monotonic", return_value=start_time + 5.0): accumulate_play_time(player) # Should have accumulated 5 seconds assert player.play_time_seconds == 5.0 # Session start should be reset to current time with unittest.mock.patch("time.monotonic", return_value=start_time + 5.0): assert player.session_start == start_time + 5.0 def test_accumulate_play_time_multiple_sessions(player): """Multiple accumulation calls should add up correctly.""" start_time = time.monotonic() player.session_start = start_time import unittest.mock # First accumulation: 3 seconds with unittest.mock.patch("time.monotonic", return_value=start_time + 3.0): accumulate_play_time(player) assert player.play_time_seconds == 3.0 # Second accumulation: 2 more seconds (from reset point) with unittest.mock.patch("time.monotonic", return_value=start_time + 5.0): accumulate_play_time(player) # Should have 3 + 2 = 5 total assert player.play_time_seconds == 5.0