Commit graph

77 commits

Author SHA1 Message Date
b90e61c4fc
Add tests for save overwrite auto-confirm and failure detection 2026-02-09 17:11:59 -05:00
de58209fd0
Add test for chunked IF response reading 2026-02-09 17:07:43 -05:00
43fce6a4ed
Fix line length issues in IF session tests 2026-02-09 16:45:36 -05:00
76488139c8
Add error handling for process communication failures in IF save/restore 2026-02-09 16:45:12 -05:00
bb1f9bbbd8
Prevent double-save when quitting IF sessions 2026-02-09 16:44:13 -05:00
8893525647
Sanitize player names in IF save paths to prevent path traversal 2026-02-09 16:43:09 -05:00
57afe9a3ce
Wire restore into play command
When starting an IF game, check for existing save file and restore
if present. Shows 'restoring saved game...' message and broadcasts
restored game state to spectators.

Also cleaned up redundant tests that didn't properly mock the
auto-save functionality now present in ::quit and stop().
2026-02-09 16:39:15 -05:00
6879da0964
Wire ::save escape command 2026-02-09 16:32:50 -05:00
5f0fec14ef
Add restore functionality to IF sessions 2026-02-09 16:32:23 -05:00
e2bd260538
Add save functionality to IF sessions 2026-02-09 16:31:47 -05:00
6ea82a8496
Add save directory helpers for IF sessions 2026-02-09 16:31:12 -05:00
3dd095b9ea
Add broadcast_to_spectators helper
Helper function sends messages to all players at the same x,y coordinates
as the source player, skipping the source player themselves. Used for IF
spectator broadcasting.
2026-02-09 16:24:48 -05:00
c3f8c8cf12
Add spectator broadcasting tests for IF mode
Tests verify:
- Spectators at same location see IF output with player name header
- Spectators at different locations see nothing
- Game start intro broadcasts to spectators
- broadcast_to_spectators skips the playing player
- Multiple spectators all receive messages

Tests currently fail as broadcast_to_spectators not yet implemented.
2026-02-09 16:24:07 -05:00
bc69f46d1a
List stories when cannot find 2026-02-09 16:14:21 -05:00
6308248d14
Fix IF session cleanup on failure and player disconnect 2026-02-09 16:11:28 -05:00
8a8e3dd6e8
Fix line-too-long lint errors in IF mode tests 2026-02-09 16:10:29 -05:00
b133f2febe
Add play command for starting interactive fiction games 2026-02-09 16:10:29 -05:00
dc342224b1
Wire IF mode into server shell loop and player model 2026-02-09 15:57:24 -05:00
d210033f33
Add IFSession class for interactive fiction subprocess management
TDD implementation of IFSession that manages a dfrotz subprocess.
IFResponse dataclass follows the editor pattern with output/done fields.
IFSession handles spawning dfrotz, routing input, and detecting the prompt.
Escape commands (::quit, ::help) are handled without sending to dfrotz.
2026-02-09 15:54:47 -05:00
af941b329b
Add spawn command and wire mobs into server
Phase 6: spawn command creates mobs at player position from loaded
templates. Server loads mob templates from content/mobs/ at startup,
injects world into combat/commands module, and runs process_mobs()
each game loop tick after process_combat().
2026-02-09 11:54:29 -05:00
d15238eb4e
Add mob AI for combat decisions
Phase 5: process_mobs() runs each tick, handling mob attack and defense
decisions. Mobs pick random attacks from their move list when IDLE,
swap roles if needed, and attempt defense during TELEGRAPH/WINDOW with
a 40% chance of correct counter. 1-second cooldown between actions.
Training dummies with empty moves never fight back.
2026-02-09 11:54:29 -05:00
91c1af86e2
Handle mob defeat in combat resolution
Phase 4: when combat ends, determine winner/loser. If the loser is a
Mob, despawn it and send a victory message to the winner. If the loser
is a Player fighting a Mob, send a defeat message instead.
2026-02-09 11:54:29 -05:00
ca53357730
Render mobs as * in viewport
Phase 3: look command now collects alive mob positions using the same
wrapping-aware relative position calc as players, and renders them as *
with the same priority as other players (after @ but before effects).
2026-02-09 11:54:29 -05:00
e6bfd77464
Add mob target resolution in combat commands
Phase 2: do_attack now searches the mobs registry after players dict
when resolving a target name. Players always take priority over mobs
with the same name. World instance injected into combat/commands module
for wrapping-aware mob proximity checks.
2026-02-09 11:54:29 -05:00
84cd75e3a3
Add mob templates, registry, and spawn/despawn/query
Phase 1 of fightable mobs: MobTemplate dataclass loaded from TOML,
global mobs list, spawn_mob/despawn_mob/get_nearby_mob with
wrapping-aware distance. Mob entity gets moves and next_action_at fields.
2026-02-09 11:54:29 -05:00
f36085c921
Add rest command for stamina recovery 2026-02-08 22:16:47 -05:00
0f7404cb12
Fix prefix matching for combat move variants
The variant handler now supports prefix matching for directional variants.
This allows 'pa hi' to match 'parry high', 'pa lo' to match 'parry low', etc.

Implementation:
- First tries exact match on variant key
- Falls back to prefix matching if no exact match
- Returns unique match if exactly one variant starts with the prefix
- Shows disambiguation message if multiple variants match
- Shows error with valid options if no variants match

Tests cover exact match, prefix match, ambiguous prefix, no match,
single-char prefix, case-insensitivity, and preservation of target args.
2026-02-08 14:41:40 -05:00
77c2e40e0e
Add reload command for hot-reloading TOML content 2026-02-08 14:32:51 -05:00
8c83130f67
Add test for alias exact match over prefix 2026-02-08 13:48:32 -05:00
6e9a89f21e
Update tests for clean command listings 2026-02-08 13:41:36 -05:00
7ae56106d6
Update tests for alias removal 2026-02-08 13:39:58 -05:00
841714d6ca
Update tests for alias removal 2026-02-08 13:37:40 -05:00
e00591b6bf
Fix test fixture to not interfere with other tests 2026-02-08 13:34:46 -05:00
20f33f45e1
Implement standalone help command 2026-02-08 13:33:46 -05:00
7c313ae307
Implement prefix matching in dispatch 2026-02-08 13:33:22 -05:00
5efdbaf4e6
Add help command tests 2026-02-08 13:32:50 -05:00
1a122b85e5
Add collision detection tests 2026-02-08 13:32:49 -05:00
f1e4cfa4dd
Add prefix matching tests 2026-02-08 13:32:32 -05:00
529320acb5
Split commands and skills into separate listings 2026-02-08 13:15:04 -05:00
a8917da59e
Fix typecheck warnings for nullable current_move 2026-02-08 13:05:16 -05:00
d0c33911f3
Wire edit command to open combat TOML files 2026-02-08 12:44:56 -05:00
1b63f87da7
Add detail view to commands command 2026-02-08 12:41:02 -05:00
6fb48e9ae1
Add commands command to list available commands 2026-02-08 12:35:50 -05:00
3c5c1490e6
Replace defense lock with sleep-based commitment blocking
Defense moves now asyncio.sleep for timing_window_ms instead of using
a cooldown field. Input queues naturally since the per-player loop is
sequential. Outside combat shows "parry the air!" flavor text.
2026-02-08 12:28:17 -05:00
2de1ebd59e
Fix variant defense mode and test cleanup
- Set variant defense registration to mode="*" (both attacks and defenses)
- Strengthen telegraph switch test to verify new move's telegraph text
- Remove unused punch parameter from four idle timeout tests
- Use single time.monotonic() call in attack() method
2026-02-08 12:28:17 -05:00
1b3684dc65
Add 30-second idle timeout for combat encounters
Encounters track last_action_at (updated on attack and defend). If 30
seconds pass with no actions, combat fizzles out with a message to both
players and combat mode is popped. start_encounter initializes the
timestamp so fresh encounters don't immediately timeout.
2026-02-08 12:28:17 -05:00
e368ed1843
Add defense commitment lock and defense-everywhere support
Defenses now work outside combat mode with stamina cost, recovery lock
(based on timing_window_ms), and broadcast to nearby players. Lock
prevents spamming defenses — you commit to the move. Stamina deduction
moved from encounter.defend() to do_defend command layer. Defense
commands registered with mode="*" instead of "combat".
2026-02-08 12:28:17 -05:00
cf423fb22b
Add attack switching (feints) during telegraph and window phases
Attacker can change their move mid-telegraph or mid-window without
resetting the timer. Old move's stamina is refunded, new move charged.
Defender gets a fresh telegraph on switch. Feedback says "switch to"
instead of "use" when swapping attacks.
2026-02-08 12:28:17 -05:00
9054962f5d
Add combat resolution messages with both-POV feedback
resolve() returns ResolveResult dataclass with attacker_msg, defender_msg,
damage, countered, and combat_ended fields. process_combat is now async
and sends messages to both participants on resolve. Counter, hit, and
slam messages give each player their own perspective on what happened.
2026-02-08 12:28:17 -05:00
6344c09275
Restructure combat moves: single-word commands with variant args
The DREAMBOOK always described "punch right/left [target]" as one command
with a direction argument, but the implementation had separate TOML files
and multi-word command names that the dispatcher couldn't reach (it only
matches the first word). Aliases like "pr" also couldn't pass targets
because the shared handler tried to re-derive the move from args.

Changes:
- Merge punch_left/right, dodge_left/right, parry_high/low into single
  TOML files with [variants] sections
- Add command/variant fields to CombatMove for tracking move families
- load_move() now returns list[CombatMove], expanding variants
- Handlers bound to moves via closures at registration time:
  variant handler for base commands (punch → parses direction from args),
  direct handler for aliases and simple moves (pr → move already known)
- Core logic in do_attack/do_defend takes a resolved move
- Combat doc rewritten as rst with architecture details
- Simplify mud.tin aliases (pr/pl/etc are built-in MUD commands now)
2026-02-08 00:20:52 -05:00