diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/Ability.java b/src/main/java/kamkeel/npcs/controllers/data/ability/Ability.java index 9c40d9c19..767df8146 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/Ability.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/Ability.java @@ -2609,6 +2609,96 @@ public abstract class Ability implements IAbility, IAbilityAction { return false; } + /** + * Check if a caster's movement in a given direction is blocked by a solid enemy barrier. + * Tests a short segment from the caster's current position along the movement direction + * against all active dome and panel barriers. + * + * @return true if a solid enemy barrier blocks the movement + */ + protected static boolean isMovementBlockedByBarrier(EntityLivingBase caster, double dirX, double dirZ, double speed) { + if (caster.worldObj == null) return false; + + double nextX = caster.posX + dirX * speed; + double nextZ = caster.posZ + dirZ * speed; + double eyeY = caster.posY + caster.getEyeHeight() * 0.5; + + java.util.List barriers = EntityEnergyBarrier.getActiveBarriers(caster.worldObj); + for (EntityEnergyBarrier barrier : barriers) { + if (barrier.isDead || barrier.isCharging()) continue; + if (!barrier.getBarrierData().solid) continue; + + // Skip barriers owned by or allied to the caster + if (barrier.getOwnerEntityId() == caster.getEntityId()) continue; + Entity barrierOwner = barrier.getOwnerEntity(); + if (barrierOwner instanceof EntityLivingBase) { + if (AbilityTargetHelper.isAlly(caster, (EntityLivingBase) barrierOwner)) continue; + } + + if (barrier instanceof EntityEnergyDome) { + EntityEnergyDome dome = (EntityEnergyDome) barrier; + float radius = dome.getDomeRadius(); + + // Check if the movement segment crosses the dome boundary from outside + double ocX = caster.posX - dome.posX; + double ocY = eyeY - dome.posY; + double ocZ = caster.posZ - dome.posZ; + double originDistSq = ocX * ocX + ocY * ocY + ocZ * ocZ; + if (originDistSq < (double) radius * radius) continue; // Inside dome, skip + + double rdX = nextX - caster.posX; + double rdZ = nextZ - caster.posZ; + double a = rdX * rdX + rdZ * rdZ; + double b = 2.0 * (ocX * rdX + ocZ * rdZ); + double c = ocX * ocX + ocZ * ocZ - (double) radius * radius; + double discriminant = b * b - 4.0 * a * c; + if (discriminant < 0 || a < 1e-10) continue; + double sqrtD = Math.sqrt(discriminant); + double t1 = (-b - sqrtD) / (2.0 * a); + if (t1 >= 0.0 && t1 <= 1.0) return true; + } else if (barrier instanceof EntityEnergyPanel) { + EntityEnergyPanel panel = (EntityEnergyPanel) barrier; + float halfW = panel.getPanelData().getPanelWidth() * 0.5f; + float halfH = panel.getPanelData().getPanelHeight() * 0.5f; + float yawRad = (float) Math.toRadians(panel.getPanelYaw()); + double normalX = -Math.sin(yawRad); + double normalZ = Math.cos(yawRad); + double cos = Math.cos(yawRad); + double sin = Math.sin(yawRad); + + double relX = caster.posX - panel.posX; + double relZ = caster.posZ - panel.posZ; + double prevDist = relX * normalX + relZ * normalZ; + + double relNX = nextX - panel.posX; + double relNZ = nextZ - panel.posZ; + double currDist = relNX * normalX + relNZ * normalZ; + + // Check if segment crosses the panel plane + if (prevDist * currDist > 0) continue; + + double rayDirX = nextX - caster.posX; + double rayDirZ = nextZ - caster.posZ; + double denom = rayDirX * normalX + rayDirZ * normalZ; + if (Math.abs(denom) < 1e-10) continue; + + double t = -prevDist / denom; + if (t < 0.0 || t > 1.0) continue; + + double hitX = caster.posX + rayDirX * t; + double hitZ = caster.posZ + rayDirZ * t; + + double localRight = (hitX - panel.posX) * cos + (hitZ - panel.posZ) * sin; + if (Math.abs(localRight) > halfW) continue; + double localY = eyeY - panel.posY; + if (Math.abs(localY) > halfH) continue; + + return true; + } + } + return false; + } + // ═══════════════════════════════════════════════════════════════════ // LINE-OF-SIGHT UTILITIES // ═══════════════════════════════════════════════════════════════════ diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityMovement.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityMovement.java index 266a0d6a4..020b8e8e3 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityMovement.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilityMovement.java @@ -202,13 +202,14 @@ public abstract class AbilityMovement extends Ability { } /** - * Check if movement direction is blocked by walls. + * Check if movement direction is blocked by walls or solid barriers. * * @return true if blocked */ protected boolean checkBlocked(EntityLivingBase caster, float speed) { if (isPreview() || movementDirection == null) return false; - return isMovementBlocked(caster, movementDirection.xCoord, movementDirection.zCoord, speed); + return isMovementBlocked(caster, movementDirection.xCoord, movementDirection.zCoord, speed) + || isMovementBlockedByBarrier(caster, movementDirection.xCoord, movementDirection.zCoord, speed); } // ═══════════════════════════════════════════════════════════════════ diff --git a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilitySlam.java b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilitySlam.java index 0a5c134f7..9e917d2d2 100644 --- a/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilitySlam.java +++ b/src/main/java/kamkeel/npcs/controllers/data/ability/type/AbilitySlam.java @@ -427,6 +427,27 @@ public class AbilitySlam extends Ability implements IAbilitySlam { ((EntityNPCInterface) caster).getNavigator().clearPathEntity(); } + // Check if slam trajectory is blocked by a solid barrier + if (!isPreview() && !caster.worldObj.isRemote) { + double moveX = caster.motionX; + double moveZ = caster.motionZ; + double hSpeed = Math.sqrt(moveX * moveX + moveZ * moveZ); + if (hSpeed > 0.01) { + double normX = moveX / hSpeed; + double normZ = moveZ / hSpeed; + if (isMovementBlockedByBarrier(caster, normX, normZ, hSpeed)) { + caster.motionX = 0; + caster.motionZ = 0; + if (caster instanceof EntityPlayerMP) { + ((EntityPlayerMP) caster).playerNetServerHandler.sendPacket( + new S12PacketEntityVelocity(caster)); + } else { + caster.velocityChanged = true; + } + } + } + } + // Track if entity has risen above launch position. // This prevents premature landing detection for players, where // server-side caster.onGround remains true from stale C03 packets