diff --git a/src/main/java/kamkeel/npcs/controllers/EnergyController.java b/src/main/java/kamkeel/npcs/controllers/EnergyController.java new file mode 100644 index 000000000..3c3ec98c9 --- /dev/null +++ b/src/main/java/kamkeel/npcs/controllers/EnergyController.java @@ -0,0 +1,197 @@ +package kamkeel.npcs.controllers; + +import kamkeel.npcs.controllers.data.energy.IEnergyHandler; +import kamkeel.npcs.entity.*; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.world.World; +import noppes.npcs.api.IEnergyController; +import noppes.npcs.api.IWorld; +import noppes.npcs.api.entity.*; +import noppes.npcs.scripted.NpcAPI; + +import java.util.ArrayList; +import java.util.List; + +/** + * Central controller for script-created energy entities and handler registration. + * Provides factory methods for creating all energy entity types and routes + * damage through registered handlers when entities have customDamageData + * but no sourceAbility. + */ +public class EnergyController implements IEnergyController { + + public static EnergyController Instance = new EnergyController(); + + private final List handlers = new ArrayList<>(); + + // ═══════════════════════════════════════════════════════════════════ + // HANDLER REGISTRATION + // ═══════════════════════════════════════════════════════════════════ + + public void registerHandler(IEnergyHandler handler) { + handlers.add(handler); + } + + public List getHandlers() { + return handlers; + } + + // ═══════════════════════════════════════════════════════════════════ + // DAMAGE ROUTING + // ═══════════════════════════════════════════════════════════════════ + + /** + * Fire onEnergyDamage on all handlers. Chain of responsibility — first true wins. + */ + public boolean fireOnEnergyDamage(Entity energyEntity, EntityLivingBase owner, + EntityLivingBase target, float damage, + float knockback, float knockbackUp, + double kbDirX, double kbDirZ, + float damageMultiplier, + NBTTagCompound damageData) { + for (IEnergyHandler handler : handlers) { + if (handler.onEnergyDamage(energyEntity, owner, target, damage, + knockback, knockbackUp, kbDirX, kbDirZ, damageMultiplier, damageData)) { + return true; + } + } + return false; + } + + /** + * Fire modifyEnergyDamage on all handlers. Cumulative — each handler's output feeds the next. + */ + public float fireModifyEnergyDamage(Entity energyEntity, EntityLivingBase owner, + float baseDamage, NBTTagCompound damageData) { + float damage = baseDamage; + for (IEnergyHandler handler : handlers) { + damage = handler.modifyEnergyDamage(energyEntity, owner, damage, damageData); + } + return damage; + } + + // ═══════════════════════════════════════════════════════════════════ + // INTERNAL FACTORY METHODS (MC types) + // ═══════════════════════════════════════════════════════════════════ + + public EntityAbilityOrb createOrbInternal(World world, Entity owner, double x, double y, double z, float size) { + EntityAbilityOrb entity = new EntityAbilityOrb(world); + entity.setPosition(x, y, z); + entity.setStartPosition(x, y, z); + entity.setOwnerEntityId(owner.getEntityId()); + entity.setProjectileSize(size); + return entity; + } + + public EntityAbilityBeam createBeamInternal(World world, Entity owner, double x, double y, double z, + float beamWidth, float headSize) { + EntityAbilityBeam entity = new EntityAbilityBeam(world); + entity.setPosition(x, y, z); + entity.setStartPosition(x, y, z); + entity.setOwnerEntityId(owner.getEntityId()); + entity.setBeamWidth(beamWidth); + entity.setHeadSize(headSize); + return entity; + } + + public EntityAbilityDisc createDiscInternal(World world, Entity owner, double x, double y, double z, + float radius, float thickness) { + EntityAbilityDisc entity = new EntityAbilityDisc(world); + entity.setPosition(x, y, z); + entity.setStartPosition(x, y, z); + entity.setOwnerEntityId(owner.getEntityId()); + entity.setDiscRadius(radius); + entity.setDiscThickness(thickness); + return entity; + } + + public EntityAbilityLaser createLaserInternal(World world, Entity owner, double x, double y, double z, + float laserWidth) { + EntityAbilityLaser entity = new EntityAbilityLaser(world); + entity.setPosition(x, y, z); + entity.setStartPosition(x, y, z); + entity.setOwnerEntityId(owner.getEntityId()); + entity.setLaserWidth(laserWidth); + return entity; + } + + public EntityAbilityZone createHazardInternal(World world, Entity owner, double x, double y, double z) { + EntityAbilityZone entity = new EntityAbilityZone(world); + entity.initAsHazard(owner, x, y, z); + return entity; + } + + public EntityAbilityZone createTrapInternal(World world, Entity owner, double x, double y, double z) { + EntityAbilityZone entity = new EntityAbilityZone(world); + entity.initAsTrap(owner, x, y, z); + return entity; + } + + public EntityEnergySweeper createSweeperInternal(World world, Entity owner, double x, double y, double z) { + EntityEnergySweeper entity = new EntityEnergySweeper(world); + entity.setPosition(x, y, z); + entity.setOwnerEntityId(owner.getEntityId()); + return entity; + } + + public EntityEnergyPanel createPanelInternal(World world, Entity owner, double x, double y, double z) { + EntityEnergyPanel entity = new EntityEnergyPanel(world); + entity.setPosition(x, y, z); + entity.setOwnerEntityId(owner.getEntityId()); + return entity; + } + + // ═══════════════════════════════════════════════════════════════════ + // IEnergyController (Script API methods) + // ═══════════════════════════════════════════════════════════════════ + + @Override + public IEnergyOrb createOrb(IWorld world, IEntity owner, double x, double y, double z, float size) { + EntityAbilityOrb entity = createOrbInternal((World) world.getMCWorld(), owner.getMCEntity(), x, y, z, size); + return (IEnergyOrb) NpcAPI.Instance().getIEntity(entity); + } + + @Override + public IEnergyBeam createBeam(IWorld world, IEntity owner, double x, double y, double z, float beamWidth, float headSize) { + EntityAbilityBeam entity = createBeamInternal((World) world.getMCWorld(), owner.getMCEntity(), x, y, z, beamWidth, headSize); + return (IEnergyBeam) NpcAPI.Instance().getIEntity(entity); + } + + @Override + public IEnergyDisc createDisc(IWorld world, IEntity owner, double x, double y, double z, float radius, float thickness) { + EntityAbilityDisc entity = createDiscInternal((World) world.getMCWorld(), owner.getMCEntity(), x, y, z, radius, thickness); + return (IEnergyDisc) NpcAPI.Instance().getIEntity(entity); + } + + @Override + public IEnergyLaser createLaser(IWorld world, IEntity owner, double x, double y, double z, float laserWidth) { + EntityAbilityLaser entity = createLaserInternal((World) world.getMCWorld(), owner.getMCEntity(), x, y, z, laserWidth); + return (IEnergyLaser) NpcAPI.Instance().getIEntity(entity); + } + + @Override + public IEnergyZone createHazard(IWorld world, IEntity owner, double x, double y, double z) { + EntityAbilityZone entity = createHazardInternal((World) world.getMCWorld(), owner.getMCEntity(), x, y, z); + return (IEnergyZone) NpcAPI.Instance().getIEntity(entity); + } + + @Override + public IEnergyZone createTrap(IWorld world, IEntity owner, double x, double y, double z) { + EntityAbilityZone entity = createTrapInternal((World) world.getMCWorld(), owner.getMCEntity(), x, y, z); + return (IEnergyZone) NpcAPI.Instance().getIEntity(entity); + } + + @Override + public IEnergySweeper createSweeper(IWorld world, IEntity owner, double x, double y, double z) { + EntityEnergySweeper entity = createSweeperInternal((World) world.getMCWorld(), owner.getMCEntity(), x, y, z); + return (IEnergySweeper) NpcAPI.Instance().getIEntity(entity); + } + + @Override + public IEnergyPanel createPanel(IWorld world, IEntity owner, double x, double y, double z) { + EntityEnergyPanel entity = createPanelInternal((World) world.getMCWorld(), owner.getMCEntity(), x, y, z); + return (IEnergyPanel) NpcAPI.Instance().getIEntity(entity); + } +} diff --git a/src/main/java/kamkeel/npcs/controllers/data/energy/IEnergyHandler.java b/src/main/java/kamkeel/npcs/controllers/data/energy/IEnergyHandler.java new file mode 100644 index 000000000..bd2e39a74 --- /dev/null +++ b/src/main/java/kamkeel/npcs/controllers/data/energy/IEnergyHandler.java @@ -0,0 +1,49 @@ +package kamkeel.npcs.controllers.data.energy; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.nbt.NBTTagCompound; + +/** + * Extension point for handling energy entity damage without a sourceAbility. + * Registered with EnergyController. DBC Addon implements this to route + * script-created energy entity damage through the DBC damage pipeline. + */ +public interface IEnergyHandler { + + /** + * Handle damage for an energy entity with custom damage data. + * Chain of Responsibility — first handler returning true claims the damage. + * + * @param energyEntity The energy entity dealing damage + * @param owner The entity that owns/created the energy entity + * @param target The entity being damaged + * @param damage Base damage amount + * @param knockback Horizontal knockback strength + * @param knockbackUp Vertical knockback strength + * @param kbDirX Knockback direction X + * @param kbDirZ Knockback direction Z + * @param damageMultiplier Damage multiplier (e.g. falloff) + * @param damageData Custom damage configuration NBT (e.g. DBC stats) + * @return true if this handler processed the damage, false to pass to next handler + */ + boolean onEnergyDamage(Entity energyEntity, EntityLivingBase owner, + EntityLivingBase target, float damage, + float knockback, float knockbackUp, + double kbDirX, double kbDirZ, + float damageMultiplier, + NBTTagCompound damageData); + + /** + * Modify outgoing damage for an energy entity with custom damage data. + * Cumulative — each handler receives the previous handler's output. + * + * @param energyEntity The energy entity dealing damage + * @param owner The entity that owns/created the energy entity + * @param baseDamage Current damage value (after previous handlers) + * @param damageData Custom damage configuration NBT + * @return Modified damage value + */ + float modifyEnergyDamage(Entity energyEntity, EntityLivingBase owner, + float baseDamage, NBTTagCompound damageData); +} diff --git a/src/main/java/noppes/npcs/scripted/entity/ScriptEnergyZone.java b/src/main/java/noppes/npcs/scripted/entity/ScriptEnergyZone.java new file mode 100644 index 000000000..9368a2475 --- /dev/null +++ b/src/main/java/noppes/npcs/scripted/entity/ScriptEnergyZone.java @@ -0,0 +1,277 @@ +package noppes.npcs.scripted.entity; + +import kamkeel.npcs.entity.EntityAbilityZone; +import net.minecraft.nbt.NBTTagCompound; +import noppes.npcs.api.INbt; +import noppes.npcs.api.entity.IEnergyZone; +import noppes.npcs.scripted.NpcAPI; + +/** + * Script wrapper for EntityAbilityZone (Hazard and Trap zones). + */ +public class ScriptEnergyZone extends ScriptEntity implements IEnergyZone { + + public ScriptEnergyZone(T entity) { + super(entity); + } + + // ==================== ZONE TYPE ==================== + + @Override + public int getZoneType() { + return entity.getZoneType().ordinal(); + } + + // ==================== COMMON ZONE PROPERTIES ==================== + + @Override + public int getZoneShape() { + return entity.getShape().ordinal(); + } + + @Override + public void setZoneShape(int shape) { + EntityAbilityZone.ZoneShape[] values = EntityAbilityZone.ZoneShape.values(); + if (shape >= 0 && shape < values.length) { + entity.setShape(values[shape]); + } + } + + @Override + public float getRadius() { + return entity.getRadius(); + } + + @Override + public void setRadius(float radius) { + entity.setRadius(radius); + } + + @Override + public float getZoneHeight() { + return entity.getZoneHeight(); + } + + @Override + public void setZoneHeight(float height) { + entity.setZoneHeight(height); + } + + @Override + public int getDuration() { + return entity.getDurationTicks(); + } + + @Override + public void setDuration(int ticks) { + entity.setDurationTicks(ticks); + } + + // ==================== COLORS ==================== + + @Override + public int getInnerColor() { + return entity.getInnerColor(); + } + + @Override + public void setInnerColor(int color) { + entity.setInnerColor(color); + } + + @Override + public int getOuterColor() { + return entity.getOuterColor(); + } + + @Override + public void setOuterColor(int color) { + entity.setOuterColor(color); + } + + @Override + public boolean isOuterColorEnabled() { + return entity.isOuterColorEnabled(); + } + + @Override + public void setOuterColorEnabled(boolean enabled) { + entity.setOuterColorEnabled(enabled); + } + + // ==================== VISUALS ==================== + + @Override + public float getParticleDensity() { + return entity.getParticleDensity(); + } + + @Override + public void setParticleDensity(float density) { + entity.setParticleDensity(density); + } + + @Override + public float getParticleScale() { + return entity.getParticleScale(); + } + + @Override + public void setParticleScale(float scale) { + entity.setParticleScale(scale); + } + + @Override + public float getAnimSpeed() { + return entity.getAnimSpeed(); + } + + @Override + public void setAnimSpeed(float speed) { + entity.setAnimSpeed(speed); + } + + @Override + public boolean isIgnoreIFrames() { + return entity.isIgnoreIFrames(); + } + + @Override + public void setIgnoreIFrames(boolean ignore) { + entity.setIgnoreIFrames(ignore); + } + + // ==================== HAZARD-SPECIFIC ==================== + + @Override + public float getDamagePerSecond() { + return entity.getDamagePerSecond(); + } + + @Override + public void setDamagePerSecond(float dps) { + entity.setDamagePerSecond(dps); + } + + @Override + public int getDamageInterval() { + return entity.getDamageInterval(); + } + + @Override + public void setDamageInterval(int ticks) { + entity.setDamageInterval(ticks); + } + + @Override + public boolean isAffectsCaster() { + return entity.isAffectsCaster(); + } + + @Override + public void setAffectsCaster(boolean affects) { + entity.setAffectsCaster(affects); + } + + // ==================== TRAP-SPECIFIC ==================== + + @Override + public float getTriggerRadius() { + return entity.getTriggerRadius(); + } + + @Override + public void setTriggerRadius(float radius) { + entity.setTriggerRadius(radius); + } + + @Override + public int getArmTime() { + return entity.getArmTime(); + } + + @Override + public void setArmTime(int ticks) { + entity.setArmTime(ticks); + } + + @Override + public int getMaxTriggers() { + return entity.getMaxTriggers(); + } + + @Override + public void setMaxTriggers(int max) { + entity.setMaxTriggers(max); + } + + @Override + public int getTriggerCooldown() { + return entity.getTriggerCooldown(); + } + + @Override + public void setTriggerCooldown(int ticks) { + entity.setTriggerCooldown(ticks); + } + + @Override + public float getDamage() { + return entity.getDamage(); + } + + @Override + public void setDamage(float damage) { + entity.setDamage(damage); + } + + @Override + public float getKnockback() { + return entity.getKnockback(); + } + + @Override + public void setKnockback(float knockback) { + entity.setKnockback(knockback); + } + + @Override + public boolean isVisible() { + return entity.isVisible(); + } + + @Override + public void setVisible(boolean visible) { + entity.setVisible(visible); + } + + // ==================== CUSTOM DAMAGE DATA ==================== + + @Override + public INbt getDamageData() { + NBTTagCompound data = entity.getCustomDamageData(); + if (data == null) { + data = new NBTTagCompound(); + entity.setCustomDamageData(data); + } + return NpcAPI.Instance().getINbt(data); + } + + @Override + public void setDamageData(INbt data) { + if (data == null) { + entity.setCustomDamageData(null); + } else { + entity.setCustomDamageData((NBTTagCompound) data.getMCNBT()); + } + } + + // ==================== SPAWNING ==================== + + @Override + public void spawn() { + if (!entity.addedToChunk && entity.worldObj != null) { + entity.worldObj.spawnEntityInWorld(entity); + } + } +}