Commit graph

101 commits

Author SHA1 Message Date
eeb65e0cc2
Remove debug print statements from zcpu opcode dispatch
The exception that follows provides the context, so these prints are redundant.
2026-02-09 21:46:34 -05:00
e5329d6788
Fix 7-bit truncation bug in write_global corrupting game state 2026-02-09 21:45:54 -05:00
311a67e80a
Fix bugs found running Zork 1 through hybrid interpreter
Spec fixes: implement op_test (bitwise AND branch), add missing branch
to op_get_child, handle call-to-address-0 as no-op in op_call/_call.
I/O fixes: correct keyboard API (keyboard_input.read_line), non-TTY
fallbacks in trivialzui, stdout flush for immediate output. Graceful
handling of unmapped ZSCII characters. Add instruction trace buffer
for debugging.
2026-02-09 21:36:30 -05:00
8d749fbb7e
Add missing newline to A2 alphabet table for V3
The V3 A2 table needs newline (ZSCII 13) at code 7, matching viola's
implementation. The previous fix only removed the erroneous '<' but
didn't add the newline, leaving the table at 24 chars instead of 25.
2026-02-09 21:17:04 -05:00
9116777603
Fix A2 alphabet table with extra character shifting symbols
DEFAULT_A2 had 26 chars instead of 25 due to an erroneous '<'. This
shifted all symbol mappings, causing periods to render as exclamation
marks and other garbled punctuation in Zork 1 output.
2026-02-09 21:11:12 -05:00
e1c6a92368
Fix Python 3 integer division in zmachine modules
Python 2 `/` did integer division, Python 3 returns float. Changed to
`//` in zobjectparser (attribute byte offset) and zmemory (address
bounds checks). Zork 1 hit this on the first test_attr opcode.
2026-02-09 21:07:16 -05:00
8eb2371ce1
Add stack and local_vars to ZStackBottom sentinel
ZStackBottom lacked attributes that normal stack operations need when
returning from a subroutine to the top level. Zork 1 hit this on the
first subroutine return during startup.
2026-02-09 21:05:27 -05:00
61765fa6ba
Allow game writes to header region in dynamic memory
write_word() was routing all header writes through game_set_header()
which enforced overly strict per-byte permissions. Zork 1 legitimately
writes to header address 2 on startup. Now uses the same dynamic/static
boundary check that viola does, matching the reference implementation.
2026-02-09 21:03:10 -05:00
d218929f02
Add step() method to ZCpu for async MUD integration
Extracts the run loop body into step() which executes one instruction
and returns True/False for continue/halt. run() now delegates to step().
2026-02-09 20:59:03 -05:00
fb8cbf7219
Implement op_verify and wire ZLexer into sread for Zork 1
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.
2026-02-09 20:57:15 -05:00
72dd047b7b
Port 5 complex opcodes to hybrid z-machine interpreter
Implement op_sread (text input), op_save/restore (file I/O stubs),
op_restart (exception-based reset), op_input_stream and op_sound_effect
(no-op stubs). Add ZCpuRestart exception. All implementations follow TDD
with comprehensive unit tests.
2026-02-09 20:44:22 -05:00
c76ee337d3
Port 4 medium opcodes to hybrid z-machine interpreter
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.
2026-02-09 20:44:22 -05:00
1b9d84f41a
Port 10 object tree opcodes to hybrid z-machine interpreter
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.
2026-02-09 20:44:21 -05:00
5ea030a0ac
Re-copy fixed repos/zvm source into src/mudlib/zmachine
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.
2026-02-09 20:44:21 -05:00
dcc952d4c5
Port 12 trivial opcodes to hybrid z-machine interpreter 2026-02-09 20:44:21 -05:00
108091bfae
Add blank line before IF mode prompt to match dfrotz 2026-02-09 17:55:14 -05:00
f8e9ae0acc
Add spacing between IF session exit messages 2026-02-09 17:55:14 -05:00
54cd0f6656
Strip dfrotz prompt even without preceding newline 2026-02-09 17:55:14 -05:00
05c9a48bb3
Strip dfrotz prompt even with trailing whitespace 2026-02-09 17:28:28 -05:00
8dc2d4b934
Auto-confirm overwrite and clean up save feedback 2026-02-09 17:10:30 -05:00
da176a1363
Use get_running_loop() instead of deprecated get_event_loop() 2026-02-09 17:07:27 -05:00
e8f16ca18a
Speed up IF reads with chunked I/O and short idle timeout 2026-02-09 17:05:27 -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
057a746687
Wire up spectator broadcasting in server and play command
- 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.
2026-02-09 16:25:39 -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
c246732b86
Add mention of ::help 2026-02-09 16:14:34 -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
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
6fd19d769b
Add a docker container solution 2026-02-09 12:34:56 -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
9c1c6a9e22
Add description field to CombatMove dataclass 2026-02-08 14:32:51 -05:00
77c2e40e0e
Add reload command for hot-reloading TOML content 2026-02-08 14:32:51 -05:00