diff --git a/src/mudlib/entity.py b/src/mudlib/entity.py index 2b166fd..216d750 100644 --- a/src/mudlib/entity.py +++ b/src/mudlib/entity.py @@ -20,6 +20,7 @@ class Entity(Object): y: int = 0 # Combat stats pl: float = 100.0 # power level (health and damage multiplier) + max_pl: float = 100.0 # maximum power level stamina: float = 100.0 # current stamina max_stamina: float = 100.0 # stamina ceiling defense_locked_until: float = 0.0 # monotonic time when defense recovery ends diff --git a/src/mudlib/gmcp.py b/src/mudlib/gmcp.py index eb161e4..1bf22f6 100644 --- a/src/mudlib/gmcp.py +++ b/src/mudlib/gmcp.py @@ -12,11 +12,12 @@ log = logging.getLogger(__name__) def send_char_vitals(player: Player) -> None: - """Send Char.Vitals — pl, stamina, max_stamina.""" - player.writer.send_gmcp( + """Send Char.Vitals — pl, max_pl, stamina, max_stamina.""" + player.send_gmcp( "Char.Vitals", { "pl": round(player.pl, 1), + "max_pl": round(player.max_pl, 1), "stamina": round(player.stamina, 1), "max_stamina": round(player.max_stamina, 1), }, @@ -25,7 +26,7 @@ def send_char_vitals(player: Player) -> None: def send_char_status(player: Player) -> None: """Send Char.Status — flying, resting, mode, in_combat.""" - player.writer.send_gmcp( + player.send_gmcp( "Char.Status", { "flying": player.flying, @@ -53,13 +54,17 @@ def send_room_info(player: Player) -> None: ("south", 0, 1), ("east", 1, 0), ("west", -1, 0), + ("northeast", 1, -1), + ("northwest", -1, -1), + ("southeast", 1, 1), + ("southwest", -1, 1), ] for name, dx, dy in directions: nx, ny = player.x + dx, player.y + dy if zone.is_passable(nx, ny): exits.append(name) - player.writer.send_gmcp( + player.send_gmcp( "Room.Info", { "zone": zone.name, @@ -90,7 +95,7 @@ def send_map_data(player: Player) -> None: row.append(zone.terrain[wy][wx]) rows.append(row) - player.writer.send_gmcp( + player.send_gmcp( "Room.Map", { "x": player.x, @@ -103,10 +108,10 @@ def send_map_data(player: Player) -> None: def send_msdp_vitals(player: Player) -> None: """Send MSDP variable updates for real-time gauges.""" - player.writer.send_msdp( + player.send_msdp( { "HEALTH": str(round(player.pl, 1)), - "HEALTH_MAX": str(round(player.pl, 1)), + "HEALTH_MAX": str(round(player.max_pl, 1)), "STAMINA": str(round(player.stamina, 1)), "STAMINA_MAX": str(round(player.max_stamina, 1)), } diff --git a/tests/test_gmcp.py b/tests/test_gmcp.py index 413da2d..f163300 100644 --- a/tests/test_gmcp.py +++ b/tests/test_gmcp.py @@ -55,6 +55,7 @@ def player(mock_writer, test_zone): p = Player(name="Goku", x=10, y=10, writer=mock_writer) p.location = test_zone p.pl = 85.5 + p.max_pl = 100.0 p.stamina = 42.3 p.max_stamina = 100.0 players[p.name] = p @@ -69,6 +70,7 @@ def test_send_char_vitals(player): "Char.Vitals", { "pl": 85.5, + "max_pl": 100.0, "stamina": 42.3, "max_stamina": 100.0, }, @@ -131,13 +133,15 @@ def test_send_room_info(player): assert data["x"] == 10 assert data["y"] == 10 assert data["terrain"] == "." - # All adjacent tiles should be passable except north (mountain at 10,5) - # north is y-1 = 9, but mountain is at y=5 - # Player at 10,10 so adjacent tiles are all "." and passable + # All adjacent tiles should be passable (player at 10,10, all tiles are ".") assert "north" in data["exits"] assert "south" in data["exits"] assert "east" in data["exits"] assert "west" in data["exits"] + assert "northeast" in data["exits"] + assert "northwest" in data["exits"] + assert "southeast" in data["exits"] + assert "southwest" in data["exits"] def test_send_room_info_blocked_exit(player, test_zone): @@ -215,7 +219,7 @@ def test_send_msdp_vitals(player): player.writer.send_msdp.assert_called_once_with( { "HEALTH": "85.5", - "HEALTH_MAX": "85.5", + "HEALTH_MAX": "100.0", "STAMINA": "42.3", "STAMINA_MAX": "100.0", } @@ -296,6 +300,7 @@ async def test_char_vitals_sent_on_rest_complete(player): assert second_call[0][0] == "Char.Vitals" assert second_call[0][1] == { "pl": round(player.pl, 1), + "max_pl": round(player.max_pl, 1), "stamina": round(player.max_stamina, 1), "max_stamina": round(player.max_stamina, 1), } @@ -364,6 +369,7 @@ async def test_char_vitals_sent_on_combat_resolve(): attacker_call = mock_writer_1.send_gmcp.call_args[0] assert attacker_call[0] == "Char.Vitals" assert "pl" in attacker_call[1] + assert "max_pl" in attacker_call[1] assert "stamina" in attacker_call[1] assert "max_stamina" in attacker_call[1] @@ -371,6 +377,7 @@ async def test_char_vitals_sent_on_combat_resolve(): defender_call = mock_writer_2.send_gmcp.call_args[0] assert defender_call[0] == "Char.Vitals" assert "pl" in defender_call[1] + assert "max_pl" in defender_call[1] assert "stamina" in defender_call[1] assert "max_stamina" in defender_call[1]