Make anchored lasers and rotation lock affect ability aim

- Anchored lasers re-derived their direction straight at the target every tick,
  discarding the rate-limited look vector, so track speed did nothing and the
  beam was undodgeable. They now converge on the point the owner is aiming at.
- Rotation-locked NPCs aimed energy projectiles and hit-scan projectiles from
  the target''s position, so the lock was purely visual. Both now respect the
  frozen heading.
This commit is contained in:
KAMKEEL committed 2026-08-11 03:43:37 -04:00
1 parent 9f4334a803
commit 7cafa4f677
3 files changed
+30 -13

No files matched your search

@@ -97,6 +97,15 @@ public class AbilityProjectile extends Ability implements IAbilityProjectile {
dz /= len;
}
// A rotation-locked NPC cannot turn to follow the target, so it can only hit what its
// frozen heading still covers - the same cone the player hit scan uses.
if (isRotationLockedForCurrentPhase()) {
Vec3 look = caster.getLookVec();
if (look != null && (dx * look.xCoord + dy * look.yCoord + dz * look.zCoord) < 0.95) {
return;
}
}
applyAbilityDamageWithDirection(caster, target, damage, knockback, dx, dz);
world.playSoundAtEntity(caster, "random.bow", 1.0f, 0.8f);
@@ -248,7 +248,12 @@ public abstract class AbilityEnergyProjectile<E extends EntityEnergyProjectile>
* NPCs use their resolved target so launch math can track the target immediately.
*/
protected EntityLivingBase getLaunchTarget(EntityLivingBase caster, EntityLivingBase target) {
return isPlayerCaster(caster) ? null : target;
if (isPlayerCaster(caster)) return null;
// A rotation-locked NPC cannot turn, so launching from the target's position would let
// it fire a perfectly aimed shot regardless of where it is facing. Fall back to the
// look vector, which reflects the frozen heading.
if (isRotationLockedForCurrentPhase()) return null;
return target;
}
/**
@@ -264,18 +264,21 @@ public class EntityAbilityLaser extends EntityEnergyProjectile {
Vec3 anchorPos = AnchorPointHelper.calculateAnchorPosition(livingOwner, anchorData);
setPosition(anchorPos.xCoord, anchorPos.yCoord, anchorPos.zCoord);
Entity targetEntity = getTargetEntity();
if (targetEntity instanceof EntityLivingBase && targetEntity.isEntityAlive()) {
EntityLivingBase target = (EntityLivingBase) targetEntity;
double dx = target.posX - anchorPos.xCoord;
double dy = (target.posY + target.getEyeHeight() - 0.4) - anchorPos.yCoord;
double dz = target.posZ - anchorPos.zCoord;
double len = Math.sqrt(dx * dx + dy * dy + dz * dz);
if (len > 0.0001) {
dirX = dx / len;
dirY = dy / len;
dirZ = dz / len;
}
// Converge on the point the owner is aiming at, not on the target itself. Aiming
// straight at the target would re-derive the direction every tick and ignore the
// rotation the owner is actually allowed to reach, which is what track speed and
// rotation lock constrain.
double aimX = livingOwner.posX + dirX * maxLength;
double aimY = livingOwner.posY + livingOwner.getEyeHeight() + dirY * maxLength;
double aimZ = livingOwner.posZ + dirZ * maxLength;
double dx = aimX - anchorPos.xCoord;
double dy = aimY - anchorPos.yCoord;
double dz = aimZ - anchorPos.zCoord;
double len = Math.sqrt(dx * dx + dy * dy + dz * dz);
if (len > 0.0001) {
dirX = dx / len;
dirY = dy / len;
dirZ = dz / len;
}
} else {
Vec3 direction = Vec3.createVectorHelper(dirX, dirY, dirZ);