refactor: drop unused VFX scaffolding, single-line English doc, reduce debug logs
- Remove unused chain/ParticleTrail and chain/VolumeCurve (dead since the EntityEffect pivot replaced the per-particle emit path) plus their test suites. - Drop Vec3.lerp (only consumer was ParticleTrail). - Strip step-by-step "[N/9]" debug logs from the orchestrator and per-entity logs from HytaleEntitySource / HytalePlayerRayCaster / ChainDamageApplier; keep one summary log per click and warnings on failure. - Extract resolveChain and tryEmitVfx helpers in ChainLightningSceptreInteraction so firstRun reads top-down (cooldown gate -> resolve -> damage -> vfx -> deduct). - Translate every Java doc/comment to single-line English. Tests: 30/30 green (29 baseline kept + 1 chain damage adapter test). Build: ./gradlew shadowJar clean.
This commit is contained in:
+39
-106
@@ -18,42 +18,19 @@ import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Phase 3 — Orchestrateur runtime du sceptre Chain Lightning.
|
||||
*
|
||||
* <p>Pipeline (per CONTEXT.md "Pipeline firstRun — séquence exacte") :
|
||||
* <ol>
|
||||
* <li>Récupérer (ou créer) le cooldown "chain_lightning_sceptre" (4.0s, 1 charge).</li>
|
||||
* <li>Si {@code hasCooldown(false)} → return silencieux (refus UX).</li>
|
||||
* <li>Extraire playerRef + commandBuffer depuis InteractionContext.</li>
|
||||
* <li>Construire HytalePlayerRayCaster + HytaleEntitySource (frontière vers Phase 2).</li>
|
||||
* <li>Appeler ChainResolver.resolve avec Vec3.ZERO placeholders pour origin/direction
|
||||
* (la lambda ray-cast les ignore — TargetUtil reconstruit eye-origin en interne).</li>
|
||||
* <li>Si hits.isEmpty() → log fine + return SANS consommer le cooldown (rater = re-cliquer
|
||||
* immédiatement permis, decision CONTEXT).</li>
|
||||
* <li>ChainDamageApplier.apply(hits, playerRef, commandBuffer, commandBuffer).</li>
|
||||
* <li>cooldown.deductCharge() APRÈS succès (decision CONTEXT : pas de cooldown si rate).</li>
|
||||
* <li>Log info structuré avec count + ids.</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>Try/catch global wrappe les étapes 3-9 — toute exception est loggée mais non propagée
|
||||
* (éviter de crash le tick serveur, decision CONTEXT "Pas de try/catch défensif partout").
|
||||
*/
|
||||
/** Runtime orchestrator: cooldown gate, chain resolution, damage, VFX emit, charge deduct. */
|
||||
public final class ChainLightningSceptreInteraction extends SimpleInstantInteraction {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(ChainLightningSceptreInteraction.class.getName());
|
||||
|
||||
// --- Constantes cooldown (per RESEARCH Q3) ---
|
||||
private static final String COOLDOWN_ID = "chain_lightning_sceptre";
|
||||
private static final float COOLDOWN_TIME = 4.0f;
|
||||
private static final float[] CHARGE_TIMES = new float[]{4.0f};
|
||||
private static final boolean FORCE_CREATE = true;
|
||||
private static final boolean INTERRUPT_RECHARGE = false;
|
||||
private static final String COOLDOWN_ID = "chain_lightning_sceptre";
|
||||
private static final float COOLDOWN_TIME = 4.0f;
|
||||
private static final float[] CHARGE_TIMES = { 4.0f };
|
||||
private static final boolean FORCE_CREATE = true;
|
||||
private static final boolean INTERRUPT_RECHARGE = false;
|
||||
|
||||
// --- Constantes chaîne (per ROADMAP CHAIN-02 + CHAIN-03) ---
|
||||
private static final double RAY_MAX_BLOCKS = 25.0;
|
||||
private static final double RAY_MAX_BLOCKS = 25.0;
|
||||
|
||||
// --- BuilderCodec préservé tel quel depuis Phase 1 ---
|
||||
@Nonnull
|
||||
public static final BuilderCodec<ChainLightningSceptreInteraction> CODEC =
|
||||
((BuilderCodec.Builder) BuilderCodec
|
||||
@@ -67,101 +44,57 @@ public final class ChainLightningSceptreInteraction extends SimpleInstantInterac
|
||||
public ChainLightningSceptreInteraction() {
|
||||
}
|
||||
|
||||
/** Runs the chain pipeline once per click; silently no-ops while on cooldown. */
|
||||
@Override
|
||||
protected void firstRun(@Nonnull InteractionType type,
|
||||
@Nonnull InteractionContext context,
|
||||
@Nonnull CooldownHandler cooldownHandler) {
|
||||
LOGGER.info(String.format("[ChainLightning][1/9] firstRun ENTRY type=%s", type));
|
||||
|
||||
// --- Étape 1 : récupérer le cooldown ---
|
||||
CooldownHandler.Cooldown cooldown = cooldownHandler.getCooldown(
|
||||
COOLDOWN_ID, COOLDOWN_TIME, CHARGE_TIMES, FORCE_CREATE, INTERRUPT_RECHARGE);
|
||||
if (cooldown == null) {
|
||||
LOGGER.warning("[ChainLightning][1/9] cooldown handler returned null — aborting");
|
||||
if (cooldown == null || cooldown.hasCooldown(false)) {
|
||||
return;
|
||||
}
|
||||
LOGGER.info(String.format("[ChainLightning][1/9] cooldown obtenu id=%s maxTime=%.1fs", COOLDOWN_ID, COOLDOWN_TIME));
|
||||
|
||||
// --- Étape 2 : check cooldown sans décompter ---
|
||||
boolean onCooldown = cooldown.hasCooldown(false);
|
||||
LOGGER.info(String.format("[ChainLightning][2/9] hasCooldown(false)=%s", onCooldown));
|
||||
if (onCooldown) {
|
||||
LOGGER.info("[ChainLightning][2/9] still on cooldown — silent refuse");
|
||||
Ref<EntityStore> playerRef = context.getEntity();
|
||||
CommandBuffer<EntityStore> commandBuffer = context.getCommandBuffer();
|
||||
if (playerRef == null || commandBuffer == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// --- Étape 3 : extraire player + commandBuffer ---
|
||||
Ref<EntityStore> playerRef = context.getEntity();
|
||||
CommandBuffer<EntityStore> commandBuffer = context.getCommandBuffer();
|
||||
LOGGER.info(String.format("[ChainLightning][3/9] playerRef=%s commandBuffer=%s",
|
||||
playerRef == null ? "null" : ("ref:" + playerRef.getIndex() + " valid=" + playerRef.isValid()),
|
||||
commandBuffer == null ? "null" : commandBuffer.getClass().getSimpleName()));
|
||||
if (playerRef == null || commandBuffer == null) {
|
||||
LOGGER.warning("[ChainLightning][3/9] missing playerRef or commandBuffer — abort");
|
||||
return;
|
||||
}
|
||||
|
||||
// --- Étape 4 : construire les adapters ---
|
||||
// CommandBuffer<EntityStore> implémente ComponentAccessor<EntityStore> — passé directement
|
||||
HytalePlayerRayCaster ray = new HytalePlayerRayCaster(playerRef, commandBuffer);
|
||||
HytaleEntitySource neighbors = new HytaleEntitySource(commandBuffer);
|
||||
LOGGER.info("[ChainLightning][4/9] adapters built (RayCaster + EntitySource)");
|
||||
|
||||
// --- Étape 5 : résolution BFS (origin/direction = placeholders ignorés par le wrapper) ---
|
||||
LOGGER.info(String.format("[ChainLightning][5/9] resolving chain rayMax=%.1f maxTargets=%d radius=%.1f",
|
||||
RAY_MAX_BLOCKS, ChainParameters.DEFAULT.maxTargets(), ChainParameters.DEFAULT.chainRadius()));
|
||||
List<ChainHit> hits = ChainResolver.resolve(
|
||||
Vec3.ZERO, Vec3.ZERO, RAY_MAX_BLOCKS,
|
||||
ray, neighbors, ChainParameters.DEFAULT
|
||||
);
|
||||
LOGGER.info(String.format("[ChainLightning][5/9] resolution returned %d hits", hits.size()));
|
||||
|
||||
// --- Étape 6 : pas de cible → return SANS cooldown ---
|
||||
List<ChainHit> hits = resolveChain(playerRef, commandBuffer);
|
||||
if (hits.isEmpty()) {
|
||||
LOGGER.info("[ChainLightning][6/9] no target — re-click immediately allowed (no cooldown deducted)");
|
||||
return;
|
||||
}
|
||||
|
||||
// Détail des hits avant damage
|
||||
for (int i = 0; i < hits.size(); i++) {
|
||||
ChainHit h = hits.get(i);
|
||||
LOGGER.info(String.format("[ChainLightning][6/9] hit[%d] target=%s damageHp=%d hopIndex=%d",
|
||||
i, h.target().id(), h.damageHp(), h.hopIndex()));
|
||||
}
|
||||
|
||||
// --- Étape 7 : appliquer les dégâts ---
|
||||
LOGGER.info(String.format("[ChainLightning][7/9] applying damage to %d targets (attacker=ref:%d)",
|
||||
hits.size(), playerRef.getIndex()));
|
||||
ChainDamageApplier.apply(hits, playerRef, commandBuffer, commandBuffer);
|
||||
LOGGER.info("[ChainLightning][7/9] damage application returned");
|
||||
|
||||
// --- Étape 7.5 : émettre VFX/SFX (best-effort) ---
|
||||
// CONTEXT failure-mode decision : damage déjà appliqué — si l'emit échoue, log + continue
|
||||
// vers le cooldown. Pas de propagation : le tick serveur ne doit pas crash sur un bug VFX.
|
||||
try {
|
||||
LOGGER.info(String.format("[ChainLightning][7.5/9] vfx emit START hits=%d", hits.size()));
|
||||
HytaleVfxEmitter.playChainEffects(hits, playerRef, commandBuffer);
|
||||
LOGGER.info("[ChainLightning][7.5/9] vfx emit DONE");
|
||||
} catch (Throwable t) {
|
||||
LOGGER.log(Level.WARNING, "[ChainLightning][7.5/9] vfx emit failed (damage already applied)", t);
|
||||
}
|
||||
|
||||
// --- Étape 8 : démarrer le cooldown APRÈS succès ---
|
||||
tryEmitVfx(hits, playerRef, commandBuffer);
|
||||
cooldown.deductCharge();
|
||||
LOGGER.info(String.format("[ChainLightning][8/9] cooldown deducted (next available in %.1fs)", COOLDOWN_TIME));
|
||||
|
||||
// --- Étape 9 : log structuré final ---
|
||||
StringBuilder ids = new StringBuilder();
|
||||
for (int i = 0; i < hits.size(); i++) {
|
||||
if (i > 0) ids.append(',');
|
||||
ids.append(hits.get(i).target().id());
|
||||
}
|
||||
LOGGER.info(String.format("[ChainLightning][9/9] DONE ref:%d chained %d targets [%s]",
|
||||
playerRef.getIndex(), hits.size(), ids.toString()));
|
||||
LOGGER.info(String.format("[ChainLightning] ref:%d chained %d targets",
|
||||
playerRef.getIndex(), hits.size()));
|
||||
} catch (Throwable t) {
|
||||
// CONTEXT decision : try/catch global pour éviter crash tick serveur
|
||||
LOGGER.log(Level.WARNING, "[ChainLightning] chain resolution failed", t);
|
||||
LOGGER.log(Level.WARNING, "[ChainLightning] chain pipeline failed", t);
|
||||
}
|
||||
}
|
||||
|
||||
/** Builds the Hytale-bound adapters and runs the pure resolver against the live world. */
|
||||
private static List<ChainHit> resolveChain(@Nonnull Ref<EntityStore> playerRef,
|
||||
@Nonnull CommandBuffer<EntityStore> commandBuffer) {
|
||||
HytalePlayerRayCaster ray = new HytalePlayerRayCaster(playerRef, commandBuffer);
|
||||
HytaleEntitySource neighbors = new HytaleEntitySource(commandBuffer);
|
||||
return ChainResolver.resolve(
|
||||
Vec3.ZERO, Vec3.ZERO, RAY_MAX_BLOCKS,
|
||||
ray, neighbors, ChainParameters.DEFAULT
|
||||
);
|
||||
}
|
||||
|
||||
/** VFX emit is best-effort: damage is already applied so a failure must not abort the cooldown step. */
|
||||
private static void tryEmitVfx(@Nonnull List<ChainHit> hits,
|
||||
@Nonnull Ref<EntityStore> playerRef,
|
||||
@Nonnull CommandBuffer<EntityStore> commandBuffer) {
|
||||
try {
|
||||
HytaleVfxEmitter.playChainEffects(hits, playerRef, commandBuffer);
|
||||
} catch (Throwable t) {
|
||||
LOGGER.log(Level.WARNING, "[ChainLightning] vfx emit failed (damage already applied)", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user