From d6eccdd556bd433efd871541e77ea8b5a5230a2d Mon Sep 17 00:00:00 2001 From: Kam Date: Sun, 1 Mar 2026 04:32:25 -0500 Subject: [PATCH] Ki Redirection for Barriers, Color Shifting, Better Logic Handling for Built in DBC Ki Abilities --- .../npcs/entity/EntityEnergyBarrier.java | 91 ++++++++++++++ .../kamkeel/npcs/entity/EntityEnergyDome.java | 50 ++++++++ .../npcs/entity/EntityEnergyPanel.java | 57 +++++++++ .../npcs/entity/EntityEnergyProjectile.java | 111 +----------------- 4 files changed, 201 insertions(+), 108 deletions(-) diff --git a/src/main/java/kamkeel/npcs/entity/EntityEnergyBarrier.java b/src/main/java/kamkeel/npcs/entity/EntityEnergyBarrier.java index 82781eed7..b16448c33 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityEnergyBarrier.java +++ b/src/main/java/kamkeel/npcs/entity/EntityEnergyBarrier.java @@ -171,6 +171,32 @@ public abstract class EntityEnergyBarrier extends EntityEnergyAbility { double prevPosX, double prevPosY, double prevPosZ, int ownerEntityId); + /** + * Compute the outward surface normal at a given hit point on this barrier. + * Used by external systems (e.g., mixin-based projectile reflection) that cannot + * access barrier-subtype internals. + * + * @param hitX, hitY, hitZ Position near the barrier surface + * @param velX, velY, velZ Incoming velocity (used as fallback direction) + * @return double[3] normalized surface normal pointing outward from the barrier + */ + public abstract double[] getSurfaceNormal(double hitX, double hitY, double hitZ, + double velX, double velY, double velZ); + + /** + * Compute a position just outside the barrier surface, along the outward normal + * from the given position. Used to snap paused/reflected projectiles outside + * the collision zone. + * + * @param px, py, pz Current projectile position + * @param velX, velY, velZ Current velocity (used as fallback for direction) + * @param bias Distance outside the surface to snap to + * @return double[3] position outside the barrier surface + */ + public abstract double[] getOutsideSurfacePoint(double px, double py, double pz, + double velX, double velY, double velZ, + float bias); + // ==================== GENERIC PROJECTILE HIT ==================== /** @@ -209,6 +235,71 @@ public abstract class EntityEnergyBarrier extends EntityEnergyAbility { return true; } + /** + * Apply damage from a generic (non-CNPC+) projectile and return full hit context. + * Same logic as onProjectileHitResolved() but for raw Entity + typeId. + * + * @param projectileEntity The entity that hit the barrier (for event context) + * @param damage Base damage to apply + * @param typeId Type identifier for damage multiplier lookup (e.g., "dbc.ki_attack") + * @return Full ProjectileHitOutcome with result, reflect data, remaining damage + */ + public ProjectileHitOutcome onGenericProjectileHitResolved(Entity projectileEntity, float damage, String typeId) { + float multiplier = barrierData.getMultiplier(typeId); + float finalDamage = damage * multiplier; + + if (!worldObj.isRemote) { + float eventDamage = EventHooks.onEnergyBarrierHit(this, ownerEntityId, null, finalDamage); + if (eventDamage < 0) { + return new ProjectileHitOutcome( + ProjectileHitResult.PASS, + currentHealth, currentHealth, + 0.0f, 0.0f, + barrierData.reflect, barrierData.reflectStrengthPct, + barrierData.useHealth + ); + } + finalDamage = eventDamage; + } + + triggerHitFlash(); + float healthBefore = currentHealth; + + if (!barrierData.useHealth) { + return new ProjectileHitOutcome( + ProjectileHitResult.BLOCKED, + healthBefore, healthBefore, + 0.0f, 0.0f, + barrierData.reflect, barrierData.reflectStrengthPct, + false + ); + } + + currentHealth -= finalDamage; + syncHealthPercent(); + + if (currentHealth <= 0) { + float remaining = Math.max(0.0f, finalDamage - healthBefore); + onBarrierDestroyed(); + this.setDead(); + return new ProjectileHitOutcome( + ProjectileHitResult.BROKEN, + healthBefore, 0.0f, + finalDamage, remaining, + barrierData.reflect, barrierData.reflectStrengthPct, + true + ); + } + + return new ProjectileHitOutcome( + ProjectileHitResult.BLOCKED, + healthBefore, currentHealth, + finalDamage, 0.0f, + barrierData.reflect, barrierData.reflectStrengthPct, + true + ); + } + // ==================== PROJECTILE HIT ==================== /** diff --git a/src/main/java/kamkeel/npcs/entity/EntityEnergyDome.java b/src/main/java/kamkeel/npcs/entity/EntityEnergyDome.java index 0e8f0bf56..535d1cdff 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityEnergyDome.java +++ b/src/main/java/kamkeel/npcs/entity/EntityEnergyDome.java @@ -213,6 +213,56 @@ public class EntityEnergyDome extends EntityEnergyBarrier { return isIncomingRay(posX, posY, posZ, prevPosX, prevPosY, prevPosZ, ownerEntityId); } + @Override + public double[] getSurfaceNormal(double hitX, double hitY, double hitZ, + double velX, double velY, double velZ) { + double nx = hitX - this.posX; + double ny = hitY - this.posY; + double nz = hitZ - this.posZ; + double lenSq = nx * nx + ny * ny + nz * nz; + if (lenSq > 1.0e-8) { + double invLen = 1.0 / Math.sqrt(lenSq); + return new double[]{nx * invLen, ny * invLen, nz * invLen}; + } + // Fallback: use negative velocity direction + double vLenSq = velX * velX + velY * velY + velZ * velZ; + if (vLenSq > 1.0e-8) { + double invVLen = 1.0 / Math.sqrt(vLenSq); + return new double[]{-velX * invVLen, -velY * invVLen, -velZ * invVLen}; + } + return new double[]{0.0, 1.0, 0.0}; + } + + @Override + public double[] getOutsideSurfacePoint(double px, double py, double pz, + double velX, double velY, double velZ, + float bias) { + double nx = px - this.posX; + double ny = py - this.posY; + double nz = pz - this.posZ; + double len = Math.sqrt(nx * nx + ny * ny + nz * nz); + + if (len < 1.0e-5) { + double vLen = Math.sqrt(velX * velX + velY * velY + velZ * velZ); + if (vLen > 1.0e-5) { + nx = -velX / vLen; + ny = -velY / vLen; + nz = -velZ / vLen; + } else { + nx = 0.0; ny = 1.0; nz = 0.0; + } + len = 1.0; + } + + double invLen = 1.0 / len; + double target = domeRadius + bias; + return new double[]{ + this.posX + nx * invLen * target, + this.posY + ny * invLen * target, + this.posZ + nz * invLen * target + }; + } + @Override public float getMaxExtent() { return domeRadius; diff --git a/src/main/java/kamkeel/npcs/entity/EntityEnergyPanel.java b/src/main/java/kamkeel/npcs/entity/EntityEnergyPanel.java index ad25afa0b..8ed770f8a 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityEnergyPanel.java +++ b/src/main/java/kamkeel/npcs/entity/EntityEnergyPanel.java @@ -362,6 +362,63 @@ public class EntityEnergyPanel extends EntityEnergyBarrier { return isIncomingRay(posX, posY, posZ, prevPosX, prevPosY, prevPosZ, ownerEntityId); } + @Override + public double[] getSurfaceNormal(double hitX, double hitY, double hitZ, + double velX, double velY, double velZ) { + float yawRad = (float) Math.toRadians(panelYaw); + double nx = -Math.sin(yawRad); + double nz = Math.cos(yawRad); + + // Determine which side the hit is on + double relX = hitX - this.posX; + double relZ = hitZ - this.posZ; + double side = relX * nx + relZ * nz; + if (Math.abs(side) < 1.0e-5) { + // Near the plane: use incoming velocity to determine side + side = velX * nx + velZ * nz; + } + if (side < 0.0) { + nx = -nx; + nz = -nz; + } + + return new double[]{nx, 0.0, nz}; + } + + @Override + public double[] getOutsideSurfacePoint(double px, double py, double pz, + double velX, double velY, double velZ, + float bias) { + float yawRad = (float) Math.toRadians(panelYaw); + double normalX = -Math.sin(yawRad); + double normalZ = Math.cos(yawRad); + + double relX = px - this.posX; + double relZ = pz - this.posZ; + double signedDist = relX * normalX + relZ * normalZ; + + double halfThickness = 0.25; + double side; + if (Math.abs(signedDist) > 0.035) { + side = signedDist >= 0 ? 1.0 : -1.0; + } else { + double vDot = velX * normalX + velZ * normalZ; + if (Math.abs(vDot) > 1.0e-5) { + side = vDot < 0 ? 1.0 : -1.0; + } else { + side = signedDist >= 0 ? 1.0 : -1.0; + } + } + + double targetDist = side * (halfThickness + bias); + double delta = targetDist - signedDist; + return new double[]{ + px + normalX * delta, + py, + pz + normalZ * delta + }; + } + @Override public float getMaxExtent() { return Math.max(panelData.panelWidth, panelData.panelHeight) * 0.5f + 1.0f; diff --git a/src/main/java/kamkeel/npcs/entity/EntityEnergyProjectile.java b/src/main/java/kamkeel/npcs/entity/EntityEnergyProjectile.java index ea1b64dfb..bba50a69d 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityEnergyProjectile.java +++ b/src/main/java/kamkeel/npcs/entity/EntityEnergyProjectile.java @@ -681,47 +681,7 @@ public abstract class EntityEnergyProjectile extends EntityEnergyAbility { } protected double[] getBarrierImpactNormal(EntityEnergyBarrier barrier, double velocityX, double velocityY, double velocityZ) { - if (barrier instanceof EntityEnergyDome) { - EntityEnergyDome dome = (EntityEnergyDome) barrier; - return normalizeVector(posX - dome.posX, posY - dome.posY, posZ - dome.posZ, velocityX, velocityY, velocityZ); - } - - if (barrier instanceof EntityEnergyPanel) { - EntityEnergyPanel panel = (EntityEnergyPanel) barrier; - float yawRad = (float) Math.toRadians(panel.getPanelYaw()); - double nx = -Math.sin(yawRad); - double nz = Math.cos(yawRad); - double relX = posX - panel.posX; - double relZ = posZ - panel.posZ; - double signedDist = relX * nx + relZ * nz; - double side = signedDist; - if (Math.abs(side) < 1.0e-5) { - side = velocityX * nx + velocityZ * nz; - } - if (side < 0.0) { - nx = -nx; - nz = -nz; - } - return normalizeVector(nx, 0.0, nz, velocityX, velocityY, velocityZ); - } - - return normalizeVector(posX - barrier.posX, posY - barrier.posY, posZ - barrier.posZ, velocityX, velocityY, velocityZ); - } - - protected double[] normalizeVector(double x, double y, double z, double fallbackX, double fallbackY, double fallbackZ) { - double lenSq = x * x + y * y + z * z; - if (lenSq > 1.0e-8) { - double invLen = 1.0 / Math.sqrt(lenSq); - return new double[]{x * invLen, y * invLen, z * invLen}; - } - - double fallbackLenSq = fallbackX * fallbackX + fallbackY * fallbackY + fallbackZ * fallbackZ; - if (fallbackLenSq > 1.0e-8) { - double invFallbackLen = 1.0 / Math.sqrt(fallbackLenSq); - return new double[]{-fallbackX * invFallbackLen, -fallbackY * invFallbackLen, -fallbackZ * invFallbackLen}; - } - - return new double[]{0.0, 1.0, 0.0}; + return barrier.getSurfaceNormal(posX, posY, posZ, velocityX, velocityY, velocityZ); } protected void beginBarrierImpactPause(boolean destroyOnResume, EntityEnergyBarrier barrier) { @@ -752,73 +712,8 @@ public abstract class EntityEnergyProjectile extends EntityEnergyAbility { if (barrier == null) return; float bias = getBarrierPauseOutsideDistance(); - if (barrier instanceof EntityEnergyDome) { - EntityEnergyDome dome = (EntityEnergyDome) barrier; - double nx = posX - dome.posX; - double ny = posY - dome.posY; - double nz = posZ - dome.posZ; - double len = Math.sqrt(nx * nx + ny * ny + nz * nz); - - if (len < 1.0e-5) { - double vLen = Math.sqrt(motionX * motionX + motionY * motionY + motionZ * motionZ); - if (vLen > 1.0e-5) { - nx = -motionX / vLen; - ny = -motionY / vLen; - nz = -motionZ / vLen; - len = 1.0; - } else { - nx = 0.0; - ny = 1.0; - nz = 0.0; - len = 1.0; - } - } - - double invLen = 1.0 / len; - double target = dome.getDomeRadius() + bias; - setPosition( - dome.posX + nx * invLen * target, - dome.posY + ny * invLen * target, - dome.posZ + nz * invLen * target - ); - return; - } - - if (barrier instanceof EntityEnergyPanel) { - EntityEnergyPanel panel = (EntityEnergyPanel) barrier; - float yawRad = (float) Math.toRadians(panel.getPanelYaw()); - double normalX = -Math.sin(yawRad); - double normalZ = Math.cos(yawRad); - double relX = posX - panel.posX; - double relZ = posZ - panel.posZ; - double signedDist = relX * normalX + relZ * normalZ; - double halfThickness = 0.25; - double side; - if (Math.abs(signedDist) > 0.035) { - side = signedDist >= 0 ? 1.0 : -1.0; - } else { - // Near the plane, use incoming direction so we don't snap through and "fling". - double vDot = motionX * normalX + motionZ * normalZ; - if (Math.abs(vDot) > 1.0e-5) { - side = vDot < 0 ? 1.0 : -1.0; - } else { - side = signedDist >= 0 ? 1.0 : -1.0; - } - } - double targetDist = side * (halfThickness + bias); - double delta = targetDist - signedDist; - setPosition(posX + normalX * delta, posY, posZ + normalZ * delta); - return; - } - - double vLen = Math.sqrt(motionX * motionX + motionY * motionY + motionZ * motionZ); - if (vLen > 1.0e-5) { - setPosition( - posX - (motionX / vLen) * bias, - posY - (motionY / vLen) * bias, - posZ - (motionZ / vLen) * bias - ); - } + double[] point = barrier.getOutsideSurfacePoint(posX, posY, posZ, motionX, motionY, motionZ, bias); + setPosition(point[0], point[1], point[2]); } protected boolean tickBarrierImpactPause() {