feat(command): save + menu commands (QuitCommand removal deferred to Task 11)
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
package thb.jeanluc.adventure.command.impl;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import thb.jeanluc.adventure.command.Command;
|
||||
import thb.jeanluc.adventure.game.Game;
|
||||
import thb.jeanluc.adventure.game.GameContext;
|
||||
import thb.jeanluc.adventure.save.SaveException;
|
||||
import thb.jeanluc.adventure.save.SaveService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Saves the active slot and returns to the main menu by stopping the loop.
|
||||
* Usage: {@code menu} (aliases {@code quit}, {@code exit}).
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class MenuCommand implements Command {
|
||||
|
||||
private final SaveService saves;
|
||||
private Game game;
|
||||
|
||||
/** Binds the running game (two-phase wiring, as the registry is built first). */
|
||||
public void bind(Game game) {
|
||||
this.game = game;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(GameContext ctx, List<String> args) {
|
||||
try {
|
||||
saves.save(ctx.getSession());
|
||||
ctx.getIo().write("Game saved. Returning to the main menu.");
|
||||
} catch (SaveException e) {
|
||||
ctx.getIo().write("Could not save: " + e.getUserMessage());
|
||||
}
|
||||
if (game != null) {
|
||||
game.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String help() {
|
||||
return "menu - save and return to the main menu (aliases: quit, exit)";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package thb.jeanluc.adventure.command.impl;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import thb.jeanluc.adventure.command.Command;
|
||||
import thb.jeanluc.adventure.game.GameContext;
|
||||
import thb.jeanluc.adventure.save.SaveException;
|
||||
import thb.jeanluc.adventure.save.SaveService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/** Manually saves the active slot. Usage: {@code save}. */
|
||||
@RequiredArgsConstructor
|
||||
public class SaveCommand implements Command {
|
||||
|
||||
private final SaveService saves;
|
||||
|
||||
@Override
|
||||
public void execute(GameContext ctx, List<String> args) {
|
||||
try {
|
||||
saves.save(ctx.getSession());
|
||||
ctx.getIo().write("Game saved.");
|
||||
} catch (SaveException e) {
|
||||
ctx.getIo().write(e.getUserMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String help() {
|
||||
return "save - save your game to the current slot";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package thb.jeanluc.adventure.command;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import thb.jeanluc.adventure.command.impl.MenuCommand;
|
||||
import thb.jeanluc.adventure.command.impl.SaveCommand;
|
||||
import thb.jeanluc.adventure.game.Game;
|
||||
import thb.jeanluc.adventure.game.GameContext;
|
||||
import thb.jeanluc.adventure.game.GameSession;
|
||||
import thb.jeanluc.adventure.io.TestIO;
|
||||
import thb.jeanluc.adventure.loader.WorldLoader;
|
||||
import thb.jeanluc.adventure.save.SaveService;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class SaveMenuCommandTest {
|
||||
|
||||
private GameContext ctx(String slot) {
|
||||
WorldLoader.LoadResult r = new WorldLoader().load();
|
||||
return new GameContext(new GameSession(r.world(), r.player(), slot), new TestIO());
|
||||
}
|
||||
|
||||
@Test
|
||||
void saveCommandWritesSlotAndReportsSuccess(@TempDir Path dir) {
|
||||
SaveService svc = new SaveService(dir);
|
||||
GameContext ctx = ctx("game-a");
|
||||
new SaveCommand(svc).execute(ctx, List.of());
|
||||
assertThat(svc.list()).extracting(s -> s.slotName()).containsExactly("game-a");
|
||||
assertThat(((TestIO) ctx.getIo()).allOutput()).contains("Game saved");
|
||||
}
|
||||
|
||||
@Test
|
||||
void menuCommandSavesThenStopsLoop(@TempDir Path dir) {
|
||||
SaveService svc = new SaveService(dir);
|
||||
GameContext ctx = ctx("game-b");
|
||||
Game game = new Game(ctx, new CommandRegistry(), new CommandParser());
|
||||
MenuCommand menu = new MenuCommand(svc);
|
||||
menu.bind(game);
|
||||
menu.execute(ctx, List.of());
|
||||
assertThat(svc.list()).hasSize(1);
|
||||
// loop is stopped: run() must return without consuming queued input
|
||||
((TestIO) ctx.getIo()).enqueue("look");
|
||||
game.run();
|
||||
assertThat(ctx.getIo().readLine()).isEqualTo("look"); // sentinel still unread
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user