mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-11 21:57:40 +00:00
Merge branch 'dev' into syntax-highlighter-unified
This commit is contained in:
commit
c0ce53a814
150 files changed
+3376
-1479
No files matched your search
+1
-1
@@ -6,7 +6,7 @@ plugins {
|
||||
id "dts.typescript-generator"
|
||||
}
|
||||
|
||||
version = "1.11-beta6.1"
|
||||
version = "1.11-beta7"
|
||||
group= "kamkeel.customnpc-plus"
|
||||
archivesBaseName = "CustomNPC-Plus"
|
||||
|
||||
|
||||
+1
-1
Submodule src/api updated: a9c77d20eb...658da549ee.
@@ -0,0 +1,38 @@
|
||||
package kamkeel.npcs.client.renderer;
|
||||
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
import kamkeel.npcs.controllers.data.energycharge.EnergyChargePreviewManager;
|
||||
import kamkeel.npcs.entity.EntityEnergyProjectile;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.entity.RenderManager;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraftforge.client.event.RenderWorldLastEvent;
|
||||
|
||||
/**
|
||||
* Renders packet-driven charging previews through RenderManager.
|
||||
*/
|
||||
public class EnergyChargePreviewRenderer {
|
||||
|
||||
@SubscribeEvent
|
||||
public void onRenderWorldLast(RenderWorldLastEvent event) {
|
||||
if (EnergyChargePreviewManager.ClientInstance == null || !EnergyChargePreviewManager.ClientInstance.hasPreviews()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Minecraft mc = Minecraft.getMinecraft();
|
||||
EntityPlayer player = mc.thePlayer;
|
||||
if (player == null) return;
|
||||
|
||||
float partialTicks = event.partialTicks;
|
||||
|
||||
for (EntityEnergyProjectile entity : EnergyChargePreviewManager.ClientInstance.getPreviews()) {
|
||||
if (entity == null || entity.isDead) continue;
|
||||
|
||||
double x = entity.prevPosX + (entity.posX - entity.prevPosX) * partialTicks - RenderManager.renderPosX;
|
||||
double y = entity.prevPosY + (entity.posY - entity.prevPosY) * partialTicks - RenderManager.renderPosY;
|
||||
double z = entity.prevPosZ + (entity.posZ - entity.prevPosZ) * partialTicks - RenderManager.renderPosZ;
|
||||
|
||||
RenderManager.instance.renderEntityWithPosYaw(entity, x, y, z, entity.rotationYaw, partialTicks);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,9 @@ public class RenderAbilityBeam extends RenderEnergyAbility {
|
||||
|
||||
@Override
|
||||
public void doRender(Entity entity, double x, double y, double z, float yaw, float partialTicks) {
|
||||
if (shouldSkipInitialActiveRender(entity)) {
|
||||
return;
|
||||
}
|
||||
EntityAbilityBeam beam = (EntityAbilityBeam) entity;
|
||||
|
||||
setupRenderState();
|
||||
|
||||
@@ -17,6 +17,9 @@ public class RenderAbilityDisc extends RenderEnergyAbility {
|
||||
|
||||
@Override
|
||||
public void doRender(Entity entity, double x, double y, double z, float yaw, float partialTicks) {
|
||||
if (shouldSkipInitialActiveRender(entity)) {
|
||||
return;
|
||||
}
|
||||
EntityAbilityDisc disc = (EntityAbilityDisc) entity;
|
||||
|
||||
setupRenderState();
|
||||
|
||||
@@ -17,6 +17,9 @@ public class RenderAbilityDome extends RenderEnergyBarrier {
|
||||
|
||||
@Override
|
||||
public void doRender(Entity entity, double x, double y, double z, float yaw, float partialTicks) {
|
||||
if (shouldSkipInitialActiveRender(entity)) {
|
||||
return;
|
||||
}
|
||||
EntityAbilityDome dome = (EntityAbilityDome) entity;
|
||||
float radius = dome.getDomeRadius();
|
||||
float healthPercent = dome.getHealthPercent();
|
||||
|
||||
@@ -23,6 +23,9 @@ public class RenderAbilityLaser extends RenderEnergyAbility {
|
||||
|
||||
@Override
|
||||
public void doRender(Entity entity, double x, double y, double z, float yaw, float partialTicks) {
|
||||
if (shouldSkipInitialActiveRender(entity)) {
|
||||
return;
|
||||
}
|
||||
EntityAbilityLaser laser = (EntityAbilityLaser) entity;
|
||||
|
||||
setupRenderState();
|
||||
|
||||
@@ -19,6 +19,10 @@ public class RenderAbilityOrb extends RenderEnergyAbility {
|
||||
public void doRender(Entity entity, double x, double y, double z, float yaw, float partialTicks) {
|
||||
EntityAbilityOrb orb = (EntityAbilityOrb) entity;
|
||||
|
||||
if (shouldSkipInitialActiveRender(entity)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setupRenderState();
|
||||
|
||||
GL11.glPushMatrix();
|
||||
|
||||
@@ -17,6 +17,9 @@ public class RenderAbilityPanel extends RenderEnergyBarrier {
|
||||
|
||||
@Override
|
||||
public void doRender(Entity entity, double x, double y, double z, float yaw, float partialTicks) {
|
||||
if (shouldSkipInitialActiveRender(entity)) {
|
||||
return;
|
||||
}
|
||||
EntityAbilityPanel panel = (EntityAbilityPanel) entity;
|
||||
|
||||
float width = panel.getPanelData().panelWidth;
|
||||
|
||||
@@ -22,11 +22,22 @@ import org.lwjgl.opengl.GL12;
|
||||
public abstract class RenderEnergyAbility extends Render {
|
||||
|
||||
protected static final ResourceLocation WHITE_TEXTURE = new ResourceLocation("customnpcs", "textures/entity/white.png");
|
||||
protected static final int HIDE_INITIAL_ACTIVE_TICKS = 1;
|
||||
|
||||
public RenderEnergyAbility() {
|
||||
this.shadowSize = 0.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the first active tick to avoid the visible spawn-frame pause at fire time.
|
||||
* Charging previews are never hidden.
|
||||
*/
|
||||
protected boolean shouldSkipInitialActiveRender(Entity entity) {
|
||||
if (!(entity instanceof EntityEnergyAbility)) return false;
|
||||
EntityEnergyAbility ability = (EntityEnergyAbility) entity;
|
||||
return !ability.isPreviewMode() && !ability.isCharging() && ability.ticksExisted <= HIDE_INITIAL_ACTIVE_TICKS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup GL state for translucent, self-illuminated rendering.
|
||||
* Forces full brightness so world lighting does not affect projectiles.
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
package kamkeel.npcs.client.renderer;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.entity.EntityEnergyExplosion;
|
||||
import net.minecraft.entity.Entity;
|
||||
import noppes.npcs.config.ConfigClient;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
/**
|
||||
* Voxel-style renderer for {@link EntityEnergyExplosion}.
|
||||
* Draws expanding cubic shells with a short fade-out.
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class RenderEnergyExplosion extends RenderEnergyAbility {
|
||||
|
||||
private static final float MAX_OUTER_ALPHA = 0.85f;
|
||||
private static final float MAX_INNER_ALPHA = 1.0f;
|
||||
private static final float MAX_SMOKE_CUBE_ALPHA = 0.55f;
|
||||
|
||||
@Override
|
||||
public void doRender(Entity entity, double x, double y, double z, float yaw, float partialTicks) {
|
||||
if (shouldSkipInitialActiveRender(entity)) return;
|
||||
if (!(entity instanceof EntityEnergyExplosion)) return;
|
||||
if (ConfigClient.LowResExplosion) return;
|
||||
EntityEnergyExplosion explosion = (EntityEnergyExplosion) entity;
|
||||
|
||||
setupRenderState();
|
||||
|
||||
GL11.glPushMatrix();
|
||||
GL11.glTranslated(x, y, z);
|
||||
|
||||
float radius = explosion.getInterpolatedRadius(partialTicks);
|
||||
float life = explosion.getLifeProgress(partialTicks);
|
||||
if (radius > 0.01f && life < 1.0f) {
|
||||
renderVoxelBurst(explosion, radius, life);
|
||||
if (explosion.hasLightningEffect()) {
|
||||
renderAttachedLightning(explosion, 0.55f, Math.max(0.7f, radius * 0.45f));
|
||||
}
|
||||
}
|
||||
|
||||
GL11.glPopMatrix();
|
||||
restoreRenderState();
|
||||
}
|
||||
|
||||
private void renderVoxelBurst(EntityEnergyExplosion explosion, float radius, float life) {
|
||||
int outerColor = explosion.getOuterColor();
|
||||
int innerColor = explosion.getInnerColor();
|
||||
boolean hasOuter = explosion.isOuterColorEnabled();
|
||||
|
||||
float fade = 1.0f - life;
|
||||
float outerAlpha = Math.min(MAX_OUTER_ALPHA, (0.25f + fade * 0.75f) * explosion.getOuterColorAlpha());
|
||||
float innerAlpha = Math.min(MAX_INNER_ALPHA, (0.30f + fade * 0.80f) * explosion.getInnerAlpha());
|
||||
|
||||
int steps = Math.max(2, Math.min(6, (int) Math.ceil(radius * 0.65f) + 1));
|
||||
float cell = radius / steps;
|
||||
float cubeHalf = Math.max(0.04f, cell * 0.22f * (0.90f - life * 0.25f));
|
||||
long seed = explosion.getRenderSeed();
|
||||
|
||||
GL11.glDepthMask(false);
|
||||
renderSmokeVoxelShell(radius, life, seed);
|
||||
|
||||
for (int ix = -steps; ix <= steps; ix++) {
|
||||
for (int iy = -steps; iy <= steps; iy++) {
|
||||
for (int iz = -steps; iz <= steps; iz++) {
|
||||
int edge = Math.max(Math.abs(ix), Math.max(Math.abs(iy), Math.abs(iz)));
|
||||
if (edge != steps) continue;
|
||||
if (!shouldRenderVoxel(ix, iy, iz, steps, seed, life)) continue;
|
||||
|
||||
float px = ix * cell + jitter(ix, iy, iz, seed, 1) * cell * 0.18f;
|
||||
float py = iy * cell + jitter(ix, iy, iz, seed, 2) * cell * 0.18f;
|
||||
float pz = iz * cell + jitter(ix, iy, iz, seed, 3) * cell * 0.18f;
|
||||
|
||||
GL11.glPushMatrix();
|
||||
GL11.glTranslatef(px, py, pz);
|
||||
|
||||
if (hasOuter) {
|
||||
GL11.glPushMatrix();
|
||||
float outerScale = 1.0f + explosion.getOuterColorWidth() * 0.35f;
|
||||
GL11.glScalef(outerScale, outerScale, outerScale);
|
||||
renderCube(outerColor, outerAlpha * fade, cubeHalf);
|
||||
GL11.glPopMatrix();
|
||||
}
|
||||
|
||||
renderCube(innerColor, innerAlpha * fade, cubeHalf * 0.9f);
|
||||
GL11.glPopMatrix();
|
||||
}
|
||||
}
|
||||
}
|
||||
renderCoreFlash(innerColor, innerAlpha, radius, cubeHalf, life);
|
||||
GL11.glDepthMask(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds large translucent white/gray cubes to fake a smoky volumetric expansion.
|
||||
*/
|
||||
private void renderSmokeVoxelShell(float radius, float life, long seed) {
|
||||
int smokeSteps = Math.max(2, Math.min(5, (int) Math.ceil(radius * 0.5f) + 1));
|
||||
float smokeCell = radius / smokeSteps * 1.2f;
|
||||
float smokeJitter = smokeCell * 0.28f;
|
||||
float smokeCubeHalf = Math.max(0.07f, smokeCell * 0.24f * (0.95f - life * 0.25f));
|
||||
float smokeAlpha = Math.min(MAX_SMOKE_CUBE_ALPHA, (0.15f + (1.0f - life) * 0.55f));
|
||||
|
||||
for (int ix = -smokeSteps; ix <= smokeSteps; ix++) {
|
||||
for (int iy = -smokeSteps; iy <= smokeSteps; iy++) {
|
||||
for (int iz = -smokeSteps; iz <= smokeSteps; iz++) {
|
||||
int edge = Math.max(Math.abs(ix), Math.max(Math.abs(iy), Math.abs(iz)));
|
||||
if (edge < smokeSteps - 1) continue;
|
||||
|
||||
int h = hash(ix * 5, iy * 7, iz * 11, seed ^ 0x4f1bbcdcL);
|
||||
if ((h & 3) != 0) continue;
|
||||
if (life > 0.72f && ((h >>> 5) & 7) > 2) continue;
|
||||
|
||||
float px = ix * smokeCell + jitter(ix, iy, iz, seed ^ 0x2299aa11L, 1) * smokeJitter;
|
||||
float py = iy * smokeCell + jitter(ix, iy, iz, seed ^ 0x2299aa11L, 2) * smokeJitter;
|
||||
float pz = iz * smokeCell + jitter(ix, iy, iz, seed ^ 0x2299aa11L, 3) * smokeJitter;
|
||||
|
||||
int smokeColor = ((h >>> 9) & 1) == 0 ? 0xFFFFFF : 0xDCDCDC;
|
||||
float alpha = smokeAlpha * (0.78f + (((h >>> 10) & 15) / 15.0f) * 0.22f);
|
||||
|
||||
GL11.glPushMatrix();
|
||||
GL11.glTranslatef(px, py, pz);
|
||||
renderCube(smokeColor, alpha, smokeCubeHalf);
|
||||
GL11.glPopMatrix();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* White-hot core flash pass for the initial frames.
|
||||
*/
|
||||
private void renderCoreFlash(int innerColor, float innerAlpha, float radius, float cubeHalf, float life) {
|
||||
float flash = saturate((0.60f - life) / 0.60f);
|
||||
if (flash <= 0.0f) return;
|
||||
|
||||
float coreHalf = Math.max(cubeHalf, radius * 0.07f) * (1.0f + flash * 0.55f);
|
||||
float whiteAlpha = Math.min(1.0f, 0.25f + flash * 0.95f);
|
||||
float innerFlashAlpha = Math.min(1.0f, innerAlpha * (0.45f + flash * 0.95f));
|
||||
|
||||
GL11.glPushMatrix();
|
||||
renderCube(0xFFFFFF, whiteAlpha, coreHalf * 1.45f);
|
||||
GL11.glScalef(0.82f, 0.82f, 0.82f);
|
||||
renderCube(innerColor, innerFlashAlpha, coreHalf);
|
||||
GL11.glPopMatrix();
|
||||
}
|
||||
|
||||
private float saturate(float value) {
|
||||
if (value < 0.0f) return 0.0f;
|
||||
if (value > 1.0f) return 1.0f;
|
||||
return value;
|
||||
}
|
||||
|
||||
private boolean shouldRenderVoxel(int ix, int iy, int iz, int steps, long seed, float life) {
|
||||
int h = hash(ix, iy, iz, seed);
|
||||
|
||||
int keepMask = steps >= 5 ? 3 : 1;
|
||||
if ((h & keepMask) != 0) return false;
|
||||
|
||||
if (life > 0.55f) {
|
||||
int decay = (h >>> 3) & 7;
|
||||
if (decay > 2) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private float jitter(int ix, int iy, int iz, long seed, int channel) {
|
||||
int h = hash(ix + channel * 17, iy - channel * 31, iz + channel * 47, seed);
|
||||
return (((h >>> 8) & 1023) / 1023.0f) - 0.5f;
|
||||
}
|
||||
|
||||
private int hash(int x, int y, int z, long seed) {
|
||||
long h = seed;
|
||||
h ^= (long) x * 73428767L;
|
||||
h ^= (long) y * 912931L;
|
||||
h ^= (long) z * 19349663L;
|
||||
h ^= (h >>> 33);
|
||||
h *= 0xff51afd7ed558ccdL;
|
||||
h ^= (h >>> 33);
|
||||
return (int) h;
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,9 @@ public class RenderEnergySlicer extends RenderEnergyAbility {
|
||||
|
||||
@Override
|
||||
public void doRender(Entity entity, double x, double y, double z, float yaw, float partialTicks) {
|
||||
if (shouldSkipInitialActiveRender(entity)) {
|
||||
return;
|
||||
}
|
||||
EntityEnergySlicer slicer = (EntityEnergySlicer) entity;
|
||||
|
||||
float width = slicer.getSliceWidth();
|
||||
|
||||
@@ -2,8 +2,8 @@ package kamkeel.npcs.command;
|
||||
|
||||
import kamkeel.npcs.controllers.AbilityController;
|
||||
import kamkeel.npcs.controllers.data.ability.Ability;
|
||||
import kamkeel.npcs.controllers.data.ability.ChainedAbility;
|
||||
import kamkeel.npcs.controllers.data.ability.UserType;
|
||||
import kamkeel.npcs.controllers.data.ability.data.ChainedAbility;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.UserType;
|
||||
import net.minecraft.command.ICommandSender;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import noppes.npcs.NoppesUtilServer;
|
||||
|
||||
@@ -1,12 +1,22 @@
|
||||
package kamkeel.npcs.controllers;
|
||||
|
||||
import kamkeel.npcs.controllers.data.ability.*;
|
||||
import kamkeel.npcs.controllers.data.ability.conditions.AbilityCondition;
|
||||
import kamkeel.npcs.controllers.data.ability.conditions.ConditionHPThreshold;
|
||||
import kamkeel.npcs.controllers.data.ability.conditions.ConditionHitCount;
|
||||
import kamkeel.npcs.controllers.data.ability.conditions.ConditionItem;
|
||||
import kamkeel.npcs.controllers.data.ability.conditions.ConditionHasEffect;
|
||||
import kamkeel.npcs.controllers.data.ability.conditions.ConditionQuestCompleted;
|
||||
import kamkeel.npcs.controllers.data.ability.Ability;
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityVariant;
|
||||
import kamkeel.npcs.controllers.data.ability.data.IAbilityAction;
|
||||
import kamkeel.npcs.controllers.data.ability.data.effect.IEffectAction;
|
||||
import kamkeel.npcs.controllers.data.ability.data.ChainedAbility;
|
||||
import kamkeel.npcs.controllers.data.ability.data.entry.ChainedAbilityEntry;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.AbilityPhase;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.UserType;
|
||||
import kamkeel.npcs.controllers.data.ability.extender.IAbilityExtender;
|
||||
import kamkeel.npcs.controllers.data.ability.gui.IAbilityFieldProvider;
|
||||
import kamkeel.npcs.controllers.data.ability.gui.IChainedAbilityFieldProvider;
|
||||
import kamkeel.npcs.controllers.data.ability.type.AbilityCharge;
|
||||
import kamkeel.npcs.controllers.data.ability.type.AbilityCutter;
|
||||
import kamkeel.npcs.controllers.data.ability.type.AbilityDash;
|
||||
@@ -14,7 +24,6 @@ import kamkeel.npcs.controllers.data.ability.type.AbilityGuard;
|
||||
import kamkeel.npcs.controllers.data.ability.type.AbilityHazard;
|
||||
import kamkeel.npcs.controllers.data.ability.type.AbilityEffect;
|
||||
import kamkeel.npcs.controllers.data.ability.type.AbilityHeavyHit;
|
||||
import kamkeel.npcs.controllers.data.ability.type.AbilityProjectile;
|
||||
import kamkeel.npcs.controllers.data.ability.type.AbilityShockwave;
|
||||
import kamkeel.npcs.controllers.data.ability.type.AbilitySlam;
|
||||
import kamkeel.npcs.controllers.data.ability.type.AbilityTeleport;
|
||||
@@ -26,7 +35,6 @@ import kamkeel.npcs.controllers.data.ability.type.energy.AbilityDome;
|
||||
import kamkeel.npcs.controllers.data.ability.type.energy.AbilityLaserShot;
|
||||
import kamkeel.npcs.controllers.data.ability.type.energy.AbilityOrb;
|
||||
import kamkeel.npcs.controllers.data.ability.type.energy.AbilityShield;
|
||||
import kamkeel.npcs.controllers.data.ability.type.energy.AbilitySlicer;
|
||||
import kamkeel.npcs.controllers.data.ability.type.energy.AbilitySweeper;
|
||||
import kamkeel.npcs.controllers.data.ability.type.energy.AbilityWall;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
@@ -38,6 +46,7 @@ import noppes.npcs.api.handler.IAbilityHandler;
|
||||
import noppes.npcs.controllers.PlayerDataController;
|
||||
import noppes.npcs.controllers.data.PlayerData;
|
||||
import noppes.npcs.util.NBTJsonUtil;
|
||||
import kamkeel.npcs.util.FileNameHelper;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
@@ -206,6 +215,7 @@ public class AbilityController implements IAbilityHandler {
|
||||
customAbilities.clear();
|
||||
customAbilitiesById.clear();
|
||||
int migrated = 0;
|
||||
Map<String, String> migratedAbilityNames = new HashMap<String, String>();
|
||||
|
||||
File dir = getDir();
|
||||
File[] files = dir.exists() ? dir.listFiles() : null;
|
||||
@@ -214,7 +224,7 @@ public class AbilityController implements IAbilityHandler {
|
||||
if (!file.isFile() || !file.getName().endsWith(".json")) continue;
|
||||
try {
|
||||
String filename = file.getName();
|
||||
String key = filename.substring(0, filename.length() - 5);
|
||||
String fileKey = filename.substring(0, filename.length() - 5);
|
||||
|
||||
NBTTagCompound nbt = NBTJsonUtil.LoadFile(file);
|
||||
Ability ability = fromNBT(nbt);
|
||||
@@ -227,48 +237,55 @@ public class AbilityController implements IAbilityHandler {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Legacy migration: UUID-named files get renamed to name-based
|
||||
if (UUID_PATTERN.matcher(key).matches()) {
|
||||
String name = ability.getName();
|
||||
if (name == null || name.isEmpty()) {
|
||||
name = key; // Fallback to UUID if no name stored
|
||||
}
|
||||
String storedName = ability.getName();
|
||||
String preferredName;
|
||||
if (UUID_PATTERN.matcher(fileKey).matches()) {
|
||||
preferredName = (storedName != null && !storedName.isEmpty()) ? storedName : fileKey;
|
||||
} else {
|
||||
preferredName = fileKey;
|
||||
}
|
||||
String name = FileNameHelper.sanitizeName(preferredName, "Ability");
|
||||
name = makeUniqueNameForLoad(customAbilities, dir, file, name, fileKey);
|
||||
|
||||
// Ensure unique name
|
||||
while (hasCustomAbilityName(name) || nameFileExists(dir, name)) {
|
||||
name = name + "_";
|
||||
}
|
||||
ability.setName(name);
|
||||
|
||||
// Rename file from UUID.json to name.json
|
||||
File newFile = new File(dir, name + ".json");
|
||||
if (file.renameTo(newFile)) {
|
||||
LogWriter.info("Migrated ability file: " + key + ".json -> " + name + ".json");
|
||||
boolean dirty = false;
|
||||
if (!name.equals(fileKey)) {
|
||||
File renamedFile = new File(dir, name + ".json");
|
||||
if (file.renameTo(renamedFile)) {
|
||||
LogWriter.info("Migrated ability file: " + fileKey + ".json -> " + name + ".json");
|
||||
file = renamedFile;
|
||||
migrated++;
|
||||
dirty = true;
|
||||
} else {
|
||||
LogWriter.error("Failed to rename ability file: " + key + ".json -> " + name + ".json");
|
||||
LogWriter.error("Failed to rename ability file: " + fileKey + ".json -> " + name + ".json");
|
||||
}
|
||||
}
|
||||
|
||||
key = name;
|
||||
migrated++;
|
||||
ability.setName(name);
|
||||
if (storedName != null && !storedName.equals(name)) {
|
||||
dirty = true;
|
||||
if (!storedName.isEmpty()) {
|
||||
migratedAbilityNames.put(storedName, name);
|
||||
}
|
||||
}
|
||||
if (!fileKey.equals(name)) {
|
||||
migratedAbilityNames.put(fileKey, name);
|
||||
}
|
||||
|
||||
// Ensure ability has a UUID; generate one for legacy abilities
|
||||
String uuid = ability.getId();
|
||||
if (uuid == null || uuid.isEmpty() || uuid.equals(key)) {
|
||||
if (uuid == null || uuid.isEmpty() || uuid.equals(fileKey) || uuid.equals(name)) {
|
||||
// Legacy: id was the name or empty — generate a real UUID
|
||||
uuid = UUID.randomUUID().toString();
|
||||
ability.setId(uuid);
|
||||
// Re-save file with the new UUID
|
||||
try {
|
||||
NBTJsonUtil.SaveFile(file, ability.writeNBT());
|
||||
} catch (Exception ex) {
|
||||
LogWriter.error("Failed to save UUID for ability: " + key, ex);
|
||||
}
|
||||
migrated++;
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
ability.setName(key);
|
||||
customAbilities.put(key, ability);
|
||||
if (dirty) {
|
||||
NBTJsonUtil.SaveFile(file, ability.writeNBT());
|
||||
}
|
||||
|
||||
customAbilities.put(name, ability);
|
||||
customAbilitiesById.put(uuid, ability);
|
||||
} catch (Exception e) {
|
||||
LogWriter.error("Error loading custom ability: " + file.getAbsolutePath(), e);
|
||||
@@ -278,23 +295,68 @@ public class AbilityController implements IAbilityHandler {
|
||||
|
||||
customAbilityRevision++;
|
||||
if (migrated > 0) {
|
||||
LogWriter.info("Migrated " + migrated + " abilities (UUID-named files or missing UUIDs)");
|
||||
LogWriter.info("Migrated " + migrated + " abilities (filename normalization / UUID repair)");
|
||||
}
|
||||
LogWriter.info("Loaded " + customAbilities.size() + " custom abilities");
|
||||
|
||||
loadChainedAbilities();
|
||||
loadChainedAbilities(migratedAbilityNames);
|
||||
}
|
||||
|
||||
private boolean nameFileExists(File dir, String name) {
|
||||
return new File(dir, name + ".json").exists();
|
||||
private boolean nameFileExists(File dir, String name, File ignoredFile) {
|
||||
File existing = new File(dir, name + ".json");
|
||||
if (!existing.exists()) return false;
|
||||
return ignoredFile == null || !existing.getAbsolutePath().equals(ignoredFile.getAbsolutePath());
|
||||
}
|
||||
|
||||
private String makeUniqueNameForLoad(Map<String, ?> loadedMap, File dir, File currentFile, String baseName, String selfKey) {
|
||||
String candidate = baseName;
|
||||
while ((loadedMap.containsKey(candidate) && !candidate.equals(selfKey))
|
||||
|| nameFileExists(dir, candidate, currentFile)) {
|
||||
candidate = candidate + "_";
|
||||
}
|
||||
return candidate;
|
||||
}
|
||||
|
||||
private String makeUniqueNameForMap(Map<String, ?> map, String baseName, String selfName) {
|
||||
String candidate = baseName;
|
||||
while (map.containsKey(candidate) && (selfName == null || !candidate.equals(selfName))) {
|
||||
candidate = candidate + "_";
|
||||
}
|
||||
return candidate;
|
||||
}
|
||||
|
||||
private boolean remapChainEntryReferences(ChainedAbility chain, Map<String, String> migratedAbilityNames) {
|
||||
if (chain == null || migratedAbilityNames == null || migratedAbilityNames.isEmpty()) return false;
|
||||
boolean changed = false;
|
||||
for (ChainedAbilityEntry entry : chain.getEntries()) {
|
||||
if (entry == null || !entry.isReference()) continue;
|
||||
String ref = entry.getAbilityReference();
|
||||
if (ref == null || ref.isEmpty()) continue;
|
||||
|
||||
String mapped = migratedAbilityNames.get(ref);
|
||||
if (mapped == null) {
|
||||
for (Map.Entry<String, String> e : migratedAbilityNames.entrySet()) {
|
||||
if (e.getKey().equalsIgnoreCase(ref)) {
|
||||
mapped = e.getValue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mapped != null && !mapped.equals(ref)) {
|
||||
entry.setAbilityReference(mapped);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
public boolean saveCustomAbility(Ability ability) {
|
||||
if (ability == null) return false;
|
||||
if (ability.isBuiltIn()) return false;
|
||||
|
||||
String name = ability.getName();
|
||||
if (name == null || name.isEmpty()) return false;
|
||||
String name = FileNameHelper.sanitizeName(ability.getName(), "Ability");
|
||||
ability.setName(name);
|
||||
|
||||
String uuid = ability.getId();
|
||||
boolean isNew = (uuid == null || uuid.isEmpty());
|
||||
@@ -305,26 +367,17 @@ public class AbilityController implements IAbilityHandler {
|
||||
ability.setId(uuid);
|
||||
|
||||
// Ensure unique name
|
||||
while (hasCustomAbilityName(name)) {
|
||||
name = name + "_";
|
||||
}
|
||||
name = makeUniqueNameForMap(customAbilities, name, null);
|
||||
ability.setName(name);
|
||||
} else {
|
||||
// Existing ability — check for rename via UUID lookup
|
||||
Ability existing = customAbilitiesById.get(uuid);
|
||||
if (existing != null) {
|
||||
String oldName = existing.getName();
|
||||
if (!oldName.equals(name)) {
|
||||
// Name changed! Ensure new name is unique (excluding self)
|
||||
String testName = name;
|
||||
while (customAbilities.containsKey(testName) && !testName.equals(oldName)) {
|
||||
testName = testName + "_";
|
||||
}
|
||||
if (!testName.equals(name)) {
|
||||
name = testName;
|
||||
ability.setName(name);
|
||||
}
|
||||
name = makeUniqueNameForMap(customAbilities, name, oldName);
|
||||
ability.setName(name);
|
||||
|
||||
if (!oldName.equals(name)) {
|
||||
// Delete old file and remove old map entry
|
||||
File oldFile = new File(getDir(), oldName + ".json");
|
||||
if (oldFile.exists()) {
|
||||
@@ -332,6 +385,9 @@ public class AbilityController implements IAbilityHandler {
|
||||
}
|
||||
customAbilities.remove(oldName);
|
||||
}
|
||||
} else {
|
||||
name = makeUniqueNameForMap(customAbilities, name, null);
|
||||
ability.setName(name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,6 +423,15 @@ public class AbilityController implements IAbilityHandler {
|
||||
if (name == null || name.isEmpty()) return false;
|
||||
|
||||
Ability removed = customAbilities.remove(name);
|
||||
if (removed == null) {
|
||||
String sanitized = FileNameHelper.sanitizeTextInput(name);
|
||||
if (!sanitized.equals(name)) {
|
||||
removed = customAbilities.remove(sanitized);
|
||||
if (removed != null) {
|
||||
name = sanitized;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (removed == null) return false;
|
||||
|
||||
// Also remove from UUID index
|
||||
@@ -405,7 +470,13 @@ public class AbilityController implements IAbilityHandler {
|
||||
}
|
||||
|
||||
public Ability getCustomAbility(String name) {
|
||||
return customAbilities.get(name);
|
||||
Ability ability = customAbilities.get(name);
|
||||
if (ability != null) return ability;
|
||||
String sanitized = FileNameHelper.sanitizeTextInput(name);
|
||||
if (!sanitized.equals(name)) {
|
||||
return customAbilities.get(sanitized);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Set<String> getCustomAbilityNames() {
|
||||
@@ -445,9 +516,10 @@ public class AbilityController implements IAbilityHandler {
|
||||
// CHAINED ABILITIES
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
private void loadChainedAbilities() {
|
||||
private void loadChainedAbilities(Map<String, String> migratedAbilityNames) {
|
||||
chainedAbilities.clear();
|
||||
chainedAbilitiesById.clear();
|
||||
int migrated = 0;
|
||||
|
||||
File dir = getChainedDir();
|
||||
File[] files = dir.exists() ? dir.listFiles() : null;
|
||||
@@ -456,19 +528,51 @@ public class AbilityController implements IAbilityHandler {
|
||||
if (!file.isFile() || !file.getName().endsWith(".json")) continue;
|
||||
try {
|
||||
String filename = file.getName();
|
||||
String key = filename.substring(0, filename.length() - 5);
|
||||
String fileKey = filename.substring(0, filename.length() - 5);
|
||||
|
||||
NBTTagCompound nbt = NBTJsonUtil.LoadFile(file);
|
||||
ChainedAbility chain = new ChainedAbility();
|
||||
chain.readNBT(nbt);
|
||||
|
||||
chain.setName(key);
|
||||
chainedAbilities.put(key, chain);
|
||||
String storedName = chain.getName();
|
||||
String name = FileNameHelper.sanitizeName(fileKey, "Chain");
|
||||
name = makeUniqueNameForLoad(chainedAbilities, dir, file, name, fileKey);
|
||||
|
||||
boolean dirty = false;
|
||||
if (!name.equals(fileKey)) {
|
||||
File renamedFile = new File(dir, name + ".json");
|
||||
if (file.renameTo(renamedFile)) {
|
||||
LogWriter.info("Migrated chained ability file: " + fileKey + ".json -> " + name + ".json");
|
||||
file = renamedFile;
|
||||
dirty = true;
|
||||
} else {
|
||||
LogWriter.error("Failed to rename chained ability file: " + fileKey + ".json -> " + name + ".json");
|
||||
}
|
||||
}
|
||||
|
||||
chain.setName(name);
|
||||
if (storedName != null && !storedName.equals(name)) {
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
if (remapChainEntryReferences(chain, migratedAbilityNames)) {
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
String uuid = chain.getId();
|
||||
if (uuid != null && !uuid.isEmpty()) {
|
||||
chainedAbilitiesById.put(uuid, chain);
|
||||
if (uuid == null || uuid.isEmpty() || uuid.equals(fileKey) || uuid.equals(name)) {
|
||||
uuid = UUID.randomUUID().toString();
|
||||
chain.setId(uuid);
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
if (dirty) {
|
||||
NBTJsonUtil.SaveFile(file, chain.writeNBT());
|
||||
migrated++;
|
||||
}
|
||||
|
||||
chainedAbilities.put(name, chain);
|
||||
chainedAbilitiesById.put(uuid, chain);
|
||||
} catch (Exception e) {
|
||||
LogWriter.error("Error loading chained ability: " + file.getAbsolutePath(), e);
|
||||
}
|
||||
@@ -476,14 +580,17 @@ public class AbilityController implements IAbilityHandler {
|
||||
}
|
||||
|
||||
chainedAbilityRevision++;
|
||||
if (migrated > 0) {
|
||||
LogWriter.info("Migrated " + migrated + " chained abilities");
|
||||
}
|
||||
LogWriter.info("Loaded " + chainedAbilities.size() + " chained abilities");
|
||||
}
|
||||
|
||||
public boolean saveChainedAbility(ChainedAbility chain) {
|
||||
if (chain == null) return false;
|
||||
|
||||
String name = chain.getName();
|
||||
if (name == null || name.isEmpty()) return false;
|
||||
String name = FileNameHelper.sanitizeName(chain.getName(), "Chain");
|
||||
chain.setName(name);
|
||||
|
||||
String uuid = chain.getId();
|
||||
boolean isNew = (uuid == null || uuid.isEmpty());
|
||||
@@ -492,30 +599,25 @@ public class AbilityController implements IAbilityHandler {
|
||||
uuid = UUID.randomUUID().toString();
|
||||
chain.setId(uuid);
|
||||
|
||||
while (hasChainedAbilityName(name)) {
|
||||
name = name + "_";
|
||||
}
|
||||
name = makeUniqueNameForMap(chainedAbilities, name, null);
|
||||
chain.setName(name);
|
||||
} else {
|
||||
ChainedAbility existing = chainedAbilitiesById.get(uuid);
|
||||
if (existing != null) {
|
||||
String oldName = existing.getName();
|
||||
if (!oldName.equals(name)) {
|
||||
String testName = name;
|
||||
while (chainedAbilities.containsKey(testName) && !testName.equals(oldName)) {
|
||||
testName = testName + "_";
|
||||
}
|
||||
if (!testName.equals(name)) {
|
||||
name = testName;
|
||||
chain.setName(name);
|
||||
}
|
||||
name = makeUniqueNameForMap(chainedAbilities, name, oldName);
|
||||
chain.setName(name);
|
||||
|
||||
if (!oldName.equals(name)) {
|
||||
File oldFile = new File(getChainedDir(), oldName + ".json");
|
||||
if (oldFile.exists()) {
|
||||
oldFile.delete();
|
||||
}
|
||||
chainedAbilities.remove(oldName);
|
||||
}
|
||||
} else {
|
||||
name = makeUniqueNameForMap(chainedAbilities, name, null);
|
||||
chain.setName(name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -551,6 +653,15 @@ public class AbilityController implements IAbilityHandler {
|
||||
if (name == null || name.isEmpty()) return false;
|
||||
|
||||
ChainedAbility removed = chainedAbilities.remove(name);
|
||||
if (removed == null) {
|
||||
String sanitized = FileNameHelper.sanitizeTextInput(name);
|
||||
if (!sanitized.equals(name)) {
|
||||
removed = chainedAbilities.remove(sanitized);
|
||||
if (removed != null) {
|
||||
name = sanitized;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (removed == null) return false;
|
||||
|
||||
String uuid = removed.getId();
|
||||
@@ -601,6 +712,12 @@ public class AbilityController implements IAbilityHandler {
|
||||
}
|
||||
}
|
||||
|
||||
String sanitizedKey = FileNameHelper.sanitizeTextInput(key);
|
||||
if (!sanitizedKey.equals(key)) {
|
||||
ChainedAbility sanitized = chainedAbilities.get(sanitizedKey);
|
||||
if (sanitized != null) return sanitized.deepCopy();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -611,12 +728,20 @@ public class AbilityController implements IAbilityHandler {
|
||||
for (String name : chainedAbilities.keySet()) {
|
||||
if (name.equalsIgnoreCase(key)) return true;
|
||||
}
|
||||
String sanitizedKey = FileNameHelper.sanitizeTextInput(key);
|
||||
if (!sanitizedKey.equals(key) && chainedAbilities.containsKey(sanitizedKey)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChainedAbility getChainedAbility(String name) {
|
||||
ChainedAbility chain = chainedAbilities.get(name);
|
||||
if (chain == null) {
|
||||
String sanitized = FileNameHelper.sanitizeTextInput(name);
|
||||
if (!sanitized.equals(name)) {
|
||||
chain = chainedAbilities.get(sanitized);
|
||||
}
|
||||
}
|
||||
return chain != null ? chain.deepCopy() : null;
|
||||
}
|
||||
|
||||
@@ -639,7 +764,9 @@ public class AbilityController implements IAbilityHandler {
|
||||
|
||||
@Override
|
||||
public boolean hasChainedAbilityName(String name) {
|
||||
return chainedAbilities.containsKey(name);
|
||||
if (chainedAbilities.containsKey(name)) return true;
|
||||
String sanitized = FileNameHelper.sanitizeTextInput(name);
|
||||
return !sanitized.equals(name) && chainedAbilities.containsKey(sanitized);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -727,6 +854,12 @@ public class AbilityController implements IAbilityHandler {
|
||||
}
|
||||
}
|
||||
|
||||
String sanitizedKey = FileNameHelper.sanitizeTextInput(key);
|
||||
if (!sanitizedKey.equals(key)) {
|
||||
Ability sanitized = customAbilities.get(sanitizedKey);
|
||||
if (sanitized != null) return sanitized.deepCopy();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -771,8 +904,14 @@ public class AbilityController implements IAbilityHandler {
|
||||
}
|
||||
|
||||
public boolean hasAbility(String key) {
|
||||
return builtAbilities.containsKey(key) || customAbilities.containsKey(key)
|
||||
|| customAbilitiesById.containsKey(key);
|
||||
if (builtAbilities.containsKey(key) || customAbilities.containsKey(key)
|
||||
|| customAbilitiesById.containsKey(key)) {
|
||||
return true;
|
||||
}
|
||||
String sanitized = FileNameHelper.sanitizeTextInput(key);
|
||||
return !sanitized.equals(key)
|
||||
&& (builtAbilities.containsKey(sanitized) || customAbilities.containsKey(sanitized)
|
||||
|| customAbilitiesById.containsKey(sanitized));
|
||||
}
|
||||
|
||||
public Ability getCustomAbilityByUUID(String uuid) {
|
||||
@@ -780,7 +919,13 @@ public class AbilityController implements IAbilityHandler {
|
||||
}
|
||||
|
||||
public Ability getCustomAbilityByName(String name) {
|
||||
return customAbilities.get(name);
|
||||
Ability ability = customAbilities.get(name);
|
||||
if (ability != null) return ability;
|
||||
String sanitized = FileNameHelper.sanitizeTextInput(name);
|
||||
if (!sanitized.equals(name)) {
|
||||
return customAbilities.get(sanitized);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -814,6 +959,9 @@ public class AbilityController implements IAbilityHandler {
|
||||
if (ability.getName() != null && ability.getName().equalsIgnoreCase(key)) return true;
|
||||
}
|
||||
|
||||
String sanitizedKey = FileNameHelper.sanitizeTextInput(key);
|
||||
if (!sanitizedKey.equals(key) && customAbilities.containsKey(sanitizedKey)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1121,7 +1269,9 @@ public class AbilityController implements IAbilityHandler {
|
||||
|
||||
@Override
|
||||
public boolean hasCustomAbilityName(String name) {
|
||||
return customAbilities.containsKey(name);
|
||||
if (customAbilities.containsKey(name)) return true;
|
||||
String sanitized = FileNameHelper.sanitizeTextInput(name);
|
||||
return !sanitized.equals(name) && customAbilities.containsKey(sanitized);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -832,15 +832,6 @@ public class ProfileController implements IProfileHandler {
|
||||
else
|
||||
compound = (NBTTagCompound) compound.copy();
|
||||
playerData.setNBT(compound);
|
||||
|
||||
// Trade data is shared across all profile slots - copy from player's live data
|
||||
PlayerData liveData = PlayerData.get(player);
|
||||
if (liveData != null) {
|
||||
NBTTagCompound tradeCompound = new NBTTagCompound();
|
||||
liveData.tradeData.writeToNBT(tradeCompound);
|
||||
playerData.tradeData.readFromNBT(tradeCompound);
|
||||
}
|
||||
|
||||
return playerData;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import kamkeel.npcs.addon.DBCAddon;
|
||||
import kamkeel.npcs.controllers.data.ability.Ability;
|
||||
import kamkeel.npcs.controllers.data.ability.ChainedAbility;
|
||||
import kamkeel.npcs.controllers.data.ability.data.ChainedAbility;
|
||||
import kamkeel.npcs.network.LargeAbstractPacket;
|
||||
import kamkeel.npcs.network.PacketHandler;
|
||||
import kamkeel.npcs.network.enums.EnumSyncAction;
|
||||
|
||||
@@ -3,6 +3,18 @@ package kamkeel.npcs.controllers.data.ability;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.AbilityController;
|
||||
import kamkeel.npcs.controllers.data.ability.data.AbilityIconData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.IAbilityAction;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.AbilityPhase;
|
||||
import kamkeel.npcs.controllers.data.ability.data.effect.AbilityPotionEffect;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.InvulnerableMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.LockMode;
|
||||
import kamkeel.npcs.controllers.data.ability.preview.PreviewEntityHandler;
|
||||
import kamkeel.npcs.controllers.data.ability.util.AbilityTargetHelper;
|
||||
import kamkeel.npcs.controllers.data.ability.gui.IAbilityFieldProvider;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.RotationMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.UserType;
|
||||
import kamkeel.npcs.controllers.data.ability.conditions.AbilityCondition;
|
||||
import kamkeel.npcs.controllers.data.telegraph.Telegraph;
|
||||
import kamkeel.npcs.controllers.data.telegraph.TelegraphInstance;
|
||||
@@ -10,6 +22,7 @@ import kamkeel.npcs.controllers.data.telegraph.TelegraphType;
|
||||
import kamkeel.npcs.entity.EntityAbilityBarrier;
|
||||
import kamkeel.npcs.entity.EntityAbilityDome;
|
||||
import kamkeel.npcs.entity.EntityAbilityPanel;
|
||||
import kamkeel.npcs.util.FileNameHelper;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
@@ -74,11 +87,12 @@ public abstract class Ability implements IAbility, IAbilityAction {
|
||||
|
||||
// Interruption
|
||||
protected boolean interruptible = true;
|
||||
protected InvulnerableMode invulnerableMode = InvulnerableMode.NONE;
|
||||
|
||||
// Feedback
|
||||
protected LockMovementType lockMovement = LockMovementType.WINDUP;
|
||||
protected LockMode lockMovement = LockMode.WINDUP;
|
||||
protected RotationMode rotationMode = RotationMode.FREE;
|
||||
protected LockMovementType rotationPhase = LockMovementType.WINDUP_AND_ACTIVE;
|
||||
protected LockMode rotationPhase = LockMode.WINDUP_AND_ACTIVE;
|
||||
protected int windUpColor = 0x80FF4400; // Telegraph color during wind up
|
||||
protected int activeColor = 0xC0FF0000; // Telegraph warning/active color
|
||||
|
||||
@@ -637,10 +651,10 @@ public abstract class Ability implements IAbility, IAbilityAction {
|
||||
.tab("General").hover("ability.hover.freeOnCast"));
|
||||
}
|
||||
defs.add(FieldDef.section("ability.section.movement").tab("General"));
|
||||
defs.add(FieldDef.stringEnumField("ability.lockMovement", LockMovementType.getDisplayKeys(),
|
||||
defs.add(FieldDef.stringEnumField("ability.lockMovement", LockMode.getDisplayKeys(),
|
||||
() -> this.getLockMovement().getDisplayKey(),
|
||||
v -> {
|
||||
for (LockMovementType t : LockMovementType.values()) {
|
||||
for (LockMode t : LockMode.values()) {
|
||||
if (t.getDisplayKey().equals(v)) {
|
||||
this.setLockMovement(t);
|
||||
break;
|
||||
@@ -664,7 +678,7 @@ public abstract class Ability implements IAbility, IAbilityAction {
|
||||
FieldDef.stringEnumField("ability.rotationPhase", getRotationPhaseKeys(),
|
||||
() -> this.getRotationPhase().getDisplayKey(),
|
||||
v -> {
|
||||
for (LockMovementType t : LockMovementType.values()) {
|
||||
for (LockMode t : LockMode.values()) {
|
||||
if (t.getDisplayKey().equals(v)) {
|
||||
this.setRotationPhase(t);
|
||||
break;
|
||||
@@ -676,10 +690,24 @@ public abstract class Ability implements IAbility, IAbilityAction {
|
||||
).tab("General"));
|
||||
defs.add(FieldDef.row(
|
||||
FieldDef.boolField("ability.interruptible", this::isInterruptible, this::setInterruptible)
|
||||
.hover("ability.hover.interruptible"),
|
||||
.hover("ability.hover.interruptible")
|
||||
.enabledWhen(() -> !invulnerableMode.invulnerableDuringWindup()),
|
||||
FieldDef.intField("ability.dazedTicks", this::getDazedTicks, this::setDazedTicks)
|
||||
.range(0, 1000).visibleWhen(this::isInterruptible)
|
||||
.range(0, 1000)
|
||||
.visibleWhen(() -> isInterruptible() && !invulnerableMode.invulnerableDuringWindup())
|
||||
).tab("General"));
|
||||
defs.add(FieldDef.stringEnumField("ability.invulnerable", InvulnerableMode.getDisplayKeys(),
|
||||
() -> this.getInvulnerableMode().getDisplayKey(),
|
||||
v -> {
|
||||
for (InvulnerableMode mode : InvulnerableMode.values()) {
|
||||
if (mode.getDisplayKey().equals(v)) {
|
||||
this.setInvulnerableMode(mode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
})
|
||||
.hover("ability.hover.invulnerable")
|
||||
.tab("General"));
|
||||
|
||||
// ── Burst section ────────────────────────────────────────────
|
||||
if (allowBurst()) {
|
||||
@@ -1163,6 +1191,9 @@ public abstract class Ability implements IAbility, IAbilityAction {
|
||||
if (!interruptible || (phase != AbilityPhase.WINDUP && phase != AbilityPhase.BURST_DELAY)) {
|
||||
return false;
|
||||
}
|
||||
if (isInvulnerableForCurrentPhase()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only direct physical hits can interrupt, not magic, fire, or other indirect damage
|
||||
if (source == null) {
|
||||
@@ -1321,6 +1352,7 @@ public abstract class Ability implements IAbility, IAbilityAction {
|
||||
nbt.setBoolean("syncWindup", syncWindupWithAnimation);
|
||||
nbt.setInteger("recovery", dazedTicks);
|
||||
nbt.setBoolean("interruptible", interruptible);
|
||||
nbt.setInteger("invulnerableMode", invulnerableMode.ordinal());
|
||||
nbt.setInteger("lockMovement", lockMovement.ordinal());
|
||||
nbt.setInteger("rotationMode", rotationMode.ordinal());
|
||||
nbt.setInteger("rotationPhase", rotationPhase.ordinal());
|
||||
@@ -1400,9 +1432,12 @@ public abstract class Ability implements IAbility, IAbilityAction {
|
||||
syncWindupWithAnimation = nbt.hasKey("syncWindup") ? nbt.getBoolean("syncWindup") : true;
|
||||
dazedTicks = nbt.hasKey("recovery") ? nbt.getInteger("recovery") : 80;
|
||||
interruptible = nbt.hasKey("interruptible") ? nbt.getBoolean("interruptible") : true;
|
||||
lockMovement = LockMovementType.fromOrdinal(nbt.getInteger("lockMovement"));
|
||||
invulnerableMode = nbt.hasKey("invulnerableMode")
|
||||
? InvulnerableMode.fromOrdinal(nbt.getInteger("invulnerableMode"))
|
||||
: InvulnerableMode.NONE;
|
||||
lockMovement = LockMode.fromOrdinal(nbt.getInteger("lockMovement"));
|
||||
rotationMode = RotationMode.fromOrdinal(nbt.getInteger("rotationMode"));
|
||||
rotationPhase = LockMovementType.fromOrdinal(nbt.getInteger("rotationPhase"));
|
||||
rotationPhase = LockMode.fromOrdinal(nbt.getInteger("rotationPhase"));
|
||||
windUpColor = nbt.getInteger("windUpColor");
|
||||
activeColor = nbt.getInteger("activeColor");
|
||||
windUpSound = nbt.getString("windUpSound");
|
||||
@@ -1490,7 +1525,12 @@ public abstract class Ability implements IAbility, IAbilityAction {
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
if (builtIn) {
|
||||
this.name = name != null ? name : "";
|
||||
return;
|
||||
}
|
||||
String fallback = (this.name != null && !this.name.isEmpty()) ? this.name : "Ability";
|
||||
this.name = FileNameHelper.sanitizeName(name, fallback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1499,7 +1539,9 @@ public abstract class Ability implements IAbility, IAbilityAction {
|
||||
* Converts & color codes to § for rendering.
|
||||
*/
|
||||
public String getDisplayName() {
|
||||
String result = (displayName != null && !displayName.isEmpty()) ? displayName : name;
|
||||
String result = (displayName != null && !displayName.isEmpty())
|
||||
? displayName
|
||||
: FileNameHelper.toDisplayName(name);
|
||||
return result != null ? result.replaceAll("&([0-9a-fk-or])", "\u00A7$1") : "";
|
||||
}
|
||||
|
||||
@@ -1653,11 +1695,27 @@ public abstract class Ability implements IAbility, IAbilityAction {
|
||||
this.interruptible = interruptible;
|
||||
}
|
||||
|
||||
public LockMovementType getLockMovement() {
|
||||
public InvulnerableMode getInvulnerableMode() {
|
||||
return invulnerableMode;
|
||||
}
|
||||
|
||||
public void setInvulnerableMode(InvulnerableMode invulnerableMode) {
|
||||
this.invulnerableMode = invulnerableMode != null ? invulnerableMode : InvulnerableMode.NONE;
|
||||
}
|
||||
|
||||
public boolean isInvulnerableDuringWindup() {
|
||||
return invulnerableMode.invulnerableDuringWindup();
|
||||
}
|
||||
|
||||
public boolean isInvulnerableDuringActive() {
|
||||
return invulnerableMode.invulnerableDuringActive();
|
||||
}
|
||||
|
||||
public LockMode getLockMovement() {
|
||||
return lockMovement;
|
||||
}
|
||||
|
||||
public void setLockMovement(LockMovementType lockMovement) {
|
||||
public void setLockMovement(LockMode lockMovement) {
|
||||
this.lockMovement = lockMovement;
|
||||
}
|
||||
|
||||
@@ -1669,11 +1727,11 @@ public abstract class Ability implements IAbility, IAbilityAction {
|
||||
this.rotationMode = rotationMode;
|
||||
}
|
||||
|
||||
public LockMovementType getRotationPhase() {
|
||||
public LockMode getRotationPhase() {
|
||||
return rotationPhase;
|
||||
}
|
||||
|
||||
public void setRotationPhase(LockMovementType rotationPhase) {
|
||||
public void setRotationPhase(LockMode rotationPhase) {
|
||||
this.rotationPhase = rotationPhase;
|
||||
}
|
||||
|
||||
@@ -1694,7 +1752,7 @@ public abstract class Ability implements IAbility, IAbilityAction {
|
||||
*/
|
||||
@Override
|
||||
public void setLockMovementType(int type) {
|
||||
this.lockMovement = LockMovementType.fromOrdinal(type);
|
||||
this.lockMovement = LockMode.fromOrdinal(type);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1734,7 +1792,7 @@ public abstract class Ability implements IAbility, IAbilityAction {
|
||||
*/
|
||||
@Override
|
||||
public void setRotationPhaseType(int type) {
|
||||
this.rotationPhase = LockMovementType.fromOrdinal(type);
|
||||
this.rotationPhase = LockMode.fromOrdinal(type);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1785,6 +1843,20 @@ public abstract class Ability implements IAbility, IAbilityAction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this ability grants invulnerability during its current execution phase.
|
||||
*/
|
||||
public boolean isInvulnerableForCurrentPhase() {
|
||||
return invulnerableMode.isInvulnerableInPhase(phase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this ability grants invulnerability for a specific phase.
|
||||
*/
|
||||
public boolean isInvulnerableForPhase(AbilityPhase phase) {
|
||||
return invulnerableMode.isInvulnerableInPhase(phase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if rotation should be locked during the current phase.
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package kamkeel.npcs.controllers.data.ability;
|
||||
|
||||
import kamkeel.npcs.controllers.AbilityController;
|
||||
import kamkeel.npcs.controllers.data.ability.data.ChainedAbility;
|
||||
import kamkeel.npcs.controllers.data.ability.data.IAbilityAction;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package kamkeel.npcs.controllers.data.ability;
|
||||
|
||||
import kamkeel.npcs.controllers.data.ability.enums.AbilityPhase;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
package kamkeel.npcs.controllers.data.ability;
|
||||
|
||||
/**
|
||||
* Defines what an ability locks when Lock Position is enabled.
|
||||
*/
|
||||
public enum LockType {
|
||||
MOVEMENT,
|
||||
ROTATION,
|
||||
BOTH;
|
||||
|
||||
public boolean locksMovement() {
|
||||
return this == MOVEMENT || this == BOTH;
|
||||
}
|
||||
|
||||
public boolean locksRotation() {
|
||||
return this == ROTATION || this == BOTH;
|
||||
}
|
||||
|
||||
public String getDisplayKey() {
|
||||
switch (this) {
|
||||
case MOVEMENT:
|
||||
return "ability.lockType.movement";
|
||||
case ROTATION:
|
||||
return "ability.lockType.rotation";
|
||||
case BOTH:
|
||||
return "ability.lockType.both";
|
||||
default:
|
||||
return "ability.lockType.both";
|
||||
}
|
||||
}
|
||||
|
||||
public static String[] getDisplayKeys() {
|
||||
return new String[]{
|
||||
"ability.lockType.movement",
|
||||
"ability.lockType.rotation",
|
||||
"ability.lockType.both"
|
||||
};
|
||||
}
|
||||
|
||||
public static LockType fromOrdinal(int ordinal) {
|
||||
LockType[] values = values();
|
||||
if (ordinal >= 0 && ordinal < values.length) {
|
||||
return values[ordinal];
|
||||
}
|
||||
return BOTH;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -3,7 +3,7 @@ package kamkeel.npcs.controllers.data.ability.conditions;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.AbilityController;
|
||||
import kamkeel.npcs.controllers.data.ability.UserType;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.UserType;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.StatCollector;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package kamkeel.npcs.controllers.data.ability;
|
||||
package kamkeel.npcs.controllers.data.ability.conditions;
|
||||
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
@@ -1,7 +1,5 @@
|
||||
package kamkeel.npcs.controllers.data.ability.conditions;
|
||||
|
||||
import kamkeel.npcs.controllers.data.ability.UserType;
|
||||
|
||||
public enum ConditionFilter {
|
||||
CASTER,
|
||||
TARGET,
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ package kamkeel.npcs.controllers.data.ability.conditions;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.data.ability.UserType;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.UserType;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ package kamkeel.npcs.controllers.data.ability.conditions;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.data.ability.UserType;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.UserType;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
package kamkeel.npcs.controllers.data.ability;
|
||||
package kamkeel.npcs.controllers.data.ability.data;
|
||||
|
||||
import kamkeel.npcs.controllers.data.ability.Ability;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
|
||||
+12
-5
@@ -1,11 +1,15 @@
|
||||
package kamkeel.npcs.controllers.data.ability;
|
||||
package kamkeel.npcs.controllers.data.ability.data;
|
||||
|
||||
import kamkeel.npcs.controllers.AbilityController;
|
||||
import kamkeel.npcs.controllers.data.ability.Ability;
|
||||
import kamkeel.npcs.controllers.data.ability.data.entry.ChainedAbilityEntry;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.UserType;
|
||||
import kamkeel.npcs.controllers.data.ability.conditions.AbilityCondition;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
import noppes.npcs.api.ability.IChainedAbility;
|
||||
import kamkeel.npcs.util.FileNameHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -74,7 +78,7 @@ public class ChainedAbility implements IChainedAbility, IAbilityAction {
|
||||
}
|
||||
|
||||
public ChainedAbility(String name) {
|
||||
this.name = name != null ? name : "";
|
||||
setName(name);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
@@ -94,7 +98,8 @@ public class ChainedAbility implements IChainedAbility, IAbilityAction {
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name != null ? name : "";
|
||||
String fallback = (this.name != null && !this.name.isEmpty()) ? this.name : "Chain";
|
||||
this.name = FileNameHelper.sanitizeName(name, fallback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,7 +108,9 @@ public class ChainedAbility implements IChainedAbility, IAbilityAction {
|
||||
* Converts & color codes to § for rendering.
|
||||
*/
|
||||
public String getDisplayName() {
|
||||
String result = (displayName != null && !displayName.isEmpty()) ? displayName : name;
|
||||
String result = (displayName != null && !displayName.isEmpty())
|
||||
? displayName
|
||||
: FileNameHelper.toDisplayName(name);
|
||||
return result != null ? result.replaceAll("&([0-9a-fk-or])", "\u00A7$1") : "";
|
||||
}
|
||||
|
||||
@@ -403,7 +410,7 @@ public class ChainedAbility implements IChainedAbility, IAbilityAction {
|
||||
|
||||
public void readNBT(NBTTagCompound nbt) {
|
||||
id = nbt.getString("Id");
|
||||
name = nbt.getString("Name");
|
||||
setName(nbt.getString("Name"));
|
||||
displayName = nbt.getString("DisplayName");
|
||||
enabled = !nbt.hasKey("Enabled") || nbt.getBoolean("Enabled");
|
||||
weight = nbt.hasKey("Weight") ? nbt.getInteger("Weight") : 10;
|
||||
+4
-1
@@ -1,5 +1,8 @@
|
||||
package kamkeel.npcs.controllers.data.ability;
|
||||
package kamkeel.npcs.controllers.data.ability.data;
|
||||
|
||||
import kamkeel.npcs.controllers.data.ability.Ability;
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityAction;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.UserType;
|
||||
import kamkeel.npcs.controllers.data.ability.conditions.AbilityCondition;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
@@ -1,6 +1,8 @@
|
||||
package kamkeel.npcs.controllers.data.ability.data;
|
||||
|
||||
import kamkeel.npcs.controllers.data.ability.AnchorPoint;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.AnchorPoint;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyAnchorData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyDisplayData;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package kamkeel.npcs.controllers.data.ability;
|
||||
package kamkeel.npcs.controllers.data.ability.data.effect;
|
||||
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package kamkeel.npcs.controllers.data.ability;
|
||||
package kamkeel.npcs.controllers.data.ability.data.effect;
|
||||
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package kamkeel.npcs.controllers.data.ability;
|
||||
package kamkeel.npcs.controllers.data.ability.data.effect;
|
||||
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
package kamkeel.npcs.controllers.data.ability.data;
|
||||
package kamkeel.npcs.controllers.data.ability.data.energy;
|
||||
|
||||
import kamkeel.npcs.controllers.data.ability.AnchorPoint;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.AnchorPoint;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import noppes.npcs.api.ability.data.IEnergyAnchorData;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package kamkeel.npcs.controllers.data.ability.data;
|
||||
package kamkeel.npcs.controllers.data.ability.data.energy;
|
||||
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package kamkeel.npcs.controllers.data.ability.data;
|
||||
package kamkeel.npcs.controllers.data.ability.data.energy;
|
||||
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
|
||||
+26
-2
@@ -1,5 +1,6 @@
|
||||
package kamkeel.npcs.controllers.data.ability.data;
|
||||
package kamkeel.npcs.controllers.data.ability.data.energy;
|
||||
|
||||
import kamkeel.npcs.controllers.data.ability.enums.HitType;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import noppes.npcs.api.ability.data.IEnergyCombatData;
|
||||
|
||||
@@ -14,10 +15,25 @@ public class EnergyCombatData implements IEnergyCombatData {
|
||||
public boolean explosive = false;
|
||||
public float explosionRadius = 3.0f;
|
||||
public float explosionDamageFalloff = 0.5f;
|
||||
public HitType hitType = HitType.SINGLE;
|
||||
public int multiHitDelayTicks = 5;
|
||||
|
||||
public EnergyCombatData() {
|
||||
}
|
||||
|
||||
public EnergyCombatData(float damage, float knockback, float knockbackUp,
|
||||
boolean explosive, float explosionRadius, float explosionDamageFalloff,
|
||||
HitType hitType, int multiHitDelay) {
|
||||
this.damage = damage;
|
||||
this.knockback = knockback;
|
||||
this.knockbackUp = knockbackUp;
|
||||
this.explosive = explosive;
|
||||
this.explosionRadius = explosionRadius;
|
||||
this.explosionDamageFalloff = explosionDamageFalloff;
|
||||
this.hitType = hitType;
|
||||
this.multiHitDelayTicks = multiHitDelay;
|
||||
}
|
||||
|
||||
public EnergyCombatData(float damage, float knockback, float knockbackUp,
|
||||
boolean explosive, float explosionRadius, float explosionDamageFalloff) {
|
||||
this.damage = damage;
|
||||
@@ -95,6 +111,8 @@ public class EnergyCombatData implements IEnergyCombatData {
|
||||
nbt.setBoolean("explosive", explosive);
|
||||
nbt.setFloat("explosionRadius", explosionRadius);
|
||||
nbt.setFloat("explosionDamageFalloff", explosionDamageFalloff);
|
||||
nbt.setInteger("hitType", hitType.ordinal());
|
||||
nbt.setInteger("multiHitDelayTicks", multiHitDelayTicks);
|
||||
}
|
||||
|
||||
public void readNBT(NBTTagCompound nbt) {
|
||||
@@ -104,15 +122,21 @@ public class EnergyCombatData implements IEnergyCombatData {
|
||||
explosive = nbt.hasKey("explosive") && nbt.getBoolean("explosive");
|
||||
explosionRadius = nbt.hasKey("explosionRadius") ? nbt.getFloat("explosionRadius") : 3.0f;
|
||||
explosionDamageFalloff = nbt.hasKey("explosionDamageFalloff") ? nbt.getFloat("explosionDamageFalloff") : 0.5f;
|
||||
hitType = HitType.fromOrdinal(nbt.hasKey("hitType") ? nbt.getInteger("hitType") : 0);
|
||||
multiHitDelayTicks = nbt.hasKey("multiHitDelayTicks") ? nbt.getInteger("multiHitDelayTicks") : 5;
|
||||
|
||||
// Sanitize
|
||||
if (Float.isNaN(damage) || Float.isInfinite(damage)) damage = 7.0f;
|
||||
if (Float.isNaN(knockback) || Float.isInfinite(knockback) || knockback < 0) knockback = 1.0f;
|
||||
if (Float.isNaN(explosionRadius) || Float.isInfinite(explosionRadius) || explosionRadius < 0) explosionRadius = 3.0f;
|
||||
if (multiHitDelayTicks < 1) multiHitDelayTicks = 1;
|
||||
}
|
||||
|
||||
public EnergyCombatData copy() {
|
||||
return new EnergyCombatData(damage, knockback, knockbackUp,
|
||||
EnergyCombatData copy = new EnergyCombatData(damage, knockback, knockbackUp,
|
||||
explosive, explosionRadius, explosionDamageFalloff);
|
||||
copy.hitType = hitType;
|
||||
copy.multiHitDelayTicks = multiHitDelayTicks;
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package kamkeel.npcs.controllers.data.ability.data;
|
||||
package kamkeel.npcs.controllers.data.ability.data.energy;
|
||||
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import noppes.npcs.api.ability.data.IEnergyDisplayData;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package kamkeel.npcs.controllers.data.ability.data;
|
||||
package kamkeel.npcs.controllers.data.ability.data.energy;
|
||||
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import noppes.npcs.api.ability.data.IEnergyHomingData;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package kamkeel.npcs.controllers.data.ability.data;
|
||||
package kamkeel.npcs.controllers.data.ability.data.energy;
|
||||
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import noppes.npcs.api.ability.data.IEnergyLifespanData;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package kamkeel.npcs.controllers.data.ability.data;
|
||||
package kamkeel.npcs.controllers.data.ability.data.energy;
|
||||
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import noppes.npcs.api.ability.data.IEnergyLightningData;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package kamkeel.npcs.controllers.data.ability.data;
|
||||
package kamkeel.npcs.controllers.data.ability.data.energy;
|
||||
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package kamkeel.npcs.controllers.data.ability.data;
|
||||
package kamkeel.npcs.controllers.data.ability.data.energy;
|
||||
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraftforge.common.util.Constants;
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
package kamkeel.npcs.controllers.data.ability;
|
||||
package kamkeel.npcs.controllers.data.ability.data.entry;
|
||||
|
||||
import kamkeel.npcs.controllers.AbilityController;
|
||||
import kamkeel.npcs.controllers.data.ability.data.effect.IEffectAction;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
|
||||
+5
-3
@@ -1,4 +1,6 @@
|
||||
package kamkeel.npcs.controllers.data.ability;
|
||||
package kamkeel.npcs.controllers.data.ability.data.entry;
|
||||
|
||||
import kamkeel.npcs.controllers.data.ability.Ability;
|
||||
|
||||
/**
|
||||
* Tracks the state of a single active toggle for an entity.
|
||||
@@ -7,12 +9,12 @@ package kamkeel.npcs.controllers.data.ability;
|
||||
* State is 1-based: state 1 = first ON state, state 2 = second, etc.
|
||||
* State 0 means OFF (entry should not exist in the map).
|
||||
*/
|
||||
public class ToggleEntry {
|
||||
public class AbilityToggleEntry {
|
||||
private final Ability ability;
|
||||
private int tickCount;
|
||||
private int state;
|
||||
|
||||
public ToggleEntry(Ability ability, int state) {
|
||||
public AbilityToggleEntry(Ability ability, int state) {
|
||||
this.ability = ability;
|
||||
this.tickCount = 0;
|
||||
this.state = Math.max(1, state);
|
||||
+3
-1
@@ -1,6 +1,8 @@
|
||||
package kamkeel.npcs.controllers.data.ability;
|
||||
package kamkeel.npcs.controllers.data.ability.data.entry;
|
||||
|
||||
import kamkeel.npcs.controllers.AbilityController;
|
||||
import kamkeel.npcs.controllers.data.ability.Ability;
|
||||
import kamkeel.npcs.controllers.data.ability.data.ChainedAbility;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
|
||||
/**
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package kamkeel.npcs.controllers.data.ability;
|
||||
package kamkeel.npcs.controllers.data.ability.enums;
|
||||
|
||||
public enum AbilityPhase {
|
||||
IDLE, // Not executing
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package kamkeel.npcs.controllers.data.ability;
|
||||
package kamkeel.npcs.controllers.data.ability.enums;
|
||||
|
||||
/**
|
||||
* Defines anchor points for ability charging effects.
|
||||
@@ -0,0 +1,32 @@
|
||||
package kamkeel.npcs.controllers.data.ability.enums;
|
||||
|
||||
/**
|
||||
* Shared hit behavior for energy projectiles.
|
||||
*/
|
||||
public enum HitType {
|
||||
SINGLE,
|
||||
PIERCE,
|
||||
MULTI;
|
||||
|
||||
public static HitType fromOrdinal(int ordinal) {
|
||||
HitType[] values = values();
|
||||
if (ordinal >= 0 && ordinal < values.length) {
|
||||
return values[ordinal];
|
||||
}
|
||||
return SINGLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
switch (this) {
|
||||
case SINGLE:
|
||||
return "ability.hitType.single";
|
||||
case PIERCE:
|
||||
return "ability.hitType.pierce";
|
||||
case MULTI:
|
||||
return "ability.hitType.multi";
|
||||
default:
|
||||
return name();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package kamkeel.npcs.controllers.data.ability.enums;
|
||||
|
||||
/**
|
||||
* Defines when a caster is invulnerable while an ability is executing.
|
||||
*/
|
||||
public enum InvulnerableMode {
|
||||
NONE,
|
||||
WINDUP,
|
||||
ACTIVE,
|
||||
BOTH;
|
||||
|
||||
public boolean invulnerableDuringWindup() {
|
||||
return this == WINDUP || this == BOTH;
|
||||
}
|
||||
|
||||
public boolean invulnerableDuringActive() {
|
||||
return this == ACTIVE || this == BOTH;
|
||||
}
|
||||
|
||||
/**
|
||||
* Treat BURST_DELAY as windup-like for interruption/damage immunity checks.
|
||||
*/
|
||||
public boolean isInvulnerableInPhase(AbilityPhase phase) {
|
||||
if (phase == null) return false;
|
||||
switch (phase) {
|
||||
case WINDUP:
|
||||
case BURST_DELAY:
|
||||
return invulnerableDuringWindup();
|
||||
case ACTIVE:
|
||||
return invulnerableDuringActive();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public String getDisplayKey() {
|
||||
switch (this) {
|
||||
case WINDUP:
|
||||
return "ability.invulnerable.windup";
|
||||
case ACTIVE:
|
||||
return "ability.invulnerable.active";
|
||||
case BOTH:
|
||||
return "ability.invulnerable.both";
|
||||
case NONE:
|
||||
default:
|
||||
return "ability.invulnerable.none";
|
||||
}
|
||||
}
|
||||
|
||||
public static String[] getDisplayKeys() {
|
||||
return new String[]{
|
||||
"ability.invulnerable.none",
|
||||
"ability.invulnerable.windup",
|
||||
"ability.invulnerable.active",
|
||||
"ability.invulnerable.both"
|
||||
};
|
||||
}
|
||||
|
||||
public static InvulnerableMode fromOrdinal(int ordinal) {
|
||||
InvulnerableMode[] values = values();
|
||||
if (ordinal >= 0 && ordinal < values.length) {
|
||||
return values[ordinal];
|
||||
}
|
||||
return NONE;
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -1,9 +1,9 @@
|
||||
package kamkeel.npcs.controllers.data.ability;
|
||||
package kamkeel.npcs.controllers.data.ability.enums;
|
||||
|
||||
/**
|
||||
* Defines when an ability locks NPC movement and rotation.
|
||||
*/
|
||||
public enum LockMovementType {
|
||||
public enum LockMode {
|
||||
/**
|
||||
* No movement lock - NPC can move freely during both phases.
|
||||
*/
|
||||
@@ -71,10 +71,10 @@ public enum LockMovementType {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get LockMovementType from ordinal, with fallback to WINDUP.
|
||||
* Get LockMode from ordinal, with fallback to WINDUP.
|
||||
*/
|
||||
public static LockMovementType fromOrdinal(int ordinal) {
|
||||
LockMovementType[] values = values();
|
||||
public static LockMode fromOrdinal(int ordinal) {
|
||||
LockMode[] values = values();
|
||||
if (ordinal >= 0 && ordinal < values.length) {
|
||||
return values[ordinal];
|
||||
}
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
package kamkeel.npcs.controllers.data.ability;
|
||||
package kamkeel.npcs.controllers.data.ability.enums;
|
||||
|
||||
/**
|
||||
* Defines what happens to NPC rotation during ability phases.
|
||||
* Used with a LockMovementType to control WHEN it applies.
|
||||
* Used with a LockMode to control WHEN it applies.
|
||||
*/
|
||||
public enum RotationMode {
|
||||
/**
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package kamkeel.npcs.controllers.data.ability;
|
||||
package kamkeel.npcs.controllers.data.ability.enums;
|
||||
|
||||
/**
|
||||
* Filter for determining which entities an ability should affect in AOE mode.
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package kamkeel.npcs.controllers.data.ability;
|
||||
package kamkeel.npcs.controllers.data.ability.enums;
|
||||
|
||||
/**
|
||||
* Defines how an ability selects its target(s).
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package kamkeel.npcs.controllers.data.ability;
|
||||
package kamkeel.npcs.controllers.data.ability.enums;
|
||||
|
||||
/**
|
||||
* Defines which entity types can use an ability.
|
||||
+3
-1
@@ -1,5 +1,7 @@
|
||||
package kamkeel.npcs.controllers.data.ability;
|
||||
package kamkeel.npcs.controllers.data.ability.extender;
|
||||
|
||||
import kamkeel.npcs.controllers.data.ability.enums.AbilityPhase;
|
||||
import kamkeel.npcs.controllers.data.ability.Ability;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
|
||||
/**
|
||||
@@ -3,10 +3,10 @@ package kamkeel.npcs.controllers.data.ability.gui;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.AbilityController;
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityCustomEffect;
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityEffectActionEntry;
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityPotionEffect;
|
||||
import kamkeel.npcs.controllers.data.ability.IEffectAction;
|
||||
import kamkeel.npcs.controllers.data.ability.data.effect.AbilityCustomEffect;
|
||||
import kamkeel.npcs.controllers.data.ability.data.entry.AbilityEffectActionEntry;
|
||||
import kamkeel.npcs.controllers.data.ability.data.effect.AbilityPotionEffect;
|
||||
import kamkeel.npcs.controllers.data.ability.data.effect.IEffectAction;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import noppes.npcs.client.gui.advanced.SubGuiCustomEffectSelect;
|
||||
|
||||
@@ -2,9 +2,9 @@ package kamkeel.npcs.controllers.data.ability.gui;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityCustomEffect;
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityEffectActionEntry;
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityPotionEffect;
|
||||
import kamkeel.npcs.controllers.data.ability.data.effect.AbilityCustomEffect;
|
||||
import kamkeel.npcs.controllers.data.ability.data.entry.AbilityEffectActionEntry;
|
||||
import kamkeel.npcs.controllers.data.ability.data.effect.AbilityPotionEffect;
|
||||
import noppes.npcs.client.gui.builder.FieldDef;
|
||||
import noppes.npcs.client.gui.builder.FieldType;
|
||||
|
||||
|
||||
+4
-2
@@ -1,4 +1,4 @@
|
||||
package noppes.npcs.client.gui.util;
|
||||
package kamkeel.npcs.controllers.data.ability.gui;
|
||||
|
||||
import kamkeel.npcs.client.renderer.TelegraphRenderer;
|
||||
import kamkeel.npcs.controllers.data.telegraph.TelegraphInstance;
|
||||
@@ -11,6 +11,8 @@ import net.minecraft.client.renderer.entity.RenderManager;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import noppes.npcs.client.ClientEventHandler;
|
||||
import noppes.npcs.client.gui.util.GuiNPCInterface2;
|
||||
import noppes.npcs.client.gui.util.GuiNpcButton;
|
||||
import noppes.npcs.entity.EntityNPCInterface;
|
||||
import org.lwjgl.input.Mouse;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
@@ -40,7 +42,7 @@ public class GuiAbilityInterface extends GuiNPCInterface2 {
|
||||
/**
|
||||
* NPC facing direction - affects anchor points and visual facing
|
||||
*/
|
||||
protected static float NPC_FACING_YAW = 160.0f;
|
||||
public static float NPC_FACING_YAW = 160.0f;
|
||||
/**
|
||||
* Camera pitch (slight downward angle)
|
||||
*/
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
package kamkeel.npcs.controllers.data.ability;
|
||||
package kamkeel.npcs.controllers.data.ability.gui;
|
||||
|
||||
import kamkeel.npcs.controllers.data.ability.Ability;
|
||||
import noppes.npcs.client.gui.builder.FieldDef;
|
||||
|
||||
import java.util.List;
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
package kamkeel.npcs.controllers.data.ability;
|
||||
package kamkeel.npcs.controllers.data.ability.gui;
|
||||
|
||||
import kamkeel.npcs.controllers.data.ability.data.ChainedAbility;
|
||||
import noppes.npcs.client.gui.builder.FieldDef;
|
||||
|
||||
import java.util.List;
|
||||
+5
-5
@@ -1,16 +1,16 @@
|
||||
package noppes.npcs.client.gui.util;
|
||||
package kamkeel.npcs.controllers.data.ability.preview;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.AbilityController;
|
||||
import kamkeel.npcs.controllers.data.ability.Ability;
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityPhase;
|
||||
import kamkeel.npcs.controllers.data.ability.ChainedAbility;
|
||||
import kamkeel.npcs.controllers.data.ability.ChainedAbilityEntry;
|
||||
import kamkeel.npcs.controllers.data.ability.PreviewEntityHandler;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.AbilityPhase;
|
||||
import kamkeel.npcs.controllers.data.ability.data.ChainedAbility;
|
||||
import kamkeel.npcs.controllers.data.ability.data.entry.ChainedAbilityEntry;
|
||||
import kamkeel.npcs.controllers.data.telegraph.TelegraphInstance;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import kamkeel.npcs.controllers.data.ability.gui.GuiAbilityInterface;
|
||||
import noppes.npcs.controllers.data.Animation;
|
||||
import noppes.npcs.controllers.data.AnimationData;
|
||||
import noppes.npcs.entity.EntityCustomNpc;
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package kamkeel.npcs.controllers.data.ability;
|
||||
package kamkeel.npcs.controllers.data.ability.preview;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
|
||||
@@ -2,10 +2,10 @@ package kamkeel.npcs.controllers.data.ability.type;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityTargetHelper;
|
||||
import kamkeel.npcs.controllers.data.ability.LockMovementType;
|
||||
import kamkeel.npcs.controllers.data.ability.TargetFilter;
|
||||
import kamkeel.npcs.controllers.data.ability.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.util.AbilityTargetHelper;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.LockMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetFilter;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.gui.AbilityFieldDefs;
|
||||
import kamkeel.npcs.controllers.data.telegraph.Telegraph;
|
||||
import kamkeel.npcs.controllers.data.telegraph.TelegraphInstance;
|
||||
@@ -43,7 +43,7 @@ public class AbilityCharge extends AbilityMovement implements IAbilityCharge {
|
||||
this.targetingMode = TargetingMode.AGGRO_TARGET;
|
||||
this.maxRange = 20.0f;
|
||||
this.minRange = 4.0f;
|
||||
this.lockMovement = LockMovementType.WINDUP;
|
||||
this.lockMovement = LockMode.WINDUP;
|
||||
this.cooldownTicks = 0;
|
||||
this.windUpTicks = 20;
|
||||
// LINE telegraph showing charge path
|
||||
|
||||
@@ -3,10 +3,10 @@ package kamkeel.npcs.controllers.data.ability.type;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.data.ability.Ability;
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityTargetHelper;
|
||||
import kamkeel.npcs.controllers.data.ability.LockMovementType;
|
||||
import kamkeel.npcs.controllers.data.ability.TargetFilter;
|
||||
import kamkeel.npcs.controllers.data.ability.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.LockMode;
|
||||
import kamkeel.npcs.controllers.data.ability.util.AbilityTargetHelper;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetFilter;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.gui.AbilityFieldDefs;
|
||||
import kamkeel.npcs.controllers.data.telegraph.Telegraph;
|
||||
import kamkeel.npcs.controllers.data.telegraph.TelegraphInstance;
|
||||
@@ -71,7 +71,7 @@ public class AbilityCutter extends Ability implements IAbilityCutter {
|
||||
this.name = "Cutter";
|
||||
this.targetingMode = TargetingMode.AOE_SELF;
|
||||
this.maxRange = 8.0f;
|
||||
this.lockMovement = LockMovementType.NO;
|
||||
this.lockMovement = LockMode.NO;
|
||||
this.cooldownTicks = 0;
|
||||
this.windUpTicks = 20;
|
||||
this.telegraphType = TelegraphType.CONE;
|
||||
|
||||
@@ -2,8 +2,8 @@ package kamkeel.npcs.controllers.data.ability.type;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.data.ability.LockMovementType;
|
||||
import kamkeel.npcs.controllers.data.ability.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.LockMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.telegraph.TelegraphType;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
@@ -91,6 +91,8 @@ public class AbilityDash extends AbilityMovement implements IAbilityDash {
|
||||
|
||||
// Type-specific runtime state
|
||||
private transient DashDirection chosenDirection;
|
||||
private transient double preDashMotionX;
|
||||
private transient double preDashMotionZ;
|
||||
|
||||
public AbilityDash() {
|
||||
this.typeId = "ability.cnpc.dash";
|
||||
@@ -98,7 +100,7 @@ public class AbilityDash extends AbilityMovement implements IAbilityDash {
|
||||
this.targetingMode = TargetingMode.AGGRO_TARGET;
|
||||
this.maxRange = 20.0f;
|
||||
this.minRange = 0.0f;
|
||||
this.lockMovement = LockMovementType.NO;
|
||||
this.lockMovement = LockMode.NO;
|
||||
this.cooldownTicks = 0;
|
||||
this.windUpTicks = 5;
|
||||
// No telegraph for dash - it's a quick evasive move
|
||||
@@ -125,6 +127,8 @@ public class AbilityDash extends AbilityMovement implements IAbilityDash {
|
||||
|
||||
@Override
|
||||
public void onExecute(EntityLivingBase caster, EntityLivingBase target) {
|
||||
preDashMotionX = caster.motionX;
|
||||
preDashMotionZ = caster.motionZ;
|
||||
initMovement(caster, dashDistance, dashSpeed);
|
||||
|
||||
if (dashMode == DashMode.DIRECTIONAL) {
|
||||
@@ -151,37 +155,46 @@ public class AbilityDash extends AbilityMovement implements IAbilityDash {
|
||||
@Override
|
||||
public void onActiveTick(EntityLivingBase caster, EntityLivingBase target, int tick) {
|
||||
if (checkTimeout(tick)) {
|
||||
stopMomentum(caster);
|
||||
finishDash(caster);
|
||||
signalCompletion();
|
||||
return;
|
||||
}
|
||||
|
||||
if (movementDirection == null) {
|
||||
finishDash(caster);
|
||||
signalCompletion();
|
||||
return;
|
||||
}
|
||||
|
||||
if (checkStall(caster, tick)) {
|
||||
stopMomentum(caster);
|
||||
finishDash(caster);
|
||||
signalCompletion();
|
||||
return;
|
||||
}
|
||||
updatePrevPosition(caster);
|
||||
|
||||
if (getDistanceTraveledSq(caster) >= (double) dashDistance * dashDistance) {
|
||||
stopMomentum(caster);
|
||||
finishDash(caster);
|
||||
signalCompletion();
|
||||
return;
|
||||
}
|
||||
|
||||
if (checkBlocked(caster, dashSpeed)) {
|
||||
stopMomentum(caster);
|
||||
finishDash(caster);
|
||||
signalCompletion();
|
||||
return;
|
||||
}
|
||||
|
||||
// Move caster (motionY left to gravity for skip arc)
|
||||
applyVelocity(caster, dashSpeed);
|
||||
// Move caster (motionY left to gravity for skip arc).
|
||||
// Directional mode layers the dash over incoming movement so momentum
|
||||
// entering/exiting the dash stays fluid.
|
||||
if (dashMode == DashMode.DIRECTIONAL) {
|
||||
applyHorizontalMomentum(caster,
|
||||
preDashMotionX + movementDirection.xCoord * dashSpeed,
|
||||
preDashMotionZ + movementDirection.zCoord * dashSpeed);
|
||||
} else {
|
||||
applyVelocity(caster, dashSpeed);
|
||||
}
|
||||
|
||||
if (!isPreview()) {
|
||||
// Trail particles
|
||||
@@ -191,24 +204,28 @@ public class AbilityDash extends AbilityMovement implements IAbilityDash {
|
||||
|
||||
@Override
|
||||
public void onComplete(EntityLivingBase caster, EntityLivingBase target) {
|
||||
stopMomentum(caster);
|
||||
finishDash(caster);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInterrupt(EntityLivingBase caster, DamageSource source, float damage) {
|
||||
stopMomentum(caster);
|
||||
finishDash(caster);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup() {
|
||||
super.cleanup();
|
||||
chosenDirection = null;
|
||||
preDashMotionX = 0;
|
||||
preDashMotionZ = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetForBurst() {
|
||||
super.resetForBurst();
|
||||
chosenDirection = null;
|
||||
preDashMotionX = 0;
|
||||
preDashMotionZ = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -279,6 +296,14 @@ public class AbilityDash extends AbilityMovement implements IAbilityDash {
|
||||
return chosenDirection;
|
||||
}
|
||||
|
||||
private void finishDash(EntityLivingBase caster) {
|
||||
if (dashMode == DashMode.DIRECTIONAL) {
|
||||
applyHorizontalMomentum(caster, preDashMotionX, preDashMotionZ);
|
||||
} else {
|
||||
stopMomentum(caster);
|
||||
}
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void getAbilityDefinitions(List<FieldDef> defs) {
|
||||
|
||||
@@ -4,13 +4,13 @@ import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.AbilityController;
|
||||
import kamkeel.npcs.controllers.data.ability.Ability;
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityCustomEffect;
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityEffectActionEntry;
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityTargetHelper;
|
||||
import kamkeel.npcs.controllers.data.ability.data.effect.AbilityCustomEffect;
|
||||
import kamkeel.npcs.controllers.data.ability.data.entry.AbilityEffectActionEntry;
|
||||
import kamkeel.npcs.controllers.data.ability.util.AbilityTargetHelper;
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityVariant;
|
||||
import kamkeel.npcs.controllers.data.ability.LockMovementType;
|
||||
import kamkeel.npcs.controllers.data.ability.TargetFilter;
|
||||
import kamkeel.npcs.controllers.data.ability.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.LockMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetFilter;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.gui.AbilityFieldDefs;
|
||||
import kamkeel.npcs.controllers.data.telegraph.TelegraphType;
|
||||
import net.minecraft.entity.Entity;
|
||||
@@ -73,7 +73,7 @@ public class AbilityEffect extends Ability implements IAbilityEffect {
|
||||
this.typeId = "ability.cnpc.effect";
|
||||
this.name = "Effect";
|
||||
this.targetingMode = TargetingMode.SELF;
|
||||
this.lockMovement = LockMovementType.WINDUP;
|
||||
this.lockMovement = LockMode.WINDUP;
|
||||
this.cooldownTicks = 0;
|
||||
this.windUpTicks = 30;
|
||||
this.telegraphType = TelegraphType.NONE;
|
||||
|
||||
@@ -3,10 +3,10 @@ package kamkeel.npcs.controllers.data.ability.type;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.data.ability.Ability;
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityPhase;
|
||||
import kamkeel.npcs.controllers.data.ability.LockMovementType;
|
||||
import kamkeel.npcs.controllers.data.ability.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.UserType;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.AbilityPhase;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.LockMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.UserType;
|
||||
import kamkeel.npcs.controllers.data.ability.gui.AbilityFieldDefs;
|
||||
import kamkeel.npcs.controllers.data.telegraph.TelegraphType;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
@@ -52,7 +52,7 @@ public class AbilityGuard extends Ability implements IAbilityGuard {
|
||||
this.typeId = "ability.cnpc.guard";
|
||||
this.name = "Guard";
|
||||
this.targetingMode = TargetingMode.SELF;
|
||||
this.lockMovement = LockMovementType.ACTIVE;
|
||||
this.lockMovement = LockMode.ACTIVE;
|
||||
this.cooldownTicks = 0;
|
||||
this.windUpTicks = 0;
|
||||
this.interruptible = false; // Hard to interrupt while guarding
|
||||
|
||||
@@ -2,7 +2,7 @@ package kamkeel.npcs.controllers.data.ability.type;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.gui.AbilityFieldDefs;
|
||||
import kamkeel.npcs.entity.EntityAbilityZone;
|
||||
import kamkeel.npcs.entity.EntityAbilityZone.ZoneShape;
|
||||
|
||||
@@ -3,10 +3,10 @@ package kamkeel.npcs.controllers.data.ability.type;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.data.ability.Ability;
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityTargetHelper;
|
||||
import kamkeel.npcs.controllers.data.ability.LockMovementType;
|
||||
import kamkeel.npcs.controllers.data.ability.TargetFilter;
|
||||
import kamkeel.npcs.controllers.data.ability.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.LockMode;
|
||||
import kamkeel.npcs.controllers.data.ability.util.AbilityTargetHelper;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetFilter;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.gui.AbilityFieldDefs;
|
||||
import kamkeel.npcs.controllers.data.telegraph.TelegraphType;
|
||||
import net.minecraft.entity.Entity;
|
||||
@@ -38,7 +38,7 @@ public class AbilityHeavyHit extends Ability implements IAbilityHeavyHit {
|
||||
this.targetingMode = TargetingMode.AOE_SELF;
|
||||
this.maxRange = 5.0f;
|
||||
this.minRange = 0.0f;
|
||||
this.lockMovement = LockMovementType.WINDUP;
|
||||
this.lockMovement = LockMode.WINDUP;
|
||||
this.cooldownTicks = 0;
|
||||
this.windUpTicks = 30;
|
||||
this.telegraphType = TelegraphType.LINE;
|
||||
|
||||
@@ -226,7 +226,7 @@ public abstract class AbilityMovement extends Ability {
|
||||
* By sending S12 manually here — before friction — the client receives the full
|
||||
* velocity and moves at the correct speed.
|
||||
*/
|
||||
private void sendPlayerVelocity(EntityLivingBase caster) {
|
||||
protected void sendPlayerVelocity(EntityLivingBase caster) {
|
||||
if (caster instanceof EntityPlayerMP) {
|
||||
((EntityPlayerMP) caster).playerNetServerHandler.sendPacket(
|
||||
new S12PacketEntityVelocity(caster));
|
||||
@@ -234,11 +234,11 @@ public abstract class AbilityMovement extends Ability {
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply movement velocity to the caster (horizontal only, Y unchanged).
|
||||
* Apply horizontal momentum and sync to client.
|
||||
*/
|
||||
protected void applyVelocity(EntityLivingBase caster, float speed) {
|
||||
caster.motionX = movementDirection.xCoord * speed;
|
||||
caster.motionZ = movementDirection.zCoord * speed;
|
||||
protected void applyHorizontalMomentum(EntityLivingBase caster, double motionX, double motionZ) {
|
||||
caster.motionX = motionX;
|
||||
caster.motionZ = motionZ;
|
||||
if (!isPreview()) {
|
||||
if (caster instanceof EntityPlayerMP) {
|
||||
sendPlayerVelocity(caster);
|
||||
@@ -248,36 +248,27 @@ public abstract class AbilityMovement extends Ability {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply movement velocity to the caster (horizontal only, Y unchanged).
|
||||
*/
|
||||
protected void applyVelocity(EntityLivingBase caster, float speed) {
|
||||
applyHorizontalMomentum(caster, movementDirection.xCoord * speed, movementDirection.zCoord * speed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply movement velocity with zero vertical motion.
|
||||
* Used for ground-locked movement like Charge.
|
||||
*/
|
||||
protected void applyVelocityFlat(EntityLivingBase caster, float speed) {
|
||||
caster.motionX = movementDirection.xCoord * speed;
|
||||
applyHorizontalMomentum(caster, movementDirection.xCoord * speed, movementDirection.zCoord * speed);
|
||||
caster.motionY = 0;
|
||||
caster.motionZ = movementDirection.zCoord * speed;
|
||||
if (!isPreview()) {
|
||||
if (caster instanceof EntityPlayerMP) {
|
||||
sendPlayerVelocity(caster);
|
||||
} else {
|
||||
caster.velocityChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop all horizontal momentum.
|
||||
*/
|
||||
protected void stopMomentum(EntityLivingBase caster) {
|
||||
caster.motionX = 0;
|
||||
caster.motionZ = 0;
|
||||
if (!isPreview()) {
|
||||
if (caster instanceof EntityPlayerMP) {
|
||||
sendPlayerVelocity(caster);
|
||||
} else {
|
||||
caster.velocityChanged = true;
|
||||
}
|
||||
}
|
||||
applyHorizontalMomentum(caster, 0, 0);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
@@ -3,8 +3,8 @@ package kamkeel.npcs.controllers.data.ability.type;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.data.ability.Ability;
|
||||
import kamkeel.npcs.controllers.data.ability.LockMovementType;
|
||||
import kamkeel.npcs.controllers.data.ability.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.LockMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.gui.AbilityFieldDefs;
|
||||
import kamkeel.npcs.controllers.data.telegraph.TelegraphType;
|
||||
import net.minecraft.entity.Entity;
|
||||
@@ -42,7 +42,7 @@ public class AbilityProjectile extends Ability implements IAbilityProjectile {
|
||||
this.maxRange = 20.0f;
|
||||
this.cooldownTicks = 0;
|
||||
this.windUpTicks = 15;
|
||||
this.lockMovement = LockMovementType.NO;
|
||||
this.lockMovement = LockMode.NO;
|
||||
// No telegraph for projectile - it's a ranged attack
|
||||
this.telegraphType = TelegraphType.NONE;
|
||||
this.showTelegraph = false;
|
||||
|
||||
@@ -3,10 +3,10 @@ package kamkeel.npcs.controllers.data.ability.type;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.data.ability.Ability;
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityTargetHelper;
|
||||
import kamkeel.npcs.controllers.data.ability.LockMovementType;
|
||||
import kamkeel.npcs.controllers.data.ability.TargetFilter;
|
||||
import kamkeel.npcs.controllers.data.ability.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.util.AbilityTargetHelper;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.LockMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetFilter;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.gui.AbilityFieldDefs;
|
||||
import kamkeel.npcs.controllers.data.telegraph.TelegraphType;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
@@ -35,7 +35,7 @@ public class AbilityShockwave extends Ability implements IAbilityShockwave {
|
||||
this.name = "Shockwave";
|
||||
this.targetingMode = TargetingMode.AOE_SELF;
|
||||
this.maxRange = 8.0f;
|
||||
this.lockMovement = LockMovementType.WINDUP_AND_ACTIVE;
|
||||
this.lockMovement = LockMode.WINDUP_AND_ACTIVE;
|
||||
this.cooldownTicks = 0;
|
||||
this.windUpTicks = 25;
|
||||
this.telegraphType = TelegraphType.CIRCLE;
|
||||
|
||||
@@ -3,10 +3,10 @@ package kamkeel.npcs.controllers.data.ability.type;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.data.ability.Ability;
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityTargetHelper;
|
||||
import kamkeel.npcs.controllers.data.ability.LockMovementType;
|
||||
import kamkeel.npcs.controllers.data.ability.TargetFilter;
|
||||
import kamkeel.npcs.controllers.data.ability.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.LockMode;
|
||||
import kamkeel.npcs.controllers.data.ability.util.AbilityTargetHelper;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetFilter;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.gui.AbilityFieldDefs;
|
||||
import kamkeel.npcs.controllers.data.telegraph.Telegraph;
|
||||
import kamkeel.npcs.controllers.data.telegraph.TelegraphInstance;
|
||||
@@ -68,7 +68,7 @@ public class AbilitySlam extends Ability implements IAbilitySlam {
|
||||
this.targetingMode = TargetingMode.AOE_SELF; // Can also be AOE_TARGET to leap to target
|
||||
this.windUpTicks = 30;
|
||||
this.cooldownTicks = 0;
|
||||
this.lockMovement = LockMovementType.WINDUP;
|
||||
this.lockMovement = LockMode.WINDUP;
|
||||
this.minRange = 2.0f;
|
||||
this.maxRange = 15.0f;
|
||||
this.telegraphType = TelegraphType.CIRCLE;
|
||||
|
||||
@@ -3,9 +3,9 @@ package kamkeel.npcs.controllers.data.ability.type;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.data.ability.Ability;
|
||||
import kamkeel.npcs.controllers.data.ability.LockMovementType;
|
||||
import kamkeel.npcs.controllers.data.ability.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.UserType;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.LockMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.UserType;
|
||||
import kamkeel.npcs.controllers.data.ability.gui.AbilityFieldDefs;
|
||||
import kamkeel.npcs.controllers.data.telegraph.TelegraphType;
|
||||
import net.minecraft.block.Block;
|
||||
@@ -78,7 +78,7 @@ public class AbilityTeleport extends Ability implements IAbilityTeleport {
|
||||
this.minRange = 5.0f;
|
||||
this.cooldownTicks = 0;
|
||||
this.windUpTicks = 10;
|
||||
this.lockMovement = LockMovementType.NO;
|
||||
this.lockMovement = LockMode.NO;
|
||||
// No telegraph for teleport - it's instant repositioning
|
||||
this.telegraphType = TelegraphType.NONE;
|
||||
this.showTelegraph = false;
|
||||
|
||||
@@ -2,8 +2,8 @@ package kamkeel.npcs.controllers.data.ability.type;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityPotionEffect;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.effect.AbilityPotionEffect;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.gui.AbilityFieldDefs;
|
||||
import kamkeel.npcs.entity.EntityAbilityZone;
|
||||
import kamkeel.npcs.entity.EntityAbilityZone.ZoneShape;
|
||||
|
||||
@@ -3,10 +3,10 @@ package kamkeel.npcs.controllers.data.ability.type;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.data.ability.Ability;
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityTargetHelper;
|
||||
import kamkeel.npcs.controllers.data.ability.LockMovementType;
|
||||
import kamkeel.npcs.controllers.data.ability.TargetFilter;
|
||||
import kamkeel.npcs.controllers.data.ability.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.util.AbilityTargetHelper;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.LockMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetFilter;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.gui.AbilityFieldDefs;
|
||||
import kamkeel.npcs.controllers.data.telegraph.TelegraphType;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
@@ -54,7 +54,7 @@ public class AbilityVortex extends Ability implements IAbilityVortex {
|
||||
this.name = "Vortex";
|
||||
this.targetingMode = TargetingMode.AOE_SELF;
|
||||
this.maxRange = 15.0f;
|
||||
this.lockMovement = LockMovementType.WINDUP_AND_ACTIVE;
|
||||
this.lockMovement = LockMode.WINDUP_AND_ACTIVE;
|
||||
this.cooldownTicks = 0;
|
||||
this.windUpTicks = 30;
|
||||
this.telegraphType = TelegraphType.CIRCLE;
|
||||
|
||||
@@ -3,11 +3,11 @@ package kamkeel.npcs.controllers.data.ability.type;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.data.ability.Ability;
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityPotionEffect;
|
||||
import kamkeel.npcs.controllers.data.ability.LockMovementType;
|
||||
import kamkeel.npcs.controllers.data.ability.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.UserType;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.effect.AbilityPotionEffect;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.LockMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.UserType;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.telegraph.Telegraph;
|
||||
import kamkeel.npcs.controllers.data.telegraph.TelegraphInstance;
|
||||
import kamkeel.npcs.entity.EntityAbilityZone;
|
||||
@@ -65,7 +65,7 @@ public abstract class AbilityZone extends Ability {
|
||||
this.colorData = defaultColors;
|
||||
this.targetingMode = TargetingMode.AGGRO_TARGET;
|
||||
this.maxRange = 20.0f;
|
||||
this.lockMovement = LockMovementType.WINDUP;
|
||||
this.lockMovement = LockMode.WINDUP;
|
||||
this.cooldownTicks = 0;
|
||||
this.allowedBy = UserType.BOTH;
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ package kamkeel.npcs.controllers.data.ability.type.energy;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.AbilityController;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyBarrierData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyBarrierData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyDisplayData;
|
||||
import kamkeel.npcs.entity.EntityAbilityBarrier;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
|
||||
@@ -3,13 +3,14 @@ package kamkeel.npcs.controllers.data.ability.type.energy;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityVariant;
|
||||
import kamkeel.npcs.controllers.data.ability.LockMovementType;
|
||||
import kamkeel.npcs.controllers.data.ability.RotationMode;
|
||||
import kamkeel.npcs.controllers.data.ability.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyCombatData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyHomingData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyLifespanData;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.HitType;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.LockMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.RotationMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyCombatData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyHomingData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyLifespanData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.ProjectileData;
|
||||
import kamkeel.npcs.controllers.data.ability.gui.AbilityFieldDefs;
|
||||
import kamkeel.npcs.controllers.data.telegraph.TelegraphType;
|
||||
@@ -36,7 +37,7 @@ public class AbilityBeam extends AbilityEnergyProjectile<EntityAbilityBeam> impl
|
||||
public AbilityBeam() {
|
||||
super(
|
||||
new EnergyDisplayData(0xFFFFFF, 0x00AAFF, true, 0.4f, 0.5f, 6.0f),
|
||||
new EnergyCombatData(10.0f, 1.5f, 0.2f, false, 4.0f, 0.5f),
|
||||
new EnergyCombatData(10.0f, 1.5f, 0.2f, false, 4.0f, 0.5f, HitType.MULTI, 5),
|
||||
new EnergyHomingData(0.4f, true, 0.1f, 15.0f),
|
||||
new EnergyLifespanData(150.0f, 200)
|
||||
);
|
||||
@@ -47,9 +48,9 @@ public class AbilityBeam extends AbilityEnergyProjectile<EntityAbilityBeam> impl
|
||||
this.minRange = 5.0f;
|
||||
this.cooldownTicks = 0;
|
||||
this.windUpTicks = 40;
|
||||
this.lockMovement = LockMovementType.WINDUP_AND_ACTIVE;
|
||||
this.lockMovement = LockMode.WINDUP_AND_ACTIVE;
|
||||
this.rotationMode = RotationMode.LOCKED;
|
||||
this.rotationPhase = LockMovementType.ACTIVE;
|
||||
this.rotationPhase = LockMode.ACTIVE;
|
||||
this.telegraphType = TelegraphType.CIRCLE;
|
||||
this.showTelegraph = true;
|
||||
this.windUpAnimationName = "Ability_Beam_Windup";
|
||||
|
||||
@@ -3,12 +3,13 @@ package kamkeel.npcs.controllers.data.ability.type.energy;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityVariant;
|
||||
import kamkeel.npcs.controllers.data.ability.LockMovementType;
|
||||
import kamkeel.npcs.controllers.data.ability.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyCombatData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyHomingData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyLifespanData;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.HitType;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.LockMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyCombatData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyHomingData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyLifespanData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.ProjectileData;
|
||||
import kamkeel.npcs.controllers.data.ability.gui.AbilityFieldDefs;
|
||||
import kamkeel.npcs.controllers.data.telegraph.TelegraphType;
|
||||
@@ -38,7 +39,7 @@ public class AbilityDisc extends AbilityEnergyProjectile<EntityAbilityDisc> impl
|
||||
public AbilityDisc() {
|
||||
super(
|
||||
new EnergyDisplayData(0xFFFFFF, 0xFF8800, true, 0.4f, 0.5f, 5.0f),
|
||||
new EnergyCombatData(8.0f, 1.2f, 0.15f, false, 3.0f, 0.5f),
|
||||
new EnergyCombatData(8.0f, 1.2f, 0.15f, false, 3.0f, 0.5f, HitType.PIERCE, 5),
|
||||
new EnergyHomingData(0.6f, true, 0.12f, 18.0f),
|
||||
new EnergyLifespanData(150.0f, 200)
|
||||
);
|
||||
@@ -49,7 +50,7 @@ public class AbilityDisc extends AbilityEnergyProjectile<EntityAbilityDisc> impl
|
||||
this.minRange = 5.0f;
|
||||
this.cooldownTicks = 0;
|
||||
this.windUpTicks = 60;
|
||||
this.lockMovement = LockMovementType.WINDUP;
|
||||
this.lockMovement = LockMode.WINDUP;
|
||||
this.telegraphType = TelegraphType.CIRCLE;
|
||||
this.showTelegraph = true;
|
||||
this.windUpAnimationName = "Ability_Disc_Windup";
|
||||
|
||||
@@ -3,10 +3,10 @@ package kamkeel.npcs.controllers.data.ability.type.energy;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityVariant;
|
||||
import kamkeel.npcs.controllers.data.ability.LockMovementType;
|
||||
import kamkeel.npcs.controllers.data.ability.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyBarrierData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.LockMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyBarrierData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.telegraph.TelegraphType;
|
||||
import kamkeel.npcs.entity.EntityAbilityBarrier;
|
||||
import kamkeel.npcs.entity.EntityAbilityDome;
|
||||
@@ -37,7 +37,7 @@ public class AbilityDome extends AbilityBarrier {
|
||||
this.targetingMode = TargetingMode.SELF;
|
||||
this.cooldownTicks = 100;
|
||||
this.windUpTicks = 30;
|
||||
this.lockMovement = LockMovementType.WINDUP;
|
||||
this.lockMovement = LockMode.WINDUP;
|
||||
this.telegraphType = TelegraphType.RING;
|
||||
this.showTelegraph = true;
|
||||
this.windUpAnimationName = "";
|
||||
|
||||
@@ -3,8 +3,8 @@ package kamkeel.npcs.controllers.data.ability.type.energy;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.data.ability.Ability;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyLightningData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyLightningData;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import noppes.npcs.client.gui.builder.FieldDef;
|
||||
|
||||
|
||||
+202
-66
@@ -2,18 +2,21 @@ package kamkeel.npcs.controllers.data.ability.type.energy;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.data.ability.AnchorPoint;
|
||||
import kamkeel.npcs.controllers.data.ability.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyCombatData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyHomingData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyLifespanData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyTrajectoryData;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.AnchorPoint;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.HitType;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyCombatData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyHomingData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyLifespanData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyTrajectoryData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.ProjectileData;
|
||||
import kamkeel.npcs.controllers.data.telegraph.Telegraph;
|
||||
import kamkeel.npcs.controllers.data.telegraph.TelegraphInstance;
|
||||
import kamkeel.npcs.controllers.data.telegraph.TelegraphType;
|
||||
import kamkeel.npcs.entity.EntityEnergyProjectile;
|
||||
import kamkeel.npcs.network.packets.data.energycharge.EnergyChargeRemovePacket;
|
||||
import kamkeel.npcs.network.packets.data.energycharge.EnergyChargeSpawnPacket;
|
||||
import kamkeel.npcs.util.AnchorPointHelper;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
@@ -54,6 +57,10 @@ public abstract class AbilityEnergyProjectile<E extends EntityEnergyProjectile>
|
||||
|
||||
// Transient entity state
|
||||
protected transient E[] entities;
|
||||
protected transient boolean[] projectileSpawned;
|
||||
protected transient int spawnedCount;
|
||||
protected transient String[] chargeVisualIds;
|
||||
protected transient EntityLivingBase chargeVisualCaster;
|
||||
|
||||
// ==================== CONSTRUCTOR ====================
|
||||
|
||||
@@ -76,7 +83,7 @@ public abstract class AbilityEnergyProjectile<E extends EntityEnergyProjectile>
|
||||
|
||||
/**
|
||||
* Create a single entity for the given projectile index.
|
||||
* Called during windup (tick 1) and burst refire (onExecute when entities are null).
|
||||
* Called when a projectile actually fires.
|
||||
*/
|
||||
protected abstract E createEntity(EntityLivingBase caster, EntityLivingBase target,
|
||||
Vec3 spawnPos, EnergyDisplayData resolved, int index);
|
||||
@@ -208,32 +215,99 @@ public abstract class AbilityEnergyProjectile<E extends EntityEnergyProjectile>
|
||||
|
||||
// ==================== ENTITY CREATION ====================
|
||||
|
||||
/**
|
||||
* Create all entities for this ability.
|
||||
* Sets effects, source ability, and sibling links.
|
||||
*/
|
||||
protected E[] createAllEntities(EntityLivingBase caster, EntityLivingBase target) {
|
||||
E[] newEntities = createEntityArray(projectileCount);
|
||||
for (int i = 0; i < projectileCount; i++) {
|
||||
Vec3 spawnPos = getSpawnPosition(caster, i);
|
||||
EnergyDisplayData resolved = projectiles[i].resolveDisplay(displayData);
|
||||
newEntities[i] = createEntity(caster, target, spawnPos, resolved, i);
|
||||
newEntities[i].setEffects(this.effects);
|
||||
newEntities[i].setSourceAbility(this);
|
||||
protected void initRuntimeState(EntityLivingBase caster) {
|
||||
entities = createEntityArray(projectileCount);
|
||||
projectileSpawned = new boolean[projectileCount];
|
||||
spawnedCount = 0;
|
||||
chargeVisualIds = new String[projectileCount];
|
||||
chargeVisualCaster = caster;
|
||||
}
|
||||
|
||||
protected E createProjectileEntity(EntityLivingBase caster, EntityLivingBase target, int index) {
|
||||
Vec3 spawnPos = getSpawnPosition(caster, index);
|
||||
EnergyDisplayData resolved = projectiles[index].resolveDisplay(displayData);
|
||||
E entity = createEntity(caster, target, spawnPos, resolved, index);
|
||||
entity.setEffects(this.effects);
|
||||
entity.setSourceAbility(this);
|
||||
return entity;
|
||||
}
|
||||
|
||||
protected void spawnProjectileEntity(E entity, int index) {
|
||||
if (entity == null) return;
|
||||
spawnAbilityEntity(entity);
|
||||
entities[index] = entity;
|
||||
if (!projectileSpawned[index]) {
|
||||
projectileSpawned[index] = true;
|
||||
spawnedCount++;
|
||||
}
|
||||
return newEntities;
|
||||
}
|
||||
|
||||
protected void spawnAndFireProjectile(EntityLivingBase caster, EntityLivingBase target, int index) {
|
||||
if (entities != null && entities[index] != null && !entities[index].isDead) return;
|
||||
E entity = createProjectileEntity(caster, target, index);
|
||||
|
||||
// Initialize launch position/motion BEFORE spawn so the
|
||||
// initial spawn packet already has centered look-vector data on all clients.
|
||||
fireEntitySafe(entity, caster, target);
|
||||
spawnProjectileEntity(entity, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for launch target selection.
|
||||
* Default returns null so projectiles launch along owner look vector.
|
||||
*/
|
||||
protected EntityLivingBase getLaunchTarget(EntityLivingBase caster, EntityLivingBase target) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire an entity if it's valid.
|
||||
* Clears charging state first so the entity can move and check collisions.
|
||||
*/
|
||||
protected void fireEntitySafe(E entity, EntityLivingBase target) {
|
||||
protected void fireEntitySafe(E entity, EntityLivingBase caster, EntityLivingBase target) {
|
||||
if (entity != null && !entity.isDead) {
|
||||
if (entity.isCharging()) {
|
||||
entity.setCharging(false);
|
||||
}
|
||||
fireEntity(entity, target);
|
||||
fireEntity(entity, getLaunchTarget(caster, target));
|
||||
}
|
||||
}
|
||||
|
||||
protected String getChargeVisualId(EntityLivingBase caster, int index) {
|
||||
return "energy_charge:" + caster.getEntityId() + ":" + executionStartTime + ":" + burstIndex + ":" + index;
|
||||
}
|
||||
|
||||
protected void spawnChargeVisual(EntityLivingBase caster, EntityLivingBase target, int index, int chargeDuration) {
|
||||
if (isPreview() || caster == null || caster.worldObj == null || caster.worldObj.isRemote) return;
|
||||
if (chargeVisualIds == null || index < 0 || index >= chargeVisualIds.length) return;
|
||||
|
||||
E previewEntity = createProjectileEntity(caster, target, index);
|
||||
previewEntity.setPreviewMode(true);
|
||||
previewEntity.setPreviewOwner(caster);
|
||||
setupEntityCharging(previewEntity, projectiles[index], index);
|
||||
previewEntity.setChargeDuration(Math.max(1, chargeDuration));
|
||||
|
||||
String id = getChargeVisualId(caster, index);
|
||||
chargeVisualIds[index] = id;
|
||||
chargeVisualCaster = caster;
|
||||
EnergyChargeSpawnPacket.sendToTracking(id, previewEntity, caster);
|
||||
}
|
||||
|
||||
protected void removeChargeVisual(EntityLivingBase caster, int index) {
|
||||
if (isPreview() || caster == null || caster.worldObj == null || caster.worldObj.isRemote) return;
|
||||
if (chargeVisualIds == null || index < 0 || index >= chargeVisualIds.length) return;
|
||||
|
||||
String id = chargeVisualIds[index];
|
||||
if (id != null && !id.isEmpty()) {
|
||||
EnergyChargeRemovePacket.sendToTracking(id, caster);
|
||||
chargeVisualIds[index] = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected void removeAllChargeVisuals(EntityLivingBase caster) {
|
||||
if (chargeVisualIds == null || caster == null) return;
|
||||
for (int i = 0; i < chargeVisualIds.length; i++) {
|
||||
removeChargeVisual(caster, i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,6 +326,8 @@ public abstract class AbilityEnergyProjectile<E extends EntityEnergyProjectile>
|
||||
@Override
|
||||
public void detach() {
|
||||
entities = null;
|
||||
projectileSpawned = null;
|
||||
spawnedCount = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -274,21 +350,31 @@ public abstract class AbilityEnergyProjectile<E extends EntityEnergyProjectile>
|
||||
if (caster.worldObj.isRemote && !isPreview()) return;
|
||||
|
||||
if (tick == 1) {
|
||||
entities = createAllEntities(caster, target);
|
||||
for (int i = 0; i < projectileCount; i++) {
|
||||
EnergyDisplayData resolved = projectiles[i].resolveDisplay(displayData);
|
||||
if (isPreview()) {
|
||||
setupEntityPreview(entities[i], caster, resolved, projectiles[i], i);
|
||||
} else {
|
||||
setupEntityCharging(entities[i], projectiles[i], i);
|
||||
initRuntimeState(caster);
|
||||
|
||||
if (isPreview()) {
|
||||
for (int i = 0; i < projectileCount; i++) {
|
||||
E previewEntity = createProjectileEntity(caster, target, i);
|
||||
EnergyDisplayData resolved = projectiles[i].resolveDisplay(displayData);
|
||||
setupEntityPreview(previewEntity, caster, resolved, projectiles[i], i);
|
||||
spawnAbilityEntity(previewEntity);
|
||||
entities[i] = previewEntity;
|
||||
projectileSpawned[i] = true;
|
||||
spawnedCount++;
|
||||
}
|
||||
spawnAbilityEntity(entities[i]);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < projectileCount; i++) {
|
||||
spawnChargeVisual(caster, target, i, windUpTicks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetForBurst() {
|
||||
removeAllChargeVisuals(chargeVisualCaster);
|
||||
|
||||
// Non-overlap: kill old entities before next burst creates new ones
|
||||
if (!burstOverlap) {
|
||||
cleanup();
|
||||
@@ -296,54 +382,71 @@ public abstract class AbilityEnergyProjectile<E extends EntityEnergyProjectile>
|
||||
// Clear reference so the next iteration creates fresh entities.
|
||||
// For overlap mode, old entities stay alive (tracked in burstEntities).
|
||||
entities = null;
|
||||
projectileSpawned = null;
|
||||
spawnedCount = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExecute(EntityLivingBase caster, EntityLivingBase target) {
|
||||
// Create entities if they don't exist yet.
|
||||
// They'll already exist if onWindUpTick ran (windup replay), otherwise we create here.
|
||||
if (entities == null) {
|
||||
entities = createAllEntities(caster, target);
|
||||
|
||||
// If there's a fire delay, put unfired entities in charging state
|
||||
// to prevent collision checks before they're actually fired.
|
||||
// Entity[0] will be fired immediately below, so skip it.
|
||||
if (fireDelay > 0 && projectileCount > 1) {
|
||||
for (int i = 1; i < projectileCount; i++) {
|
||||
if (entities[i] != null) {
|
||||
entities[i].setCharging(true);
|
||||
}
|
||||
// GUI preview keeps the original non-world preview entity flow.
|
||||
if (isPreview()) {
|
||||
if (entities == null) {
|
||||
initRuntimeState(caster);
|
||||
for (int i = 0; i < projectileCount; i++) {
|
||||
E previewEntity = createProjectileEntity(caster, target, i);
|
||||
EnergyDisplayData resolved = projectiles[i].resolveDisplay(displayData);
|
||||
setupEntityPreview(previewEntity, caster, resolved, projectiles[i], i);
|
||||
spawnAbilityEntity(previewEntity);
|
||||
entities[i] = previewEntity;
|
||||
projectileSpawned[i] = true;
|
||||
spawnedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
for (E entity : entities) {
|
||||
spawnAbilityEntity(entity);
|
||||
fireEntitySafe(entities[0], caster, target);
|
||||
if (fireDelay <= 0) {
|
||||
for (int i = 1; i < projectileCount; i++) {
|
||||
fireEntitySafe(entities[i], caster, target);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Fire first projectile immediately
|
||||
fireEntitySafe(entities[0], target);
|
||||
if (entities == null || projectileSpawned == null || entities.length != projectileCount) {
|
||||
initRuntimeState(caster);
|
||||
}
|
||||
|
||||
// Fire remaining projectiles if no delay
|
||||
removeAllChargeVisuals(caster);
|
||||
|
||||
// Fire first projectile immediately.
|
||||
spawnAndFireProjectile(caster, target, 0);
|
||||
|
||||
// Fire remaining projectiles immediately if there is no stagger delay.
|
||||
if (fireDelay <= 0) {
|
||||
for (int i = 1; i < projectileCount; i++) {
|
||||
fireEntitySafe(entities[i], target);
|
||||
spawnAndFireProjectile(caster, target, i);
|
||||
}
|
||||
} else {
|
||||
// During active stagger, keep client-only charging visuals for unfired projectiles.
|
||||
for (int i = 1; i < projectileCount; i++) {
|
||||
spawnChargeVisual(caster, target, i, fireDelay * i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActiveTick(EntityLivingBase caster, EntityLivingBase target, int tick) {
|
||||
if (entities == null) {
|
||||
if (entities == null || projectileSpawned == null) {
|
||||
signalCompletion();
|
||||
return;
|
||||
}
|
||||
|
||||
// Fire staggered projectiles
|
||||
// Fire staggered projectiles: spawn real world entity only when it is time to fire.
|
||||
if (fireDelay > 0) {
|
||||
for (int i = 1; i < projectileCount; i++) {
|
||||
if (tick == fireDelay * i) {
|
||||
fireEntitySafe(entities[i], target);
|
||||
removeChargeVisual(caster, i);
|
||||
spawnAndFireProjectile(caster, target, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -357,21 +460,21 @@ public abstract class AbilityEnergyProjectile<E extends EntityEnergyProjectile>
|
||||
}
|
||||
}
|
||||
|
||||
// Check if all entities are dead or unreachable
|
||||
boolean allDead = true;
|
||||
for (E entity : entities) {
|
||||
if (entity == null || entity.isDead) continue;
|
||||
// Before all projectiles have fired, the ability is still active by definition.
|
||||
boolean allDead = spawnedCount >= projectileCount;
|
||||
if (allDead) {
|
||||
for (E entity : entities) {
|
||||
if (entity == null || entity.isDead) continue;
|
||||
|
||||
// Check if entity was removed from world (chunk unload, etc.)
|
||||
// Skip this check for preview entities - they're not registered in the world
|
||||
if (!isPreview() && tick > 5 && entity.worldObj != null
|
||||
&& entity.worldObj.getEntityByID(entity.getEntityId()) != entity) {
|
||||
entity.setDead();
|
||||
continue;
|
||||
if (!isPreview() && tick > 5 && entity.worldObj != null
|
||||
&& entity.worldObj.getEntityByID(entity.getEntityId()) != entity) {
|
||||
entity.setDead();
|
||||
continue;
|
||||
}
|
||||
|
||||
allDead = false;
|
||||
break;
|
||||
}
|
||||
|
||||
allDead = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Failsafe timeout: force completion if active phase exceeded max possible flight time
|
||||
@@ -391,15 +494,19 @@ public abstract class AbilityEnergyProjectile<E extends EntityEnergyProjectile>
|
||||
|
||||
@Override
|
||||
public void onComplete(EntityLivingBase caster, EntityLivingBase target) {
|
||||
removeAllChargeVisuals(caster);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInterrupt(EntityLivingBase caster, DamageSource source, float damage) {
|
||||
removeAllChargeVisuals(caster);
|
||||
cleanup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup() {
|
||||
removeAllChargeVisuals(chargeVisualCaster);
|
||||
|
||||
if (entities != null) {
|
||||
for (int i = 0; i < entities.length; i++) {
|
||||
if (entities[i] != null && !entities[i].isDead) {
|
||||
@@ -409,6 +516,10 @@ public abstract class AbilityEnergyProjectile<E extends EntityEnergyProjectile>
|
||||
}
|
||||
}
|
||||
entities = null;
|
||||
projectileSpawned = null;
|
||||
spawnedCount = 0;
|
||||
chargeVisualIds = null;
|
||||
chargeVisualCaster = null;
|
||||
}
|
||||
|
||||
// ==================== TELEGRAPH ====================
|
||||
@@ -557,6 +668,22 @@ public abstract class AbilityEnergyProjectile<E extends EntityEnergyProjectile>
|
||||
combatData.explosionDamageFalloff = falloff;
|
||||
}
|
||||
|
||||
public int getHitType() {
|
||||
return combatData.hitType.ordinal();
|
||||
}
|
||||
|
||||
public void setHitType(int hitType) {
|
||||
this.combatData.hitType = HitType.fromOrdinal(hitType);
|
||||
}
|
||||
|
||||
public int getMultiHitDelayTicks() {
|
||||
return combatData.multiHitDelayTicks;
|
||||
}
|
||||
|
||||
public void setMultiHitDelayTicks(int delay) {
|
||||
this.combatData.multiHitDelayTicks = Math.max(1, delay);
|
||||
}
|
||||
|
||||
// Lifespan data
|
||||
@Override
|
||||
public float getMaxDistance() {
|
||||
@@ -696,6 +823,15 @@ public abstract class AbilityEnergyProjectile<E extends EntityEnergyProjectile>
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public final void getAbilityDefinitions(List<FieldDef> defs) {
|
||||
defs.add(FieldDef.enumField("ability.hitType", HitType.class,
|
||||
() -> HitType.fromOrdinal(getHitType()),
|
||||
(v) -> setHitType(v.ordinal()))
|
||||
.hover("ability.hover.hitType"));
|
||||
defs.add(FieldDef.intField("ability.multiHitDelay", this::getMultiHitDelayTicks, this::setMultiHitDelayTicks)
|
||||
.range(1, 200)
|
||||
.visibleWhen(() -> getHitType() == HitType.MULTI.ordinal())
|
||||
.hover("ability.hover.multiHitDelay"));
|
||||
|
||||
// Type-specific fields first
|
||||
addTypeDefinitions(defs);
|
||||
|
||||
|
||||
+11
-27
@@ -2,14 +2,11 @@ package kamkeel.npcs.controllers.data.ability.type.energy;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.data.ability.AnchorPoint;
|
||||
import kamkeel.npcs.controllers.data.ability.LockMovementType;
|
||||
import kamkeel.npcs.controllers.data.ability.RotationMode;
|
||||
import kamkeel.npcs.controllers.data.ability.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyCombatData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyHomingData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyLifespanData;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.*;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyCombatData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyHomingData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyLifespanData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.ProjectileData;
|
||||
import kamkeel.npcs.controllers.data.ability.gui.AbilityFieldDefs;
|
||||
import kamkeel.npcs.controllers.data.telegraph.Telegraph;
|
||||
@@ -25,8 +22,8 @@ import noppes.npcs.client.gui.builder.FieldDef;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Laser Shot ability: Fast expanding thin line that pierces through targets.
|
||||
* Travels in a straight line from origin, damaging all entities it passes through.
|
||||
* Laser Shot ability: Fast expanding thin line.
|
||||
* Travels in a straight line from origin.
|
||||
* Single-projectile only (always projectileCount=1).
|
||||
*/
|
||||
public class AbilityLaserShot extends AbilityEnergyProjectile<EntityAbilityLaser> implements IAbilityLaserShot {
|
||||
@@ -34,12 +31,11 @@ public class AbilityLaserShot extends AbilityEnergyProjectile<EntityAbilityLaser
|
||||
private float laserWidth = 0.3f;
|
||||
private float expansionSpeed = 3.0f;
|
||||
private int lingerTicks = 8;
|
||||
private boolean dieOnImpact = false;
|
||||
|
||||
public AbilityLaserShot() {
|
||||
super(
|
||||
new EnergyDisplayData(0xFFFFFF, 0xFF0000, true, 0.4f, 0.5f, 0.0f),
|
||||
new EnergyCombatData(6.0f, 0.5f, 0.05f, false, 2.0f, 0.5f),
|
||||
new EnergyCombatData(6.0f, 0.5f, 0.05f, false, 2.0f, 0.5f, HitType.PIERCE, 5),
|
||||
new EnergyHomingData(),
|
||||
new EnergyLifespanData(150.0f, 100)
|
||||
);
|
||||
@@ -50,9 +46,9 @@ public class AbilityLaserShot extends AbilityEnergyProjectile<EntityAbilityLaser
|
||||
this.minRange = 3.0f;
|
||||
this.cooldownTicks = 0;
|
||||
this.windUpTicks = 15;
|
||||
this.lockMovement = LockMovementType.WINDUP_AND_ACTIVE;
|
||||
this.lockMovement = LockMode.WINDUP_AND_ACTIVE;
|
||||
this.rotationMode = RotationMode.TRACK;
|
||||
this.rotationPhase = LockMovementType.WINDUP;
|
||||
this.rotationPhase = LockMode.WINDUP;
|
||||
this.telegraphType = TelegraphType.LINE;
|
||||
this.showTelegraph = true;
|
||||
this.windUpAnimationName = "Ability_Laser_Windup";
|
||||
@@ -123,7 +119,6 @@ public class AbilityLaserShot extends AbilityEnergyProjectile<EntityAbilityLaser
|
||||
laserWidth,
|
||||
resolved, combatData, lightningData, lifespanData, trajectoryData,
|
||||
expansionSpeed, lingerTicks);
|
||||
laser.setDieOnImpact(dieOnImpact);
|
||||
return laser;
|
||||
}
|
||||
|
||||
@@ -141,7 +136,7 @@ public class AbilityLaserShot extends AbilityEnergyProjectile<EntityAbilityLaser
|
||||
@Override
|
||||
protected void setupEntityPreview(EntityAbilityLaser laser, EntityLivingBase caster,
|
||||
EnergyDisplayData resolved, ProjectileData projData, int index) {
|
||||
laser.setupPreview(caster, laserWidth, resolved, lightningData, expansionSpeed, lifespanData.maxDistance);
|
||||
laser.setupPreview(caster, laserWidth, resolved, lightningData, projData.anchor, windUpTicks, expansionSpeed, lifespanData.maxDistance);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -161,7 +156,6 @@ public class AbilityLaserShot extends AbilityEnergyProjectile<EntityAbilityLaser
|
||||
nbt.setFloat("laserWidth", laserWidth);
|
||||
nbt.setFloat("expansionSpeed", expansionSpeed);
|
||||
nbt.setInteger("lingerTicks", lingerTicks);
|
||||
nbt.setBoolean("dieOnImpact", dieOnImpact);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -169,7 +163,6 @@ public class AbilityLaserShot extends AbilityEnergyProjectile<EntityAbilityLaser
|
||||
this.laserWidth = nbt.getFloat("laserWidth");
|
||||
this.expansionSpeed = nbt.getFloat("expansionSpeed");
|
||||
this.lingerTicks = nbt.getInteger("lingerTicks");
|
||||
this.dieOnImpact = nbt.getBoolean("dieOnImpact");
|
||||
}
|
||||
|
||||
// ==================== TYPE-SPECIFIC GETTERS ====================
|
||||
@@ -198,14 +191,6 @@ public class AbilityLaserShot extends AbilityEnergyProjectile<EntityAbilityLaser
|
||||
this.lingerTicks = lingerTicks;
|
||||
}
|
||||
|
||||
public boolean isDieOnImpact() {
|
||||
return dieOnImpact;
|
||||
}
|
||||
|
||||
public void setDieOnImpact(boolean dieOnImpact) {
|
||||
this.dieOnImpact = dieOnImpact;
|
||||
}
|
||||
|
||||
// ==================== TYPE-SPECIFIC GUI ====================
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@@ -226,7 +211,6 @@ public class AbilityLaserShot extends AbilityEnergyProjectile<EntityAbilityLaser
|
||||
FieldDef.intField("ability.lifetime", this::getMaxLifetime, this::setMaxLifetime).range(1, 1200)
|
||||
));
|
||||
defs.add(FieldDef.floatField("ability.maxDistance", this::getMaxDistance, this::setMaxDistance).range(1.0f, 500.0f));
|
||||
defs.add(FieldDef.boolField("ability.dieOnImpact", this::isDieOnImpact, this::setDieOnImpact));
|
||||
defs.add(FieldDef.section("ability.section.explosive"));
|
||||
defs.add(FieldDef.boolField("gui.enabled", this::isExplosive, this::setExplosive).hover("ability.hover.explosive"));
|
||||
defs.add(FieldDef.floatField("gui.radius", this::getExplosionRadius, this::setExplosionRadius).visibleWhen(this::isExplosive));
|
||||
|
||||
@@ -3,12 +3,12 @@ package kamkeel.npcs.controllers.data.ability.type.energy;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityVariant;
|
||||
import kamkeel.npcs.controllers.data.ability.LockMovementType;
|
||||
import kamkeel.npcs.controllers.data.ability.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyCombatData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyHomingData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyLifespanData;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.LockMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyCombatData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyHomingData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyLifespanData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.ProjectileData;
|
||||
import kamkeel.npcs.controllers.data.ability.gui.AbilityFieldDefs;
|
||||
import kamkeel.npcs.controllers.data.telegraph.TelegraphType;
|
||||
@@ -44,7 +44,7 @@ public class AbilityOrb extends AbilityEnergyProjectile<EntityAbilityOrb> implem
|
||||
this.minRange = 5.0f;
|
||||
this.cooldownTicks = 0;
|
||||
this.windUpTicks = 30;
|
||||
this.lockMovement = LockMovementType.WINDUP;
|
||||
this.lockMovement = LockMode.WINDUP;
|
||||
this.telegraphType = TelegraphType.CIRCLE;
|
||||
this.showTelegraph = true;
|
||||
this.windUpAnimationName = "Ability_Orb_Windup";
|
||||
@@ -117,7 +117,7 @@ public class AbilityOrb extends AbilityEnergyProjectile<EntityAbilityOrb> implem
|
||||
orb.setKnockbackUp(0.0f);
|
||||
orb.setProjectileCount(2);
|
||||
orb.setFireDelay(2);
|
||||
a.setLockMovement(LockMovementType.WINDUP_AND_ACTIVE);
|
||||
a.setLockMovement(LockMode.WINDUP_AND_ACTIVE);
|
||||
a.setMaxRange(75.0f);
|
||||
a.setBurstEnabled(true);
|
||||
a.setBurstAmount(15);
|
||||
|
||||
@@ -3,12 +3,12 @@ package kamkeel.npcs.controllers.data.ability.type.energy;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityVariant;
|
||||
import kamkeel.npcs.controllers.data.ability.LockMovementType;
|
||||
import kamkeel.npcs.controllers.data.ability.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.UserType;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyBarrierData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyPanelData;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.LockMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.UserType;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyBarrierData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyPanelData;
|
||||
import kamkeel.npcs.controllers.data.telegraph.TelegraphType;
|
||||
import kamkeel.npcs.entity.EntityAbilityBarrier;
|
||||
import kamkeel.npcs.entity.EntityAbilityPanel;
|
||||
@@ -39,7 +39,7 @@ public class AbilityShield extends AbilityBarrier {
|
||||
this.targetingMode = TargetingMode.SELF;
|
||||
this.cooldownTicks = 60;
|
||||
this.windUpTicks = 10;
|
||||
this.lockMovement = LockMovementType.WINDUP_AND_ACTIVE;
|
||||
this.lockMovement = LockMode.WINDUP_AND_ACTIVE;
|
||||
this.telegraphType = TelegraphType.NONE;
|
||||
this.showTelegraph = false;
|
||||
this.windUpAnimationName = "";
|
||||
|
||||
@@ -3,12 +3,12 @@ package kamkeel.npcs.controllers.data.ability.type.energy;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityVariant;
|
||||
import kamkeel.npcs.controllers.data.ability.LockMovementType;
|
||||
import kamkeel.npcs.controllers.data.ability.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyCombatData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyHomingData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyLifespanData;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.LockMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyCombatData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyHomingData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyLifespanData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.ProjectileData;
|
||||
import kamkeel.npcs.controllers.data.ability.gui.AbilityFieldDefs;
|
||||
import kamkeel.npcs.controllers.data.telegraph.TelegraphType;
|
||||
@@ -23,14 +23,13 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* Energy Slicer ability: Fires a thin, wide blade projectile in a straight line.
|
||||
* JJK Cleave/Dismantle inspired. Piercing by default.
|
||||
* JJK Cleave/Dismantle inspired.
|
||||
* Extends AbilityEnergyProjectile to leverage the existing projectile infrastructure.
|
||||
*/
|
||||
public class AbilitySlicer extends AbilityEnergyProjectile<EntityEnergySlicer> {
|
||||
|
||||
private float sliceWidth = 3.0f;
|
||||
private float sliceThickness = 0.15f;
|
||||
private boolean piercing = true;
|
||||
|
||||
public AbilitySlicer() {
|
||||
super(
|
||||
@@ -46,7 +45,7 @@ public class AbilitySlicer extends AbilityEnergyProjectile<EntityEnergySlicer> {
|
||||
this.minRange = 3.0f;
|
||||
this.cooldownTicks = 40;
|
||||
this.windUpTicks = 15;
|
||||
this.lockMovement = LockMovementType.WINDUP;
|
||||
this.lockMovement = LockMode.WINDUP;
|
||||
this.telegraphType = TelegraphType.LINE;
|
||||
this.showTelegraph = true;
|
||||
this.windUpAnimationName = "";
|
||||
@@ -62,8 +61,7 @@ public class AbilitySlicer extends AbilityEnergyProjectile<EntityEnergySlicer> {
|
||||
caster.worldObj, caster, target,
|
||||
spawnPos.xCoord, spawnPos.yCoord, spawnPos.zCoord,
|
||||
sliceWidth, sliceThickness,
|
||||
resolved, combatData, homingData, lightningData, lifespanData, trajectoryData,
|
||||
piercing
|
||||
resolved, combatData, homingData, lightningData, lifespanData, trajectoryData
|
||||
);
|
||||
}
|
||||
|
||||
@@ -122,14 +120,12 @@ public class AbilitySlicer extends AbilityEnergyProjectile<EntityEnergySlicer> {
|
||||
protected void writeTypeSpecificNBT(NBTTagCompound nbt) {
|
||||
nbt.setFloat("sliceWidth", sliceWidth);
|
||||
nbt.setFloat("sliceThickness", sliceThickness);
|
||||
nbt.setBoolean("piercing", piercing);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void readTypeSpecificNBT(NBTTagCompound nbt) {
|
||||
this.sliceWidth = nbt.hasKey("sliceWidth") ? nbt.getFloat("sliceWidth") : 3.0f;
|
||||
this.sliceThickness = nbt.hasKey("sliceThickness") ? nbt.getFloat("sliceThickness") : 0.15f;
|
||||
this.piercing = !nbt.hasKey("piercing") || nbt.getBoolean("piercing");
|
||||
}
|
||||
|
||||
// ==================== TYPE-SPECIFIC GETTERS ====================
|
||||
@@ -158,14 +154,6 @@ public class AbilitySlicer extends AbilityEnergyProjectile<EntityEnergySlicer> {
|
||||
this.sliceThickness = Math.max(0.05f, thickness);
|
||||
}
|
||||
|
||||
public boolean isPiercing() {
|
||||
return piercing;
|
||||
}
|
||||
|
||||
public void setPiercing(boolean piercing) {
|
||||
this.piercing = piercing;
|
||||
}
|
||||
|
||||
// ==================== TYPE-SPECIFIC GUI ====================
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@@ -186,8 +174,6 @@ public class AbilitySlicer extends AbilityEnergyProjectile<EntityEnergySlicer> {
|
||||
FieldDef.floatField("gui.width", this::getSliceWidth, this::setSliceWidth).range(0.5f, 100.0f),
|
||||
FieldDef.floatField("gui.thickness", this::getSliceThickness, this::setSliceThickness).range(0.05f, 100.0f)
|
||||
));
|
||||
defs.add(FieldDef.boolField("ability.piercing", this::isPiercing, this::setPiercing)
|
||||
.hover("ability.hover.piercing"));
|
||||
defs.add(FieldDef.row(
|
||||
FieldDef.floatField("ability.maxDistance", this::getMaxDistance, this::setMaxDistance).range(1.0f, 500.0f),
|
||||
FieldDef.intField("ability.lifetime", this::getMaxLifetime, this::setMaxLifetime).range(1, 1200)
|
||||
|
||||
@@ -2,9 +2,9 @@ package kamkeel.npcs.controllers.data.ability.type.energy;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.data.ability.LockMovementType;
|
||||
import kamkeel.npcs.controllers.data.ability.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.LockMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.gui.AbilityFieldDefs;
|
||||
import kamkeel.npcs.controllers.data.telegraph.Telegraph;
|
||||
import kamkeel.npcs.controllers.data.telegraph.TelegraphInstance;
|
||||
@@ -49,7 +49,7 @@ public class AbilitySweeper extends AbilityEnergy implements IAbilitySweeper {
|
||||
this.targetingMode = TargetingMode.AGGRO_TARGET;
|
||||
this.maxRange = 15.0f;
|
||||
this.minRange = 0.0f;
|
||||
this.lockMovement = LockMovementType.WINDUP_AND_ACTIVE;
|
||||
this.lockMovement = LockMode.WINDUP_AND_ACTIVE;
|
||||
this.cooldownTicks = 0;
|
||||
this.windUpTicks = 60;
|
||||
// Circle telegraph to show range
|
||||
|
||||
@@ -3,11 +3,11 @@ package kamkeel.npcs.controllers.data.ability.type.energy;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityVariant;
|
||||
import kamkeel.npcs.controllers.data.ability.LockMovementType;
|
||||
import kamkeel.npcs.controllers.data.ability.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyBarrierData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyPanelData;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.LockMode;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetingMode;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyBarrierData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyPanelData;
|
||||
import kamkeel.npcs.controllers.data.telegraph.TelegraphType;
|
||||
import kamkeel.npcs.entity.EntityAbilityBarrier;
|
||||
import kamkeel.npcs.entity.EntityAbilityPanel;
|
||||
@@ -40,7 +40,7 @@ public class AbilityWall extends AbilityBarrier {
|
||||
this.minRange = 2.0f;
|
||||
this.cooldownTicks = 80;
|
||||
this.windUpTicks = 20;
|
||||
this.lockMovement = LockMovementType.WINDUP;
|
||||
this.lockMovement = LockMode.WINDUP;
|
||||
this.telegraphType = TelegraphType.LINE;
|
||||
this.showTelegraph = true;
|
||||
this.windUpAnimationName = "";
|
||||
|
||||
+3
-2
@@ -1,5 +1,6 @@
|
||||
package kamkeel.npcs.controllers.data.ability;
|
||||
package kamkeel.npcs.controllers.data.ability.util;
|
||||
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetFilter;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
@@ -84,7 +85,7 @@ public class AbilityTargetHelper {
|
||||
* @return true if the target should be affected
|
||||
*/
|
||||
public static boolean shouldAffect(EntityLivingBase caster, Entity target,
|
||||
TargetFilter filter, boolean includeSelf) {
|
||||
TargetFilter filter, boolean includeSelf) {
|
||||
if (target == caster) return includeSelf;
|
||||
if (!(target instanceof EntityLivingBase)) return false;
|
||||
if (!target.isEntityAlive()) return false;
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
package kamkeel.npcs.controllers.data.energycharge;
|
||||
|
||||
import kamkeel.npcs.entity.EntityEnergyProjectile;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Client-side manager for packet-driven charging preview entities.
|
||||
* These entities are never spawned into the world; they are ticked and rendered manually.
|
||||
*/
|
||||
public class EnergyChargePreviewManager {
|
||||
|
||||
public static EnergyChargePreviewManager ClientInstance;
|
||||
|
||||
private final ConcurrentHashMap<String, EntityEnergyProjectile> previews = new ConcurrentHashMap<String, EntityEnergyProjectile>();
|
||||
|
||||
public static void initClient() {
|
||||
ClientInstance = new EnergyChargePreviewManager();
|
||||
}
|
||||
|
||||
public void addPreview(String instanceId, EntityEnergyProjectile entity) {
|
||||
if (instanceId == null || entity == null) return;
|
||||
previews.put(instanceId, entity);
|
||||
}
|
||||
|
||||
public void removePreview(String instanceId) {
|
||||
if (instanceId == null) return;
|
||||
previews.remove(instanceId);
|
||||
}
|
||||
|
||||
public Collection<EntityEnergyProjectile> getPreviews() {
|
||||
return previews.values();
|
||||
}
|
||||
|
||||
public boolean hasPreviews() {
|
||||
return !previews.isEmpty();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
previews.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tick all preview entities and remove invalid/expired entries.
|
||||
*/
|
||||
public void tick(World world) {
|
||||
if (world == null) {
|
||||
clear();
|
||||
return;
|
||||
}
|
||||
|
||||
Iterator<EntityEnergyProjectile> iterator = previews.values().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
EntityEnergyProjectile entity = iterator.next();
|
||||
if (entity == null || entity.isDead || entity.worldObj != world) {
|
||||
iterator.remove();
|
||||
continue;
|
||||
}
|
||||
entity.onUpdate();
|
||||
if (entity.isDead) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package kamkeel.npcs.entity;
|
||||
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityTargetHelper;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyBarrierData;
|
||||
import kamkeel.npcs.controllers.data.ability.util.AbilityTargetHelper;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyBarrierData;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
package kamkeel.npcs.entity;
|
||||
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyAnchorData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyCombatData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyHomingData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyLifespanData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyLightningData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyTrajectoryData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyAnchorData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyCombatData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyHomingData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyLifespanData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyLightningData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyTrajectoryData;
|
||||
import kamkeel.npcs.util.AnchorPointHelper;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
import net.minecraft.util.AxisAlignedBB;
|
||||
import net.minecraft.util.MovingObjectPosition;
|
||||
import net.minecraft.util.Vec3;
|
||||
import net.minecraft.world.World;
|
||||
import noppes.npcs.EventHooks;
|
||||
import noppes.npcs.LogWriter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -33,6 +32,11 @@ import java.util.List;
|
||||
*/
|
||||
public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
|
||||
private enum BeamMode {
|
||||
ANCHORED,
|
||||
FREE_TRAIL
|
||||
}
|
||||
|
||||
// Beam shape properties
|
||||
private float beamWidth = 0.3f;
|
||||
private float headSize = 0.5f;
|
||||
@@ -41,22 +45,18 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
private List<Vec3> trailPoints = new ArrayList<>();
|
||||
private static final int MAX_TRAIL_POINTS = 200;
|
||||
private static final double MIN_POINT_DISTANCE = 0.2;
|
||||
private static final int TRAIL_COMPACT_THRESHOLD = 128;
|
||||
private int trailStartIndex = 0;
|
||||
|
||||
// Head position relative to origin
|
||||
private double headOffsetX, headOffsetY, headOffsetZ;
|
||||
private double prevHeadOffsetX, prevHeadOffsetY, prevHeadOffsetZ;
|
||||
|
||||
// Origin stays fixed (or follows owner)
|
||||
private boolean attachedToOwner = true;
|
||||
|
||||
// Whether to render tail orb (only when anchored)
|
||||
private boolean renderTailOrb = true;
|
||||
// Beam behavior mode (single source of truth).
|
||||
private BeamMode beamMode = BeamMode.ANCHORED;
|
||||
|
||||
// Charging state (beam-specific)
|
||||
private float chargeOffsetDistance = 1.0f;
|
||||
|
||||
// Trail fading for non-anchored beams (comet effect)
|
||||
private boolean fadeTrail = false;
|
||||
private int trailFadeTime = 20; // Ticks for trail to fully fade
|
||||
private List<Integer> trailPointAges = new ArrayList<>();
|
||||
|
||||
@@ -67,6 +67,55 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
super(world);
|
||||
}
|
||||
|
||||
private static BeamMode modeFromAnchored(boolean anchored) {
|
||||
return anchored ? BeamMode.ANCHORED : BeamMode.FREE_TRAIL;
|
||||
}
|
||||
|
||||
private boolean isAnchoredMode() {
|
||||
return beamMode == BeamMode.ANCHORED;
|
||||
}
|
||||
|
||||
private boolean isFadingMode() {
|
||||
return beamMode == BeamMode.FREE_TRAIL;
|
||||
}
|
||||
|
||||
private int getActiveTrailSize() {
|
||||
return Math.max(0, trailPoints.size() - trailStartIndex);
|
||||
}
|
||||
|
||||
private void resetHeadOffsets() {
|
||||
headOffsetX = 0;
|
||||
headOffsetY = 0;
|
||||
headOffsetZ = 0;
|
||||
prevHeadOffsetX = 0;
|
||||
prevHeadOffsetY = 0;
|
||||
prevHeadOffsetZ = 0;
|
||||
}
|
||||
|
||||
private void resetTrailStorage() {
|
||||
trailPoints.clear();
|
||||
trailPointAges.clear();
|
||||
trailStartIndex = 0;
|
||||
}
|
||||
|
||||
private void setBeamMode(BeamMode mode) {
|
||||
beamMode = mode != null ? mode : BeamMode.ANCHORED;
|
||||
if (isFadingMode()) {
|
||||
while (trailPointAges.size() < trailPoints.size()) {
|
||||
trailPointAges.add(0);
|
||||
}
|
||||
while (trailPointAges.size() > trailPoints.size()) {
|
||||
trailPointAges.remove(trailPointAges.size() - 1);
|
||||
}
|
||||
} else {
|
||||
if (trailStartIndex > 0 && trailStartIndex < trailPoints.size()) {
|
||||
trailPoints = new ArrayList<Vec3>(trailPoints.subList(trailStartIndex, trailPoints.size()));
|
||||
}
|
||||
trailStartIndex = 0;
|
||||
trailPointAges.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Full constructor with all parameters using data classes.
|
||||
*
|
||||
@@ -91,17 +140,11 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
this.beamWidth = beamWidth;
|
||||
this.headSize = headSize;
|
||||
|
||||
// Anchored mode controls whether origin follows owner and tail orb is rendered
|
||||
this.attachedToOwner = anchoredMode;
|
||||
this.renderTailOrb = anchoredMode;
|
||||
// Beam mode controls tail attachment/orb/fading behavior.
|
||||
setBeamMode(modeFromAnchored(anchoredMode));
|
||||
|
||||
// Initialize head offset at origin (0,0,0 relative)
|
||||
this.headOffsetX = 0;
|
||||
this.headOffsetY = 0;
|
||||
this.headOffsetZ = 0;
|
||||
this.prevHeadOffsetX = 0;
|
||||
this.prevHeadOffsetY = 0;
|
||||
this.prevHeadOffsetZ = 0;
|
||||
resetHeadOffsets();
|
||||
|
||||
// Add initial trail point at origin (relative 0,0,0)
|
||||
trailPoints.add(Vec3.createVectorHelper(0, 0, 0));
|
||||
@@ -115,166 +158,58 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
}
|
||||
|
||||
public void setupCharging(EnergyAnchorData anchor, int chargeDuration, float chargeOffsetDistance) {
|
||||
setCharging(true);
|
||||
this.chargeDuration = chargeDuration;
|
||||
this.chargeTick = 0;
|
||||
setupChargingState(anchor, chargeDuration);
|
||||
this.chargeOffsetDistance = chargeOffsetDistance;
|
||||
this.anchorData = anchor;
|
||||
this.fadeTrail = !attachedToOwner;
|
||||
this.motionX = 0;
|
||||
this.motionY = 0;
|
||||
this.motionZ = 0;
|
||||
clearMotion();
|
||||
}
|
||||
|
||||
public void setupPreview(EntityLivingBase owner, float beamWidth, float headSize, EnergyDisplayData display, EnergyLightningData lightning, EnergyAnchorData anchor, int chargeDuration, float chargeOffsetDistance) {
|
||||
this.setPreviewMode(true);
|
||||
this.setPreviewOwner(owner);
|
||||
setupPreviewState(owner, display, lightning, anchor, chargeDuration);
|
||||
|
||||
// Set visual properties
|
||||
this.beamWidth = beamWidth;
|
||||
this.headSize = headSize;
|
||||
this.size = headSize;
|
||||
this.displayData = display;
|
||||
this.lightningData = lightning;
|
||||
setVisualSize(headSize);
|
||||
|
||||
// Set charging state
|
||||
this.setCharging(true);
|
||||
this.chargeDuration = chargeDuration;
|
||||
this.chargeTick = 0;
|
||||
this.chargeOffsetDistance = chargeOffsetDistance;
|
||||
this.anchorData = anchor;
|
||||
|
||||
// Initial position at anchor point
|
||||
Vec3 pos = AnchorPointHelper.calculateAnchorPosition(owner, anchorData, chargeOffsetDistance);
|
||||
this.setPosition(pos.xCoord, pos.yCoord, pos.zCoord);
|
||||
this.prevPosX = pos.xCoord;
|
||||
this.prevPosY = pos.yCoord;
|
||||
this.prevPosZ = pos.zCoord;
|
||||
this.startX = pos.xCoord;
|
||||
this.startY = pos.yCoord;
|
||||
this.startZ = pos.zCoord;
|
||||
setChargeOriginFromAnchor(owner, anchorData, chargeOffsetDistance);
|
||||
|
||||
// Initialize head offsets
|
||||
this.headOffsetX = 0;
|
||||
this.headOffsetY = 0;
|
||||
this.headOffsetZ = 0;
|
||||
this.prevHeadOffsetX = 0;
|
||||
this.prevHeadOffsetY = 0;
|
||||
this.prevHeadOffsetZ = 0;
|
||||
|
||||
// Attach to owner for anchor following
|
||||
this.attachedToOwner = true;
|
||||
this.renderTailOrb = true;
|
||||
resetHeadOffsets();
|
||||
|
||||
// Clear motion
|
||||
this.motionX = 0;
|
||||
this.motionY = 0;
|
||||
this.motionZ = 0;
|
||||
clearMotion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start preview firing (simulates firing toward a point in front of NPC).
|
||||
*/
|
||||
public void startPreviewFiring() {
|
||||
setCharging(false);
|
||||
|
||||
// Origin (tail) stays at the charged position
|
||||
startX = posX;
|
||||
startY = posY;
|
||||
startZ = posZ;
|
||||
startPreviewFiringDefault();
|
||||
|
||||
// Reset head offset to origin
|
||||
headOffsetX = 0;
|
||||
headOffsetY = 0;
|
||||
headOffsetZ = 0;
|
||||
prevHeadOffsetX = 0;
|
||||
prevHeadOffsetY = 0;
|
||||
prevHeadOffsetZ = 0;
|
||||
|
||||
// Origin no longer follows owner after firing
|
||||
attachedToOwner = false;
|
||||
|
||||
// Sync prev position to prevent visual jump on first frame
|
||||
prevPosX = posX;
|
||||
prevPosY = posY;
|
||||
prevPosZ = posZ;
|
||||
resetHeadOffsets();
|
||||
|
||||
// Reset trail
|
||||
trailPoints.clear();
|
||||
trailPointAges.clear();
|
||||
resetTrailStorage();
|
||||
trailPoints.add(Vec3.createVectorHelper(0, 0, 0));
|
||||
|
||||
// Fire forward based on owner facing direction
|
||||
Entity owner = getOwnerEntity();
|
||||
if (owner != null) {
|
||||
float yaw = (float) Math.toRadians(owner.rotationYaw);
|
||||
motionX = -Math.sin(yaw) * getSpeed();
|
||||
motionY = 0;
|
||||
motionZ = Math.cos(yaw) * getSpeed();
|
||||
} else {
|
||||
motionX = getSpeed();
|
||||
motionY = 0;
|
||||
motionZ = 0;
|
||||
}
|
||||
if (isFadingMode()) trailPointAges.add(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the beam firing (exit charging mode).
|
||||
* Called by ability when windup ends.
|
||||
* <p>
|
||||
* For anchored beams: origin follows owner, head starts at charged position
|
||||
* For non-anchored beams: origin fixed at charged position, head starts there
|
||||
*/
|
||||
public void startFiring(EntityLivingBase target) {
|
||||
setCharging(false);
|
||||
|
||||
// For player casters, snap to look vector for crosshair-aligned launch
|
||||
snapToPlayerLookVector();
|
||||
|
||||
// Origin (tail) stays at the charged position - where the orb was
|
||||
// This is the same for both anchored and non-anchored modes
|
||||
startX = posX;
|
||||
startY = posY;
|
||||
startZ = posZ;
|
||||
startMovingTowardTargetFromStartDefault(target);
|
||||
|
||||
// Head starts at the origin (tail position)
|
||||
headOffsetX = 0;
|
||||
headOffsetY = 0;
|
||||
headOffsetZ = 0;
|
||||
prevHeadOffsetX = 0;
|
||||
prevHeadOffsetY = 0;
|
||||
prevHeadOffsetZ = 0;
|
||||
|
||||
// Origin no longer follows owner after firing starts
|
||||
// (for anchored mode, tail is fixed in space; for non-anchored, there's no tail)
|
||||
attachedToOwner = false;
|
||||
resetHeadOffsets();
|
||||
|
||||
// Initialize trail with just the origin point
|
||||
trailPoints.clear();
|
||||
trailPointAges.clear();
|
||||
resetTrailStorage();
|
||||
trailPoints.add(Vec3.createVectorHelper(0, 0, 0));
|
||||
if (fadeTrail) trailPointAges.add(0);
|
||||
|
||||
// Calculate velocity toward target (head starts at origin = startX/Y/Z)
|
||||
Entity owner = getOwnerEntity();
|
||||
|
||||
if (target != null) {
|
||||
double dx = target.posX - startX;
|
||||
double dy = (target.posY + target.getEyeHeight()) - startY;
|
||||
double dz = target.posZ - startZ;
|
||||
double len = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
||||
if (len > 0) {
|
||||
motionX = (dx / len) * getSpeed();
|
||||
motionY = (dy / len) * getSpeed();
|
||||
motionZ = (dz / len) * getSpeed();
|
||||
}
|
||||
} else if (owner != null) {
|
||||
float yaw = (float) Math.toRadians(owner.rotationYaw);
|
||||
float pitch = (float) Math.toRadians(owner.rotationPitch);
|
||||
motionX = -Math.sin(yaw) * Math.cos(pitch) * getSpeed();
|
||||
motionY = -Math.sin(pitch) * getSpeed();
|
||||
motionZ = Math.cos(yaw) * Math.cos(pitch) * getSpeed();
|
||||
}
|
||||
if (isFadingMode()) trailPointAges.add(0);
|
||||
|
||||
if (DEBUG_LOGGING && !worldObj.isRemote) {
|
||||
LogWriter.info("[Beam] startFiring: origin=(" + startX + "," + startY + "," + startZ +
|
||||
@@ -312,7 +247,7 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
prevHeadOffsetZ = headOffsetZ;
|
||||
|
||||
// Age trail points for fading effect
|
||||
if (fadeTrail) {
|
||||
if (isFadingMode()) {
|
||||
ageTrailPoints();
|
||||
}
|
||||
|
||||
@@ -346,7 +281,7 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
headOffsetZ += motionZ;
|
||||
|
||||
// Update origin if attached to owner
|
||||
if (attachedToOwner) {
|
||||
if (isAnchoredMode()) {
|
||||
Entity owner = getOwnerEntity();
|
||||
if (owner != null) {
|
||||
startX = owner.posX;
|
||||
@@ -374,7 +309,7 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
String.format("%.2f", headOffsetZ) + ") origin=(" +
|
||||
String.format("%.2f", startX) + "," +
|
||||
String.format("%.2f", startY) + "," +
|
||||
String.format("%.2f", startZ) + ") trail=" + trailPoints.size());
|
||||
String.format("%.2f", startZ) + ") trail=" + getActiveTrailSize());
|
||||
}
|
||||
|
||||
// Skip collision checks on first few ticks
|
||||
@@ -420,25 +355,43 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
* Age trail points and remove old ones (for comet effect).
|
||||
*/
|
||||
private void ageTrailPoints() {
|
||||
// Age all trail points
|
||||
for (int i = 0; i < trailPointAges.size(); i++) {
|
||||
if (trailStartIndex >= trailPoints.size()) {
|
||||
resetTrailStorage();
|
||||
return;
|
||||
}
|
||||
|
||||
// Age all active trail points.
|
||||
for (int i = trailStartIndex; i < trailPointAges.size(); i++) {
|
||||
trailPointAges.set(i, trailPointAges.get(i) + 1);
|
||||
}
|
||||
|
||||
// Remove trail points that have fully faded
|
||||
while (!trailPointAges.isEmpty() && trailPointAges.get(0) >= trailFadeTime) {
|
||||
trailPointAges.remove(0);
|
||||
if (!trailPoints.isEmpty()) {
|
||||
trailPoints.remove(0);
|
||||
}
|
||||
// Move logical start index forward for fully faded points.
|
||||
while (trailStartIndex < trailPointAges.size() && trailPointAges.get(trailStartIndex) >= trailFadeTime) {
|
||||
trailStartIndex++;
|
||||
}
|
||||
|
||||
if (trailStartIndex >= trailPoints.size()) {
|
||||
resetTrailStorage();
|
||||
return;
|
||||
}
|
||||
|
||||
// Compact stale prefix to avoid unbounded growth of dead entries.
|
||||
if (trailStartIndex >= TRAIL_COMPACT_THRESHOLD && trailStartIndex * 2 >= trailPoints.size()) {
|
||||
trailPoints = new ArrayList<Vec3>(trailPoints.subList(trailStartIndex, trailPoints.size()));
|
||||
trailPointAges = new ArrayList<Integer>(trailPointAges.subList(trailStartIndex, trailPointAges.size()));
|
||||
trailStartIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void addTrailPoint() {
|
||||
if (trailStartIndex >= trailPoints.size()) {
|
||||
resetTrailStorage();
|
||||
}
|
||||
|
||||
// Trail points are RELATIVE to origin
|
||||
if (trailPoints.isEmpty()) {
|
||||
trailPoints.add(Vec3.createVectorHelper(headOffsetX, headOffsetY, headOffsetZ));
|
||||
if (fadeTrail) {
|
||||
if (isFadingMode()) {
|
||||
trailPointAges.add(0);
|
||||
}
|
||||
return;
|
||||
@@ -453,12 +406,12 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
|
||||
if (dist >= MIN_POINT_DISTANCE) {
|
||||
trailPoints.add(Vec3.createVectorHelper(headOffsetX, headOffsetY, headOffsetZ));
|
||||
if (fadeTrail) {
|
||||
if (isFadingMode()) {
|
||||
trailPointAges.add(0);
|
||||
}
|
||||
|
||||
// Limit trail length (only if not using fading - fading handles its own cleanup)
|
||||
if (!fadeTrail) {
|
||||
if (!isFadingMode()) {
|
||||
while (trailPoints.size() > MAX_TRAIL_POINTS) {
|
||||
trailPoints.remove(0);
|
||||
}
|
||||
@@ -476,7 +429,7 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
headOffsetZ += motionZ;
|
||||
|
||||
// Update origin if attached to owner
|
||||
if (attachedToOwner) {
|
||||
if (isAnchoredMode()) {
|
||||
Entity owner = getOwnerEntity();
|
||||
if (owner != null) {
|
||||
startX = owner.posX;
|
||||
@@ -546,24 +499,7 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
double prevHeadWorldY = startY + prevHeadOffsetY;
|
||||
double prevHeadWorldZ = startZ + prevHeadOffsetZ;
|
||||
|
||||
Vec3 currentPos = Vec3.createVectorHelper(prevHeadWorldX, prevHeadWorldY, prevHeadWorldZ);
|
||||
Vec3 nextPos = Vec3.createVectorHelper(headX, headY, headZ);
|
||||
// Use full raytrace that doesn't stop at liquids and checks all blocks
|
||||
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;
|
||||
posY = blockHit.hitVec.yCoord;
|
||||
posZ = blockHit.hitVec.zCoord;
|
||||
doExplosion();
|
||||
}
|
||||
this.setDead();
|
||||
}
|
||||
handleBlockImpact(rayTraceBlocks(prevHeadWorldX, prevHeadWorldY, prevHeadWorldZ, headX, headY, headZ), true);
|
||||
}
|
||||
|
||||
private void checkEntityCollision(double headX, double headY, double headZ) {
|
||||
@@ -577,24 +513,12 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
List<EntityLivingBase> entities = worldObj.getEntitiesWithinAABB(EntityLivingBase.class, hitBox);
|
||||
|
||||
for (EntityLivingBase entity : entities) {
|
||||
if (shouldIgnoreEntity(entity)) continue;
|
||||
|
||||
if (DEBUG_LOGGING) {
|
||||
LogWriter.info("[Beam] DEAD: Entity collision with " + entity.getClass().getSimpleName() + " at tick " + ticksExisted);
|
||||
if (processEntityHit(entity, headX, headY, headZ)) {
|
||||
if (DEBUG_LOGGING) {
|
||||
LogWriter.info("[Beam] DEAD: Entity collision with " + entity.getClass().getSimpleName() + " at tick " + ticksExisted);
|
||||
}
|
||||
return;
|
||||
}
|
||||
hasHit = true;
|
||||
|
||||
if (isExplosive()) {
|
||||
posX = headX;
|
||||
posY = headY;
|
||||
posZ = headZ;
|
||||
doExplosion();
|
||||
} else {
|
||||
applyDamage(entity);
|
||||
}
|
||||
|
||||
this.setDead();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -617,7 +541,7 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
}
|
||||
|
||||
public void setAttachedToOwner(boolean attached) {
|
||||
this.attachedToOwner = attached;
|
||||
setBeamMode(modeFromAnchored(attached));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -640,7 +564,13 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
* Get trail points. These are RELATIVE to origin (startX/Y/Z).
|
||||
*/
|
||||
public List<Vec3> getTrailPoints() {
|
||||
return trailPoints;
|
||||
if (trailStartIndex <= 0) {
|
||||
return trailPoints;
|
||||
}
|
||||
if (trailStartIndex >= trailPoints.size()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return trailPoints.subList(trailStartIndex, trailPoints.size());
|
||||
}
|
||||
|
||||
public double getOriginX() {
|
||||
@@ -659,28 +589,34 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
* Whether the tail orb should be rendered (only true in anchored mode).
|
||||
*/
|
||||
public boolean shouldRenderTailOrb() {
|
||||
return renderTailOrb;
|
||||
return isAnchoredMode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the beam is attached to its owner (anchored mode).
|
||||
*/
|
||||
public boolean isAttachedToOwner() {
|
||||
return attachedToOwner;
|
||||
return isAnchoredMode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether trail should fade (comet effect for non-anchored beams).
|
||||
*/
|
||||
public boolean hasFadingTrail() {
|
||||
return fadeTrail;
|
||||
return isFadingMode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get trail point ages for fading calculation.
|
||||
*/
|
||||
public List<Integer> getTrailPointAges() {
|
||||
return trailPointAges;
|
||||
if (trailStartIndex <= 0) {
|
||||
return trailPointAges;
|
||||
}
|
||||
if (trailStartIndex >= trailPointAges.size()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return trailPointAges.subList(trailStartIndex, trailPointAges.size());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -702,17 +638,26 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
this.prevHeadOffsetX = this.headOffsetX;
|
||||
this.prevHeadOffsetY = this.headOffsetY;
|
||||
this.prevHeadOffsetZ = this.headOffsetZ;
|
||||
this.attachedToOwner = !nbt.hasKey("AttachedToOwner") || nbt.getBoolean("AttachedToOwner");
|
||||
this.renderTailOrb = !nbt.hasKey("RenderTailOrb") || nbt.getBoolean("RenderTailOrb");
|
||||
readChargingNBT(nbt);
|
||||
this.chargeOffsetDistance = nbt.hasKey("ChargeOffsetDistance") ? nbt.getFloat("ChargeOffsetDistance") : 1.0f;
|
||||
this.fadeTrail = nbt.hasKey("FadeTrail") && nbt.getBoolean("FadeTrail");
|
||||
this.trailFadeTime = nbt.hasKey("TrailFadeTime") ? nbt.getInteger("TrailFadeTime") : 20;
|
||||
|
||||
boolean attachedLegacy = !nbt.hasKey("AttachedToOwner") || nbt.getBoolean("AttachedToOwner");
|
||||
BeamMode loadedMode = modeFromAnchored(attachedLegacy);
|
||||
if (nbt.hasKey("BeamMode")) {
|
||||
try {
|
||||
loadedMode = BeamMode.valueOf(nbt.getString("BeamMode"));
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
loadedMode = modeFromAnchored(attachedLegacy);
|
||||
}
|
||||
} else if (nbt.hasKey("FadeTrail")) {
|
||||
loadedMode = nbt.getBoolean("FadeTrail") ? BeamMode.FREE_TRAIL : modeFromAnchored(attachedLegacy);
|
||||
}
|
||||
|
||||
// Read trail points (relative to origin)
|
||||
trailPoints.clear();
|
||||
trailPointAges.clear();
|
||||
trailStartIndex = 0;
|
||||
if (nbt.hasKey("Trail")) {
|
||||
NBTTagList trailList = nbt.getTagList("Trail", 10);
|
||||
for (int i = 0; i < trailList.tagCount(); i++) {
|
||||
@@ -732,10 +677,7 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
trailPointAges.add(age);
|
||||
}
|
||||
}
|
||||
// If trail points exist but ages don't (legacy data), initialize ages to 0
|
||||
while (trailPointAges.size() < trailPoints.size()) {
|
||||
trailPointAges.add(0);
|
||||
}
|
||||
setBeamMode(loadedMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -745,16 +687,18 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
nbt.setDouble("HeadOffsetX", headOffsetX);
|
||||
nbt.setDouble("HeadOffsetY", headOffsetY);
|
||||
nbt.setDouble("HeadOffsetZ", headOffsetZ);
|
||||
nbt.setBoolean("AttachedToOwner", attachedToOwner);
|
||||
nbt.setBoolean("RenderTailOrb", renderTailOrb);
|
||||
nbt.setBoolean("AttachedToOwner", isAnchoredMode());
|
||||
nbt.setBoolean("RenderTailOrb", isAnchoredMode());
|
||||
nbt.setString("BeamMode", beamMode.name());
|
||||
writeChargingNBT(nbt);
|
||||
nbt.setFloat("ChargeOffsetDistance", chargeOffsetDistance);
|
||||
nbt.setBoolean("FadeTrail", fadeTrail);
|
||||
nbt.setBoolean("FadeTrail", isFadingMode());
|
||||
nbt.setInteger("TrailFadeTime", trailFadeTime);
|
||||
|
||||
// Write trail points
|
||||
NBTTagList trailList = new NBTTagList();
|
||||
for (Vec3 point : trailPoints) {
|
||||
for (int i = trailStartIndex; i < trailPoints.size(); i++) {
|
||||
Vec3 point = trailPoints.get(i);
|
||||
NBTTagCompound pointNbt = new NBTTagCompound();
|
||||
pointNbt.setDouble("X", point.xCoord);
|
||||
pointNbt.setDouble("Y", point.yCoord);
|
||||
@@ -764,9 +708,11 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
nbt.setTag("Trail", trailList);
|
||||
|
||||
// Write trail point ages for fading trail sync
|
||||
int[] ages = new int[trailPointAges.size()];
|
||||
for (int i = 0; i < trailPointAges.size(); i++) {
|
||||
ages[i] = trailPointAges.get(i);
|
||||
int activeSize = isFadingMode() ? Math.max(0, trailPoints.size() - trailStartIndex) : 0;
|
||||
int[] ages = new int[activeSize];
|
||||
for (int i = 0; i < activeSize; i++) {
|
||||
int idx = trailStartIndex + i;
|
||||
ages[i] = idx < trailPointAges.size() ? trailPointAges.get(idx) : 0;
|
||||
}
|
||||
nbt.setIntArray("TrailAges", ages);
|
||||
}
|
||||
|
||||
@@ -1,23 +1,19 @@
|
||||
package kamkeel.npcs.entity;
|
||||
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyAnchorData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyCombatData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyHomingData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyLifespanData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyLightningData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyTrajectoryData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyAnchorData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyCombatData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyHomingData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyLifespanData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyLightningData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyTrajectoryData;
|
||||
import kamkeel.npcs.util.AnchorPointHelper;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.AxisAlignedBB;
|
||||
import net.minecraft.util.MovingObjectPosition;
|
||||
import net.minecraft.util.Vec3;
|
||||
import net.minecraft.world.World;
|
||||
import noppes.npcs.EventHooks;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Disc projectile - flat spinning disc with optional boomerang behavior.
|
||||
@@ -98,18 +94,7 @@ public class EntityAbilityDisc extends EntityEnergyProjectile {
|
||||
* Can be fired when transitioning to active phase.
|
||||
*/
|
||||
public void setupPreview(EntityLivingBase owner, float discRadius, float discThickness, EnergyDisplayData display, EnergyLightningData lightning, EnergyAnchorData anchor, int chargeDuration, boolean vertical) {
|
||||
this.setPreviewMode(true);
|
||||
this.setPreviewOwner(owner);
|
||||
|
||||
// Set visual properties
|
||||
this.displayData = display;
|
||||
this.lightningData = lightning;
|
||||
|
||||
// Set charging state
|
||||
this.setCharging(true);
|
||||
this.chargeDuration = chargeDuration;
|
||||
this.chargeTick = 0;
|
||||
this.anchorData = anchor;
|
||||
setupPreviewState(owner, display, lightning, anchor, chargeDuration);
|
||||
this.vertical = vertical;
|
||||
|
||||
// Store target size and start at 0 for grow effect
|
||||
@@ -117,55 +102,16 @@ public class EntityAbilityDisc extends EntityEnergyProjectile {
|
||||
this.targetDiscThickness = discThickness;
|
||||
this.discRadius = 0.01f;
|
||||
this.discThickness = 0.01f;
|
||||
this.size = 0.01f;
|
||||
this.renderCurrentSize = 0.01f;
|
||||
this.prevRenderSize = 0.01f;
|
||||
|
||||
// Initial position at anchor point
|
||||
Vec3 pos = AnchorPointHelper.calculateAnchorPosition(owner, anchorData);
|
||||
this.setPosition(pos.xCoord, pos.yCoord, pos.zCoord);
|
||||
this.prevPosX = pos.xCoord;
|
||||
this.prevPosY = pos.yCoord;
|
||||
this.prevPosZ = pos.zCoord;
|
||||
this.startX = pos.xCoord;
|
||||
this.startY = pos.yCoord;
|
||||
this.startZ = pos.zCoord;
|
||||
|
||||
// Clear motion
|
||||
this.motionX = 0;
|
||||
this.motionY = 0;
|
||||
this.motionZ = 0;
|
||||
setVisualSize(0.01f);
|
||||
setChargeOriginFromAnchor(owner, anchorData);
|
||||
clearMotion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start preview firing (simulates firing toward a point in front of NPC).
|
||||
*/
|
||||
public void startPreviewFiring() {
|
||||
setCharging(false);
|
||||
|
||||
// Update start position to current position
|
||||
startX = posX;
|
||||
startY = posY;
|
||||
startZ = posZ;
|
||||
|
||||
// Sync prev position to prevent visual jump on first frame
|
||||
prevPosX = posX;
|
||||
prevPosY = posY;
|
||||
prevPosZ = posZ;
|
||||
|
||||
// Fire forward based on owner facing direction
|
||||
Entity owner = getOwnerEntity();
|
||||
if (owner != null) {
|
||||
float yaw = (float) Math.toRadians(owner.rotationYaw);
|
||||
float pitch = 0; // Fire horizontally
|
||||
motionX = -Math.sin(yaw) * Math.cos(pitch) * getSpeed();
|
||||
motionY = -Math.sin(pitch) * getSpeed();
|
||||
motionZ = Math.cos(yaw) * Math.cos(pitch) * getSpeed();
|
||||
} else {
|
||||
motionX = getSpeed();
|
||||
motionY = 0;
|
||||
motionZ = 0;
|
||||
}
|
||||
startPreviewFiringDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -173,36 +119,7 @@ public class EntityAbilityDisc extends EntityEnergyProjectile {
|
||||
* Called by ability when windup ends.
|
||||
*/
|
||||
public void startMoving(EntityLivingBase target) {
|
||||
setCharging(false);
|
||||
|
||||
// For player casters, snap to look vector for crosshair-aligned launch
|
||||
snapToPlayerLookVector();
|
||||
|
||||
// Update start position to current position
|
||||
startX = posX;
|
||||
startY = posY;
|
||||
startZ = posZ;
|
||||
|
||||
// Calculate velocity toward target
|
||||
Entity owner = getOwnerEntity();
|
||||
|
||||
if (target != null) {
|
||||
double dx = target.posX - posX;
|
||||
double dy = (target.posY + target.getEyeHeight()) - posY;
|
||||
double dz = target.posZ - posZ;
|
||||
double len = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
||||
if (len > 0) {
|
||||
motionX = (dx / len) * getSpeed();
|
||||
motionY = (dy / len) * getSpeed();
|
||||
motionZ = (dz / len) * getSpeed();
|
||||
}
|
||||
} else if (owner != null) {
|
||||
float yaw = (float) Math.toRadians(owner.rotationYaw);
|
||||
float pitch = (float) Math.toRadians(owner.rotationPitch);
|
||||
motionX = -Math.sin(yaw) * Math.cos(pitch) * getSpeed();
|
||||
motionY = -Math.sin(pitch) * getSpeed();
|
||||
motionZ = Math.cos(yaw) * Math.cos(pitch) * getSpeed();
|
||||
}
|
||||
startMovingTowardTargetDefault(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -248,15 +165,7 @@ public class EntityAbilityDisc extends EntityEnergyProjectile {
|
||||
}
|
||||
|
||||
// Check wall collision
|
||||
if (this.isCollidedHorizontally || this.isCollidedVertically) {
|
||||
if (!worldObj.isRemote) {
|
||||
hasHit = true;
|
||||
if (isExplosive()) {
|
||||
doExplosion();
|
||||
}
|
||||
}
|
||||
this.setDead();
|
||||
}
|
||||
handleSolidCollisionTermination();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -352,24 +261,7 @@ public class EntityAbilityDisc extends EntityEnergyProjectile {
|
||||
}
|
||||
|
||||
private void checkBlockCollision() {
|
||||
Vec3 currentPos = Vec3.createVectorHelper(posX, posY, posZ);
|
||||
Vec3 nextPos = Vec3.createVectorHelper(posX + motionX, posY + motionY, posZ + motionZ);
|
||||
// Use full raytrace that doesn't stop at liquids and checks all blocks
|
||||
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;
|
||||
posY = blockHit.hitVec.yCoord;
|
||||
posZ = blockHit.hitVec.zCoord;
|
||||
doExplosion();
|
||||
}
|
||||
this.setDead();
|
||||
}
|
||||
handleBlockImpact(rayTraceBlocks(posX, posY, posZ, posX + motionX, posY + motionY, posZ + motionZ), true);
|
||||
}
|
||||
|
||||
private void checkEntityCollision() {
|
||||
@@ -381,23 +273,7 @@ public class EntityAbilityDisc extends EntityEnergyProjectile {
|
||||
posX + halfWidth, posY + halfHeight, posZ + halfWidth
|
||||
);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<EntityLivingBase> entities = worldObj.getEntitiesWithinAABB(EntityLivingBase.class, hitBox);
|
||||
|
||||
for (EntityLivingBase entity : entities) {
|
||||
if (shouldIgnoreEntity(entity)) continue;
|
||||
|
||||
hasHit = true;
|
||||
|
||||
if (isExplosive()) {
|
||||
doExplosion();
|
||||
} else {
|
||||
applyDamage(entity);
|
||||
}
|
||||
|
||||
this.setDead();
|
||||
return;
|
||||
}
|
||||
processEntitiesInHitBox(hitBox, posX, posY, posZ);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,9 +2,9 @@ package kamkeel.npcs.entity;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyBarrierData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyLightningData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyBarrierData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyLightningData;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
package kamkeel.npcs.entity;
|
||||
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyCombatData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyLifespanData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyLightningData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyTrajectoryData;
|
||||
import kamkeel.npcs.util.AnchorPointHelper;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyAnchorData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyCombatData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyLifespanData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyLightningData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyTrajectoryData;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.AxisAlignedBB;
|
||||
import net.minecraft.util.MovingObjectPosition;
|
||||
@@ -17,12 +16,10 @@ import net.minecraft.world.World;
|
||||
import noppes.npcs.EventHooks;
|
||||
import noppes.npcs.entity.EntityNPCInterface;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Laser projectile - fast expanding thin line that pierces through multiple targets.
|
||||
* Laser projectile - fast expanding thin line.
|
||||
* No homing, travels in a straight line from origin to max distance.
|
||||
* <p>
|
||||
* Design inspired by LouisXIV's energy attack system.
|
||||
@@ -45,12 +42,6 @@ public class EntityAbilityLaser extends EntityEnergyProjectile {
|
||||
// Lock vertical direction after firing (only update yaw, keep pitch fixed)
|
||||
private boolean lockVerticalDirection = false;
|
||||
|
||||
// Whether to die on first entity impact (hit-scan mode)
|
||||
private boolean dieOnImpact = false;
|
||||
|
||||
// Track hit entities to avoid double-damage
|
||||
private Set<Integer> hitEntities = new HashSet<>();
|
||||
|
||||
// End point for rendering
|
||||
private double endX, endY, endZ;
|
||||
|
||||
@@ -164,13 +155,12 @@ public class EntityAbilityLaser extends EntityEnergyProjectile {
|
||||
}
|
||||
|
||||
/**
|
||||
* Update laser origin and direction from the owner's current anchor position and rotation.
|
||||
* Called each tick so the laser follows the NPC when tracking a target.
|
||||
* Update laser origin and direction from the owner's current look rotation.
|
||||
* Called each tick so the laser origin stays centered on look vector.
|
||||
*/
|
||||
private void updateLaserOriginAndDirection() {
|
||||
Entity owner = getOwnerEntity();
|
||||
if (owner == null || !(owner instanceof EntityLivingBase)) return;
|
||||
|
||||
EntityLivingBase livingOwner = (EntityLivingBase) owner;
|
||||
|
||||
// Update direction from owner's current rotation
|
||||
@@ -188,71 +178,51 @@ public class EntityAbilityLaser extends EntityEnergyProjectile {
|
||||
dirZ = Math.cos(yaw) * Math.cos(pitch);
|
||||
}
|
||||
|
||||
// Update origin: for player casters, snap to look vector so laser aligns with crosshair.
|
||||
// For NPCs, use the configured anchor point.
|
||||
if (owner instanceof EntityPlayer) {
|
||||
double eyeY = owner.posY + owner.getEyeHeight();
|
||||
float frontDist = Math.max(0.5f, laserWidth * 0.5f);
|
||||
startX = owner.posX + dirX * frontDist;
|
||||
startY = eyeY + dirY * frontDist;
|
||||
startZ = owner.posZ + dirZ * frontDist;
|
||||
} else if (anchorData != null) {
|
||||
Vec3 pos = AnchorPointHelper.calculateAnchorPosition(livingOwner, anchorData);
|
||||
startX = pos.xCoord;
|
||||
startY = pos.yCoord;
|
||||
startZ = pos.zCoord;
|
||||
// Keep direction normalized for consistent expansion and collision math.
|
||||
Vec3 direction = Vec3.createVectorHelper(dirX, dirY, dirZ);
|
||||
double dirLen = Math.sqrt(direction.xCoord * direction.xCoord + direction.yCoord * direction.yCoord + direction.zCoord * direction.zCoord);
|
||||
if (dirLen > 0.0001) {
|
||||
direction = Vec3.createVectorHelper(
|
||||
direction.xCoord / dirLen,
|
||||
direction.yCoord / dirLen,
|
||||
direction.zCoord / dirLen
|
||||
);
|
||||
} else {
|
||||
startX = owner.posX;
|
||||
startY = owner.posY + owner.height * 0.7;
|
||||
startZ = owner.posZ;
|
||||
}
|
||||
|
||||
// Keep entity positioned at origin
|
||||
prevPosX = startX;
|
||||
prevPosY = startY;
|
||||
prevPosZ = startZ;
|
||||
setPosition(startX, startY, startZ);
|
||||
}
|
||||
|
||||
public void startMoving(EntityLivingBase target) {
|
||||
setCharging(false);
|
||||
|
||||
// Set initial direction from owner's rotation
|
||||
Entity owner = getOwnerEntity();
|
||||
if (owner != null) {
|
||||
float yaw = (float) Math.toRadians(owner.rotationYaw);
|
||||
float pitch = (float) Math.toRadians(owner.rotationPitch);
|
||||
this.dirX = -Math.sin(yaw) * Math.cos(pitch);
|
||||
this.dirY = -Math.sin(pitch);
|
||||
this.dirZ = Math.cos(yaw) * Math.cos(pitch);
|
||||
} else if (target != null) {
|
||||
double dx = target.posX - posX;
|
||||
double dy = (target.posY + target.getEyeHeight() - 0.4) - posY;
|
||||
double dz = target.posZ - posZ;
|
||||
double len = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
||||
if (len > 0) {
|
||||
this.dirX = dx / len;
|
||||
this.dirY = dy / len;
|
||||
this.dirZ = dz / len;
|
||||
Vec3 look = livingOwner.getLookVec();
|
||||
if (look != null) {
|
||||
direction = look;
|
||||
}
|
||||
}
|
||||
|
||||
// For player casters, snap origin to look vector so laser aligns with crosshair.
|
||||
// For NPCs, use current anchor position.
|
||||
if (owner instanceof EntityPlayer) {
|
||||
double eyeY = owner.posY + owner.getEyeHeight();
|
||||
float frontDist = Math.max(0.5f, laserWidth * 0.5f);
|
||||
startX = owner.posX + dirX * frontDist;
|
||||
startY = eyeY + dirY * frontDist;
|
||||
startZ = owner.posZ + dirZ * frontDist;
|
||||
setPosition(startX, startY, startZ);
|
||||
prevPosX = startX;
|
||||
prevPosY = startY;
|
||||
prevPosZ = startZ;
|
||||
} else {
|
||||
startX = posX;
|
||||
startY = posY;
|
||||
startZ = posZ;
|
||||
dirX = direction.xCoord;
|
||||
dirY = direction.yCoord;
|
||||
dirZ = direction.zCoord;
|
||||
|
||||
// Keep origin centered on look vector while active and clear owner bbox.
|
||||
setLookVectorLaunchPosition(livingOwner, direction, false);
|
||||
syncStartPositionToCurrent();
|
||||
syncPositionStateToCurrent(false);
|
||||
}
|
||||
|
||||
public void startMoving(EntityLivingBase target) {
|
||||
beginLookVectorLaunch(false);
|
||||
|
||||
currentLength = 0.0f;
|
||||
fullyExtended = false;
|
||||
ticksSinceFullExtension = 0;
|
||||
|
||||
// Set initial direction: target-based when available, otherwise owner look vector.
|
||||
Vec3 look = getOwnerLookVector();
|
||||
if (!setDirectionTowardTarget(target, startX, startY, startZ)) {
|
||||
if (look != null) {
|
||||
dirX = look.xCoord;
|
||||
dirY = look.yCoord;
|
||||
dirZ = look.zCoord;
|
||||
} else {
|
||||
dirX = 1.0;
|
||||
dirY = 0.0;
|
||||
dirZ = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize end point at start (will expand from here)
|
||||
@@ -261,11 +231,22 @@ public class EntityAbilityLaser extends EntityEnergyProjectile {
|
||||
this.endZ = startZ;
|
||||
}
|
||||
|
||||
private boolean setDirectionTowardTarget(EntityLivingBase target, double sourceX, double sourceY, double sourceZ) {
|
||||
if (target == null) return false;
|
||||
double dx = target.posX - sourceX;
|
||||
double dy = (target.posY + target.getEyeHeight() - 0.4) - sourceY;
|
||||
double dz = target.posZ - sourceZ;
|
||||
double len = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
||||
if (len <= 0.0001) return false;
|
||||
|
||||
dirX = dx / len;
|
||||
dirY = dy / len;
|
||||
dirZ = dz / len;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void checkBlockCollision() {
|
||||
Vec3 start = Vec3.createVectorHelper(startX, startY, startZ);
|
||||
Vec3 end = Vec3.createVectorHelper(endX, endY, endZ);
|
||||
// Use full raytrace that doesn't stop at liquids and checks all blocks
|
||||
MovingObjectPosition blockHit = worldObj.func_147447_a(start, end, false, true, false);
|
||||
MovingObjectPosition blockHit = rayTraceBlocks(startX, startY, startZ, endX, endY, endZ);
|
||||
|
||||
if (blockHit != null && blockHit.typeOfHit == MovingObjectPosition.MovingObjectType.BLOCK) {
|
||||
if (!worldObj.isRemote) {
|
||||
@@ -315,15 +296,12 @@ public class EntityAbilityLaser extends EntityEnergyProjectile {
|
||||
|
||||
for (EntityLivingBase entity : entities) {
|
||||
if (shouldIgnoreEntity(entity)) continue;
|
||||
if (hitEntities.contains(entity.getEntityId())) continue;
|
||||
if (!canHitEntityNow(entity)) continue;
|
||||
|
||||
// Check if entity's bounding box intersects with the laser line
|
||||
if (isEntityOnLine(entity)) {
|
||||
hitEntities.add(entity.getEntityId());
|
||||
applyDamage(entity);
|
||||
|
||||
if (dieOnImpact) {
|
||||
// Stop at impact point and begin linger/fade
|
||||
if (processEntityHit(entity, entity.posX, entity.posY + entity.height * 0.5, entity.posZ)) {
|
||||
// Stop at impact point and terminate
|
||||
double dx = entity.posX - startX;
|
||||
double dy = (entity.posY + entity.height * 0.5) - startY;
|
||||
double dz = entity.posZ - startZ;
|
||||
@@ -332,11 +310,10 @@ public class EntityAbilityLaser extends EntityEnergyProjectile {
|
||||
endX = startX + dirX * currentLength;
|
||||
endY = startY + dirY * currentLength;
|
||||
endZ = startZ + dirZ * currentLength;
|
||||
fullyExtended = true;
|
||||
return; // Stop checking further entities
|
||||
return;
|
||||
}
|
||||
|
||||
// Piercing - don't stop, continue to next entity
|
||||
// PIERCE/MULTI continue through remaining entities.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -357,8 +334,6 @@ public class EntityAbilityLaser extends EntityEnergyProjectile {
|
||||
double dirLenXZ = Math.sqrt(dirX * dirX + dirZ * dirZ);
|
||||
double dot;
|
||||
if (dirLenXZ > 0.001) {
|
||||
dot = (vx * dirX + vz * dirZ) / (dirLenXZ * dirLenXZ) * dirLenXZ;
|
||||
// Remap to full 3D parameter
|
||||
dot = (vx * dirX + vz * dirZ + (entity.posY + entity.height * 0.5 - startY) * dirY);
|
||||
} else {
|
||||
// Laser fires nearly straight up/down - use full 3D projection
|
||||
@@ -517,14 +492,6 @@ public class EntityAbilityLaser extends EntityEnergyProjectile {
|
||||
this.lingerTicks = ticks;
|
||||
}
|
||||
|
||||
public boolean isDieOnImpact() {
|
||||
return dieOnImpact;
|
||||
}
|
||||
|
||||
public void setDieOnImpact(boolean dieOnImpact) {
|
||||
this.dieOnImpact = dieOnImpact;
|
||||
}
|
||||
|
||||
public void setDirection(double x, double y, double z) {
|
||||
this.dirX = x;
|
||||
this.dirY = y;
|
||||
@@ -584,6 +551,24 @@ public class EntityAbilityLaser extends EntityEnergyProjectile {
|
||||
return Math.max(0.0f, 1.0f - ((float) ticksSinceFullExtension / lingerTicks));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getLaunchClearanceRadius() {
|
||||
return Math.max(0.1f, laserWidth * 0.5f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupCharging(EnergyAnchorData anchor, int chargeDuration) {
|
||||
// Ensure charging visuals grow from a small orb based on laser width.
|
||||
this.size = laserWidth;
|
||||
this.currentLength = 0.0f;
|
||||
this.fullyExtended = false;
|
||||
this.ticksSinceFullExtension = 0;
|
||||
super.setupCharging(anchor, chargeDuration);
|
||||
this.endX = posX;
|
||||
this.endY = posY;
|
||||
this.endZ = posZ;
|
||||
}
|
||||
|
||||
// ==================== NBT ====================
|
||||
|
||||
@Override
|
||||
@@ -593,7 +578,6 @@ public class EntityAbilityLaser extends EntityEnergyProjectile {
|
||||
if (Float.isNaN(expansionSpeed) || Float.isInfinite(expansionSpeed) || expansionSpeed <= 0) expansionSpeed = 2.0f;
|
||||
this.lingerTicks = nbt.hasKey("LingerTicks") ? nbt.getInteger("LingerTicks") : 10;
|
||||
if (lingerTicks <= 0) lingerTicks = 1;
|
||||
this.dieOnImpact = nbt.getBoolean("DieOnImpact");
|
||||
this.lockVerticalDirection = nbt.getBoolean("LockVerticalDir");
|
||||
this.ticksSinceFullExtension = nbt.getInteger("TicksSinceExtended");
|
||||
this.dirX = nbt.getDouble("DirX");
|
||||
@@ -613,7 +597,6 @@ public class EntityAbilityLaser extends EntityEnergyProjectile {
|
||||
nbt.setFloat("LaserWidth", laserWidth);
|
||||
nbt.setFloat("ExpansionSpeed", expansionSpeed);
|
||||
nbt.setInteger("LingerTicks", lingerTicks);
|
||||
nbt.setBoolean("DieOnImpact", dieOnImpact);
|
||||
nbt.setBoolean("LockVerticalDir", lockVerticalDirection);
|
||||
nbt.setInteger("TicksSinceExtended", ticksSinceFullExtension);
|
||||
nbt.setDouble("DirX", dirX);
|
||||
@@ -630,42 +613,47 @@ public class EntityAbilityLaser extends EntityEnergyProjectile {
|
||||
|
||||
/**
|
||||
* Setup this laser in preview mode for GUI display.
|
||||
* Laser doesn't have charging state - spawns at active phase and fires immediately.
|
||||
* Uses charging-orb visuals during windup before firing.
|
||||
*/
|
||||
public void setupPreview(EntityLivingBase owner, float laserWidth, EnergyDisplayData display,
|
||||
EnergyLightningData lightning, float expansionSpeed, float maxDistance) {
|
||||
this.setPreviewMode(true);
|
||||
this.setPreviewOwner(owner);
|
||||
EnergyLightningData lightning, EnergyAnchorData anchor, int chargeDuration,
|
||||
float expansionSpeed, float maxDistance) {
|
||||
setupPreviewState(owner, display, lightning, anchor, chargeDuration);
|
||||
|
||||
// Set visual properties
|
||||
// Laser-specific visual/behavior state.
|
||||
this.laserWidth = laserWidth;
|
||||
this.size = laserWidth;
|
||||
this.displayData = display;
|
||||
this.expansionSpeed = expansionSpeed;
|
||||
this.lifespanData.maxDistance = Math.min(maxDistance, 5.0f); // Limit for GUI preview
|
||||
this.lightningData = lightning;
|
||||
this.currentLength = 0.0f;
|
||||
this.fullyExtended = false;
|
||||
this.ticksSinceFullExtension = 0;
|
||||
|
||||
// Position at chest height
|
||||
double x = owner.posX;
|
||||
double y = owner.posY + owner.height * 0.7;
|
||||
double z = owner.posZ;
|
||||
this.setPosition(x, y, z);
|
||||
this.prevPosX = x;
|
||||
this.prevPosY = y;
|
||||
this.prevPosZ = z;
|
||||
this.startX = x;
|
||||
this.startY = y;
|
||||
this.startZ = z;
|
||||
// Charge as an orb from tiny size to laser width while anchored.
|
||||
this.targetSize = laserWidth;
|
||||
setVisualSize(0.01f);
|
||||
setChargeOriginFromAnchor(owner, anchorData);
|
||||
clearMotion();
|
||||
this.endX = posX;
|
||||
this.endY = posY;
|
||||
this.endZ = posZ;
|
||||
|
||||
// Fire in owner's facing direction
|
||||
float yaw = (float) Math.toRadians(owner.rotationYaw);
|
||||
this.dirX = -Math.sin(yaw);
|
||||
this.dirY = 0;
|
||||
this.dirZ = Math.cos(yaw);
|
||||
|
||||
// Initialize end point (same as start, will expand)
|
||||
this.endX = x;
|
||||
this.endY = y;
|
||||
this.endZ = z;
|
||||
// Precompute initial direction from owner look for launch.
|
||||
Vec3 look = owner == null ? null : owner.getLookVec();
|
||||
if (look == null) {
|
||||
if (owner != null) {
|
||||
float yaw = (float) Math.toRadians(owner.rotationYaw);
|
||||
float pitch = (float) Math.toRadians(owner.rotationPitch);
|
||||
look = Vec3.createVectorHelper(
|
||||
-Math.sin(yaw) * Math.cos(pitch),
|
||||
-Math.sin(pitch),
|
||||
Math.cos(yaw) * Math.cos(pitch)
|
||||
);
|
||||
} else {
|
||||
look = Vec3.createVectorHelper(1.0, 0.0, 0.0);
|
||||
}
|
||||
}
|
||||
this.dirX = look.xCoord;
|
||||
this.dirY = look.yCoord;
|
||||
this.dirZ = look.zCoord;
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,16 @@
|
||||
package kamkeel.npcs.entity;
|
||||
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyAnchorData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyCombatData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyHomingData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyLifespanData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyLightningData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyTrajectoryData;
|
||||
import kamkeel.npcs.util.AnchorPointHelper;
|
||||
import net.minecraft.entity.Entity;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyAnchorData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyCombatData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyHomingData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyLifespanData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyLightningData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyTrajectoryData;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.AxisAlignedBB;
|
||||
import net.minecraft.util.MovingObjectPosition;
|
||||
import net.minecraft.util.Vec3;
|
||||
import net.minecraft.world.World;
|
||||
import noppes.npcs.EventHooks;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Orb projectile - spherical homing energy ball.
|
||||
@@ -26,6 +19,8 @@ import java.util.List;
|
||||
* Design inspired by LouisXIV's energy attack system.
|
||||
*/
|
||||
public class EntityAbilityOrb extends EntityEnergyProjectile {
|
||||
private static final int HOMING_STARTUP_TICKS = 4;
|
||||
private static final int HOMING_RAMP_TICKS = 8;
|
||||
|
||||
public EntityAbilityOrb(World world) {
|
||||
super(world);
|
||||
@@ -56,71 +51,20 @@ public class EntityAbilityOrb extends EntityEnergyProjectile {
|
||||
* Can be fired when transitioning to active phase.
|
||||
*/
|
||||
public void setupPreview(EntityLivingBase owner, float orbSize, EnergyDisplayData display, EnergyLightningData lightning, EnergyAnchorData anchor, int chargeDuration) {
|
||||
this.setPreviewMode(true);
|
||||
this.setPreviewOwner(owner);
|
||||
|
||||
// Set visual properties
|
||||
this.displayData = display;
|
||||
this.lightningData = lightning;
|
||||
|
||||
// Set charging state
|
||||
this.setCharging(true);
|
||||
this.chargeDuration = chargeDuration;
|
||||
this.chargeTick = 0;
|
||||
this.anchorData = anchor;
|
||||
setupPreviewState(owner, display, lightning, anchor, chargeDuration);
|
||||
|
||||
// Store target size and start at 0 for grow effect
|
||||
this.targetSize = orbSize;
|
||||
this.size = 0.01f;
|
||||
this.renderCurrentSize = 0.01f;
|
||||
this.prevRenderSize = 0.01f;
|
||||
|
||||
// Initial position at anchor point
|
||||
Vec3 pos = AnchorPointHelper.calculateAnchorPosition(owner, anchorData);
|
||||
this.setPosition(pos.xCoord, pos.yCoord, pos.zCoord);
|
||||
this.prevPosX = pos.xCoord;
|
||||
this.prevPosY = pos.yCoord;
|
||||
this.prevPosZ = pos.zCoord;
|
||||
this.startX = pos.xCoord;
|
||||
this.startY = pos.yCoord;
|
||||
this.startZ = pos.zCoord;
|
||||
|
||||
// Clear motion
|
||||
this.motionX = 0;
|
||||
this.motionY = 0;
|
||||
this.motionZ = 0;
|
||||
setVisualSize(0.01f);
|
||||
setChargeOriginFromAnchor(owner, anchorData);
|
||||
clearMotion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start preview firing (simulates firing toward a point in front of NPC).
|
||||
*/
|
||||
public void startPreviewFiring() {
|
||||
setCharging(false);
|
||||
|
||||
// Update start position to current position
|
||||
startX = posX;
|
||||
startY = posY;
|
||||
startZ = posZ;
|
||||
|
||||
// Sync prev position to prevent visual jump on first frame
|
||||
prevPosX = posX;
|
||||
prevPosY = posY;
|
||||
prevPosZ = posZ;
|
||||
|
||||
// Fire forward based on owner facing direction
|
||||
Entity owner = getOwnerEntity();
|
||||
if (owner != null) {
|
||||
float yaw = (float) Math.toRadians(owner.rotationYaw);
|
||||
float pitch = (float) Math.toRadians(0); // Fire horizontally
|
||||
motionX = -Math.sin(yaw) * Math.cos(pitch) * getSpeed();
|
||||
motionY = -Math.sin(pitch) * getSpeed();
|
||||
motionZ = Math.cos(yaw) * Math.cos(pitch) * getSpeed();
|
||||
} else {
|
||||
// Default: fire forward (positive X in model space)
|
||||
motionX = getSpeed();
|
||||
motionY = 0;
|
||||
motionZ = 0;
|
||||
}
|
||||
startPreviewFiringDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,36 +72,7 @@ public class EntityAbilityOrb extends EntityEnergyProjectile {
|
||||
* Called by ability when windup ends.
|
||||
*/
|
||||
public void startMoving(EntityLivingBase target) {
|
||||
setCharging(false);
|
||||
|
||||
// For player casters, snap to look vector for crosshair-aligned launch
|
||||
snapToPlayerLookVector();
|
||||
|
||||
// Update start position to current position
|
||||
startX = posX;
|
||||
startY = posY;
|
||||
startZ = posZ;
|
||||
|
||||
// Calculate velocity toward target
|
||||
Entity owner = getOwnerEntity();
|
||||
|
||||
if (target != null) {
|
||||
double dx = target.posX - posX;
|
||||
double dy = (target.posY + target.getEyeHeight()) - posY;
|
||||
double dz = target.posZ - posZ;
|
||||
double len = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
||||
if (len > 0) {
|
||||
motionX = (dx / len) * getSpeed();
|
||||
motionY = (dy / len) * getSpeed();
|
||||
motionZ = (dz / len) * getSpeed();
|
||||
}
|
||||
} else if (owner != null) {
|
||||
float yaw = (float) Math.toRadians(owner.rotationYaw);
|
||||
float pitch = (float) Math.toRadians(owner.rotationPitch);
|
||||
motionX = -Math.sin(yaw) * Math.cos(pitch) * getSpeed();
|
||||
motionY = -Math.sin(pitch) * getSpeed();
|
||||
motionZ = Math.cos(yaw) * Math.cos(pitch) * getSpeed();
|
||||
}
|
||||
startMovingAlongLookVectorDefault();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -185,15 +100,7 @@ public class EntityAbilityOrb extends EntityEnergyProjectile {
|
||||
}
|
||||
|
||||
// Check wall collision
|
||||
if (this.isCollidedHorizontally || this.isCollidedVertically) {
|
||||
if (!worldObj.isRemote) {
|
||||
hasHit = true;
|
||||
if (isExplosive()) {
|
||||
doExplosion();
|
||||
}
|
||||
}
|
||||
this.setDead();
|
||||
}
|
||||
handleSolidCollisionTermination();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -210,10 +117,26 @@ public class EntityAbilityOrb extends EntityEnergyProjectile {
|
||||
if (!isTrajectoryConcluded()) {
|
||||
updateTrajectory();
|
||||
} else {
|
||||
updateHoming();
|
||||
if (ticksExisted > HOMING_STARTUP_TICKS) {
|
||||
int homingTicks = ticksExisted - HOMING_STARTUP_TICKS;
|
||||
if (homingTicks >= HOMING_RAMP_TICKS) {
|
||||
updateHoming();
|
||||
} else {
|
||||
float ramp = homingTicks / (float) HOMING_RAMP_TICKS;
|
||||
updateHomingWithRamp(ramp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateHomingWithRamp(float ramp) {
|
||||
if (ramp <= 0.0f) return;
|
||||
float originalStrength = homingData.homingStrength;
|
||||
homingData.homingStrength = originalStrength * ramp;
|
||||
updateHoming();
|
||||
homingData.homingStrength = originalStrength;
|
||||
}
|
||||
|
||||
private void updateTrajectory() {
|
||||
if (isTrajectoryConcluded()) return;
|
||||
// i really fucking hate math
|
||||
@@ -233,50 +156,21 @@ public class EntityAbilityOrb extends EntityEnergyProjectile {
|
||||
}
|
||||
|
||||
private void checkBlockCollision() {
|
||||
Vec3 currentPos = Vec3.createVectorHelper(posX, posY, posZ);
|
||||
Vec3 nextPos = Vec3.createVectorHelper(posX + motionX, posY + motionY, posZ + motionZ);
|
||||
// Use full raytrace that doesn't stop at liquids and checks all blocks
|
||||
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;
|
||||
posY = blockHit.hitVec.yCoord;
|
||||
posZ = blockHit.hitVec.zCoord;
|
||||
doExplosion();
|
||||
}
|
||||
this.setDead();
|
||||
}
|
||||
handleBlockImpact(rayTraceBlocks(posX, posY, posZ, posX + motionX, posY + motionY, posZ + motionZ), true);
|
||||
}
|
||||
|
||||
private void checkEntityCollision() {
|
||||
double hitSize = size * 0.5;
|
||||
// Swept collision to avoid fast orbs skipping targets between ticks.
|
||||
double nextX = posX + motionX;
|
||||
double nextY = posY + motionY;
|
||||
double nextZ = posZ + motionZ;
|
||||
// Keep collision radius proportional to rendered orb size.
|
||||
double hitSize = Math.max(0.05, size * 0.5);
|
||||
AxisAlignedBB hitBox = AxisAlignedBB.getBoundingBox(
|
||||
posX - hitSize, posY - hitSize, posZ - hitSize,
|
||||
posX + hitSize, posY + hitSize, posZ + hitSize
|
||||
Math.min(posX, nextX) - hitSize, Math.min(posY, nextY) - hitSize, Math.min(posZ, nextZ) - hitSize,
|
||||
Math.max(posX, nextX) + hitSize, Math.max(posY, nextY) + hitSize, Math.max(posZ, nextZ) + hitSize
|
||||
);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<EntityLivingBase> entities = worldObj.getEntitiesWithinAABB(EntityLivingBase.class, hitBox);
|
||||
|
||||
for (EntityLivingBase entity : entities) {
|
||||
if (shouldIgnoreEntity(entity)) continue;
|
||||
|
||||
hasHit = true;
|
||||
|
||||
if (isExplosive()) {
|
||||
doExplosion();
|
||||
} else {
|
||||
applyDamage(entity);
|
||||
}
|
||||
|
||||
this.setDead();
|
||||
return;
|
||||
}
|
||||
processEntitiesInHitBox(hitBox, nextX, nextY, nextZ);
|
||||
}
|
||||
|
||||
// ==================== GETTERS ====================
|
||||
|
||||
@@ -2,10 +2,10 @@ package kamkeel.npcs.entity;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyBarrierData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyLightningData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyPanelData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyBarrierData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyLightningData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyPanelData;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
@@ -168,6 +168,17 @@ public class EntityAbilityPanel extends EntityAbilityBarrier {
|
||||
this.prevPosZ = owner.prevPosZ + (Math.cos(prevYawRad) * frontDist);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPositionAndRotation2(double x, double y, double z, float yaw, float pitch, int posRotationIncrements) {
|
||||
// HELD panels are fully owner-driven each tick via updateHeld().
|
||||
// Applying network position lerp on top of owner-following causes visible jitter/stutter
|
||||
// when the caster is moving, so ignore remote corrections in this mode.
|
||||
if (worldObj != null && worldObj.isRemote && mode == PanelMode.HELD && getOwnerEntity() != null) {
|
||||
return;
|
||||
}
|
||||
super.setPositionAndRotation2(x, y, z, yaw, pitch, posRotationIncrements);
|
||||
}
|
||||
|
||||
private void updateLaunched() {
|
||||
// Move forward on both sides for smooth client rendering
|
||||
this.posX += motionX;
|
||||
|
||||
@@ -2,7 +2,7 @@ package kamkeel.npcs.entity;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import kamkeel.npcs.controllers.data.ability.data.EnergyDisplayData;
|
||||
import kamkeel.npcs.controllers.data.ability.data.energy.EnergyDisplayData;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
@@ -10,7 +10,6 @@ import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.AxisAlignedBB;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.world.World;
|
||||
import noppes.npcs.LogWriter;
|
||||
import noppes.npcs.NpcDamageSource;
|
||||
import noppes.npcs.controllers.PartyController;
|
||||
import noppes.npcs.controllers.data.Party;
|
||||
|
||||
@@ -6,9 +6,9 @@ import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import kamkeel.npcs.controllers.data.ability.Ability;
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityPotionEffect;
|
||||
import kamkeel.npcs.controllers.data.ability.AbilityTargetHelper;
|
||||
import kamkeel.npcs.controllers.data.ability.TargetFilter;
|
||||
import kamkeel.npcs.controllers.data.ability.data.effect.AbilityPotionEffect;
|
||||
import kamkeel.npcs.controllers.data.ability.util.AbilityTargetHelper;
|
||||
import kamkeel.npcs.controllers.data.ability.enums.TargetFilter;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
|
||||
Loaded 100 of 150 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user