Commit Graph

13 Commits

Author SHA1 Message Date
kayjaydee 3070353579 feat(04-02): register /gravityflip command in start() (CMD-01)
- wire getCommandRegistry().registerCommand(new GravityFlipCommand(this))
- import com.mythlane.gravityflip.command.GravityFlipCommand
- no manual cleanup: CommandRegistry.register auto-adds shutdownTask
2026-04-24 14:07:44 +02:00
kayjaydee ae0e2064dc feat(04-01): register GravityFlipWand interaction + item JSON (WAND-01, WAND-02)
- GravityFlipWandInteraction extends SimpleInstantInteraction; Primary/Secondary
  routed through firstRun(), writes to WandSelectionStore via volatile bindStore()
- Items/gravityflip_wand.json: Utility item (Icon Torch_Fire) whose Primary and
  Secondary Interactions reference Type=GravityFlipWand
- GravityFlipPlugin.setup(): constructs WandSelectionStore, injects it into the
  interaction class, then registers via getCodecRegistry(Interaction.CODEC)
  (pattern from InstancesPlugin:158 / ExitInstanceInteraction)
- Expose wandSelections() getter for Phase 04-02+ commands
2026-04-23 18:51:13 +02:00
kayjaydee 5b2484be3b feat: add showcase region on first run to enhance user experience
- Implemented a mechanism to bootstrap a demo gravity flip region when regions.json is absent or empty.
- The showcase region features a 10×20×10 box with upward force and Torch_Fire particles, allowing immediate demonstration of functionality.
- Users can customize or remove this region via regions.json.
2026-04-23 16:52:23 +02:00
kayjaydee f5e6cc24f5 refactor: remove all debug code (DumpParticles, RegionEnterNotifier, dead ctors) 2026-04-23 16:45:37 +02:00
kayjaydee 1e47f4e846 refactor(03-06): remove debug logs + efficiency pass
Log cleanup:
- Drop redundant INFO "Gravity Flip enabled" in setup() (substantive version at
  start() remains as the single startup line).
- Drop INFO "debug enter/exit notifier ENABLED" (the notifier itself emits per
  region transition; extra startup noise not needed).
- Remove dead back-compat GravityApplier ctors that accepted an infoHandler
  Consumer (no callers post-03-06; debug logs already stripped).
- Clean stale javadoc referencing removed [DBG npc.woken]/[DBG npc.ctrlNull]
  one-shot logs.
- Also ship the pre-staged cleanup work: RegionEnterNotifier gated behind
  GRAVITYFLIP_DEBUG_NOTIFY env var, GravityApplier infoHandler field removed,
  tick logs stripped.

Efficiency:
- RegionVisualizer.resolveParticleId now memoises the requested->resolved
  mapping in a ConcurrentHashMap, eliminating the ParticleSystem.getAssetMap()
  lookup on every tick emission per region. Warn-once semantics preserved via
  warnedInvalidIds. Fail-open path (AssetMap unavailable in tests) intentionally
  does not populate the cache.
- Document in GravityApplier javadoc why Pass 1 + Pass 2 cannot be fused:
  restore requires the complete currentlyInRegion set before diffing against
  previouslyInverted.

Considered but not applied:
- ParticleEdgeEmitter.edgePoints caching per (box, density): throttled to
  >=100ms refresh and typical <20 regions => alloc pressure negligible;
  premature without measurement.
- Reusing RegionRegistry snapshot in RegionEnterNotifier: notifier is
  opt-in/off-by-default, so its independent ECS scan has zero prod cost.

Tests: ./gradlew clean build green, no test changes required.
2026-04-23 16:43:07 +02:00
kayjaydee 7bbd65dad2 feat(03-06): add particle-system dump for discovery (Task 1)
- New DumpParticlesCommand utility: enumerates loaded ParticleSystem
  asset-ids via ParticleSystem.getAssetMap().getAssetMap().keySet().
- Wired in GravityFlipPlugin.start() behind GRAVITYFLIP_DUMP_PARTICLES
  env var (or -Dgravityflip.dumpParticles sysprop). No-op when unset.
- Boot-time fallback approach (plan-allowed) -- proper CommandBase
  registration deferred to avoid i18n translation-key plumbing for a
  one-shot discovery dump.
- See .planning/phases/03-gravity-physics/03-06-DUMP-NOTES.md for
  trigger instructions and the user checkpoint before Task 3.
2026-04-23 15:32:55 +02:00
kayjaydee 3511493687 feat(03-05): cable RegionVisualizer dans RegionTickLoop + shutdown clearAll (Task 3)
- RegionTickLoop: nouveau ctor 4-arg (registry, gravityApplier, regionVisualizer,
  errorHandler). Les ctors 2-arg et 3-arg existants delegent (back-compat tests).
  Tick appelle regionVisualizer.visualize(w, snapshot) apres gravityApplier.apply()
  avec null-safe gate (meme pattern que gravityApplier).
- GravityFlipPlugin.start(): construit RegionVisualizer avec errorHandler log WARN,
  le passe au tickLoop.
- GravityFlipPlugin.shutdown(): appelle regionVisualizer.clearAll(world) AVANT
  tickLoop.stop() (si Universe encore vivante), emet ClearDebugShapes a tous
  les PlayerRefs. Si universe null, fallback TTL expiration.
- Aucun nouveau scheduler/thread cree.
2026-04-23 14:55:24 +02:00
kayjaydee d43aa4a98c feat(03-04): FallDamageSuppressorSystem + wiring + cleanup [DBG throttle] (Task 3)
- FallDamageSuppressorSystem: subclass DamageEventSystem, cancel Damage
  cause=Fall quand guard.shouldSuppressFallDamage; FALL_INDEX lazy via
  DamageCause.getAssetMap().getIndex; groupe inspectDamageGroup
- GravityApplier: 3-arg constructor avec FallDamageGuard; first-match
  region, filtres AffectPlayers/Npcs/Items avant wake, seed addForce
  paramétré par VerticalForce (remplace hardcode 0.1); notifie
  guard.markInRegion / markExit (via lastKnownRegion map pour Pass 2)
- Cleanup: retire les AtomicInteger counters + log [DBG tick=...]
  throttle 1s; conserve logs one-shot [DBG npc.woken]/[DBG npc.ctrlNull]
- GravityFlipPlugin.setup(): instancie FallDamageGuard, registerSystem
  sur entityStoreRegistry; start() passe guard au GravityApplier
- Imports Query + CommandBuffer alignés sur FlockMembershipSystems
2026-04-23 14:03:58 +02:00
kayjaydee a834c59b66 feat(03-04): ajoute 6 champs optionnels sur GravityFlipRegion (Task 1)
- POJO: FallDamage (false), GracePeriodMs (2500), VerticalForce (0.1),
  AffectPlayers/Npcs/Items (true) avec getters/setters
- CODEC: 6 .append sans nonNull validator (sémantique optionnelle)
- Tests: 6 round-trip + back-compat defaults (9 tests total)
2026-04-23 14:00:12 +02:00
kayjaydee f7dcc7d59a feat(03-01): wire GravityApplier into RegionTickLoop + plugin (Task 2)
- RegionTickLoop gagne un constructeur 3-args (registry, gravityApplier, errorHandler)
- Ancien constructeur 2-args conservé pour rétrocompat des tests Phase 02
- Tick body appelle gravityApplier.apply(w, registry.currentSnapshot(w)) après refreshFor
- GravityFlipPlugin.start() construit l'applier et l'injecte dans le tickLoop
- Log de démarrage mis à jour: 'gravity inversion active'
2026-04-23 10:32:28 +02:00
kayjaydee 53b43d83c7 feat(02-02): add RegionTickLoop + plugin wiring (Task 3)
- RegionTickLoop: single-thread daemon scheduler @100ms, 2s initial delay,
  shutdown -> awaitTermination(5s) -> shutdownNow lifecycle.
  Three start overloads: (World), (Supplier<World>), (Runnable). Errors are
  routed to a Consumer<Throwable> so an exception in one tick never kills
  the scheduler.
- GravityFlipPlugin.start():
  * builds RegionRegistry from configHolder.get() + holder for save()
  * starts the tick loop with a Supplier<World> that lazily resolves
    Universe.get().getDefaultWorld() (deviation: PrepareUniverseEvent in the
    pinned API only carries WorldConfigProvider — no Universe/World access,
    so we use the lazy-supplier pattern from MythWorld instead)
  * registers the ScheduledFuture with TaskRegistry (raw cast, try/catch fallback)
- GravityFlipPlugin.shutdown(): tickLoop.stop() BEFORE super.shutdown()
- RegionTickLoopTest: 3 timing tests pass (>=8 ticks/sec, stop within 5s,
  exception resilience).
- gradle build green; fat jar contains both region/ and tick/ class dirs.
2026-04-23 00:56:12 +02:00
kayjaydee 216f544d9b feat(02-01): wire GravityFlipConfig to regions.json via named withConfig
- GravityFlipConfig wraps List<GravityFlipRegion> via ArrayCodec under the
  KeyedCodec("Regions", ...) entry. Decoded list is a mutable ArrayList
  (NOT List.of(arr)) so Phase 4 commands can mutate at runtime.
- GravityFlipPlugin.configHolder uses the named overload
  withConfig("regions", GravityFlipConfig.CODEC) — produces
  <dataDirectory>/regions.json. The 1-arg overload would hardcode config.json.
- configHolder() javadoc documents the save contract (no auto-save on
  shutdown; mutators must call configHolder.save() explicitly) and the shared
  mutable reference caveat that 02-02's RegionRegistry will resolve.
- 4 round-trip tests cover: empty default, two-region order preservation,
  empty list, and list mutability (regression guard against List.of).
2026-04-23 00:43:26 +02:00
kayjaydee 2e26f8a10f feat(01): plugin manifest and entry class 2026-04-22 23:28:13 +02:00