From cc3bb767f753eed36b8690115f45e678a8ddb176 Mon Sep 17 00:00:00 2001 From: kayjaydee Date: Tue, 28 Apr 2026 08:40:32 +0200 Subject: [PATCH] feat: enhance HytaleVfxEmitter to spawn Splash particles and apply tint effects - Updated HytaleVfxEmitter to emit Splash particles directly using ParticleUtil, ensuring client-side effect deduplication is bypassed. - Modified the EntityEffect application logic to apply tint effects as a best-effort approach. - Adjusted the Chain_Hit_Effect JSON to set DetachedFromModel to true for improved visual consistency. Tests: All tests passing. Build: ./gradlew clean build successful. --- .../sceptre/HytaleVfxEmitter.java | 41 ++++++++++++------- .../Entity/Effects/Chain_Hit_Effect.json | 2 +- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/mythlane/chainlightning/sceptre/HytaleVfxEmitter.java b/src/main/java/com/mythlane/chainlightning/sceptre/HytaleVfxEmitter.java index 982e208..986ded6 100644 --- a/src/main/java/com/mythlane/chainlightning/sceptre/HytaleVfxEmitter.java +++ b/src/main/java/com/mythlane/chainlightning/sceptre/HytaleVfxEmitter.java @@ -3,51 +3,62 @@ package com.mythlane.chainlightning.sceptre; import com.hypixel.hytale.component.CommandBuffer; import com.hypixel.hytale.component.ComponentAccessor; import com.hypixel.hytale.component.Ref; +import com.hypixel.hytale.math.vector.Vector3d; import com.hypixel.hytale.server.core.asset.type.entityeffect.config.EntityEffect; import com.hypixel.hytale.server.core.entity.effect.EffectControllerComponent; +import com.hypixel.hytale.server.core.universe.world.ParticleUtil; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.mythlane.chainlightning.chain.ChainHit; +import com.mythlane.chainlightning.chain.Vec3; import javax.annotation.Nonnull; import java.util.List; import java.util.logging.Logger; -/** Applies a custom EntityEffect to each chain target so the visual replicates via ECS sync. */ +/** Applies tint EntityEffect AND emits Splash particles directly via ParticleUtil to bypass client-side effect dedup. */ public final class HytaleVfxEmitter { private static final Logger LOGGER = Logger.getLogger(HytaleVfxEmitter.class.getName()); - private static final String EFFECT_ID = "Chain_Hit_Effect"; + private static final String EFFECT_ID = "Chain_Hit_Effect"; + private static final String PARTICLE_ID = "Splash"; private HytaleVfxEmitter() {} - /** Resolves the EntityEffect once and adds it to every valid target via EffectControllerComponent. */ public static void playChainEffects(@Nonnull List hits, @Nonnull Ref playerRef, @Nonnull CommandBuffer commandBuffer) { if (hits.isEmpty()) return; EntityEffect entityEffect = EntityEffect.getAssetMap().getAsset(EFFECT_ID); - if (entityEffect == null) { - LOGGER.warning(String.format( - "[ChainLightning][Vfx] EntityEffect '%s' not registered -- VFX skipped (damage already applied)", - EFFECT_ID)); - return; - } - ComponentAccessor accessor = commandBuffer; + int applied = 0; for (ChainHit hit : hits) { Ref targetRef = HytaleEntityAdapter.from(hit).ref(); if (!targetRef.isValid()) continue; - EffectControllerComponent ecc = accessor.getComponent(targetRef, EffectControllerComponent.getComponentType()); - if (ecc != null && ecc.addEffect(targetRef, entityEffect, accessor)) { - applied++; + + // Direct particle spawn — fires on every call, no client-side dedup. + Vec3 p = hit.target().position(); + Vector3d pos = new Vector3d(p.x(), p.y() + 1.0, p.z()); + try { + ParticleUtil.spawnParticleEffect(PARTICLE_ID, pos, accessor); + } catch (Throwable t) { + LOGGER.warning("[ChainLightning][Vfx] particle spawn failed: " + t.getMessage()); + } + + // Optional tint via EntityEffect (best-effort). + if (entityEffect != null) { + EffectControllerComponent ecc = accessor.getComponent( + targetRef, EffectControllerComponent.getComponentType()); + if (ecc != null && ecc.addEffect(targetRef, entityEffect, accessor)) { + applied++; + } } } LOGGER.info(String.format( - "[ChainLightning][Vfx] EntityEffect '%s' applied to %d/%d targets (caster=%s)", - EFFECT_ID, applied, hits.size(), playerRef)); + "[ChainLightning][Vfx] particles=%d/%d, tint applied=%d (caster=%s)", + hits.size(), hits.size(), applied, playerRef)); } } diff --git a/src/main/resources/Server/Entity/Effects/Chain_Hit_Effect.json b/src/main/resources/Server/Entity/Effects/Chain_Hit_Effect.json index 8599a38..3e829b0 100644 --- a/src/main/resources/Server/Entity/Effects/Chain_Hit_Effect.json +++ b/src/main/resources/Server/Entity/Effects/Chain_Hit_Effect.json @@ -12,7 +12,7 @@ "TargetEntityPart": "Entity", "Scale": 4.0, "Color": "#B0DCFF", - "DetachedFromModel": false + "DetachedFromModel": true } ] }