mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-12 06:07:27 +00:00
Refactor entity owner checks and enhance FULL_MODEL rotation support in ability entities
This commit is contained in:
1 parent
ce24c0a4ff
commit
eda8931b5f
6 files changed
+175
-57
No files matched your search
@@ -386,33 +386,26 @@ public class EntityAbilityBeam extends EntityAbilityProjectile {
|
||||
chargeTick++;
|
||||
|
||||
Entity owner = getOwnerEntity();
|
||||
if (owner == null || owner.isDead) {
|
||||
setDead();
|
||||
return;
|
||||
if (owner != null) {
|
||||
Vec3 pos;
|
||||
if (owner instanceof EntityLivingBase) {
|
||||
pos = AnchorPointHelper.calculateAnchorPosition((EntityLivingBase) owner, anchorData, chargeOffsetDistance);
|
||||
} else {
|
||||
float yaw = (float) Math.toRadians(owner.rotationYaw);
|
||||
double offsetX = -Math.sin(yaw) * chargeOffsetDistance;
|
||||
double offsetZ = Math.cos(yaw) * chargeOffsetDistance;
|
||||
pos = Vec3.createVectorHelper(
|
||||
owner.posX + offsetX,
|
||||
owner.posY + owner.getEyeHeight() * 0.7,
|
||||
owner.posZ + offsetZ
|
||||
);
|
||||
}
|
||||
|
||||
setPosition(pos.xCoord, pos.yCoord, pos.zCoord);
|
||||
startX = pos.xCoord;
|
||||
startY = pos.yCoord;
|
||||
startZ = pos.zCoord;
|
||||
}
|
||||
|
||||
// Calculate position based on anchor point
|
||||
Vec3 pos;
|
||||
if (owner instanceof EntityLivingBase) {
|
||||
pos = AnchorPointHelper.calculateAnchorPosition((EntityLivingBase) owner, anchorData, chargeOffsetDistance);
|
||||
} else {
|
||||
// Fallback for non-living entities (shouldn't happen normally)
|
||||
float yaw = (float) Math.toRadians(owner.rotationYaw);
|
||||
double offsetX = -Math.sin(yaw) * chargeOffsetDistance;
|
||||
double offsetZ = Math.cos(yaw) * chargeOffsetDistance;
|
||||
pos = Vec3.createVectorHelper(
|
||||
owner.posX + offsetX,
|
||||
owner.posY + owner.getEyeHeight() * 0.7,
|
||||
owner.posZ + offsetZ
|
||||
);
|
||||
}
|
||||
|
||||
setPosition(pos.xCoord, pos.yCoord, pos.zCoord);
|
||||
|
||||
// Also update origin for when firing starts
|
||||
startX = pos.xCoord;
|
||||
startY = pos.yCoord;
|
||||
startZ = pos.zCoord;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -391,19 +391,13 @@ public class EntityAbilityDisc extends EntityAbilityProjectile {
|
||||
protected void updateCharging() {
|
||||
chargeTick++;
|
||||
|
||||
Entity owner = getOwnerEntity();
|
||||
if (owner == null || owner.isDead) {
|
||||
setDead();
|
||||
return;
|
||||
}
|
||||
|
||||
// Grow size based on charge progress
|
||||
float progress = getChargeProgress();
|
||||
this.discRadius = targetDiscRadius * progress;
|
||||
this.discThickness = targetDiscThickness * progress;
|
||||
this.size = this.discRadius; // Base size for interpolation
|
||||
|
||||
// Calculate position based on anchor point, offset downward by half radius to center
|
||||
Entity owner = getOwnerEntity();
|
||||
if (owner instanceof EntityLivingBase) {
|
||||
Vec3 pos = AnchorPointHelper.calculateAnchorPosition((EntityLivingBase) owner, anchorData);
|
||||
setPosition(pos.xCoord, pos.yCoord, pos.zCoord);
|
||||
|
||||
@@ -156,8 +156,7 @@ public class EntityAbilityLaser extends EntityAbilityProjectile {
|
||||
*/
|
||||
private void updateLaserOriginAndDirection() {
|
||||
Entity owner = getOwnerEntity();
|
||||
if (owner == null || owner.isDead) return;
|
||||
if (!(owner instanceof EntityLivingBase)) return;
|
||||
if (owner == null || !(owner instanceof EntityLivingBase)) return;
|
||||
|
||||
EntityLivingBase livingOwner = (EntityLivingBase) owner;
|
||||
|
||||
|
||||
@@ -184,16 +184,20 @@ public abstract class EntityAbilityProjectile extends Entity implements IEnergyP
|
||||
|
||||
// Skip lifetime/distance checks in preview mode
|
||||
if (!previewMode) {
|
||||
// Failsafe: if owner entity is gone, dead, or NPC was killed/reset, self-destruct
|
||||
// Only self-destruct if the owner is confirmed dead/killed.
|
||||
// A null owner (unloaded/out of chunk) is NOT treated as dead —
|
||||
// the projectile keeps acting normally and deathWorldTime handles lifetime.
|
||||
if (ownerEntityId >= 0 && ticksExisted > 5) {
|
||||
Entity owner = worldObj.getEntityByID(ownerEntityId);
|
||||
if (owner == null || owner.isDead) {
|
||||
this.setDead();
|
||||
return;
|
||||
}
|
||||
if (owner instanceof EntityNPCInterface && ((EntityNPCInterface) owner).isKilled()) {
|
||||
this.setDead();
|
||||
return;
|
||||
if (owner != null) {
|
||||
if (owner.isDead) {
|
||||
this.setDead();
|
||||
return;
|
||||
}
|
||||
if (owner instanceof EntityNPCInterface && ((EntityNPCInterface) owner).isKilled()) {
|
||||
this.setDead();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -506,16 +510,14 @@ public abstract class EntityAbilityProjectile extends Entity implements IEnergyP
|
||||
* Update during charging state - grow size and follow anchor.
|
||||
* Default implementation grows {@code size} from 0 to {@code targetSize}.
|
||||
* Override in subclasses for type-specific charging (Disc, Beam).
|
||||
*
|
||||
* Note: Owner death checks are handled by onUpdate() before this is called.
|
||||
*/
|
||||
protected void updateCharging() {
|
||||
chargeTick++;
|
||||
Entity owner = getOwnerEntity();
|
||||
if (owner == null || owner.isDead) {
|
||||
setDead();
|
||||
return;
|
||||
}
|
||||
float progress = getChargeProgress();
|
||||
this.size = targetSize * progress;
|
||||
Entity owner = getOwnerEntity();
|
||||
if (owner instanceof EntityLivingBase) {
|
||||
Vec3 pos = AnchorPointHelper.calculateAnchorPosition((EntityLivingBase) owner, anchorData);
|
||||
setPosition(pos.xCoord, pos.yCoord, pos.zCoord);
|
||||
|
||||
@@ -144,15 +144,19 @@ public class EntityAbilitySweeper extends Entity implements IEntityAdditionalSpa
|
||||
return;
|
||||
}
|
||||
|
||||
// Follow owner
|
||||
Entity owner = getOwner();
|
||||
if (owner == null) {
|
||||
if (owner != null && owner.isDead) {
|
||||
this.setDead();
|
||||
return;
|
||||
}
|
||||
if (owner instanceof EntityNPCInterface && ((EntityNPCInterface) owner).isKilled()) {
|
||||
this.setDead();
|
||||
return;
|
||||
}
|
||||
|
||||
// Update position to follow owner
|
||||
this.setPosition(owner.posX, owner.posY + beamHeight, owner.posZ);
|
||||
if (owner != null) {
|
||||
this.setPosition(owner.posX, owner.posY + beamHeight, owner.posZ);
|
||||
}
|
||||
|
||||
// Check if sweep is done
|
||||
if (completedRotations >= numberOfRotations) {
|
||||
|
||||
@@ -119,6 +119,20 @@ public class AnchorPointHelper {
|
||||
y += rotatedOffset.yCoord;
|
||||
z += rotatedOffset.zCoord;
|
||||
|
||||
// Apply FULL_MODEL rotation if active
|
||||
FramePart fullModel = getFullModelPart(entity);
|
||||
if (fullModel != null) {
|
||||
double dx = x - entity.posX;
|
||||
double dy = y - entity.posY;
|
||||
double dz = z - entity.posZ;
|
||||
Vec3 rotated = applyFullModelToWorldOffset(fullModel, entity, dx, dy, dz, bodyYaw, scale);
|
||||
return Vec3.createVectorHelper(
|
||||
entity.posX + rotated.xCoord,
|
||||
entity.posY + rotated.yCoord,
|
||||
entity.posZ + rotated.zCoord
|
||||
);
|
||||
}
|
||||
|
||||
return Vec3.createVectorHelper(x, y, z);
|
||||
}
|
||||
|
||||
@@ -162,16 +176,32 @@ public class AnchorPointHelper {
|
||||
|
||||
/**
|
||||
* Calculate hand position using animation data if available.
|
||||
* Applies FULL_MODEL rotation if an active animation has it.
|
||||
*/
|
||||
private static Vec3 calculateHandPosition(EntityLivingBase entity, EnergyAnchorData anchor, boolean rightHand, float scale) {
|
||||
// Try to get animated position first
|
||||
Vec3 animatedPos = getAnimatedHandPosition(entity, anchor, rightHand, scale);
|
||||
if (animatedPos != null) {
|
||||
return animatedPos;
|
||||
Vec3 pos = getAnimatedHandPosition(entity, anchor, rightHand, scale);
|
||||
if (pos == null) {
|
||||
// Fallback to static position
|
||||
pos = calculateFallbackHandPosition(entity, anchor, rightHand, scale);
|
||||
}
|
||||
|
||||
// Fallback to static position
|
||||
return calculateFallbackHandPosition(entity, anchor, rightHand, scale);
|
||||
// Apply FULL_MODEL rotation if active
|
||||
FramePart fullModel = getFullModelPart(entity);
|
||||
if (fullModel != null) {
|
||||
double dx = pos.xCoord - entity.posX;
|
||||
double dy = pos.yCoord - entity.posY;
|
||||
double dz = pos.zCoord - entity.posZ;
|
||||
float bodyYaw = (float) Math.toRadians(entity.renderYawOffset);
|
||||
Vec3 rotated = applyFullModelToWorldOffset(fullModel, entity, dx, dy, dz, bodyYaw, scale);
|
||||
return Vec3.createVectorHelper(
|
||||
entity.posX + rotated.xCoord,
|
||||
entity.posY + rotated.yCoord,
|
||||
entity.posZ + rotated.zCoord
|
||||
);
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -390,4 +420,100 @@ public class AnchorPointHelper {
|
||||
double worldZ = -offsetX * sin + offsetZ * cos;
|
||||
return Vec3.createVectorHelper(worldX, offsetY, worldZ);
|
||||
}
|
||||
|
||||
// ==================== FULL_MODEL ANIMATION SUPPORT ====================
|
||||
|
||||
/**
|
||||
* Get the FULL_MODEL FramePart if the entity has an active animation with it.
|
||||
* Returns null if no FULL_MODEL animation is active.
|
||||
*/
|
||||
private static FramePart getFullModelPart(EntityLivingBase entity) {
|
||||
AnimationData animData = AnimationData.getData(entity);
|
||||
if (animData == null || !animData.isActive() || animData.animation == null) {
|
||||
return null;
|
||||
}
|
||||
Frame frame = (Frame) animData.animation.currentFrame();
|
||||
if (frame == null) {
|
||||
return null;
|
||||
}
|
||||
FramePart part = frame.frameParts.get(EnumAnimationPart.FULL_MODEL);
|
||||
return part;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply FULL_MODEL rotation to a world-space offset from entity position.
|
||||
* Converts the offset to model space, applies FULL_MODEL rotation and pivots,
|
||||
* then converts back to world space.
|
||||
*
|
||||
* The rendering pipeline is: entity_pos * body_yaw * scale(-1,-1,1) * FULL_MODEL * parts
|
||||
* Combined body_yaw + scale maps model space to world space:
|
||||
* worldX = mx*cos(yaw) + mz*sin(yaw)
|
||||
* worldY = -my (model Y-down → world Y-up)
|
||||
* worldZ = mx*sin(yaw) - mz*cos(yaw)
|
||||
* This transform is its own inverse for X,Z.
|
||||
*
|
||||
* FULL_MODEL rotation order matches renderer: vertex sees Z, then Y, then X.
|
||||
*/
|
||||
private static Vec3 applyFullModelToWorldOffset(FramePart fullModel, EntityLivingBase entity,
|
||||
double worldDX, double worldDY, double worldDZ,
|
||||
float bodyYaw, float scale) {
|
||||
float[] rotations = getRotations(fullModel, entity);
|
||||
float[] pivots = getPivots(fullModel, entity);
|
||||
|
||||
// Quick exit if no transform needed
|
||||
boolean hasRotation = rotations[0] != 0 || rotations[1] != 0 || rotations[2] != 0;
|
||||
boolean hasPivot = pivots[0] != 0 || pivots[1] != 0 || pivots[2] != 0;
|
||||
if (!hasRotation && !hasPivot) {
|
||||
return Vec3.createVectorHelper(worldDX, worldDY, worldDZ);
|
||||
}
|
||||
|
||||
// Convert world offset to model space
|
||||
// Body yaw + scale combined transform is an involution (self-inverse) for X,Z
|
||||
double cosYaw = Math.cos(bodyYaw);
|
||||
double sinYaw = Math.sin(bodyYaw);
|
||||
|
||||
double mx = worldDX * cosYaw + worldDZ * sinYaw;
|
||||
double mz = worldDX * sinYaw - worldDZ * cosYaw;
|
||||
double my = -worldDY; // Invert Y (world Y-up → model Y-down)
|
||||
|
||||
// Apply FULL_MODEL rotations (vertex order: Z first, then Y, then X)
|
||||
// Matches renderer: glRotatef(X), glRotatef(Y), glRotatef(Z) → vertex sees Z,Y,X
|
||||
|
||||
// Z rotation
|
||||
double cosZ = Math.cos(rotations[2]);
|
||||
double sinZ = Math.sin(rotations[2]);
|
||||
double nx = mx * cosZ - my * sinZ;
|
||||
double ny = mx * sinZ + my * cosZ;
|
||||
mx = nx;
|
||||
my = ny;
|
||||
|
||||
// Y rotation
|
||||
double cosY = Math.cos(rotations[1]);
|
||||
double sinY = Math.sin(rotations[1]);
|
||||
nx = mx * cosY + mz * sinY;
|
||||
double nz = -mx * sinY + mz * cosY;
|
||||
mx = nx;
|
||||
mz = nz;
|
||||
|
||||
// X rotation
|
||||
double cosX = Math.cos(rotations[0]);
|
||||
double sinX = Math.sin(rotations[0]);
|
||||
ny = my * cosX - mz * sinX;
|
||||
nz = my * sinX + mz * cosX;
|
||||
my = ny;
|
||||
mz = nz;
|
||||
|
||||
// Apply pivots in model space (after rotation, matching GL vertex ordering)
|
||||
// Renderer: glTranslatef(px, -py, pz) → pivot Y is negated for model Y-down space
|
||||
mx += pivots[0] * MODEL_SCALE * scale;
|
||||
my -= pivots[1] * MODEL_SCALE * scale;
|
||||
mz += pivots[2] * MODEL_SCALE * scale;
|
||||
|
||||
// Convert back to world space (same involution for X,Z; invert Y back)
|
||||
double newDX = mx * cosYaw + mz * sinYaw;
|
||||
double newDZ = mx * sinYaw - mz * cosYaw;
|
||||
double newDY = -my;
|
||||
|
||||
return Vec3.createVectorHelper(newDX, newDY, newDZ);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user