op_verify now performs actual checksum validation against the header
instead of raising NotImplemented. ZLexer is injected into ZCpu and
sread tokenizes input into the parse buffer per the V3 spec.
Implements op_print_addr, op_print_num, op_ret, and op_show_status following
TDD approach with tests first. Each opcode now properly decodes/prints text,
handles signed numbers, returns from routines, or acts as a no-op as appropriate.
Implemented 10 opcodes for object tree manipulation: get_sibling,
test_attr, set_attr, clear_attr, jin, remove_obj, print_obj,
get_prop_addr, get_next_prop, and get_prop_len.
Added 6 supporting methods to ZObjectParser: set_attribute,
clear_attribute, remove_object, get_property_data_address,
get_next_property, and get_property_length.
Fixed bug in insert_object where sibling chain walk never advanced
the prev pointer, potentially causing infinite loops.
Added 16 unit tests with MockObjectParser to verify CPU opcode
behavior. All tests passing.
Copies the cleaned-up zvm source (ruff-compliant, ty-clean) back into
the zmachine module. Adds __init__.py with proper exports and updates
.gitignore for debug.log/disasm.log.
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().
- server.py: broadcast IF output to spectators after each input (skip :: escape commands)
- server.py: broadcast leave message when player exits IF mode
- play.py: broadcast game intro text when player starts a game
Spectators at the same x,y coordinates now see formatted output with
[PlayerName's terminal] header and game text.
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.
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.
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.
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().
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.
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.
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).
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.
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.