feat: condition-locked exits (+ room/dialogue state plumbing)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-31 21:52:06 +02:00
parent 228efaffcb
commit df4a46ae5b
11 changed files with 159 additions and 1 deletions

View File

@@ -1,10 +1,18 @@
package thb.jeanluc.adventure.loader;
import lombok.RequiredArgsConstructor;
import thb.jeanluc.adventure.loader.dto.ConditionDto;
import thb.jeanluc.adventure.loader.dto.DescriptionStateDto;
import thb.jeanluc.adventure.loader.dto.DialogueLineDto;
import thb.jeanluc.adventure.loader.dto.EffectDto;
import thb.jeanluc.adventure.loader.dto.ExitLockDto;
import thb.jeanluc.adventure.loader.dto.NpcDto;
import thb.jeanluc.adventure.loader.dto.ReactionDto;
import thb.jeanluc.adventure.loader.dto.RoomDto;
import thb.jeanluc.adventure.model.DescriptionState;
import thb.jeanluc.adventure.model.DialogueLine;
import thb.jeanluc.adventure.model.Direction;
import thb.jeanluc.adventure.model.ExitLock;
import thb.jeanluc.adventure.model.Npc;
import thb.jeanluc.adventure.model.NpcReaction;
import thb.jeanluc.adventure.model.Room;
@@ -75,6 +83,25 @@ public class ReferenceResolver {
room.addNpc(npc);
}
}
if (dto.exitLocks() != null) {
for (ExitLockDto lock : dto.exitLocks()) {
Direction direction;
try {
direction = Direction.fromString(lock.direction());
} catch (IllegalArgumentException ex) {
throw new WorldLoadException(
"Room '" + dto.id() + "' has exit lock with unknown direction '" + lock.direction() + "'");
}
room.addExitLock(direction, new ExitLock(
ConditionDto.toModelList(lock.requires()), lock.blocked()));
}
}
if (dto.descriptionStates() != null) {
for (DescriptionStateDto ds : dto.descriptionStates()) {
room.addDescriptionState(new DescriptionState(
ConditionDto.toModelList(ds.when()), ds.text()));
}
}
}
}

View File

@@ -0,0 +1,7 @@
package thb.jeanluc.adventure.loader.dto;
import java.util.List;
/** YAML representation of a conditional room description. */
public record DescriptionStateDto(List<ConditionDto> when, String text) {
}

View File

@@ -0,0 +1,7 @@
package thb.jeanluc.adventure.loader.dto;
import java.util.List;
/** YAML representation of a conditional NPC line. */
public record DialogueLineDto(List<ConditionDto> when, String text) {
}

View File

@@ -0,0 +1,7 @@
package thb.jeanluc.adventure.loader.dto;
import java.util.List;
/** YAML representation of a single condition-gated exit. */
public record ExitLockDto(String direction, List<ConditionDto> requires, String blocked) {
}

View File

@@ -12,6 +12,8 @@ import java.util.Map;
* @param exits direction-name to target-room-id map
* @param items ids of items initially in this room
* @param npcs ids of NPCs initially in this room
* @param exitLocks optional condition-gates per exit direction
* @param descriptionStates optional condition-gated description variants
*/
public record RoomDto(
String id,
@@ -19,6 +21,13 @@ public record RoomDto(
String description,
Map<String, String> exits,
List<String> items,
List<String> npcs
List<String> npcs,
List<ExitLockDto> exitLocks,
List<DescriptionStateDto> descriptionStates
) {
/** Backward-compatible constructor without the optional state fields. */
public RoomDto(String id, String name, String description,
Map<String, String> exits, List<String> items, List<String> npcs) {
this(id, name, description, exits, items, npcs, null, null);
}
}