mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-12 03:47:27 +00:00
381 lines
11 KiB
Java
381 lines
11 KiB
Java
package kamkeel.npcs.entity;
|
|
|
|
import cpw.mods.fml.common.registry.IEntityAdditionalSpawnData;
|
|
import cpw.mods.fml.relauncher.Side;
|
|
import cpw.mods.fml.relauncher.SideOnly;
|
|
import io.netty.buffer.ByteBuf;
|
|
import kamkeel.npcs.controllers.data.ability.Ability;
|
|
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyDisplayData;
|
|
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyLightningData;
|
|
import kamkeel.npcs.util.ByteBufUtils;
|
|
import net.minecraft.entity.Entity;
|
|
import net.minecraft.entity.EntityLivingBase;
|
|
import net.minecraft.nbt.NBTTagCompound;
|
|
import net.minecraft.world.World;
|
|
|
|
/**
|
|
* Universal base class for all energy ability entities (Projectiles, Barriers, Sweeper).
|
|
* Provides shared visual data, owner tracking, charging state, and common entity properties.
|
|
*/
|
|
public abstract class EntityEnergyAbility extends Entity implements IEntityAdditionalSpawnData {
|
|
|
|
// ==================== SAFETY CONSTANTS ====================
|
|
protected static final int CHARGE_TIMEOUT_GRACE = 60; // Ticks of grace after charge should have ended
|
|
protected static final int HARD_LIFETIME_CAP = 1200; // 60 seconds absolute max for projectiles/sweepers
|
|
protected static final int BARRIER_HARD_LIFETIME_CAP = 12000; // 10 minutes absolute max for barriers
|
|
protected static final float MAX_ENTITY_SIZE = 100.0f; // Max size/width/length for any energy entity
|
|
protected static final float MAX_ENTITY_RADIUS = 50.0f; // Max radius for dome barriers
|
|
|
|
// ==================== VISUAL PROPERTIES ====================
|
|
protected EnergyDisplayData displayData = new EnergyDisplayData();
|
|
protected EnergyLightningData lightningData = new EnergyLightningData();
|
|
|
|
// Client-side lightning state (not saved to NBT)
|
|
@SideOnly(Side.CLIENT)
|
|
public transient Object lightningState; // Actually AttachedLightningRenderer.LightningState
|
|
|
|
// ==================== TRACKING ====================
|
|
protected int ownerEntityId = -1;
|
|
|
|
// ==================== STATE ====================
|
|
/**
|
|
* The ability that spawned this entity. Transient, not saved to NBT.
|
|
*/
|
|
protected transient Ability sourceAbility = null;
|
|
protected boolean previewMode = false;
|
|
protected EntityLivingBase previewOwner = null;
|
|
|
|
// ==================== CHARGING STATE ====================
|
|
protected boolean charging = false;
|
|
protected int chargeDuration = 0;
|
|
protected int chargeTick = 0;
|
|
protected static final int DW_CHARGING = 20;
|
|
|
|
public EntityEnergyAbility(World world) {
|
|
super(world);
|
|
this.isImmuneToFire = true;
|
|
this.ignoreFrustumCheck = true;
|
|
}
|
|
|
|
@Override
|
|
protected void entityInit() {
|
|
this.dataWatcher.addObject(DW_CHARGING, (byte) 0);
|
|
}
|
|
|
|
// ==================== CHARGING ====================
|
|
|
|
/**
|
|
* Check if entity is in charging state (synced via data watcher).
|
|
* In preview mode, uses local field since data watcher isn't synced.
|
|
*/
|
|
public boolean isCharging() {
|
|
if (previewMode) return this.charging;
|
|
return this.dataWatcher.getWatchableObjectByte(DW_CHARGING) == 1;
|
|
}
|
|
|
|
/**
|
|
* Set charging state (server only, synced to clients via data watcher).
|
|
*/
|
|
public void setCharging(boolean value) {
|
|
this.charging = value;
|
|
if (!worldObj.isRemote) {
|
|
this.dataWatcher.updateObject(DW_CHARGING, (byte) (value ? 1 : 0));
|
|
}
|
|
}
|
|
|
|
public void setChargeDuration(int duration) {
|
|
this.chargeDuration = duration;
|
|
}
|
|
|
|
public float getChargeProgress() {
|
|
if (chargeDuration <= 0) return 1.0f;
|
|
return Math.min(1.0f, (float) chargeTick / chargeDuration);
|
|
}
|
|
|
|
public float getInterpolatedChargeProgress(float partialTicks) {
|
|
if (chargeDuration <= 0) return 1.0f;
|
|
float prevProgress = Math.max(0, (float) (chargeTick - 1) / chargeDuration);
|
|
float currProgress = Math.min(1.0f, (float) chargeTick / chargeDuration);
|
|
return prevProgress + (currProgress - prevProgress) * partialTicks;
|
|
}
|
|
|
|
// ==================== OWNER ====================
|
|
|
|
/**
|
|
* Get the owner entity. In preview mode, returns the preview owner directly.
|
|
*/
|
|
public Entity getOwnerEntity() {
|
|
if (previewMode && previewOwner != null) {
|
|
return previewOwner;
|
|
}
|
|
if (ownerEntityId == -1) return null;
|
|
return worldObj.getEntityByID(ownerEntityId);
|
|
}
|
|
|
|
public int getOwnerEntityId() {
|
|
return ownerEntityId;
|
|
}
|
|
|
|
public void setOwnerEntityId(int id) {
|
|
this.ownerEntityId = id;
|
|
}
|
|
|
|
// ==================== SOURCE ABILITY ====================
|
|
|
|
public Ability getSourceAbility() {
|
|
return sourceAbility;
|
|
}
|
|
|
|
public void setSourceAbility(Ability ability) {
|
|
this.sourceAbility = ability;
|
|
}
|
|
|
|
// ==================== PREVIEW MODE ====================
|
|
|
|
public boolean isPreviewMode() {
|
|
return previewMode;
|
|
}
|
|
|
|
public void setPreviewMode(boolean preview) {
|
|
this.previewMode = preview;
|
|
}
|
|
|
|
public void setPreviewOwner(EntityLivingBase owner) {
|
|
this.previewOwner = owner;
|
|
}
|
|
|
|
// ==================== DISPLAY GETTERS & SETTERS ====================
|
|
|
|
public EnergyDisplayData getDisplayData() {
|
|
return displayData;
|
|
}
|
|
|
|
public EnergyLightningData getLightningData() {
|
|
return lightningData;
|
|
}
|
|
|
|
public int getInnerColor() {
|
|
return displayData.getInnerColor();
|
|
}
|
|
|
|
public void setInnerColor(int color) {
|
|
displayData.setInnerColor(color);
|
|
}
|
|
|
|
public float getInnerAlpha() {
|
|
return displayData.getInnerAlpha();
|
|
}
|
|
|
|
public void setInnerAlpha(float alpha) {
|
|
displayData.setInnerAlpha(alpha);
|
|
}
|
|
|
|
public int getOuterColor() {
|
|
return displayData.getOuterColor();
|
|
}
|
|
|
|
public void setOuterColor(int color) {
|
|
displayData.setOuterColor(color);
|
|
}
|
|
|
|
public boolean isOuterColorEnabled() {
|
|
return displayData.isOuterColorEnabled();
|
|
}
|
|
|
|
public void setOuterColorEnabled(boolean enabled) {
|
|
displayData.setOuterColorEnabled(enabled);
|
|
}
|
|
|
|
public float getOuterColorWidth() {
|
|
return displayData.getOuterColorWidth();
|
|
}
|
|
|
|
public void setOuterColorWidth(float width) {
|
|
displayData.setOuterColorWidth(width);
|
|
}
|
|
|
|
public float getOuterColorAlpha() {
|
|
return displayData.getOuterColorAlpha();
|
|
}
|
|
|
|
public void setOuterColorAlpha(float alpha) {
|
|
displayData.setOuterColorAlpha(alpha);
|
|
}
|
|
|
|
public float getRotationSpeed() {
|
|
return displayData.getRotationSpeed();
|
|
}
|
|
|
|
public void setRotationSpeed(float speed) {
|
|
displayData.setRotationSpeed(speed);
|
|
}
|
|
|
|
// ==================== LIGHTNING GETTERS & SETTERS ====================
|
|
|
|
public boolean hasLightningEffect() {
|
|
return lightningData.isLightningEffect();
|
|
}
|
|
|
|
public void setLightningEffect(boolean enabled) {
|
|
lightningData.setLightningEffect(enabled);
|
|
}
|
|
|
|
public float getLightningDensity() {
|
|
return lightningData.getLightningDensity();
|
|
}
|
|
|
|
public void setLightningDensity(float density) {
|
|
lightningData.setLightningDensity(density);
|
|
}
|
|
|
|
public float getLightningRadius() {
|
|
return lightningData.getLightningRadius();
|
|
}
|
|
|
|
public void setLightningRadius(float radius) {
|
|
lightningData.setLightningRadius(radius);
|
|
}
|
|
|
|
public int getLightningFadeTime() {
|
|
return lightningData.getLightningFadeTime();
|
|
}
|
|
|
|
public void setLightningFadeTime(int ticks) {
|
|
lightningData.setLightningFadeTime(ticks);
|
|
}
|
|
|
|
// ==================== BRIGHTNESS ====================
|
|
|
|
@Override
|
|
public float getBrightness(float partialTicks) {
|
|
return 1.0f;
|
|
}
|
|
|
|
@Override
|
|
@SideOnly(Side.CLIENT)
|
|
public int getBrightnessForRender(float partialTicks) {
|
|
return 0xF000F0;
|
|
}
|
|
|
|
@Override
|
|
public boolean shouldRenderInPass(int pass) {
|
|
return pass == 1; // Translucent pass
|
|
}
|
|
|
|
// ==================== COLLISION SETTINGS ====================
|
|
|
|
@Override
|
|
public boolean canBePushed() {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
protected boolean canTriggerWalking() {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean isBurning() {
|
|
return false;
|
|
}
|
|
|
|
@SideOnly(Side.CLIENT)
|
|
public float getShadowSize() {
|
|
return 0.0f;
|
|
}
|
|
|
|
// ==================== SAFETY HELPERS ====================
|
|
|
|
/**
|
|
* Sanitize a float value loaded from NBT or external sources.
|
|
* Guards against NaN, Infinity, negative values, and values exceeding a max.
|
|
*/
|
|
protected static float sanitize(float value, float fallback, float max) {
|
|
if (Float.isNaN(value) || Float.isInfinite(value) || value < 0) return fallback;
|
|
return Math.min(value, max);
|
|
}
|
|
|
|
// ==================== NBT HELPERS ====================
|
|
|
|
/**
|
|
* Write shared energy base fields to NBT (owner, display, lightning).
|
|
* Call from subclass NBT writers before writing type-specific fields.
|
|
*/
|
|
protected void writeEnergyBaseNBT(NBTTagCompound nbt) {
|
|
nbt.setInteger("OwnerId", ownerEntityId);
|
|
displayData.writeNBT(nbt);
|
|
lightningData.writeNBT(nbt);
|
|
}
|
|
|
|
/**
|
|
* Read shared energy base fields from NBT (owner, display, lightning).
|
|
* Call from subclass NBT readers before reading type-specific fields.
|
|
*/
|
|
protected void readEnergyBaseNBT(NBTTagCompound nbt) {
|
|
this.ownerEntityId = nbt.getInteger("OwnerId");
|
|
displayData.readNBT(nbt);
|
|
lightningData.readNBT(nbt);
|
|
}
|
|
|
|
// ==================== SPAWN DATA ====================
|
|
|
|
/**
|
|
* Write entity data for client sync. Subclasses override this
|
|
* to serialize their fields. Separate from writeEntityToNBT
|
|
* which is intentionally empty (entities are transient, not saved to world).
|
|
*/
|
|
protected void writeSpawnNBT(NBTTagCompound nbt) {
|
|
writeEnergyBaseNBT(nbt);
|
|
}
|
|
|
|
/**
|
|
* Read entity data from client sync. Subclasses override this
|
|
* to deserialize their fields. Separate from readEntityFromNBT
|
|
* which is intentionally empty (entities are transient, not saved to world).
|
|
*/
|
|
protected void readSpawnNBT(NBTTagCompound nbt) {
|
|
readEnergyBaseNBT(nbt);
|
|
}
|
|
|
|
/**
|
|
* Export spawn NBT for non-world preview rendering sync.
|
|
*/
|
|
public final NBTTagCompound exportSpawnNBT() {
|
|
NBTTagCompound nbt = new NBTTagCompound();
|
|
writeSpawnNBT(nbt);
|
|
return nbt;
|
|
}
|
|
|
|
/**
|
|
* Import spawn NBT for non-world preview rendering sync.
|
|
*/
|
|
public final void importSpawnNBT(NBTTagCompound nbt) {
|
|
if (nbt != null) {
|
|
readSpawnNBT(nbt);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void writeSpawnData(ByteBuf buffer) {
|
|
try {
|
|
NBTTagCompound compound = new NBTTagCompound();
|
|
this.writeSpawnNBT(compound);
|
|
ByteBufUtils.writeNBT(buffer, compound);
|
|
} catch (Exception e) {
|
|
noppes.npcs.LogWriter.error("Error writing energy ability spawn data", e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void readSpawnData(ByteBuf buffer) {
|
|
try {
|
|
NBTTagCompound compound = ByteBufUtils.readNBT(buffer);
|
|
if (compound != null) {
|
|
this.readSpawnNBT(compound);
|
|
}
|
|
} catch (Exception e) {
|
|
noppes.npcs.LogWriter.error("Error reading energy ability spawn data", e);
|
|
}
|
|
}
|
|
}
|