Add NPC inline edit support and adjust ability launch mechanics

This commit is contained in:
Kam committed 2026-02-13 15:01:28 -05:00
1 parent ac223cbb53
commit 910f3b3744
7 files changed
+76 -3

No files matched your search

@@ -130,6 +130,12 @@ public abstract class Ability implements IAbility {
protected transient boolean previewMode = false;
protected transient PreviewEntityHandler previewEntityHandler;
// ═══════════════════════════════════════════════════════════════════
// GUI CONTEXT (client-side only, not saved)
// ═══════════════════════════════════════════════════════════════════
protected transient boolean npcInlineEdit = false;
// ═══════════════════════════════════════════════════════════════════
// ABSTRACT METHODS
// ═══════════════════════════════════════════════════════════════════
@@ -1282,6 +1288,14 @@ public abstract class Ability implements IAbility {
return false;
}
public boolean isNpcInlineEdit() {
return npcInlineEdit;
}
public void setNpcInlineEdit(boolean npcInlineEdit) {
this.npcInlineEdit = npcInlineEdit;
}
public int getWeight() {
return weight;
}
@@ -274,6 +274,9 @@ public class EntityAbilityBeam extends EntityAbilityProjectile {
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;
@@ -226,6 +226,9 @@ public class EntityAbilityDisc extends EntityAbilityProjectile {
setCharging(false);
// For player casters, snap to look vector for crosshair-aligned launch
snapToPlayerLookVector();
// Update start position to current position
startX = posX;
startY = posY;
@@ -200,6 +200,9 @@ public class EntityAbilityOrb extends EntityAbilityProjectile {
setCharging(false);
// For player casters, snap to look vector for crosshair-aligned launch
snapToPlayerLookVector();
// Update start position to current position
startX = posX;
startY = posY;
@@ -17,6 +17,7 @@ import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.DamageSource;
import net.minecraft.util.Vec3;
import net.minecraft.world.World;
import noppes.npcs.NpcDamageSource;
import noppes.npcs.api.entity.IEnergyProjectile;
@@ -428,6 +429,32 @@ public abstract class EntityAbilityProjectile extends Entity implements IEnergyP
}
}
// ==================== LAUNCH HELPERS ====================
/**
* For player casters, snap the projectile to a position along the player's look vector.
* This ensures projectiles launch aligned with the crosshair rather than from the hand anchor.
* Only affects player casters; NPC projectiles launch from their anchor position as configured.
* Should be called in startMoving/startFiring BEFORE setting startX/Y/Z.
*/
protected void snapToPlayerLookVector() {
Entity owner = getOwnerEntity();
if (!(owner instanceof EntityPlayer)) return;
Vec3 look = owner.getLookVec();
double eyeY = owner.posY + owner.getEyeHeight();
float frontDist = Math.max(0.5f, size * 0.5f);
double newX = owner.posX + look.xCoord * frontDist;
double newY = eyeY + look.yCoord * frontDist;
double newZ = owner.posZ + look.zCoord * frontDist;
setPosition(newX, newY, newZ);
prevPosX = newX;
prevPosY = newY;
prevPosZ = newZ;
}
// ==================== ENTITY HELPERS ====================
public Entity getOwnerEntity() {
@@ -2,6 +2,7 @@ package kamkeel.npcs.util;
import kamkeel.npcs.controllers.data.ability.AnchorPoint;
import kamkeel.npcs.controllers.data.ability.data.EnergyAnchorData;
import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.util.Vec3;
import noppes.npcs.constants.EnumAnimationPart;
@@ -18,6 +19,12 @@ import noppes.npcs.entity.data.ModelScalePart;
*/
public class AnchorPointHelper {
// Y offset correction for the local client player.
// EntityPlayerSP.posY is at feet, but the renderer adds yOffset (1.62) visually,
// so anchor positions computed from posY appear ~1.6 blocks too high for the local player.
// Remote players and NPCs don't have this discrepancy.
private static final float CLIENT_PLAYER_Y_OFFSET = -1.6F;
// Default biped model constants (in model units, 1 unit = 1/16 block)
private static final float MODEL_SCALE = 1f / 16f;
@@ -68,7 +75,7 @@ public class AnchorPointHelper {
float anchorZ = anchorData.anchorOffsetZ;
double x = entity.posX;
double y = entity.posY;
double y = entity.posY + getClientPlayerYCorrection(entity);
double z = entity.posZ;
float headYaw = (float) Math.toRadians(entity.rotationYawHead);
@@ -127,6 +134,18 @@ public class AnchorPointHelper {
return 1f;
}
/**
* Returns Y offset correction for the local client player.
* On the client, EntityPlayerSP.posY is at feet level but the renderer adds yOffset (1.62),
* causing anchor positions to appear ~1.6 blocks too high. This matches DBC's correction.
*/
private static double getClientPlayerYCorrection(EntityLivingBase entity) {
if (entity.worldObj.isRemote && entity instanceof EntityPlayerSP) {
return CLIENT_PLAYER_Y_OFFSET;
}
return 0.0;
}
// Default arm scale (no scaling)
private static final ModelScalePart DEFAULT_ARM_SCALE = new ModelScalePart();
@@ -251,7 +270,7 @@ public class AnchorPointHelper {
// Final world position
double worldX = entity.posX + worldOffsetX;
double worldY = entity.posY + shoulderHeight - blockY;
double worldY = entity.posY + getClientPlayerYCorrection(entity) + shoulderHeight - blockY;
double worldZ = entity.posZ + worldOffsetZ;
// Rotate offsets relative to entity's body yaw
@@ -324,7 +343,7 @@ public class AnchorPointHelper {
double armLengthOffset = armScale.scaleY * 0.3 * scale;
double x = entity.posX + lateralX + forwardX;
double y = entity.posY + entity.height * FALLBACK_ARM_HEIGHT + armLengthOffset;
double y = entity.posY + getClientPlayerYCorrection(entity) + entity.height * FALLBACK_ARM_HEIGHT + armLengthOffset;
double z = entity.posZ + lateralZ + forwardZ;
// Rotate offsets relative to entity's body yaw
@@ -254,6 +254,7 @@ public class GuiNPCAbilities extends GuiNPCInterface2 implements IScrollData, IC
} else {
Ability ability = slot.getAbility();
if (ability != null) {
ability.setNpcInlineEdit(true);
setSubGui(ability.createConfigGui(this));
}
}
@@ -303,6 +304,7 @@ public class GuiNPCAbilities extends GuiNPCInterface2 implements IScrollData, IC
Ability abilityToSave = npcSlots.get(selectedAbilityIndex).getAbility();
if (abilityToSave != null) {
if (abilityToSave.getName() == null || abilityToSave.getName().isEmpty()) {
abilityToSave.setNpcInlineEdit(true);
setSubGui(abilityToSave.createConfigGui(this));
} else {
setSubGui(new SubGuiAbilitySaveConfirm(abilityToSave, null, existingPresetNames));
@@ -632,6 +634,7 @@ public class GuiNPCAbilities extends GuiNPCInterface2 implements IScrollData, IC
setSubGui(new SubGuiAbilityEditMode());
} else {
if (ability != null) {
ability.setNpcInlineEdit(true);
setSubGui(ability.createConfigGui(this));
}
}
@@ -690,6 +693,7 @@ public class GuiNPCAbilities extends GuiNPCInterface2 implements IScrollData, IC
Ability ability = slot.getAbility();
if (ability != null) {
ability.setId(UUID.randomUUID().toString());
ability.setNpcInlineEdit(true);
setSubGui(ability.createConfigGui(this));
}
updateNpcAbilitiesList();