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.ChainHit; import com.mythlane.chainlightning.chain.Vec3; import javax.annotation.Nonnull; /** Immutable Ref<EntityStore> -> ChainEntity adapter; eager snapshot keeps BFS robust to mid-tick entity changes. */ public final class HytaleEntityAdapter implements ChainEntity { private final Ref ref; private final String id; private final Vec3 position; private final boolean alive; private HytaleEntityAdapter(Ref ref, String id, Vec3 position, boolean alive) { this.ref = ref; this.id = id; this.position = position; this.alive = alive; } /** Test-only factory that bypasses Hytale TransformComponent initialization. */ static HytaleEntityAdapter forTest(@Nonnull Ref ref, @Nonnull String id, @Nonnull Vec3 position, boolean alive) { return new HytaleEntityAdapter(ref, id, position, alive); } /** Reads TransformComponent + EntityStatMap once and freezes the result for the chain resolver. */ @Nonnull public static HytaleEntityAdapter snapshot(@Nonnull Ref ref, @Nonnull ComponentAccessor 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); } /** Single cast point so callers stop reaching into ChainEntity to recover the runtime adapter. */ @Nonnull public static HytaleEntityAdapter from(@Nonnull ChainHit hit) { return (HytaleEntityAdapter) hit.target(); } /** Underlying Hytale ref, exposed so DamageSystems and EffectControllerComponent can address the entity. */ @Nonnull public Ref ref() { return ref; } @Override public String id() { return id; } @Override public Vec3 position() { return position; } @Override public boolean isAlive() { return alive; } }