fix(combinations): guard null response; reject self-combination at load
- Combinations.tryUse: only write response when non-null and non-blank, preventing NPE/IllegalArgument for effects-only recipes. - CombinationFactory.fromDto: throw WorldLoadException when a == b, since item singletons make a self-combination impossible at runtime. - Tests: +effectsOnlyRecipeAppliesEffectsWithoutProduceOrResponse (CombinationsTest 7→8), +rejectsSelfCombination (CombinationFactoryTest 4→5). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -47,8 +47,10 @@ public final class Combinations {
|
||||
}
|
||||
}
|
||||
Effects.applyAll(c.effects(), ctx);
|
||||
if (c.response() != null && !c.response().isBlank()) {
|
||||
ctx.getIo().write(c.response());
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean present(GameContext ctx, String id) {
|
||||
return ctx.getPlayer().hasItem(id)
|
||||
|
||||
@@ -18,6 +18,10 @@ public final class CombinationFactory {
|
||||
public static Combination fromDto(CombinationDto dto, Map<String, Item> items) {
|
||||
requireItem(items, dto.a(), "a");
|
||||
requireItem(items, dto.b(), "b");
|
||||
if (dto.a().equals(dto.b())) {
|
||||
throw new WorldLoadException(
|
||||
"Combination cannot combine an item with itself: '" + dto.a() + "'");
|
||||
}
|
||||
if (dto.consume() != null) {
|
||||
for (String id : dto.consume()) {
|
||||
requireItem(items, id, "consume");
|
||||
|
||||
@@ -123,6 +123,20 @@ class CombinationsTest {
|
||||
assertThat(ctx.getPlayer().hasItem("matches")).isTrue(); // not consumed
|
||||
}
|
||||
|
||||
@Test
|
||||
void effectsOnlyRecipeAppliesEffectsWithoutProduceOrResponse() {
|
||||
Combination effectsOnly = new Combination("matches", "shovel",
|
||||
java.util.List.of(), java.util.List.of(), null,
|
||||
java.util.List.of(new Effect(Effect.Type.SET_FLAG, "rite_done")),
|
||||
null, null);
|
||||
GameContext ctx = ctx(effectsOnly);
|
||||
give(ctx, "matches");
|
||||
give(ctx, "shovel");
|
||||
Combinations.tryUse(ctx, "matches", "shovel");
|
||||
assertThat(ctx.getState().isSet("rite_done")).isTrue();
|
||||
assertThat(out(ctx)).doesNotContain("null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void metRequiresAppliesEffectsAndProduces() {
|
||||
Combination gated = new Combination("matches", "torch",
|
||||
|
||||
@@ -58,4 +58,12 @@ class CombinationFactoryTest {
|
||||
.isInstanceOf(WorldLoadException.class)
|
||||
.hasMessageContaining("wraith");
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsSelfCombination() {
|
||||
CombinationDto dto = new CombinationDto("torch", "torch", null, null, null, null, "x", null);
|
||||
assertThatThrownBy(() -> CombinationFactory.fromDto(dto, items()))
|
||||
.isInstanceOf(WorldLoadException.class)
|
||||
.hasMessageContaining("itself");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user