feat(phase-3): runtime integration — chain damage application + cooldown
Wires the pure ChainResolver (Phase 2) to the live Hytale runtime via four sceptre/ adapters: - HytaleEntityAdapter — eager snapshot Ref<EntityStore> -> ChainEntity via TransformComponent.getPosition() and EntityStatMap.get(health) > 0 - HytalePlayerRayCaster — captures playerRef, delegates to TargetUtil.getTargetEntity (auto eye-origin + head-rotation) - HytaleEntitySource — wraps TargetUtil.getAllEntitiesInSphere - ChainDamageApplier — fires DamageSystems.executeDamage per hit; injectable DamageExecutor SAM keeps the helper unit-testable. ChainLightningSceptreInteraction.firstRun is rewritten end-to-end: cooldown gate (hasCooldown(false) -> deductCharge() only on success), ray-cast -> BFS -> damage application, structured logging, try/catch wrapper to keep a runtime fault from killing the server tick. API corrections discovered against the decompiled jar: - Ref has no uuid() — use "ref:" + getIndex() for the chain id - DamageCause.PHYSICAL is @Nullable until runtime — use the int-index overload of Damage with index 0 - Static mock of DamageSystems crashes class init — abstracted behind a DamageExecutor SAM with a default lazy holder Tests: 33/33 green (25 from Phase 2 + 4 ChainDamageApplier tests + 4 fixture sanity). ./gradlew build SUCCESSFUL, JAR auto-deployed. MANUAL UAT (10 items) pending in-game.
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
package com.mythlane.chainlightning.sceptre;
|
||||
|
||||
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.modules.entity.component.TransformComponent;
|
||||
import com.hypixel.hytale.server.core.modules.entitystats.EntityStatMap;
|
||||
import com.hypixel.hytale.server.core.modules.entitystats.EntityStatValue;
|
||||
import com.hypixel.hytale.server.core.modules.entitystats.asset.DefaultEntityStatTypes;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import com.mythlane.chainlightning.chain.ChainEntity;
|
||||
import com.mythlane.chainlightning.chain.Vec3;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Adapter immuable Ref<EntityStore> -> ChainEntity (Phase 2 SAM).
|
||||
*
|
||||
* <p>Snapshot eager : la position et l'etat alive sont lus AU MOMENT de la creation.
|
||||
* La resolution BFS de ChainResolver lit ces valeurs figees -- robuste face a un mob
|
||||
* qui bouge ou meurt pendant la resolution.
|
||||
*
|
||||
* <p>Si TransformComponent est null (entite hors monde), l'adapter retourne un snapshot
|
||||
* "mort" (alive=false, position=Vec3.ZERO) qui sera filtre par ChainResolver.
|
||||
*/
|
||||
public final class HytaleEntityAdapter implements ChainEntity {
|
||||
|
||||
private final Ref<EntityStore> ref;
|
||||
private final String id;
|
||||
private final Vec3 position;
|
||||
private final boolean alive;
|
||||
|
||||
private HytaleEntityAdapter(Ref<EntityStore> ref, String id, Vec3 position, boolean alive) {
|
||||
this.ref = ref;
|
||||
this.id = id;
|
||||
this.position = position;
|
||||
this.alive = alive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructeur package-private pour les tests : permet de construire un adapter
|
||||
* sans passer par snapshot() (qui initialise TransformComponent.getComponentType()
|
||||
* et donc le runtime Hytale complet).
|
||||
*/
|
||||
static HytaleEntityAdapter forTest(@Nonnull Ref<EntityStore> ref, @Nonnull String id,
|
||||
@Nonnull Vec3 position, boolean alive) {
|
||||
return new HytaleEntityAdapter(ref, id, position, alive);
|
||||
}
|
||||
|
||||
/**
|
||||
* Projette un Ref<EntityStore> vers un ChainEntity en lisant TransformComponent + EntityStatMap.
|
||||
*
|
||||
* @param ref reference entite Hytale
|
||||
* @param accessor ComponentAccessor (CommandBuffer implemente ComponentAccessor)
|
||||
* @return adapter snapshot. Jamais null.
|
||||
*/
|
||||
@Nonnull
|
||||
public static HytaleEntityAdapter snapshot(@Nonnull Ref<EntityStore> ref,
|
||||
@Nonnull ComponentAccessor<EntityStore> accessor) {
|
||||
String id = "ref:" + ref.getIndex();
|
||||
|
||||
TransformComponent tc = accessor.getComponent(ref, TransformComponent.getComponentType());
|
||||
if (tc == null) {
|
||||
return new HytaleEntityAdapter(ref, id, Vec3.ZERO, false);
|
||||
}
|
||||
Vector3d pos = tc.getPosition();
|
||||
Vec3 vec = new Vec3(pos.x, pos.y, pos.z);
|
||||
|
||||
EntityStatMap statMap = accessor.getComponent(ref, EntityStatMap.getComponentType());
|
||||
EntityStatValue health = statMap != null ? statMap.get(DefaultEntityStatTypes.getHealth()) : null;
|
||||
boolean alive = health != null && health.get() > 0.0f;
|
||||
|
||||
return new HytaleEntityAdapter(ref, id, vec, alive);
|
||||
}
|
||||
|
||||
/** Reference Hytale sous-jacente, exposee pour DamageSystems.executeDamage. */
|
||||
@Nonnull
|
||||
public Ref<EntityStore> ref() {
|
||||
return ref;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3 position() {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAlive() {
|
||||
return alive;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user