Commit graph

403 commits

Author SHA1 Message Date
b3471a8b94
Add zone registry with register and lookup
Implements a module-level zone registry for looking up zones by name.
Includes register_zone() and get_zone() functions with comprehensive
tests covering single/multiple zones, unknown lookups, and overwrites.
2026-02-11 20:40:31 -05:00
303ce2c89e
Add Portal class with target zone and coordinates
Portals are non-portable Things that exist in zones and define
transitions to other zones via target coordinates.
2026-02-11 20:38:47 -05:00
621c42b833
Add Container class with capacity and open/closed state 2026-02-11 20:38:40 -05:00
05a739da74
Add test for duplicate item persistence 2026-02-11 20:29:59 -05:00
8acfa5ea22
Wire thing templates and inventory into server startup
Loads thing templates from content/things/ at startup. Registers
get/drop/inventory commands via things module import. Reconstructs
player inventory from saved template names on login, with graceful
fallback for unknown templates.
2026-02-11 20:29:59 -05:00
6081c90ad1
Add inventory persistence to player saves
Inventory saved as JSON list of thing template names in an inventory
column. Migration adds column to existing databases. load_player_data
returns inventory list, save_player serializes Thing names from contents.
2026-02-11 20:29:58 -05:00
c43b3346ae
Add Thing templates, TOML loading, and spawning
ThingTemplate dataclass mirrors MobTemplate pattern. load_thing_template
and load_thing_templates parse TOML files from content/things/. spawn_thing
creates Thing instances from templates. Includes rock and fountain examples.
2026-02-11 20:01:15 -05:00
2e79255aec
Show ground items in look command
After the viewport, look lists Things at the player's position
in a "On the ground: item1, item2" line. No output when empty.
2026-02-11 20:01:10 -05:00
e96fd50de5
Add inventory command with alias "i"
Lists Thing objects in player.contents with 2-space indented format.
Shows "You aren't carrying anything." when inventory is empty.
2026-02-11 20:01:05 -05:00
7c12bf3318
Add Object.move_to(), get and drop commands
Object.move_to() handles containment transfer: removes from old location's
contents, updates location pointer and coordinates, adds to new location.
get/drop commands use move_to to transfer Things between zone and inventory.
Supports name and alias matching for item lookup.
2026-02-11 19:57:38 -05:00
9437728435
Add Thing class and Entity.can_accept() for inventory
Thing is an Object subclass with description, portable flag, and aliases.
Entity.can_accept() returns True for portable Things, enabling the
containment model where entities carry items in their contents.
2026-02-11 19:55:58 -05:00
957a411601
Clean up global state, migrate broadcast_to_spectators to Zone
Removes dependency on global players dict for spatial queries by using
Zone.contents_at() for spectator lookup. Makes _world local to run_server()
since it's only used during initialization to create the overworld Zone.
Updates test fixtures to provide zones for spatial query tests.
2026-02-11 19:42:12 -05:00
f5646589b5
Migrate look to use player.location (Zone)
- Removed world module-level variable from look.py
- look.cmd_look() now uses player.location.get_viewport() instead of world.get_viewport()
- look.cmd_look() uses zone.contents_near() to find nearby entities instead of iterating global players/mobs lists
- Wrapping calculations use zone.width/height/toroidal instead of world properties
- Added type check for player.location being a Zone instance
- Removed look.world injection from server.py
- Updated all tests to remove look.world injection
- spawn_mob() and combat commands also migrated to use Zone (player.location)
- Removed orphaned code from test_mob_ai.py and test_variant_prefix.py
2026-02-11 19:36:46 -05:00
1349c2f860
Add zone_name to persistence schema
Adds zone_name field to PlayerData and accounts table to track which
zone a player is in. Defaults to 'overworld'. Includes migration logic
to handle existing databases without the column. Server now resolves
zone from zone_name when loading player data.
2026-02-11 19:33:23 -05:00
875ded5762
Migrate fly to use player.location (Zone)
Removed module-level world variable and replaced all world.wrap() calls
with player.location.wrap(). Added Zone assertion for type safety,
matching the pattern in movement.py. Updated tests to remove fly.world
injection since it's no longer needed.
2026-02-11 19:33:15 -05:00
404a1cdf0c
Migrate movement to use player.location (Zone)
Movement commands now access the zone through player.location instead of
a module-level world variable. send_nearby_message uses
zone.contents_near() to find nearby entities, eliminating the need for
the global players dict and manual distance calculations.

Tests updated to create zones and add entities via location assignment.
2026-02-11 19:28:27 -05:00
66c6e1ebd4
Create overworld Zone at startup, set player.location 2026-02-11 19:19:15 -05:00
6f58ae0501
Add contents_near() spatial query to Zone 2026-02-11 19:17:17 -05:00
b4fca95830
Add Zone class with terrain, spatial queries, and viewport
Zone(Object) is a spatial area with a terrain grid. Supports
toroidal wrapping and bounded clamping, passability checks,
viewport extraction, and contents_at(x, y) spatial queries.
Zones are top-level containers (location=None) that accept
everything via can_accept().
2026-02-11 19:08:30 -05:00
51dc583818
Make Entity inherit from Object
Entity gains location from Object and narrows x/y back to int
(entities always have spatial coordinates). No behavioral change —
all existing tests pass unchanged.
2026-02-11 18:40:35 -05:00
d9e9d1b785
Add Object base class with containment primitives
Object provides name, location, x/y, contents reverse-lookup, and
can_accept() — the foundation for the containment tree that zones,
things, and inventory will build on.
2026-02-11 18:40:31 -05:00
9671f3c286
Add a roadmap outline 2026-02-11 18:21:43 -05:00
544b7dfee1
Add reading guide to index and refresh stale references 2026-02-11 16:19:34 -05:00
400ebe4275
Add object model design doc 2026-02-11 12:12:30 -05:00
94a01186c8
Add a doc index 2026-02-11 09:39:48 -05:00
da809bea31
Add zones and building doc 2026-02-10 22:48:44 -05:00
01d5848178
Add prompt system design doc 2026-02-10 19:07:32 -05:00
74538756d5
Handle object 0 (nothing) gracefully in object parser
Per the Z-machine spec, object 0 means "nothing" and operations on it
should be safe no-ops: get_child/get_sibling/get_parent return 0,
test_attr returns false, set/clear_attr are no-ops, etc. Previously
these threw ZObjectIllegalObjectNumber, crashing on games like Curses
that pass object 0 to get_child during room transitions.
2026-02-10 18:32:36 -05:00
5a98adb6ee
Add instruction tracing to step_fast and improve error messages
step_fast() never recorded trace entries, so crash dumps always showed
an empty trace. Now records PC + opcode info in the same deque as
step(). Also includes exception type in player-facing error messages
when the exception string is empty.
2026-02-10 18:29:27 -05:00
c8d9bdfae9
Map empty Enter to ZSCII 13 in read_char
MudInputStream.read_char() returned 0 for empty input, which no game
recognizes as a valid keypress. Now returns 13 (Enter/Return) so
"press any key" prompts like Curses' intro work from a MUD client.
2026-02-10 18:26:41 -05:00
fd977b91a2
Guard bare > stripping with has_prompt check 2026-02-10 17:53:11 -05:00
b81bc3edc8
Add consistent > prompt for IF mode in server loop 2026-02-10 17:50:35 -05:00
ac1d16095e
Strip trailing > prompt from embedded z-machine output 2026-02-10 17:50:06 -05:00
909ee0932b
Replace deprecated chunk module with inline IFF parser
The chunk module was deprecated in 3.11 and removed in 3.13.
Our usage was minimal (read name, size, data, skip padding),
so a small _read_iff_chunk() helper replaces it with no deps.
2026-02-10 17:20:01 -05:00
3140a4d617
Add return_addr to ZStackBottom for uniform frame access
ZStackBottom already had stack and local_vars for uniform treatment,
but was missing return_addr. Adding it removes 5 type: ignore
suppressions and fixes all ty possibly-missing-attribute warnings.
2026-02-10 17:16:25 -05:00
e72f13e78a
Add provenance documentation for test story files 2026-02-10 17:10:29 -05:00
6d7b404365
Add pytest regression harness for z-machine game compatibility
Implements Phase 4 of the z-machine compatibility plan.

Creates automated regression tests that smoke-test all supported games
(V3, V5, V8) by loading each story, executing basic commands, and verifying
the interpreter doesn't crash.

Key features:
- Parametrized test covering 7 games (zork1, curses, photopia, Tangle,
  shade, LostPig, anchor)
- QuietScreen class that disables [MORE] prompts for unattended testing
- AutoInputStream that auto-feeds commands then exits cleanly
- Tests verify: no crashes, unimplemented opcodes, and minimum instruction count
- All tests pass in ~2 seconds

Tests skip gracefully if story files aren't present, making this safe to
run in CI or on systems without all game files.
2026-02-10 17:10:29 -05:00
6d29ec00fb
Implement stub opcodes for game compatibility
set_colour, piracy, erase_line, get_cursor, not_v5, print_table
2026-02-10 17:10:29 -05:00
b08ce668a6
Add smoke test script for z-machine game compatibility 2026-02-10 17:10:04 -05:00
243a44e3fb
Add plan for zvm compatability 2026-02-10 16:50:23 -05:00
bc1a2e5489
Add undo command support 2026-02-10 16:49:46 -05:00
8288b2535a
Add multiplayer zmachine design notes 2026-02-10 16:09:33 -05:00
1b3a3646d6
Pre-consume store byte in op_aread and op_read_char before blocking reads
Without this, MUD-level saves during read_line/read_char capture PC pointing
at the store byte, which gets misinterpreted as an opcode on restore.
2026-02-10 15:45:52 -05:00
ad47ee05bd
Document z-machine performance analysis and optimization roadmap 2026-02-10 15:45:52 -05:00
4f570ae1af
Add profiling and timing scripts for z-machine performance analysis 2026-02-10 15:45:52 -05:00
bb2f1989cb
Optimize z-machine hot loop: fast step, dispatch table, inline bit ops
Add step_fast() that skips trace/logging overhead (saves ~22% at 1M+
avoided log calls). Pre-resolve opcode dispatch table at init to
eliminate per-instruction version checks and isinstance calls. Replace
BitField allocations with direct bit masks in opcode decoder.

Cold start: 4720ms -> 786ms. Steady state: ~500ms -> ~460ms.
2026-02-10 15:05:34 -05:00
802c72819c
Fix op_read_char to accept optional timing arguments
Lost Pig calls read_char with only the required first operand.
The Z-machine spec says time and input_routine are optional.
2026-02-10 14:48:57 -05:00
4313941334
Update if-journey: mark V8 MUD wiring as done, note upper window fix 2026-02-10 14:36:57 -05:00
5f12a4f841
Suppress upper window writes in MudScreen to fix Lost Pig output
V5+ games write room names to the upper window (status line) via
select_window(1). Since select_window was a no-op, status line text
leaked into the main output buffer, causing ">Outside" on prompt lines.

Track the active window and only buffer writes to window 0 (lower).
2026-02-10 14:36:42 -05:00
e55294af78
Implement V5+ save/restore opcodes and handle in-game saves on restore
- op_save_v5: generates Quetzal save, stores 1 on success / 0 on failure
- op_restore_v5: loads Quetzal save, stores 2 ("restored") via store byte
- _try_restore: detect V5+ in-game saves (0xBE 0x00 before PC) and process
  the store byte with result 2, matching the V3 branch-on-restore pattern
2026-02-10 14:18:42 -05:00