From 0069d4c47eb36e218c9a6568dce476426e70c344 Mon Sep 17 00:00:00 2001 From: kayjaydee Date: Thu, 23 Apr 2026 14:52:42 +0200 Subject: [PATCH] feat(03-05): ajoute 4 champs Visual* optionnels + round-trip tests (Task 1) - GravityFlipRegion: VisualColor/VisualMode/VisualRefreshMs/VisualOpacity avec defaults (#00FFFF, Outline, 1000, 0.5), getters/setters, javadoc. - CODEC: 4 KeyedCodec optionnels (pas de validator nonNull) => back-compat legacy regions.json. - 2 tests round-trip (custom + defaults quand champs absents). --- .../gravityflip/region/GravityFlipRegion.java | 37 +++++++++++++++++++ .../region/GravityFlipRegionCodecTest.java | 35 ++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/src/main/java/com/mythlane/gravityflip/region/GravityFlipRegion.java b/src/main/java/com/mythlane/gravityflip/region/GravityFlipRegion.java index c22827f..352dd0d 100644 --- a/src/main/java/com/mythlane/gravityflip/region/GravityFlipRegion.java +++ b/src/main/java/com/mythlane/gravityflip/region/GravityFlipRegion.java @@ -29,6 +29,16 @@ import com.hypixel.hytale.math.vector.Vector3d; * NPCs in-region are NOT flipped / seeded. *
  • {@code AffectItems} — optional, default {@code true}. If {@code false}, * item {@code PhysicsValues.invertedGravity} is NOT mutated.
  • + *
  • {@code VisualColor} — optional (Plan 03-05). Default {@code "#00FFFF"} (cyan). + * Hex string {@code #RRGGBB} rendered as debug cube color. Fallback to cyan on invalid hex + * (parsing + validation lives in {@code RegionVisualizer}, NOT here).
  • + *
  • {@code VisualMode} — optional, default {@code "Outline"}. One of + * {@code "Outline"} / {@code "Faces"} / {@code "Both"} / {@code "None"}. Unknown value + * falls back to {@code "Outline"}.
  • + *
  • {@code VisualRefreshMs} — optional, default {@code 1000}. Emission period in ms + * for the debug shape (refresh cadence ; TTL = refreshMs * 1.2 to avoid flicker).
  • + *
  • {@code VisualOpacity} — optional, default {@code 0.5}. Cube opacity in + * {@code [0.0, 1.0]} (clamped in {@code RegionVisualizer}).
  • * * *

    Back-compat : a legacy {@code regions.json} containing only Name+Box+Enabled @@ -69,6 +79,15 @@ public final class GravityFlipRegion { (r, v) -> r.affectNpcs = v, r -> r.affectNpcs).add() .append(new KeyedCodec<>("AffectItems", Codec.BOOLEAN), (r, v) -> r.affectItems = v, r -> r.affectItems).add() + // --- Plan 03-05 : 4 optional visualization fields --- + .append(new KeyedCodec<>("VisualColor", Codec.STRING), + (r, v) -> r.visualColor = v, r -> r.visualColor).add() + .append(new KeyedCodec<>("VisualMode", Codec.STRING), + (r, v) -> r.visualMode = v, r -> r.visualMode).add() + .append(new KeyedCodec<>("VisualRefreshMs", Codec.INTEGER), + (r, v) -> r.visualRefreshMs = v, r -> r.visualRefreshMs).add() + .append(new KeyedCodec<>("VisualOpacity", Codec.DOUBLE), + (r, v) -> r.visualOpacity = v, r -> r.visualOpacity).add() .build(); // Package-private mutable fields written directly by the codec setters. @@ -84,6 +103,12 @@ public final class GravityFlipRegion { boolean affectNpcs = true; boolean affectItems = true; + // Plan 03-05 : visualization fields — defaults applied when key absent in BSON. + String visualColor = "#00FFFF"; + String visualMode = "Outline"; + int visualRefreshMs = 1000; + double visualOpacity = 0.5; + public GravityFlipRegion() {} public GravityFlipRegion(String name, Box box, boolean enabled) { @@ -118,6 +143,18 @@ public final class GravityFlipRegion { public void setAffectNpcs(boolean v) { this.affectNpcs = v; } public void setAffectItems(boolean v) { this.affectItems = v; } + // --- Plan 03-05 getters / setters --- + + public String getVisualColor() { return visualColor; } + public String getVisualMode() { return visualMode; } + public int getVisualRefreshMs() { return visualRefreshMs; } + public double getVisualOpacity() { return visualOpacity; } + + public void setVisualColor(String v) { this.visualColor = v; } + public void setVisualMode(String v) { this.visualMode = v; } + public void setVisualRefreshMs(int v) { this.visualRefreshMs = v; } + public void setVisualOpacity(double v) { this.visualOpacity = v; } + /** Convenience accessor for tick-loop / physics consumers in Phase 02-02. */ public Box asBox() { return box; } } diff --git a/src/test/java/com/mythlane/gravityflip/region/GravityFlipRegionCodecTest.java b/src/test/java/com/mythlane/gravityflip/region/GravityFlipRegionCodecTest.java index df797e3..a8e1ff7 100644 --- a/src/test/java/com/mythlane/gravityflip/region/GravityFlipRegionCodecTest.java +++ b/src/test/java/com/mythlane/gravityflip/region/GravityFlipRegionCodecTest.java @@ -144,6 +144,41 @@ class GravityFlipRegionCodecTest { assertFalse(decoded.isAffectItems()); } + // ---------- Plan 03-05 : 4 visualization fields ---------- + + @Test + void roundTripPreservesVisualFields() { + GravityFlipRegion src = baseRegion(); + src.setVisualColor("#FF8800"); + src.setVisualMode("Faces"); + src.setVisualRefreshMs(500); + src.setVisualOpacity(0.75); + + GravityFlipRegion decoded = roundTrip(src); + + assertEquals("#FF8800", decoded.getVisualColor()); + assertEquals("Faces", decoded.getVisualMode()); + assertEquals(500, decoded.getVisualRefreshMs()); + assertEquals(0.75, decoded.getVisualOpacity(), 1e-9); + } + + @Test + void roundTripPreservesVisualDefaultsWhenFieldsAbsent() { + // Region construite via constructeur legacy 3-arg — simule un regions.json legacy + // (ni 03-04 ni 03-05 présents). + GravityFlipRegion src = new GravityFlipRegion( + "legacy-viz", + new Box(new Vector3d(0, 0, 0), new Vector3d(1, 1, 1)), + true); + + GravityFlipRegion decoded = roundTrip(src); + + assertEquals("#00FFFF", decoded.getVisualColor(), "default VisualColor=#00FFFF (cyan)"); + assertEquals("Outline", decoded.getVisualMode(), "default VisualMode=Outline"); + assertEquals(1000, decoded.getVisualRefreshMs(), "default VisualRefreshMs=1000"); + assertEquals(0.5, decoded.getVisualOpacity(), 1e-9, "default VisualOpacity=0.5"); + } + private static GravityFlipRegion baseRegion() { return new GravityFlipRegion( "r",