Merge remote-tracking branch 'origin/syntax-highlighter-unified' into syntax-highlighter-unified

This commit is contained in:
bigguy345 committed 2026-03-11 02:58:59 +02:00
commit 81d5cc25a6
59 files changed
+905 -211

No files matched your search

@@ -401,6 +401,24 @@ public class AbilityController implements IAbilityHandler {
}
}
public Ability cloneCustomAbility(String originalName) {
Ability original = customAbilities.get(originalName);
if (original == null || original.isBuiltIn()) return null;
NBTTagCompound nbt = original.writeNBT(true);
nbt.setString("id", UUID.randomUUID().toString());
Ability clone = fromNBT(nbt);
if (clone == null) return null;
String name = clone.getName();
while (customAbilities.containsKey(name)) name += "_";
clone.setName(name);
saveCustomAbility(clone);
return clone;
}
public boolean deleteCustomAbility(String name) {
if (name == null || name.isEmpty()) return false;
@@ -748,6 +766,24 @@ public class AbilityController implements IAbilityHandler {
}
}
public ChainedAbility cloneChainedAbility(String originalName) {
ChainedAbility original = chainedAbilities.get(originalName);
if (original == null) return null;
NBTTagCompound nbt = original.writeNBT(true);
nbt.setString("Id", UUID.randomUUID().toString());
ChainedAbility clone = new ChainedAbility();
clone.readNBT(nbt);
String name = clone.getName();
while (chainedAbilities.containsKey(name)) name += "_";
clone.setName(name);
saveChainedAbility(clone);
return clone;
}
public boolean deleteChainedAbility(String name) {
if (name == null || name.isEmpty()) return false;
@@ -1,12 +1,12 @@
package kamkeel.npcs.controllers;
import kamkeel.npcs.controllers.data.energy.IEnergyHandler;
import kamkeel.npcs.controllers.data.energy.IEnergyExtender;
import kamkeel.npcs.entity.*;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.World;
import noppes.npcs.api.IEnergyController;
import noppes.npcs.api.IEnergyHandler;
import noppes.npcs.api.IWorld;
import noppes.npcs.api.entity.*;
import noppes.npcs.api.entity.IEnergyExplosion;
@@ -22,22 +22,22 @@ import java.util.List;
* damage through registered handlers when entities have customDamageData
* but no sourceAbility.
*/
public class EnergyController implements IEnergyController {
public class EnergyController implements IEnergyHandler {
public static EnergyController Instance = new EnergyController();
private final List<IEnergyHandler> handlers = new ArrayList<>();
private final List<IEnergyExtender> extenders = new ArrayList<>();
// ═══════════════════════════════════════════════════════════════════
// HANDLER REGISTRATION
// ═══════════════════════════════════════════════════════════════════
public void registerHandler(IEnergyHandler handler) {
handlers.add(handler);
public void registerExtender(IEnergyExtender handler) {
extenders.add(handler);
}
public List<IEnergyHandler> getHandlers() {
return handlers;
public List<IEnergyExtender> getExtenders() {
return extenders;
}
// ═══════════════════════════════════════════════════════════════════
@@ -53,7 +53,7 @@ public class EnergyController implements IEnergyController {
double kbDirX, double kbDirZ,
float damageMultiplier,
NBTTagCompound damageData) {
for (IEnergyHandler handler : handlers) {
for (IEnergyExtender handler : extenders) {
if (handler.onEnergyDamage(energyEntity, owner, target, damage,
knockback, knockbackUp, kbDirX, kbDirZ, damageMultiplier, damageData)) {
return true;
@@ -68,7 +68,7 @@ public class EnergyController implements IEnergyController {
public float fireModifyEnergyDamage(Entity energyEntity, EntityLivingBase owner,
float baseDamage, NBTTagCompound damageData) {
float damage = baseDamage;
for (IEnergyHandler handler : handlers) {
for (IEnergyExtender handler : extenders) {
damage = handler.modifyEnergyDamage(energyEntity, owner, damage, damageData);
}
return damage;
@@ -150,7 +150,7 @@ public class EnergyController implements IEnergyController {
}
// ═══════════════════════════════════════════════════════════════════
// IEnergyController (Script API methods)
// IEnergyHandler (Script API methods)
// ═══════════════════════════════════════════════════════════════════
@Override
@@ -184,6 +184,14 @@ public class SyncController {
DBCAddon.instance.syncPlayer(player);
syncPlayerData(player, false);
PartyInfoPacket.sendPartyData(player);
// Sync skin overlays after full handshake to ensure the client is ready.
// Overlays are not part of getSyncNBTFull(), and packets sent during
// PlayerLoggedInEvent can arrive before the client entity exists.
PlayerData data = PlayerData.get(player);
if (data != null) {
data.skinOverlays.updateClient();
}
}
private static EnumMap<EnumSyncType, Integer> getServerRevisionSnapshot() {
@@ -308,6 +308,11 @@ public class TelegraphController implements ITelegraphHandler {
return new ScriptTelegraph(Telegraph.cone(length, angle));
}
@Override
public ITelegraph createSquare(float radius) {
return new ScriptTelegraph(Telegraph.square(radius));
}
@Override
public ITelegraph createPoint() {
return new ScriptTelegraph(Telegraph.point());
@@ -332,6 +337,9 @@ public class TelegraphController implements ITelegraphHandler {
case CONE:
telegraph = Telegraph.cone(5.0f, 45.0f);
break;
case SQUARE:
telegraph = Telegraph.square(3.0f);
break;
case POINT:
telegraph = Telegraph.point();
break;
@@ -5,6 +5,8 @@ import kamkeel.npcs.controllers.data.ability.data.ChainedAbility;
import kamkeel.npcs.controllers.data.ability.data.IAbilityAction;
import net.minecraft.nbt.NBTTagCompound;
import java.util.UUID;
/**
* Unified slot for storing any combat action: inline ability, ability reference, or chain reference.
* Support for all {@link IAbilityAction} types.
@@ -218,7 +220,9 @@ public class AbilityAction {
Ability resolved = getAbility();
if (resolved == null) return false;
inlineAbility = AbilityController.Instance.fromNBT(resolved.writeNBT(true));
NBTTagCompound nbt = resolved.writeNBT(true);
nbt.setString("id", UUID.randomUUID().toString());
inlineAbility = AbilityController.Instance.fromNBT(nbt);
slotType = SlotType.INLINE_ABILITY;
referenceId = null;
cachedAction = null;
@@ -230,7 +234,10 @@ public class AbilityAction {
ChainedAbility resolved = getChainedAbility();
if (resolved == null) return false;
inlineChain = resolved.deepCopy();
inlineChain = new ChainedAbility();
NBTTagCompound chainNbt = resolved.writeNBT(true);
chainNbt.setString("Id", UUID.randomUUID().toString());
inlineChain.readNBT(chainNbt);
slotType = SlotType.INLINE_CHAIN;
referenceId = null;
cachedAction = null;
@@ -32,6 +32,11 @@ public class EnergyBarrierData {
// Absorbing: caster's damage is redirected to the barrier
public boolean absorbing = false;
// Absorb radius: max distance from barrier's current position for absorption to work
// -1 = no radius limit (absorb regardless of distance)
// 0 = use barrier's own extent as the radius (dome radius, panel half-size, etc.)
// positive = owner must be within this many blocks of the barrier entity
public float absorbRadius = 0.0f;
// Melee: barrier can be hit by melee attacks
public boolean meleeEnabled = false;
@@ -148,6 +153,14 @@ public class EnergyBarrierData {
this.targetOwner = targetOwner;
}
public float getAbsorbRadius() {
return absorbRadius;
}
public void setAbsorbRadius(float absorbRadius) {
this.absorbRadius = absorbRadius;
}
// ==================== NBT ====================
public void writeNBT(NBTTagCompound nbt) {
@@ -160,6 +173,7 @@ public class EnergyBarrierData {
nbt.setBoolean("barrierKnockback", knockbackEnabled);
nbt.setFloat("barrierKnockbackStr", knockbackStrength);
nbt.setBoolean("barrierAbsorbing", absorbing);
nbt.setFloat("barrierAbsorbRadius", absorbRadius);
nbt.setBoolean("barrierMelee", meleeEnabled);
nbt.setFloat("barrierMeleeMult", meleeDamageMultiplier);
nbt.setBoolean("barrierReflect", reflect);
@@ -183,6 +197,7 @@ public class EnergyBarrierData {
knockbackEnabled = nbt.hasKey("barrierKnockback") && nbt.getBoolean("barrierKnockback");
knockbackStrength = nbt.hasKey("barrierKnockbackStr") ? nbt.getFloat("barrierKnockbackStr") : 1.0f;
absorbing = nbt.hasKey("barrierAbsorbing") && nbt.getBoolean("barrierAbsorbing");
absorbRadius = nbt.hasKey("barrierAbsorbRadius") ? nbt.getFloat("barrierAbsorbRadius") : 0.0f;
meleeEnabled = nbt.hasKey("barrierMelee") && nbt.getBoolean("barrierMelee");
meleeDamageMultiplier = nbt.hasKey("barrierMeleeMult") ? nbt.getFloat("barrierMeleeMult") : 1.0f;
reflect = nbt.hasKey("barrierReflect") && nbt.getBoolean("barrierReflect");
@@ -207,6 +222,7 @@ public class EnergyBarrierData {
copy.knockbackEnabled = knockbackEnabled;
copy.knockbackStrength = knockbackStrength;
copy.absorbing = absorbing;
copy.absorbRadius = absorbRadius;
copy.meleeEnabled = meleeEnabled;
copy.meleeDamageMultiplier = meleeDamageMultiplier;
copy.reflect = reflect;
@@ -5,6 +5,8 @@ import kamkeel.npcs.controllers.data.ability.Ability;
import kamkeel.npcs.controllers.data.ability.data.ChainedAbility;
import net.minecraft.nbt.NBTTagCompound;
import java.util.UUID;
/**
* A single entry in a {@link ChainedAbility}, representing one ability
* to execute in sequence along with its delay configuration.
@@ -155,7 +157,9 @@ public class ChainedAbilityEntry {
Ability resolved = ctrl.resolveAbility(abilityReference);
if (resolved == null) return false;
inlineAbility = ctrl.fromNBT(resolved.writeNBT(true));
NBTTagCompound nbt = resolved.writeNBT(true);
nbt.setString("id", UUID.randomUUID().toString());
inlineAbility = ctrl.fromNBT(nbt);
entryType = EntryType.INLINE;
abilityReference = "";
return true;
@@ -292,6 +292,14 @@ public abstract class AbilityBarrier extends AbilityEnergy {
barrierData.absorbing = absorbing;
}
public float getAbsorbRadius() {
return barrierData.absorbRadius;
}
public void setAbsorbRadius(float radius) {
barrierData.absorbRadius = radius;
}
// Reflection data
public boolean isReflect() {
return barrierData.reflect;
@@ -395,6 +403,9 @@ public abstract class AbilityBarrier extends AbilityEnergy {
.range(0, 10).visibleWhen(this::isKnockbackEnabled));
defs.add(FieldDef.boolField("ability.absorbing", this::isAbsorbing, this::setAbsorbing)
.hover("ability.hover.absorbing"));
defs.add(FieldDef.floatField("ability.absorbRadius", this::getAbsorbRadius, this::setAbsorbRadius)
.range(-1, 128).visibleWhen(this::isAbsorbing)
.hover("ability.hover.absorbRadius"));
defs.add(FieldDef.boolField("ability.reflect", this::isReflect, this::setReflect)
.hover("ability.hover.reflect"));
defs.add(FieldDef.floatField("ability.reflectStrength", this::getReflectStrengthPct, this::setReflectStrengthPct)
@@ -9,7 +9,7 @@ import net.minecraft.nbt.NBTTagCompound;
* Registered with EnergyController. DBC Addon implements this to route
* script-created energy entity damage through the DBC damage pipeline.
*/
public interface IEnergyHandler {
public interface IEnergyExtender {
/**
* Handle damage for an energy entity with custom damage data.
@@ -201,7 +201,7 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD
double x, double y, double z) {
this(world);
this.zoneType = type;
this.ownerEntityId = owner.getEntityId();
this.ownerEntityId = owner != null ? owner.getEntityId() : -1;
// Snap to ground with tiny offset to prevent z-clipping
double groundY = Ability.findGroundLevel(world, x, y, z) + GROUND_OFFSET;
this.setPosition(x, groundY, z);
@@ -213,7 +213,7 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD
*/
public void initAsHazard(Entity owner, double x, double y, double z) {
this.zoneType = ZoneType.HAZARD;
this.ownerEntityId = owner.getEntityId();
this.ownerEntityId = owner != null ? owner.getEntityId() : -1;
double groundY = Ability.findGroundLevel(worldObj, x, y, z) + GROUND_OFFSET;
this.setPosition(x, groundY, z);
this.ticksSinceDamage = damageInterval;
@@ -225,7 +225,7 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD
*/
public void initAsTrap(Entity owner, double x, double y, double z) {
this.zoneType = ZoneType.TRAP;
this.ownerEntityId = owner.getEntityId();
this.ownerEntityId = owner != null ? owner.getEntityId() : -1;
double groundY = Ability.findGroundLevel(worldObj, x, y, z) + GROUND_OFFSET;
this.setPosition(x, groundY, z);
this.ticksSinceLastTrigger = triggerCooldown;
@@ -321,6 +321,16 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD
protected void entityInit() {
}
@Override
protected void readEntityFromNBT(NBTTagCompound nbt) {
this.setDead();
}
@Override
protected void writeEntityToNBT(NBTTagCompound nbt) {
// Intentionally empty — ability entities are transient (not saved to world)
}
@Override
public void handleHealthUpdate(byte id) {
if (id == TRIGGER_FLASH_STATUS) {
@@ -834,15 +844,6 @@ public class EntityAbilityZone extends Entity implements IEntityAdditionalSpawnD
// NBT SERIALIZATION
// ═══════════════════════════════════════════════════════════════════
@Override
protected void readEntityFromNBT(NBTTagCompound nbt) {
// Intentionally empty — ability entities are transient (not saved to world)
}
@Override
protected void writeEntityToNBT(NBTTagCompound nbt) {
// Intentionally empty — ability entities are transient (not saved to world)
}
// ═══════════════════════════════════════════════════════════════════
// SPAWN DATA (ByteBuf for client sync)
@@ -376,6 +376,22 @@ public abstract class EntityEnergyAbility extends Entity implements IEntityAddit
magicData.readToNBT(nbt);
}
// ==================== WORLD NBT (INTENTIONALLY EMPTY) ====================
/**
* Ability entities are transient — they must not survive world saves.
* If Minecraft deserializes one from a chunk, kill it immediately.
*/
@Override
protected void readEntityFromNBT(NBTTagCompound nbt) {
this.setDead();
}
@Override
protected void writeEntityToNBT(NBTTagCompound nbt) {
// Intentionally empty — ability entities are transient (not saved to world)
}
// ==================== SPAWN DATA ====================
/**
@@ -630,7 +630,13 @@ public abstract class EntityEnergyBarrier extends EntityEnergyAbility {
/**
* Find a barrier that would absorb damage for the given entity (its caster).
* Returns the barrier if: absorbing is enabled AND entity is the barrier's owner.
* Returns the barrier if: absorbing is enabled, entity is the barrier's owner,
* and entity is within the barrier's absorb radius of the barrier's current position.
*
* Absorb radius behavior:
* -1 = no limit (absorb regardless of distance)
* 0 = uses the barrier's extent as the radius (dome radius, panel half-size, etc.)
* >0 = owner must be within this many blocks of the barrier entity
*/
public static EntityEnergyBarrier getAbsorbingBarrier(Entity entity) {
if (entity == null || entity.worldObj == null) return null;
@@ -638,7 +644,33 @@ public abstract class EntityEnergyBarrier extends EntityEnergyAbility {
for (EntityEnergyBarrier barrier : barriers) {
if (barrier.isDead) continue;
if (!barrier.barrierData.absorbing) continue;
if (barrier.ownerEntityId == entity.getEntityId()) {
if (barrier.ownerEntityId != entity.getEntityId()) continue;
float absorbRadius = barrier.barrierData.absorbRadius;
// -1 = unlimited range
if (absorbRadius < 0) {
return barrier;
}
// 0 = use barrier's own extent as the effective radius
// Domes: dome radius. Panels: max(width, height) * 0.5 + 1.0, doubled.
float effectiveRadius;
if (absorbRadius > 0) {
effectiveRadius = absorbRadius;
} else if (barrier instanceof EntityEnergyDome) {
effectiveRadius = barrier.getMaxExtent();
} else {
effectiveRadius = barrier.getMaxExtent() * 3.0f;
}
// Distance check from barrier's current position to the owner
double dx = entity.posX - barrier.posX;
double dy = (entity.posY + entity.height * 0.5) - barrier.posY;
double dz = entity.posZ - barrier.posZ;
double distSq = dx * dx + dy * dy + dz * dz;
if (distSq <= (double) effectiveRadius * effectiveRadius) {
return barrier;
}
}
@@ -709,6 +741,13 @@ public abstract class EntityEnergyBarrier extends EntityEnergyAbility {
double px = ent.posX + dirX * probe;
double py = ent.posY + dirY * probe;
double pz = ent.posZ + dirZ * probe;
// Never probe below the entity's feet — a downward probe always detects the
// block the entity is already standing/hovering above (false positive).
// MC's own collision system prevents entities from falling through floors,
// so the downward check is redundant. Upward probes (ceiling detection) still work.
if (py < ent.posY) {
py = ent.posY;
}
AxisAlignedBB box = AxisAlignedBB.getBoundingBox(
px - halfW, py, pz - halfW,
px + halfW, py + ent.height, pz + halfW
@@ -51,7 +51,7 @@ public class EntityEnergyDome extends EntityEnergyBarrier {
this(world);
this.noClip = true;
this.stepHeight = 0.0F;
this.ownerEntityId = owner.getEntityId();
this.ownerEntityId = owner != null ? owner.getEntityId() : -1;
this.setDomeRadius(domeRadius);
// Defensive copy: entities must never share data objects with the source ability.
this.displayData = display != null ? display.copy() : new EnergyDisplayData();
@@ -351,8 +351,10 @@ public class EntityEnergyDome extends EntityEnergyBarrier {
double radialVel = ent.motionX * nx + ent.motionY * ny + ent.motionZ * nz;
if (wasInside != isInside) {
// Entity crossed the boundary this tick — push back to original side
double pushDist = wasInside ? (r - 0.3) : (r + 0.3);
// Entity crossed the boundary this tick — push back to original side.
// Use a larger buffer (1.0) to prevent flying entities from crossing
// again in a single tick (their AI hard-sets motionY every tick).
double pushDist = wasInside ? (r - 1.0) : (r + 0.3);
double surfaceX = posX + nx * pushDist;
double surfaceY = posY + ny * pushDist - ent.height * 0.5;
double surfaceZ = posZ + nz * pushDist;
@@ -366,13 +368,16 @@ public class EntityEnergyDome extends EntityEnergyBarrier {
teleportEntity(ent, surfaceX, surfaceY, surfaceZ);
// Cancel radial velocity and add corrective push
// Cancel radial velocity and add corrective push (all 3 axes).
// The Y component is critical for the dome top where nx/nz ≈ 0:
// without it, flying entities get no inward push at the apex.
ent.motionX -= radialVel * nx;
ent.motionY -= radialVel * ny;
ent.motionZ -= radialVel * nz;
double pushForce = wasInside ? -0.15 : 0.15;
if (canPushInDirection(ent, nx * pushForce, 0, nz * pushForce)) {
if (canPushInDirection(ent, nx * pushForce, ny * pushForce, nz * pushForce)) {
ent.motionX += nx * pushForce;
ent.motionY += ny * pushForce;
ent.motionZ += nz * pushForce;
}
ent.velocityChanged = true;
@@ -387,8 +392,9 @@ public class EntityEnergyDome extends EntityEnergyBarrier {
ent.motionY -= radialVel * ny;
ent.motionZ -= radialVel * nz;
double pushForce = isInside ? -0.05 : 0.05;
if (canPushInDirection(ent, nx * pushForce, 0, nz * pushForce)) {
if (canPushInDirection(ent, nx * pushForce, ny * pushForce, nz * pushForce)) {
ent.motionX += nx * pushForce;
ent.motionY += ny * pushForce;
ent.motionZ += nz * pushForce;
}
ent.velocityChanged = true;
@@ -695,15 +701,6 @@ public class EntityEnergyDome extends EntityEnergyBarrier {
// ==================== NBT ====================
@Override
protected void readEntityFromNBT(NBTTagCompound nbt) {
// Intentionally empty — ability entities are transient (not saved to world)
}
@Override
protected void writeEntityToNBT(NBTTagCompound nbt) {
// Intentionally empty — ability entities are transient (not saved to world)
}
@Override
protected void writeSpawnNBT(NBTTagCompound nbt) {
@@ -289,15 +289,6 @@ public class EntityEnergyExplosion extends EntityEnergyAbility {
// ==================== NBT ====================
@Override
protected void readEntityFromNBT(NBTTagCompound nbt) {
// Intentionally empty - transient visual entity.
}
@Override
protected void writeEntityToNBT(NBTTagCompound nbt) {
// Intentionally empty - transient visual entity.
}
@Override
protected void writeSpawnNBT(NBTTagCompound nbt) {
@@ -55,7 +55,7 @@ public class EntityEnergyPanel extends EntityEnergyBarrier {
EnergyBarrierData barrier, EnergyPanelData panel) {
this(world);
this.setPosition(x, y + panel.heightOffset, z);
this.ownerEntityId = owner.getEntityId();
this.ownerEntityId = owner != null ? owner.getEntityId() : -1;
this.panelYaw = yaw;
this.prevPanelYaw = yaw;
this.mode = mode;
@@ -730,15 +730,6 @@ public class EntityEnergyPanel extends EntityEnergyBarrier {
// ==================== NBT ====================
@Override
protected void readEntityFromNBT(NBTTagCompound nbt) {
// Intentionally empty — ability entities are transient (not saved to world)
}
@Override
protected void writeEntityToNBT(NBTTagCompound nbt) {
// Intentionally empty — ability entities are transient (not saved to world)
}
@Override
protected void writeSpawnNBT(NBTTagCompound nbt) {
@@ -184,7 +184,7 @@ public abstract class EntityEnergyProjectile extends EntityEnergyAbility {
this.startY = y;
this.startZ = z;
this.ownerEntityId = owner.getEntityId();
this.ownerEntityId = owner != null ? owner.getEntityId() : -1;
this.targetEntityId = target != null ? target.getEntityId() : -1;
// Visual
@@ -2413,15 +2413,6 @@ public abstract class EntityEnergyProjectile extends EntityEnergyAbility {
// ==================== NBT ====================
@Override
protected void readEntityFromNBT(NBTTagCompound nbt) {
// Intentionally empty — ability entities are transient (not saved to world)
}
@Override
protected void writeEntityToNBT(NBTTagCompound nbt) {
// Intentionally empty — ability entities are transient (not saved to world)
}
@Override
protected void writeSpawnNBT(NBTTagCompound nbt) {
@@ -78,7 +78,7 @@ public class EntityEnergySweeper extends EntityEnergyAbility {
boolean lockOnTarget) {
this(world);
this.ownerEntityId = owner.getEntityId();
this.ownerEntityId = owner != null ? owner.getEntityId() : -1;
this.targetEntityId = target != null ? target.getEntityId() : -1;
this.beamLength = beamLength;
this.beamWidth = beamWidth;
@@ -95,16 +95,18 @@ public class EntityEnergySweeper extends EntityEnergyAbility {
this.maxTicks = (int) ((360.0f * numberOfRotations) / sweepSpeed) + 10;
// Calculate base yaw
if (lockOnTarget && target != null) {
if (lockOnTarget && target != null && owner != null) {
double dx = target.posX - owner.posX;
double dz = target.posZ - owner.posZ;
this.baseYaw = (float) Math.toDegrees(Math.atan2(-dx, dz));
} else {
} else if (owner != null) {
this.baseYaw = owner.rotationYaw;
}
// Position at beam center (bottom at feet, top at feet + beamHeight)
this.setPosition(owner.posX, owner.posY + beamHeight / 2.0, owner.posZ);
if (owner != null) {
this.setPosition(owner.posX, owner.posY + beamHeight / 2.0, owner.posZ);
}
// Ready to deal damage immediately
this.ticksSinceDamage = damageInterval;
@@ -423,15 +425,6 @@ public class EntityEnergySweeper extends EntityEnergyAbility {
// ==================== NBT ====================
@Override
protected void readEntityFromNBT(NBTTagCompound nbt) {
// Intentionally empty — ability entities are transient (not saved to world)
}
@Override
protected void writeEntityToNBT(NBTTagCompound nbt) {
// Intentionally empty — ability entities are transient (not saved to world)
}
@Override
protected void writeSpawnNBT(NBTTagCompound nbt) {
@@ -90,6 +90,7 @@ import kamkeel.npcs.network.packets.request.ability.AbilitiesNpcGetPacket;
import kamkeel.npcs.network.packets.request.ability.AbilitiesNpcSavePacket;
import kamkeel.npcs.network.packets.request.ability.BuiltInAbilityGetPacket;
import kamkeel.npcs.network.packets.request.ability.ChainedAbilitiesGetPacket;
import kamkeel.npcs.network.packets.request.ability.ChainedAbilityClonePacket;
import kamkeel.npcs.network.packets.request.ability.ChainedAbilityGetPacket;
import kamkeel.npcs.network.packets.request.ability.ChainedAbilityRemovePacket;
import kamkeel.npcs.network.packets.request.ability.ChainedAbilitySavePacket;
@@ -100,6 +101,7 @@ import kamkeel.npcs.network.packets.request.category.CategoryListRequestPacket;
import kamkeel.npcs.network.packets.request.category.CategoryMoveItemPacket;
import kamkeel.npcs.network.packets.request.category.CategoryRemovePacket;
import kamkeel.npcs.network.packets.request.category.CategorySavePacket;
import kamkeel.npcs.network.packets.request.ability.CustomAbilityClonePacket;
import kamkeel.npcs.network.packets.request.ability.CustomAbilityGetPacket;
import kamkeel.npcs.network.packets.request.ability.CustomAbilityRemovePacket;
import kamkeel.npcs.network.packets.request.ability.CustomAbilitySavePacket;
@@ -136,6 +138,7 @@ import kamkeel.npcs.network.packets.request.dialog.DialogNpcSetPacket;
import kamkeel.npcs.network.packets.request.dialog.DialogRemovePacket;
import kamkeel.npcs.network.packets.request.dialog.DialogSavePacket;
import kamkeel.npcs.network.packets.request.dialog.DialogsGetPacket;
import kamkeel.npcs.network.packets.request.effects.EffectClonePacket;
import kamkeel.npcs.network.packets.request.effects.EffectGetPacket;
import kamkeel.npcs.network.packets.request.effects.EffectRemovePacket;
import kamkeel.npcs.network.packets.request.effects.EffectSavePacket;
@@ -156,6 +159,7 @@ import kamkeel.npcs.network.packets.request.jobs.JobSpawnerRemovePacket;
import kamkeel.npcs.network.packets.request.linked.LinkedGetAllPacket;
import kamkeel.npcs.network.packets.request.linked.LinkedGetPacket;
import kamkeel.npcs.network.packets.request.linked.LinkedItemBuildPacket;
import kamkeel.npcs.network.packets.request.linked.LinkedItemClonePacket;
import kamkeel.npcs.network.packets.request.linked.LinkedItemRemovePacket;
import kamkeel.npcs.network.packets.request.linked.LinkedItemSavePacket;
import kamkeel.npcs.network.packets.request.linked.LinkedNPCAddPacket;
@@ -353,6 +357,7 @@ public class PacketHandler {
REQUEST_PACKET.registerPacket(new LinkedItemSavePacket());
REQUEST_PACKET.registerPacket(new LinkedItemRemovePacket());
REQUEST_PACKET.registerPacket(new LinkedItemBuildPacket());
REQUEST_PACKET.registerPacket(new LinkedItemClonePacket());
// Bank Packets
@@ -509,6 +514,7 @@ public class PacketHandler {
REQUEST_PACKET.registerPacket(new EffectsGetPacket());
REQUEST_PACKET.registerPacket(new EffectSavePacket());
REQUEST_PACKET.registerPacket(new EffectRemovePacket());
REQUEST_PACKET.registerPacket(new EffectClonePacket());
// Magic Packets
REQUEST_PACKET.registerPacket(new MagicCycleRemovePacket());
@@ -533,6 +539,8 @@ public class PacketHandler {
REQUEST_PACKET.registerPacket(new ChainedAbilityGetPacket());
REQUEST_PACKET.registerPacket(new ChainedAbilitySavePacket());
REQUEST_PACKET.registerPacket(new ChainedAbilityRemovePacket());
REQUEST_PACKET.registerPacket(new CustomAbilityClonePacket());
REQUEST_PACKET.registerPacket(new ChainedAbilityClonePacket());
// Category Packets
REQUEST_PACKET.registerPacket(new CategorySavePacket());
@@ -223,4 +223,9 @@ public enum EnumRequestPacket {
CategoryListRequest,
CategoryItemsRequest,
AbilityCategoryMoveItem,
CustomAbilityClone,
ChainedAbilityClone,
LinkedItemClone,
EffectClone,
}
@@ -0,0 +1,71 @@
package kamkeel.npcs.network.packets.request.ability;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import io.netty.buffer.ByteBuf;
import kamkeel.npcs.controllers.AbilityController;
import kamkeel.npcs.controllers.data.ability.data.ChainedAbility;
import kamkeel.npcs.network.AbstractPacket;
import kamkeel.npcs.network.PacketChannel;
import kamkeel.npcs.network.PacketHandler;
import kamkeel.npcs.network.PacketUtil;
import kamkeel.npcs.network.enums.EnumItemPacketType;
import kamkeel.npcs.network.enums.EnumRequestPacket;
import kamkeel.npcs.network.packets.data.large.GuiDataPacket;
import kamkeel.npcs.util.ByteBufUtils;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import noppes.npcs.CustomNpcsPermissions;
import noppes.npcs.NoppesUtilServer;
import java.io.IOException;
public final class ChainedAbilityClonePacket extends AbstractPacket {
public static String packetName = "Request|ChainedAbilityClone";
private String name;
public ChainedAbilityClonePacket() {
}
public ChainedAbilityClonePacket(String name) {
this.name = name;
}
@Override
public Enum getType() {
return EnumRequestPacket.ChainedAbilityClone;
}
@Override
public PacketChannel getChannel() {
return PacketHandler.REQUEST_PACKET;
}
@Override
public CustomNpcsPermissions.Permission getPermission() {
return CustomNpcsPermissions.GLOBAL_ABILITY;
}
@SideOnly(Side.CLIENT)
@Override
public void sendData(ByteBuf out) throws IOException {
ByteBufUtils.writeString(out, name);
}
@Override
public void receiveData(ByteBuf in, EntityPlayer player) throws IOException {
if (!(player instanceof EntityPlayerMP))
return;
if (!PacketUtil.verifyItemPacket(packetName, EnumItemPacketType.WAND, player))
return;
String chainName = ByteBufUtils.readString(in);
ChainedAbility clone = AbilityController.Instance.cloneChainedAbility(chainName);
if (clone != null) {
NoppesUtilServer.sendChainedAbilitiesData((EntityPlayerMP) player);
GuiDataPacket.sendGuiData((EntityPlayerMP) player, clone.writeNBT(false));
}
}
}
@@ -0,0 +1,71 @@
package kamkeel.npcs.network.packets.request.ability;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import io.netty.buffer.ByteBuf;
import kamkeel.npcs.controllers.AbilityController;
import kamkeel.npcs.controllers.data.ability.Ability;
import kamkeel.npcs.network.AbstractPacket;
import kamkeel.npcs.network.PacketChannel;
import kamkeel.npcs.network.PacketHandler;
import kamkeel.npcs.network.PacketUtil;
import kamkeel.npcs.network.enums.EnumItemPacketType;
import kamkeel.npcs.network.enums.EnumRequestPacket;
import kamkeel.npcs.network.packets.data.large.GuiDataPacket;
import kamkeel.npcs.util.ByteBufUtils;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import noppes.npcs.CustomNpcsPermissions;
import noppes.npcs.NoppesUtilServer;
import java.io.IOException;
public final class CustomAbilityClonePacket extends AbstractPacket {
public static String packetName = "Request|CustomAbilityClone";
private String name;
public CustomAbilityClonePacket() {
}
public CustomAbilityClonePacket(String name) {
this.name = name;
}
@Override
public Enum getType() {
return EnumRequestPacket.CustomAbilityClone;
}
@Override
public PacketChannel getChannel() {
return PacketHandler.REQUEST_PACKET;
}
@Override
public CustomNpcsPermissions.Permission getPermission() {
return CustomNpcsPermissions.GLOBAL_ABILITY;
}
@SideOnly(Side.CLIENT)
@Override
public void sendData(ByteBuf out) throws IOException {
ByteBufUtils.writeString(out, name);
}
@Override
public void receiveData(ByteBuf in, EntityPlayer player) throws IOException {
if (!(player instanceof EntityPlayerMP))
return;
if (!PacketUtil.verifyItemPacket(packetName, EnumItemPacketType.WAND, player))
return;
String abilityName = ByteBufUtils.readString(in);
Ability clone = AbilityController.Instance.cloneCustomAbility(abilityName);
if (clone != null) {
NoppesUtilServer.sendCustomAbilitiesData((EntityPlayerMP) player);
GuiDataPacket.sendGuiData((EntityPlayerMP) player, clone.writeNBT(false));
}
}
}
@@ -14,6 +14,7 @@ import kamkeel.npcs.util.ByteBufUtils;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import noppes.npcs.constants.EnumCategoryType;
import noppes.npcs.controllers.CategoryManager;
import java.io.IOException;
@@ -63,6 +64,11 @@ public final class AbilityCategoryMovePacket extends AbstractPacket {
String itemName = ByteBufUtils.readString(in);
int cat = in.readInt();
// Validate target category exists (0 = Uncategorized is always valid)
CategoryManager cm = CategorySavePacket.getManager(type);
if (cm == null) return;
if (cat != CategoryManager.UNCATEGORIZED_ID && cm.getCategory(cat) == null) return;
switch (type) {
case EnumCategoryType.ABILITY:
AbilityController.Instance.moveCustomAbilityToCategory(itemName, cat);
@@ -14,6 +14,7 @@ import net.minecraft.entity.player.EntityPlayerMP;
import kamkeel.npcs.controllers.AbilityController;
import noppes.npcs.constants.EnumCategoryType;
import noppes.npcs.controllers.AnimationController;
import noppes.npcs.controllers.CategoryManager;
import noppes.npcs.controllers.CustomEffectController;
import noppes.npcs.controllers.LinkedItemController;
@@ -65,6 +66,11 @@ public final class CategoryMoveItemPacket extends AbstractPacket {
int item = in.readInt();
int cat = in.readInt();
// Validate target category exists (0 = Uncategorized is always valid)
CategoryManager cm = CategorySavePacket.getManager(type);
if (cm == null) return;
if (cat != CategoryManager.UNCATEGORIZED_ID && cm.getCategory(cat) == null) return;
switch (type) {
case EnumCategoryType.EFFECT: CustomEffectController.getInstance().moveItemToCategory(item, cat); break;
case EnumCategoryType.ANIMATION: AnimationController.getInstance().moveItemToCategory(item, cat); break;
@@ -0,0 +1,69 @@
package kamkeel.npcs.network.packets.request.effects;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import io.netty.buffer.ByteBuf;
import kamkeel.npcs.network.AbstractPacket;
import kamkeel.npcs.network.PacketChannel;
import kamkeel.npcs.network.PacketHandler;
import kamkeel.npcs.network.PacketUtil;
import kamkeel.npcs.network.enums.EnumItemPacketType;
import kamkeel.npcs.network.enums.EnumRequestPacket;
import kamkeel.npcs.network.packets.data.large.GuiDataPacket;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import noppes.npcs.CustomNpcsPermissions;
import noppes.npcs.NoppesUtilServer;
import noppes.npcs.controllers.CustomEffectController;
import noppes.npcs.controllers.data.CustomEffect;
import java.io.IOException;
public final class EffectClonePacket extends AbstractPacket {
public static String packetName = "Request|EffectClone";
private int id;
public EffectClonePacket() {
}
public EffectClonePacket(int id) {
this.id = id;
}
@Override
public Enum getType() {
return EnumRequestPacket.EffectClone;
}
@Override
public PacketChannel getChannel() {
return PacketHandler.REQUEST_PACKET;
}
@Override
public CustomNpcsPermissions.Permission getPermission() {
return CustomNpcsPermissions.GLOBAL_EFFECT;
}
@SideOnly(Side.CLIENT)
@Override
public void sendData(ByteBuf out) throws IOException {
out.writeInt(this.id);
}
@Override
public void receiveData(ByteBuf in, EntityPlayer player) throws IOException {
if (!(player instanceof EntityPlayerMP))
return;
if (!PacketUtil.verifyItemPacket(packetName, EnumItemPacketType.WAND, player))
return;
int effectId = in.readInt();
CustomEffect clone = CustomEffectController.getInstance().cloneEffect(effectId);
if (clone != null) {
NoppesUtilServer.sendCustomEffectDataAll((EntityPlayerMP) player);
GuiDataPacket.sendGuiData((EntityPlayerMP) player, clone.writeToNBT(false));
}
}
}
@@ -0,0 +1,69 @@
package kamkeel.npcs.network.packets.request.linked;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import io.netty.buffer.ByteBuf;
import kamkeel.npcs.network.AbstractPacket;
import kamkeel.npcs.network.PacketChannel;
import kamkeel.npcs.network.PacketHandler;
import kamkeel.npcs.network.PacketUtil;
import kamkeel.npcs.network.enums.EnumItemPacketType;
import kamkeel.npcs.network.enums.EnumRequestPacket;
import kamkeel.npcs.network.packets.data.large.GuiDataPacket;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import noppes.npcs.CustomNpcsPermissions;
import noppes.npcs.NoppesUtilServer;
import noppes.npcs.controllers.LinkedItemController;
import noppes.npcs.controllers.data.LinkedItem;
import java.io.IOException;
public final class LinkedItemClonePacket extends AbstractPacket {
public static String packetName = "Request|LinkedItemClone";
private int id;
public LinkedItemClonePacket() {
}
public LinkedItemClonePacket(int id) {
this.id = id;
}
@Override
public Enum getType() {
return EnumRequestPacket.LinkedItemClone;
}
@Override
public PacketChannel getChannel() {
return PacketHandler.REQUEST_PACKET;
}
@Override
public CustomNpcsPermissions.Permission getPermission() {
return CustomNpcsPermissions.GLOBAL_LINKED;
}
@SideOnly(Side.CLIENT)
@Override
public void sendData(ByteBuf out) throws IOException {
out.writeInt(this.id);
}
@Override
public void receiveData(ByteBuf in, EntityPlayer player) throws IOException {
if (!(player instanceof EntityPlayerMP))
return;
if (!PacketUtil.verifyItemPacket(packetName, EnumItemPacketType.WAND, player))
return;
int linkedId = in.readInt();
LinkedItem clone = LinkedItemController.getInstance().cloneLinkedItem(linkedId);
if (clone != null) {
NoppesUtilServer.sendLinkedItemDataAll((EntityPlayerMP) player);
GuiDataPacket.sendGuiData((EntityPlayerMP) player, clone.writeToNBT(false));
}
}
}
+1 -1
View File
@@ -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-prerelease1")
@Mod(modid = "customnpcs", name = "CustomNPC+", version = "1.11-prerelease3")
public class CustomNpcs {
@SidedProxy(clientSide = "noppes.npcs.client.ClientProxy", serverSide = "noppes.npcs.CommonProxy")
@@ -199,7 +199,7 @@ public class GuiNpcMobSpawnerFullscreen extends GuiDirectory implements IGuiData
navScroll.setSelected(getNavSelection());
navScroll.scrollY = Math.min(savedNavScroll, Math.max(0, navScroll.maxScrollY));
// Phase 2: color nav items orange (selectable) or red (current location, not selectable)
// Phase 2: color nav items orange (selectable) or red (current location)
if (movePhase == 2) {
String currentNav = getNavSelection();
for (String name : navNames) {
@@ -38,6 +38,7 @@ public class SubGuiTagSelect extends SubGuiInterface implements IScrollData, ICu
public SubGuiTagSelect(HashSet<UUID> tagUUIDs) {
this.tagUUIDs = tagUUIDs;
setBackground("menubg.png");
closeOnEsc = true;
xSize = 430;
ySize = 220;
@@ -142,6 +143,10 @@ public class SubGuiTagSelect extends SubGuiInterface implements IScrollData, ICu
}
public void keyTyped(char c, int i) {
if (i == 1) {
close();
return;
}
super.keyTyped(c, i);
if (getTextField(4) != null) {
if (search.equals(getTextField(4).getText()))
@@ -88,12 +88,15 @@ public class SubGuiAbilityVariantSelect extends SubGuiInterface implements ICust
scroll.setSize(190, 155);
// Mark blank separators and group headers as non-interactive colored entries
scroll.nonInteractive.add("");
scroll.colors.put("", 0x000000);
if (lastGroup != null) {
for (int i = 0; i < variants.size(); i++) {
String group = variants.get(i).getGroup();
if (group != null)
if (group != null) {
scroll.nonInteractive.add(group);
scroll.colors.put(group, 0xFFFF00); // Yellow
}
}
}
@@ -11,10 +11,12 @@ import kamkeel.npcs.controllers.data.telegraph.TelegraphInstance;
import kamkeel.npcs.network.PacketClient;
import kamkeel.npcs.network.packets.request.ability.BuiltInAbilityGetPacket;
import kamkeel.npcs.network.packets.request.ability.ChainedAbilityGetPacket;
import kamkeel.npcs.network.packets.request.ability.ChainedAbilityClonePacket;
import kamkeel.npcs.network.packets.request.ability.ChainedAbilityRemovePacket;
import kamkeel.npcs.network.packets.request.ability.ChainedAbilitySavePacket;
import kamkeel.npcs.network.packets.request.ability.CustomAbilitiesGetPacket;
import kamkeel.npcs.network.packets.request.ability.CustomAbilityGetPacket;
import kamkeel.npcs.network.packets.request.ability.CustomAbilityClonePacket;
import kamkeel.npcs.network.packets.request.ability.CustomAbilityRemovePacket;
import kamkeel.npcs.network.packets.request.ability.CustomAbilitySavePacket;
import kamkeel.npcs.network.packets.request.category.AbilityCategoryMovePacket;
@@ -269,7 +271,14 @@ public class GuiAbilityDirectory extends GuiDirectoryCategorized
@Override
protected void onCloneItem() {
// Abilities don't support cloning through this mechanism
if (currentIsBuiltIn) return;
if (isChainedMode() && selectedChain != null) {
PacketClient.sendClient(new ChainedAbilityClonePacket(selectedChain.getName()));
if (selectedCatId >= 0) requestItemsInCategory(selectedCatId);
} else if (selectedAbility != null) {
PacketClient.sendClient(new CustomAbilityClonePacket(selectedAbility.getName()));
if (selectedCatId >= 0 && isCustomMode()) requestItemsInCategory(selectedCatId);
}
}
@Override
@@ -672,10 +681,10 @@ public class GuiAbilityDirectory extends GuiDirectoryCategorized
if (!isPlaying || isPaused) {
String statusKey = isPaused ? "animation.paused" : "animation.stopped";
addLabel(new GuiNpcLabel(90, statusKey, btnX, playY + 5));
addLabel(new GuiNpcLabel(90, statusKey, btnX, playY + 5, 0xFFFFFF));
addButton(new GuiTexturedButton(91, "", btnX + 65, playY, 11, 20, animTexture, 18, 71));
} else {
addLabel(new GuiNpcLabel(90, "animation.playing", btnX, playY + 5));
addLabel(new GuiNpcLabel(90, "animation.playing", btnX, playY + 5, 0xFFFFFF));
addButton(new GuiTexturedButton(92, "", btnX + 65, playY, 14, 20, animTexture, 0, 71));
}
if (isActive) {
@@ -683,13 +692,19 @@ public class GuiAbilityDirectory extends GuiDirectoryCategorized
}
}
// Edit + Remove at bottom
int editY = contentY + contentH - btnH * 2 - gap;
GuiNpcButton editBtn = new GuiNpcButton(51, rightX, editY, rightPanelW, btnH, "gui.edit");
// Edit + Copy on one row, Remove below
int btnY = contentY + contentH - btnH * 2 - gap;
int halfW = (rightPanelW - gap) / 2;
GuiNpcButton editBtn = new GuiNpcButton(51, rightX, btnY, halfW, btnH, "gui.edit");
editBtn.enabled = hasSelectedItem() && movePhase == 0 && !currentIsBuiltIn;
addButton(editBtn);
int removeY = editY + btnH + gap;
GuiNpcButton cloneBtn = new GuiNpcButton(52, rightX + halfW + gap, btnY, halfW, btnH, "gui.copy");
cloneBtn.enabled = hasSelectedItem() && movePhase == 0 && !currentIsBuiltIn;
addButton(cloneBtn);
int removeY = btnY + btnH + gap;
GuiNpcButton removeBtn = new GuiNpcButton(53, rightX, removeY, rightPanelW, btnH, "gui.remove");
removeBtn.enabled = hasSelectedItem() && movePhase == 0 && !currentIsBuiltIn;
removeBtn.setTextColor(0xFF5555);
@@ -718,6 +733,8 @@ public class GuiAbilityDirectory extends GuiDirectoryCategorized
* Matches GuiAbilityInterface's rendering pipeline.
*/
private void drawAbilityPreview(float partialTicks) {
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
// Set NPC facing direction
npc.prevRenderYawOffset = npc.renderYawOffset = NPC_FACING_YAW;
npc.prevRotationYaw = npc.rotationYaw = NPC_FACING_YAW;
@@ -747,44 +764,47 @@ public class GuiAbilityDirectory extends GuiDirectoryCategorized
GL11.glEnable(GL11.GL_SCISSOR_TEST);
setScissorClip(previewX, previewY, previewW, previewH);
GL11.glEnable(GL11.GL_COLOR_MATERIAL);
GL11.glPushMatrix();
GL11.glTranslatef(npcScreenX, npcScreenY, 500F);
GL11.glScalef(-renderZoom, renderZoom, renderZoom);
GL11.glRotatef(180F, 0.0F, 0.0F, 1.0F);
GL11.glRotatef(CAMERA_PITCH, 1.0F, 0.0F, 0.0F);
GL11.glRotatef(rotation, 0.0F, 1.0F, 0.0F);
GL11.glRotatef(135F, 0.0F, 1.0F, 0.0F);
RenderHelper.enableStandardItemLighting();
GL11.glRotatef(-135F, 0.0F, 1.0F, 0.0F);
GL11.glTranslatef(0.0F, npc.yOffset, 0.0F);
RenderManager.instance.playerViewY = 180F;
ClientEventHandler.renderingEntityInGUI = true;
// Render NPC at its position delta
try {
RenderManager.instance.renderEntityWithPosYaw(npc, npcDeltaX, npcDeltaY, npcDeltaZ, 0.0F, partialTicks);
} catch (Exception ignored) {}
GL11.glEnable(GL11.GL_COLOR_MATERIAL);
GL11.glPushMatrix();
// Render preview entities
renderPreviewEntities(partialTicks);
GL11.glTranslatef(npcScreenX, npcScreenY, 500F);
GL11.glScalef(-renderZoom, renderZoom, renderZoom);
GL11.glRotatef(180F, 0.0F, 0.0F, 1.0F);
GL11.glRotatef(CAMERA_PITCH, 1.0F, 0.0F, 0.0F);
GL11.glRotatef(rotation, 0.0F, 1.0F, 0.0F);
// Render telegraph
renderPreviewTelegraph(partialTicks);
GL11.glRotatef(135F, 0.0F, 1.0F, 0.0F);
RenderHelper.enableStandardItemLighting();
GL11.glRotatef(-135F, 0.0F, 1.0F, 0.0F);
ClientEventHandler.renderingEntityInGUI = false;
GL11.glPopMatrix();
GL11.glTranslatef(0.0F, npc.yOffset, 0.0F);
RenderManager.instance.playerViewY = 180F;
ClientEventHandler.renderingEntityInGUI = true;
RenderHelper.disableStandardItemLighting();
GL11.glDisable(GL12.GL_RESCALE_NORMAL);
OpenGlHelper.setActiveTexture(OpenGlHelper.lightmapTexUnit);
GL11.glDisable(GL11.GL_TEXTURE_2D);
OpenGlHelper.setActiveTexture(OpenGlHelper.defaultTexUnit);
// Render NPC at its position delta
try {
RenderManager.instance.renderEntityWithPosYaw(npc, npcDeltaX, npcDeltaY, npcDeltaZ, 0.0F, partialTicks);
} catch (Exception ignored) {}
GL11.glDisable(GL11.GL_SCISSOR_TEST);
// Render preview entities
renderPreviewEntities(partialTicks);
// Render telegraph
renderPreviewTelegraph(partialTicks);
ClientEventHandler.renderingEntityInGUI = false;
GL11.glPopMatrix();
RenderHelper.disableStandardItemLighting();
GL11.glDisable(GL12.GL_RESCALE_NORMAL);
OpenGlHelper.setActiveTexture(OpenGlHelper.lightmapTexUnit);
GL11.glDisable(GL11.GL_TEXTURE_2D);
OpenGlHelper.setActiveTexture(OpenGlHelper.defaultTexUnit);
} finally {
ClientEventHandler.renderingEntityInGUI = false;
GL11.glDisable(GL11.GL_SCISSOR_TEST);
}
GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT);
}
@@ -822,14 +842,17 @@ public class GuiAbilityDirectory extends GuiDirectoryCategorized
GL11.glTranslatef(0.0F, entity.yOffset, 1F);
RenderManager.instance.playerViewY = 180F;
ClientEventHandler.renderingEntityInGUI = true;
try {
RenderManager.instance.renderEntityWithPosYaw(entity, 0.0, 0.0, 0.0, 0.0F, 1.0F);
} catch (Exception ignored) {}
ClientEventHandler.renderingEntityInGUI = false;
entity.prevRenderYawOffset = entity.renderYawOffset = f2;
entity.prevRotationYaw = entity.rotationYaw = f3;
entity.rotationPitch = f4;
entity.prevRotationYawHead = entity.rotationYawHead = f7;
GL11.glPopMatrix();
RenderHelper.disableStandardItemLighting();
GL11.glDisable(GL12.GL_RESCALE_NORMAL);
@@ -837,7 +860,6 @@ public class GuiAbilityDirectory extends GuiDirectoryCategorized
GL11.glDisable(GL11.GL_TEXTURE_2D);
OpenGlHelper.setActiveTexture(OpenGlHelper.defaultTexUnit);
GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT);
GL11.glPopMatrix();
}
private void renderPreviewEntities(float partialTicks) {
@@ -959,32 +981,32 @@ public class GuiAbilityDirectory extends GuiDirectoryCategorized
if (isChainedMode() && selectedChain != null) {
fontRendererObj.drawString(selectedChain.getDisplayName(), x, y, 0xFFFFFF, true);
y += 14;
fontRendererObj.drawString("Entries: " + selectedChain.getEntries().size(), x, y, 0xB5B5B5, false);
fontRendererObj.drawString(StatCollector.translateToLocal("ability.entries") + ": " + selectedChain.getEntries().size(), x, y, 0xB5B5B5, false);
y += 12;
fontRendererObj.drawString("Cooldown: " + selectedChain.getCooldownTicks() + "t", x, y, 0xB5B5B5, false);
fontRendererObj.drawString(StatCollector.translateToLocal("ability.cooldown") + ": " + selectedChain.getCooldownTicks() + "t", x, y, 0xB5B5B5, false);
y += 12;
UserType ut = selectedChain.getAllowedBy();
fontRendererObj.drawString("Allowed: " + ut.name(), x, y, 0xB5B5B5, false);
fontRendererObj.drawString(StatCollector.translateToLocal("ability.allowed") + ": " + ut.name(), x, y, 0xB5B5B5, false);
} else if (selectedAbility != null) {
fontRendererObj.drawString(selectedAbility.getDisplayName(), x, y, 0xFFFFFF, true);
y += 14;
String typeId = selectedAbility.getTypeId();
String typeName = StatCollector.translateToLocal(typeId);
fontRendererObj.drawString("Type: " + typeName, x, y, 0xFFAE0D, false);
fontRendererObj.drawString(StatCollector.translateToLocal("gui.type") + ": " + typeName, x, y, 0xFFAE0D, false);
y += 12;
fontRendererObj.drawString("Cooldown: " + selectedAbility.getCooldownTicks() + "t", x, y, 0xB5B5B5, false);
fontRendererObj.drawString(StatCollector.translateToLocal("ability.cooldown") + ": " + selectedAbility.getCooldownTicks() + "t", x, y, 0xB5B5B5, false);
y += 12;
fontRendererObj.drawString("Windup: " + selectedAbility.getWindUpTicks() + "t", x, y, 0xB5B5B5, false);
fontRendererObj.drawString(StatCollector.translateToLocal("ability.windup") + ": " + selectedAbility.getWindUpTicks() + "t", x, y, 0xB5B5B5, false);
y += 12;
UserType ut = selectedAbility.getAllowedBy();
fontRendererObj.drawString("Allowed: " + ut.name(), x, y, 0xB5B5B5, false);
fontRendererObj.drawString(StatCollector.translateToLocal("ability.allowed") + ": " + ut.name(), x, y, 0xB5B5B5, false);
if (selectedAbility.hasDamage()) {
y += 12;
fontRendererObj.drawString("Damage: " + selectedAbility.getDisplayDamage(), x, y, 0xFF5555, false);
fontRendererObj.drawString(StatCollector.translateToLocal("ability.damage") + ": " + selectedAbility.getDisplayDamage(), x, y, 0xFF5555, false);
}
if (currentIsBuiltIn) {
y += 12;
fontRendererObj.drawString("(Built-in)", x, y, 0x55FF55, false);
fontRendererObj.drawString(StatCollector.translateToLocal("gui.builtin.tag"), x, y, 0x55FF55, false);
}
}
}
@@ -19,6 +19,7 @@ import net.minecraft.client.renderer.RenderHelper;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.StatCollector;
import noppes.npcs.client.NoppesUtil;
import noppes.npcs.client.gui.util.GuiCustomScroll;
import noppes.npcs.client.gui.util.GuiDirectoryCategorized;
@@ -34,6 +35,7 @@ import noppes.npcs.controllers.data.AnimationData;
import noppes.npcs.controllers.data.Category;
import noppes.npcs.controllers.data.Frame;
import noppes.npcs.controllers.data.FramePart;
import noppes.npcs.client.ClientEventHandler;
import noppes.npcs.entity.EntityCustomNpc;
import noppes.npcs.entity.EntityNPCInterface;
import org.lwjgl.opengl.GL11;
@@ -263,10 +265,13 @@ public class GuiAnimationDirectory extends GuiDirectoryCategorized {
@Override
protected void initRightPanel(int startY) {
// Calculate layout: playback row + edit/copy + remove rows
// Calculate layout: playback rows + edit/copy + remove rows
int bottomRows = 2; // edit+copy, remove
boolean hasPlayback = animation != null && !animation.frames.isEmpty() && hasSelectedItem();
if (hasPlayback) bottomRows++; // playback row
if (hasPlayback) {
bottomRows++; // playback controls row
if (playingAnimation) bottomRows++; // frame info row
}
int bottomH = bottomRows * (btnH + gap) + 14;
previewX = rightX;
@@ -283,15 +288,17 @@ public class GuiAnimationDirectory extends GuiDirectoryCategorized {
if (!playingAnimation || (data.animation != null && data.animation.paused)) {
String statusKey = (data.animation != null && data.animation.paused) ? "animation.paused" : "animation.stopped";
addLabel(new GuiNpcLabel(90, statusKey, btnX, playY + 5));
addLabel(new GuiNpcLabel(90, statusKey, btnX, playY + 5, 0xFFFFFF));
addButton(new GuiTexturedButton(91, "", btnX + 65, playY, 11, 20, animTexture, 18, 71));
} else {
addLabel(new GuiNpcLabel(90, "animation.playing", btnX, playY + 5));
addLabel(new GuiNpcLabel(94, "", btnX + 60, playY + 5));
addLabel(new GuiNpcLabel(90, "animation.playing", btnX, playY + 5, 0xFFFFFF));
addButton(new GuiTexturedButton(92, "", btnX + 65, playY, 14, 20, animTexture, 0, 71));
}
if (playingAnimation) {
addButton(new GuiTexturedButton(93, "", btnX + 85, playY, 14, 20, animTexture, 33, 71));
// Frame info on its own line below playback controls
int frameInfoY = playY + btnH + gap;
addLabel(new GuiNpcLabel(94, "", btnX, frameInfoY + 5, 0xFFFFFF));
}
}
@@ -472,15 +479,18 @@ public class GuiAnimationDirectory extends GuiDirectoryCategorized {
GL11.glTranslatef(0.0F, entity.yOffset, 1F);
RenderManager.instance.playerViewY = 180F;
ClientEventHandler.renderingEntityInGUI = true;
try {
RenderManager.instance.renderEntityWithPosYaw(entity, 0.0, 0.0, 0.0, 0.0F, 1.0F);
} catch (Exception ignored) {
}
ClientEventHandler.renderingEntityInGUI = false;
entity.prevRenderYawOffset = entity.renderYawOffset = f2;
entity.prevRotationYaw = entity.rotationYaw = f3;
entity.rotationPitch = f4;
entity.prevRotationYawHead = entity.rotationYawHead = f7;
GL11.glPopMatrix();
RenderHelper.disableStandardItemLighting();
GL11.glDisable(GL12.GL_RESCALE_NORMAL);
@@ -488,7 +498,6 @@ public class GuiAnimationDirectory extends GuiDirectoryCategorized {
GL11.glDisable(GL11.GL_TEXTURE_2D);
OpenGlHelper.setActiveTexture(OpenGlHelper.defaultTexUnit);
GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT);
GL11.glPopMatrix();
}
@Override
@@ -498,15 +507,15 @@ public class GuiAnimationDirectory extends GuiDirectoryCategorized {
fontRendererObj.drawString(animation.name, x, y, 0xFFFFFF, true);
y += 14;
fontRendererObj.drawString("Frames: " + animation.frames.size(), x, y, 0xB5B5B5, false);
fontRendererObj.drawString(StatCollector.translateToLocal("animation.frames") + ": " + animation.frames.size(), x, y, 0xB5B5B5, false);
y += 12;
fontRendererObj.drawString("Speed: " + animation.speed, x, y, 0xB5B5B5, false);
fontRendererObj.drawString(StatCollector.translateToLocal("stats.speed") + ": " + animation.speed, x, y, 0xB5B5B5, false);
y += 12;
String loopStr = animation.loop == 0 ? "None" : animation.loop == 1 ? "Loop" : "Mirror";
fontRendererObj.drawString("Loop: " + loopStr, x, y, 0xB5B5B5, false);
String loopStr = animation.loop == 0 ? StatCollector.translateToLocal("gui.none") : animation.loop == 1 ? StatCollector.translateToLocal("animation.loop") : StatCollector.translateToLocal("animation.mirror");
fontRendererObj.drawString(StatCollector.translateToLocal("animation.loop") + ": " + loopStr, x, y, 0xB5B5B5, false);
if (currentIsBuiltIn) {
y += 12;
fontRendererObj.drawString("(Built-in)", x, y, 0x55FF55, false);
fontRendererObj.drawString(StatCollector.translateToLocal("gui.builtin.tag"), x, y, 0x55FF55, false);
}
}
@@ -8,6 +8,7 @@ import kamkeel.npcs.network.packets.request.category.CategoryRemovePacket;
import kamkeel.npcs.network.packets.request.category.CategorySavePacket;
import kamkeel.npcs.network.packets.request.effects.EffectGetPacket;
import kamkeel.npcs.network.packets.request.effects.EffectRemovePacket;
import kamkeel.npcs.network.packets.request.effects.EffectClonePacket;
import kamkeel.npcs.network.packets.request.effects.EffectSavePacket;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.renderer.texture.TextureManager;
@@ -15,7 +16,6 @@ import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.StatCollector;
import noppes.npcs.client.ClientCacheHandler;
import noppes.npcs.client.CustomNpcResourceListener;
import noppes.npcs.client.gui.SubGuiEffectGeneral;
import noppes.npcs.client.gui.util.GuiDirectoryCategorized;
import noppes.npcs.client.gui.util.SubGuiInterface;
@@ -90,9 +90,7 @@ public class GuiEffectDirectory extends GuiDirectoryCategorized {
@Override
protected void onCloneItem() {
if (effect != null && effect.id >= 0) {
CustomEffect clone = effect.cloneEffect();
while (itemData.containsKey(clone.name)) clone.name += "_";
PacketClient.sendClient(new EffectSavePacket(clone.writeToNBT(false), ""));
PacketClient.sendClient(new EffectClonePacket(effect.id));
}
}
@@ -180,7 +178,7 @@ public class GuiEffectDirectory extends GuiDirectoryCategorized {
if (effect == null || effect.id == -1) return;
String drawString = effect.getMenuName();
fontRendererObj.drawString(drawString, x, y, CustomNpcResourceListener.DefaultTextColor, true);
fontRendererObj.drawString(drawString, x, y, 0xFFFFFF, true);
y += 14;
fontRendererObj.drawString(StatCollector.translateToLocal("gui.name") + ": " + effect.name, x, y, 0xFFFFFF, false);
@@ -9,6 +9,7 @@ import kamkeel.npcs.network.packets.request.category.CategorySavePacket;
import kamkeel.npcs.network.packets.request.linked.LinkedGetPacket;
import kamkeel.npcs.network.packets.request.linked.LinkedItemBuildPacket;
import kamkeel.npcs.network.packets.request.linked.LinkedItemRemovePacket;
import kamkeel.npcs.network.packets.request.linked.LinkedItemClonePacket;
import kamkeel.npcs.network.packets.request.linked.LinkedItemSavePacket;
import kamkeel.npcs.util.ColorUtil;
import net.minecraft.client.gui.GuiScreen;
@@ -17,7 +18,6 @@ import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.StatCollector;
import noppes.npcs.client.ClientCacheHandler;
import noppes.npcs.client.CustomNpcResourceListener;
import net.minecraft.client.gui.GuiButton;
import noppes.npcs.client.gui.item.SubGuiLinkedItem;
import noppes.npcs.client.gui.util.GuiDirectoryCategorized;
@@ -91,10 +91,7 @@ public class GuiLinkedItemDirectory extends GuiDirectoryCategorized {
@Override
protected void onCloneItem() {
if (linkedItem != null && linkedItem.id >= 0) {
LinkedItem clone = linkedItem.clone();
while (itemData.containsKey(clone.name)) clone.name += "_";
clone.id = -1;
PacketClient.sendClient(new LinkedItemSavePacket(clone.writeToNBT(false), ""));
PacketClient.sendClient(new LinkedItemClonePacket(linkedItem.id));
}
}
@@ -215,7 +212,7 @@ public class GuiLinkedItemDirectory extends GuiDirectoryCategorized {
protected void drawItemDetails(int x, int y, int w) {
if (linkedItem == null || linkedItem.id < 0) return;
fontRendererObj.drawString(linkedItem.name, x, y, CustomNpcResourceListener.DefaultTextColor, true);
fontRendererObj.drawString(linkedItem.name, x, y, 0xFFFFFF, true);
y += 14;
int labelColor = 0xffae0d;
@@ -3,6 +3,7 @@ package noppes.npcs.client.gui.global;
import kamkeel.npcs.network.PacketClient;
import kamkeel.npcs.network.packets.request.effects.EffectGetPacket;
import kamkeel.npcs.network.packets.request.effects.EffectRemovePacket;
import kamkeel.npcs.network.packets.request.effects.EffectClonePacket;
import kamkeel.npcs.network.packets.request.effects.EffectSavePacket;
import kamkeel.npcs.network.packets.request.effects.EffectsGetPacket;
import net.minecraft.client.gui.GuiButton;
@@ -106,10 +107,9 @@ public class GuiNPCManageEffects extends GuiNPCInterface2 implements ICustomScro
displayGuiScreen(guiyesno);
}
} else if (button.id == 2) {
CustomEffect effect = this.effect.cloneEffect();
while (data.containsKey(effect.name))
effect.name += "_";
PacketClient.sendClient(new EffectSavePacket(effect.writeToNBT(false), ""));
if (effect != null && effect.id >= 0) {
PacketClient.sendClient(new EffectClonePacket(effect.id));
}
}
if (button.id == 66) {
@@ -5,6 +5,7 @@ import kamkeel.npcs.network.packets.request.linked.LinkedGetAllPacket;
import kamkeel.npcs.network.packets.request.linked.LinkedGetPacket;
import kamkeel.npcs.network.packets.request.linked.LinkedItemBuildPacket;
import kamkeel.npcs.network.packets.request.linked.LinkedItemRemovePacket;
import kamkeel.npcs.network.packets.request.linked.LinkedItemClonePacket;
import kamkeel.npcs.network.packets.request.linked.LinkedItemSavePacket;
import kamkeel.npcs.network.packets.request.linked.LinkedNPCAddPacket;
import kamkeel.npcs.network.packets.request.linked.LinkedNPCRemovePacket;
@@ -288,13 +289,7 @@ public class GuiNPCManageLinked extends GuiNPCInterface2 implements IScrollData,
}
if (button.id == 4) {
if (data.containsKey(scroll.getSelected()) && linkedItem != null && linkedItem.id >= 0) {
String name = linkedItem.name;
while (data.containsKey(name))
name += "_";
LinkedItem linkedItemClone = this.linkedItem.clone();
linkedItemClone.name = name;
linkedItemClone.id = -1;
PacketClient.sendClient(new LinkedItemSavePacket(linkedItemClone.writeToNBT(false), ""));
PacketClient.sendClient(new LinkedItemClonePacket(this.linkedItem.id));
}
}
if (button.id == 5) {
@@ -19,6 +19,7 @@ public class GuiCustomScroll extends GuiScreen {
public static final ResourceLocation resource = new ResourceLocation("customnpcs", "textures/gui/misc.png");
public List<String> list;
public final HashMap<String, Integer> colors = new HashMap<>();
public final HashSet<String> nonInteractive = new HashSet<>();
public int id;
public int guiLeft = 0;
@@ -217,21 +218,25 @@ public class GuiCustomScroll extends GuiScreen {
} else
text = displayString;
// Entries with a custom color are non-interactive (headers/separators)
// Non-interactive entries (headers/separators) — colored, no hover/selection
Integer customColor = colors.get(rawEntry);
if (customColor != null) {
if (nonInteractive.contains(rawEntry)) {
if (!text.isEmpty())
fontRendererObj.drawString(text, j, k, customColor);
fontRendererObj.drawString(text, j, k, customColor != null ? customColor : 0xffffff);
} else if ((multipleSelection && selectedList.contains(text)) || (!multipleSelection && selected == i)) {
drawVerticalLine(j - 2, k - 4, k + 10, 0xffffffff);
drawVerticalLine(j + xSize - 18 + xOffset, k - 4, k + 10, 0xffffffff);
drawHorizontalLine(j - 2, j + xSize - 18 + xOffset, k - 3, 0xffffffff);
drawHorizontalLine(j - 2, j + xSize - 18 + xOffset, k + 10, 0xffffffff);
fontRendererObj.drawString(text, j, k, 0xffffff);
int selColor = customColor != null ? customColor : 0xffffff;
fontRendererObj.drawString(text, j, k, selColor);
} else if (i == hover) {
fontRendererObj.drawString(text, j, k, 0x00ff00);
} else
fontRendererObj.drawString(text, j, k, 0xffffff);
int hoverColor = customColor != null ? customColor : 0x00ff00;
fontRendererObj.drawString(text, j, k, hoverColor);
} else {
int defaultColor = customColor != null ? customColor : 0xffffff;
fontRendererObj.drawString(text, j, k, defaultColor);
}
}
}
@@ -252,8 +257,8 @@ public class GuiCustomScroll extends GuiScreen {
continue;
}
// Skip non-interactive entries (colored headers/separators)
if (colors.containsKey(list.get(j1))) {
// Skip non-interactive entries (headers/separators)
if (nonInteractive.contains(list.get(j1))) {
continue;
}
@@ -63,6 +63,10 @@ public abstract class GuiDirectoryCategorized extends GuiDirectory
// ========== New Item Category ==========
protected int pendingNewItemCatId = -1;
// ========== Pending Move Destination ==========
private int pendingMoveDestCatId = -1;
private String pendingMoveDestName = "";
// ========== Category Data Loaded ==========
private boolean itemDataLoaded = false;
@@ -184,7 +188,7 @@ public abstract class GuiDirectoryCategorized extends GuiDirectory
x += topBtnW + 2;
// MOVE
String moveLabel = movePhase > 0 ? "Moving" : "Move";
String moveLabel = movePhase > 0 ? "gui.moving" : "gui.move";
GuiNpcButton moveBtn = new GuiNpcButton(54, x, topBtnY, topBtnW, btnH, moveLabel);
moveBtn.enabled = selectedCatId >= 0 && !leftCollapsed && hasCategories();
addButton(moveBtn);
@@ -192,8 +196,9 @@ public abstract class GuiDirectoryCategorized extends GuiDirectory
// Confirm (only during move phase 1)
if (movePhase == 1) {
GuiNpcButton confirmBtn = new GuiNpcButton(55, x, topBtnY, topBtnW, btnH, "Confirm");
GuiNpcButton confirmBtn = new GuiNpcButton(55, x, topBtnY, topBtnW, btnH, "gui.confirm");
confirmBtn.enabled = !moveSelection.isEmpty();
confirmBtn.setTextColor(0x55FFFF);
addButton(confirmBtn);
x += topBtnW + 2;
}
@@ -230,9 +235,18 @@ public abstract class GuiDirectoryCategorized extends GuiDirectory
catScroll.setSelected(prevCatName);
catScroll.scrollY = Math.max(0, Math.min(savedCatScrollY, catScroll.maxScrollY));
// Phase 2: ensure scroll is clickable for destination selection
// Phase 1: categories locked (not clickable)
if (movePhase == 1) {
catScroll.setSelectable(false);
}
// Phase 2: color nav items orange (selectable) or red (current/source category)
if (movePhase == 2) {
catScroll.setSelectable(true);
for (String name : navNames) {
catScroll.colors.put(name, name.equals(prevCatName) ? 0xFF5555 : 0xFFAA00);
}
} else if (movePhase == 0) {
catScroll.setSelectable(true);
}
addScroll(catScroll);
@@ -391,19 +405,19 @@ public abstract class GuiDirectoryCategorized extends GuiDirectory
// Draw ID (skip if -1, e.g. abilities use names not IDs)
if (hasSelectedItem() && getSelectedItemId() >= 0) {
String idStr = "ID: " + getSelectedItemId();
int idY = contentY + contentH - btnH - 14;
String idStr = StatCollector.translateToLocal("gui.id") + ": " + getSelectedItemId();
int idY = contentY + contentH - btnH * 2 - gap - 12;
fontRendererObj.drawString(idStr, previewX + 4, idY, 0xFFFFFF, true);
}
}
// Move phase overlay text
if (movePhase == 1) {
String text = "Select Items to Move (max " + MAX_MOVE_ITEMS + ")";
String text = String.format(StatCollector.translateToLocal("gui.move.selectItems"), MAX_MOVE_ITEMS);
int textW = fontRendererObj.getStringWidth(text);
fontRendererObj.drawStringWithShadow(text, width / 2 - textW / 2, height - pad - 10, 0x55FF55);
} else if (movePhase == 2) {
String text = "Select Destination Category";
String text = StatCollector.translateToLocal("gui.move.selectCategory");
int textW = fontRendererObj.getStringWidth(text);
fontRendererObj.drawStringWithShadow(text, width / 2 - textW / 2, height - pad - 10, 0xFF5555);
}
@@ -592,15 +606,20 @@ public abstract class GuiDirectoryCategorized extends GuiDirectory
public void customScrollClicked(int i, int j, int k, GuiCustomScroll guiCustomScroll) {
// Category scroll
if (guiCustomScroll.id == 2) {
// Block category clicks during move phase 1 (selecting items)
if (movePhase == 1) return;
String selected = catScroll.getSelected();
if (selected == null) return;
// Move phase 2: destination selected
// Move phase 2: destination selected — show confirmation
if (movePhase == 2) {
if (!selected.equals(prevCatName)) {
Integer destCatId = catData.get(selected);
if (destCatId != null) {
executeMoveItems(destCatId);
pendingMoveDestCatId = destCatId;
pendingMoveDestName = selected;
setSubGui(new SubGuiCategoryMoveConfirm(moveSelection.size(), selected));
}
}
return;
@@ -752,7 +771,7 @@ public abstract class GuiDirectoryCategorized extends GuiDirectory
if (subgui instanceof SubGuiEditText) {
if (!((SubGuiEditText) subgui).cancelled && selectedCatId > 0) {
String name = ((SubGuiEditText) subgui).text;
if (name != null && !name.isEmpty() && !catData.containsKey(name)) {
if (name != null && !name.isEmpty() && !name.equals(prevCatName) && !catData.containsKey(name)) {
Category cat = new Category(selectedCatId, name);
onSaveCategory(cat);
catData.remove(prevCatName);
@@ -760,6 +779,18 @@ public abstract class GuiDirectoryCategorized extends GuiDirectory
prevCatName = name;
}
}
// Restore category selection to current name (handles validation failure)
catScroll.setSelected(prevCatName);
}
if (subgui instanceof SubGuiCategoryMoveConfirm) {
SubGuiCategoryMoveConfirm confirm = (SubGuiCategoryMoveConfirm) subgui;
if (confirm.confirmed && !moveSelection.isEmpty() && pendingMoveDestCatId >= 0) {
executeMoveItems(pendingMoveDestCatId);
} else {
// Cancelled — stay in phase 2
}
pendingMoveDestCatId = -1;
pendingMoveDestName = "";
}
onSubGuiClosed(subgui);
}
@@ -5,6 +5,7 @@ import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.renderer.RenderHelper;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.StatCollector;
@@ -166,8 +167,13 @@ public class GuiNpcButton extends GuiButton {
}
}
}
// First, draw the button normally using the superclass method.
super.drawButton(mc, mouseX, mouseY);
// Wide buttons (>396px) break the vanilla 200px button texture.
// Draw them with a three-patch approach instead.
if (this.width > 396) {
drawWideButton(mc, mouseX, mouseY);
} else {
super.drawButton(mc, mouseX, mouseY);
}
// Restore original display string
this.displayString = original;
// Then, if an icon texture is set, draw it.
@@ -175,17 +181,58 @@ public class GuiNpcButton extends GuiButton {
mc.getTextureManager().bindTexture(iconTexture);
GL11.glPushMatrix();
RenderHelper.enableGUIStandardItemLighting();
// Calculate the icon position so that it's centered in the button.
// (Assumes the button's (xPosition,yPosition) is its top left.)
int iconX = this.xPosition + (this.width - iconWidth) / 2;
int iconY = this.yPosition + (this.height - iconHeight) / 2;
// Draw the texture; we assume texture coordinates start at (0,0) with the desired size.
this.drawTexturedModalRect(iconX, iconY, iconPosX, iconPosY, iconWidth, iconHeight);
RenderHelper.disableStandardItemLighting();
GL11.glPopMatrix();
}
}
/**
* Draws a button wider than the vanilla 200px texture allows.
* Uses three-patch rendering: left cap, tiled center, right cap.
*/
private void drawWideButton(Minecraft mc, int mouseX, int mouseY) {
FontRenderer fontrenderer = mc.fontRenderer;
mc.getTextureManager().bindTexture(buttonTextures);
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
this.field_146123_n = mouseX >= this.xPosition && mouseY >= this.yPosition
&& mouseX < this.xPosition + this.width && mouseY < this.yPosition + this.height;
int k = this.getHoverState(this.field_146123_n);
GL11.glEnable(GL11.GL_BLEND);
OpenGlHelper.glBlendFunc(770, 771, 1, 0);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
int texY = 46 + k * 20;
int cap = 100;
// Left cap (first 100px of texture)
this.drawTexturedModalRect(this.xPosition, this.yPosition, 0, texY, cap, this.height);
// Right cap (last 100px of texture)
this.drawTexturedModalRect(this.xPosition + this.width - cap, this.yPosition, 200 - cap, texY, cap, this.height);
// Tile the center column to fill the gap
int gapStart = this.xPosition + cap;
int gapEnd = this.xPosition + this.width - cap;
for (int x = gapStart; x < gapEnd; x += 2) {
int drawW = Math.min(2, gapEnd - x);
this.drawTexturedModalRect(x, this.yPosition, 99, texY, drawW, this.height);
}
this.mouseDragged(mc, mouseX, mouseY);
int l = 14737632;
if (packedFGColour != 0) {
l = packedFGColour;
} else if (!this.enabled) {
l = 10526880;
} else if (this.field_146123_n) {
l = 16777120;
}
this.drawCenteredString(fontrenderer, this.displayString,
this.xPosition + this.width / 2, this.yPosition + (this.height - 8) / 2, l);
}
public void drawHover(int i, int j, boolean hasSubGui) {
if (hasSubGui || !visible || hoverableText.isEmpty())
return;
@@ -0,0 +1,48 @@
package noppes.npcs.client.gui.util;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.util.StatCollector;
public class SubGuiCategoryMoveConfirm extends SubGuiInterface {
public boolean confirmed = false;
private final int count;
public final String destCategoryName;
public SubGuiCategoryMoveConfirm(int count, String destCategoryName) {
this.count = count;
this.destCategoryName = destCategoryName;
xSize = 220;
ySize = 90;
closeOnEsc = true;
}
@Override
public void initGui() {
super.initGui();
String line1 = String.format(StatCollector.translateToLocal("gui.move.confirmLine"), count);
addLabel(new GuiNpcLabel(0, line1, guiLeft + 10, guiTop + 10, 0xFFFFFF));
String dest = StatCollector.translateToLocal("gui.category") + ": \"" + destCategoryName + "\"";
addLabel(new GuiNpcLabel(1, " " + dest, guiLeft + 10, guiTop + 25, 0xFFFFFF));
addButton(new GuiNpcButton(0, guiLeft + 10, guiTop + 55, 95, 20, "gui.yes"));
addButton(new GuiNpcButton(1, guiLeft + 115, guiTop + 55, 95, 20, "gui.cancel"));
}
@Override
protected void actionPerformed(GuiButton guibutton) {
if (guibutton.id == 0) {
confirmed = true;
close();
}
if (guibutton.id == 1) {
close();
}
}
@Override
public void save() {
}
}
@@ -708,4 +708,23 @@ public class CustomEffectController implements ICustomEffectHandler {
return getCustomEffects().get(customEffect.getID());
}
public CustomEffect cloneEffect(int originalId) {
CustomEffect original = getCustomEffects().get(originalId);
if (original == null) return null;
NBTTagCompound nbt = original.writeToNBT(true);
int newId = getUnusedId();
nbt.setInteger("ID", newId);
CustomEffect clone = new CustomEffect();
clone.readFromNBT(nbt);
String name = clone.getName();
while (has(name)) name += "_";
clone.name = name;
saveEffect(clone);
return clone;
}
}
@@ -241,6 +241,25 @@ public class LinkedItemController implements ILinkedItemHandler {
return linkedItems.get(linkedItem.getId());
}
public LinkedItem cloneLinkedItem(int originalId) {
LinkedItem original = linkedItems.get(originalId);
if (original == null) return null;
NBTTagCompound nbt = original.writeToNBT(true);
int newId = getUnusedId();
nbt.setInteger("Id", newId);
LinkedItem clone = new LinkedItem();
clone.readFromNBT(nbt);
String name = clone.getName();
while (hasName(name)) name += "_";
clone.name = name;
saveLinkedItem(clone);
return clone;
}
private boolean hasOther(String name, int id) {
for (LinkedItem linkedItem : linkedItems.values()) {
if (linkedItem.getId() != id && linkedItem.getName().equalsIgnoreCase(name))
@@ -3,6 +3,7 @@ package noppes.npcs.controllers.data;
import kamkeel.npcs.network.packets.request.script.EffectScriptPacket;
import net.minecraft.nbt.NBTTagCompound;
import noppes.npcs.constants.ScriptContext;
import noppes.npcs.janino.EventJaninoScript;
/**
* Script handler for CustomEffect scripts.
@@ -35,6 +36,11 @@ public class EffectScript extends SingleScriptHandler implements IScriptHandlerP
return ScriptContext.EFFECT;
}
@Override
public IScriptUnit createJaninoScriptUnit() {
return new EventJaninoScript(ScriptContext.EFFECT);
}
@Override
public String noticeString() {
return effectId >= 0 ? "CustomEffect[" + effectId + "]" : "CustomEffect";
@@ -3,6 +3,7 @@ package noppes.npcs.controllers.data;
import kamkeel.npcs.network.packets.request.script.item.LinkedItemScriptPacket;
import net.minecraft.nbt.NBTTagCompound;
import noppes.npcs.constants.ScriptContext;
import noppes.npcs.janino.EventJaninoScript;
/**
* Script handler for LinkedItem scripts.
@@ -35,6 +36,11 @@ public class LinkedItemScript extends SingleScriptHandler implements IScriptHand
return ScriptContext.LINKED_ITEM;
}
@Override
public IScriptUnit createJaninoScriptUnit() {
return new EventJaninoScript(ScriptContext.LINKED_ITEM);
}
@Override
public String noticeString() {
return "LinkedItem";
@@ -3,6 +3,7 @@ package noppes.npcs.controllers.data;
import kamkeel.npcs.network.packets.request.script.RecipeScriptPacket;
import net.minecraft.nbt.NBTTagCompound;
import noppes.npcs.constants.ScriptContext;
import noppes.npcs.janino.EventJaninoScript;
/**
* Script handler for Recipe scripts (both carpentry and anvil).
@@ -42,6 +43,11 @@ public class RecipeScript extends SingleScriptHandler implements IScriptHandlerP
return ScriptContext.RECIPE;
}
@Override
public IScriptUnit createJaninoScriptUnit() {
return new EventJaninoScript(ScriptContext.RECIPE);
}
@Override
public String noticeString() {
return "RecipeScript";
@@ -183,6 +183,7 @@ public class SkinOverlay implements ISkinOverlay {
private void updateClient() {
if (this.parent != null) {
this.parent.updateClient();
this.parent.save();
}
}
}
@@ -86,7 +86,8 @@ public class EntityCustomNpc extends EntityNPCFlying {
worldObj.MAX_ENTITY_RADIUS = newWidth / 2;
}
setSize(newWidth, newHeight);
this.width = newWidth;
this.height = newHeight;
this.setPosition(posX, posY, posZ);
}
}
@@ -37,7 +37,9 @@ public class EntityNPCGolem extends EntityNPCInterface {
newWidth = 1.4f;
newHeight = 2.5f;
}
setSize(newWidth, newHeight);
this.width = newWidth;
this.height = newHeight;
this.setPosition(posX, posY, posZ);
if (width / 2 > World.MAX_ENTITY_RADIUS) {
World.MAX_ENTITY_RADIUS = width / 2;
}
@@ -1285,7 +1285,8 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti
World.MAX_ENTITY_RADIUS = newWidth / 2;
}
setSize(newWidth, newHeight);
this.width = newWidth;
this.height = newHeight;
this.setPosition(posX, posY, posZ);
}
@@ -115,7 +115,9 @@ public class EntityNpcDragon extends EntityNPCInterface {
// }
@Override
public void updateHitbox() {
setSize(1.8f, 1.4f);
this.width = 1.8f;
this.height = 1.4f;
this.setPosition(posX, posY, posZ);
if (width / 2 > World.MAX_ENTITY_RADIUS) {
World.MAX_ENTITY_RADIUS = width / 2;
}
@@ -17,7 +17,9 @@ public class EntityNpcSlime extends EntityNPCInterface {
@Override
public void updateHitbox() {
setSize(0.8f, 0.8f);
this.width = 0.8f;
this.height = 0.8f;
this.setPosition(posX, posY, posZ);
if (width / 2 > World.MAX_ENTITY_RADIUS) {
World.MAX_ENTITY_RADIUS = width / 2;
}
@@ -49,6 +49,12 @@ public class DataSkinOverlays implements IOverlayHandler {
return nbttagcompound;
}
public void save() {
if (parent instanceof PlayerData) {
((PlayerData) parent).save();
}
}
public void updateClient() {
if (parent != null) {
if (parent instanceof PlayerData && ((PlayerData) parent).player != null) {
@@ -69,6 +75,7 @@ public class DataSkinOverlays implements IOverlayHandler {
((SkinOverlay) data).parent = this;
this.overlayList.put(id, data);
updateClient();
save();
}
public ISkinOverlay get(int id) {
@@ -82,6 +89,7 @@ public class DataSkinOverlays implements IOverlayHandler {
public boolean remove(int id) {
boolean removed = this.overlayList.remove(id) != null;
updateClient();
save();
return removed;
}
@@ -92,5 +100,6 @@ public class DataSkinOverlays implements IOverlayHandler {
public void clear() {
this.overlayList.clear();
updateClient();
save();
}
}
@@ -40,7 +40,9 @@ public class EntityNPCEnderman extends EntityNpcEnderchibi {
newHeight = (newHeight / 5f) * display.modelSize;
newWidth = Math.max(newWidth, 0.00001f);
newHeight = Math.max(newHeight, 0.00001f);
setSize(newWidth, newHeight);
this.width = newWidth;
this.height = newHeight;
this.setPosition(posX, posY, posZ);
if (width / 2 > World.MAX_ENTITY_RADIUS) {
World.MAX_ENTITY_RADIUS = width / 2;
}
@@ -47,6 +47,7 @@ public class JobSpawner extends JobInterface {
private EntityLivingBase target;
private boolean isResetting = false;
public JobSpawner(EntityNPCInterface npc) {
super(npc);
@@ -314,16 +315,25 @@ public class JobSpawner extends JobInterface {
@Override
public void reset() {
number = 0;
if (spawned.isEmpty())
spawned = getNearbySpawned();
if (isResetting)
return;
isResetting = true;
try {
number = 0;
if (spawned.isEmpty())
spawned = getNearbySpawned();
target = null;
checkSpawns();
target = null;
checkSpawns();
} finally {
isResetting = false;
}
}
@Override
public void delete() {
if (isResetting)
return;
if (spawnType == 3 && npc.stats.spawnCycle == 3) {
spawnEntity(compound1);
spawnEntity(compound2);
@@ -63,7 +63,7 @@ import noppes.npcs.api.IBlock;
import noppes.npcs.api.ICommand;
import noppes.npcs.api.IContainer;
import noppes.npcs.api.IDamageSource;
import noppes.npcs.api.IEnergyController;
import noppes.npcs.api.IEnergyHandler;
import noppes.npcs.api.INbt;
import noppes.npcs.api.IParticle;
import noppes.npcs.api.IPos;
@@ -623,7 +623,7 @@ public class NpcAPI extends AbstractNpcAPI {
}
@Override
public IEnergyController getEnergyController() {
public IEnergyHandler getEnergyHandler() {
return EnergyController.Instance;
}
@@ -121,6 +121,14 @@ public class ScriptEnergyBarrier<T extends EntityEnergyBarrier> extends ScriptEn
entity.getBarrierData().absorbing = absorbing;
}
public float getAbsorbRadius() {
return entity.getBarrierData().absorbRadius;
}
public void setAbsorbRadius(float radius) {
entity.getBarrierData().absorbRadius = radius;
}
public boolean isReflect() {
return entity.getBarrierData().reflect;
}