- Expand race TOML schema: 7 stats, body shape (size/weight/custom slots), natural armor and attacks with damage types, resistances, traits/disadvantages, regen multipliers, vision types, XP rate, guild compatibility - Replace equipped_weapon/equipped_armor with slot-based HashMap<String, Object> - Each race defines available equipment slots; default humanoid slots as fallback - Combat uses natural weapons/armor from race when no gear equipped - DB migration from old weapon/armor columns to equipped_json - Add Dragon race: huge body, custom slots (forelegs/wings/tail), fire breath, natural armor 8, fire immune, slow XP rate for balance - Update all existing races with expanded fields (traits, resistances, vision, regen) - Objects gain optional slot field; kind=weapon/armor still works as fallback - Update chargen to display race traits, size, natural attacks, vision - Update stats display to show equipment and natural bonuses separately - Update TESTING.md and AGENTS.md with race/slot system documentation Made-with: Cursor
9.8 KiB
9.8 KiB
Pre-Commit Test Checklist
Run through these checks before every commit to ensure consistent feature coverage.
Build
cargo buildsucceeds with no errorscargo build --bin mudtoolsucceeds
Server Startup
- Server starts:
RUST_LOG=info ./target/debug/mudserver - World loads all rooms, NPCs, objects, races, classes (check log output)
- Database opens (or creates) successfully
- Tick engine starts and logs tick rate
Character Creation
- New player SSH → gets chargen flow (race + class selection)
- Chargen accepts both number and name input
- All races display with expanded info (size, traits, natural attacks, vision)
- Dragon race shows custom body slots, natural armor, fire breath, vision types
- After chargen, player appears in spawn room with correct stats
- Stats reflect race modifiers (STR, DEX, CON, INT, WIS, PER, CHA)
- Player saved to DB after creation
Player Persistence
- Reconnecting player skips chargen, sees "Welcome back!"
- Room, stats, inventory, equipment all restored from DB
- Status effects persist across logout/login (stored in DB)
- Status effects continue ticking while player is offline
- On reconnect, expired effects are gone; active effects resume
- Verify with:
sqlite3 mudserver.db "SELECT * FROM players;"
Movement & Navigation
go north,n,south,s, etc. all work- Invalid direction shows error
- Room view shows: NPCs (colored by attitude), objects, exits, other players
- Cannot move while in combat (combat lockout)
NPC Interaction
examine <npc>shows description, stats, attitude labeltalk <friendly>shows greeting dialoguetalk <hostile>shows snarl message- Dead NPCs don't appear in room view
Combat - Tick-Based
attack <npc>enters combat state with any NPC that has combat stats- Attacking friendly/neutral NPCs is allowed but incurs attitude penalties
- Attacking non-hostile NPC: attitude shift -30 individual, -15 faction
- "The locals look on in horror" message when attacking non-hostile
- Combat rounds resolve automatically on server ticks (not on command)
- Player receives tick-by-tick combat output (damage dealt, damage taken)
- Default combat action is "attack" if no other action queued
defend/defsets defensive stance (reduced incoming damage next tick)- NPC death: awards XP, shifts attitude -10, shifts faction -5
- Player death: respawns at spawn room with full HP, combat cleared, effects cleared
- NPCs respawn after configured time
- Combat lockout: can only attack/defend/flee/look/stats/inv/use/quit during combat
fleequeues escape attempt — may fail based on statsuse <item>in combat queues item use for next tick- Multiple ticks of combat resolve correctly without player input
- Combat ends when NPC dies (player exits combat state)
- Combat ends when player flees successfully
- NPCs without explicit [combat] section get default stats (20 HP, 4 ATK, 2 DEF, 5 XP)
Combat - NPC AI
- Hostile NPCs auto-engage players who enter their room
- Aggressive NPCs do NOT auto-engage (only attackable, not initiating)
- NPC attacks resolve on tick alongside player attacks
- NPCs in combat continue attacking even if player sends no commands
Combat - RNG
- Damage varies between hits (not identical each time)
- Multiple rapid attacks produce different damage values
Items & Equipment Slots
take <item>picks up takeable objectsdrop <item>places item in roomequip <weapon>equips tomain_handslot (backwards-compat via kind)equip <armor>equips to appropriate slot (objslotfield or fallback)- Equipping to an occupied slot returns old item to inventory
equipfails if race doesn't have the required slot- Objects with explicit
slotfield use that slot use <consumable>heals and removes item (immediate out of combat)use <consumable>in combat queues for next tickinventoryshows equipped items by slot name + bag itemsstatsshows equipment bonuses and natural bonuses separately
Status Effects
- Poison deals damage each tick, shows message to player
- Regeneration heals each tick, shows message to player
- Status effects expire after their duration (in ticks)
statscommand shows active status effects and remaining duration- Multiple status effects can be active simultaneously
- Negative status effects cleared on player death/respawn
- Status effects on offline players resolve by wall-clock time on next login
Passive Regeneration
- Players out of combat slowly regenerate HP over ticks
- Regeneration does not occur while in combat
- HP does not exceed max_hp
Tick Engine
- Tick runs at configured interval (~3 seconds)
- Tick processes: NPC AI → combat rounds → status effects → respawns → regen
- Tick output is delivered to players promptly
- Server remains responsive to immediate commands between ticks
- Multiple players in separate combats are processed independently per tick
Race System
- Existing races (Human, Elf, Dwarf, Orc, Halfling) load with expanded fields
- Dragon race loads with custom body, natural attacks, resistances, traits
- Dragon gets custom equipment slots (forelegs, hindlegs, wings, tail)
- Dragon's natural armor (8) shows in stats and affects defense
- Dragon's natural attacks (fire breath 15dmg) affect effective attack
- Items magically resize — no size restrictions on gear (dragon can use swords)
- Races without explicit [body.slots] get default humanoid slots
- Stat modifiers include PER (perception) and CHA (charisma)
- Race traits and disadvantages display during chargen
- XP rate modifier stored per race (dragon = 0.7x)
- Regen modifiers stored per race (dragon HP regen = 1.5x)
Attitude System
- Per-player NPC attitudes stored in DB
examineshows attitude label per-player- Killing NPC shifts attitude (individual -10, faction -5)
- Verify:
sqlite3 mudserver.db "SELECT * FROM npc_attitudes;"
Admin System
- Non-admin can't use
admincommands (gets error) - Set admin via mudtool:
mudtool players set-admin <name> true admin helpshows admin command listadmin promote <player>grants admin (verify in DB)admin demote <player>revokes adminadmin kick <player>disconnects target playeradmin teleport <room_id>warps to room (shows room list on invalid)admin registration offblocks new player creationadmin registration onre-enables itadmin announce <msg>broadcasts to all playersadmin healheals self;admin heal <player>heals targetadmin info <player>shows detailed stats + attitudesadmin setattitude <player> <npc> <value>modifies attitudeadmin listshows all players with online/offline status
Registration Gate
- With registration open (default), new players can create characters
- With registration off, new SSH connections get rejection message
- Existing players can still log in when registration is closed
MUD Tool - CLI
mudtool players listshows all playersmudtool players show <name>shows detailsmudtool players set-admin <name> trueworksmudtool players delete <name>removes player + attitudesmudtool settings listshows settingsmudtool settings set registration_open falseworksmudtool attitudes list <player>shows attitudesmudtool attitudes set <player> <npc> <value>works
MUD Tool - TUI
mudtool tuilaunches interactive interface- Tab/1/2/3 switches between Players, Settings, Attitudes tabs
- Arrow keys navigate rows
- 'a' toggles admin on Players tab
- 'd' prompts delete confirmation on Players tab
- Enter edits value on Settings and Attitudes tabs
- ←→ switches player on Attitudes tab
- 'q' exits TUI
Quick Smoke Test Script
# Start server in background
RUST_LOG=info ./target/debug/mudserver &
SERVER_PID=$!
sleep 2
# Test 1: New player creation + basic commands
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 2222 smoketest@localhost <<'EOF'
1
1
look
stats
go north
talk barkeep
go south
go south
examine thief
attack thief
flee
quit
EOF
# Test 2: Persistence - reconnect
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 2222 smoketest@localhost <<'EOF'
look
stats
quit
EOF
# Test 3: Admin via mudtool
./target/debug/mudtool players list
./target/debug/mudtool players set-admin smoketest true
./target/debug/mudtool players show smoketest
./target/debug/mudtool settings set registration_open false
./target/debug/mudtool settings list
# Test 4: Admin commands in-game
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 2222 smoketest@localhost <<'EOF'
admin help
admin list
admin registration on
admin info smoketest
quit
EOF
# Test 5: Registration gate
./target/debug/mudtool settings set registration_open false
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 2222 newplayer@localhost <<'EOF'
quit
EOF
# Test 6: Tick-based combat (connect and wait for ticks)
./target/debug/mudtool settings set registration_open true
./target/debug/mudtool players delete smoketest
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 2222 smoketest@localhost <<'EOF'
1
1
go south
go south
attack thief
EOF
# Wait for several combat ticks to resolve
sleep 8
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p 2222 smoketest@localhost <<'EOF'
stats
quit
EOF
# Verify XP changed (combat happened via ticks)
# Cleanup
./target/debug/mudtool settings set registration_open true
./target/debug/mudtool players delete smoketest
kill $SERVER_PID