diff --git a/src/main/java/kamkeel/npcs/entity/EntityAbilityBeam.java b/src/main/java/kamkeel/npcs/entity/EntityAbilityBeam.java index bfdca5346..db57660e4 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityAbilityBeam.java +++ b/src/main/java/kamkeel/npcs/entity/EntityAbilityBeam.java @@ -3,6 +3,7 @@ package kamkeel.npcs.entity; import kamkeel.npcs.controllers.data.ability.data.*; import kamkeel.npcs.util.AnchorPointHelper; import net.minecraft.entity.Entity; +import noppes.npcs.EventHooks; import net.minecraft.entity.EntityLivingBase; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; @@ -544,6 +545,9 @@ public class EntityAbilityBeam extends EntityAbilityProjectile { MovingObjectPosition blockHit = worldObj.func_147447_a(currentPos, nextPos, false, true, false); if (blockHit != null && blockHit.typeOfHit == MovingObjectPosition.MovingObjectType.BLOCK) { + if (!worldObj.isRemote) { + EventHooks.onEnergyProjectileBlockImpact(this, blockHit.blockX, blockHit.blockY, blockHit.blockZ); + } hasHit = true; if (isExplosive()) { posX = blockHit.hitVec.xCoord; @@ -593,10 +597,22 @@ public class EntityAbilityBeam extends EntityAbilityProjectile { return beamWidth; } + public void setBeamWidth(float beamWidth) { + this.beamWidth = beamWidth; + } + public float getHeadSize() { return headSize; } + public void setHeadSize(float headSize) { + this.headSize = headSize; + } + + public void setAttachedToOwner(boolean attached) { + this.attachedToOwner = attached; + } + /** * Get interpolated head offset for smooth rendering. * Returns offset RELATIVE to origin. diff --git a/src/main/java/kamkeel/npcs/entity/EntityAbilityDisc.java b/src/main/java/kamkeel/npcs/entity/EntityAbilityDisc.java index c0f3cbe8d..dd499f122 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityAbilityDisc.java +++ b/src/main/java/kamkeel/npcs/entity/EntityAbilityDisc.java @@ -3,6 +3,7 @@ package kamkeel.npcs.entity; import kamkeel.npcs.controllers.data.ability.data.*; import kamkeel.npcs.util.AnchorPointHelper; import net.minecraft.entity.Entity; +import noppes.npcs.EventHooks; import net.minecraft.entity.EntityLivingBase; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.AxisAlignedBB; @@ -344,6 +345,9 @@ public class EntityAbilityDisc extends EntityAbilityProjectile { MovingObjectPosition blockHit = worldObj.func_147447_a(currentPos, nextPos, false, true, false); if (blockHit != null && blockHit.typeOfHit == MovingObjectPosition.MovingObjectType.BLOCK) { + if (!worldObj.isRemote) { + EventHooks.onEnergyProjectileBlockImpact(this, blockHit.blockX, blockHit.blockY, blockHit.blockZ); + } hasHit = true; if (isExplosive()) { posX = blockHit.hitVec.xCoord; @@ -413,14 +417,34 @@ public class EntityAbilityDisc extends EntityAbilityProjectile { return discRadius; } + public void setDiscRadius(float radius) { + this.discRadius = radius; + } + public float getDiscThickness() { return discThickness; } + public void setDiscThickness(float thickness) { + this.discThickness = thickness; + } + public boolean isBoomerang() { return boomerang; } + public void setBoomerang(boolean boomerang) { + this.boomerang = boomerang; + } + + public int getBoomerangDelay() { + return boomerangDelay; + } + + public void setBoomerangDelay(int delay) { + this.boomerangDelay = delay; + } + public boolean isReturning() { return returning; } @@ -429,6 +453,10 @@ public class EntityAbilityDisc extends EntityAbilityProjectile { return vertical; } + public void setVertical(boolean vertical) { + this.vertical = vertical; + } + /** * Get the yaw angle (degrees) from motion vector for vertical disc orientation. * During charging, uses owner's facing direction. diff --git a/src/main/java/kamkeel/npcs/entity/EntityAbilityLaser.java b/src/main/java/kamkeel/npcs/entity/EntityAbilityLaser.java index d17024680..5adabc4b6 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityAbilityLaser.java +++ b/src/main/java/kamkeel/npcs/entity/EntityAbilityLaser.java @@ -3,6 +3,7 @@ package kamkeel.npcs.entity; import kamkeel.npcs.controllers.data.ability.data.*; import kamkeel.npcs.util.AnchorPointHelper; import net.minecraft.entity.Entity; +import noppes.npcs.EventHooks; import net.minecraft.entity.EntityLivingBase; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.AxisAlignedBB; @@ -145,6 +146,9 @@ public class EntityAbilityLaser extends EntityAbilityProjectile { endZ = startZ + dirZ * currentLength; if (ticksSinceFullExtension >= lingerTicks) { + if (!worldObj.isRemote) { + EventHooks.onEnergyProjectileExpired(this); + } this.setDead(); } } @@ -236,6 +240,9 @@ public class EntityAbilityLaser extends EntityAbilityProjectile { MovingObjectPosition blockHit = worldObj.func_147447_a(start, end, false, true, false); if (blockHit != null && blockHit.typeOfHit == MovingObjectPosition.MovingObjectType.BLOCK) { + if (!worldObj.isRemote) { + EventHooks.onEnergyProjectileBlockImpact(this, blockHit.blockX, blockHit.blockY, blockHit.blockZ); + } // Laser stops at block currentLength = (float) Math.sqrt( (blockHit.hitVec.xCoord - startX) * (blockHit.hitVec.xCoord - startX) + @@ -355,6 +362,32 @@ public class EntityAbilityLaser extends EntityAbilityProjectile { return laserWidth; } + public void setLaserWidth(float width) { + this.laserWidth = width; + } + + public float getExpansionSpeed() { + return expansionSpeed; + } + + public void setExpansionSpeed(float speed) { + this.expansionSpeed = speed; + } + + public int getLingerTicks() { + return lingerTicks; + } + + public void setLingerTicks(int ticks) { + this.lingerTicks = ticks; + } + + public void setDirection(double x, double y, double z) { + this.dirX = x; + this.dirY = y; + this.dirZ = z; + } + public float getCurrentLength() { return currentLength; } diff --git a/src/main/java/kamkeel/npcs/entity/EntityAbilityOrb.java b/src/main/java/kamkeel/npcs/entity/EntityAbilityOrb.java index c85b7be40..2eb5c0084 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityAbilityOrb.java +++ b/src/main/java/kamkeel/npcs/entity/EntityAbilityOrb.java @@ -3,6 +3,7 @@ package kamkeel.npcs.entity; import kamkeel.npcs.controllers.data.ability.data.*; import kamkeel.npcs.util.AnchorPointHelper; import net.minecraft.entity.Entity; +import noppes.npcs.EventHooks; import net.minecraft.entity.EntityLivingBase; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.AxisAlignedBB; @@ -232,6 +233,9 @@ public class EntityAbilityOrb extends EntityAbilityProjectile { MovingObjectPosition blockHit = worldObj.func_147447_a(currentPos, nextPos, false, true, false); if (blockHit != null && blockHit.typeOfHit == MovingObjectPosition.MovingObjectType.BLOCK) { + if (!worldObj.isRemote) { + EventHooks.onEnergyProjectileBlockImpact(this, blockHit.blockX, blockHit.blockY, blockHit.blockZ); + } hasHit = true; if (isExplosive()) { posX = blockHit.hitVec.xCoord; diff --git a/src/main/java/kamkeel/npcs/entity/EntityAbilityProjectile.java b/src/main/java/kamkeel/npcs/entity/EntityAbilityProjectile.java index ac6add74d..99c66edaa 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityAbilityProjectile.java +++ b/src/main/java/kamkeel/npcs/entity/EntityAbilityProjectile.java @@ -20,13 +20,17 @@ import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.DamageSource; import net.minecraft.util.Vec3; import net.minecraft.world.World; +import noppes.npcs.EventHooks; import noppes.npcs.NpcDamageSource; -import noppes.npcs.api.entity.IEnergyProjectile; import noppes.npcs.api.entity.IEntity; import noppes.npcs.entity.EntityNPCInterface; import noppes.npcs.scripted.NpcAPI; +import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; @@ -36,7 +40,38 @@ import java.util.Map; * * Design inspired by LouisXIV's energy attack system. */ -public abstract class EntityAbilityProjectile extends Entity implements IEnergyProjectile, IEntityAdditionalSpawnData { +public abstract class EntityAbilityProjectile extends Entity implements IEntityAdditionalSpawnData { + + // ==================== ACTIVE PROJECTILE TRACKING ==================== + private static final Map>> activeProjectiles = new HashMap<>(); + + public static void trackProjectile(EntityAbilityProjectile projectile) { + int ownerId = projectile.getOwnerEntityId(); + if (ownerId < 0) return; + List> refs = activeProjectiles.get(ownerId); + if (refs == null) { + refs = new ArrayList<>(); + activeProjectiles.put(ownerId, refs); + } + refs.add(new WeakReference<>(projectile)); + } + + public static List getActiveProjectiles(int ownerEntityId) { + List> refs = activeProjectiles.get(ownerEntityId); + if (refs == null) return Collections.emptyList(); + List result = new ArrayList<>(); + Iterator> it = refs.iterator(); + while (it.hasNext()) { + EntityAbilityProjectile p = it.next().get(); + if (p == null || p.isDead) { + it.remove(); + } else { + result.add(p); + } + } + if (refs.isEmpty()) activeProjectiles.remove(ownerEntityId); + return result; + } // ==================== VISUAL PROPERTIES ==================== protected float size = 1.0f; @@ -174,6 +209,11 @@ public abstract class EntityAbilityProjectile extends Entity implements IEnergyP // Skip super.onUpdate() in preview mode to avoid world checks if (!previewMode) { super.onUpdate(); + + // Track on first server tick + if (ticksExisted == 1 && !worldObj.isRemote) { + trackProjectile(this); + } } // Update rotation @@ -208,12 +248,18 @@ public abstract class EntityAbilityProjectile extends Entity implements IEnergyP // Check lifespan using world time (survives chunk unload/reload) if (deathWorldTime > 0 && worldObj.getTotalWorldTime() >= deathWorldTime) { + if (!worldObj.isRemote) { + EventHooks.onEnergyProjectileExpired(this); + } this.setDead(); return; } // Check max distance (subclass can override if needed) if (checkMaxDistance()) { + if (!worldObj.isRemote) { + EventHooks.onEnergyProjectileExpired(this); + } this.setDead(); return; } @@ -226,6 +272,11 @@ public abstract class EntityAbilityProjectile extends Entity implements IEnergyP // Subclass-specific update updateProjectile(); + + // Fire tick event (server-side, non-preview only) + if (!previewMode && !worldObj.isRemote) { + EventHooks.onEnergyProjectileTick(this); + } } /** @@ -328,6 +379,14 @@ public abstract class EntityAbilityProjectile extends Entity implements IEnergyP protected void applyDamage(EntityLivingBase target, float dmg, float kb) { if (previewMode) return; // Skip damage in preview mode + + // Fire entity impact event (may cancel or modify damage) + if (!worldObj.isRemote) { + float result = EventHooks.onEnergyProjectileEntityImpact(this, target, dmg); + if (result < 0) return; // Event was cancelled + dmg = result; + } + Entity owner = getOwnerEntity(); // Check for ability extenders (e.g., DBC Addon damage routing) @@ -627,12 +686,14 @@ public abstract class EntityAbilityProjectile extends Entity implements IEnergyP return worldObj.getEntityByID(ownerEntityId); } - @Override public int getOwnerEntityId() { return ownerEntityId; } - @Override + public void setOwnerEntityId(int id) { + this.ownerEntityId = id; + } + public IEntity getOwner() { if (previewMode) return null; if (getOwnerEntity() == null) return null; @@ -647,17 +708,19 @@ public abstract class EntityAbilityProjectile extends Entity implements IEnergyP this.previewOwner = owner; } - @Override public int getTargetEntityId() { return targetEntityId; } + public void setTargetEntityId(int id) { + this.targetEntityId = id; + } + public Entity getTargetEntity() { if (targetEntityId == -1) return null; return worldObj.getEntityByID(targetEntityId); } - @Override public IEntity getTarget() { if (getTargetEntity() == null) return null; return NpcAPI.Instance().getIEntity(getTargetEntity()); @@ -686,162 +749,135 @@ public abstract class EntityAbilityProjectile extends Entity implements IEnergyP return false; } - // ==================== VISUAL GETTERS ==================== + // ==================== VISUAL GETTERS & SETTERS ==================== - @Override - public float getSize() { - return size; + public float getSize() { return size; } + public void setProjectileSize(float size) { + this.size = size; + this.renderCurrentSize = size; + this.prevRenderSize = size; } - @Override - public int getInnerColor() { - return displayData.getInnerColor(); + /** + * Set the start position for distance calculations. + * Used by factory methods when creating projectiles without the full constructor. + */ + public void setStartPosition(double x, double y, double z) { + this.startX = x; + this.startY = y; + this.startZ = z; } - @Override - public int getOuterColor() { - return displayData.getOuterColor(); - } + public int getInnerColor() { return displayData.getInnerColor(); } + public void setInnerColor(int color) { displayData.setInnerColor(color); } - @Override - public boolean isOuterColorEnabled() { - return displayData.isOuterColorEnabled(); - } + public int getOuterColor() { return displayData.getOuterColor(); } + public void setOuterColor(int color) { displayData.setOuterColor(color); } - @Override - public float getOuterColorWidth() { - return displayData.getOuterColorWidth(); - } + public boolean isOuterColorEnabled() { return displayData.isOuterColorEnabled(); } + public void setOuterColorEnabled(boolean enabled) { displayData.setOuterColorEnabled(enabled); } - @Override - public float getOuterColorAlpha() { - return displayData.getOuterColorAlpha(); - } + public float getOuterColorWidth() { return displayData.getOuterColorWidth(); } + public void setOuterColorWidth(float width) { displayData.setOuterColorWidth(width); } - @Override - public float getRotationSpeed() { - return displayData.getRotationSpeed(); - } + 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); } // ==================== ROTATION GETTERS ==================== - @Override public float getInterpolatedRotationX(float partialTicks) { return this.prevRotationValX + (this.rotationValX - this.prevRotationValX) * partialTicks; } - @Override public float getInterpolatedRotationY(float partialTicks) { return this.prevRotationValY + (this.rotationValY - this.prevRotationValY) * partialTicks; } - @Override public float getInterpolatedRotationZ(float partialTicks) { return this.prevRotationValZ + (this.rotationValZ - this.prevRotationValZ) * partialTicks; } - @Override public float getInterpolatedSize(float partialTicks) { return this.prevRenderSize + (this.renderCurrentSize - this.prevRenderSize) * partialTicks; } - // ==================== LIGHTNING GETTERS ==================== + // ==================== LIGHTNING GETTERS & SETTERS ==================== - @Override - public boolean hasLightningEffect() { - return lightningData.isLightningEffect(); - } + public boolean hasLightningEffect() { return lightningData.isLightningEffect(); } + public void setLightningEffect(boolean enabled) { lightningData.setLightningEffect(enabled); } - @Override - public float getLightningDensity() { - return lightningData.getLightningDensity(); - } + public float getLightningDensity() { return lightningData.getLightningDensity(); } + public void setLightningDensity(float density) { lightningData.setLightningDensity(density); } - @Override - public float getLightningRadius() { - return lightningData.getLightningRadius(); - } + public float getLightningRadius() { return lightningData.getLightningRadius(); } + public void setLightningRadius(float radius) { lightningData.setLightningRadius(radius); } - @Override - public int getLightningFadeTime() { - return lightningData.getLightningFadeTime(); - } + public int getLightningFadeTime() { return lightningData.getLightningFadeTime(); } + public void setLightningFadeTime(int ticks) { lightningData.setLightningFadeTime(ticks); } - // ==================== LIFESPAN GETTERS ==================== + // ==================== LIFESPAN GETTERS & SETTERS ==================== - @Override public float getMaxDistance() { return lifespanData.getMaxDistance(); } + public void setMaxDistance(float distance) { lifespanData.setMaxDistance(distance); } - @Override public int getMaxLifetime() { return lifespanData.getMaxLifetime(); } + public void setMaxLifetime(int ticks) { lifespanData.setMaxLifetime(ticks); } - // ==================== COMBAT GETTERS ==================== + // ==================== COMBAT GETTERS & SETTERS ==================== - @Override public float getDamage() { return combatData.getDamage(); } + public void setCombatDamage(float damage) { combatData.setDamage(damage); } - @Override public float getKnockback() { return combatData.knockback; } + public void setCombatKnockback(float knockback) { combatData.setKnockback(knockback); } - @Override public float getKnockbackUp() { return combatData.knockbackUp; } + public void setCombatKnockbackUp(float knockbackUp) { combatData.setKnockbackUp(knockbackUp); } - @Override public boolean isExplosive() { return combatData.isExplosive(); } + public void setExplosive(boolean explosive) { combatData.setExplosive(explosive); } - @Override public float getExplosionRadius() { return combatData.explosionRadius; } + public void setExplosionRadius(float radius) { combatData.setExplosionRadius(radius); } - @Override public float getExplosionDamageFalloff() { return combatData.explosionDamageFalloff; } + public void setExplosionDamageFalloff(float falloff) { combatData.setExplosionDamageFalloff(falloff); } - // ==================== HOMING GETTERS ==================== + // ==================== HOMING GETTERS & SETTERS ==================== - @Override public boolean isHoming() { return homingData.isHoming(); } + public void setHomingEnabled(boolean homing) { homingData.setHoming(homing); } - @Override public float getHomingStrength() { return homingData.getHomingStrength(); } + public void setHomingStrength(float strength) { homingData.setHomingStrength(strength); } - @Override public float getHomingRange() { return homingData.getHomingRange(); } + public void setHomingRange(float range) { homingData.setHomingRange(range); } - // ==================== TRAJECTORY GETTERS ==================== + // ==================== SPEED & ANCHOR GETTERS & SETTERS ==================== - - - // ==================== ANCHOR GETTERS ==================== - - @Override public float getSpeed() { return homingData.getSpeed(); } + public void setSpeed(float speed) { homingData.setSpeed(speed); } public AnchorPoint getAnchorPoint() { return anchorData.getAnchorPoint(); } - @Override public int getAnchor() { return anchorData.getAnchor(); } - - @Override public float getAnchorOffsetX() { return anchorData.getAnchorOffsetX(); } - - @Override public float getAnchorOffsetY() { return anchorData.getAnchorOffsetY(); } - - @Override public float getAnchorOffsetZ() { return anchorData.getAnchorOffsetZ(); } // ==================== MOVEMENT GETTERS ==================== - @Override public double getStartX() { return startX; } + public double getStartX() { return startX; } + public double getStartY() { return startY; } + public double getStartZ() { return startZ; } - @Override public double getStartY() { return startY; } + // ==================== STATE GETTERS ==================== - @Override public double getStartZ() { return startZ; } - - @Override public double getMotionX() { return motionX; } - - @Override public double getMotionY() { return motionY; } - - @Override public double getMotionZ() { return motionZ; } + public boolean getHasHit() { return hasHit; } // ==================== BRIGHTNESS ==================== diff --git a/src/main/java/noppes/npcs/EventHooks.java b/src/main/java/noppes/npcs/EventHooks.java index 5f9d1388f..75b615671 100644 --- a/src/main/java/noppes/npcs/EventHooks.java +++ b/src/main/java/noppes/npcs/EventHooks.java @@ -1,6 +1,7 @@ package noppes.npcs; import cpw.mods.fml.common.eventhandler.Event; +import kamkeel.npcs.entity.EntityAbilityProjectile; import kamkeel.npcs.network.packets.data.AchievementPacket; import net.minecraft.command.ICommandSender; import net.minecraft.entity.Entity; @@ -16,7 +17,10 @@ import net.minecraft.world.World; import net.minecraft.world.WorldServer; import net.minecraftforge.event.entity.EntityEvent; import net.minecraftforge.event.world.WorldEvent; +import noppes.npcs.api.IBlock; +import noppes.npcs.api.IPos; import noppes.npcs.api.IWorld; +import noppes.npcs.api.entity.IEnergyProjectile; import noppes.npcs.api.entity.IAnimatable; import noppes.npcs.api.entity.ICustomNpc; import noppes.npcs.api.entity.IEntity; @@ -52,6 +56,7 @@ import noppes.npcs.scripted.NpcAPI; import noppes.npcs.scripted.event.AnimationEvent; import noppes.npcs.scripted.event.BlockEvent; import noppes.npcs.scripted.event.CustomNPCsEvent; +import noppes.npcs.scripted.event.EnergyProjectileEvent; import noppes.npcs.scripted.event.ForgeEvent; import noppes.npcs.scripted.event.ItemEvent; import noppes.npcs.scripted.event.LinkedItemEvent; @@ -448,6 +453,110 @@ public class EventHooks { NpcAPI.EVENT_BUS.post(event); } + // ==================== ENERGY PROJECTILE EVENTS ==================== + + public static void onEnergyProjectileFired(EntityAbilityProjectile projectile) { + IEnergyProjectile wrapped = (IEnergyProjectile) NpcAPI.Instance().getIEntity(projectile); + EnergyProjectileEvent.FiredEvent event = new EnergyProjectileEvent.FiredEvent(wrapped); + ScriptController.Instance.globalNpcScripts.callScript(EnumScriptType.ENERGY_PROJECTILE_FIRED, event); + + Entity owner = projectile.worldObj.getEntityByID(projectile.getOwnerEntityId()); + if (owner instanceof EntityNPCInterface) { + ((EntityNPCInterface) owner).script.callScript(EnumScriptType.ENERGY_PROJECTILE_FIRED, event); + } else if (owner instanceof EntityPlayer) { + PlayerDataScript handler = ScriptController.Instance.getPlayerScripts((EntityPlayer) owner); + if (handler != null) { + handler.callScript(EnumScriptType.ENERGY_PROJECTILE_FIRED, event); + } + } + + NpcAPI.EVENT_BUS.post(event); + } + + public static void onEnergyProjectileTick(EntityAbilityProjectile projectile) { + IEnergyProjectile wrapped = (IEnergyProjectile) NpcAPI.Instance().getIEntity(projectile); + EnergyProjectileEvent.UpdateEvent event = new EnergyProjectileEvent.UpdateEvent(wrapped); + ScriptController.Instance.globalNpcScripts.callScript(EnumScriptType.ENERGY_PROJECTILE_TICK, event); + + Entity owner = projectile.worldObj.getEntityByID(projectile.getOwnerEntityId()); + if (owner instanceof EntityNPCInterface) { + ((EntityNPCInterface) owner).script.callScript(EnumScriptType.ENERGY_PROJECTILE_TICK, event); + } else if (owner instanceof EntityPlayer) { + PlayerDataScript handler = ScriptController.Instance.getPlayerScripts((EntityPlayer) owner); + if (handler != null) { + handler.callScript(EnumScriptType.ENERGY_PROJECTILE_TICK, event); + } + } + + NpcAPI.EVENT_BUS.post(event); + } + + /** + * Fires the entity impact event. Returns the (possibly modified) damage, or -1 if cancelled. + */ + public static float onEnergyProjectileEntityImpact(EntityAbilityProjectile projectile, EntityLivingBase target, float damage) { + IEnergyProjectile wrapped = (IEnergyProjectile) NpcAPI.Instance().getIEntity(projectile); + IEntity wrappedTarget = NpcAPI.Instance().getIEntity(target); + EnergyProjectileEvent.EntityImpactEvent event = new EnergyProjectileEvent.EntityImpactEvent(wrapped, wrappedTarget, damage); + ScriptController.Instance.globalNpcScripts.callScript(EnumScriptType.ENERGY_PROJECTILE_ENTITY_IMPACT, event); + + Entity owner = projectile.worldObj.getEntityByID(projectile.getOwnerEntityId()); + if (owner instanceof EntityNPCInterface) { + ((EntityNPCInterface) owner).script.callScript(EnumScriptType.ENERGY_PROJECTILE_ENTITY_IMPACT, event); + } else if (owner instanceof EntityPlayer) { + PlayerDataScript handler = ScriptController.Instance.getPlayerScripts((EntityPlayer) owner); + if (handler != null) { + handler.callScript(EnumScriptType.ENERGY_PROJECTILE_ENTITY_IMPACT, event); + } + } + + NpcAPI.EVENT_BUS.post(event); + + if (event.isCanceled()) { + return -1; + } + return event.getDamage(); + } + + public static void onEnergyProjectileBlockImpact(EntityAbilityProjectile projectile, int blockX, int blockY, int blockZ) { + IEnergyProjectile wrapped = (IEnergyProjectile) NpcAPI.Instance().getIEntity(projectile); + IPos pos = NpcAPI.Instance().getIPos(blockX, blockY, blockZ); + IWorld world = NpcAPI.Instance().getIWorld(projectile.worldObj); + IBlock block = NpcAPI.Instance().getIBlock(world, pos); + EnergyProjectileEvent.BlockImpactEvent event = new EnergyProjectileEvent.BlockImpactEvent(wrapped, block); + ScriptController.Instance.globalNpcScripts.callScript(EnumScriptType.ENERGY_PROJECTILE_BLOCK_IMPACT, event); + + Entity owner = projectile.worldObj.getEntityByID(projectile.getOwnerEntityId()); + if (owner instanceof EntityNPCInterface) { + ((EntityNPCInterface) owner).script.callScript(EnumScriptType.ENERGY_PROJECTILE_BLOCK_IMPACT, event); + } else if (owner instanceof EntityPlayer) { + PlayerDataScript handler = ScriptController.Instance.getPlayerScripts((EntityPlayer) owner); + if (handler != null) { + handler.callScript(EnumScriptType.ENERGY_PROJECTILE_BLOCK_IMPACT, event); + } + } + + NpcAPI.EVENT_BUS.post(event); + } + + public static void onEnergyProjectileExpired(EntityAbilityProjectile projectile) { + IEnergyProjectile wrapped = (IEnergyProjectile) NpcAPI.Instance().getIEntity(projectile); + EnergyProjectileEvent.ExpiredEvent event = new EnergyProjectileEvent.ExpiredEvent(wrapped); + ScriptController.Instance.globalNpcScripts.callScript(EnumScriptType.ENERGY_PROJECTILE_EXPIRED, event); + + Entity owner = projectile.worldObj.getEntityByID(projectile.getOwnerEntityId()); + if (owner instanceof EntityNPCInterface) { + ((EntityNPCInterface) owner).script.callScript(EnumScriptType.ENERGY_PROJECTILE_EXPIRED, event); + } else if (owner instanceof EntityPlayer) { + PlayerDataScript handler = ScriptController.Instance.getPlayerScripts((EntityPlayer) owner); + if (handler != null) { + handler.callScript(EnumScriptType.ENERGY_PROJECTILE_EXPIRED, event); + } + } + + NpcAPI.EVENT_BUS.post(event); + } + public static void onPlayerInit(PlayerDataScript handler, IPlayer player) { PlayerEvent.InitEvent event = new PlayerEvent.InitEvent(player); handler.callScript(EnumScriptType.INIT, event); @@ -693,6 +802,31 @@ public class EventHooks { return NpcAPI.EVENT_BUS.post(event); } + public static boolean onPlayerAbilityStart(PlayerDataScript handler, PlayerAbilityEvent.StartEvent event) { + handler.callScript(EnumScriptType.ABILITY_START, event); + return NpcAPI.EVENT_BUS.post(event); + } + + public static boolean onPlayerAbilityExecute(PlayerDataScript handler, PlayerAbilityEvent.ExecuteEvent event) { + handler.callScript(EnumScriptType.ABILITY_EXECUTE, event); + return NpcAPI.EVENT_BUS.post(event); + } + + public static void onPlayerAbilityTick(PlayerDataScript handler, PlayerAbilityEvent.TickEvent event) { + handler.callScript(EnumScriptType.ABILITY_TICK, event); + NpcAPI.EVENT_BUS.post(event); + } + + public static void onPlayerAbilityInterrupt(PlayerDataScript handler, PlayerAbilityEvent.InterruptEvent event) { + handler.callScript(EnumScriptType.ABILITY_INTERRUPT, event); + NpcAPI.EVENT_BUS.post(event); + } + + public static void onPlayerAbilityComplete(PlayerDataScript handler, PlayerAbilityEvent.CompleteEvent event) { + handler.callScript(EnumScriptType.ABILITY_COMPLETE, event); + NpcAPI.EVENT_BUS.post(event); + } + public static boolean onPlayerAbilityHit(PlayerDataScript handler, PlayerAbilityEvent.HitEvent event) { handler.callScript(EnumScriptType.ABILITY_HIT, event); return NpcAPI.EVENT_BUS.post(event); diff --git a/src/main/java/noppes/npcs/constants/EnumScriptType.java b/src/main/java/noppes/npcs/constants/EnumScriptType.java index 99a8b4091..da4e8f136 100644 --- a/src/main/java/noppes/npcs/constants/EnumScriptType.java +++ b/src/main/java/noppes/npcs/constants/EnumScriptType.java @@ -125,7 +125,13 @@ public enum EnumScriptType { ABILITY_INTERRUPT("abilityInterrupt"), ABILITY_COMPLETE("abilityComplete"), ABILITY_HIT("abilityHit"), - ABILITY_TICK("abilityTick"); + ABILITY_TICK("abilityTick"), + + ENERGY_PROJECTILE_FIRED("energyProjectileFired"), + ENERGY_PROJECTILE_TICK("energyProjectileTick"), + ENERGY_PROJECTILE_ENTITY_IMPACT("energyProjectileEntityImpact"), + ENERGY_PROJECTILE_BLOCK_IMPACT("energyProjectileBlockImpact"), + ENERGY_PROJECTILE_EXPIRED("energyProjectileExpired"); public String function; diff --git a/src/main/java/noppes/npcs/constants/ScriptContext.java b/src/main/java/noppes/npcs/constants/ScriptContext.java index dcc5b5d95..c5861927a 100644 --- a/src/main/java/noppes/npcs/constants/ScriptContext.java +++ b/src/main/java/noppes/npcs/constants/ScriptContext.java @@ -1,14 +1,17 @@ package noppes.npcs.constants; +import noppes.npcs.api.event.IAbilityEvent; import noppes.npcs.api.event.IAnimationEvent; import noppes.npcs.api.event.IBlockEvent; import noppes.npcs.api.event.ICustomGuiEvent; import noppes.npcs.api.event.IDialogEvent; +import noppes.npcs.api.event.IEnergyProjectileEvent; import noppes.npcs.api.event.IFactionEvent; import noppes.npcs.api.event.IForgeEvent; import noppes.npcs.api.event.IItemEvent; import noppes.npcs.api.event.INpcEvent; import noppes.npcs.api.event.IPartyEvent; +import noppes.npcs.api.event.IPlayerAbilityEvent; import noppes.npcs.api.event.IPlayerEvent; import noppes.npcs.api.event.IProjectileEvent; import noppes.npcs.api.event.IQuestEvent; @@ -52,7 +55,10 @@ public class ScriptContext { public static final ScriptContext NPC = register("NPC", "npc", INpcEvent.class, - IProjectileEvent.class + IProjectileEvent.class, + IEnergyProjectileEvent.class, + IAbilityEvent.class, + IAnimationEvent.class ); public static final ScriptContext PLAYER = register("PLAYER", "player", @@ -62,7 +68,9 @@ public class ScriptContext { IDialogEvent.class, IQuestEvent.class, IFactionEvent.class, - ICustomGuiEvent.class + ICustomGuiEvent.class, + IEnergyProjectileEvent.class, + IPlayerAbilityEvent.class ); public static final ScriptContext BLOCK = register("BLOCK", "block", diff --git a/src/main/java/noppes/npcs/controllers/ScriptHookController.java b/src/main/java/noppes/npcs/controllers/ScriptHookController.java index f3bad8e9d..88a12e20d 100644 --- a/src/main/java/noppes/npcs/controllers/ScriptHookController.java +++ b/src/main/java/noppes/npcs/controllers/ScriptHookController.java @@ -2,10 +2,12 @@ package noppes.npcs.controllers; import noppes.npcs.api.event.IAbilityEvent; import noppes.npcs.api.event.IAnimationEvent; +import noppes.npcs.api.event.IPlayerAbilityEvent; import noppes.npcs.api.event.IBlockEvent; import noppes.npcs.api.event.ICustomGuiEvent; import noppes.npcs.api.event.ICustomNPCsEvent; import noppes.npcs.api.event.IDialogEvent; +import noppes.npcs.api.event.IEnergyProjectileEvent; import noppes.npcs.api.event.IFactionEvent; import noppes.npcs.api.event.IForgeEvent; import noppes.npcs.api.event.IItemEvent; @@ -106,6 +108,13 @@ public class ScriptHookController implements IScriptHookHandler { hook(NPC, PROJECTILE_TICK, IProjectileEvent.UpdateEvent.class); hook(NPC, PROJECTILE_IMPACT, IProjectileEvent.ImpactEvent.class); + // Energy Projectile + hook(NPC, ENERGY_PROJECTILE_FIRED, IEnergyProjectileEvent.FiredEvent.class); + hook(NPC, ENERGY_PROJECTILE_TICK, IEnergyProjectileEvent.UpdateEvent.class); + hook(NPC, ENERGY_PROJECTILE_ENTITY_IMPACT, IEnergyProjectileEvent.EntityImpactEvent.class); + hook(NPC, ENERGY_PROJECTILE_BLOCK_IMPACT, IEnergyProjectileEvent.BlockImpactEvent.class); + hook(NPC, ENERGY_PROJECTILE_EXPIRED, IEnergyProjectileEvent.ExpiredEvent.class); + // Ability hook(NPC, ABILITY_START, IAbilityEvent.StartEvent.class); hook(NPC, ABILITY_EXECUTE, IAbilityEvent.ExecuteEvent.class); @@ -113,6 +122,12 @@ public class ScriptHookController implements IScriptHookHandler { hook(NPC, ABILITY_TICK, IAbilityEvent.TickEvent.class); hook(NPC, ABILITY_INTERRUPT, IAbilityEvent.InterruptEvent.class); hook(NPC, ABILITY_COMPLETE, IAbilityEvent.CompleteEvent.class); + + // Animation + hook(NPC, ANIMATION_START, IAnimationEvent.Started.class); + hook(NPC, ANIMATION_END, IAnimationEvent.Ended.class); + hook(NPC, ANIMATION_FRAME_ENTER, IAnimationEvent.IFrameEvent.Entered.class); + hook(NPC, ANIMATION_FRAME_EXIT, IAnimationEvent.IFrameEvent.Exited.class); } private void initializePlayerHooks() { @@ -208,6 +223,21 @@ public class ScriptHookController implements IScriptHookHandler { hook(PLAYER, ANIMATION_FRAME_ENTER, IAnimationEvent.IFrameEvent.Entered.class); hook(PLAYER, ANIMATION_FRAME_EXIT, IAnimationEvent.IFrameEvent.Exited.class); + // Energy Projectile + hook(PLAYER, ENERGY_PROJECTILE_FIRED, IEnergyProjectileEvent.FiredEvent.class); + hook(PLAYER, ENERGY_PROJECTILE_TICK, IEnergyProjectileEvent.UpdateEvent.class); + hook(PLAYER, ENERGY_PROJECTILE_ENTITY_IMPACT, IEnergyProjectileEvent.EntityImpactEvent.class); + hook(PLAYER, ENERGY_PROJECTILE_BLOCK_IMPACT, IEnergyProjectileEvent.BlockImpactEvent.class); + hook(PLAYER, ENERGY_PROJECTILE_EXPIRED, IEnergyProjectileEvent.ExpiredEvent.class); + + // Ability + hook(PLAYER, ABILITY_START, IPlayerAbilityEvent.StartEvent.class); + hook(PLAYER, ABILITY_EXECUTE, IPlayerAbilityEvent.ExecuteEvent.class); + hook(PLAYER, ABILITY_HIT, IPlayerAbilityEvent.HitEvent.class); + hook(PLAYER, ABILITY_TICK, IPlayerAbilityEvent.TickEvent.class); + hook(PLAYER, ABILITY_INTERRUPT, IPlayerAbilityEvent.InterruptEvent.class); + hook(PLAYER, ABILITY_COMPLETE, IPlayerAbilityEvent.CompleteEvent.class); + // Profile hook(PLAYER, PROFILE_CHANGE, IPlayerEvent.ProfileEvent.Changed.class); hook(PLAYER, PROFILE_REMOVE, IPlayerEvent.ProfileEvent.Removed.class); diff --git a/src/main/java/noppes/npcs/controllers/data/PlayerAbilityData.java b/src/main/java/noppes/npcs/controllers/data/PlayerAbilityData.java index 6ced6f68a..088017e62 100644 --- a/src/main/java/noppes/npcs/controllers/data/PlayerAbilityData.java +++ b/src/main/java/noppes/npcs/controllers/data/PlayerAbilityData.java @@ -15,9 +15,14 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.nbt.NBTTagString; import net.minecraft.util.DamageSource; +import noppes.npcs.EventHooks; import noppes.npcs.api.ability.IPlayerAbilityData; +import noppes.npcs.api.entity.IPlayer; import noppes.npcs.controllers.AnimationController; +import noppes.npcs.controllers.ScriptController; import noppes.npcs.controllers.data.Animation; +import noppes.npcs.scripted.NpcAPI; +import noppes.npcs.scripted.event.player.PlayerAbilityEvent; import kamkeel.npcs.network.packets.data.ability.PlayerAbilityStatePacket; import kamkeel.npcs.network.packets.data.ability.PlayerAbilitySyncPacket; @@ -154,6 +159,9 @@ public class PlayerAbilityData implements IPlayerAbilityData { // Tick advances time and possibly changes phase boolean phaseChanged = currentAbility.tick(); + // Fire tick event for scripts + firePlayerTickEvent(player, currentAbility, currentTarget); + // Fire extender tick hook (e.g., per-tick resource drain) if (!AbilityController.Instance.fireOnAbilityTick(currentAbility, player, currentTarget, currentAbility.getPhase(), currentAbility.getCurrentTick())) { @@ -211,6 +219,11 @@ public class PlayerAbilityData implements IPlayerAbilityData { playAbilitySound(player, currentAbility.getActiveSound()); playAbilityAnimation(currentAbility.getActiveAnimation()); + // Fire execute event (cancelable) + if (firePlayerExecuteEvent(player, currentAbility, currentTarget)) { + return; // Cancelled + } + // Call onExecute currentAbility.onExecute(player, currentTarget); @@ -268,6 +281,9 @@ public class PlayerAbilityData implements IPlayerAbilityData { currentAbility.onComplete(player, currentTarget); + // Fire complete event + firePlayerCompleteEvent(player, currentAbility, currentTarget); + // Release all locks releaseRotationControl(); releaseLockedPosition(); @@ -340,6 +356,11 @@ public class PlayerAbilityData implements IPlayerAbilityData { return false; } + // Fire start event (cancelable) + if (firePlayerStartEvent(player, ability)) { + return false; // Cancelled + } + // Start the ability currentAbility = ability; currentAbilityKey = key; @@ -380,6 +401,11 @@ public class PlayerAbilityData implements IPlayerAbilityData { playAbilitySound(player, ability.getActiveSound()); playAbilityAnimation(ability.getActiveAnimation()); + // Fire execute event (cancelable) + if (firePlayerExecuteEvent(player, ability, currentTarget)) { + return; // Cancelled + } + // Call onExecute ability.onExecute(player, currentTarget); @@ -538,10 +564,17 @@ public class PlayerAbilityData implements IPlayerAbilityData { } public void interruptCurrentAbility() { + interruptCurrentAbility(null, 0); + } + + public void interruptCurrentAbility(DamageSource source, float damage) { if (currentAbility != null && currentAbility.isExecuting()) { // Fire extender complete hook (interrupted) AbilityController.Instance.fireOnAbilityComplete(currentAbility, playerData.player, currentTarget, true); + // Fire interrupt event + firePlayerInterruptEvent(playerData.player, currentAbility, currentTarget, source, damage); + currentAbility.interrupt(); stopAbilityAnimation(); releaseRotationControl(); @@ -594,7 +627,7 @@ public class PlayerAbilityData implements IPlayerAbilityData { // Check for ability interruption if (currentAbility != null && currentAbility.canInterrupt(source)) { - interruptCurrentAbility(); + interruptCurrentAbility(source, amount); } return amount; @@ -834,6 +867,73 @@ public class PlayerAbilityData implements IPlayerAbilityData { } } + // ═══════════════════════════════════════════════════════════════════ + // SCRIPT EVENTS + // ═══════════════════════════════════════════════════════════════════ + + /** + * Fire the ability start event. Returns true if cancelled. + */ + private boolean firePlayerStartEvent(EntityPlayer player, Ability ability) { + if (ScriptController.Instance == null) return false; + PlayerDataScript handler = ScriptController.Instance.getPlayerScripts(player); + if (handler == null) return false; + IPlayer iPlayer = (IPlayer) NpcAPI.Instance().getIEntity(player); + PlayerAbilityEvent.StartEvent event = new PlayerAbilityEvent.StartEvent(iPlayer, ability, null); + return EventHooks.onPlayerAbilityStart(handler, event); + } + + /** + * Fire the ability execute event. Returns true if cancelled. + */ + private boolean firePlayerExecuteEvent(EntityPlayer player, Ability ability, EntityLivingBase target) { + if (ScriptController.Instance == null) return false; + PlayerDataScript handler = ScriptController.Instance.getPlayerScripts(player); + if (handler == null) return false; + IPlayer iPlayer = (IPlayer) NpcAPI.Instance().getIEntity(player); + PlayerAbilityEvent.ExecuteEvent event = new PlayerAbilityEvent.ExecuteEvent(iPlayer, ability, target); + return EventHooks.onPlayerAbilityExecute(handler, event); + } + + /** + * Fire the ability tick event. + */ + private void firePlayerTickEvent(EntityPlayer player, Ability ability, EntityLivingBase target) { + if (ScriptController.Instance == null) return; + PlayerDataScript handler = ScriptController.Instance.getPlayerScripts(player); + if (handler == null) return; + IPlayer iPlayer = (IPlayer) NpcAPI.Instance().getIEntity(player); + PlayerAbilityEvent.TickEvent event = new PlayerAbilityEvent.TickEvent( + iPlayer, ability, target, ability.getPhase().ordinal(), ability.getCurrentTick()); + EventHooks.onPlayerAbilityTick(handler, event); + } + + /** + * Fire the ability interrupt event. + */ + private void firePlayerInterruptEvent(EntityPlayer player, Ability ability, EntityLivingBase target, + DamageSource source, float damage) { + if (ScriptController.Instance == null) return; + PlayerDataScript handler = ScriptController.Instance.getPlayerScripts(player); + if (handler == null) return; + IPlayer iPlayer = (IPlayer) NpcAPI.Instance().getIEntity(player); + PlayerAbilityEvent.InterruptEvent event = new PlayerAbilityEvent.InterruptEvent( + iPlayer, ability, target, source, damage); + EventHooks.onPlayerAbilityInterrupt(handler, event); + } + + /** + * Fire the ability complete event. + */ + private void firePlayerCompleteEvent(EntityPlayer player, Ability ability, EntityLivingBase target) { + if (ScriptController.Instance == null) return; + PlayerDataScript handler = ScriptController.Instance.getPlayerScripts(player); + if (handler == null) return; + IPlayer iPlayer = (IPlayer) NpcAPI.Instance().getIEntity(player); + PlayerAbilityEvent.CompleteEvent event = new PlayerAbilityEvent.CompleteEvent(iPlayer, ability, target); + EventHooks.onPlayerAbilityComplete(handler, event); + } + // ═══════════════════════════════════════════════════════════════════ // NBT SERIALIZATION // ═══════════════════════════════════════════════════════════════════ diff --git a/src/main/java/noppes/npcs/scripted/NpcAPI.java b/src/main/java/noppes/npcs/scripted/NpcAPI.java index c80c18d28..24f271f33 100644 --- a/src/main/java/noppes/npcs/scripted/NpcAPI.java +++ b/src/main/java/noppes/npcs/scripted/NpcAPI.java @@ -7,6 +7,10 @@ import kamkeel.npcs.controllers.AttributeController; import kamkeel.npcs.controllers.ProfileController; import kamkeel.npcs.controllers.TelegraphController; import kamkeel.npcs.controllers.data.ability.AbilityController; +import kamkeel.npcs.entity.EntityAbilityBeam; +import kamkeel.npcs.entity.EntityAbilityDisc; +import kamkeel.npcs.entity.EntityAbilityLaser; +import kamkeel.npcs.entity.EntityAbilityOrb; import net.minecraft.block.Block; import net.minecraft.command.CommandHandler; import net.minecraft.entity.Entity; @@ -119,6 +123,10 @@ import noppes.npcs.items.ItemScripted; import noppes.npcs.scripted.entity.ScriptAnimal; import noppes.npcs.scripted.entity.ScriptArrow; import noppes.npcs.scripted.entity.ScriptDBCPlayer; +import noppes.npcs.scripted.entity.ScriptEnergyBeam; +import noppes.npcs.scripted.entity.ScriptEnergyDisc; +import noppes.npcs.scripted.entity.ScriptEnergyLaser; +import noppes.npcs.scripted.entity.ScriptEnergyOrb; import noppes.npcs.scripted.entity.ScriptEntity; import noppes.npcs.scripted.entity.ScriptEntityItem; import noppes.npcs.scripted.entity.ScriptFishHook; @@ -454,6 +462,14 @@ public class NpcAPI extends AbstractNpcAPI { data = new ScriptEntityData(new ScriptArrow<>((EntityArrow) entity)); else if (entity instanceof EntityFishHook) data = new ScriptEntityData(new ScriptFishHook<>((EntityFishHook) entity)); + else if (entity instanceof EntityAbilityOrb) + data = new ScriptEntityData(new ScriptEnergyOrb<>((EntityAbilityOrb) entity)); + else if (entity instanceof EntityAbilityBeam) + data = new ScriptEntityData(new ScriptEnergyBeam<>((EntityAbilityBeam) entity)); + else if (entity instanceof EntityAbilityDisc) + data = new ScriptEntityData(new ScriptEnergyDisc<>((EntityAbilityDisc) entity)); + else if (entity instanceof EntityAbilityLaser) + data = new ScriptEntityData(new ScriptEnergyLaser<>((EntityAbilityLaser) entity)); else data = new ScriptEntityData(new ScriptEntity<>(entity)); entity.registerExtendedProperties("ScriptedObject", data); diff --git a/src/main/java/noppes/npcs/scripted/ScriptWorld.java b/src/main/java/noppes/npcs/scripted/ScriptWorld.java index c9b806ca9..b827315d2 100644 --- a/src/main/java/noppes/npcs/scripted/ScriptWorld.java +++ b/src/main/java/noppes/npcs/scripted/ScriptWorld.java @@ -21,6 +21,10 @@ import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.util.BlockSnapshot; import net.minecraftforge.common.util.FakePlayer; import net.minecraftforge.event.world.BlockEvent; +import kamkeel.npcs.entity.EntityAbilityBeam; +import kamkeel.npcs.entity.EntityAbilityDisc; +import kamkeel.npcs.entity.EntityAbilityLaser; +import kamkeel.npcs.entity.EntityAbilityOrb; import noppes.npcs.CustomNpcs; import noppes.npcs.NoppesUtilServer; import noppes.npcs.api.IBlock; @@ -28,6 +32,10 @@ import noppes.npcs.api.IParticle; import noppes.npcs.api.IPos; import noppes.npcs.api.ITileEntity; import noppes.npcs.api.IWorld; +import noppes.npcs.api.entity.IEnergyBeam; +import noppes.npcs.api.entity.IEnergyDisc; +import noppes.npcs.api.entity.IEnergyLaser; +import noppes.npcs.api.entity.IEnergyOrb; import noppes.npcs.api.entity.IEntity; import noppes.npcs.api.entity.IPlayer; import noppes.npcs.api.handler.data.ISound; @@ -1242,6 +1250,48 @@ public class ScriptWorld implements IWorld { return "DIM" + this.getDimensionID(); } + @Override + public IEnergyOrb createEnergyOrb(IEntity 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.getMCEntity().getEntityId()); + entity.setProjectileSize(size); + return (IEnergyOrb) NpcAPI.Instance().getIEntity(entity); + } + + @Override + public IEnergyBeam createEnergyBeam(IEntity 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.getMCEntity().getEntityId()); + entity.setBeamWidth(beamWidth); + entity.setHeadSize(headSize); + return (IEnergyBeam) NpcAPI.Instance().getIEntity(entity); + } + + @Override + public IEnergyDisc createEnergyDisc(IEntity 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.getMCEntity().getEntityId()); + entity.setDiscRadius(radius); + entity.setDiscThickness(thickness); + return (IEnergyDisc) NpcAPI.Instance().getIEntity(entity); + } + + @Override + public IEnergyLaser createEnergyLaser(IEntity 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.getMCEntity().getEntityId()); + entity.setLaserWidth(laserWidth); + return (IEnergyLaser) NpcAPI.Instance().getIEntity(entity); + } + @Override public void broadcast(String message) { world.func_73046_m().getConfigurationManager().sendChatMsg(new ChatComponentText(message)); diff --git a/src/main/java/noppes/npcs/scripted/constants/EntityType.java b/src/main/java/noppes/npcs/scripted/constants/EntityType.java index 995969682..355173646 100644 --- a/src/main/java/noppes/npcs/scripted/constants/EntityType.java +++ b/src/main/java/noppes/npcs/scripted/constants/EntityType.java @@ -15,4 +15,8 @@ public class EntityType { public static final int THROWABLE = 11; public static final int FISH_HOOK = 12; public static final int ENERGY_PROJECTILE = 13; + public static final int ENERGY_ORB = 14; + public static final int ENERGY_BEAM = 15; + public static final int ENERGY_DISC = 16; + public static final int ENERGY_LASER = 17; } diff --git a/src/main/java/noppes/npcs/scripted/entity/ScriptEnergyBeam.java b/src/main/java/noppes/npcs/scripted/entity/ScriptEnergyBeam.java new file mode 100644 index 000000000..b42a32b1d --- /dev/null +++ b/src/main/java/noppes/npcs/scripted/entity/ScriptEnergyBeam.java @@ -0,0 +1,69 @@ +package noppes.npcs.scripted.entity; + +import kamkeel.npcs.entity.EntityAbilityBeam; +import net.minecraft.entity.EntityLivingBase; +import noppes.npcs.api.entity.IEnergyBeam; +import noppes.npcs.api.entity.IEntity; +import noppes.npcs.scripted.constants.EntityType; + +public class ScriptEnergyBeam extends ScriptEnergyProjectile implements IEnergyBeam { + + public ScriptEnergyBeam(T entity) { + super(entity); + } + + @Override + public int getType() { + return EntityType.ENERGY_BEAM; + } + + @Override + public boolean typeOf(int type) { + return type == EntityType.ENERGY_BEAM || super.typeOf(type); + } + + @Override + public int getEnergyType() { + return 1; + } + + // ==================== BEAM-SPECIFIC ==================== + + public float getBeamWidth() { return entity.getBeamWidth(); } + public void setBeamWidth(float width) { entity.setBeamWidth(width); } + + public float getHeadSize() { return entity.getHeadSize(); } + public void setHeadSize(float size) { entity.setHeadSize(size); } + + public boolean isAttachedToOwner() { return entity.isAttachedToOwner(); } + public void setAttachedToOwner(boolean attached) { entity.setAttachedToOwner(attached); } + + public boolean shouldRenderTailOrb() { return entity.shouldRenderTailOrb(); } + + // ==================== FIRE ==================== + + @Override + public void fire(IEntity target) { + ensureSpawned(); + EntityLivingBase mcTarget = null; + if (target != null && target.getMCEntity() instanceof EntityLivingBase) { + mcTarget = (EntityLivingBase) target.getMCEntity(); + } + entity.startFiring(mcTarget); + } + + @Override + public void fire(double x, double y, double z) { + ensureSpawned(); + double dx = x - entity.posX; + double dy = y - entity.posY; + double dz = z - entity.posZ; + double len = Math.sqrt(dx * dx + dy * dy + dz * dz); + if (len > 0) { + entity.motionX = (dx / len) * entity.getSpeed(); + entity.motionY = (dy / len) * entity.getSpeed(); + entity.motionZ = (dz / len) * entity.getSpeed(); + } + entity.startFiring(null); + } +} diff --git a/src/main/java/noppes/npcs/scripted/entity/ScriptEnergyDisc.java b/src/main/java/noppes/npcs/scripted/entity/ScriptEnergyDisc.java new file mode 100644 index 000000000..62dde7979 --- /dev/null +++ b/src/main/java/noppes/npcs/scripted/entity/ScriptEnergyDisc.java @@ -0,0 +1,75 @@ +package noppes.npcs.scripted.entity; + +import kamkeel.npcs.entity.EntityAbilityDisc; +import net.minecraft.entity.EntityLivingBase; +import noppes.npcs.api.entity.IEnergyDisc; +import noppes.npcs.api.entity.IEntity; +import noppes.npcs.scripted.constants.EntityType; + +public class ScriptEnergyDisc extends ScriptEnergyProjectile implements IEnergyDisc { + + public ScriptEnergyDisc(T entity) { + super(entity); + } + + @Override + public int getType() { + return EntityType.ENERGY_DISC; + } + + @Override + public boolean typeOf(int type) { + return type == EntityType.ENERGY_DISC || super.typeOf(type); + } + + @Override + public int getEnergyType() { + return 2; + } + + // ==================== DISC-SPECIFIC ==================== + + public float getDiscRadius() { return entity.getDiscRadius(); } + public void setDiscRadius(float radius) { entity.setDiscRadius(radius); } + + public float getDiscThickness() { return entity.getDiscThickness(); } + public void setDiscThickness(float thickness) { entity.setDiscThickness(thickness); } + + public boolean isVertical() { return entity.isVertical(); } + public void setVertical(boolean vertical) { entity.setVertical(vertical); } + + public boolean isBoomerang() { return entity.isBoomerang(); } + public void setBoomerang(boolean boomerang) { entity.setBoomerang(boomerang); } + + public int getBoomerangDelay() { return entity.getBoomerangDelay(); } + public void setBoomerangDelay(int ticks) { entity.setBoomerangDelay(ticks); } + + public boolean isReturning() { return entity.isReturning(); } + + // ==================== FIRE ==================== + + @Override + public void fire(IEntity target) { + ensureSpawned(); + EntityLivingBase mcTarget = null; + if (target != null && target.getMCEntity() instanceof EntityLivingBase) { + mcTarget = (EntityLivingBase) target.getMCEntity(); + } + entity.startMoving(mcTarget); + } + + @Override + public void fire(double x, double y, double z) { + ensureSpawned(); + double dx = x - entity.posX; + double dy = y - entity.posY; + double dz = z - entity.posZ; + double len = Math.sqrt(dx * dx + dy * dy + dz * dz); + if (len > 0) { + entity.motionX = (dx / len) * entity.getSpeed(); + entity.motionY = (dy / len) * entity.getSpeed(); + entity.motionZ = (dz / len) * entity.getSpeed(); + } + entity.startMoving(null); + } +} diff --git a/src/main/java/noppes/npcs/scripted/entity/ScriptEnergyLaser.java b/src/main/java/noppes/npcs/scripted/entity/ScriptEnergyLaser.java new file mode 100644 index 000000000..976fd948d --- /dev/null +++ b/src/main/java/noppes/npcs/scripted/entity/ScriptEnergyLaser.java @@ -0,0 +1,82 @@ +package noppes.npcs.scripted.entity; + +import kamkeel.npcs.entity.EntityAbilityLaser; +import net.minecraft.entity.EntityLivingBase; +import noppes.npcs.api.entity.IEnergyLaser; +import noppes.npcs.api.entity.IEntity; +import noppes.npcs.scripted.constants.EntityType; + +public class ScriptEnergyLaser extends ScriptEnergyProjectile implements IEnergyLaser { + + public ScriptEnergyLaser(T entity) { + super(entity); + } + + @Override + public int getType() { + return EntityType.ENERGY_LASER; + } + + @Override + public boolean typeOf(int type) { + return type == EntityType.ENERGY_LASER || super.typeOf(type); + } + + @Override + public int getEnergyType() { + return 3; + } + + // ==================== LASER-SPECIFIC ==================== + + public float getLaserWidth() { return entity.getLaserWidth(); } + public void setLaserWidth(float width) { entity.setLaserWidth(width); } + + public float getExpansionSpeed() { return entity.getExpansionSpeed(); } + public void setExpansionSpeed(float speed) { entity.setExpansionSpeed(speed); } + + public int getLingerTicks() { return entity.getLingerTicks(); } + public void setLingerTicks(int ticks) { entity.setLingerTicks(ticks); } + + public float getCurrentLength() { return entity.getCurrentLength(); } + public boolean isFullyExtended() { return entity.isFullyExtended(); } + + public double getDirX() { return entity.getDirX(); } + public double getDirY() { return entity.getDirY(); } + public double getDirZ() { return entity.getDirZ(); } + public void setDirection(double x, double y, double z) { entity.setDirection(x, y, z); } + + public double getEndX() { return entity.getEndX(); } + public double getEndY() { return entity.getEndY(); } + public double getEndZ() { return entity.getEndZ(); } + + public float getLingerAlpha() { return entity.getLingerAlpha(); } + + public boolean isLockVerticalDirection() { return entity.isLockVerticalDirection(); } + public void setLockVerticalDirection(boolean lock) { entity.setLockVerticalDirection(lock); } + + // ==================== FIRE ==================== + + @Override + public void fire(IEntity target) { + ensureSpawned(); + EntityLivingBase mcTarget = null; + if (target != null && target.getMCEntity() instanceof EntityLivingBase) { + mcTarget = (EntityLivingBase) target.getMCEntity(); + } + entity.startMoving(mcTarget); + } + + @Override + public void fire(double x, double y, double z) { + ensureSpawned(); + double dx = x - entity.posX; + double dy = y - entity.posY; + double dz = z - entity.posZ; + double len = Math.sqrt(dx * dx + dy * dy + dz * dz); + if (len > 0) { + entity.setDirection(dx / len, dy / len, dz / len); + } + entity.startMoving(null); + } +} diff --git a/src/main/java/noppes/npcs/scripted/entity/ScriptEnergyOrb.java b/src/main/java/noppes/npcs/scripted/entity/ScriptEnergyOrb.java new file mode 100644 index 000000000..1b6a95fb7 --- /dev/null +++ b/src/main/java/noppes/npcs/scripted/entity/ScriptEnergyOrb.java @@ -0,0 +1,53 @@ +package noppes.npcs.scripted.entity; + +import kamkeel.npcs.entity.EntityAbilityOrb; +import net.minecraft.entity.EntityLivingBase; +import noppes.npcs.api.entity.IEnergyOrb; +import noppes.npcs.api.entity.IEntity; +import noppes.npcs.scripted.constants.EntityType; + +public class ScriptEnergyOrb extends ScriptEnergyProjectile implements IEnergyOrb { + + public ScriptEnergyOrb(T entity) { + super(entity); + } + + @Override + public int getType() { + return EntityType.ENERGY_ORB; + } + + @Override + public boolean typeOf(int type) { + return type == EntityType.ENERGY_ORB || super.typeOf(type); + } + + @Override + public int getEnergyType() { + return 0; + } + + @Override + public void fire(IEntity target) { + ensureSpawned(); + EntityLivingBase mcTarget = null; + if (target != null && target.getMCEntity() instanceof EntityLivingBase) { + mcTarget = (EntityLivingBase) target.getMCEntity(); + } + entity.startMoving(mcTarget); + } + + @Override + public void fire(double x, double y, double z) { + ensureSpawned(); + double dx = x - entity.posX; + double dy = y - entity.posY; + double dz = z - entity.posZ; + double len = Math.sqrt(dx * dx + dy * dy + dz * dz); + if (len > 0) { + entity.motionX = (dx / len) * entity.getSpeed(); + entity.motionY = (dy / len) * entity.getSpeed(); + entity.motionZ = (dz / len) * entity.getSpeed(); + } + } +} diff --git a/src/main/java/noppes/npcs/scripted/entity/ScriptEnergyProjectile.java b/src/main/java/noppes/npcs/scripted/entity/ScriptEnergyProjectile.java new file mode 100644 index 000000000..f614b8122 --- /dev/null +++ b/src/main/java/noppes/npcs/scripted/entity/ScriptEnergyProjectile.java @@ -0,0 +1,252 @@ +package noppes.npcs.scripted.entity; + +import kamkeel.npcs.entity.EntityAbilityProjectile; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import noppes.npcs.api.entity.IEnergyProjectile; +import noppes.npcs.api.entity.IEntity; +import noppes.npcs.api.entity.IEntityLivingBase; +import noppes.npcs.EventHooks; +import noppes.npcs.scripted.NpcAPI; +import noppes.npcs.scripted.constants.EntityType; + +public class ScriptEnergyProjectile extends ScriptEntity implements IEnergyProjectile { + protected T entity; + + public ScriptEnergyProjectile(T entity) { + super(entity); + this.entity = entity; + } + + // ==================== TYPE ==================== + + @Override + public int getType() { + return EntityType.ENERGY_PROJECTILE; + } + + @Override + public boolean typeOf(int type) { + return type == EntityType.ENERGY_PROJECTILE || super.typeOf(type); + } + + public int getEnergyType() { + return -1; + } + + // ==================== OWNER & TARGET ==================== + + public int getOwnerEntityId() { + return entity.getOwnerEntityId(); + } + + public IEntity getOwner() { + return entity.getOwner(); + } + + public void setOwner(IEntity owner) { + if (owner != null) { + entity.setOwnerEntityId(owner.getMCEntity().getEntityId()); + } + } + + public int getTargetEntityId() { + return entity.getTargetEntityId(); + } + + public IEntity getTarget() { + return entity.getTarget(); + } + + public void setTarget(IEntity target) { + if (target != null) { + entity.setTargetEntityId(target.getMCEntity().getEntityId()); + } else { + entity.setTargetEntityId(-1); + } + } + + // ==================== SIZE ==================== + + public float getSize() { + return entity.getSize(); + } + + public void setSize(float size) { + entity.setProjectileSize(size); + } + + // ==================== DISPLAY ==================== + + public int getInnerColor() { return entity.getInnerColor(); } + public void setInnerColor(int color) { entity.setInnerColor(color); } + + public int getOuterColor() { return entity.getOuterColor(); } + public void setOuterColor(int color) { entity.setOuterColor(color); } + + public boolean isOuterColorEnabled() { return entity.isOuterColorEnabled(); } + public void setOuterColorEnabled(boolean enabled) { entity.setOuterColorEnabled(enabled); } + + public float getOuterColorWidth() { return entity.getOuterColorWidth(); } + public void setOuterColorWidth(float width) { entity.setOuterColorWidth(width); } + + public float getOuterColorAlpha() { return entity.getOuterColorAlpha(); } + public void setOuterColorAlpha(float alpha) { entity.setOuterColorAlpha(alpha); } + + public float getRotationSpeed() { return entity.getRotationSpeed(); } + public void setRotationSpeed(float speed) { entity.setRotationSpeed(speed); } + + // ==================== INTERPOLATION ==================== + + public float getInterpolatedRotationX(float partialTicks) { return entity.getInterpolatedRotationX(partialTicks); } + public float getInterpolatedRotationY(float partialTicks) { return entity.getInterpolatedRotationY(partialTicks); } + public float getInterpolatedRotationZ(float partialTicks) { return entity.getInterpolatedRotationZ(partialTicks); } + public float getInterpolatedSize(float partialTicks) { return entity.getInterpolatedSize(partialTicks); } + + // ==================== LIGHTNING ==================== + + public boolean hasLightningEffect() { return entity.hasLightningEffect(); } + public void setLightningEffect(boolean enabled) { entity.setLightningEffect(enabled); } + + public float getLightningDensity() { return entity.getLightningDensity(); } + public void setLightningDensity(float density) { entity.setLightningDensity(density); } + + public float getLightningRadius() { return entity.getLightningRadius(); } + public void setLightningRadius(float radius) { entity.setLightningRadius(radius); } + + public int getLightningFadeTime() { return entity.getLightningFadeTime(); } + public void setLightningFadeTime(int ticks) { entity.setLightningFadeTime(ticks); } + + // ==================== LIFESPAN ==================== + + public float getMaxDistance() { return entity.getMaxDistance(); } + public void setMaxDistance(float distance) { entity.setMaxDistance(distance); } + + public int getMaxLifetime() { return entity.getMaxLifetime(); } + public void setMaxLifetime(int ticks) { entity.setMaxLifetime(ticks); } + + // ==================== COMBAT ==================== + + public float getDamage() { return entity.getDamage(); } + public void setDamage(float damage) { entity.setCombatDamage(damage); } + + public float getKnockback() { return entity.getKnockback(); } + public void setKnockback(float knockback) { entity.setCombatKnockback(knockback); } + + public float getKnockbackUp() { return entity.getKnockbackUp(); } + public void setKnockbackUp(float knockbackUp) { entity.setCombatKnockbackUp(knockbackUp); } + + public boolean isExplosive() { return entity.isExplosive(); } + public void setExplosive(boolean explosive) { entity.setExplosive(explosive); } + + public float getExplosionRadius() { return entity.getExplosionRadius(); } + public void setExplosionRadius(float radius) { entity.setExplosionRadius(radius); } + + public float getExplosionDamageFalloff() { return entity.getExplosionDamageFalloff(); } + public void setExplosionDamageFalloff(float falloff) { entity.setExplosionDamageFalloff(falloff); } + + // ==================== MOVEMENT ==================== + + public float getSpeed() { return entity.getSpeed(); } + public void setSpeed(float speed) { entity.setSpeed(speed); } + + public boolean isHoming() { return entity.isHoming(); } + public void setHoming(boolean homing) { entity.setHomingEnabled(homing); } + + public float getHomingStrength() { return entity.getHomingStrength(); } + public void setHomingStrength(float strength) { entity.setHomingStrength(strength); } + + public float getHomingRange() { return entity.getHomingRange(); } + public void setHomingRange(float range) { entity.setHomingRange(range); } + + // ==================== ANCHOR ==================== + + public int getAnchor() { return entity.getAnchor(); } + public float getAnchorOffsetX() { return entity.getAnchorOffsetX(); } + public float getAnchorOffsetY() { return entity.getAnchorOffsetY(); } + public float getAnchorOffsetZ() { return entity.getAnchorOffsetZ(); } + + // ==================== POSITION ==================== + + public double getStartX() { return entity.getStartX(); } + public double getStartY() { return entity.getStartY(); } + public double getStartZ() { return entity.getStartZ(); } + + // ==================== STATE ==================== + + public boolean hasHit() { return entity.getHasHit(); } + public boolean isCharging() { return entity.isCharging(); } + public float getChargeProgress() { return entity.getChargeProgress(); } + + // ==================== FIRE ==================== + + public void fire(IEntity target) { + if (target != null) { + Entity mcTarget = target.getMCEntity(); + setMotionToward(mcTarget.posX, mcTarget.posY + mcTarget.getEyeHeight(), mcTarget.posZ); + } + ensureSpawned(); + } + + public void fire(double x, double y, double z) { + setMotionToward(x, y, z); + ensureSpawned(); + } + + public void fire(float yaw, float pitch) { + float yawRad = (float) Math.toRadians(yaw); + float pitchRad = (float) Math.toRadians(pitch); + entity.motionX = -Math.sin(yawRad) * Math.cos(pitchRad) * entity.getSpeed(); + entity.motionY = -Math.sin(pitchRad) * entity.getSpeed(); + entity.motionZ = Math.cos(yawRad) * Math.cos(pitchRad) * entity.getSpeed(); + ensureSpawned(); + } + + public void fire(IEntityLivingBase caster) { + if (caster == null) return; + initFromCaster(caster); + fire(caster.getRotation(), caster.getPitch()); + } + + public void fire(IEntityLivingBase caster, IEntity target) { + if (caster == null) return; + initFromCaster(caster); + fire(target); + } + + /** + * Sets owner from caster and positions projectile at caster's eye level. + */ + private void initFromCaster(IEntityLivingBase caster) { + EntityLivingBase mc = (EntityLivingBase) caster.getMCEntity(); + entity.setOwnerEntityId(mc.getEntityId()); + double eyeY = mc.posY + mc.getEyeHeight(); + entity.setPosition(mc.posX, eyeY, mc.posZ); + entity.setStartPosition(mc.posX, eyeY, mc.posZ); + } + + /** + * Calculates and sets motion from the projectile's current position toward a target point. + */ + private void setMotionToward(double x, double y, double z) { + double dx = x - entity.posX; + double dy = y - entity.posY; + double dz = z - entity.posZ; + double len = Math.sqrt(dx * dx + dy * dy + dz * dz); + if (len > 0) { + entity.motionX = (dx / len) * entity.getSpeed(); + entity.motionY = (dy / len) * entity.getSpeed(); + entity.motionZ = (dz / len) * entity.getSpeed(); + } + } + + protected void ensureSpawned() { + if (!entity.addedToChunk && entity.worldObj != null) { + entity.worldObj.spawnEntityInWorld(entity); + if (!entity.worldObj.isRemote) { + EventHooks.onEnergyProjectileFired(entity); + } + } + } + +} diff --git a/src/main/java/noppes/npcs/scripted/entity/ScriptEntity.java b/src/main/java/noppes/npcs/scripted/entity/ScriptEntity.java index caac8d87d..65b2866af 100644 --- a/src/main/java/noppes/npcs/scripted/entity/ScriptEntity.java +++ b/src/main/java/noppes/npcs/scripted/entity/ScriptEntity.java @@ -1,5 +1,10 @@ package noppes.npcs.scripted.entity; +import kamkeel.npcs.entity.EntityAbilityBeam; +import kamkeel.npcs.entity.EntityAbilityDisc; +import kamkeel.npcs.entity.EntityAbilityLaser; +import kamkeel.npcs.entity.EntityAbilityOrb; +import kamkeel.npcs.entity.EntityAbilityProjectile; import net.minecraft.block.material.Material; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityList; @@ -526,6 +531,16 @@ public class ScriptEntity implements IEntity { cls = EntityMob.class; else if (type == EntityType.NPC) cls = EntityNPCInterface.class; + else if (type == EntityType.ENERGY_PROJECTILE) + cls = EntityAbilityProjectile.class; + else if (type == EntityType.ENERGY_ORB) + cls = EntityAbilityOrb.class; + else if (type == EntityType.ENERGY_BEAM) + cls = EntityAbilityBeam.class; + else if (type == EntityType.ENERGY_DISC) + cls = EntityAbilityDisc.class; + else if (type == EntityType.ENERGY_LASER) + cls = EntityAbilityLaser.class; List entities = entity.worldObj.getEntitiesWithinAABB(cls, entity.boundingBox.expand(range, range, range)); List list = new ArrayList(); diff --git a/src/main/java/noppes/npcs/scripted/entity/ScriptNpc.java b/src/main/java/noppes/npcs/scripted/entity/ScriptNpc.java index f534f9dbc..2379c2035 100644 --- a/src/main/java/noppes/npcs/scripted/entity/ScriptNpc.java +++ b/src/main/java/noppes/npcs/scripted/entity/ScriptNpc.java @@ -7,6 +7,7 @@ import noppes.npcs.api.IPos; import noppes.npcs.api.ITimers; import noppes.npcs.api.ability.IDataAbilities; import noppes.npcs.api.entity.ICustomNpc; +import noppes.npcs.api.entity.IEnergyProjectile; import noppes.npcs.api.entity.IEntityLivingBase; import noppes.npcs.api.entity.IPlayer; import noppes.npcs.api.entity.data.IModelData; @@ -28,6 +29,7 @@ import noppes.npcs.constants.EnumMovingType; import noppes.npcs.constants.EnumNavType; import noppes.npcs.constants.EnumRoleType; import noppes.npcs.constants.EnumStandingType; +import kamkeel.npcs.entity.EntityAbilityProjectile; import noppes.npcs.controllers.FactionController; import noppes.npcs.controllers.data.DialogOption; import noppes.npcs.controllers.data.Line; @@ -1463,4 +1465,13 @@ public class ScriptNpc extends ScriptLiving imp public IDataAbilities getAbilityData() { return new ScriptDataAbilities(npc); } + + public IEnergyProjectile[] getActiveEnergyProjectiles() { + java.util.List entities = EntityAbilityProjectile.getActiveProjectiles(npc.getEntityId()); + IEnergyProjectile[] result = new IEnergyProjectile[entities.size()]; + for (int i = 0; i < entities.size(); i++) { + result[i] = (IEnergyProjectile) NpcAPI.Instance().getIEntity(entities.get(i)); + } + return result; + } } diff --git a/src/main/java/noppes/npcs/scripted/entity/ScriptPlayer.java b/src/main/java/noppes/npcs/scripted/entity/ScriptPlayer.java index a1b91e1db..3608b9d1e 100644 --- a/src/main/java/noppes/npcs/scripted/entity/ScriptPlayer.java +++ b/src/main/java/noppes/npcs/scripted/entity/ScriptPlayer.java @@ -1,6 +1,7 @@ package noppes.npcs.scripted.entity; import kamkeel.npcs.controllers.AttributeController; +import kamkeel.npcs.entity.EntityAbilityProjectile; import kamkeel.npcs.network.PacketHandler; import kamkeel.npcs.network.packets.data.AchievementPacket; import kamkeel.npcs.network.packets.data.ChatAlertPacket; @@ -31,6 +32,7 @@ import noppes.npcs.api.IPos; import noppes.npcs.api.IScreenSize; import noppes.npcs.api.ITimers; import noppes.npcs.api.IWorld; +import noppes.npcs.api.entity.IEnergyProjectile; import noppes.npcs.api.entity.IEntity; import noppes.npcs.api.entity.IPlayer; import noppes.npcs.api.gui.ICustomGui; @@ -933,4 +935,13 @@ public class ScriptPlayer extends ScriptLivingBase public String getFormattedCurrencyBalance() { return getData().tradeData.formatBalance(); } + + public IEnergyProjectile[] getActiveEnergyProjectiles() { + List entities = EntityAbilityProjectile.getActiveProjectiles(player.getEntityId()); + IEnergyProjectile[] result = new IEnergyProjectile[entities.size()]; + for (int i = 0; i < entities.size(); i++) { + result[i] = (IEnergyProjectile) NpcAPI.Instance().getIEntity(entities.get(i)); + } + return result; + } } diff --git a/src/main/java/noppes/npcs/scripted/event/EnergyProjectileEvent.java b/src/main/java/noppes/npcs/scripted/event/EnergyProjectileEvent.java new file mode 100644 index 000000000..4a1fab5a2 --- /dev/null +++ b/src/main/java/noppes/npcs/scripted/event/EnergyProjectileEvent.java @@ -0,0 +1,132 @@ +package noppes.npcs.scripted.event; + +import cpw.mods.fml.common.eventhandler.Cancelable; +import noppes.npcs.api.IBlock; +import noppes.npcs.api.entity.IEnergyProjectile; +import noppes.npcs.api.entity.IEntity; +import noppes.npcs.api.event.IEnergyProjectileEvent; +import noppes.npcs.constants.EnumScriptType; + +public class EnergyProjectileEvent extends CustomNPCsEvent implements IEnergyProjectileEvent { + public final IEnergyProjectile projectile; + public final IEntity owner; + + public EnergyProjectileEvent(IEnergyProjectile projectile) { + this.projectile = projectile; + this.owner = projectile.getOwner(); + } + + @Override + public IEnergyProjectile getProjectile() { + return projectile; + } + + @Override + public IEntity getOwner() { + return owner; + } + + @Override + public String getHookName() { + return "energyProjectileEvent"; + } + + /** + * energyProjectileFired + */ + public static class FiredEvent extends EnergyProjectileEvent implements IEnergyProjectileEvent.FiredEvent { + public FiredEvent(IEnergyProjectile projectile) { + super(projectile); + } + + @Override + public String getHookName() { + return EnumScriptType.ENERGY_PROJECTILE_FIRED.function; + } + } + + /** + * energyProjectileTick + */ + public static class UpdateEvent extends EnergyProjectileEvent implements IEnergyProjectileEvent.UpdateEvent { + public UpdateEvent(IEnergyProjectile projectile) { + super(projectile); + } + + @Override + public String getHookName() { + return EnumScriptType.ENERGY_PROJECTILE_TICK.function; + } + } + + /** + * energyProjectileEntityImpact + */ + @Cancelable + public static class EntityImpactEvent extends EnergyProjectileEvent implements IEnergyProjectileEvent.EntityImpactEvent { + private final IEntity target; + private float damage; + + public EntityImpactEvent(IEnergyProjectile projectile, IEntity target, float damage) { + super(projectile); + this.target = target; + this.damage = damage; + } + + @Override + public IEntity getTarget() { + return target; + } + + @Override + public float getDamage() { + return damage; + } + + @Override + public void setDamage(float damage) { + this.damage = damage; + } + + @Override + public String getHookName() { + return EnumScriptType.ENERGY_PROJECTILE_ENTITY_IMPACT.function; + } + } + + /** + * energyProjectileBlockImpact + */ + public static class BlockImpactEvent extends EnergyProjectileEvent implements IEnergyProjectileEvent.BlockImpactEvent { + private final IBlock block; + + public BlockImpactEvent(IEnergyProjectile projectile, IBlock block) { + super(projectile); + this.block = block; + } + + @Override + public IBlock getBlock() { + return block; + } + + @Override + public String getHookName() { + return EnumScriptType.ENERGY_PROJECTILE_BLOCK_IMPACT.function; + } + } + + /** + * energyProjectileExpired + */ + public static class ExpiredEvent extends EnergyProjectileEvent implements IEnergyProjectileEvent.ExpiredEvent { + public ExpiredEvent(IEnergyProjectile projectile) { + super(projectile); + } + + @Override + public String getHookName() { + return EnumScriptType.ENERGY_PROJECTILE_EXPIRED.function; + } + } +} diff --git a/src/main/java/noppes/npcs/scripted/event/player/PlayerAbilityEvent.java b/src/main/java/noppes/npcs/scripted/event/player/PlayerAbilityEvent.java index cb7def405..b99aac1a1 100644 --- a/src/main/java/noppes/npcs/scripted/event/player/PlayerAbilityEvent.java +++ b/src/main/java/noppes/npcs/scripted/event/player/PlayerAbilityEvent.java @@ -3,6 +3,8 @@ package noppes.npcs.scripted.event.player; import cpw.mods.fml.common.eventhandler.Cancelable; import kamkeel.npcs.controllers.data.ability.Ability; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.util.DamageSource; +import noppes.npcs.api.IDamageSource; import noppes.npcs.api.ability.IAbility; import noppes.npcs.api.entity.IEntityLivingBase; import noppes.npcs.api.entity.IPlayer; @@ -38,6 +40,108 @@ public class PlayerAbilityEvent extends PlayerEvent implements IPlayerAbilityEve return "playerAbilityEvent"; } + /** + * Fired when a player's ability starts executing (enters WINDUP phase). + */ + @Cancelable + public static class StartEvent extends PlayerAbilityEvent implements IPlayerAbilityEvent.StartEvent { + public StartEvent(IPlayer player, Ability ability, EntityLivingBase target) { + super(player, ability, target); + } + + @Override + public String getHookName() { + return EnumScriptType.ABILITY_START.function; + } + } + + /** + * Fired when a player's ability enters the ACTIVE phase and performs its effect. + */ + @Cancelable + public static class ExecuteEvent extends PlayerAbilityEvent implements IPlayerAbilityEvent.ExecuteEvent { + public ExecuteEvent(IPlayer player, Ability ability, EntityLivingBase target) { + super(player, ability, target); + } + + @Override + public String getHookName() { + return EnumScriptType.ABILITY_EXECUTE.function; + } + } + + /** + * Fired when a player's ability is interrupted by damage. + */ + public static class InterruptEvent extends PlayerAbilityEvent implements IPlayerAbilityEvent.InterruptEvent { + private final IDamageSource damageSource; + private final float damage; + + public InterruptEvent(IPlayer player, Ability ability, EntityLivingBase target, DamageSource source, float damage) { + super(player, ability, target); + this.damageSource = source != null ? NpcAPI.Instance().getIDamageSource(source) : null; + this.damage = damage; + } + + @Override + public IDamageSource getDamageSource() { + return damageSource; + } + + @Override + public float getDamage() { + return damage; + } + + @Override + public String getHookName() { + return EnumScriptType.ABILITY_INTERRUPT.function; + } + } + + /** + * Fired when a player's ability completes its full execution cycle. + */ + public static class CompleteEvent extends PlayerAbilityEvent implements IPlayerAbilityEvent.CompleteEvent { + public CompleteEvent(IPlayer player, Ability ability, EntityLivingBase target) { + super(player, ability, target); + } + + @Override + public String getHookName() { + return EnumScriptType.ABILITY_COMPLETE.function; + } + } + + /** + * Fired every tick while a player's ability is executing. + */ + public static class TickEvent extends PlayerAbilityEvent implements IPlayerAbilityEvent.TickEvent { + private final int abilityPhase; + private final int tick; + + public TickEvent(IPlayer player, Ability ability, EntityLivingBase target, int phase, int tick) { + super(player, ability, target); + this.abilityPhase = phase; + this.tick = tick; + } + + @Override + public int getAbilityPhase() { + return abilityPhase; + } + + @Override + public int getTick() { + return tick; + } + + @Override + public String getHookName() { + return EnumScriptType.ABILITY_TICK.function; + } + } + /** * Fired when a player's ability hits an entity with damage. * Canceling prevents the damage from being applied.