mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-12 20:07:32 +00:00
Add Free Aim Functionality for Beams
This commit is contained in:
1 parent
de2fcea154
commit
1a0ce41e00
4 files changed
+109
-3
No files matched your search
@@ -33,6 +33,7 @@ public class AbilityBeam extends AbilityEnergyProjectile<EntityAbilityBeam> impl
|
||||
|
||||
private float beamWidth = 0.4f;
|
||||
private float headSize = 0.6f;
|
||||
private boolean freeAim = false;
|
||||
|
||||
public AbilityBeam() {
|
||||
super(
|
||||
@@ -67,12 +68,14 @@ public class AbilityBeam extends AbilityEnergyProjectile<EntityAbilityBeam> impl
|
||||
@Override
|
||||
protected EntityAbilityBeam createEntity(EntityLivingBase caster, EntityLivingBase target,
|
||||
Vec3 spawnPos, EnergyDisplayData resolved, int index) {
|
||||
return new EntityAbilityBeam(
|
||||
EntityAbilityBeam beam = new EntityAbilityBeam(
|
||||
caster.worldObj, caster, target,
|
||||
spawnPos.xCoord, spawnPos.yCoord, spawnPos.zCoord,
|
||||
beamWidth, headSize,
|
||||
resolved, combatData, homingData, lightningData, lifespanData,
|
||||
lockMovement.locksActive());
|
||||
beam.setFreeAim(freeAim);
|
||||
return beam;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -100,6 +103,18 @@ public class AbilityBeam extends AbilityEnergyProjectile<EntityAbilityBeam> impl
|
||||
return new EntityAbilityBeam[size];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void detach() {
|
||||
if (entities != null) {
|
||||
for (EntityAbilityBeam beam : entities) {
|
||||
if (beam != null && !beam.isDead) {
|
||||
beam.setAttachedToOwner(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
super.detach();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getProjectileTelegraphRadius() {
|
||||
return headSize * 2.0f;
|
||||
@@ -139,12 +154,14 @@ public class AbilityBeam extends AbilityEnergyProjectile<EntityAbilityBeam> impl
|
||||
protected void writeTypeSpecificNBT(NBTTagCompound nbt) {
|
||||
nbt.setFloat("beamWidth", beamWidth);
|
||||
nbt.setFloat("headSize", headSize);
|
||||
nbt.setBoolean("freeAim", freeAim);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void readTypeSpecificNBT(NBTTagCompound nbt) {
|
||||
this.beamWidth = nbt.getFloat("beamWidth");
|
||||
this.headSize = nbt.getFloat("headSize");
|
||||
this.freeAim = nbt.getBoolean("freeAim");
|
||||
}
|
||||
|
||||
// ==================== TYPE-SPECIFIC GETTERS ====================
|
||||
@@ -173,6 +190,14 @@ public class AbilityBeam extends AbilityEnergyProjectile<EntityAbilityBeam> impl
|
||||
this.headSize = headSize;
|
||||
}
|
||||
|
||||
public boolean isFreeAim() {
|
||||
return freeAim;
|
||||
}
|
||||
|
||||
public void setFreeAim(boolean freeAim) {
|
||||
this.freeAim = freeAim;
|
||||
}
|
||||
|
||||
// ==================== TYPE-SPECIFIC GUI ====================
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@@ -189,6 +214,9 @@ public class AbilityBeam extends AbilityEnergyProjectile<EntityAbilityBeam> impl
|
||||
));
|
||||
defs.add(FieldDef.floatField("ability.knockback", this::getKnockback, this::setKnockback));
|
||||
defs.add(FieldDef.section("ability.section.beam"));
|
||||
defs.add(FieldDef.boolField("ability.freeAim", this::isFreeAim, this::setFreeAim)
|
||||
.hover("ability.hover.freeAim")
|
||||
.enabledWhen(() -> lockMovement.locksActive() && rotationMode == RotationMode.FREE && !freeOnCast));
|
||||
defs.add(FieldDef.row(
|
||||
FieldDef.floatField("ability.beamWidth", this::getBeamWidth, this::setBeamWidth).range(0.1f, 100.0f),
|
||||
FieldDef.floatField("ability.headSize", this::getHeadSize, this::setHeadSize).range(0.1f, 100.0f)
|
||||
|
||||
@@ -213,7 +213,10 @@ public class AbilityLaser extends AbilityEnergyProjectile<EntityAbilityLaser> im
|
||||
FieldDef.floatField("ability.laserWidth", this::getLaserWidth, this::setLaserWidth).range(0.1f, 100.0f),
|
||||
FieldDef.floatField("ability.expansionSpeed", this::getExpansionSpeed, this::setExpansionSpeed).range(0.1f, 50.0f)
|
||||
));
|
||||
defs.add(FieldDef.floatField("ability.maxLength", this::getMaxLength, this::setMaxLength).range(1.0f, 500.0f));
|
||||
defs.add(FieldDef.row(
|
||||
FieldDef.floatField("ability.maxLength", this::getMaxLength, this::setMaxLength).range(1.0f, 500.0f),
|
||||
FieldDef.intField("ability.lifetime", this::getMaxLifetime, this::setMaxLifetime).range(1, 1200)
|
||||
));
|
||||
defs.add(FieldDef.section("ability.section.explosive"));
|
||||
defs.add(FieldDef.boolField("gui.enabled", this::isExplosive, this::setExplosive).hover("ability.hover.explosive"));
|
||||
defs.add(FieldDef.floatField("gui.radius", this::getExplosionRadius, this::setExplosionRadius)
|
||||
|
||||
@@ -11,6 +11,7 @@ import kamkeel.npcs.controllers.data.ability.data.energy.EnergyLightningData;
|
||||
import kamkeel.npcs.util.AnchorPointHelper;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
import net.minecraft.util.AxisAlignedBB;
|
||||
@@ -55,6 +56,10 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
// Beam behavior mode (single source of truth).
|
||||
private BeamMode beamMode = BeamMode.ANCHORED;
|
||||
|
||||
// Free Aim (players only) - steers beam toward player's look direction
|
||||
private boolean freeAim = false;
|
||||
private static final float FREE_AIM_STRENGTH = 0.15f;
|
||||
|
||||
// Charging state (beam-specific)
|
||||
private float chargeOffsetDistance = 1.0f;
|
||||
private int trailFadeTime = 20; // Ticks for trail to fully fade
|
||||
@@ -433,8 +438,64 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
addTrailPoint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Steer beam toward player's look direction (Free Aim mode).
|
||||
* Target point is projected along look vector at the current head distance from origin.
|
||||
*/
|
||||
private void updateFreeAim() {
|
||||
Entity owner = getOwnerEntity();
|
||||
if (!(owner instanceof EntityPlayer)) return;
|
||||
|
||||
Vec3 look = getOwnerLookVector();
|
||||
if (look == null) return;
|
||||
|
||||
double headDist = Math.sqrt(headOffsetX * headOffsetX + headOffsetY * headOffsetY + headOffsetZ * headOffsetZ);
|
||||
if (headDist < 0.1) return;
|
||||
|
||||
// Target point along look vector at current head distance
|
||||
double targetX = startX + look.xCoord * headDist;
|
||||
double targetY = startY + look.yCoord * headDist;
|
||||
double targetZ = startZ + look.zCoord * headDist;
|
||||
|
||||
// Current head world position
|
||||
double headWorldX = startX + headOffsetX;
|
||||
double headWorldY = startY + headOffsetY;
|
||||
double headWorldZ = startZ + headOffsetZ;
|
||||
|
||||
double dx = targetX - headWorldX;
|
||||
double dy = targetY - headWorldY;
|
||||
double dz = targetZ - headWorldZ;
|
||||
double dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
||||
|
||||
if (dist < 0.01) return;
|
||||
|
||||
double desiredVX = (dx / dist) * getSpeed();
|
||||
double desiredVY = (dy / dist) * getSpeed();
|
||||
double desiredVZ = (dz / dist) * getSpeed();
|
||||
|
||||
motionX += (desiredVX - motionX) * FREE_AIM_STRENGTH;
|
||||
motionY += (desiredVY - motionY) * FREE_AIM_STRENGTH;
|
||||
motionZ += (desiredVZ - motionZ) * FREE_AIM_STRENGTH;
|
||||
|
||||
double vLen = Math.sqrt(motionX * motionX + motionY * motionY + motionZ * motionZ);
|
||||
if (vLen > 0) {
|
||||
motionX = (motionX / vLen) * getSpeed();
|
||||
motionY = (motionY / vLen) * getSpeed();
|
||||
motionZ = (motionZ / vLen) * getSpeed();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateHoming() {
|
||||
if (freeAim) {
|
||||
Entity owner = getOwnerEntity();
|
||||
if (owner instanceof EntityPlayer) {
|
||||
updateFreeAim();
|
||||
return;
|
||||
}
|
||||
// NPCs fall through to normal homing
|
||||
}
|
||||
|
||||
if (!isHoming()) return;
|
||||
|
||||
Entity target = getTargetEntity();
|
||||
@@ -545,6 +606,14 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
setBeamMode(modeFromAnchored(attached));
|
||||
}
|
||||
|
||||
public boolean isFreeAim() {
|
||||
return freeAim;
|
||||
}
|
||||
|
||||
public void setFreeAim(boolean freeAim) {
|
||||
this.freeAim = freeAim;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get interpolated head offset for smooth rendering.
|
||||
* Returns offset RELATIVE to origin.
|
||||
@@ -643,8 +712,9 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
resetHeadOffsets();
|
||||
resetTrailStorage();
|
||||
|
||||
// Disable homing — reflected beams fly straight.
|
||||
// Disable homing and free aim — reflected beams fly straight.
|
||||
homingData.setHoming(false);
|
||||
freeAim = false;
|
||||
setTargetEntityId(-1);
|
||||
}
|
||||
return reflected;
|
||||
@@ -668,6 +738,7 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
resetHeadOffsets();
|
||||
resetTrailStorage();
|
||||
homingData.setHoming(false);
|
||||
freeAim = false;
|
||||
setTargetEntityId(-1);
|
||||
}
|
||||
|
||||
@@ -686,6 +757,7 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
readChargingNBT(nbt);
|
||||
this.chargeOffsetDistance = nbt.hasKey("ChargeOffsetDistance") ? nbt.getFloat("ChargeOffsetDistance") : 1.0f;
|
||||
this.trailFadeTime = nbt.hasKey("TrailFadeTime") ? nbt.getInteger("TrailFadeTime") : 20;
|
||||
this.freeAim = nbt.getBoolean("FreeAim");
|
||||
|
||||
boolean attachedLegacy = !nbt.hasKey("AttachedToOwner") || nbt.getBoolean("AttachedToOwner");
|
||||
BeamMode loadedMode = modeFromAnchored(attachedLegacy);
|
||||
@@ -737,6 +809,7 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
|
||||
nbt.setString("BeamMode", beamMode.name());
|
||||
writeChargingNBT(nbt);
|
||||
nbt.setFloat("ChargeOffsetDistance", chargeOffsetDistance);
|
||||
nbt.setBoolean("FreeAim", freeAim);
|
||||
nbt.setBoolean("FadeTrail", isFadingMode());
|
||||
nbt.setInteger("TrailFadeTime", trailFadeTime);
|
||||
|
||||
|
||||
Reference in new issue
Block a user