From 569cef20fe142aa303d8271c30728c77cfcbad80 Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Sun, 31 May 2026 23:07:36 +0200 Subject: [PATCH] feat: darkness gating for go/look/take/examine + HUD light Co-Authored-By: Claude Opus 4.8 (1M context) --- .../command/impl/ExamineCommand.java | 5 ++ .../adventure/command/impl/GoCommand.java | 6 ++ .../adventure/command/impl/LookCommand.java | 5 ++ .../adventure/command/impl/TakeCommand.java | 5 ++ .../java/thb/jeanluc/adventure/game/Game.java | 2 +- .../adventure/command/impl/DarknessTest.java | 64 +++++++++++++++++++ 6 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 Semesterprojekt/src/test/java/thb/jeanluc/adventure/command/impl/DarknessTest.java diff --git a/Semesterprojekt/src/main/java/thb/jeanluc/adventure/command/impl/ExamineCommand.java b/Semesterprojekt/src/main/java/thb/jeanluc/adventure/command/impl/ExamineCommand.java index 17ed54f..ba1352d 100644 --- a/Semesterprojekt/src/main/java/thb/jeanluc/adventure/command/impl/ExamineCommand.java +++ b/Semesterprojekt/src/main/java/thb/jeanluc/adventure/command/impl/ExamineCommand.java @@ -2,6 +2,7 @@ package thb.jeanluc.adventure.command.impl; import thb.jeanluc.adventure.command.Command; import thb.jeanluc.adventure.game.GameContext; +import thb.jeanluc.adventure.game.Light; import thb.jeanluc.adventure.model.Npc; import thb.jeanluc.adventure.model.item.Item; @@ -20,6 +21,10 @@ public class ExamineCommand implements Command { ctx.getIo().write("Examine what?"); return; } + if (!Light.isLit(ctx, ctx.getPlayer().getCurrentRoom())) { + ctx.getIo().write("It's too dark to see that."); + return; + } String id = args.getFirst(); Optional item = ctx.getPlayer().findItem(id); if (item.isEmpty()) { diff --git a/Semesterprojekt/src/main/java/thb/jeanluc/adventure/command/impl/GoCommand.java b/Semesterprojekt/src/main/java/thb/jeanluc/adventure/command/impl/GoCommand.java index ac31efb..c98af8b 100644 --- a/Semesterprojekt/src/main/java/thb/jeanluc/adventure/command/impl/GoCommand.java +++ b/Semesterprojekt/src/main/java/thb/jeanluc/adventure/command/impl/GoCommand.java @@ -3,6 +3,7 @@ package thb.jeanluc.adventure.command.impl; import thb.jeanluc.adventure.command.Command; import thb.jeanluc.adventure.game.Conditions; import thb.jeanluc.adventure.game.GameContext; +import thb.jeanluc.adventure.game.Light; import thb.jeanluc.adventure.model.Direction; import thb.jeanluc.adventure.model.ExitLock; import thb.jeanluc.adventure.model.Room; @@ -39,6 +40,11 @@ public class GoCommand implements Command { ctx.getIo().write(lock.blocked()); return; } + if (!Light.isLit(ctx, next.get())) { + ctx.getIo().write("It's pitch black beyond the doorway — you need a lit light source " + + "(try lighting your lamp with 'use lamp')."); + return; + } ctx.getPlayer().setCurrentRoom(next.get()); new LookCommand().execute(ctx, List.of()); } diff --git a/Semesterprojekt/src/main/java/thb/jeanluc/adventure/command/impl/LookCommand.java b/Semesterprojekt/src/main/java/thb/jeanluc/adventure/command/impl/LookCommand.java index d5a08c8..e5ca6e1 100644 --- a/Semesterprojekt/src/main/java/thb/jeanluc/adventure/command/impl/LookCommand.java +++ b/Semesterprojekt/src/main/java/thb/jeanluc/adventure/command/impl/LookCommand.java @@ -3,6 +3,7 @@ package thb.jeanluc.adventure.command.impl; import thb.jeanluc.adventure.command.Command; import thb.jeanluc.adventure.game.Conditions; import thb.jeanluc.adventure.game.GameContext; +import thb.jeanluc.adventure.game.Light; import thb.jeanluc.adventure.io.text.RoomView; import thb.jeanluc.adventure.model.DescriptionState; import thb.jeanluc.adventure.model.Direction; @@ -21,6 +22,10 @@ public class LookCommand implements Command { @Override public void execute(GameContext ctx, List args) { Room room = ctx.getPlayer().getCurrentRoom(); + if (!Light.isLit(ctx, room)) { + ctx.getIo().write("It's pitch black; you can't make anything out. You need a light source."); + return; + } List items = room.getItems().values().stream().map(Item::getName).toList(); List npcs = room.getNpcs().values().stream().map(Npc::getName).toList(); List exits = room.getExits().keySet().stream().map(Direction::getLabel).toList(); diff --git a/Semesterprojekt/src/main/java/thb/jeanluc/adventure/command/impl/TakeCommand.java b/Semesterprojekt/src/main/java/thb/jeanluc/adventure/command/impl/TakeCommand.java index d28721f..1d58170 100644 --- a/Semesterprojekt/src/main/java/thb/jeanluc/adventure/command/impl/TakeCommand.java +++ b/Semesterprojekt/src/main/java/thb/jeanluc/adventure/command/impl/TakeCommand.java @@ -2,6 +2,7 @@ package thb.jeanluc.adventure.command.impl; import thb.jeanluc.adventure.command.Command; import thb.jeanluc.adventure.game.GameContext; +import thb.jeanluc.adventure.game.Light; import thb.jeanluc.adventure.model.Room; import thb.jeanluc.adventure.model.item.Item; @@ -20,6 +21,10 @@ public class TakeCommand implements Command { ctx.getIo().write("Take what?"); return; } + if (!Light.isLit(ctx, ctx.getPlayer().getCurrentRoom())) { + ctx.getIo().write("It's too dark to see that."); + return; + } String itemId = args.getFirst(); Room room = ctx.getPlayer().getCurrentRoom(); Optional taken = room.removeItem(itemId); diff --git a/Semesterprojekt/src/main/java/thb/jeanluc/adventure/game/Game.java b/Semesterprojekt/src/main/java/thb/jeanluc/adventure/game/Game.java index 20d0f02..2cf1058 100644 --- a/Semesterprojekt/src/main/java/thb/jeanluc/adventure/game/Game.java +++ b/Semesterprojekt/src/main/java/thb/jeanluc/adventure/game/Game.java @@ -77,7 +77,7 @@ public class Game { ctx.getPlayer().getCurrentRoom().getName(), ctx.getPlayer().getGold(), turn, - false)); + Light.carryingLight(ctx))); MapView map = MapLayout.compute( ctx.getWorld(), ctx.getPlayer().getVisitedRoomIds(), diff --git a/Semesterprojekt/src/test/java/thb/jeanluc/adventure/command/impl/DarknessTest.java b/Semesterprojekt/src/test/java/thb/jeanluc/adventure/command/impl/DarknessTest.java new file mode 100644 index 0000000..b4a9780 --- /dev/null +++ b/Semesterprojekt/src/test/java/thb/jeanluc/adventure/command/impl/DarknessTest.java @@ -0,0 +1,64 @@ +package thb.jeanluc.adventure.command.impl; + +import org.junit.jupiter.api.Test; +import thb.jeanluc.adventure.game.GameContext; +import thb.jeanluc.adventure.io.TestIO; +import thb.jeanluc.adventure.model.Direction; +import thb.jeanluc.adventure.model.Player; +import thb.jeanluc.adventure.model.Room; +import thb.jeanluc.adventure.model.item.SwitchableItem; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class DarknessTest { + private SwitchableItem lamp(boolean on) { + return SwitchableItem.builder().id("lamp").name("Lamp").description("d") + .state(on).onText("on").offText("off").light(true).build(); + } + + @Test + void cannotEnterDarkRoomWithoutLight() { + Room hall = new Room("hall", "Hall", "d"); + Room cave = new Room("cave", "Cave", "d"); + cave.setDark(true); + hall.addExit(Direction.NORTH, cave); + Player p = new Player(hall, 0); + GameContext ctx = new GameContext(null, p, new TestIO()); + + new GoCommand().execute(ctx, List.of("north")); + assertThat(p.getCurrentRoom()).isEqualTo(hall); + assertThat(((TestIO) ctx.getIo()).allOutput()).containsIgnoringCase("pitch black"); + } + + @Test + void canEnterDarkRoomWithLitLamp() { + Room hall = new Room("hall", "Hall", "d"); + Room cave = new Room("cave", "Cave", "d"); + cave.setDark(true); + hall.addExit(Direction.NORTH, cave); + Player p = new Player(hall, 0); + p.addItem(lamp(true)); + GameContext ctx = new GameContext(null, p, new TestIO()); + + new GoCommand().execute(ctx, List.of("north")); + assertThat(p.getCurrentRoom()).isEqualTo(cave); + } + + @Test + void lookAndTakeAreBlockedInTheDark() { + Room cave = new Room("cave", "Cave", "d"); + cave.setDark(true); + Player p = new Player(cave, 0); + TestIO io = new TestIO(); + GameContext ctx = new GameContext(null, p, io); + + new LookCommand().execute(ctx, List.of()); + assertThat(io.allOutput()).containsIgnoringCase("pitch black"); + + io.outputs().clear(); + new TakeCommand().execute(ctx, List.of("anything")); + assertThat(io.allOutput()).containsIgnoringCase("too dark"); + } +}