Wire edit command to open combat TOML files
This commit is contained in:
parent
1b63f87da7
commit
d0c33911f3
3 changed files with 208 additions and 7 deletions
|
|
@ -12,6 +12,7 @@ from mudlib.player import Player, players
|
||||||
|
|
||||||
# Combat moves will be injected after loading
|
# Combat moves will be injected after loading
|
||||||
combat_moves: dict[str, CombatMove] = {}
|
combat_moves: dict[str, CombatMove] = {}
|
||||||
|
combat_content_dir: Path | None = None
|
||||||
|
|
||||||
|
|
||||||
async def do_attack(player: Player, target_args: str, move: CombatMove) -> None:
|
async def do_attack(player: Player, target_args: str, move: CombatMove) -> None:
|
||||||
|
|
@ -170,7 +171,10 @@ def register_combat_commands(content_dir: Path) -> None:
|
||||||
Args:
|
Args:
|
||||||
content_dir: Path to directory containing combat move TOML files
|
content_dir: Path to directory containing combat move TOML files
|
||||||
"""
|
"""
|
||||||
global combat_moves
|
global combat_moves, combat_content_dir
|
||||||
|
|
||||||
|
# Save content directory for use by edit command
|
||||||
|
combat_content_dir = content_dir
|
||||||
|
|
||||||
# Load all moves from content directory
|
# Load all moves from content directory
|
||||||
combat_moves = load_moves(content_dir)
|
combat_moves = load_moves(content_dir)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
"""Edit command for entering the text editor."""
|
"""Edit command for entering the text editor."""
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from mudlib.commands import CommandDefinition, register
|
from mudlib.commands import CommandDefinition, register
|
||||||
from mudlib.editor import Editor
|
from mudlib.editor import Editor
|
||||||
from mudlib.player import Player
|
from mudlib.player import Player
|
||||||
|
|
@ -9,21 +11,80 @@ async def cmd_edit(player: Player, args: str) -> None:
|
||||||
"""Enter the text editor.
|
"""Enter the text editor.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
player: The player executing the command
|
player: The player entering the editor
|
||||||
args: Command arguments (unused for now)
|
args: Optional argument - combat move name to edit
|
||||||
"""
|
"""
|
||||||
|
args = args.strip()
|
||||||
|
|
||||||
async def save_callback(content: str) -> None:
|
# Default: blank editor
|
||||||
await player.send("Content saved.\r\n")
|
initial_content = ""
|
||||||
|
content_type = "text"
|
||||||
|
save_callback_fn = _make_default_save_callback(player)
|
||||||
|
toml_path: Path | None = None
|
||||||
|
|
||||||
|
# If args provided, try to load combat move TOML
|
||||||
|
if args:
|
||||||
|
from mudlib.combat.commands import combat_content_dir, combat_moves
|
||||||
|
|
||||||
|
if combat_content_dir is None:
|
||||||
|
await player.send("Combat content not loaded.\r\n")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Look up the move - could be by name or alias
|
||||||
|
move = combat_moves.get(args)
|
||||||
|
|
||||||
|
# If not found, try to find by command name (for variant bases)
|
||||||
|
if move is None:
|
||||||
|
for m in combat_moves.values():
|
||||||
|
if m.command == args:
|
||||||
|
move = m
|
||||||
|
break
|
||||||
|
|
||||||
|
if move is None:
|
||||||
|
await player.send(f"Unknown content: {args}\r\n")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get the base command name to find the TOML file
|
||||||
|
toml_filename = f"{move.command}.toml"
|
||||||
|
toml_path = combat_content_dir / toml_filename
|
||||||
|
|
||||||
|
if not toml_path.exists():
|
||||||
|
await player.send(f"TOML file not found: {toml_filename}\r\n")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Read the file content
|
||||||
|
initial_content = toml_path.read_text()
|
||||||
|
content_type = "toml"
|
||||||
|
save_callback_fn = _make_toml_save_callback(player, toml_path)
|
||||||
|
|
||||||
player.editor = Editor(
|
player.editor = Editor(
|
||||||
save_callback=save_callback,
|
save_callback=save_callback_fn,
|
||||||
content_type="text",
|
content_type=content_type,
|
||||||
color_depth=player.color_depth,
|
color_depth=player.color_depth,
|
||||||
|
initial_content=initial_content,
|
||||||
)
|
)
|
||||||
player.mode_stack.append("editor")
|
player.mode_stack.append("editor")
|
||||||
await player.send("Entering editor. Type :h for help.\r\n")
|
await player.send("Entering editor. Type :h for help.\r\n")
|
||||||
|
|
||||||
|
|
||||||
|
def _make_default_save_callback(player: Player):
|
||||||
|
"""Create default save callback for blank editor."""
|
||||||
|
|
||||||
|
async def save_callback(content: str) -> None:
|
||||||
|
await player.send("Content saved.\r\n")
|
||||||
|
|
||||||
|
return save_callback
|
||||||
|
|
||||||
|
|
||||||
|
def _make_toml_save_callback(player: Player, toml_path: Path):
|
||||||
|
"""Create save callback for TOML file editing."""
|
||||||
|
|
||||||
|
async def save_callback(content: str) -> None:
|
||||||
|
toml_path.write_text(content)
|
||||||
|
await player.send(f"Saved {toml_path.name}\r\n")
|
||||||
|
|
||||||
|
return save_callback
|
||||||
|
|
||||||
|
|
||||||
# Register the edit command
|
# Register the edit command
|
||||||
register(CommandDefinition("edit", cmd_edit, mode="normal"))
|
register(CommandDefinition("edit", cmd_edit, mode="normal"))
|
||||||
|
|
|
||||||
|
|
@ -165,3 +165,139 @@ async def test_editor_prompt_uses_cursor(player):
|
||||||
# This test verifies the cursor field exists and can be used for prompts
|
# This test verifies the cursor field exists and can be used for prompts
|
||||||
assert player.editor.cursor == 1
|
assert player.editor.cursor == 1
|
||||||
# Shell loop prompt: f" {player.editor.cursor + 1}> " = " 2> "
|
# Shell loop prompt: f" {player.editor.cursor + 1}> " = " 2> "
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_edit_no_args_opens_blank_editor(player):
|
||||||
|
"""Test that edit with no args opens a blank editor."""
|
||||||
|
await cmd_edit(player, "")
|
||||||
|
|
||||||
|
assert player.editor is not None
|
||||||
|
assert player.editor.buffer == []
|
||||||
|
assert player.mode == "editor"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_edit_combat_move_opens_toml(player, tmp_path):
|
||||||
|
"""Test that edit roundhouse opens the TOML file for editing."""
|
||||||
|
from mudlib.combat import commands as combat_commands
|
||||||
|
|
||||||
|
# Create a test TOML file
|
||||||
|
toml_content = """name = "roundhouse"
|
||||||
|
aliases = ["rh"]
|
||||||
|
move_type = "attack"
|
||||||
|
stamina_cost = 8.0
|
||||||
|
timing_window_ms = 2000
|
||||||
|
"""
|
||||||
|
toml_file = tmp_path / "roundhouse.toml"
|
||||||
|
toml_file.write_text(toml_content)
|
||||||
|
|
||||||
|
# Set up combat moves
|
||||||
|
combat_commands.combat_moves = {"roundhouse": MagicMock(command="roundhouse")}
|
||||||
|
combat_commands.combat_content_dir = tmp_path
|
||||||
|
|
||||||
|
await cmd_edit(player, "roundhouse")
|
||||||
|
|
||||||
|
assert player.editor is not None
|
||||||
|
assert player.mode == "editor"
|
||||||
|
assert toml_content in "\n".join(player.editor.buffer)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_edit_combat_move_saves_to_disk(player, tmp_path, mock_writer):
|
||||||
|
"""Test that saving in editor writes back to the TOML file."""
|
||||||
|
from mudlib.combat import commands as combat_commands
|
||||||
|
|
||||||
|
# Create a test TOML file
|
||||||
|
original_content = """name = "roundhouse"
|
||||||
|
aliases = ["rh"]
|
||||||
|
move_type = "attack"
|
||||||
|
"""
|
||||||
|
toml_file = tmp_path / "roundhouse.toml"
|
||||||
|
toml_file.write_text(original_content)
|
||||||
|
|
||||||
|
# Set up combat moves
|
||||||
|
combat_commands.combat_moves = {"roundhouse": MagicMock(command="roundhouse")}
|
||||||
|
combat_commands.combat_content_dir = tmp_path
|
||||||
|
|
||||||
|
await cmd_edit(player, "roundhouse")
|
||||||
|
mock_writer.reset_mock()
|
||||||
|
|
||||||
|
# Modify the buffer
|
||||||
|
player.editor.buffer = [
|
||||||
|
'name = "roundhouse"',
|
||||||
|
'aliases = ["rh"]',
|
||||||
|
'move_type = "attack"',
|
||||||
|
"stamina_cost = 9.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
# Save
|
||||||
|
await player.editor.handle_input(":w")
|
||||||
|
|
||||||
|
# Check that file was written
|
||||||
|
saved_content = toml_file.read_text()
|
||||||
|
assert "stamina_cost = 9.0" in saved_content
|
||||||
|
assert mock_writer.write.called
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_edit_variant_base_opens_toml(player, tmp_path):
|
||||||
|
"""Test that edit punch opens punch.toml (variant base name)."""
|
||||||
|
from mudlib.combat import commands as combat_commands
|
||||||
|
|
||||||
|
# Create punch.toml with variants
|
||||||
|
toml_content = """name = "punch"
|
||||||
|
move_type = "attack"
|
||||||
|
|
||||||
|
[variants.left]
|
||||||
|
aliases = ["pl"]
|
||||||
|
"""
|
||||||
|
toml_file = tmp_path / "punch.toml"
|
||||||
|
toml_file.write_text(toml_content)
|
||||||
|
|
||||||
|
# Set up combat moves with variant
|
||||||
|
combat_commands.combat_moves = {
|
||||||
|
"punch left": MagicMock(command="punch", variant="left")
|
||||||
|
}
|
||||||
|
combat_commands.combat_content_dir = tmp_path
|
||||||
|
|
||||||
|
await cmd_edit(player, "punch")
|
||||||
|
|
||||||
|
assert player.editor is not None
|
||||||
|
assert player.mode == "editor"
|
||||||
|
assert "[variants.left]" in "\n".join(player.editor.buffer)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_edit_unknown_content_shows_error(player, mock_writer, tmp_path):
|
||||||
|
"""Test that edit nonexistent shows an error."""
|
||||||
|
from mudlib.combat import commands as combat_commands
|
||||||
|
|
||||||
|
combat_commands.combat_moves = {}
|
||||||
|
combat_commands.combat_content_dir = tmp_path
|
||||||
|
|
||||||
|
await cmd_edit(player, "nonexistent")
|
||||||
|
|
||||||
|
assert player.editor is None
|
||||||
|
assert player.mode == "normal"
|
||||||
|
assert mock_writer.write.called
|
||||||
|
output = "".join([call[0][0] for call in mock_writer.write.call_args_list])
|
||||||
|
assert "unknown" in output.lower()
|
||||||
|
assert "nonexistent" in output.lower()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_edit_combat_move_uses_toml_content_type(player, tmp_path):
|
||||||
|
"""Test that editor for combat moves uses toml content type."""
|
||||||
|
from mudlib.combat import commands as combat_commands
|
||||||
|
|
||||||
|
toml_file = tmp_path / "roundhouse.toml"
|
||||||
|
toml_file.write_text("name = 'roundhouse'\n")
|
||||||
|
|
||||||
|
combat_commands.combat_moves = {"roundhouse": MagicMock(command="roundhouse")}
|
||||||
|
combat_commands.combat_content_dir = tmp_path
|
||||||
|
|
||||||
|
await cmd_edit(player, "roundhouse")
|
||||||
|
|
||||||
|
assert player.editor is not None
|
||||||
|
assert player.editor.content_type == "toml"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue