03754a0646
- 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.
101 lines
4.6 KiB
Java
101 lines
4.6 KiB
Java
package com.mythlane.chainlightning.sceptre;
|
|
|
|
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
|
import com.hypixel.hytale.component.CommandBuffer;
|
|
import com.hypixel.hytale.component.Ref;
|
|
import com.hypixel.hytale.protocol.InteractionType;
|
|
import com.hypixel.hytale.server.core.entity.InteractionContext;
|
|
import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler;
|
|
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.SimpleInstantInteraction;
|
|
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
|
import com.mythlane.chainlightning.chain.ChainHit;
|
|
import com.mythlane.chainlightning.chain.ChainParameters;
|
|
import com.mythlane.chainlightning.chain.ChainResolver;
|
|
import com.mythlane.chainlightning.chain.Vec3;
|
|
|
|
import javax.annotation.Nonnull;
|
|
import java.util.List;
|
|
import java.util.logging.Level;
|
|
import java.util.logging.Logger;
|
|
|
|
/** 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());
|
|
|
|
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;
|
|
|
|
private static final double RAY_MAX_BLOCKS = 25.0;
|
|
|
|
@Nonnull
|
|
public static final BuilderCodec<ChainLightningSceptreInteraction> CODEC =
|
|
((BuilderCodec.Builder) BuilderCodec
|
|
.builder(ChainLightningSceptreInteraction.class,
|
|
ChainLightningSceptreInteraction::new,
|
|
SimpleInstantInteraction.CODEC)
|
|
.documentation(
|
|
"Chain Lightning sceptre: primary/secondary click chains lightning to up to 5 targets."))
|
|
.build();
|
|
|
|
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) {
|
|
CooldownHandler.Cooldown cooldown = cooldownHandler.getCooldown(
|
|
COOLDOWN_ID, COOLDOWN_TIME, CHARGE_TIMES, FORCE_CREATE, INTERRUPT_RECHARGE);
|
|
if (cooldown == null || cooldown.hasCooldown(false)) {
|
|
return;
|
|
}
|
|
|
|
Ref<EntityStore> playerRef = context.getEntity();
|
|
CommandBuffer<EntityStore> commandBuffer = context.getCommandBuffer();
|
|
if (playerRef == null || commandBuffer == null) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
List<ChainHit> hits = resolveChain(playerRef, commandBuffer);
|
|
if (hits.isEmpty()) {
|
|
return;
|
|
}
|
|
ChainDamageApplier.apply(hits, playerRef, commandBuffer, commandBuffer);
|
|
tryEmitVfx(hits, playerRef, commandBuffer);
|
|
cooldown.deductCharge();
|
|
LOGGER.info(String.format("[ChainLightning] ref:%d chained %d targets",
|
|
playerRef.getIndex(), hits.size()));
|
|
} catch (Throwable 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);
|
|
}
|
|
}
|
|
}
|