mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-12 20:07:32 +00:00
Merge branch 'dev' into syntax-highlighter-unified
This commit is contained in:
commit
c5cd2d7ce1
9 files changed
+160
-41
No files matched your search
+1
-1
@@ -6,7 +6,7 @@ plugins {
|
||||
id "dts.typescript-generator"
|
||||
}
|
||||
|
||||
version = "1.11-beta13"
|
||||
version = "1.11-beta13.1"
|
||||
group= "kamkeel.customnpc-plus"
|
||||
archivesBaseName = "CustomNPC-Plus"
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.AxisAlignedBB;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.world.World;
|
||||
import noppes.npcs.CustomNpcs;
|
||||
@@ -672,8 +673,11 @@ public abstract class EntityEnergyBarrier extends EntityEnergyAbility {
|
||||
/**
|
||||
* Teleport an entity to a position. Uses network handler for players
|
||||
* to ensure reliable client synchronization.
|
||||
* Skips teleport if destination intersects solid blocks.
|
||||
*/
|
||||
protected void teleportEntity(EntityLivingBase ent, double x, double y, double z) {
|
||||
if (!isPositionClear(ent, x, y, z)) return;
|
||||
|
||||
if (ent instanceof EntityPlayerMP) {
|
||||
EntityPlayerMP player = (EntityPlayerMP) ent;
|
||||
player.playerNetServerHandler.setPlayerLocation(x, y, z, player.rotationYaw, player.rotationPitch);
|
||||
@@ -682,6 +686,35 @@ public abstract class EntityEnergyBarrier extends EntityEnergyAbility {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if placing the entity at (x, y, z) would not intersect solid blocks.
|
||||
*/
|
||||
protected boolean isPositionClear(EntityLivingBase ent, double x, double y, double z) {
|
||||
double halfW = ent.width * 0.5;
|
||||
AxisAlignedBB box = AxisAlignedBB.getBoundingBox(
|
||||
x - halfW, y, z - halfW,
|
||||
x + halfW, y + ent.height, z + halfW
|
||||
);
|
||||
return worldObj.getCollidingBoundingBoxes(ent, box).isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the entity can be pushed in the given direction without hitting solid blocks.
|
||||
* Probes a small distance (0.3 blocks) ahead in the push direction.
|
||||
*/
|
||||
protected boolean canPushInDirection(EntityLivingBase ent, double dirX, double dirY, double dirZ) {
|
||||
double probe = 0.3;
|
||||
double halfW = ent.width * 0.5;
|
||||
double px = ent.posX + dirX * probe;
|
||||
double py = ent.posY + dirY * probe;
|
||||
double pz = ent.posZ + dirZ * probe;
|
||||
AxisAlignedBB box = AxisAlignedBB.getBoundingBox(
|
||||
px - halfW, py, pz - halfW,
|
||||
px + halfW, py + ent.height, pz + halfW
|
||||
);
|
||||
return worldObj.getCollidingBoundingBoxes(ent, box).isEmpty();
|
||||
}
|
||||
|
||||
// ==================== GETTERS ====================
|
||||
|
||||
public EnergyBarrierData getBarrierData() {
|
||||
|
||||
@@ -371,8 +371,10 @@ public class EntityEnergyDome extends EntityEnergyBarrier {
|
||||
ent.motionY -= radialVel * ny;
|
||||
ent.motionZ -= radialVel * nz;
|
||||
double pushForce = wasInside ? -0.15 : 0.15;
|
||||
ent.motionX += nx * pushForce;
|
||||
ent.motionZ += nz * pushForce;
|
||||
if (canPushInDirection(ent, nx * pushForce, 0, nz * pushForce)) {
|
||||
ent.motionX += nx * pushForce;
|
||||
ent.motionZ += nz * pushForce;
|
||||
}
|
||||
ent.velocityChanged = true;
|
||||
} else {
|
||||
// Preemptive: near surface and moving toward it — cancel radial velocity
|
||||
@@ -385,8 +387,10 @@ public class EntityEnergyDome extends EntityEnergyBarrier {
|
||||
ent.motionY -= radialVel * ny;
|
||||
ent.motionZ -= radialVel * nz;
|
||||
double pushForce = isInside ? -0.05 : 0.05;
|
||||
ent.motionX += nx * pushForce;
|
||||
ent.motionZ += nz * pushForce;
|
||||
if (canPushInDirection(ent, nx * pushForce, 0, nz * pushForce)) {
|
||||
ent.motionX += nx * pushForce;
|
||||
ent.motionZ += nz * pushForce;
|
||||
}
|
||||
ent.velocityChanged = true;
|
||||
}
|
||||
}
|
||||
@@ -400,16 +404,21 @@ public class EntityEnergyDome extends EntityEnergyBarrier {
|
||||
double proximity = 1.0 - (surfaceDist / knockbackMargin);
|
||||
double force = proximity * strength * 0.06;
|
||||
|
||||
double pushX, pushY, pushZ;
|
||||
if (isInside) {
|
||||
// Push toward center (away from surface)
|
||||
ent.motionX += -nx * force;
|
||||
ent.motionY += -ny * force * 0.5;
|
||||
ent.motionZ += -nz * force;
|
||||
pushX = -nx * force;
|
||||
pushY = -ny * force * 0.5;
|
||||
pushZ = -nz * force;
|
||||
} else {
|
||||
// Push outward (away from surface)
|
||||
ent.motionX += nx * force;
|
||||
ent.motionY += ny * force * 0.5;
|
||||
ent.motionZ += nz * force;
|
||||
pushX = nx * force;
|
||||
pushY = ny * force * 0.5;
|
||||
pushZ = nz * force;
|
||||
}
|
||||
|
||||
if (canPushInDirection(ent, pushX, pushY, pushZ)) {
|
||||
ent.motionX += pushX;
|
||||
ent.motionY += pushY;
|
||||
ent.motionZ += pushZ;
|
||||
}
|
||||
ent.velocityChanged = true;
|
||||
}
|
||||
|
||||
@@ -257,12 +257,12 @@ public class EntityEnergyPanel extends EntityEnergyBarrier {
|
||||
double dz = target.posZ - posZ;
|
||||
double len = Math.sqrt(dx * dx + dz * dz);
|
||||
if (len > 0) {
|
||||
target.addVelocity(
|
||||
(dx / len) * panelData.launchKnockback * 0.5,
|
||||
0.1,
|
||||
(dz / len) * panelData.launchKnockback * 0.5
|
||||
);
|
||||
target.velocityChanged = true;
|
||||
double kbX = (dx / len) * panelData.launchKnockback * 0.5;
|
||||
double kbZ = (dz / len) * panelData.launchKnockback * 0.5;
|
||||
if (canPushInDirection(target, kbX, 0.1, kbZ)) {
|
||||
target.addVelocity(kbX, 0.1, kbZ);
|
||||
target.velocityChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -580,8 +580,12 @@ public class EntityEnergyPanel extends EntityEnergyBarrier {
|
||||
// Cancel normal velocity and add corrective push
|
||||
ent.motionX -= normalVel * normalX;
|
||||
ent.motionZ -= normalVel * normalZ;
|
||||
ent.motionX += normalX * prevSide * 0.15;
|
||||
ent.motionZ += normalZ * prevSide * 0.15;
|
||||
double pushX = normalX * prevSide * 0.15;
|
||||
double pushZ = normalZ * prevSide * 0.15;
|
||||
if (canPushInDirection(ent, pushX, 0, pushZ)) {
|
||||
ent.motionX += pushX;
|
||||
ent.motionZ += pushZ;
|
||||
}
|
||||
ent.velocityChanged = true;
|
||||
} else {
|
||||
// Preemptive: near the plane and moving toward it — cancel normal velocity
|
||||
@@ -592,8 +596,12 @@ public class EntityEnergyPanel extends EntityEnergyBarrier {
|
||||
if (movingToward) {
|
||||
ent.motionX -= normalVel * normalX;
|
||||
ent.motionZ -= normalVel * normalZ;
|
||||
ent.motionX += normalX * side * 0.05;
|
||||
ent.motionZ += normalZ * side * 0.05;
|
||||
double pushX = normalX * side * 0.05;
|
||||
double pushZ = normalZ * side * 0.05;
|
||||
if (canPushInDirection(ent, pushX, 0, pushZ)) {
|
||||
ent.motionX += pushX;
|
||||
ent.motionZ += pushZ;
|
||||
}
|
||||
ent.velocityChanged = true;
|
||||
}
|
||||
}
|
||||
@@ -606,8 +614,12 @@ public class EntityEnergyPanel extends EntityEnergyBarrier {
|
||||
if (absDist < searchExtension) {
|
||||
double proximity = 1.0 - (absDist / searchExtension);
|
||||
double force = proximity * strength * 0.06;
|
||||
ent.motionX += normalX * side * force;
|
||||
ent.motionZ += normalZ * side * force;
|
||||
double pushX = normalX * side * force;
|
||||
double pushZ = normalZ * side * force;
|
||||
if (canPushInDirection(ent, pushX, 0, pushZ)) {
|
||||
ent.motionX += pushX;
|
||||
ent.motionZ += pushZ;
|
||||
}
|
||||
ent.velocityChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ public final class EffectScriptPacket extends AbstractPacket {
|
||||
if (!data.saveScript(in, player))
|
||||
return;
|
||||
if (ConfigDebug.PlayerLogging && FMLCommonHandler.instance().getEffectiveSide() == Side.SERVER) {
|
||||
LogWriter.script(String.format("[%s] (Player) %s SAVED EFFECT %s [%s]", "EFFECT SCRIPTS", player.getCommandSenderName(), effect.getName()));
|
||||
LogWriter.script(String.format("[%s] (Player) %s SAVED EFFECT %s [%s]", "EFFECT SCRIPTS", player.getCommandSenderName(), effect.getName(), effect.id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Mod(modid = "customnpcs", name = "CustomNPC+", version = "1.11-beta13")
|
||||
@Mod(modid = "customnpcs", name = "CustomNPC+", version = "1.11-beta13.1")
|
||||
public class CustomNpcs {
|
||||
|
||||
@SidedProxy(clientSide = "noppes.npcs.client.ClientProxy", serverSide = "noppes.npcs.CommonProxy")
|
||||
|
||||
@@ -14,6 +14,7 @@ import noppes.npcs.scripted.NpcAPI;
|
||||
import javax.script.Bindings;
|
||||
import javax.script.Compilable;
|
||||
import javax.script.CompiledScript;
|
||||
import javax.script.Invocable;
|
||||
import javax.script.ScriptContext;
|
||||
import javax.script.ScriptEngine;
|
||||
import java.io.PrintWriter;
|
||||
@@ -272,20 +273,18 @@ public class ScriptContainer implements IScriptUnit {
|
||||
unknownFunctions.add(type);
|
||||
}
|
||||
} else {
|
||||
if (!this.cachedFunctions.containsKey(type)) {
|
||||
ScriptObjectMirror func;
|
||||
if (instanceBindings != null) {
|
||||
if (instanceBindings != null) {
|
||||
if (!this.cachedFunctions.containsKey(type)) {
|
||||
Object val = instanceBindings.get(type);
|
||||
func = val instanceof ScriptObjectMirror ? (ScriptObjectMirror) val : null;
|
||||
} else {
|
||||
ScriptObjectMirror global = (ScriptObjectMirror) engine.getBindings(ScriptContext.ENGINE_SCOPE);
|
||||
func = (ScriptObjectMirror) global.get(type);
|
||||
ScriptObjectMirror func = val instanceof ScriptObjectMirror ? (ScriptObjectMirror) val : null;
|
||||
this.cachedFunctions.put(type, func);
|
||||
}
|
||||
this.cachedFunctions.put(type, func);
|
||||
}
|
||||
ScriptObjectMirror func = this.cachedFunctions.get(type);
|
||||
if (func != null) {
|
||||
func.call(null, event);
|
||||
ScriptObjectMirror func = this.cachedFunctions.get(type);
|
||||
if (func != null) {
|
||||
func.call(null, event);
|
||||
}
|
||||
} else {
|
||||
((Invocable) engine).invokeFunction(type, event);
|
||||
}
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
|
||||
@@ -8,6 +8,8 @@ import kamkeel.npcs.controllers.data.ability.data.ChainedAbility;
|
||||
import kamkeel.npcs.controllers.data.ability.data.IAbilityAction;
|
||||
import kamkeel.npcs.controllers.data.ability.data.entry.AbilityToggleEntry;
|
||||
import kamkeel.npcs.controllers.data.telegraph.TelegraphInstance;
|
||||
import kamkeel.npcs.network.PacketHandler;
|
||||
import kamkeel.npcs.network.packets.data.UpdateAnimationsPacket;
|
||||
import kamkeel.npcs.network.packets.data.ability.AbilityCooldownSyncPacket;
|
||||
import kamkeel.npcs.network.packets.data.ability.PlayerAbilityStatePacket;
|
||||
import kamkeel.npcs.network.packets.data.ability.PlayerAbilitySyncPacket;
|
||||
@@ -177,6 +179,22 @@ public class PlayerAbilityData extends AbstractDataAbilities implements IPlayerA
|
||||
protected void clearAnimationData() {
|
||||
playerData.animationData.setAnimation(null);
|
||||
playerData.animationData.updateClient();
|
||||
|
||||
// Directly send animation clear to the owning player. updateClient() uses
|
||||
// getEntitiesWithinAABB to find recipients, but during teleport the player
|
||||
// entity hasn't migrated to the new chunk yet — so the AABB search centered
|
||||
// on the new position won't find them, and the clear packet is never delivered.
|
||||
// This guarantees the player's client always receives the clear.
|
||||
EntityPlayer player = playerData.player;
|
||||
if (player instanceof EntityPlayerMP) {
|
||||
NBTTagCompound data = new NBTTagCompound();
|
||||
data.setBoolean("AllowAnimation", playerData.animationData.enabled());
|
||||
PacketHandler.Instance.sendToPlayer(
|
||||
new UpdateAnimationsPacket(data, player.getCommandSenderName()),
|
||||
(EntityPlayerMP) player
|
||||
);
|
||||
}
|
||||
|
||||
playingAbilityAnimation = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -85,6 +85,7 @@ import noppes.npcs.DataInventory;
|
||||
import noppes.npcs.DataStats;
|
||||
import noppes.npcs.EventHooks;
|
||||
import noppes.npcs.IChatMessages;
|
||||
import noppes.npcs.LogWriter;
|
||||
import noppes.npcs.NBTTags;
|
||||
import noppes.npcs.NoppesUtilPlayer;
|
||||
import noppes.npcs.NoppesUtilServer;
|
||||
@@ -482,8 +483,42 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti
|
||||
abilities.tick();
|
||||
}
|
||||
|
||||
// DEBUG: Log client-side health state every tick to diagnose DI green health spam
|
||||
if (worldObj.isRemote && ticksExisted % 20 == 0) {
|
||||
float h = getHealth();
|
||||
float mh = (float) getMaxHealth();
|
||||
int flags = dataWatcher.getWatchableObjectInt(15);
|
||||
boolean killed = isKilled();
|
||||
LogWriter.info("[CLIENT-HEALTH-DEBUG] NPC=" + display.name
|
||||
+ " id=" + getEntityId()
|
||||
+ " health=" + h
|
||||
+ " maxHealth=" + mh
|
||||
+ " flags=" + Integer.toBinaryString(flags)
|
||||
+ " isKilled=" + killed
|
||||
+ " wasKilled=" + wasKilled
|
||||
+ " isDead=" + isDead
|
||||
+ " deathTime=" + deathTime
|
||||
+ " statsMaxHP=" + stats.maxHealth
|
||||
+ " tick=" + ticksExisted);
|
||||
}
|
||||
|
||||
if (wasKilled != isKilled() && wasKilled) {
|
||||
reset();
|
||||
if (!worldObj.isRemote) {
|
||||
reset();
|
||||
} else {
|
||||
// Client: only reset visual state. Health/flags are synced via DataWatcher.
|
||||
// Full reset() on client can cause health oscillation when maxHealth is desynced.
|
||||
LogWriter.info("[CLIENT-RESET-DEBUG] NPC=" + display.name
|
||||
+ " id=" + getEntityId()
|
||||
+ " health=" + getHealth()
|
||||
+ " maxHealth=" + (float) getMaxHealth()
|
||||
+ " wasKilled=" + wasKilled
|
||||
+ " isKilled=" + isKilled()
|
||||
+ " tick=" + ticksExisted);
|
||||
deathTime = 0;
|
||||
currentAnimation = EnumAnimation.NONE;
|
||||
updateHitbox();
|
||||
}
|
||||
}
|
||||
|
||||
wasKilled = isKilled();
|
||||
@@ -1881,6 +1916,10 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti
|
||||
readSpawnData(ByteBufUtils.readNBT(buf));
|
||||
} catch (IOException e) {
|
||||
if (this.worldObj != null && this.worldObj.isRemote) {
|
||||
LogWriter.info("[SPAWN-DATA-DEBUG] ByteBuf readSpawnData FAILED for id=" + getEntityId()
|
||||
+ " health=" + getHealth()
|
||||
+ " maxHealth=" + (float) getMaxHealth()
|
||||
+ " statsMaxHP=" + stats.maxHealth);
|
||||
RequestProperSpawnData.reportMissingData(this);
|
||||
}
|
||||
}
|
||||
@@ -1888,8 +1927,17 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti
|
||||
|
||||
public void readSpawnData(NBTTagCompound compound) {
|
||||
if (this.worldObj != null && this.worldObj.isRemote) {
|
||||
if(requestClientFix(compound))
|
||||
if(requestClientFix(compound)) {
|
||||
LogWriter.info("[SPAWN-DATA-DEBUG] requestClientFix returned true (bad compound) for id=" + getEntityId()
|
||||
+ " health=" + getHealth()
|
||||
+ " maxHealth=" + (float) getMaxHealth()
|
||||
+ " statsMaxHP=" + stats.maxHealth);
|
||||
return;
|
||||
}
|
||||
LogWriter.info("[SPAWN-DATA-DEBUG] readSpawnData SUCCESS for id=" + getEntityId()
|
||||
+ " health=" + getHealth()
|
||||
+ " oldMaxHP=" + (float) getMaxHealth()
|
||||
+ " newMaxHP=" + compound.getDouble("MaxHealth"));
|
||||
}
|
||||
|
||||
stats.maxHealth = compound.getDouble("MaxHealth");
|
||||
|
||||
Reference in new issue
Block a user