From db09c9a44ff0b83aaee57a3242b24fbc055ff160 Mon Sep 17 00:00:00 2001 From: Kam Date: Fri, 31 Oct 2025 17:57:13 -0400 Subject: [PATCH 01/38] Handle null results in conditional actions --- .../data/action/action/ConditionalAction.java | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/main/java/noppes/npcs/controllers/data/action/action/ConditionalAction.java b/src/main/java/noppes/npcs/controllers/data/action/action/ConditionalAction.java index 7bcd0ef97..9e608df37 100644 --- a/src/main/java/noppes/npcs/controllers/data/action/action/ConditionalAction.java +++ b/src/main/java/noppes/npcs/controllers/data/action/action/ConditionalAction.java @@ -88,7 +88,7 @@ public class ConditionalAction extends Action implements IConditionalAction { @Override public boolean isTerminated() { - return terminateWhen != null && terminateWhen.apply(this); + return checkTermination(); } public void inheritedTick() { @@ -107,10 +107,10 @@ public class ConditionalAction extends Action implements IConditionalAction { checkCount++; Runnable execute = () -> { - if (condition != null && condition.apply(this)) + if (evaluatePredicate(condition, "Condition")) executeTask(); - if (isTerminated()) { + if (checkTermination()) { if (onTermination != null) executeOnTermination(); markDone(); @@ -153,6 +153,32 @@ public class ConditionalAction extends Action implements IConditionalAction { manager.LOGGER.log("Finished executing onTermination task", this); } + private boolean checkTermination() { + return evaluatePredicate(terminateWhen, "Termination condition"); + } + + private boolean evaluatePredicate(Function predicate, String name) { + if (predicate == null) + return false; + + Boolean result; + try { + result = predicate.apply(this); + } catch (Throwable t) { + manager.LOGGER.error(name + " of " + this + " threw an exception:", t); + markDone(); + return false; + } + + if (result == null) { + if (manager.inDebugMode()) + manager.LOGGER.log(name + " returned null; treating as false", this); + return false; + } + + return result; + } + public String toString() { return String.format("%s [queue='%s', scheduled=%s, done=%s, paused=%s, updateEvery=%s, duration=%d/%d, count=%d/%d, checks=%d/%d, taskExecuted=%s, threaded=%s]", getIdentifier(), getQueueName(), From 3f0b93a2d2aabbffa57b9be396f97614e99415ef Mon Sep 17 00:00:00 2001 From: Kam Date: Sat, 1 Nov 2025 04:19:39 -0400 Subject: [PATCH 02/38] Fix server handling for SaveBookPacket --- .../kamkeel/npcs/network/packets/player/SaveBookPacket.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/kamkeel/npcs/network/packets/player/SaveBookPacket.java b/src/main/java/kamkeel/npcs/network/packets/player/SaveBookPacket.java index 0cac183e5..2822a4c46 100644 --- a/src/main/java/kamkeel/npcs/network/packets/player/SaveBookPacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/player/SaveBookPacket.java @@ -1,7 +1,5 @@ package kamkeel.npcs.network.packets.player; -import cpw.mods.fml.relauncher.Side; -import cpw.mods.fml.relauncher.SideOnly; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import kamkeel.npcs.network.LargeAbstractPacket; @@ -52,7 +50,6 @@ public class SaveBookPacket extends LargeAbstractPacket { } @Override - @SideOnly(Side.CLIENT) protected void handleCompleteData(ByteBuf data, EntityPlayer player) throws IOException { int x = data.readInt(), y = data.readInt(), z = data.readInt(); From 323a0c454af1f7e36d62c33b10a2f57e3a2e2f40 Mon Sep 17 00:00:00 2001 From: Kam Date: Wed, 5 Nov 2025 15:56:46 -0500 Subject: [PATCH 03/38] Fix Player Data Buttons --- .../npcs/client/gui/global/GuiNpcManagePlayerData.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/noppes/npcs/client/gui/global/GuiNpcManagePlayerData.java b/src/main/java/noppes/npcs/client/gui/global/GuiNpcManagePlayerData.java index 58b50de0a..d25a6939f 100644 --- a/src/main/java/noppes/npcs/client/gui/global/GuiNpcManagePlayerData.java +++ b/src/main/java/noppes/npcs/client/gui/global/GuiNpcManagePlayerData.java @@ -70,7 +70,6 @@ public class GuiNpcManagePlayerData extends GuiNPCInterface2 implements GuiYesNo @Override public void mouseClicked(int i, int j, int k) { - ; super.mouseClicked(i, j, k); if (k == 0 && playerScroll != null) playerScroll.mouseClicked(i, j, k); @@ -132,11 +131,10 @@ public class GuiNpcManagePlayerData extends GuiNPCInterface2 implements GuiYesNo } if (id == 1) { PacketClient.sendClient(new PlayerDataMapRegenPacket()); - close(); } - } else { - displayGuiScreen(this); } + + displayGuiScreen(this); } // IScrollData interface. From b47a2bc1d2c68cee19f362e3bbea93ba7c5dc343 Mon Sep 17 00:00:00 2001 From: Kam Date: Wed, 5 Nov 2025 16:52:38 -0500 Subject: [PATCH 04/38] Reset sync caches between worlds --- .../kamkeel/npcs/controllers/SyncController.java | 15 +++++++++++++++ src/main/java/noppes/npcs/CustomNpcs.java | 3 +++ 2 files changed, 18 insertions(+) diff --git a/src/main/java/kamkeel/npcs/controllers/SyncController.java b/src/main/java/kamkeel/npcs/controllers/SyncController.java index 7678c2a5e..9e960fc1e 100644 --- a/src/main/java/kamkeel/npcs/controllers/SyncController.java +++ b/src/main/java/kamkeel/npcs/controllers/SyncController.java @@ -86,6 +86,15 @@ public class SyncController { registerCache(EnumSyncType.MAGIC_CYCLE, SyncController::magicCyclesNBT); } + public static void resetServerState() { + for (SyncCacheEntry entry : CACHE_ENTRIES.values()) { + if (entry != null) { + entry.reset(); + } + } + PLAYER_SYNC_STATE.clear(); + } + public static void syncPlayer(EntityPlayerMP player) { syncPlayer(player, true); } @@ -939,6 +948,12 @@ public class SyncController { return revision; } + private synchronized void reset() { + dirty = true; + payload = null; + revision = 0; + } + private static byte[][] splitIntoChunks(byte[] payload) { int totalSize = payload.length; int chunkCount = (totalSize + LargeAbstractPacket.CHUNK_SIZE - 1) / LargeAbstractPacket.CHUNK_SIZE; diff --git a/src/main/java/noppes/npcs/CustomNpcs.java b/src/main/java/noppes/npcs/CustomNpcs.java index 3cfcd18e4..a8504edbe 100644 --- a/src/main/java/noppes/npcs/CustomNpcs.java +++ b/src/main/java/noppes/npcs/CustomNpcs.java @@ -19,6 +19,7 @@ import kamkeel.npcs.command.CommandKamkeel; import kamkeel.npcs.command.profile.CommandProfile; import kamkeel.npcs.controllers.AttributeController; import kamkeel.npcs.controllers.ProfileController; +import kamkeel.npcs.controllers.SyncController; import kamkeel.npcs.controllers.data.profile.CNPCData; import kamkeel.npcs.developer.Developer; import kamkeel.npcs.network.PacketHandler; @@ -303,6 +304,7 @@ public class CustomNpcs { //Loading items in the about to start event was corrupting items with a damage value @EventHandler public void started(FMLServerStartedEvent event) { + SyncController.resetServerState(); RecipeController.Instance.load(); new BankController(); DialogController.Instance.load(); @@ -315,6 +317,7 @@ public class CustomNpcs { @EventHandler public void stopped(FMLServerStoppedEvent event) { + SyncController.resetServerState(); ServerCloneController.Instance = null; GlobalDataController.Instance.saveData(); ScriptController.Instance.saveForgeScripts(); From 5e8e004ef6579ded7a88045847207165d842463e Mon Sep 17 00:00:00 2001 From: Kam Date: Wed, 5 Nov 2025 17:23:21 -0500 Subject: [PATCH 05/38] Scope sync cache rebuild to server load --- .../npcs/controllers/SyncController.java | 85 +++++++++++++------ src/main/java/noppes/npcs/CustomNpcs.java | 2 +- 2 files changed, 58 insertions(+), 29 deletions(-) diff --git a/src/main/java/kamkeel/npcs/controllers/SyncController.java b/src/main/java/kamkeel/npcs/controllers/SyncController.java index 9e960fc1e..7876b71d9 100644 --- a/src/main/java/kamkeel/npcs/controllers/SyncController.java +++ b/src/main/java/kamkeel/npcs/controllers/SyncController.java @@ -53,6 +53,23 @@ public class SyncController { public static boolean DEBUG_SYNC_LOGGING = false; + private static final SyncController SERVER = new SyncController(); + + private final EnumMap cacheEntries = new EnumMap<>(EnumSyncType.class); + private final ConcurrentHashMap playerSyncState = new ConcurrentHashMap<>(); + + private SyncController() { + registerCache(EnumSyncType.FACTION, SyncController::factionsNBT); + registerCache(EnumSyncType.DIALOG_CATEGORY, SyncController::dialogCategoriesNBT); + registerCache(EnumSyncType.QUEST_CATEGORY, SyncController::questCategoriesNBT); + registerCache(EnumSyncType.WORKBENCH_RECIPES, SyncController::workbenchNBT); + registerCache(EnumSyncType.CARPENTRY_RECIPES, SyncController::carpentryNBT); + registerCache(EnumSyncType.ANVIL_RECIPES, SyncController::anvilNBT); + registerCache(EnumSyncType.CUSTOM_EFFECTS, SyncController::customEffectsNBT); + registerCache(EnumSyncType.MAGIC, SyncController::magicsNBT); + registerCache(EnumSyncType.MAGIC_CYCLE, SyncController::magicCyclesNBT); + } + private static void debug(String message, Object... args) { if (DEBUG_SYNC_LOGGING) { System.out.println("[SyncController] " + String.format(message, args)); @@ -71,39 +88,51 @@ public class SyncController { EnumSyncType.MAGIC_CYCLE }; - private static final EnumMap CACHE_ENTRIES = new EnumMap<>(EnumSyncType.class); - private static final ConcurrentHashMap PLAYER_SYNC_STATE = new ConcurrentHashMap<>(); + public static void load() { + SERVER.loadInternal(); + } - static { - registerCache(EnumSyncType.FACTION, SyncController::factionsNBT); - registerCache(EnumSyncType.DIALOG_CATEGORY, SyncController::dialogCategoriesNBT); - registerCache(EnumSyncType.QUEST_CATEGORY, SyncController::questCategoriesNBT); - registerCache(EnumSyncType.WORKBENCH_RECIPES, SyncController::workbenchNBT); - registerCache(EnumSyncType.CARPENTRY_RECIPES, SyncController::carpentryNBT); - registerCache(EnumSyncType.ANVIL_RECIPES, SyncController::anvilNBT); - registerCache(EnumSyncType.CUSTOM_EFFECTS, SyncController::customEffectsNBT); - registerCache(EnumSyncType.MAGIC, SyncController::magicsNBT); - registerCache(EnumSyncType.MAGIC_CYCLE, SyncController::magicCyclesNBT); + @Deprecated + public static void rebuildAllCaches() { + load(); } public static void resetServerState() { - for (SyncCacheEntry entry : CACHE_ENTRIES.values()) { + SERVER.resetState(); + } + + private void loadInternal() { + resetState(); + rebuildAllCachesInternal(); + } + + private void resetState() { + for (SyncCacheEntry entry : cacheEntries.values()) { if (entry != null) { entry.reset(); } } - PLAYER_SYNC_STATE.clear(); + playerSyncState.clear(); + } + + private void rebuildAllCachesInternal() { + for (EnumSyncType type : EnumSyncType.values()) { + SyncCacheEntry entry = cacheEntries.get(type); + if (entry != null) { + entry.getPayload(type); + } + } } public static void syncPlayer(EntityPlayerMP player) { - syncPlayer(player, true); + SERVER.syncPlayerInternal(player, true); } - private static void syncPlayer(EntityPlayerMP player, boolean includePostPackets) { - PlayerSyncState state = PLAYER_SYNC_STATE.computeIfAbsent(player.getUniqueID(), PlayerSyncState::new); + private void syncPlayerInternal(EntityPlayerMP player, boolean includePostPackets) { + PlayerSyncState state = playerSyncState.computeIfAbsent(player.getUniqueID(), PlayerSyncState::new); for (EnumSyncType type : LOGIN_SYNC_TYPES) { - SyncCacheEntry entry = CACHE_ENTRIES.get(type); + SyncCacheEntry entry = cacheEntries.get(type); if (entry == null) { continue; } @@ -132,7 +161,7 @@ public class SyncController { } public static void beginLogin(EntityPlayerMP player) { - PLAYER_SYNC_STATE.computeIfAbsent(player.getUniqueID(), PlayerSyncState::new); + SERVER.playerSyncState.computeIfAbsent(player.getUniqueID(), PlayerSyncState::new); PacketHandler.Instance.sendToPlayer( new LoginPacket(getServerCacheKey(), getServerRevisionSnapshot()), player @@ -146,7 +175,7 @@ public class SyncController { Map clientRevisions ) { String currentServerKey = getServerCacheKey(); - PlayerSyncState state = PLAYER_SYNC_STATE.computeIfAbsent(player.getUniqueID(), PlayerSyncState::new); + PlayerSyncState state = SERVER.playerSyncState.computeIfAbsent(player.getUniqueID(), PlayerSyncState::new); if (!currentServerKey.equals(serverKey)) { state.reset(); @@ -184,7 +213,7 @@ public class SyncController { private static EnumMap getServerRevisionSnapshot() { EnumMap snapshot = new EnumMap<>(EnumSyncType.class); for (EnumSyncType type : LOGIN_SYNC_TYPES) { - SyncCacheEntry entry = CACHE_ENTRIES.get(type); + SyncCacheEntry entry = SERVER.cacheEntries.get(type); if (entry != null) { snapshot.put(type, entry.getRevisionValue()); } @@ -208,7 +237,7 @@ public class SyncController { } public static int getCurrentRevision(EnumSyncType type) { - SyncCacheEntry entry = CACHE_ENTRIES.get(type); + SyncCacheEntry entry = SERVER.cacheEntries.get(type); return entry == null ? -1 : entry.getRevisionValue(); } @@ -800,12 +829,12 @@ public class SyncController { } } - private static void registerCache(EnumSyncType type, Supplier supplier) { - CACHE_ENTRIES.put(type, new SyncCacheEntry(type, supplier)); + private void registerCache(EnumSyncType type, Supplier supplier) { + cacheEntries.put(type, new SyncCacheEntry(type, supplier)); } private static CachedSyncPayload rebuildNow(EnumSyncType type) { - SyncCacheEntry entry = CACHE_ENTRIES.get(type); + SyncCacheEntry entry = SERVER.cacheEntries.get(type); if (entry == null) { return null; } @@ -817,7 +846,7 @@ public class SyncController { EnumSet targets = getInvalidationTargets(type); EnumMap revisions = new EnumMap<>(EnumSyncType.class); for (EnumSyncType target : targets) { - SyncCacheEntry entry = CACHE_ENTRIES.get(target); + SyncCacheEntry entry = SERVER.cacheEntries.get(target); if (entry != null) { int newRevision = entry.invalidate(); revisions.put(target, newRevision); @@ -857,7 +886,7 @@ public class SyncController { if (revision < 0) { return; } - for (PlayerSyncState state : PLAYER_SYNC_STATE.values()) { + for (PlayerSyncState state : SERVER.playerSyncState.values()) { state.updateRevision(type, revision); } } @@ -866,7 +895,7 @@ public class SyncController { if (revisions.isEmpty()) { return; } - for (PlayerSyncState state : PLAYER_SYNC_STATE.values()) { + for (PlayerSyncState state : SERVER.playerSyncState.values()) { for (Map.Entry entry : revisions.entrySet()) { state.updateRevision(entry.getKey(), entry.getValue()); } diff --git a/src/main/java/noppes/npcs/CustomNpcs.java b/src/main/java/noppes/npcs/CustomNpcs.java index a8504edbe..b4ec0a43c 100644 --- a/src/main/java/noppes/npcs/CustomNpcs.java +++ b/src/main/java/noppes/npcs/CustomNpcs.java @@ -304,7 +304,6 @@ public class CustomNpcs { //Loading items in the about to start event was corrupting items with a damage value @EventHandler public void started(FMLServerStartedEvent event) { - SyncController.resetServerState(); RecipeController.Instance.load(); new BankController(); DialogController.Instance.load(); @@ -312,6 +311,7 @@ public class CustomNpcs { ScriptController.HasStart = true; ServerCloneController.Instance = new ServerCloneController(); ServerTagMapController.Instance = new ServerTagMapController(); + SyncController.load(); } From 3009c97bc9b1d93829fce646dd660253a7966618 Mon Sep 17 00:00:00 2001 From: Kam Date: Wed, 5 Nov 2025 17:25:14 -0500 Subject: [PATCH 06/38] Close On Esc Support --- src/main/java/noppes/npcs/client/gui/util/GuiNPCInterface.java | 3 +++ .../java/noppes/npcs/client/gui/util/GuiNPCInterface2.java | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/noppes/npcs/client/gui/util/GuiNPCInterface.java b/src/main/java/noppes/npcs/client/gui/util/GuiNPCInterface.java index 27e5da0ec..069f81764 100644 --- a/src/main/java/noppes/npcs/client/gui/util/GuiNPCInterface.java +++ b/src/main/java/noppes/npcs/client/gui/util/GuiNPCInterface.java @@ -219,6 +219,9 @@ public abstract class GuiNPCInterface extends GuiScreen { } public void close() { + if(GuiNpcTextField.activeTextfield != null) + GuiNpcTextField.unfocus(); + Keyboard.enableRepeatEvents(false); displayGuiScreen(null); mc.setIngameFocus(); diff --git a/src/main/java/noppes/npcs/client/gui/util/GuiNPCInterface2.java b/src/main/java/noppes/npcs/client/gui/util/GuiNPCInterface2.java index 7c9f1d43e..190a03613 100644 --- a/src/main/java/noppes/npcs/client/gui/util/GuiNPCInterface2.java +++ b/src/main/java/noppes/npcs/client/gui/util/GuiNPCInterface2.java @@ -17,7 +17,7 @@ public abstract class GuiNPCInterface2 extends GuiNPCInterface { xSize = 420; ySize = 200; menu = new GuiNpcMenu(this, activeMenu, npc); - + closeOnEsc = true; } @Override From 082d22ba6e7f677d7daf335b5efea12788e78ef4 Mon Sep 17 00:00:00 2001 From: Kam Date: Wed, 5 Nov 2025 17:33:24 -0500 Subject: [PATCH 07/38] Syncing Issues --- .../npcs/controllers/SyncController.java | 80 ++++++------------- src/main/java/noppes/npcs/CustomNpcs.java | 1 - 2 files changed, 23 insertions(+), 58 deletions(-) diff --git a/src/main/java/kamkeel/npcs/controllers/SyncController.java b/src/main/java/kamkeel/npcs/controllers/SyncController.java index 7876b71d9..9617f4d12 100644 --- a/src/main/java/kamkeel/npcs/controllers/SyncController.java +++ b/src/main/java/kamkeel/npcs/controllers/SyncController.java @@ -53,22 +53,9 @@ public class SyncController { public static boolean DEBUG_SYNC_LOGGING = false; - private static final SyncController SERVER = new SyncController(); + private static final EnumMap cacheEntries = new EnumMap<>(EnumSyncType.class); + private static final ConcurrentHashMap playerSyncState = new ConcurrentHashMap<>(); - private final EnumMap cacheEntries = new EnumMap<>(EnumSyncType.class); - private final ConcurrentHashMap playerSyncState = new ConcurrentHashMap<>(); - - private SyncController() { - registerCache(EnumSyncType.FACTION, SyncController::factionsNBT); - registerCache(EnumSyncType.DIALOG_CATEGORY, SyncController::dialogCategoriesNBT); - registerCache(EnumSyncType.QUEST_CATEGORY, SyncController::questCategoriesNBT); - registerCache(EnumSyncType.WORKBENCH_RECIPES, SyncController::workbenchNBT); - registerCache(EnumSyncType.CARPENTRY_RECIPES, SyncController::carpentryNBT); - registerCache(EnumSyncType.ANVIL_RECIPES, SyncController::anvilNBT); - registerCache(EnumSyncType.CUSTOM_EFFECTS, SyncController::customEffectsNBT); - registerCache(EnumSyncType.MAGIC, SyncController::magicsNBT); - registerCache(EnumSyncType.MAGIC_CYCLE, SyncController::magicCyclesNBT); - } private static void debug(String message, Object... args) { if (DEBUG_SYNC_LOGGING) { @@ -89,46 +76,25 @@ public class SyncController { }; public static void load() { - SERVER.loadInternal(); - } - - @Deprecated - public static void rebuildAllCaches() { - load(); - } - - public static void resetServerState() { - SERVER.resetState(); - } - - private void loadInternal() { - resetState(); - rebuildAllCachesInternal(); - } - - private void resetState() { - for (SyncCacheEntry entry : cacheEntries.values()) { - if (entry != null) { - entry.reset(); - } - } + cacheEntries.clear(); playerSyncState.clear(); - } - private void rebuildAllCachesInternal() { - for (EnumSyncType type : EnumSyncType.values()) { - SyncCacheEntry entry = cacheEntries.get(type); - if (entry != null) { - entry.getPayload(type); - } - } + registerCache(EnumSyncType.FACTION, SyncController::factionsNBT); + registerCache(EnumSyncType.DIALOG_CATEGORY, SyncController::dialogCategoriesNBT); + registerCache(EnumSyncType.QUEST_CATEGORY, SyncController::questCategoriesNBT); + registerCache(EnumSyncType.WORKBENCH_RECIPES, SyncController::workbenchNBT); + registerCache(EnumSyncType.CARPENTRY_RECIPES, SyncController::carpentryNBT); + registerCache(EnumSyncType.ANVIL_RECIPES, SyncController::anvilNBT); + registerCache(EnumSyncType.CUSTOM_EFFECTS, SyncController::customEffectsNBT); + registerCache(EnumSyncType.MAGIC, SyncController::magicsNBT); + registerCache(EnumSyncType.MAGIC_CYCLE, SyncController::magicCyclesNBT); } public static void syncPlayer(EntityPlayerMP player) { - SERVER.syncPlayerInternal(player, true); + SyncController.syncPlayerInternal(player, true); } - private void syncPlayerInternal(EntityPlayerMP player, boolean includePostPackets) { + private static void syncPlayerInternal(EntityPlayerMP player, boolean includePostPackets) { PlayerSyncState state = playerSyncState.computeIfAbsent(player.getUniqueID(), PlayerSyncState::new); for (EnumSyncType type : LOGIN_SYNC_TYPES) { @@ -161,7 +127,7 @@ public class SyncController { } public static void beginLogin(EntityPlayerMP player) { - SERVER.playerSyncState.computeIfAbsent(player.getUniqueID(), PlayerSyncState::new); + playerSyncState.computeIfAbsent(player.getUniqueID(), PlayerSyncState::new); PacketHandler.Instance.sendToPlayer( new LoginPacket(getServerCacheKey(), getServerRevisionSnapshot()), player @@ -175,7 +141,7 @@ public class SyncController { Map clientRevisions ) { String currentServerKey = getServerCacheKey(); - PlayerSyncState state = SERVER.playerSyncState.computeIfAbsent(player.getUniqueID(), PlayerSyncState::new); + PlayerSyncState state = playerSyncState.computeIfAbsent(player.getUniqueID(), PlayerSyncState::new); if (!currentServerKey.equals(serverKey)) { state.reset(); @@ -213,7 +179,7 @@ public class SyncController { private static EnumMap getServerRevisionSnapshot() { EnumMap snapshot = new EnumMap<>(EnumSyncType.class); for (EnumSyncType type : LOGIN_SYNC_TYPES) { - SyncCacheEntry entry = SERVER.cacheEntries.get(type); + SyncCacheEntry entry = cacheEntries.get(type); if (entry != null) { snapshot.put(type, entry.getRevisionValue()); } @@ -237,7 +203,7 @@ public class SyncController { } public static int getCurrentRevision(EnumSyncType type) { - SyncCacheEntry entry = SERVER.cacheEntries.get(type); + SyncCacheEntry entry = cacheEntries.get(type); return entry == null ? -1 : entry.getRevisionValue(); } @@ -829,12 +795,12 @@ public class SyncController { } } - private void registerCache(EnumSyncType type, Supplier supplier) { + private static void registerCache(EnumSyncType type, Supplier supplier) { cacheEntries.put(type, new SyncCacheEntry(type, supplier)); } private static CachedSyncPayload rebuildNow(EnumSyncType type) { - SyncCacheEntry entry = SERVER.cacheEntries.get(type); + SyncCacheEntry entry = cacheEntries.get(type); if (entry == null) { return null; } @@ -846,7 +812,7 @@ public class SyncController { EnumSet targets = getInvalidationTargets(type); EnumMap revisions = new EnumMap<>(EnumSyncType.class); for (EnumSyncType target : targets) { - SyncCacheEntry entry = SERVER.cacheEntries.get(target); + SyncCacheEntry entry = cacheEntries.get(target); if (entry != null) { int newRevision = entry.invalidate(); revisions.put(target, newRevision); @@ -886,7 +852,7 @@ public class SyncController { if (revision < 0) { return; } - for (PlayerSyncState state : SERVER.playerSyncState.values()) { + for (PlayerSyncState state : playerSyncState.values()) { state.updateRevision(type, revision); } } @@ -895,7 +861,7 @@ public class SyncController { if (revisions.isEmpty()) { return; } - for (PlayerSyncState state : SERVER.playerSyncState.values()) { + for (PlayerSyncState state : playerSyncState.values()) { for (Map.Entry entry : revisions.entrySet()) { state.updateRevision(entry.getKey(), entry.getValue()); } diff --git a/src/main/java/noppes/npcs/CustomNpcs.java b/src/main/java/noppes/npcs/CustomNpcs.java index b4ec0a43c..68eaa60b7 100644 --- a/src/main/java/noppes/npcs/CustomNpcs.java +++ b/src/main/java/noppes/npcs/CustomNpcs.java @@ -317,7 +317,6 @@ public class CustomNpcs { @EventHandler public void stopped(FMLServerStoppedEvent event) { - SyncController.resetServerState(); ServerCloneController.Instance = null; GlobalDataController.Instance.saveData(); ScriptController.Instance.saveForgeScripts(); From 11e326de48db543dfb38ec406aa099f06ef2d4df Mon Sep 17 00:00:00 2001 From: Kam Date: Thu, 6 Nov 2025 00:20:22 -0500 Subject: [PATCH 08/38] Add mount role for NPCs with rider controls --- src/main/java/noppes/npcs/DataAdvanced.java | 9 +- .../client/gui/mainmenu/GuiNpcAdvanced.java | 5 +- .../npcs/client/gui/roles/GuiNpcMount.java | 109 +++++++++++++++++ .../noppes/npcs/constants/EnumRoleType.java | 2 +- .../noppes/npcs/entity/EntityNPCFlying.java | 45 +++++++ .../npcs/entity/EntityNPCInterface.java | 102 +++++++++++++++- .../java/noppes/npcs/roles/RoleMount.java | 112 ++++++++++++++++++ .../assets/customnpcs/lang/cs_CZ.lang | 6 + .../assets/customnpcs/lang/de_DE.lang | 6 + .../assets/customnpcs/lang/en_US.lang | 6 + .../assets/customnpcs/lang/es_AR.lang | 6 + .../assets/customnpcs/lang/es_MX.lang | 6 + .../assets/customnpcs/lang/fr_FR.lang | 6 + .../assets/customnpcs/lang/hu_HU.lang | 6 + .../assets/customnpcs/lang/id_ID.lang | 6 + .../assets/customnpcs/lang/ko_KR.lang | 6 + .../assets/customnpcs/lang/nl_NL.lang | 8 +- .../assets/customnpcs/lang/pl_PL.lang | 6 + .../assets/customnpcs/lang/pt_BR.lang | 6 + .../assets/customnpcs/lang/ru_RU.lang | 6 + .../assets/customnpcs/lang/sv_SE.lang | 6 + .../assets/customnpcs/lang/zh_CN.lang | 6 + .../assets/customnpcs/lang/zh_TW.lang | 6 + 23 files changed, 476 insertions(+), 6 deletions(-) create mode 100644 src/main/java/noppes/npcs/client/gui/roles/GuiNpcMount.java create mode 100644 src/main/java/noppes/npcs/roles/RoleMount.java diff --git a/src/main/java/noppes/npcs/DataAdvanced.java b/src/main/java/noppes/npcs/DataAdvanced.java index cc9e08614..45ecfe4a8 100644 --- a/src/main/java/noppes/npcs/DataAdvanced.java +++ b/src/main/java/noppes/npcs/DataAdvanced.java @@ -21,6 +21,7 @@ import noppes.npcs.roles.JobSpawner; import noppes.npcs.roles.RoleBank; import noppes.npcs.roles.RoleCompanion; import noppes.npcs.roles.RoleFollower; +import noppes.npcs.roles.RoleMount; import noppes.npcs.roles.RolePostman; import noppes.npcs.roles.RoleTrader; import noppes.npcs.roles.RoleTransporter; @@ -196,7 +197,11 @@ public class DataAdvanced { if (EnumRoleType.values().length <= i) { i -= 2; } - role = EnumRoleType.values()[i]; + EnumRoleType newRole = EnumRoleType.values()[i]; + if (npc.roleInterface instanceof RoleMount && newRole != EnumRoleType.Mount) { + ((RoleMount) npc.roleInterface).onDisable(); + } + role = newRole; if (role == EnumRoleType.None) npc.roleInterface = null; else if (role == EnumRoleType.Bank && !(npc.roleInterface instanceof RoleBank)) @@ -211,6 +216,8 @@ public class DataAdvanced { npc.roleInterface = new RoleTransporter(npc); else if (role == EnumRoleType.Companion && !(npc.roleInterface instanceof RoleCompanion)) npc.roleInterface = new RoleCompanion(npc); + else if (role == EnumRoleType.Mount && !(npc.roleInterface instanceof RoleMount)) + npc.roleInterface = new RoleMount(npc); } public void setJob(int i) { diff --git a/src/main/java/noppes/npcs/client/gui/mainmenu/GuiNpcAdvanced.java b/src/main/java/noppes/npcs/client/gui/mainmenu/GuiNpcAdvanced.java index d7b5ac919..6452145a6 100644 --- a/src/main/java/noppes/npcs/client/gui/mainmenu/GuiNpcAdvanced.java +++ b/src/main/java/noppes/npcs/client/gui/mainmenu/GuiNpcAdvanced.java @@ -25,6 +25,7 @@ import noppes.npcs.client.gui.roles.GuiNpcFollowerJob; import noppes.npcs.client.gui.roles.GuiNpcGuard; import noppes.npcs.client.gui.roles.GuiNpcHealer; import noppes.npcs.client.gui.roles.GuiNpcSpawner; +import noppes.npcs.client.gui.roles.GuiNpcMount; import noppes.npcs.client.gui.roles.GuiNpcTransporter; import noppes.npcs.client.gui.util.GuiNPCInterface2; import noppes.npcs.client.gui.util.GuiNpcButton; @@ -47,7 +48,7 @@ public class GuiNpcAdvanced extends GuiNPCInterface2 implements IGuiData { super.initGui(); int y = guiTop + 10; this.addButton(new GuiNpcButton(3, guiLeft + 85 + 160, y, 52, 20, "selectServer.edit")); - this.addButton(new GuiNpcButton(8, guiLeft + 85, y, 155, 20, new String[]{"role.none", "role.trader", "role.follower", "role.bank", "role.transporter", "role.mailman", NoppesStringUtils.translate("role.companion", "(WIP)")}, npc.advanced.role.ordinal())); + this.addButton(new GuiNpcButton(8, guiLeft + 85, y, 155, 20, new String[]{"role.none", "role.trader", "role.follower", "role.bank", "role.transporter", "role.mailman", NoppesStringUtils.translate("role.companion", "(WIP)"), "role.mount"}, npc.advanced.role.ordinal())); getButton(3).setEnabled(npc.advanced.role != EnumRoleType.None && npc.advanced.role != EnumRoleType.Postman); this.addButton(new GuiNpcButton(4, guiLeft + 85 + 160, y += 22, 52, 20, "selectServer.edit")); @@ -148,6 +149,8 @@ public class GuiNpcAdvanced extends GuiNPCInterface2 implements IGuiData { displayGuiScreen(new GuiNpcTransporter(npc)); else if (npc.advanced.role == EnumRoleType.Companion) displayGuiScreen(new GuiNpcCompanion(npc)); + else if (npc.advanced.role == EnumRoleType.Mount) + displayGuiScreen(new GuiNpcMount(npc)); } else if (compound.hasKey("JobData")) { if (npc.jobInterface != null) npc.jobInterface.readFromNBT(compound); diff --git a/src/main/java/noppes/npcs/client/gui/roles/GuiNpcMount.java b/src/main/java/noppes/npcs/client/gui/roles/GuiNpcMount.java new file mode 100644 index 000000000..57f39a6d8 --- /dev/null +++ b/src/main/java/noppes/npcs/client/gui/roles/GuiNpcMount.java @@ -0,0 +1,109 @@ +package noppes.npcs.client.gui.roles; + +import kamkeel.npcs.network.PacketClient; +import kamkeel.npcs.network.packets.request.role.RoleSavePacket; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.StatCollector; +import noppes.npcs.client.gui.util.GuiNPCInterface2; +import noppes.npcs.client.gui.util.GuiNpcButton; +import noppes.npcs.client.gui.util.GuiNpcLabel; +import noppes.npcs.client.gui.util.GuiNpcTextField; +import noppes.npcs.client.gui.util.ITextfieldListener; +import noppes.npcs.entity.EntityNPCInterface; +import noppes.npcs.roles.RoleMount; + +import java.util.Locale; + +public class GuiNpcMount extends GuiNPCInterface2 implements ITextfieldListener { + + private final RoleMount role; + + public GuiNpcMount(EntityNPCInterface npc) { + super(npc); + this.role = npc.roleInterface instanceof RoleMount ? (RoleMount) npc.roleInterface : null; + } + + @Override + public void initGui() { + super.initGui(); + if (role == null) { + close(); + return; + } + + int y = guiTop + 6; + addOffsetRow(0, "role.mount.offsetx", role.getOffsetX(), y); + y += 24; + addOffsetRow(1, "role.mount.offsety", role.getOffsetY(), y); + y += 24; + addOffsetRow(2, "role.mount.offsetz", role.getOffsetZ(), y); + + addButton(new GuiNpcButton(3, guiLeft + 4, y + 26, 80, 20, "role.mount.reset")); + + GuiNpcLabel speedLabel = new GuiNpcLabel(4, "", guiLeft + 4, y + 6); + speedLabel.label = StatCollector.translateToLocalFormatted("role.mount.walkspeed", npc.ais.getWalkingSpeed()); + addLabel(speedLabel); + } + + private void addOffsetRow(int id, String translationKey, float value, int y) { + addLabel(new GuiNpcLabel(id, translationKey, guiLeft + 4, y + 5)); + GuiNpcTextField field = new GuiNpcTextField(id, this, guiLeft + 140, y, 80, 20, format(value)); + field.floatsOnly = true; + field.setMinMaxDefaultFloat(-5.0F, 5.0F, 0.0F); + addTextField(field); + } + + private String format(float value) { + return String.format(Locale.ROOT, "%.2f", value); + } + + @Override + public void buttonEvent(GuiButton guibutton) { + if (role == null) + return; + if (guibutton.id == 3) { + role.resetOffsets(); + initGui(); + } + } + + @Override + public void unFocused(GuiNpcTextField textfield) { + if (role == null) + return; + float value = textfield.isFloat() ? textfield.getFloat() : 0.0F; + switch (textfield.id) { + case 0: + role.setOffsetX(value); + break; + case 1: + role.setOffsetY(value); + break; + case 2: + role.setOffsetZ(value); + break; + } + textfield.setText(format(valueForField(textfield.id))); + } + + private float valueForField(int id) { + switch (id) { + case 0: + return role.getOffsetX(); + case 1: + return role.getOffsetY(); + case 2: + return role.getOffsetZ(); + default: + return 0.0F; + } + } + + @Override + public void save() { + if (role != null) { + PacketClient.sendClient(new RoleSavePacket(role.writeToNBT(new NBTTagCompound()))); + } + } +} diff --git a/src/main/java/noppes/npcs/constants/EnumRoleType.java b/src/main/java/noppes/npcs/constants/EnumRoleType.java index a82f4b607..624658fb3 100644 --- a/src/main/java/noppes/npcs/constants/EnumRoleType.java +++ b/src/main/java/noppes/npcs/constants/EnumRoleType.java @@ -2,5 +2,5 @@ package noppes.npcs.constants; public enum EnumRoleType { - None, Trader, Follower, Bank, Transporter, Postman, Companion + None, Trader, Follower, Bank, Transporter, Postman, Companion, Mount } diff --git a/src/main/java/noppes/npcs/entity/EntityNPCFlying.java b/src/main/java/noppes/npcs/entity/EntityNPCFlying.java index 92ad030c5..bba547c61 100644 --- a/src/main/java/noppes/npcs/entity/EntityNPCFlying.java +++ b/src/main/java/noppes/npcs/entity/EntityNPCFlying.java @@ -40,7 +40,10 @@ public abstract class EntityNPCFlying extends EntityNPCInterface { super.onLivingUpdate(); } + @Override public void moveEntityWithHeading(float p_70612_1_, float p_70612_2_) { + if (handleMountedMovement(p_70612_1_, p_70612_2_)) + return; if (!this.canFly() || this.hurtTime != 0 || !this.canBreathe()) { super.moveEntityWithHeading(p_70612_1_, p_70612_2_); return; @@ -92,4 +95,46 @@ public abstract class EntityNPCFlying extends EntityNPCInterface { this.limbSwingAmount += (distance - this.limbSwingAmount) * 0.4F; this.limbSwing += this.limbSwingAmount; } + + @Override + protected void doMountedMovement(float strafe, float forward) { + if (!this.canFly() || this.hurtTime != 0 || !this.canBreathe()) { + super.doMountedMovement(strafe, forward); + return; + } + + boolean aboveLimit = false; + double heightOffGround = this.posY - this.worldObj.getTopSolidOrLiquidBlock((int) this.posX, (int) this.posZ); + if (heightOffGround < 0) { + Vec3 pos = Vec3.createVectorHelper(this.posX, this.posY, this.posZ); + Vec3 posLimit = Vec3.createVectorHelper(this.posX, this.posY - this.ais.flyHeightLimit, this.posZ); + MovingObjectPosition mob = this.worldObj.rayTraceBlocks(pos, posLimit, true); + if (mob == null || mob.typeOfHit == MovingObjectPosition.MovingObjectType.MISS) { + aboveLimit = true; + } + } else if (heightOffGround > this.ais.flyHeightLimit) { + aboveLimit = true; + } + if (aboveLimit && this.ais.hasFlyLimit || (heightOffGround < Math.ceil(this.height) && this.motionY == 0)) { + this.flyLimitAllow = false; + if (!this.getNavigator().noPath() && this.motionY > 0) { + this.motionY = 0; + } else { + super.doMountedMovement(strafe, forward); + return; + } + } + this.flyLimitAllow = true; + + double d3 = this.motionY; + super.doMountedMovement(strafe, forward); + this.motionY = d3; + + if (this.getNavigator().noPath()) { + this.motionY = -Math.abs(this.ais.flyGravity); + } + + this.updateLimbSwing(); + this.velocityChanged = true; + } } diff --git a/src/main/java/noppes/npcs/entity/EntityNPCInterface.java b/src/main/java/noppes/npcs/entity/EntityNPCInterface.java index c72aa7117..fb125982e 100644 --- a/src/main/java/noppes/npcs/entity/EntityNPCInterface.java +++ b/src/main/java/noppes/npcs/entity/EntityNPCInterface.java @@ -152,6 +152,7 @@ import noppes.npcs.roles.JobFollower; import noppes.npcs.roles.JobInterface; import noppes.npcs.roles.RoleCompanion; import noppes.npcs.roles.RoleFollower; +import noppes.npcs.roles.RoleMount; import noppes.npcs.roles.RoleInterface; import noppes.npcs.scripted.NpcAPI; import noppes.npcs.scripted.entity.ScriptNpc; @@ -1753,6 +1754,11 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti roleInterface.writeToNBT(bard); compound.setTag("Companion", bard); } + if (advanced.role == EnumRoleType.Mount) { + NBTTagCompound mount = new NBTTagCompound(); + roleInterface.writeToNBT(mount); + compound.setTag("Mount", mount); + } if (this instanceof EntityCustomNpc) { compound.setTag("ModelData", ((EntityCustomNpc) this).modelData.writeToNBT()); @@ -1790,6 +1796,10 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti NBTTagCompound companion = compound.getCompoundTag("Companion"); roleInterface.readFromNBT(companion); } + if (advanced.role == EnumRoleType.Mount) { + NBTTagCompound mount = compound.getCompoundTag("Mount"); + roleInterface.readFromNBT(mount); + } if (this instanceof EntityCustomNpc) { ((EntityCustomNpc) this).modelData.readFromNBT(compound.getCompoundTag("ModelData")); } @@ -2056,15 +2066,86 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti } @Override - public void moveEntityWithHeading(float p_70612_1_, float p_70612_2_) { + public void moveEntityWithHeading(float strafe, float forward) { + if (handleMountedMovement(strafe, forward)) { + return; + } double d0 = this.posX; double d1 = this.posY; double d2 = this.posZ; - super.moveEntityWithHeading(p_70612_1_, p_70612_2_); + super.moveEntityWithHeading(strafe, forward); if (advanced.role == EnumRoleType.Companion && !isRemote()) ((RoleCompanion) roleInterface).addMovementStat(this.posX - d0, this.posY - d1, this.posZ - d2); } + protected boolean handleMountedMovement(float strafe, float forward) { + if (advanced.role != EnumRoleType.Mount || !(roleInterface instanceof RoleMount)) + return false; + if (!(riddenByEntity instanceof EntityLivingBase)) + return false; + + EntityLivingBase rider = (EntityLivingBase) riddenByEntity; + this.prevRotationYaw = this.rotationYaw = rider.rotationYaw; + this.rotationPitch = rider.rotationPitch * 0.5F; + this.setRotation(this.rotationYaw, this.rotationPitch); + this.renderYawOffset = this.rotationYaw; + this.rotationYawHead = this.rotationYaw; + + float controlledStrafe = rider.moveStrafing * 0.5F; + float controlledForward = rider.moveForward; + if (controlledForward <= 0.0F) { + controlledForward *= 0.25F; + } + + float moveSpeed = getSpeed(); + if (rider.isSprinting()) { + moveSpeed *= 1.2F; + } + + this.setSprinting(rider.isSprinting()); + this.setAIMoveSpeed(moveSpeed); + this.stepHeight = 1.0F; + this.jumpMovementFactor = this.getAIMoveSpeed() * 0.1F; + + this.getNavigator().clearPathEntity(); + this.setAttackTarget(null); + this.setRevengeTarget(null); + + this.fallDistance = 0.0F; + rider.fallDistance = 0.0F; + + applyMountedVerticalMotion(rider, moveSpeed); + doMountedMovement(controlledStrafe, controlledForward); + + return true; + } + + protected void doMountedMovement(float strafe, float forward) { + super.moveEntityWithHeading(strafe, forward); + } + + protected void applyMountedVerticalMotion(EntityLivingBase rider, float moveSpeed) { + if (canFly()) { + double vertical = Math.max(0.1D, moveSpeed * 0.6D); + if (rider.isJumping()) { + this.motionY = Math.min(this.motionY + vertical, vertical); + this.isAirBorne = true; + } else { + float pitch = rider.rotationPitch; + if (pitch < -10F) { + this.motionY = vertical; + } else if (pitch > 10F) { + this.motionY = -vertical; + } else if (!this.onGround) { + this.motionY *= 0.6D; + } + } + } else if (rider.isJumping() && this.onGround) { + this.motionY = 0.5D; + this.isAirBorne = true; + } + } + @Override public boolean allowLeashing() { return false; @@ -2075,6 +2156,23 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti return false; } + @Override + public void updateRiderPosition() { + super.updateRiderPosition(); + if (advanced.role == EnumRoleType.Mount && roleInterface instanceof RoleMount && riddenByEntity != null) { + RoleMount mount = (RoleMount) roleInterface; + double yaw = Math.toRadians(this.renderYawOffset); + double offsetX = mount.getOffsetX(); + double offsetZ = mount.getOffsetZ(); + double rotatedX = offsetX * Math.cos(yaw) - offsetZ * Math.sin(yaw); + double rotatedZ = offsetX * Math.sin(yaw) + offsetZ * Math.cos(yaw); + double offsetY = mount.getOffsetY(); + riddenByEntity.setPosition(this.posX + rotatedX, + this.posY + this.getMountedYOffset() + riddenByEntity.getYOffset() + offsetY, + this.posZ + rotatedZ); + } + } + // Model Types: 0: Steve 64x32, 1: Steve 64x64, 2: Alex 64x64 public int getModelType() { return this.display.modelType; diff --git a/src/main/java/noppes/npcs/roles/RoleMount.java b/src/main/java/noppes/npcs/roles/RoleMount.java new file mode 100644 index 000000000..455f8f329 --- /dev/null +++ b/src/main/java/noppes/npcs/roles/RoleMount.java @@ -0,0 +1,112 @@ +package noppes.npcs.roles; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.nbt.NBTTagCompound; +import noppes.npcs.entity.EntityNPCInterface; +import noppes.npcs.util.ValueUtil; + +public class RoleMount extends RoleInterface { + + private float offsetX; + private float offsetY; + private float offsetZ; + private boolean storedReturnToStart; + + public RoleMount(EntityNPCInterface npc) { + super(npc); + this.storedReturnToStart = npc.ais.returnToStart; + npc.ais.returnToStart = false; + } + + @Override + public NBTTagCompound writeToNBT(NBTTagCompound compound) { + compound.setFloat("MountOffsetX", offsetX); + compound.setFloat("MountOffsetY", offsetY); + compound.setFloat("MountOffsetZ", offsetZ); + compound.setBoolean("MountReturnFlag", storedReturnToStart); + return compound; + } + + @Override + public void readFromNBT(NBTTagCompound compound) { + setOffsetX(compound.getFloat("MountOffsetX")); + setOffsetY(compound.getFloat("MountOffsetY")); + setOffsetZ(compound.getFloat("MountOffsetZ")); + if (compound.hasKey("MountReturnFlag")) { + storedReturnToStart = compound.getBoolean("MountReturnFlag"); + } else { + storedReturnToStart = npc.ais.returnToStart; + } + npc.ais.returnToStart = false; + } + + @Override + public void interact(EntityPlayer player) { + if (player == null || npc.worldObj.isRemote) { + return; + } + if (npc.riddenByEntity != null && npc.riddenByEntity != player) { + return; + } + if (player.ridingEntity != null && player.ridingEntity != npc) { + player.mountEntity(null); + } + player.fallDistance = 0.0F; + npc.fallDistance = 0.0F; + player.mountEntity(npc); + npc.getNavigator().clearPathEntity(); + npc.setAttackTarget(null); + npc.setRevengeTarget(null); + } + + @Override + public boolean aiShouldExecute() { + if (npc.ais.returnToStart) { + npc.ais.returnToStart = false; + } + return false; + } + + public void setOffsetX(float value) { + this.offsetX = clamp(value); + } + + public void setOffsetY(float value) { + this.offsetY = clamp(value); + } + + public void setOffsetZ(float value) { + this.offsetZ = clamp(value); + } + + public float getOffsetX() { + return offsetX; + } + + public float getOffsetY() { + return offsetY; + } + + public float getOffsetZ() { + return offsetZ; + } + + private float clamp(float value) { + return ValueUtil.clamp(value, -5.0F, 5.0F); + } + + public void resetOffsets() { + offsetX = 0.0F; + offsetY = 0.0F; + offsetZ = 0.0F; + } + + public void onDisable() { + npc.ais.returnToStart = storedReturnToStart; + } + + @Override + public void delete() { + onDisable(); + } +} diff --git a/src/main/resources/assets/customnpcs/lang/cs_CZ.lang b/src/main/resources/assets/customnpcs/lang/cs_CZ.lang index f8f092bbf..e9cd29b99 100644 --- a/src/main/resources/assets/customnpcs/lang/cs_CZ.lang +++ b/src/main/resources/assets/customnpcs/lang/cs_CZ.lang @@ -439,3 +439,9 @@ item.npcOfficerHelmet.name=důstojnické brnění item.npcCrown.name=Koruna item.npcCrown2.name=Koruna key.npcInventory=NPC inventář +role.mount=Mount +role.mount.offsetx=Seat Offset X +role.mount.offsety=Seat Offset Y +role.mount.offsetz=Seat Offset Z +role.mount.reset=Reset Position +role.mount.walkspeed=Walking speed: %s diff --git a/src/main/resources/assets/customnpcs/lang/de_DE.lang b/src/main/resources/assets/customnpcs/lang/de_DE.lang index f88dd2769..38f3d3377 100644 --- a/src/main/resources/assets/customnpcs/lang/de_DE.lang +++ b/src/main/resources/assets/customnpcs/lang/de_DE.lang @@ -871,3 +871,9 @@ companion.stage=Bühne #German by DewTix key.npcInventory=NPC Inventar +role.mount=Mount +role.mount.offsetx=Seat Offset X +role.mount.offsety=Seat Offset Y +role.mount.offsetz=Seat Offset Z +role.mount.reset=Reset Position +role.mount.walkspeed=Walking speed: %s diff --git a/src/main/resources/assets/customnpcs/lang/en_US.lang b/src/main/resources/assets/customnpcs/lang/en_US.lang index 57dc34cdc..c3edc664e 100644 --- a/src/main/resources/assets/customnpcs/lang/en_US.lang +++ b/src/main/resources/assets/customnpcs/lang/en_US.lang @@ -417,6 +417,12 @@ role.transporter=Transporter role.mailman=Mailman role.marketname=Linked Marketname role.companion=Companion +role.mount=Mount +role.mount.offsetx=Seat Offset X +role.mount.offsety=Seat Offset Y +role.mount.offsetz=Seat Offset Z +role.mount.reset=Reset Position +role.mount.walkspeed=Walking speed: %s trader.insufficient=Insufficient Currency trader.sufficient=Sufficient Currency diff --git a/src/main/resources/assets/customnpcs/lang/es_AR.lang b/src/main/resources/assets/customnpcs/lang/es_AR.lang index 27c79cd78..5ca50fc49 100644 --- a/src/main/resources/assets/customnpcs/lang/es_AR.lang +++ b/src/main/resources/assets/customnpcs/lang/es_AR.lang @@ -499,3 +499,9 @@ item.npcSlingshot.name=Slingshot item.npcMusket.name=Mosquete item.npcExcalibur.name=Excalibur key.npcInventory=Inventario de NPC +role.mount=Mount +role.mount.offsetx=Seat Offset X +role.mount.offsety=Seat Offset Y +role.mount.offsetz=Seat Offset Z +role.mount.reset=Reset Position +role.mount.walkspeed=Walking speed: %s diff --git a/src/main/resources/assets/customnpcs/lang/es_MX.lang b/src/main/resources/assets/customnpcs/lang/es_MX.lang index 53bca03af..fd58878af 100644 --- a/src/main/resources/assets/customnpcs/lang/es_MX.lang +++ b/src/main/resources/assets/customnpcs/lang/es_MX.lang @@ -499,3 +499,9 @@ item.npcSlingshot.name=Slingshot item.npcMusket.name=Mosquete item.npcExcalibur.name=Excalibur key.npcInventory=Inventario de NPC +role.mount=Mount +role.mount.offsetx=Seat Offset X +role.mount.offsety=Seat Offset Y +role.mount.offsetz=Seat Offset Z +role.mount.reset=Reset Position +role.mount.walkspeed=Walking speed: %s diff --git a/src/main/resources/assets/customnpcs/lang/fr_FR.lang b/src/main/resources/assets/customnpcs/lang/fr_FR.lang index b22a75ac3..6229aa1b9 100644 --- a/src/main/resources/assets/customnpcs/lang/fr_FR.lang +++ b/src/main/resources/assets/customnpcs/lang/fr_FR.lang @@ -541,3 +541,9 @@ item.npcSlingshot.name=Lance-Pièrre item.npcMusket.name=Mousquet item.npcExcalibur.name=Excalibur key.npcInventory=Inventaire Pnj +role.mount=Mount +role.mount.offsetx=Seat Offset X +role.mount.offsety=Seat Offset Y +role.mount.offsetz=Seat Offset Z +role.mount.reset=Reset Position +role.mount.walkspeed=Walking speed: %s diff --git a/src/main/resources/assets/customnpcs/lang/hu_HU.lang b/src/main/resources/assets/customnpcs/lang/hu_HU.lang index 1a9fc71f5..e3112252c 100644 --- a/src/main/resources/assets/customnpcs/lang/hu_HU.lang +++ b/src/main/resources/assets/customnpcs/lang/hu_HU.lang @@ -541,3 +541,9 @@ item.npcSlingshot.name=Csúzli item.npcMusket.name=Muskéta item.npcExcalibur.name=Excalibur key.npcInventory=NPC felszerelés +role.mount=Mount +role.mount.offsetx=Seat Offset X +role.mount.offsety=Seat Offset Y +role.mount.offsetz=Seat Offset Z +role.mount.reset=Reset Position +role.mount.walkspeed=Walking speed: %s diff --git a/src/main/resources/assets/customnpcs/lang/id_ID.lang b/src/main/resources/assets/customnpcs/lang/id_ID.lang index a5952d7ab..a7de30101 100644 --- a/src/main/resources/assets/customnpcs/lang/id_ID.lang +++ b/src/main/resources/assets/customnpcs/lang/id_ID.lang @@ -316,3 +316,9 @@ item.npcAmethyst.name=Permata Amethyst item.npcRuby.name=Permata Rubi +role.mount=Mount +role.mount.offsetx=Seat Offset X +role.mount.offsety=Seat Offset Y +role.mount.offsetz=Seat Offset Z +role.mount.reset=Reset Position +role.mount.walkspeed=Walking speed: %s diff --git a/src/main/resources/assets/customnpcs/lang/ko_KR.lang b/src/main/resources/assets/customnpcs/lang/ko_KR.lang index 993006c34..64b370445 100644 --- a/src/main/resources/assets/customnpcs/lang/ko_KR.lang +++ b/src/main/resources/assets/customnpcs/lang/ko_KR.lang @@ -796,3 +796,9 @@ enchantment.infinite=무한 enchantment.poison=독 key.npcInventory=NPC 인벤토리 +role.mount=Mount +role.mount.offsetx=Seat Offset X +role.mount.offsety=Seat Offset Y +role.mount.offsetz=Seat Offset Z +role.mount.reset=Reset Position +role.mount.walkspeed=Walking speed: %s diff --git a/src/main/resources/assets/customnpcs/lang/nl_NL.lang b/src/main/resources/assets/customnpcs/lang/nl_NL.lang index 6756f197f..6998ad909 100644 --- a/src/main/resources/assets/customnpcs/lang/nl_NL.lang +++ b/src/main/resources/assets/customnpcs/lang/nl_NL.lang @@ -230,4 +230,10 @@ item.npcCrown2.name=Kroon item.npcNinjaHead.name=Ninja Helm item.npcNinjaChest.name=Ninja Borst -item.npcNinjaPants.name=Ninja Broek \ No newline at end of file +item.npcNinjaPants.name=Ninja Broek +role.mount=Mount +role.mount.offsetx=Seat Offset X +role.mount.offsety=Seat Offset Y +role.mount.offsetz=Seat Offset Z +role.mount.reset=Reset Position +role.mount.walkspeed=Walking speed: %s diff --git a/src/main/resources/assets/customnpcs/lang/pl_PL.lang b/src/main/resources/assets/customnpcs/lang/pl_PL.lang index 2f1047663..0526906d6 100644 --- a/src/main/resources/assets/customnpcs/lang/pl_PL.lang +++ b/src/main/resources/assets/customnpcs/lang/pl_PL.lang @@ -497,3 +497,9 @@ item.npcSlingshot.name=Proca item.npcMusket.name=Muszkiet item.npcExcalibur.name=Excalibur key.npcInventory=Inwentarz npc +role.mount=Mount +role.mount.offsetx=Seat Offset X +role.mount.offsety=Seat Offset Y +role.mount.offsetz=Seat Offset Z +role.mount.reset=Reset Position +role.mount.walkspeed=Walking speed: %s diff --git a/src/main/resources/assets/customnpcs/lang/pt_BR.lang b/src/main/resources/assets/customnpcs/lang/pt_BR.lang index c3bbd3c26..3e5411213 100644 --- a/src/main/resources/assets/customnpcs/lang/pt_BR.lang +++ b/src/main/resources/assets/customnpcs/lang/pt_BR.lang @@ -497,3 +497,9 @@ item.npcSlingshot.name=Estilingue item.npcMusket.name=Mosquete item.npcExcalibur.name=Excalibur key.npcInventory=Inventário NPC +role.mount=Mount +role.mount.offsetx=Seat Offset X +role.mount.offsety=Seat Offset Y +role.mount.offsetz=Seat Offset Z +role.mount.reset=Reset Position +role.mount.walkspeed=Walking speed: %s diff --git a/src/main/resources/assets/customnpcs/lang/ru_RU.lang b/src/main/resources/assets/customnpcs/lang/ru_RU.lang index 0b286b3ae..f190a84f0 100644 --- a/src/main/resources/assets/customnpcs/lang/ru_RU.lang +++ b/src/main/resources/assets/customnpcs/lang/ru_RU.lang @@ -1020,3 +1020,9 @@ questgui.reject=Отменить questgui.accept=Принять key.npcInventory=Инвентарь NPC +role.mount=Mount +role.mount.offsetx=Seat Offset X +role.mount.offsety=Seat Offset Y +role.mount.offsetz=Seat Offset Z +role.mount.reset=Reset Position +role.mount.walkspeed=Walking speed: %s diff --git a/src/main/resources/assets/customnpcs/lang/sv_SE.lang b/src/main/resources/assets/customnpcs/lang/sv_SE.lang index 91be6d6bc..adf43d88c 100644 --- a/src/main/resources/assets/customnpcs/lang/sv_SE.lang +++ b/src/main/resources/assets/customnpcs/lang/sv_SE.lang @@ -307,3 +307,9 @@ item.npcKey.name=Key item.npcPendant.name=Pendant item.npcBlueprint.name=Blueprint item.npcRing.name=Ring +role.mount=Mount +role.mount.offsetx=Seat Offset X +role.mount.offsety=Seat Offset Y +role.mount.offsetz=Seat Offset Z +role.mount.reset=Reset Position +role.mount.walkspeed=Walking speed: %s diff --git a/src/main/resources/assets/customnpcs/lang/zh_CN.lang b/src/main/resources/assets/customnpcs/lang/zh_CN.lang index 7831cf590..41799f778 100644 --- a/src/main/resources/assets/customnpcs/lang/zh_CN.lang +++ b/src/main/resources/assets/customnpcs/lang/zh_CN.lang @@ -1243,3 +1243,9 @@ hitbox.width=宽度比例 hitbox.height=身高比例 key.npcInventory=NPC物品栏 +role.mount=Mount +role.mount.offsetx=Seat Offset X +role.mount.offsety=Seat Offset Y +role.mount.offsetz=Seat Offset Z +role.mount.reset=Reset Position +role.mount.walkspeed=Walking speed: %s diff --git a/src/main/resources/assets/customnpcs/lang/zh_TW.lang b/src/main/resources/assets/customnpcs/lang/zh_TW.lang index 168709725..6b864af53 100644 --- a/src/main/resources/assets/customnpcs/lang/zh_TW.lang +++ b/src/main/resources/assets/customnpcs/lang/zh_TW.lang @@ -313,3 +313,9 @@ item.npcBronzeIngot.name=銅錠 item.npcSaphire.name=藍寶石 item.npcAmethyst.name=紫水晶 item.npcRuby.name=紅寶石 +role.mount=Mount +role.mount.offsetx=Seat Offset X +role.mount.offsety=Seat Offset Y +role.mount.offsetz=Seat Offset Z +role.mount.reset=Reset Position +role.mount.walkspeed=Walking speed: %s From d00c1cf15637f1601bbd3fc85c3ace71cca51e17 Mon Sep 17 00:00:00 2001 From: Kam Date: Thu, 6 Nov 2025 01:42:55 -0500 Subject: [PATCH 09/38] Refine mount role rider positioning and home behavior --- .../npcs/client/gui/mainmenu/GuiNpcAI.java | 16 ++++- .../npcs/entity/EntityNPCInterface.java | 69 ++++++++++++++++--- .../java/noppes/npcs/roles/RoleMount.java | 10 +++ .../npcs/scripted/entity/ScriptNpc.java | 10 ++- 4 files changed, 91 insertions(+), 14 deletions(-) diff --git a/src/main/java/noppes/npcs/client/gui/mainmenu/GuiNpcAI.java b/src/main/java/noppes/npcs/client/gui/mainmenu/GuiNpcAI.java index 538d58b59..cc785f299 100644 --- a/src/main/java/noppes/npcs/client/gui/mainmenu/GuiNpcAI.java +++ b/src/main/java/noppes/npcs/client/gui/mainmenu/GuiNpcAI.java @@ -16,6 +16,7 @@ import noppes.npcs.client.gui.util.ITextfieldListener; import noppes.npcs.constants.EnumCombatPolicy; import noppes.npcs.constants.EnumNavType; import noppes.npcs.entity.EntityNPCInterface; +import noppes.npcs.roles.RoleMount; public class GuiNpcAI extends GuiNPCInterface2 implements ITextfieldListener, IGuiData { private DataAI ai; @@ -44,7 +45,12 @@ public class GuiNpcAI extends GuiNPCInterface2 implements ITextfieldListener, IG addLabel(new GuiNpcLabel(10, "ai.avoidwater", guiLeft + 150, guiTop + 17)); addButton(new GuiNpcButton(5, guiLeft + 230, guiTop + 10, 60, 20, new String[]{"gui.no", "gui.yes"}, npc.ais.avoidsWater ? 1 : 0)); addLabel(new GuiNpcLabel(11, "ai.return", guiLeft + 150, guiTop + 40)); - addButton(new GuiNpcButton(6, guiLeft + 230, guiTop + 35, 60, 20, new String[]{"gui.no", "gui.yes"}, npc.ais.returnToStart ? 1 : 0)); + int returnValue = npc.ais.returnToStart ? 1 : 0; + if (npc.advanced.role == EnumRoleType.Mount && npc.roleInterface instanceof RoleMount) { + returnValue = ((RoleMount) npc.roleInterface).getReturnToStartPreference() ? 1 : 0; + } + addButton(new GuiNpcButton(6, guiLeft + 230, guiTop + 35, 60, 20, new String[]{"gui.no", "gui.yes"}, returnValue)); + getButton(6).setEnabled(npc.advanced.role != EnumRoleType.Mount); addLabel(new GuiNpcLabel(17, "ai.leapattarget", guiLeft + 150, guiTop + 65)); addButton(new GuiNpcButton(15, guiLeft + 230, guiTop + 60, 60, 20, new String[]{"gui.no", "ai.jump", "ai.pounce"}, npc.ais.leapType)); addLabel(new GuiNpcLabel(15, "ai.indirect", guiLeft + 150, guiTop + 90)); @@ -132,7 +138,13 @@ public class GuiNpcAI extends GuiNPCInterface2 implements ITextfieldListener, IG } else if (button.id == 5) { npc.setAvoidWater(button.getValue() == 1); } else if (button.id == 6) { - ai.returnToStart = (button.getValue() == 1); + if (npc.advanced.role == EnumRoleType.Mount && npc.roleInterface instanceof RoleMount) { + ((RoleMount) npc.roleInterface).setReturnToStartPreference(button.getValue() == 1); + ai.returnToStart = false; + initGui(); + } else { + ai.returnToStart = (button.getValue() == 1); + } } else if (button.id == 7) { ai.canSwim = (button.getValue() == 1); } else if (button.id == 9) { diff --git a/src/main/java/noppes/npcs/entity/EntityNPCInterface.java b/src/main/java/noppes/npcs/entity/EntityNPCInterface.java index fb125982e..5602ba027 100644 --- a/src/main/java/noppes/npcs/entity/EntityNPCInterface.java +++ b/src/main/java/noppes/npcs/entity/EntityNPCInterface.java @@ -65,6 +65,7 @@ import net.minecraft.util.IChatComponent; import net.minecraft.util.IIcon; import net.minecraft.util.MathHelper; import net.minecraft.util.ResourceLocation; +import net.minecraft.util.Vec3; import net.minecraft.world.World; import net.minecraft.world.WorldServer; import net.minecraftforge.common.MinecraftForge; @@ -209,6 +210,8 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti public ResourceLocation textureLocation = null; + private Entity lastRider; + public EnumAnimation currentAnimation = EnumAnimation.NONE; public int npcVersion = VersionCompatibility.ModRev; @@ -469,6 +472,8 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti super.onLivingUpdate(); + handleMountRiderState(); + if (worldObj.isRemote) { if (roleInterface != null) { roleInterface.clientUpdate(); @@ -1479,10 +1484,13 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti this.spawnExplosionParticle(); delete(); } else { - if (this.riddenByEntity != null) + if (this.riddenByEntity != null) { + stabilizeDismountedRider(this.riddenByEntity); this.riddenByEntity.mountEntity(null); + } if (this.ridingEntity != null) this.mountEntity(null); + lastRider = null; setHealth(-1); setSprinting(false); getNavigator().clearPathEntity(); @@ -2158,21 +2166,60 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti @Override public void updateRiderPosition() { - super.updateRiderPosition(); if (advanced.role == EnumRoleType.Mount && roleInterface instanceof RoleMount && riddenByEntity != null) { RoleMount mount = (RoleMount) roleInterface; - double yaw = Math.toRadians(this.renderYawOffset); - double offsetX = mount.getOffsetX(); - double offsetZ = mount.getOffsetZ(); - double rotatedX = offsetX * Math.cos(yaw) - offsetZ * Math.sin(yaw); - double rotatedZ = offsetX * Math.sin(yaw) + offsetZ * Math.cos(yaw); - double offsetY = mount.getOffsetY(); - riddenByEntity.setPosition(this.posX + rotatedX, - this.posY + this.getMountedYOffset() + riddenByEntity.getYOffset() + offsetY, - this.posZ + rotatedZ); + double scale = Math.max(0.1D, this.display.modelSize / 5.0D); + Vec3 seat = Vec3.createVectorHelper(mount.getOffsetX(), 0.0D, mount.getOffsetZ() + (0.8D * scale)); + seat.rotateAroundY((float) Math.toRadians(-this.renderYawOffset)); + double px = this.posX + seat.xCoord; + double py = this.posY + this.getMountedYOffset() + riddenByEntity.getYOffset() + mount.getOffsetY(); + double pz = this.posZ + seat.zCoord; + + riddenByEntity.setPosition(px, py, pz); + + if (riddenByEntity instanceof EntityLivingBase) { + EntityLivingBase rider = (EntityLivingBase) riddenByEntity; + rider.prevRotationPitch = rider.rotationPitch; + rider.prevRotationYaw = rider.rotationYaw; + rider.setRenderYawOffset(this.renderYawOffset); + rider.setRotationYawHead(this.renderYawOffset); + } + } else { + super.updateRiderPosition(); } } + private void handleMountRiderState() { + if (worldObj.isRemote) { + if (advanced.role != EnumRoleType.Mount || !(roleInterface instanceof RoleMount)) { + lastRider = null; + } + return; + } + + if (advanced.role != EnumRoleType.Mount || !(roleInterface instanceof RoleMount)) { + lastRider = null; + return; + } + + Entity currentRider = this.riddenByEntity; + if (currentRider != lastRider) { + if (lastRider != null) { + stabilizeDismountedRider(lastRider); + } + lastRider = currentRider; + } + } + + private void stabilizeDismountedRider(Entity rider) { + rider.motionX = 0.0D; + rider.motionY = 0.0D; + rider.motionZ = 0.0D; + rider.fallDistance = 0.0F; + rider.velocityChanged = true; + rider.onGround = true; + } + // Model Types: 0: Steve 64x32, 1: Steve 64x64, 2: Alex 64x64 public int getModelType() { return this.display.modelType; diff --git a/src/main/java/noppes/npcs/roles/RoleMount.java b/src/main/java/noppes/npcs/roles/RoleMount.java index 455f8f329..e3ee3bf25 100644 --- a/src/main/java/noppes/npcs/roles/RoleMount.java +++ b/src/main/java/noppes/npcs/roles/RoleMount.java @@ -62,6 +62,7 @@ public class RoleMount extends RoleInterface { @Override public boolean aiShouldExecute() { if (npc.ais.returnToStart) { + storedReturnToStart = true; npc.ais.returnToStart = false; } return false; @@ -91,6 +92,15 @@ public class RoleMount extends RoleInterface { return offsetZ; } + public void setReturnToStartPreference(boolean value) { + this.storedReturnToStart = value; + npc.ais.returnToStart = false; + } + + public boolean getReturnToStartPreference() { + return storedReturnToStart; + } + private float clamp(float value) { return ValueUtil.clamp(value, -5.0F, 5.0F); } diff --git a/src/main/java/noppes/npcs/scripted/entity/ScriptNpc.java b/src/main/java/noppes/npcs/scripted/entity/ScriptNpc.java index 53f26f5d9..8b632f7ce 100644 --- a/src/main/java/noppes/npcs/scripted/entity/ScriptNpc.java +++ b/src/main/java/noppes/npcs/scripted/entity/ScriptNpc.java @@ -32,6 +32,7 @@ import noppes.npcs.controllers.data.DialogOption; import noppes.npcs.controllers.data.Line; import noppes.npcs.entity.EntityCustomNpc; import noppes.npcs.entity.EntityNPCInterface; +import noppes.npcs.roles.RoleMount; import noppes.npcs.scripted.NpcAPI; import noppes.npcs.scripted.constants.AnimationType; import noppes.npcs.scripted.constants.EntityType; @@ -269,13 +270,20 @@ public class ScriptNpc extends ScriptLiving imp * @param bo Whether or not the npc will try to return to his home position */ public void setReturnToHome(boolean bo) { - npc.ais.returnToStart = bo; + if (npc.advanced.role == EnumRoleType.Mount && npc.roleInterface instanceof RoleMount) { + ((RoleMount) npc.roleInterface).setReturnToStartPreference(bo); + } else { + npc.ais.returnToStart = bo; + } } /** * @return Whether or not the npc returns home */ public boolean getReturnToHome() { + if (npc.advanced.role == EnumRoleType.Mount && npc.roleInterface instanceof RoleMount) { + return ((RoleMount) npc.roleInterface).getReturnToStartPreference(); + } return npc.ais.returnToStart; } From 1754aa536a74f113aed20cacb841de563cdc9252 Mon Sep 17 00:00:00 2001 From: Kam Date: Thu, 6 Nov 2025 01:43:00 -0500 Subject: [PATCH 10/38] Fix mount rider jump handling --- .../java/noppes/npcs/client/gui/mainmenu/GuiNpcAI.java | 1 + .../java/noppes/npcs/entity/EntityNPCInterface.java | 10 ++++++---- src/main/resources/META-INF/customnpcs_at.cfg | 1 + 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/noppes/npcs/client/gui/mainmenu/GuiNpcAI.java b/src/main/java/noppes/npcs/client/gui/mainmenu/GuiNpcAI.java index cc785f299..6e0ebfa6e 100644 --- a/src/main/java/noppes/npcs/client/gui/mainmenu/GuiNpcAI.java +++ b/src/main/java/noppes/npcs/client/gui/mainmenu/GuiNpcAI.java @@ -15,6 +15,7 @@ import noppes.npcs.client.gui.util.IGuiData; import noppes.npcs.client.gui.util.ITextfieldListener; import noppes.npcs.constants.EnumCombatPolicy; import noppes.npcs.constants.EnumNavType; +import noppes.npcs.constants.EnumRoleType; import noppes.npcs.entity.EntityNPCInterface; import noppes.npcs.roles.RoleMount; diff --git a/src/main/java/noppes/npcs/entity/EntityNPCInterface.java b/src/main/java/noppes/npcs/entity/EntityNPCInterface.java index 5602ba027..5b5bb06c2 100644 --- a/src/main/java/noppes/npcs/entity/EntityNPCInterface.java +++ b/src/main/java/noppes/npcs/entity/EntityNPCInterface.java @@ -2135,7 +2135,7 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti protected void applyMountedVerticalMotion(EntityLivingBase rider, float moveSpeed) { if (canFly()) { double vertical = Math.max(0.1D, moveSpeed * 0.6D); - if (rider.isJumping()) { + if (rider.isJumping) { this.motionY = Math.min(this.motionY + vertical, vertical); this.isAirBorne = true; } else { @@ -2148,7 +2148,7 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti this.motionY *= 0.6D; } } - } else if (rider.isJumping() && this.onGround) { + } else if (rider.isJumping && this.onGround) { this.motionY = 0.5D; this.isAirBorne = true; } @@ -2181,8 +2181,10 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti EntityLivingBase rider = (EntityLivingBase) riddenByEntity; rider.prevRotationPitch = rider.rotationPitch; rider.prevRotationYaw = rider.rotationYaw; - rider.setRenderYawOffset(this.renderYawOffset); - rider.setRotationYawHead(this.renderYawOffset); + rider.prevRenderYawOffset = this.renderYawOffset; + rider.renderYawOffset = this.renderYawOffset; + rider.prevRotationYawHead = this.renderYawOffset; + rider.rotationYawHead = this.renderYawOffset; } } else { super.updateRiderPosition(); diff --git a/src/main/resources/META-INF/customnpcs_at.cfg b/src/main/resources/META-INF/customnpcs_at.cfg index 990efc151..5597bbba8 100644 --- a/src/main/resources/META-INF/customnpcs_at.cfg +++ b/src/main/resources/META-INF/customnpcs_at.cfg @@ -17,6 +17,7 @@ public net.minecraft.client.renderer.ItemRenderer field_78453_b # itemToRender public net.minecraft.client.renderer.entity.RenderPlayer func_77043_a(Lnet/minecraft/entity/EntityLivingBase;FFF)V # rotateCorpse #EntityLivingBase public net.minecraft.entity.EntityLivingBase func_82166_i()I # getArmSwingAnimationEnd +public net.minecraft.entity.EntityLivingBase field_70703_bu # isJumping # Minecraft public net.minecraft.client.Minecraft field_147126_aw # mcMusicTicker # MusicTicker From b035f4e18846181bb6b43d4fc21e3a31d4d90150 Mon Sep 17 00:00:00 2001 From: Kam Date: Thu, 6 Nov 2025 02:00:55 -0500 Subject: [PATCH 11/38] Smooth mount steering and halt dismount drift --- .../noppes/npcs/entity/EntityNPCFlying.java | 8 +-- .../npcs/entity/EntityNPCInterface.java | 59 ++++++++++++++++--- 2 files changed, 54 insertions(+), 13 deletions(-) diff --git a/src/main/java/noppes/npcs/entity/EntityNPCFlying.java b/src/main/java/noppes/npcs/entity/EntityNPCFlying.java index bba547c61..78a7ee23a 100644 --- a/src/main/java/noppes/npcs/entity/EntityNPCFlying.java +++ b/src/main/java/noppes/npcs/entity/EntityNPCFlying.java @@ -97,9 +97,9 @@ public abstract class EntityNPCFlying extends EntityNPCInterface { } @Override - protected void doMountedMovement(float strafe, float forward) { + protected void doMountedMovement(float strafe, float forward, float moveSpeed) { if (!this.canFly() || this.hurtTime != 0 || !this.canBreathe()) { - super.doMountedMovement(strafe, forward); + super.doMountedMovement(strafe, forward, moveSpeed); return; } @@ -120,14 +120,14 @@ public abstract class EntityNPCFlying extends EntityNPCInterface { if (!this.getNavigator().noPath() && this.motionY > 0) { this.motionY = 0; } else { - super.doMountedMovement(strafe, forward); + super.doMountedMovement(strafe, forward, moveSpeed); return; } } this.flyLimitAllow = true; double d3 = this.motionY; - super.doMountedMovement(strafe, forward); + super.doMountedMovement(strafe, forward, moveSpeed); this.motionY = d3; if (this.getNavigator().noPath()) { diff --git a/src/main/java/noppes/npcs/entity/EntityNPCInterface.java b/src/main/java/noppes/npcs/entity/EntityNPCInterface.java index 5b5bb06c2..56bc0df8c 100644 --- a/src/main/java/noppes/npcs/entity/EntityNPCInterface.java +++ b/src/main/java/noppes/npcs/entity/EntityNPCInterface.java @@ -68,6 +68,7 @@ import net.minecraft.util.ResourceLocation; import net.minecraft.util.Vec3; import net.minecraft.world.World; import net.minecraft.world.WorldServer; +import net.minecraftforge.common.ForgeHooks; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.util.FakePlayer; import net.minecraftforge.event.ServerChatEvent; @@ -1487,6 +1488,7 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti if (this.riddenByEntity != null) { stabilizeDismountedRider(this.riddenByEntity); this.riddenByEntity.mountEntity(null); + haltMountedMotion(); } if (this.ridingEntity != null) this.mountEntity(null); @@ -2105,15 +2107,14 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti controlledForward *= 0.25F; } - float moveSpeed = getSpeed(); + float moveSpeed = Math.max(0.1F, getSpeed()); if (rider.isSprinting()) { moveSpeed *= 1.2F; } this.setSprinting(rider.isSprinting()); - this.setAIMoveSpeed(moveSpeed); this.stepHeight = 1.0F; - this.jumpMovementFactor = this.getAIMoveSpeed() * 0.1F; + this.jumpMovementFactor = moveSpeed * 0.1F; this.getNavigator().clearPathEntity(); this.setAttackTarget(null); @@ -2122,22 +2123,37 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti this.fallDistance = 0.0F; rider.fallDistance = 0.0F; - applyMountedVerticalMotion(rider, moveSpeed); - doMountedMovement(controlledStrafe, controlledForward); + applyMountedVerticalMotion(rider, moveSpeed, controlledForward); + doMountedMovement(controlledStrafe, controlledForward, moveSpeed); return true; } - protected void doMountedMovement(float strafe, float forward) { - super.moveEntityWithHeading(strafe, forward); + protected void doMountedMovement(float strafe, float forward, float moveSpeed) { + this.moveStrafing = strafe; + this.moveForward = forward; + if (!worldObj.isRemote) { + this.setAIMoveSpeed(moveSpeed); + super.moveEntityWithHeading(strafe, forward); + this.prevLimbSwingAmount = this.limbSwingAmount; + double deltaX = this.posX - this.prevPosX; + double deltaZ = this.posZ - this.prevPosZ; + float limbSwingSpeed = MathHelper.sqrt_double(deltaX * deltaX + deltaZ * deltaZ) * 4.0F; + if (limbSwingSpeed > 1.0F) { + limbSwingSpeed = 1.0F; + } + this.limbSwingAmount += (limbSwingSpeed - this.limbSwingAmount) * 0.4F; + this.limbSwing += this.limbSwingAmount; + } } - protected void applyMountedVerticalMotion(EntityLivingBase rider, float moveSpeed) { + protected void applyMountedVerticalMotion(EntityLivingBase rider, float moveSpeed, float forward) { if (canFly()) { double vertical = Math.max(0.1D, moveSpeed * 0.6D); if (rider.isJumping) { this.motionY = Math.min(this.motionY + vertical, vertical); this.isAirBorne = true; + this.velocityChanged = true; } else { float pitch = rider.rotationPitch; if (pitch < -10F) { @@ -2149,8 +2165,17 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti } } } else if (rider.isJumping && this.onGround) { - this.motionY = 0.5D; + double jumpVelocity = MathHelper.clamp_double(0.4D + moveSpeed * 1.5D, 0.4D, 0.9D); + this.motionY = jumpVelocity; this.isAirBorne = true; + this.velocityChanged = true; + if (forward > 0.0F) { + float yawRadians = this.rotationYaw * (float) Math.PI / 180.0F; + double boost = 0.4D * forward; + this.motionX += -MathHelper.sin(yawRadians) * boost; + this.motionZ += MathHelper.cos(yawRadians) * boost; + } + ForgeHooks.onLivingJump(this); } } @@ -2208,6 +2233,7 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti if (currentRider != lastRider) { if (lastRider != null) { stabilizeDismountedRider(lastRider); + haltMountedMotion(); } lastRider = currentRider; } @@ -2222,6 +2248,21 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti rider.onGround = true; } + private void haltMountedMotion() { + this.motionX = 0.0D; + this.motionY = 0.0D; + this.motionZ = 0.0D; + this.velocityChanged = true; + this.isAirBorne = false; + this.setSprinting(false); + this.moveForward = 0.0F; + this.moveStrafing = 0.0F; + this.setAIMoveSpeed(0.0F); + this.limbSwingAmount = 0.0F; + this.limbSwing = 0.0F; + this.getNavigator().clearPathEntity(); + } + // Model Types: 0: Steve 64x32, 1: Steve 64x64, 2: Alex 64x64 public int getModelType() { return this.display.modelType; From bed60002c35ec58c866e7bc2536ee00f69cc22d1 Mon Sep 17 00:00:00 2001 From: Kam Date: Thu, 6 Nov 2025 02:14:11 -0500 Subject: [PATCH 12/38] Restrict mount riders and expose jump controls --- .../npcs/client/gui/roles/GuiNpcMount.java | 27 ++++++++- .../npcs/entity/EntityNPCInterface.java | 60 ++++++++++++++----- .../java/noppes/npcs/roles/RoleMount.java | 29 +++++++++ .../assets/customnpcs/lang/cs_CZ.lang | 2 + .../assets/customnpcs/lang/de_DE.lang | 2 + .../assets/customnpcs/lang/en_US.lang | 2 + .../assets/customnpcs/lang/es_AR.lang | 2 + .../assets/customnpcs/lang/es_MX.lang | 2 + .../assets/customnpcs/lang/fr_FR.lang | 2 + .../assets/customnpcs/lang/hu_HU.lang | 2 + .../assets/customnpcs/lang/id_ID.lang | 2 + .../assets/customnpcs/lang/ko_KR.lang | 2 + .../assets/customnpcs/lang/nl_NL.lang | 2 + .../assets/customnpcs/lang/pl_PL.lang | 2 + .../assets/customnpcs/lang/pt_BR.lang | 2 + .../assets/customnpcs/lang/ru_RU.lang | 2 + .../assets/customnpcs/lang/sv_SE.lang | 2 + .../assets/customnpcs/lang/zh_CN.lang | 2 + .../assets/customnpcs/lang/zh_TW.lang | 2 + 19 files changed, 130 insertions(+), 18 deletions(-) diff --git a/src/main/java/noppes/npcs/client/gui/roles/GuiNpcMount.java b/src/main/java/noppes/npcs/client/gui/roles/GuiNpcMount.java index 57f39a6d8..0a2a2a450 100644 --- a/src/main/java/noppes/npcs/client/gui/roles/GuiNpcMount.java +++ b/src/main/java/noppes/npcs/client/gui/roles/GuiNpcMount.java @@ -38,10 +38,16 @@ public class GuiNpcMount extends GuiNPCInterface2 implements ITextfieldListener addOffsetRow(1, "role.mount.offsety", role.getOffsetY(), y); y += 24; addOffsetRow(2, "role.mount.offsetz", role.getOffsetZ(), y); + y += 24; + addJumpRow(3, "role.mount.jumpheight", role.getJumpStrength(), y); + y += 24; - addButton(new GuiNpcButton(3, guiLeft + 4, y + 26, 80, 20, "role.mount.reset")); + addLabel(new GuiNpcLabel(5, "role.mount.sprint", guiLeft + 4, y + 5)); + addButton(new GuiNpcButton(5, guiLeft + 140, y, 60, 20, new String[]{"gui.no", "gui.yes"}, role.isSprintAllowed() ? 1 : 0)); - GuiNpcLabel speedLabel = new GuiNpcLabel(4, "", guiLeft + 4, y + 6); + addButton(new GuiNpcButton(4, guiLeft + 4, y + 30, 80, 20, "role.mount.reset")); + + GuiNpcLabel speedLabel = new GuiNpcLabel(6, "", guiLeft + 4, y + 56); speedLabel.label = StatCollector.translateToLocalFormatted("role.mount.walkspeed", npc.ais.getWalkingSpeed()); addLabel(speedLabel); } @@ -54,6 +60,14 @@ public class GuiNpcMount extends GuiNPCInterface2 implements ITextfieldListener addTextField(field); } + private void addJumpRow(int id, String translationKey, float value, int y) { + addLabel(new GuiNpcLabel(id, translationKey, guiLeft + 4, y + 5)); + GuiNpcTextField field = new GuiNpcTextField(id, this, guiLeft + 140, y, 80, 20, format(value)); + field.floatsOnly = true; + field.setMinMaxDefaultFloat(0.1F, 3.0F, 1.0F); + addTextField(field); + } + private String format(float value) { return String.format(Locale.ROOT, "%.2f", value); } @@ -62,9 +76,11 @@ public class GuiNpcMount extends GuiNPCInterface2 implements ITextfieldListener public void buttonEvent(GuiButton guibutton) { if (role == null) return; - if (guibutton.id == 3) { + if (guibutton.id == 4) { role.resetOffsets(); initGui(); + } else if (guibutton.id == 5 && guibutton instanceof GuiNpcButton) { + role.setSprintAllowed(((GuiNpcButton) guibutton).getValue() == 1); } } @@ -83,6 +99,9 @@ public class GuiNpcMount extends GuiNPCInterface2 implements ITextfieldListener case 2: role.setOffsetZ(value); break; + case 3: + role.setJumpStrength(value); + break; } textfield.setText(format(valueForField(textfield.id))); } @@ -95,6 +114,8 @@ public class GuiNpcMount extends GuiNPCInterface2 implements ITextfieldListener return role.getOffsetY(); case 2: return role.getOffsetZ(); + case 3: + return role.getJumpStrength(); default: return 0.0F; } diff --git a/src/main/java/noppes/npcs/entity/EntityNPCInterface.java b/src/main/java/noppes/npcs/entity/EntityNPCInterface.java index 56bc0df8c..4f0abbe61 100644 --- a/src/main/java/noppes/npcs/entity/EntityNPCInterface.java +++ b/src/main/java/noppes/npcs/entity/EntityNPCInterface.java @@ -2091,10 +2091,15 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti protected boolean handleMountedMovement(float strafe, float forward) { if (advanced.role != EnumRoleType.Mount || !(roleInterface instanceof RoleMount)) return false; - if (!(riddenByEntity instanceof EntityLivingBase)) + if (riddenByEntity != null && !(riddenByEntity instanceof EntityPlayer)) { + riddenByEntity.mountEntity(null); + return false; + } + if (!(riddenByEntity instanceof EntityPlayer)) return false; - EntityLivingBase rider = (EntityLivingBase) riddenByEntity; + RoleMount mount = (RoleMount) roleInterface; + EntityPlayer rider = (EntityPlayer) riddenByEntity; this.prevRotationYaw = this.rotationYaw = rider.rotationYaw; this.rotationPitch = rider.rotationPitch * 0.5F; this.setRotation(this.rotationYaw, this.rotationPitch); @@ -2108,11 +2113,16 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti } float moveSpeed = Math.max(0.1F, getSpeed()); - if (rider.isSprinting()) { + boolean riderSprinting = rider.isSprinting() && mount.isSprintAllowed(); + if (riderSprinting) { moveSpeed *= 1.2F; } - this.setSprinting(rider.isSprinting()); + if (!mount.isSprintAllowed() && rider.isSprinting()) { + rider.setSprinting(false); + } + + this.setSprinting(riderSprinting); this.stepHeight = 1.0F; this.jumpMovementFactor = moveSpeed * 0.1F; @@ -2135,21 +2145,33 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti if (!worldObj.isRemote) { this.setAIMoveSpeed(moveSpeed); super.moveEntityWithHeading(strafe, forward); - this.prevLimbSwingAmount = this.limbSwingAmount; - double deltaX = this.posX - this.prevPosX; - double deltaZ = this.posZ - this.prevPosZ; - float limbSwingSpeed = MathHelper.sqrt_double(deltaX * deltaX + deltaZ * deltaZ) * 4.0F; - if (limbSwingSpeed > 1.0F) { - limbSwingSpeed = 1.0F; - } - this.limbSwingAmount += (limbSwingSpeed - this.limbSwingAmount) * 0.4F; - this.limbSwing += this.limbSwingAmount; } + + this.prevLimbSwingAmount = this.limbSwingAmount; + double deltaX = this.posX - this.prevPosX; + double deltaZ = this.posZ - this.prevPosZ; + if (worldObj.isRemote && deltaX * deltaX + deltaZ * deltaZ < 1.0E-6D) { + deltaX = this.motionX; + deltaZ = this.motionZ; + } + float limbSwingSpeed = MathHelper.sqrt_double(deltaX * deltaX + deltaZ * deltaZ) * 4.0F; + if (limbSwingSpeed > 1.0F) { + limbSwingSpeed = 1.0F; + } + this.limbSwingAmount += (limbSwingSpeed - this.limbSwingAmount) * 0.4F; + this.limbSwing += this.limbSwingAmount; } protected void applyMountedVerticalMotion(EntityLivingBase rider, float moveSpeed, float forward) { + if (!(roleInterface instanceof RoleMount)) { + return; + } + RoleMount mount = (RoleMount) roleInterface; + double jumpFactor = MathHelper.clamp_double(mount.getJumpStrength(), 0.1D, 3.0D); + if (canFly()) { - double vertical = Math.max(0.1D, moveSpeed * 0.6D); + double verticalBase = Math.max(0.1D, moveSpeed * 0.6D); + double vertical = MathHelper.clamp_double(verticalBase * jumpFactor, 0.05D, 2.0D); if (rider.isJumping) { this.motionY = Math.min(this.motionY + vertical, vertical); this.isAirBorne = true; @@ -2165,7 +2187,8 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti } } } else if (rider.isJumping && this.onGround) { - double jumpVelocity = MathHelper.clamp_double(0.4D + moveSpeed * 1.5D, 0.4D, 0.9D); + double baseVelocity = MathHelper.clamp_double(0.4D + moveSpeed * 1.5D, 0.2D, 1.2D); + double jumpVelocity = MathHelper.clamp_double(baseVelocity * jumpFactor, 0.2D, 2.0D); this.motionY = jumpVelocity; this.isAirBorne = true; this.velocityChanged = true; @@ -2230,6 +2253,13 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti } Entity currentRider = this.riddenByEntity; + if (currentRider != null && !(currentRider instanceof EntityPlayer)) { + Entity dismount = currentRider; + dismount.mountEntity(null); + stabilizeDismountedRider(dismount); + haltMountedMotion(); + currentRider = null; + } if (currentRider != lastRider) { if (lastRider != null) { stabilizeDismountedRider(lastRider); diff --git a/src/main/java/noppes/npcs/roles/RoleMount.java b/src/main/java/noppes/npcs/roles/RoleMount.java index e3ee3bf25..a1a07c800 100644 --- a/src/main/java/noppes/npcs/roles/RoleMount.java +++ b/src/main/java/noppes/npcs/roles/RoleMount.java @@ -10,7 +10,12 @@ public class RoleMount extends RoleInterface { private float offsetX; private float offsetY; private float offsetZ; + private static final float MIN_JUMP_STRENGTH = 0.1F; + private static final float MAX_JUMP_STRENGTH = 3.0F; + private boolean storedReturnToStart; + private float jumpStrength = 1.0F; + private boolean allowSprint = true; public RoleMount(EntityNPCInterface npc) { super(npc); @@ -24,6 +29,8 @@ public class RoleMount extends RoleInterface { compound.setFloat("MountOffsetY", offsetY); compound.setFloat("MountOffsetZ", offsetZ); compound.setBoolean("MountReturnFlag", storedReturnToStart); + compound.setFloat("MountJumpStrength", jumpStrength); + compound.setBoolean("MountAllowSprint", allowSprint); return compound; } @@ -37,6 +44,12 @@ public class RoleMount extends RoleInterface { } else { storedReturnToStart = npc.ais.returnToStart; } + if (compound.hasKey("MountJumpStrength")) { + setJumpStrength(compound.getFloat("MountJumpStrength")); + } else { + jumpStrength = 1.0F; + } + allowSprint = !compound.hasKey("MountAllowSprint") || compound.getBoolean("MountAllowSprint"); npc.ais.returnToStart = false; } @@ -92,6 +105,22 @@ public class RoleMount extends RoleInterface { return offsetZ; } + public void setJumpStrength(float value) { + this.jumpStrength = ValueUtil.clamp(value, MIN_JUMP_STRENGTH, MAX_JUMP_STRENGTH); + } + + public float getJumpStrength() { + return jumpStrength; + } + + public void setSprintAllowed(boolean allowSprint) { + this.allowSprint = allowSprint; + } + + public boolean isSprintAllowed() { + return allowSprint; + } + public void setReturnToStartPreference(boolean value) { this.storedReturnToStart = value; npc.ais.returnToStart = false; diff --git a/src/main/resources/assets/customnpcs/lang/cs_CZ.lang b/src/main/resources/assets/customnpcs/lang/cs_CZ.lang index e9cd29b99..e574a1fb1 100644 --- a/src/main/resources/assets/customnpcs/lang/cs_CZ.lang +++ b/src/main/resources/assets/customnpcs/lang/cs_CZ.lang @@ -443,5 +443,7 @@ role.mount=Mount role.mount.offsetx=Seat Offset X role.mount.offsety=Seat Offset Y role.mount.offsetz=Seat Offset Z +role.mount.jumpheight=Jump Strength +role.mount.sprint=Allow Sprinting role.mount.reset=Reset Position role.mount.walkspeed=Walking speed: %s diff --git a/src/main/resources/assets/customnpcs/lang/de_DE.lang b/src/main/resources/assets/customnpcs/lang/de_DE.lang index 38f3d3377..86269b7b0 100644 --- a/src/main/resources/assets/customnpcs/lang/de_DE.lang +++ b/src/main/resources/assets/customnpcs/lang/de_DE.lang @@ -875,5 +875,7 @@ role.mount=Mount role.mount.offsetx=Seat Offset X role.mount.offsety=Seat Offset Y role.mount.offsetz=Seat Offset Z +role.mount.jumpheight=Jump Strength +role.mount.sprint=Allow Sprinting role.mount.reset=Reset Position role.mount.walkspeed=Walking speed: %s diff --git a/src/main/resources/assets/customnpcs/lang/en_US.lang b/src/main/resources/assets/customnpcs/lang/en_US.lang index c3edc664e..6026ed59a 100644 --- a/src/main/resources/assets/customnpcs/lang/en_US.lang +++ b/src/main/resources/assets/customnpcs/lang/en_US.lang @@ -421,6 +421,8 @@ role.mount=Mount role.mount.offsetx=Seat Offset X role.mount.offsety=Seat Offset Y role.mount.offsetz=Seat Offset Z +role.mount.jumpheight=Jump Strength +role.mount.sprint=Allow Sprinting role.mount.reset=Reset Position role.mount.walkspeed=Walking speed: %s diff --git a/src/main/resources/assets/customnpcs/lang/es_AR.lang b/src/main/resources/assets/customnpcs/lang/es_AR.lang index 5ca50fc49..f0d9533a2 100644 --- a/src/main/resources/assets/customnpcs/lang/es_AR.lang +++ b/src/main/resources/assets/customnpcs/lang/es_AR.lang @@ -503,5 +503,7 @@ role.mount=Mount role.mount.offsetx=Seat Offset X role.mount.offsety=Seat Offset Y role.mount.offsetz=Seat Offset Z +role.mount.jumpheight=Jump Strength +role.mount.sprint=Allow Sprinting role.mount.reset=Reset Position role.mount.walkspeed=Walking speed: %s diff --git a/src/main/resources/assets/customnpcs/lang/es_MX.lang b/src/main/resources/assets/customnpcs/lang/es_MX.lang index fd58878af..9b3ee6a1f 100644 --- a/src/main/resources/assets/customnpcs/lang/es_MX.lang +++ b/src/main/resources/assets/customnpcs/lang/es_MX.lang @@ -503,5 +503,7 @@ role.mount=Mount role.mount.offsetx=Seat Offset X role.mount.offsety=Seat Offset Y role.mount.offsetz=Seat Offset Z +role.mount.jumpheight=Jump Strength +role.mount.sprint=Allow Sprinting role.mount.reset=Reset Position role.mount.walkspeed=Walking speed: %s diff --git a/src/main/resources/assets/customnpcs/lang/fr_FR.lang b/src/main/resources/assets/customnpcs/lang/fr_FR.lang index 6229aa1b9..6fc66154c 100644 --- a/src/main/resources/assets/customnpcs/lang/fr_FR.lang +++ b/src/main/resources/assets/customnpcs/lang/fr_FR.lang @@ -545,5 +545,7 @@ role.mount=Mount role.mount.offsetx=Seat Offset X role.mount.offsety=Seat Offset Y role.mount.offsetz=Seat Offset Z +role.mount.jumpheight=Jump Strength +role.mount.sprint=Allow Sprinting role.mount.reset=Reset Position role.mount.walkspeed=Walking speed: %s diff --git a/src/main/resources/assets/customnpcs/lang/hu_HU.lang b/src/main/resources/assets/customnpcs/lang/hu_HU.lang index e3112252c..63ee943af 100644 --- a/src/main/resources/assets/customnpcs/lang/hu_HU.lang +++ b/src/main/resources/assets/customnpcs/lang/hu_HU.lang @@ -545,5 +545,7 @@ role.mount=Mount role.mount.offsetx=Seat Offset X role.mount.offsety=Seat Offset Y role.mount.offsetz=Seat Offset Z +role.mount.jumpheight=Jump Strength +role.mount.sprint=Allow Sprinting role.mount.reset=Reset Position role.mount.walkspeed=Walking speed: %s diff --git a/src/main/resources/assets/customnpcs/lang/id_ID.lang b/src/main/resources/assets/customnpcs/lang/id_ID.lang index a7de30101..2b53a9639 100644 --- a/src/main/resources/assets/customnpcs/lang/id_ID.lang +++ b/src/main/resources/assets/customnpcs/lang/id_ID.lang @@ -320,5 +320,7 @@ role.mount=Mount role.mount.offsetx=Seat Offset X role.mount.offsety=Seat Offset Y role.mount.offsetz=Seat Offset Z +role.mount.jumpheight=Jump Strength +role.mount.sprint=Allow Sprinting role.mount.reset=Reset Position role.mount.walkspeed=Walking speed: %s diff --git a/src/main/resources/assets/customnpcs/lang/ko_KR.lang b/src/main/resources/assets/customnpcs/lang/ko_KR.lang index 64b370445..89f7c8d7d 100644 --- a/src/main/resources/assets/customnpcs/lang/ko_KR.lang +++ b/src/main/resources/assets/customnpcs/lang/ko_KR.lang @@ -800,5 +800,7 @@ role.mount=Mount role.mount.offsetx=Seat Offset X role.mount.offsety=Seat Offset Y role.mount.offsetz=Seat Offset Z +role.mount.jumpheight=Jump Strength +role.mount.sprint=Allow Sprinting role.mount.reset=Reset Position role.mount.walkspeed=Walking speed: %s diff --git a/src/main/resources/assets/customnpcs/lang/nl_NL.lang b/src/main/resources/assets/customnpcs/lang/nl_NL.lang index 6998ad909..acae2f273 100644 --- a/src/main/resources/assets/customnpcs/lang/nl_NL.lang +++ b/src/main/resources/assets/customnpcs/lang/nl_NL.lang @@ -235,5 +235,7 @@ role.mount=Mount role.mount.offsetx=Seat Offset X role.mount.offsety=Seat Offset Y role.mount.offsetz=Seat Offset Z +role.mount.jumpheight=Jump Strength +role.mount.sprint=Allow Sprinting role.mount.reset=Reset Position role.mount.walkspeed=Walking speed: %s diff --git a/src/main/resources/assets/customnpcs/lang/pl_PL.lang b/src/main/resources/assets/customnpcs/lang/pl_PL.lang index 0526906d6..ff860e0ab 100644 --- a/src/main/resources/assets/customnpcs/lang/pl_PL.lang +++ b/src/main/resources/assets/customnpcs/lang/pl_PL.lang @@ -501,5 +501,7 @@ role.mount=Mount role.mount.offsetx=Seat Offset X role.mount.offsety=Seat Offset Y role.mount.offsetz=Seat Offset Z +role.mount.jumpheight=Jump Strength +role.mount.sprint=Allow Sprinting role.mount.reset=Reset Position role.mount.walkspeed=Walking speed: %s diff --git a/src/main/resources/assets/customnpcs/lang/pt_BR.lang b/src/main/resources/assets/customnpcs/lang/pt_BR.lang index 3e5411213..066697e8c 100644 --- a/src/main/resources/assets/customnpcs/lang/pt_BR.lang +++ b/src/main/resources/assets/customnpcs/lang/pt_BR.lang @@ -501,5 +501,7 @@ role.mount=Mount role.mount.offsetx=Seat Offset X role.mount.offsety=Seat Offset Y role.mount.offsetz=Seat Offset Z +role.mount.jumpheight=Jump Strength +role.mount.sprint=Allow Sprinting role.mount.reset=Reset Position role.mount.walkspeed=Walking speed: %s diff --git a/src/main/resources/assets/customnpcs/lang/ru_RU.lang b/src/main/resources/assets/customnpcs/lang/ru_RU.lang index f190a84f0..a34739f0d 100644 --- a/src/main/resources/assets/customnpcs/lang/ru_RU.lang +++ b/src/main/resources/assets/customnpcs/lang/ru_RU.lang @@ -1024,5 +1024,7 @@ role.mount=Mount role.mount.offsetx=Seat Offset X role.mount.offsety=Seat Offset Y role.mount.offsetz=Seat Offset Z +role.mount.jumpheight=Jump Strength +role.mount.sprint=Allow Sprinting role.mount.reset=Reset Position role.mount.walkspeed=Walking speed: %s diff --git a/src/main/resources/assets/customnpcs/lang/sv_SE.lang b/src/main/resources/assets/customnpcs/lang/sv_SE.lang index adf43d88c..70a29ebc9 100644 --- a/src/main/resources/assets/customnpcs/lang/sv_SE.lang +++ b/src/main/resources/assets/customnpcs/lang/sv_SE.lang @@ -311,5 +311,7 @@ role.mount=Mount role.mount.offsetx=Seat Offset X role.mount.offsety=Seat Offset Y role.mount.offsetz=Seat Offset Z +role.mount.jumpheight=Jump Strength +role.mount.sprint=Allow Sprinting role.mount.reset=Reset Position role.mount.walkspeed=Walking speed: %s diff --git a/src/main/resources/assets/customnpcs/lang/zh_CN.lang b/src/main/resources/assets/customnpcs/lang/zh_CN.lang index 41799f778..00fb8c796 100644 --- a/src/main/resources/assets/customnpcs/lang/zh_CN.lang +++ b/src/main/resources/assets/customnpcs/lang/zh_CN.lang @@ -1247,5 +1247,7 @@ role.mount=Mount role.mount.offsetx=Seat Offset X role.mount.offsety=Seat Offset Y role.mount.offsetz=Seat Offset Z +role.mount.jumpheight=Jump Strength +role.mount.sprint=Allow Sprinting role.mount.reset=Reset Position role.mount.walkspeed=Walking speed: %s diff --git a/src/main/resources/assets/customnpcs/lang/zh_TW.lang b/src/main/resources/assets/customnpcs/lang/zh_TW.lang index 6b864af53..a4f3d4ae5 100644 --- a/src/main/resources/assets/customnpcs/lang/zh_TW.lang +++ b/src/main/resources/assets/customnpcs/lang/zh_TW.lang @@ -317,5 +317,7 @@ role.mount=Mount role.mount.offsetx=Seat Offset X role.mount.offsety=Seat Offset Y role.mount.offsetz=Seat Offset Z +role.mount.jumpheight=Jump Strength +role.mount.sprint=Allow Sprinting role.mount.reset=Reset Position role.mount.walkspeed=Walking speed: %s From f65662efafd3a86e9f73d681429334d2dac58cb1 Mon Sep 17 00:00:00 2001 From: Kam Date: Thu, 6 Nov 2025 02:34:47 -0500 Subject: [PATCH 13/38] Preserve airborne momentum when mounts dismount riders --- .../npcs/entity/EntityNPCInterface.java | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/main/java/noppes/npcs/entity/EntityNPCInterface.java b/src/main/java/noppes/npcs/entity/EntityNPCInterface.java index 4f0abbe61..96310a01e 100644 --- a/src/main/java/noppes/npcs/entity/EntityNPCInterface.java +++ b/src/main/java/noppes/npcs/entity/EntityNPCInterface.java @@ -2270,12 +2270,26 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti } private void stabilizeDismountedRider(Entity rider) { - rider.motionX = 0.0D; - rider.motionY = 0.0D; - rider.motionZ = 0.0D; - rider.fallDistance = 0.0F; + boolean wasOnGround = rider.onGround; + double previousMotionX = rider.motionX; + double previousMotionY = rider.motionY; + double previousMotionZ = rider.motionZ; + float previousFall = rider.fallDistance; + + if (wasOnGround) { + rider.motionX = 0.0D; + rider.motionY = 0.0D; + rider.motionZ = 0.0D; + rider.fallDistance = 0.0F; + rider.onGround = true; + } else { + rider.motionX = previousMotionX; + rider.motionY = previousMotionY; + rider.motionZ = previousMotionZ; + rider.fallDistance = previousFall; + } + rider.velocityChanged = true; - rider.onGround = true; } private void haltMountedMotion() { From 6ff481498324159fe7e1ba42a9d14cc9c11d8e32 Mon Sep 17 00:00:00 2001 From: Kam Date: Thu, 6 Nov 2025 14:49:36 -0500 Subject: [PATCH 14/38] Fix Bow Rendering positioning for Compat with Mixins --- .../noppes/npcs/client/renderer/RenderNPCHumanMale.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/noppes/npcs/client/renderer/RenderNPCHumanMale.java b/src/main/java/noppes/npcs/client/renderer/RenderNPCHumanMale.java index 452627bad..565454766 100644 --- a/src/main/java/noppes/npcs/client/renderer/RenderNPCHumanMale.java +++ b/src/main/java/noppes/npcs/client/renderer/RenderNPCHumanMale.java @@ -8,6 +8,7 @@ import net.minecraft.client.renderer.entity.RenderBiped; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.init.Items; import net.minecraft.item.Item; import net.minecraft.item.ItemArmor; import net.minecraft.item.ItemBlock; @@ -229,7 +230,8 @@ public class RenderNPCHumanMale extends RenderNPCInterface { GL11.glRotatef(20.0F, 1.0F, 0.0F, 0.0F); GL11.glRotatef(45.0F, 0.0F, 1.0F, 0.0F); GL11.glScalef(-var6, -var6, var6); - } else if (itemstack2.getItem() instanceof ItemBow && (customRenderer == null || pluginMod)) { + } else if ((itemstack2.getItem() != null && itemstack2.getItem() == Items.bow) || + (itemstack2.getItem() instanceof ItemBow && (customRenderer == null || pluginMod))) { var6 = 0.625F; GL11.glTranslatef(0.0F, 0.125F, 0.3125F); GL11.glRotatef(-20.0F, 0.0F, 1.0F, 0.0F); @@ -322,7 +324,8 @@ public class RenderNPCHumanMale extends RenderNPCInterface { GL11.glRotatef(20.0F, 1.0F, 0.0F, 0.0F); GL11.glRotatef(45.0F, 0.0F, 1.0F, 0.0F); GL11.glScalef(var6, -var6, var6); - } else if (itemstack2.getItem() instanceof ItemBow && (customRenderer == null || pluginMod)) { + } else if ((itemstack2.getItem() != null && itemstack2.getItem() == Items.bow) || + (itemstack2.getItem() instanceof ItemBow && (customRenderer == null || pluginMod))) { var6 = 0.625F; GL11.glTranslatef(0.0F, 0.125F, 0.3125F); GL11.glRotatef(-20.0F, 0.0F, 1.0F, 0.0F); From 6c84263769bfb2b51ed04c5ea563106f00279fc8 Mon Sep 17 00:00:00 2001 From: Kam Date: Fri, 7 Nov 2025 16:11:44 -0500 Subject: [PATCH 15/38] Update GuiNpcMount.java --- src/main/java/noppes/npcs/client/gui/roles/GuiNpcMount.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/noppes/npcs/client/gui/roles/GuiNpcMount.java b/src/main/java/noppes/npcs/client/gui/roles/GuiNpcMount.java index 0a2a2a450..5f4d228e2 100644 --- a/src/main/java/noppes/npcs/client/gui/roles/GuiNpcMount.java +++ b/src/main/java/noppes/npcs/client/gui/roles/GuiNpcMount.java @@ -64,7 +64,7 @@ public class GuiNpcMount extends GuiNPCInterface2 implements ITextfieldListener addLabel(new GuiNpcLabel(id, translationKey, guiLeft + 4, y + 5)); GuiNpcTextField field = new GuiNpcTextField(id, this, guiLeft + 140, y, 80, 20, format(value)); field.floatsOnly = true; - field.setMinMaxDefaultFloat(0.1F, 3.0F, 1.0F); + field.setMinMaxDefaultFloat(0.1F, 15.0F, 1.0F); addTextField(field); } From 9744209938793fcf2e408d64d8534d514ad93c18 Mon Sep 17 00:00:00 2001 From: Kam Date: Fri, 7 Nov 2025 20:15:49 -0500 Subject: [PATCH 16/38] Refactor mount helpers and expose NPC flight state --- .../kamkeel/npcs/network/PacketHandler.java | 2 + .../npcs/network/enums/EnumPlayerPacket.java | 1 + .../packets/player/SpecialKeyStatePacket.java | 60 ++++ .../npcs/ai/EntityAILeapAtTargetNpc.java | 63 ++++ .../noppes/npcs/ai/EntityAIPounceTarget.java | 12 +- .../java/noppes/npcs/client/ClientProxy.java | 3 + .../noppes/npcs/client/ClientTickHandler.java | 29 ++ .../npcs/client/gui/roles/GuiNpcMount.java | 109 ++++-- .../npcs/controllers/data/PlayerData.java | 9 + .../noppes/npcs/entity/EntityNPCFlying.java | 14 +- .../npcs/entity/EntityNPCInterface.java | 210 +++-------- .../noppes/npcs/entity/EntityNpcDragon.java | 1 + .../noppes/npcs/entity/EntityNpcPony.java | 6 + .../java/noppes/npcs/roles/RoleMount.java | 59 ++- .../java/noppes/npcs/util/NPCMountUtil.java | 335 ++++++++++++++++++ .../assets/customnpcs/lang/cs_CZ.lang | 13 +- .../assets/customnpcs/lang/de_DE.lang | 13 +- .../assets/customnpcs/lang/en_US.lang | 18 +- .../assets/customnpcs/lang/es_AR.lang | 13 +- .../assets/customnpcs/lang/es_MX.lang | 13 +- .../assets/customnpcs/lang/fr_FR.lang | 13 +- .../assets/customnpcs/lang/hu_HU.lang | 13 +- .../assets/customnpcs/lang/id_ID.lang | 13 +- .../assets/customnpcs/lang/ko_KR.lang | 13 +- .../assets/customnpcs/lang/nl_NL.lang | 13 +- .../assets/customnpcs/lang/pl_PL.lang | 13 +- .../assets/customnpcs/lang/pt_BR.lang | 13 +- .../assets/customnpcs/lang/ru_RU.lang | 13 +- .../assets/customnpcs/lang/sv_SE.lang | 13 +- .../assets/customnpcs/lang/zh_CN.lang | 13 +- .../assets/customnpcs/lang/zh_TW.lang | 13 +- 31 files changed, 821 insertions(+), 305 deletions(-) create mode 100644 src/main/java/kamkeel/npcs/network/packets/player/SpecialKeyStatePacket.java create mode 100644 src/main/java/noppes/npcs/ai/EntityAILeapAtTargetNpc.java create mode 100644 src/main/java/noppes/npcs/util/NPCMountUtil.java diff --git a/src/main/java/kamkeel/npcs/network/PacketHandler.java b/src/main/java/kamkeel/npcs/network/PacketHandler.java index 4dcf50bac..745962aba 100644 --- a/src/main/java/kamkeel/npcs/network/PacketHandler.java +++ b/src/main/java/kamkeel/npcs/network/PacketHandler.java @@ -60,6 +60,7 @@ import kamkeel.npcs.network.packets.player.MailActionPacket; import kamkeel.npcs.network.packets.player.SaveBookPacket; import kamkeel.npcs.network.packets.player.SaveSignPacket; import kamkeel.npcs.network.packets.player.ScreenSizePacket; +import kamkeel.npcs.network.packets.player.SpecialKeyStatePacket; import kamkeel.npcs.network.packets.player.TransportPacket; import kamkeel.npcs.network.packets.player.customgui.CustomGuiButtonPacket; import kamkeel.npcs.network.packets.player.customgui.CustomGuiClosePacket; @@ -575,6 +576,7 @@ public class PacketHandler { PLAYER_PACKET.registerPacket(new InputDevicePacket()); PLAYER_PACKET.registerPacket(new ScreenSizePacket()); + PLAYER_PACKET.registerPacket(new SpecialKeyStatePacket()); PLAYER_PACKET.registerPacket(new SyncRevisionInfoPacket()); diff --git a/src/main/java/kamkeel/npcs/network/enums/EnumPlayerPacket.java b/src/main/java/kamkeel/npcs/network/enums/EnumPlayerPacket.java index 04d3d3396..0c1db3cd9 100644 --- a/src/main/java/kamkeel/npcs/network/enums/EnumPlayerPacket.java +++ b/src/main/java/kamkeel/npcs/network/enums/EnumPlayerPacket.java @@ -14,6 +14,7 @@ public enum EnumPlayerPacket { CompanionAction, GetRole, InputDevice, + SpecialKeyState, ScreenSize, MagicCycles, diff --git a/src/main/java/kamkeel/npcs/network/packets/player/SpecialKeyStatePacket.java b/src/main/java/kamkeel/npcs/network/packets/player/SpecialKeyStatePacket.java new file mode 100644 index 000000000..3b9f56690 --- /dev/null +++ b/src/main/java/kamkeel/npcs/network/packets/player/SpecialKeyStatePacket.java @@ -0,0 +1,60 @@ +package kamkeel.npcs.network.packets.player; + +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.PacketClient; +import kamkeel.npcs.network.PacketHandler; +import kamkeel.npcs.network.enums.EnumPlayerPacket; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import noppes.npcs.controllers.PlayerDataController; +import noppes.npcs.controllers.data.PlayerData; + +import java.io.IOException; + +public final class SpecialKeyStatePacket extends AbstractPacket { + private boolean pressed; + + public SpecialKeyStatePacket() { + } + + private SpecialKeyStatePacket(boolean pressed) { + this.pressed = pressed; + } + + public static void send(boolean pressed) { + PacketClient.sendClient(new SpecialKeyStatePacket(pressed)); + } + + @Override + public Enum getType() { + return EnumPlayerPacket.SpecialKeyState; + } + + @Override + public PacketChannel getChannel() { + return PacketHandler.PLAYER_PACKET; + } + + @Override + @SideOnly(Side.CLIENT) + public void sendData(ByteBuf out) throws IOException { + out.writeBoolean(pressed); + } + + @Override + public void receiveData(ByteBuf in, EntityPlayer player) throws IOException { + boolean keyDown = in.readBoolean(); + if (!(player instanceof EntityPlayerMP)) { + return; + } + + PlayerData data = PlayerDataController.Instance.getPlayerData((EntityPlayerMP) player); + if (data != null) { + data.setSpecialKeyDown(keyDown); + } + } +} diff --git a/src/main/java/noppes/npcs/ai/EntityAILeapAtTargetNpc.java b/src/main/java/noppes/npcs/ai/EntityAILeapAtTargetNpc.java new file mode 100644 index 000000000..437dbd6d3 --- /dev/null +++ b/src/main/java/noppes/npcs/ai/EntityAILeapAtTargetNpc.java @@ -0,0 +1,63 @@ +package noppes.npcs.ai; + +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.ai.EntityAIBase; +import net.minecraft.util.MathHelper; +import noppes.npcs.entity.EntityNPCInterface; + +public class EntityAILeapAtTargetNpc extends EntityAIBase { + private final EntityNPCInterface npc; + private EntityLivingBase leapTarget; + private final float leapMotionY; + + public EntityAILeapAtTargetNpc(EntityNPCInterface npc, float leapMotionY) { + this.npc = npc; + this.leapMotionY = leapMotionY; + this.setMutexBits(5); + } + + @Override + public boolean shouldExecute() { + this.leapTarget = this.npc.getAttackTarget(); + if (this.leapTarget == null) { + return false; + } + double distanceSq = this.npc.getDistanceSqToEntity(this.leapTarget); + if (distanceSq < 4.0D || distanceSq > 16.0D) { + return false; + } + if (!this.npc.onGround) { + return false; + } + return this.npc.getRNG().nextInt(5) == 0; + } + + @Override + public boolean continueExecuting() { + boolean airborne = !this.npc.onGround; + if (!airborne) { + this.npc.setNpcJumpingState(false); + } + return airborne; + } + + @Override + public void startExecuting() { + double d0 = this.leapTarget.posX - this.npc.posX; + double d1 = this.leapTarget.posZ - this.npc.posZ; + float f = MathHelper.sqrt_double(d0 * d0 + d1 * d1); + + if ((double) f >= 1.0E-4D) { + this.npc.motionX += d0 / (double) f * 0.5D * 0.8D + this.npc.motionX * 0.2D; + this.npc.motionZ += d1 / (double) f * 0.5D * 0.8D + this.npc.motionZ * 0.2D; + } + + this.npc.motionY = this.leapMotionY; + this.npc.setNpcJumpingState(true); + } + + @Override + public void resetTask() { + this.npc.setNpcJumpingState(false); + } +} diff --git a/src/main/java/noppes/npcs/ai/EntityAIPounceTarget.java b/src/main/java/noppes/npcs/ai/EntityAIPounceTarget.java index 5dbe5f233..4f87575bf 100644 --- a/src/main/java/noppes/npcs/ai/EntityAIPounceTarget.java +++ b/src/main/java/noppes/npcs/ai/EntityAIPounceTarget.java @@ -30,7 +30,11 @@ public class EntityAIPounceTarget extends EntityAIBase { @Override public boolean continueExecuting() { - return !this.npc.onGround; + boolean airborne = !this.npc.onGround; + if (!airborne) { + this.npc.setNpcJumpingState(false); + } + return airborne; } @Override @@ -47,6 +51,12 @@ public class EntityAIPounceTarget extends EntityAIBase { this.npc.motionX *= this.leapSpeed; this.npc.motionZ *= this.leapSpeed; this.npc.motionY *= this.leapSpeed; + this.npc.setNpcJumpingState(true); + } + + @Override + public void resetTask() { + this.npc.setNpcJumpingState(false); } public float getAngleForXYZ(double varX, double varY, double varZ, double horiDist) { diff --git a/src/main/java/noppes/npcs/client/ClientProxy.java b/src/main/java/noppes/npcs/client/ClientProxy.java index f0346a5d3..7cc3d7b76 100644 --- a/src/main/java/noppes/npcs/client/ClientProxy.java +++ b/src/main/java/noppes/npcs/client/ClientProxy.java @@ -222,6 +222,7 @@ import java.util.Random; public class ClientProxy extends CommonProxy { public static KeyBinding NPCButton; + public static KeyBinding SpecialKey; public static final Random RAND = new Random(); public static FontContainer Font; @@ -288,8 +289,10 @@ public class ClientProxy extends CommonProxy { Minecraft mc = Minecraft.getMinecraft(); NPCButton = new KeyBinding("NPC Inventory", Keyboard.KEY_N, "key.categories.customnpc"); + SpecialKey = new KeyBinding("key.customnpcs.special", Keyboard.KEY_C, "key.categories.customnpc"); ClientRegistry.registerKeyBinding(NPCButton); + ClientRegistry.registerKeyBinding(SpecialKey); new PresetController(CustomNpcs.Dir); diff --git a/src/main/java/noppes/npcs/client/ClientTickHandler.java b/src/main/java/noppes/npcs/client/ClientTickHandler.java index 46af7df1a..af44405dc 100644 --- a/src/main/java/noppes/npcs/client/ClientTickHandler.java +++ b/src/main/java/noppes/npcs/client/ClientTickHandler.java @@ -8,6 +8,7 @@ import cpw.mods.fml.common.gameevent.TickEvent.Phase; import kamkeel.npcs.network.PacketClient; import kamkeel.npcs.network.packets.player.CheckPlayerValue; import kamkeel.npcs.network.packets.player.InputDevicePacket; +import kamkeel.npcs.network.packets.player.SpecialKeyStatePacket; import kamkeel.npcs.network.packets.player.ScreenSizePacket; import net.minecraft.client.Minecraft; import net.minecraft.entity.Entity; @@ -23,9 +24,12 @@ import noppes.npcs.client.gui.hud.EnumHudComponent; import noppes.npcs.client.gui.hud.HudComponent; import noppes.npcs.client.gui.player.inventory.GuiCNPCInventory; import noppes.npcs.client.renderer.RenderNPCInterface; +import noppes.npcs.constants.EnumRoleType; import noppes.npcs.constants.MarkType; import noppes.npcs.controllers.data.MarkData; +import noppes.npcs.controllers.data.PlayerData; import noppes.npcs.entity.EntityNPCInterface; +import noppes.npcs.roles.RoleMount; import org.lwjgl.input.Keyboard; import org.lwjgl.input.Mouse; import tconstruct.client.tabs.InventoryTabCustomNpc; @@ -42,6 +46,7 @@ public class ClientTickHandler { private int buttonPressed = -1; private long buttonTime = 0L; private final int[] ignoreKeys = new int[]{157, 29, 54, 42, 184, 56, 220, 219}; + private boolean lastSpecialKeyDown = false; @SubscribeEvent(priority = EventPriority.LOWEST) public void onClientTick(TickEvent.ClientTickEvent event) { @@ -52,6 +57,30 @@ public class ClientTickHandler { } this.prevWorld = mc.theWorld; } + if (event.phase == Phase.START) { + EntityPlayer player = mc.thePlayer; + if (player != null) { + boolean specialKeyDown = ClientProxy.SpecialKey != null && Keyboard.isKeyDown(ClientProxy.SpecialKey.getKeyCode()); + if (specialKeyDown != lastSpecialKeyDown) { + PlayerData data = CustomNpcs.proxy.getPlayerData(player); + if (data != null) { + data.setSpecialKeyDown(specialKeyDown); + } + SpecialKeyStatePacket.send(specialKeyDown); + lastSpecialKeyDown = specialKeyDown; + } + + if (player.ridingEntity instanceof EntityNPCInterface) { + EntityNPCInterface mount = (EntityNPCInterface) player.ridingEntity; + if (mount.advanced.role == EnumRoleType.Mount && mount.roleInterface instanceof RoleMount) { + RoleMount role = (RoleMount) mount.roleInterface; + if (!role.isSprintAllowed() && player.isSprinting()) { + player.setSprinting(false); + } + } + } + } + } if (event.phase == Phase.END) { if (mc.thePlayer != null && mc.theWorld != null && !mc.isGamePaused() && ClientEventHandler.hasOverlays(mc.thePlayer)) { renderCNPCPlayer.itemRenderer.updateEquippedItem(); diff --git a/src/main/java/noppes/npcs/client/gui/roles/GuiNpcMount.java b/src/main/java/noppes/npcs/client/gui/roles/GuiNpcMount.java index 5f4d228e2..8c63d2c99 100644 --- a/src/main/java/noppes/npcs/client/gui/roles/GuiNpcMount.java +++ b/src/main/java/noppes/npcs/client/gui/roles/GuiNpcMount.java @@ -13,8 +13,6 @@ import noppes.npcs.client.gui.util.ITextfieldListener; import noppes.npcs.entity.EntityNPCInterface; import noppes.npcs.roles.RoleMount; -import java.util.Locale; - public class GuiNpcMount extends GuiNPCInterface2 implements ITextfieldListener { private final RoleMount role; @@ -32,55 +30,86 @@ public class GuiNpcMount extends GuiNPCInterface2 implements ITextfieldListener return; } - int y = guiTop + 6; - addOffsetRow(0, "role.mount.offsetx", role.getOffsetX(), y); - y += 24; - addOffsetRow(1, "role.mount.offsety", role.getOffsetY(), y); - y += 24; - addOffsetRow(2, "role.mount.offsetz", role.getOffsetZ(), y); - y += 24; - addJumpRow(3, "role.mount.jumpheight", role.getJumpStrength(), y); - y += 24; + int leftLabelX = guiLeft + 4; + int leftFieldX = guiLeft + 140; + int leftY = guiTop + 6; - addLabel(new GuiNpcLabel(5, "role.mount.sprint", guiLeft + 4, y + 5)); - addButton(new GuiNpcButton(5, guiLeft + 140, y, 60, 20, new String[]{"gui.no", "gui.yes"}, role.isSprintAllowed() ? 1 : 0)); + leftY = addOffsetRow(0, 10, "role.mount.offsetx", role.getOffsetX(), leftLabelX, leftFieldX, leftY); + leftY = addOffsetRow(1, 11, "role.mount.offsety", role.getOffsetY(), leftLabelX, leftFieldX, leftY); + leftY = addOffsetRow(2, 12, "role.mount.offsetz", role.getOffsetZ(), leftLabelX, leftFieldX, leftY); + leftY = addJumpRow(3, 13, "role.mount.jumpheight", role.getJumpStrength(), leftLabelX, leftFieldX, leftY); - addButton(new GuiNpcButton(4, guiLeft + 4, y + 30, 80, 20, "role.mount.reset")); + addLabel(new GuiNpcLabel(4, "role.mount.sprint", leftLabelX, leftY + 5)); + addButton(new GuiNpcButton(101, leftFieldX, leftY, 60, 20, new String[]{"gui.no", "gui.yes"}, role.isSprintAllowed() ? 1 : 0)); + leftY += 24; - GuiNpcLabel speedLabel = new GuiNpcLabel(6, "", guiLeft + 4, y + 56); + GuiNpcLabel speedLabel = new GuiNpcLabel(5, "", leftLabelX, leftY + 5); speedLabel.label = StatCollector.translateToLocalFormatted("role.mount.walkspeed", npc.ais.getWalkingSpeed()); addLabel(speedLabel); + + int rightLabelX = guiLeft + 214; + int rightFieldX = guiLeft + 320; + int rightY = guiTop + 6; + + addLabel(new GuiNpcLabel(20, "role.mount.fly", rightLabelX, rightY + 5)); + addButton(new GuiNpcButton(102, rightFieldX, rightY, 60, 20, new String[]{"gui.no", "gui.yes"}, role.isFlyingMountEnabled() ? 1 : 0)); + rightY += 24; + + if (role.isFlyingMountEnabled()) { + addLabel(new GuiNpcLabel(21, "role.mount.hover", rightLabelX, rightY + 5)); + addButton(new GuiNpcButton(103, rightFieldX, rightY, 60, 20, new String[]{"gui.no", "gui.yes"}, role.isHoverModeEnabled() ? 1 : 0)); + rightY += 24; + + rightY = addSpeedRow(22, 14, "role.mount.ascendspeed", role.getFlyingAscendSpeed(), rightLabelX, rightFieldX, rightY, 0.1F, 3.0F, 0.60F); + rightY = addSpeedRow(23, 15, "role.mount.descendspeed", role.getFlyingDescendSpeed(), rightLabelX, rightFieldX, rightY, 0.05F, 3.0F, 0.35F); + } } - private void addOffsetRow(int id, String translationKey, float value, int y) { - addLabel(new GuiNpcLabel(id, translationKey, guiLeft + 4, y + 5)); - GuiNpcTextField field = new GuiNpcTextField(id, this, guiLeft + 140, y, 80, 20, format(value)); + private int addOffsetRow(int labelId, int fieldId, String translationKey, float value, int labelX, int fieldX, int y) { + addLabel(new GuiNpcLabel(labelId, translationKey, labelX, y + 5)); + GuiNpcTextField field = new GuiNpcTextField(fieldId, this, fieldX, y, 80, 20, format(value)); field.floatsOnly = true; field.setMinMaxDefaultFloat(-5.0F, 5.0F, 0.0F); addTextField(field); + return y + 24; } - private void addJumpRow(int id, String translationKey, float value, int y) { - addLabel(new GuiNpcLabel(id, translationKey, guiLeft + 4, y + 5)); - GuiNpcTextField field = new GuiNpcTextField(id, this, guiLeft + 140, y, 80, 20, format(value)); + private int addJumpRow(int labelId, int fieldId, String translationKey, float value, int labelX, int fieldX, int y) { + addLabel(new GuiNpcLabel(labelId, translationKey, labelX, y + 5)); + GuiNpcTextField field = new GuiNpcTextField(fieldId, this, fieldX, y, 80, 20, format(value)); field.floatsOnly = true; field.setMinMaxDefaultFloat(0.1F, 15.0F, 1.0F); addTextField(field); + return y + 24; + } + + private int addSpeedRow(int labelId, int fieldId, String translationKey, float value, int labelX, int fieldX, int y, float min, float max, float defaultValue) { + addLabel(new GuiNpcLabel(labelId, translationKey, labelX, y + 5)); + GuiNpcTextField field = new GuiNpcTextField(fieldId, this, fieldX, y, 80, 20, format(value)); + field.floatsOnly = true; + field.setMinMaxDefaultFloat(min, max, defaultValue); + addTextField(field); + return y + 24; } private String format(float value) { - return String.format(Locale.ROOT, "%.2f", value); + return String.format(java.util.Locale.ROOT, "%.2f", value); } @Override public void buttonEvent(GuiButton guibutton) { if (role == null) return; - if (guibutton.id == 4) { - role.resetOffsets(); - initGui(); - } else if (guibutton.id == 5 && guibutton instanceof GuiNpcButton) { - role.setSprintAllowed(((GuiNpcButton) guibutton).getValue() == 1); + if (guibutton instanceof GuiNpcButton) { + GuiNpcButton button = (GuiNpcButton) guibutton; + if (guibutton.id == 101) { + role.setSprintAllowed(button.getValue() == 1); + } else if (guibutton.id == 102) { + role.setFlyingMountEnabled(button.getValue() == 1); + initGui(); + } else if (guibutton.id == 103) { + role.setHoverModeEnabled(button.getValue() == 1); + } } } @@ -90,32 +119,42 @@ public class GuiNpcMount extends GuiNPCInterface2 implements ITextfieldListener return; float value = textfield.isFloat() ? textfield.getFloat() : 0.0F; switch (textfield.id) { - case 0: + case 10: role.setOffsetX(value); break; - case 1: + case 11: role.setOffsetY(value); break; - case 2: + case 12: role.setOffsetZ(value); break; - case 3: + case 13: role.setJumpStrength(value); break; + case 14: + role.setFlyingAscendSpeed(value); + break; + case 15: + role.setFlyingDescendSpeed(value); + break; } textfield.setText(format(valueForField(textfield.id))); } private float valueForField(int id) { switch (id) { - case 0: + case 10: return role.getOffsetX(); - case 1: + case 11: return role.getOffsetY(); - case 2: + case 12: return role.getOffsetZ(); - case 3: + case 13: return role.getJumpStrength(); + case 14: + return role.getFlyingAscendSpeed(); + case 15: + return role.getFlyingDescendSpeed(); default: return 0.0F; } diff --git a/src/main/java/noppes/npcs/controllers/data/PlayerData.java b/src/main/java/noppes/npcs/controllers/data/PlayerData.java index 5f6a5e00d..599f81ff1 100644 --- a/src/main/java/noppes/npcs/controllers/data/PlayerData.java +++ b/src/main/java/noppes/npcs/controllers/data/PlayerData.java @@ -84,6 +84,7 @@ public class PlayerData implements IExtendedEntityProperties, IPlayerData { public ScreenSize screenSize = new ScreenSize(-1, -1); public int profileSlot = 0; + private boolean specialKeyDown = false; public void onLogin() { // Continue playing animation for self when re-logging @@ -267,6 +268,14 @@ public class PlayerData implements IExtendedEntityProperties, IPlayerData { return this.isGUIOpen; } + public boolean isSpecialKeyDown() { + return specialKeyDown; + } + + public void setSpecialKeyDown(boolean specialKeyDown) { + this.specialKeyDown = specialKeyDown; + } + public ScreenSize getScreenSize() { return screenSize; } diff --git a/src/main/java/noppes/npcs/entity/EntityNPCFlying.java b/src/main/java/noppes/npcs/entity/EntityNPCFlying.java index 78a7ee23a..2996ca7f3 100644 --- a/src/main/java/noppes/npcs/entity/EntityNPCFlying.java +++ b/src/main/java/noppes/npcs/entity/EntityNPCFlying.java @@ -45,6 +45,7 @@ public abstract class EntityNPCFlying extends EntityNPCInterface { if (handleMountedMovement(p_70612_1_, p_70612_2_)) return; if (!this.canFly() || this.hurtTime != 0 || !this.canBreathe()) { + setNpcFlyingState(false); super.moveEntityWithHeading(p_70612_1_, p_70612_2_); return; } @@ -66,11 +67,13 @@ public abstract class EntityNPCFlying extends EntityNPCInterface { if (!this.getNavigator().noPath() && this.motionY > 0) { this.motionY = 0; } else { + setNpcFlyingState(false); super.moveEntityWithHeading(p_70612_1_, p_70612_2_); return; } } this.flyLimitAllow = true; + setNpcFlyingState(true); double d3 = this.motionY; super.moveEntityWithHeading(p_70612_1_, p_70612_2_); @@ -97,9 +100,10 @@ public abstract class EntityNPCFlying extends EntityNPCInterface { } @Override - protected void doMountedMovement(float strafe, float forward, float moveSpeed) { + public void performMountedMovement(float strafe, float forward, float moveSpeed) { if (!this.canFly() || this.hurtTime != 0 || !this.canBreathe()) { - super.doMountedMovement(strafe, forward, moveSpeed); + setNpcFlyingState(false); + super.performMountedMovement(strafe, forward, moveSpeed); return; } @@ -120,14 +124,16 @@ public abstract class EntityNPCFlying extends EntityNPCInterface { if (!this.getNavigator().noPath() && this.motionY > 0) { this.motionY = 0; } else { - super.doMountedMovement(strafe, forward, moveSpeed); + setNpcFlyingState(false); + super.performMountedMovement(strafe, forward, moveSpeed); return; } } this.flyLimitAllow = true; + setNpcFlyingState(true); double d3 = this.motionY; - super.doMountedMovement(strafe, forward, moveSpeed); + super.performMountedMovement(strafe, forward, moveSpeed); this.motionY = d3; if (this.getNavigator().noPath()) { diff --git a/src/main/java/noppes/npcs/entity/EntityNPCInterface.java b/src/main/java/noppes/npcs/entity/EntityNPCInterface.java index 96310a01e..305a9098b 100644 --- a/src/main/java/noppes/npcs/entity/EntityNPCInterface.java +++ b/src/main/java/noppes/npcs/entity/EntityNPCInterface.java @@ -32,7 +32,6 @@ import net.minecraft.entity.SharedMonsterAttributes; import net.minecraft.entity.ai.EntityAIAttackOnCollide; import net.minecraft.entity.ai.EntityAIBase; import net.minecraft.entity.ai.EntityAIHurtByTarget; -import net.minecraft.entity.ai.EntityAILeapAtTarget; import net.minecraft.entity.ai.EntityAIOpenDoor; import net.minecraft.entity.ai.EntityAIRestrictSun; import net.minecraft.entity.ai.EntityAISwimming; @@ -101,6 +100,7 @@ import noppes.npcs.ai.EntityAIMoveIndoors; import noppes.npcs.ai.EntityAIMovingPath; import noppes.npcs.ai.EntityAIOrbitTarget; import noppes.npcs.ai.EntityAIPanic; +import noppes.npcs.ai.EntityAILeapAtTargetNpc; import noppes.npcs.ai.EntityAIPounceTarget; import noppes.npcs.ai.EntityAIRangedAttack; import noppes.npcs.ai.EntityAIReturn; @@ -160,6 +160,7 @@ import noppes.npcs.scripted.NpcAPI; import noppes.npcs.scripted.entity.ScriptNpc; import noppes.npcs.scripted.event.NpcEvent; import noppes.npcs.util.GameProfileAlt; +import noppes.npcs.util.NPCMountUtil; import java.io.IOException; import java.util.ArrayList; @@ -211,7 +212,9 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti public ResourceLocation textureLocation = null; - private Entity lastRider; + private final NPCMountUtil.MountState mountState = new NPCMountUtil.MountState(); + private static final int NPC_STATE_FLAG_FLYING = 1; + private static final int NPC_STATE_FLAG_JUMPING = 1 << 1; public EnumAnimation currentAnimation = EnumAnimation.NONE; @@ -282,6 +285,7 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti this.dataWatcher.addObject(14, Integer.valueOf(0)); // Animation this.dataWatcher.addObject(15, Integer.valueOf(0)); // isWalking this.dataWatcher.addObject(16, ""); // Role + this.dataWatcher.addObject(17, Byte.valueOf((byte) 0)); // Mount state flags } protected boolean isAIEnabled() { @@ -967,7 +971,7 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti */ public void setLeapTask() { if (this.ais.leapType == 1) - this.tasks.addTask(this.taskCount++, aiLeap = new EntityAILeapAtTarget(this, 0.4F)); + this.tasks.addTask(this.taskCount++, aiLeap = new EntityAILeapAtTargetNpc(this, 0.4F)); if (this.ais.leapType == 2) this.tasks.addTask(this.taskCount++, aiLeap = new EntityAIPounceTarget(this)); } @@ -1486,13 +1490,14 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti delete(); } else { if (this.riddenByEntity != null) { - stabilizeDismountedRider(this.riddenByEntity); + NPCMountUtil.stabilizeDismountedRider(this.riddenByEntity); this.riddenByEntity.mountEntity(null); - haltMountedMotion(); + NPCMountUtil.haltMountedMotion(this, mountState); } if (this.ridingEntity != null) this.mountEntity(null); - lastRider = null; + mountState.lastRider = null; + NPCMountUtil.resetMountedFlightState(this, mountState); setHealth(-1); setSprinting(false); getNavigator().clearPathEntity(); @@ -1623,7 +1628,32 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti } public boolean isFlying() { - return isWalking() && !onGround; + return isNpcFlying(); + } + + public boolean isNpcFlying() { + return (dataWatcher.getWatchableObjectByte(17) & NPC_STATE_FLAG_FLYING) != 0; + } + + public boolean isNpcJumping() { + return (dataWatcher.getWatchableObjectByte(17) & NPC_STATE_FLAG_JUMPING) != 0; + } + + private void setNpcStateFlag(int mask, boolean value) { + byte current = dataWatcher.getWatchableObjectByte(17); + byte updated = value ? (byte) (current | mask) : (byte) (current & ~mask); + if (current != updated) { + dataWatcher.updateObject(17, updated); + } + } + + public void setNpcFlyingState(boolean flying) { + setNpcStateFlag(NPC_STATE_FLAG_FLYING, flying); + } + + public void setNpcJumpingState(boolean jumping) { + setNpcStateFlag(NPC_STATE_FLAG_JUMPING, jumping); + this.isJumping = jumping; } public void setBoolFlag(boolean bo, int id) { @@ -1872,9 +1902,12 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti } @Override - protected void fall(float par1) { + protected void fall(float distance) { + if (NPCMountUtil.isFlyingMountWithFlightEnabled(this)) { + return; + } if (!this.stats.noFallDamage) - super.fall(par1); + super.fall(distance); } @Override @@ -2089,57 +2122,10 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti } protected boolean handleMountedMovement(float strafe, float forward) { - if (advanced.role != EnumRoleType.Mount || !(roleInterface instanceof RoleMount)) - return false; - if (riddenByEntity != null && !(riddenByEntity instanceof EntityPlayer)) { - riddenByEntity.mountEntity(null); - return false; - } - if (!(riddenByEntity instanceof EntityPlayer)) - return false; - - RoleMount mount = (RoleMount) roleInterface; - EntityPlayer rider = (EntityPlayer) riddenByEntity; - this.prevRotationYaw = this.rotationYaw = rider.rotationYaw; - this.rotationPitch = rider.rotationPitch * 0.5F; - this.setRotation(this.rotationYaw, this.rotationPitch); - this.renderYawOffset = this.rotationYaw; - this.rotationYawHead = this.rotationYaw; - - float controlledStrafe = rider.moveStrafing * 0.5F; - float controlledForward = rider.moveForward; - if (controlledForward <= 0.0F) { - controlledForward *= 0.25F; - } - - float moveSpeed = Math.max(0.1F, getSpeed()); - boolean riderSprinting = rider.isSprinting() && mount.isSprintAllowed(); - if (riderSprinting) { - moveSpeed *= 1.2F; - } - - if (!mount.isSprintAllowed() && rider.isSprinting()) { - rider.setSprinting(false); - } - - this.setSprinting(riderSprinting); - this.stepHeight = 1.0F; - this.jumpMovementFactor = moveSpeed * 0.1F; - - this.getNavigator().clearPathEntity(); - this.setAttackTarget(null); - this.setRevengeTarget(null); - - this.fallDistance = 0.0F; - rider.fallDistance = 0.0F; - - applyMountedVerticalMotion(rider, moveSpeed, controlledForward); - doMountedMovement(controlledStrafe, controlledForward, moveSpeed); - - return true; + return NPCMountUtil.handleMountedMovement(this, mountState, strafe, forward); } - - protected void doMountedMovement(float strafe, float forward, float moveSpeed) { + + public void performMountedMovement(float strafe, float forward, float moveSpeed) { this.moveStrafing = strafe; this.moveForward = forward; if (!worldObj.isRemote) { @@ -2162,46 +2148,6 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti this.limbSwing += this.limbSwingAmount; } - protected void applyMountedVerticalMotion(EntityLivingBase rider, float moveSpeed, float forward) { - if (!(roleInterface instanceof RoleMount)) { - return; - } - RoleMount mount = (RoleMount) roleInterface; - double jumpFactor = MathHelper.clamp_double(mount.getJumpStrength(), 0.1D, 3.0D); - - if (canFly()) { - double verticalBase = Math.max(0.1D, moveSpeed * 0.6D); - double vertical = MathHelper.clamp_double(verticalBase * jumpFactor, 0.05D, 2.0D); - if (rider.isJumping) { - this.motionY = Math.min(this.motionY + vertical, vertical); - this.isAirBorne = true; - this.velocityChanged = true; - } else { - float pitch = rider.rotationPitch; - if (pitch < -10F) { - this.motionY = vertical; - } else if (pitch > 10F) { - this.motionY = -vertical; - } else if (!this.onGround) { - this.motionY *= 0.6D; - } - } - } else if (rider.isJumping && this.onGround) { - double baseVelocity = MathHelper.clamp_double(0.4D + moveSpeed * 1.5D, 0.2D, 1.2D); - double jumpVelocity = MathHelper.clamp_double(baseVelocity * jumpFactor, 0.2D, 2.0D); - this.motionY = jumpVelocity; - this.isAirBorne = true; - this.velocityChanged = true; - if (forward > 0.0F) { - float yawRadians = this.rotationYaw * (float) Math.PI / 180.0F; - double boost = 0.4D * forward; - this.motionX += -MathHelper.sin(yawRadians) * boost; - this.motionZ += MathHelper.cos(yawRadians) * boost; - } - ForgeHooks.onLivingJump(this); - } - } - @Override public boolean allowLeashing() { return false; @@ -2240,71 +2186,7 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti } private void handleMountRiderState() { - if (worldObj.isRemote) { - if (advanced.role != EnumRoleType.Mount || !(roleInterface instanceof RoleMount)) { - lastRider = null; - } - return; - } - - if (advanced.role != EnumRoleType.Mount || !(roleInterface instanceof RoleMount)) { - lastRider = null; - return; - } - - Entity currentRider = this.riddenByEntity; - if (currentRider != null && !(currentRider instanceof EntityPlayer)) { - Entity dismount = currentRider; - dismount.mountEntity(null); - stabilizeDismountedRider(dismount); - haltMountedMotion(); - currentRider = null; - } - if (currentRider != lastRider) { - if (lastRider != null) { - stabilizeDismountedRider(lastRider); - haltMountedMotion(); - } - lastRider = currentRider; - } - } - - private void stabilizeDismountedRider(Entity rider) { - boolean wasOnGround = rider.onGround; - double previousMotionX = rider.motionX; - double previousMotionY = rider.motionY; - double previousMotionZ = rider.motionZ; - float previousFall = rider.fallDistance; - - if (wasOnGround) { - rider.motionX = 0.0D; - rider.motionY = 0.0D; - rider.motionZ = 0.0D; - rider.fallDistance = 0.0F; - rider.onGround = true; - } else { - rider.motionX = previousMotionX; - rider.motionY = previousMotionY; - rider.motionZ = previousMotionZ; - rider.fallDistance = previousFall; - } - - rider.velocityChanged = true; - } - - private void haltMountedMotion() { - this.motionX = 0.0D; - this.motionY = 0.0D; - this.motionZ = 0.0D; - this.velocityChanged = true; - this.isAirBorne = false; - this.setSprinting(false); - this.moveForward = 0.0F; - this.moveStrafing = 0.0F; - this.setAIMoveSpeed(0.0F); - this.limbSwingAmount = 0.0F; - this.limbSwing = 0.0F; - this.getNavigator().clearPathEntity(); + NPCMountUtil.handleMountRiderState(this, mountState); } // Model Types: 0: Steve 64x32, 1: Steve 64x64, 2: Alex 64x64 diff --git a/src/main/java/noppes/npcs/entity/EntityNpcDragon.java b/src/main/java/noppes/npcs/entity/EntityNpcDragon.java index 6f490583f..a9df9da99 100644 --- a/src/main/java/noppes/npcs/entity/EntityNpcDragon.java +++ b/src/main/java/noppes/npcs/entity/EntityNpcDragon.java @@ -94,6 +94,7 @@ public class EntityNpcDragon extends EntityNPCInterface { field_40172_ax += f1 * 0.5F; } super.onLivingUpdate(); + this.isFlying = isNpcFlying(); } // public float ticks = 0.0f; diff --git a/src/main/java/noppes/npcs/entity/EntityNpcPony.java b/src/main/java/noppes/npcs/entity/EntityNpcPony.java index ed6ca617c..81ba41233 100644 --- a/src/main/java/noppes/npcs/entity/EntityNpcPony.java +++ b/src/main/java/noppes/npcs/entity/EntityNpcPony.java @@ -35,4 +35,10 @@ public class EntityNpcPony extends EntityNPCInterface { super.onUpdate(); } + @Override + public void onLivingUpdate() { + super.onLivingUpdate(); + this.isFlying = isNpcFlying(); + } + } diff --git a/src/main/java/noppes/npcs/roles/RoleMount.java b/src/main/java/noppes/npcs/roles/RoleMount.java index a1a07c800..f930a4975 100644 --- a/src/main/java/noppes/npcs/roles/RoleMount.java +++ b/src/main/java/noppes/npcs/roles/RoleMount.java @@ -10,12 +10,13 @@ public class RoleMount extends RoleInterface { private float offsetX; private float offsetY; private float offsetZ; - private static final float MIN_JUMP_STRENGTH = 0.1F; - private static final float MAX_JUMP_STRENGTH = 3.0F; - private boolean storedReturnToStart; private float jumpStrength = 1.0F; private boolean allowSprint = true; + private boolean flyingMountEnabled = false; + private boolean hoverMode = false; + private float flyingAscendSpeed = 0.60F; + private float flyingDescendSpeed = 0.35F; public RoleMount(EntityNPCInterface npc) { super(npc); @@ -31,6 +32,10 @@ public class RoleMount extends RoleInterface { compound.setBoolean("MountReturnFlag", storedReturnToStart); compound.setFloat("MountJumpStrength", jumpStrength); compound.setBoolean("MountAllowSprint", allowSprint); + compound.setBoolean("MountAllowFlying", flyingMountEnabled); + compound.setBoolean("MountHoverMode", hoverMode); + compound.setFloat("MountFlyingAscendSpeed", flyingAscendSpeed); + compound.setFloat("MountFlyingDescendSpeed", flyingDescendSpeed); return compound; } @@ -50,6 +55,20 @@ public class RoleMount extends RoleInterface { jumpStrength = 1.0F; } allowSprint = !compound.hasKey("MountAllowSprint") || compound.getBoolean("MountAllowSprint"); + flyingMountEnabled = compound.hasKey("MountAllowFlying") && compound.getBoolean("MountAllowFlying"); + hoverMode = compound.hasKey("MountHoverMode") && compound.getBoolean("MountHoverMode"); + if (compound.hasKey("MountFlyingAscendSpeed")) { + setFlyingAscendSpeed(compound.getFloat("MountFlyingAscendSpeed")); + } else { + flyingAscendSpeed = 0.60F; + } + if (compound.hasKey("MountFlyingDescendSpeed")) { + setFlyingDescendSpeed(compound.getFloat("MountFlyingDescendSpeed")); + } else if (compound.hasKey("MountFlyingFallSpeed")) { + setFlyingDescendSpeed(compound.getFloat("MountFlyingFallSpeed")); + } else { + flyingDescendSpeed = 0.35F; + } npc.ais.returnToStart = false; } @@ -106,7 +125,7 @@ public class RoleMount extends RoleInterface { } public void setJumpStrength(float value) { - this.jumpStrength = ValueUtil.clamp(value, MIN_JUMP_STRENGTH, MAX_JUMP_STRENGTH); + this.jumpStrength = ValueUtil.clamp(value, 0.1F, 3.0F); } public float getJumpStrength() { @@ -121,6 +140,38 @@ public class RoleMount extends RoleInterface { return allowSprint; } + public void setFlyingMountEnabled(boolean enabled) { + this.flyingMountEnabled = enabled; + } + + public boolean isFlyingMountEnabled() { + return flyingMountEnabled; + } + + public void setHoverModeEnabled(boolean enabled) { + this.hoverMode = enabled; + } + + public boolean isHoverModeEnabled() { + return hoverMode; + } + + public void setFlyingAscendSpeed(float value) { + this.flyingAscendSpeed = ValueUtil.clamp(value, 0.1F, 3.0F); + } + + public float getFlyingAscendSpeed() { + return flyingAscendSpeed; + } + + public void setFlyingDescendSpeed(float value) { + this.flyingDescendSpeed = ValueUtil.clamp(value, 0.05F, 3.0F); + } + + public float getFlyingDescendSpeed() { + return flyingDescendSpeed; + } + public void setReturnToStartPreference(boolean value) { this.storedReturnToStart = value; npc.ais.returnToStart = false; diff --git a/src/main/java/noppes/npcs/util/NPCMountUtil.java b/src/main/java/noppes/npcs/util/NPCMountUtil.java new file mode 100644 index 000000000..fc04248a8 --- /dev/null +++ b/src/main/java/noppes/npcs/util/NPCMountUtil.java @@ -0,0 +1,335 @@ +package noppes.npcs.util; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.MathHelper; +import noppes.npcs.CustomNpcs; +import noppes.npcs.controllers.PlayerDataController; +import noppes.npcs.controllers.data.PlayerData; +import noppes.npcs.entity.EntityNPCInterface; +import noppes.npcs.constants.EnumRoleType; +import noppes.npcs.roles.RoleMount; + +public final class NPCMountUtil { + private static final int MOUNT_FLIGHT_TOGGLE_WINDOW = 7; + + private NPCMountUtil() { + } + + public static class MountState { + public Entity lastRider; + public boolean flightMode; + public boolean jumpPressed; + public int flightToggleTimer; + } + + public static boolean handleMountedMovement(EntityNPCInterface npc, MountState state, float strafe, float forward) { + if (npc.advanced.role != EnumRoleType.Mount || !(npc.roleInterface instanceof RoleMount)) { + resetMountedFlightState(npc, state); + return false; + } + if (npc.riddenByEntity != null && !(npc.riddenByEntity instanceof EntityPlayer)) { + npc.riddenByEntity.mountEntity(null); + return false; + } + if (!(npc.riddenByEntity instanceof EntityPlayer)) { + resetMountedFlightState(npc, state); + return false; + } + + RoleMount mount = (RoleMount) npc.roleInterface; + EntityPlayer rider = (EntityPlayer) npc.riddenByEntity; + updateMountedFlightState(npc, state, mount, rider); + + npc.prevRotationYaw = npc.rotationYaw = rider.rotationYaw; + npc.rotationPitch = rider.rotationPitch * 0.5F; + npc.setPositionAndRotation(npc.posX, npc.posY, npc.posZ, npc.rotationYaw, npc.rotationPitch); + npc.renderYawOffset = npc.rotationYaw; + npc.rotationYawHead = npc.rotationYaw; + + float controlledStrafe = rider.moveStrafing; + float controlledForward = rider.moveForward; + if (controlledForward <= 0.0F) { + controlledForward *= 0.25F; + } + + float moveSpeed = Math.max(0.0F, npc.getSpeed()); + boolean riderSprinting = rider.isSprinting() && mount.isSprintAllowed(); + if (riderSprinting) { + moveSpeed *= 1.2F; + } + + if (!mount.isSprintAllowed() && rider.isSprinting()) { + rider.setSprinting(false); + } + + if (moveSpeed <= 0.0F) { + controlledStrafe = 0.0F; + controlledForward = 0.0F; + } + + npc.setSprinting(riderSprinting); + npc.stepHeight = 1.0F; + npc.jumpMovementFactor = moveSpeed * 0.1F; + + npc.getNavigator().clearPathEntity(); + npc.setAttackTarget(null); + npc.setRevengeTarget(null); + + npc.fallDistance = 0.0F; + rider.fallDistance = 0.0F; + + applyMountedVerticalMotion(npc, state, mount, rider, moveSpeed, controlledForward); + npc.performMountedMovement(controlledStrafe, controlledForward, moveSpeed); + + return true; + } + + private static void applyMountedVerticalMotion(EntityNPCInterface npc, MountState state, RoleMount mount, EntityLivingBase rider, float moveSpeed, float forward) { + if (mount == null) { + return; + } + double jumpFactor = MathHelper.clamp_double(mount.getJumpStrength(), 0.1D, 3.0D); + boolean flyingEnabled = mount.isFlyingMountEnabled(); + + npc.setNpcJumpingState(false); + + if (flyingEnabled) { + double ascendSpeed = MathHelper.clamp_double(mount.getFlyingAscendSpeed(), 0.1D, 3.0D); + double descendSpeed = MathHelper.clamp_double(mount.getFlyingDescendSpeed(), 0.05D, 3.0D); + boolean flightMode = isMountInFlightMode(state); + boolean jumpPressed = rider.isJumping; + boolean descendPressed = rider instanceof EntityPlayer && isSpecialKeyDown((EntityPlayer) rider); + + if (flightMode) { + npc.motionY = 0.0D; + + if (jumpPressed) { + npc.motionY += ascendSpeed; + } else if (descendPressed) { + npc.motionY -= descendSpeed; + } + + npc.motionY = MathHelper.clamp_double(npc.motionY, -descendSpeed, ascendSpeed); + + npc.setNpcFlyingState(true); + npc.setNpcJumpingState(jumpPressed); + npc.isAirBorne = true; + npc.velocityChanged = true; + npc.fallDistance = 0.0F; + + if (!npc.worldObj.isRemote && rider instanceof EntityPlayer) { + EntityPlayer player = (EntityPlayer) rider; + if (!player.capabilities.isFlying) { + player.motionY = npc.motionY; + player.velocityChanged = true; + } + } + return; + } + + if (jumpPressed && npc.onGround) { + npc.motionY = jumpFactor; + npc.setNpcJumpingState(true); + npc.isAirBorne = true; + npc.velocityChanged = true; + npc.fallDistance = 0.0F; + if (!npc.worldObj.isRemote) { + net.minecraftforge.common.ForgeHooks.onLivingJump(npc); + } + return; + } + + if (!npc.onGround) { + npc.motionY = Math.max(npc.motionY, -descendSpeed); + npc.setNpcFlyingState(!npc.isInWater()); + } else { + npc.setNpcFlyingState(false); + } + + return; + } + + if (npc.onGround) { + if (rider.isJumping) { + npc.motionY = jumpFactor; + npc.setNpcJumpingState(true); + npc.isAirBorne = true; + npc.velocityChanged = true; + npc.fallDistance = 0.0F; + if (!npc.worldObj.isRemote) { + net.minecraftforge.common.ForgeHooks.onLivingJump(npc); + } + } else { + npc.motionY = 0.0D; + } + return; + } + + npc.setNpcFlyingState(false); + } + + public static void handleMountRiderState(EntityNPCInterface npc, MountState state) { + if (npc.worldObj.isRemote) { + if (npc.advanced.role != EnumRoleType.Mount || !(npc.roleInterface instanceof RoleMount)) { + state.lastRider = null; + } + return; + } + + if (npc.advanced.role != EnumRoleType.Mount || !(npc.roleInterface instanceof RoleMount)) { + state.lastRider = null; + return; + } + + RoleMount mount = (RoleMount) npc.roleInterface; + Entity currentRider = npc.riddenByEntity; + if (currentRider != null && !(currentRider instanceof EntityPlayer)) { + Entity dismount = currentRider; + dismount.mountEntity(null); + stabilizeDismountedRider(dismount); + haltMountedMotion(npc, state); + applyUnriddenFlightDescent(npc, state, mount); + currentRider = null; + } + if (currentRider != state.lastRider) { + if (state.lastRider != null) { + stabilizeDismountedRider(state.lastRider); + } + if (currentRider == null) { + haltMountedMotion(npc, state); + applyUnriddenFlightDescent(npc, state, mount); + } else { + resetMountedFlightState(npc, state); + } + state.lastRider = currentRider; + } else if (currentRider == null) { + applyUnriddenFlightDescent(npc, state, mount); + } + } + + public static boolean isFlyingMountWithFlightEnabled(EntityNPCInterface npc) { + return npc.advanced.role == EnumRoleType.Mount && npc.roleInterface instanceof RoleMount && ((RoleMount) npc.roleInterface).isFlyingMountEnabled(); + } + + public static void haltMountedMotion(EntityNPCInterface npc, MountState state) { + npc.motionX = 0.0D; + npc.motionY = 0.0D; + npc.motionZ = 0.0D; + npc.velocityChanged = true; + npc.isAirBorne = false; + npc.setSprinting(false); + npc.moveForward = 0.0F; + npc.moveStrafing = 0.0F; + npc.setAIMoveSpeed(0.0F); + npc.limbSwingAmount = 0.0F; + npc.limbSwing = 0.0F; + npc.getNavigator().clearPathEntity(); + npc.fallDistance = 0.0F; + resetMountedFlightState(npc, state); + } + + public static void applyUnriddenFlightDescent(EntityNPCInterface npc, MountState state, RoleMount mount) { + if (mount == null || !mount.isFlyingMountEnabled()) { + npc.setNpcFlyingState(false); + npc.setNpcJumpingState(false); + return; + } + if (npc.onGround) { + npc.motionY = 0.0D; + npc.setNpcFlyingState(false); + npc.setNpcJumpingState(false); + return; + } + + double descendSpeed = MathHelper.clamp_double(mount.getFlyingDescendSpeed(), 0.05D, 3.0D); + if (descendSpeed <= 0.0D) { + npc.setNpcFlyingState(false); + npc.setNpcJumpingState(false); + return; + } + + double desired = -descendSpeed; + npc.motionY = desired; + npc.isAirBorne = true; + npc.velocityChanged = true; + npc.fallDistance = 0.0F; + npc.setNpcFlyingState(true); + npc.setNpcJumpingState(false); + } + + public static void stabilizeDismountedRider(Entity rider) { + boolean wasOnGround = rider.onGround; + double previousMotionX = rider.motionX; + double previousMotionY = rider.motionY; + double previousMotionZ = rider.motionZ; + float previousFall = rider.fallDistance; + + if (wasOnGround) { + rider.motionX = 0.0D; + rider.motionY = 0.0D; + rider.motionZ = 0.0D; + rider.fallDistance = 0.0F; + rider.onGround = true; + } else { + rider.motionX = previousMotionX; + rider.motionY = previousMotionY; + rider.motionZ = previousMotionZ; + rider.fallDistance = previousFall; + } + + rider.velocityChanged = true; + } + + private static void updateMountedFlightState(EntityNPCInterface npc, MountState state, RoleMount mount, EntityPlayer rider) { + if (mount == null || rider == null || !mount.isFlyingMountEnabled()) { + resetMountedFlightState(npc, state); + return; + } + + if (npc.onGround) { + state.flightMode = false; + } + + boolean jumpPressed = rider.isJumping; + if (jumpPressed && !state.jumpPressed) { + if (state.flightToggleTimer > 0) { + state.flightMode = !state.flightMode; + state.flightToggleTimer = 0; + } else { + state.flightToggleTimer = MOUNT_FLIGHT_TOGGLE_WINDOW; + } + } + + if (state.flightToggleTimer > 0) { + state.flightToggleTimer--; + } + + state.jumpPressed = jumpPressed; + } + + public static boolean isMountInFlightMode(MountState state) { + return state.flightMode; + } + + public static void resetMountedFlightState(EntityNPCInterface npc, MountState state) { + state.flightMode = false; + state.jumpPressed = false; + state.flightToggleTimer = 0; + npc.setNpcFlyingState(false); + npc.setNpcJumpingState(false); + } + + private static boolean isSpecialKeyDown(EntityPlayer rider) { + if (rider == null) { + return false; + } + if (rider.worldObj.isRemote) { + PlayerData data = CustomNpcs.proxy.getPlayerData(rider); + return data != null && data.isSpecialKeyDown(); + } + PlayerData data = PlayerDataController.Instance.getPlayerData(rider); + return data != null && data.isSpecialKeyDown(); + } +} diff --git a/src/main/resources/assets/customnpcs/lang/cs_CZ.lang b/src/main/resources/assets/customnpcs/lang/cs_CZ.lang index e574a1fb1..c45e3dade 100644 --- a/src/main/resources/assets/customnpcs/lang/cs_CZ.lang +++ b/src/main/resources/assets/customnpcs/lang/cs_CZ.lang @@ -440,10 +440,11 @@ item.npcCrown.name=Koruna item.npcCrown2.name=Koruna key.npcInventory=NPC inventář role.mount=Mount -role.mount.offsetx=Seat Offset X -role.mount.offsety=Seat Offset Y -role.mount.offsetz=Seat Offset Z -role.mount.jumpheight=Jump Strength -role.mount.sprint=Allow Sprinting +role.mount.offsetx=Seat X +role.mount.offsety=Seat Y +role.mount.offsetz=Seat Z +role.mount.jumpheight=Jump +role.mount.sprint=Sprint role.mount.reset=Reset Position -role.mount.walkspeed=Walking speed: %s +role.mount.walkspeed=Walk Speed: %s +key.customnpcs.special=Special Key diff --git a/src/main/resources/assets/customnpcs/lang/de_DE.lang b/src/main/resources/assets/customnpcs/lang/de_DE.lang index 86269b7b0..32170875b 100644 --- a/src/main/resources/assets/customnpcs/lang/de_DE.lang +++ b/src/main/resources/assets/customnpcs/lang/de_DE.lang @@ -872,10 +872,11 @@ companion.stage=Bühne key.npcInventory=NPC Inventar role.mount=Mount -role.mount.offsetx=Seat Offset X -role.mount.offsety=Seat Offset Y -role.mount.offsetz=Seat Offset Z -role.mount.jumpheight=Jump Strength -role.mount.sprint=Allow Sprinting +role.mount.offsetx=Seat X +role.mount.offsety=Seat Y +role.mount.offsetz=Seat Z +role.mount.jumpheight=Jump +role.mount.sprint=Sprint role.mount.reset=Reset Position -role.mount.walkspeed=Walking speed: %s +role.mount.walkspeed=Walk Speed: %s +key.customnpcs.special=Special Key diff --git a/src/main/resources/assets/customnpcs/lang/en_US.lang b/src/main/resources/assets/customnpcs/lang/en_US.lang index 6026ed59a..466acfb99 100644 --- a/src/main/resources/assets/customnpcs/lang/en_US.lang +++ b/src/main/resources/assets/customnpcs/lang/en_US.lang @@ -1,6 +1,7 @@ customnpcs.defaultTextColor=404040 key.categories.customnpc=CustomNPC+ +key.customnpcs.special=Special Key key.npcInventory=NPC Inventory entity.CustomNpc.name=Custom Npc @@ -418,13 +419,16 @@ role.mailman=Mailman role.marketname=Linked Marketname role.companion=Companion role.mount=Mount -role.mount.offsetx=Seat Offset X -role.mount.offsety=Seat Offset Y -role.mount.offsetz=Seat Offset Z -role.mount.jumpheight=Jump Strength -role.mount.sprint=Allow Sprinting -role.mount.reset=Reset Position -role.mount.walkspeed=Walking speed: %s +role.mount.offsetx=Seat X +role.mount.offsety=Seat Y +role.mount.offsetz=Seat Z +role.mount.jumpheight=Jump +role.mount.sprint=Sprint +role.mount.fly=Flying +role.mount.hover=Hover +role.mount.ascendspeed=Ascend Speed +role.mount.descendspeed=Descend Speed +role.mount.walkspeed=Walk Speed: %s trader.insufficient=Insufficient Currency trader.sufficient=Sufficient Currency diff --git a/src/main/resources/assets/customnpcs/lang/es_AR.lang b/src/main/resources/assets/customnpcs/lang/es_AR.lang index f0d9533a2..03e80082e 100644 --- a/src/main/resources/assets/customnpcs/lang/es_AR.lang +++ b/src/main/resources/assets/customnpcs/lang/es_AR.lang @@ -500,10 +500,11 @@ item.npcMusket.name=Mosquete item.npcExcalibur.name=Excalibur key.npcInventory=Inventario de NPC role.mount=Mount -role.mount.offsetx=Seat Offset X -role.mount.offsety=Seat Offset Y -role.mount.offsetz=Seat Offset Z -role.mount.jumpheight=Jump Strength -role.mount.sprint=Allow Sprinting +role.mount.offsetx=Seat X +role.mount.offsety=Seat Y +role.mount.offsetz=Seat Z +role.mount.jumpheight=Jump +role.mount.sprint=Sprint role.mount.reset=Reset Position -role.mount.walkspeed=Walking speed: %s +role.mount.walkspeed=Walk Speed: %s +key.customnpcs.special=Special Key diff --git a/src/main/resources/assets/customnpcs/lang/es_MX.lang b/src/main/resources/assets/customnpcs/lang/es_MX.lang index 9b3ee6a1f..7ea1b25b2 100644 --- a/src/main/resources/assets/customnpcs/lang/es_MX.lang +++ b/src/main/resources/assets/customnpcs/lang/es_MX.lang @@ -500,10 +500,11 @@ item.npcMusket.name=Mosquete item.npcExcalibur.name=Excalibur key.npcInventory=Inventario de NPC role.mount=Mount -role.mount.offsetx=Seat Offset X -role.mount.offsety=Seat Offset Y -role.mount.offsetz=Seat Offset Z -role.mount.jumpheight=Jump Strength -role.mount.sprint=Allow Sprinting +role.mount.offsetx=Seat X +role.mount.offsety=Seat Y +role.mount.offsetz=Seat Z +role.mount.jumpheight=Jump +role.mount.sprint=Sprint role.mount.reset=Reset Position -role.mount.walkspeed=Walking speed: %s +role.mount.walkspeed=Walk Speed: %s +key.customnpcs.special=Special Key diff --git a/src/main/resources/assets/customnpcs/lang/fr_FR.lang b/src/main/resources/assets/customnpcs/lang/fr_FR.lang index 6fc66154c..cea83f824 100644 --- a/src/main/resources/assets/customnpcs/lang/fr_FR.lang +++ b/src/main/resources/assets/customnpcs/lang/fr_FR.lang @@ -542,10 +542,11 @@ item.npcMusket.name=Mousquet item.npcExcalibur.name=Excalibur key.npcInventory=Inventaire Pnj role.mount=Mount -role.mount.offsetx=Seat Offset X -role.mount.offsety=Seat Offset Y -role.mount.offsetz=Seat Offset Z -role.mount.jumpheight=Jump Strength -role.mount.sprint=Allow Sprinting +role.mount.offsetx=Seat X +role.mount.offsety=Seat Y +role.mount.offsetz=Seat Z +role.mount.jumpheight=Jump +role.mount.sprint=Sprint role.mount.reset=Reset Position -role.mount.walkspeed=Walking speed: %s +role.mount.walkspeed=Walk Speed: %s +key.customnpcs.special=Special Key diff --git a/src/main/resources/assets/customnpcs/lang/hu_HU.lang b/src/main/resources/assets/customnpcs/lang/hu_HU.lang index 63ee943af..db2fc6c38 100644 --- a/src/main/resources/assets/customnpcs/lang/hu_HU.lang +++ b/src/main/resources/assets/customnpcs/lang/hu_HU.lang @@ -542,10 +542,11 @@ item.npcMusket.name=Muskéta item.npcExcalibur.name=Excalibur key.npcInventory=NPC felszerelés role.mount=Mount -role.mount.offsetx=Seat Offset X -role.mount.offsety=Seat Offset Y -role.mount.offsetz=Seat Offset Z -role.mount.jumpheight=Jump Strength -role.mount.sprint=Allow Sprinting +role.mount.offsetx=Seat X +role.mount.offsety=Seat Y +role.mount.offsetz=Seat Z +role.mount.jumpheight=Jump +role.mount.sprint=Sprint role.mount.reset=Reset Position -role.mount.walkspeed=Walking speed: %s +role.mount.walkspeed=Walk Speed: %s +key.customnpcs.special=Special Key diff --git a/src/main/resources/assets/customnpcs/lang/id_ID.lang b/src/main/resources/assets/customnpcs/lang/id_ID.lang index 2b53a9639..025320030 100644 --- a/src/main/resources/assets/customnpcs/lang/id_ID.lang +++ b/src/main/resources/assets/customnpcs/lang/id_ID.lang @@ -317,10 +317,11 @@ item.npcRuby.name=Permata Rubi role.mount=Mount -role.mount.offsetx=Seat Offset X -role.mount.offsety=Seat Offset Y -role.mount.offsetz=Seat Offset Z -role.mount.jumpheight=Jump Strength -role.mount.sprint=Allow Sprinting +role.mount.offsetx=Seat X +role.mount.offsety=Seat Y +role.mount.offsetz=Seat Z +role.mount.jumpheight=Jump +role.mount.sprint=Sprint role.mount.reset=Reset Position -role.mount.walkspeed=Walking speed: %s +role.mount.walkspeed=Walk Speed: %s +key.customnpcs.special=Special Key diff --git a/src/main/resources/assets/customnpcs/lang/ko_KR.lang b/src/main/resources/assets/customnpcs/lang/ko_KR.lang index 89f7c8d7d..e3a672289 100644 --- a/src/main/resources/assets/customnpcs/lang/ko_KR.lang +++ b/src/main/resources/assets/customnpcs/lang/ko_KR.lang @@ -797,10 +797,11 @@ enchantment.poison=독 key.npcInventory=NPC 인벤토리 role.mount=Mount -role.mount.offsetx=Seat Offset X -role.mount.offsety=Seat Offset Y -role.mount.offsetz=Seat Offset Z -role.mount.jumpheight=Jump Strength -role.mount.sprint=Allow Sprinting +role.mount.offsetx=Seat X +role.mount.offsety=Seat Y +role.mount.offsetz=Seat Z +role.mount.jumpheight=Jump +role.mount.sprint=Sprint role.mount.reset=Reset Position -role.mount.walkspeed=Walking speed: %s +role.mount.walkspeed=Walk Speed: %s +key.customnpcs.special=Special Key diff --git a/src/main/resources/assets/customnpcs/lang/nl_NL.lang b/src/main/resources/assets/customnpcs/lang/nl_NL.lang index acae2f273..297cfa1fc 100644 --- a/src/main/resources/assets/customnpcs/lang/nl_NL.lang +++ b/src/main/resources/assets/customnpcs/lang/nl_NL.lang @@ -232,10 +232,11 @@ item.npcNinjaHead.name=Ninja Helm item.npcNinjaChest.name=Ninja Borst item.npcNinjaPants.name=Ninja Broek role.mount=Mount -role.mount.offsetx=Seat Offset X -role.mount.offsety=Seat Offset Y -role.mount.offsetz=Seat Offset Z -role.mount.jumpheight=Jump Strength -role.mount.sprint=Allow Sprinting +role.mount.offsetx=Seat X +role.mount.offsety=Seat Y +role.mount.offsetz=Seat Z +role.mount.jumpheight=Jump +role.mount.sprint=Sprint role.mount.reset=Reset Position -role.mount.walkspeed=Walking speed: %s +role.mount.walkspeed=Walk Speed: %s +key.customnpcs.special=Special Key diff --git a/src/main/resources/assets/customnpcs/lang/pl_PL.lang b/src/main/resources/assets/customnpcs/lang/pl_PL.lang index ff860e0ab..e84de64ee 100644 --- a/src/main/resources/assets/customnpcs/lang/pl_PL.lang +++ b/src/main/resources/assets/customnpcs/lang/pl_PL.lang @@ -498,10 +498,11 @@ item.npcMusket.name=Muszkiet item.npcExcalibur.name=Excalibur key.npcInventory=Inwentarz npc role.mount=Mount -role.mount.offsetx=Seat Offset X -role.mount.offsety=Seat Offset Y -role.mount.offsetz=Seat Offset Z -role.mount.jumpheight=Jump Strength -role.mount.sprint=Allow Sprinting +role.mount.offsetx=Seat X +role.mount.offsety=Seat Y +role.mount.offsetz=Seat Z +role.mount.jumpheight=Jump +role.mount.sprint=Sprint role.mount.reset=Reset Position -role.mount.walkspeed=Walking speed: %s +role.mount.walkspeed=Walk Speed: %s +key.customnpcs.special=Special Key diff --git a/src/main/resources/assets/customnpcs/lang/pt_BR.lang b/src/main/resources/assets/customnpcs/lang/pt_BR.lang index 066697e8c..6d07b2df0 100644 --- a/src/main/resources/assets/customnpcs/lang/pt_BR.lang +++ b/src/main/resources/assets/customnpcs/lang/pt_BR.lang @@ -498,10 +498,11 @@ item.npcMusket.name=Mosquete item.npcExcalibur.name=Excalibur key.npcInventory=Inventário NPC role.mount=Mount -role.mount.offsetx=Seat Offset X -role.mount.offsety=Seat Offset Y -role.mount.offsetz=Seat Offset Z -role.mount.jumpheight=Jump Strength -role.mount.sprint=Allow Sprinting +role.mount.offsetx=Seat X +role.mount.offsety=Seat Y +role.mount.offsetz=Seat Z +role.mount.jumpheight=Jump +role.mount.sprint=Sprint role.mount.reset=Reset Position -role.mount.walkspeed=Walking speed: %s +role.mount.walkspeed=Walk Speed: %s +key.customnpcs.special=Special Key diff --git a/src/main/resources/assets/customnpcs/lang/ru_RU.lang b/src/main/resources/assets/customnpcs/lang/ru_RU.lang index a34739f0d..2c6f76422 100644 --- a/src/main/resources/assets/customnpcs/lang/ru_RU.lang +++ b/src/main/resources/assets/customnpcs/lang/ru_RU.lang @@ -1021,10 +1021,11 @@ questgui.accept=Принять key.npcInventory=Инвентарь NPC role.mount=Mount -role.mount.offsetx=Seat Offset X -role.mount.offsety=Seat Offset Y -role.mount.offsetz=Seat Offset Z -role.mount.jumpheight=Jump Strength -role.mount.sprint=Allow Sprinting +role.mount.offsetx=Seat X +role.mount.offsety=Seat Y +role.mount.offsetz=Seat Z +role.mount.jumpheight=Jump +role.mount.sprint=Sprint role.mount.reset=Reset Position -role.mount.walkspeed=Walking speed: %s +role.mount.walkspeed=Walk Speed: %s +key.customnpcs.special=Special Key diff --git a/src/main/resources/assets/customnpcs/lang/sv_SE.lang b/src/main/resources/assets/customnpcs/lang/sv_SE.lang index 70a29ebc9..7e536400b 100644 --- a/src/main/resources/assets/customnpcs/lang/sv_SE.lang +++ b/src/main/resources/assets/customnpcs/lang/sv_SE.lang @@ -308,10 +308,11 @@ item.npcPendant.name=Pendant item.npcBlueprint.name=Blueprint item.npcRing.name=Ring role.mount=Mount -role.mount.offsetx=Seat Offset X -role.mount.offsety=Seat Offset Y -role.mount.offsetz=Seat Offset Z -role.mount.jumpheight=Jump Strength -role.mount.sprint=Allow Sprinting +role.mount.offsetx=Seat X +role.mount.offsety=Seat Y +role.mount.offsetz=Seat Z +role.mount.jumpheight=Jump +role.mount.sprint=Sprint role.mount.reset=Reset Position -role.mount.walkspeed=Walking speed: %s +role.mount.walkspeed=Walk Speed: %s +key.customnpcs.special=Special Key diff --git a/src/main/resources/assets/customnpcs/lang/zh_CN.lang b/src/main/resources/assets/customnpcs/lang/zh_CN.lang index 00fb8c796..32105c7db 100644 --- a/src/main/resources/assets/customnpcs/lang/zh_CN.lang +++ b/src/main/resources/assets/customnpcs/lang/zh_CN.lang @@ -1244,10 +1244,11 @@ hitbox.height=身高比例 key.npcInventory=NPC物品栏 role.mount=Mount -role.mount.offsetx=Seat Offset X -role.mount.offsety=Seat Offset Y -role.mount.offsetz=Seat Offset Z -role.mount.jumpheight=Jump Strength -role.mount.sprint=Allow Sprinting +role.mount.offsetx=Seat X +role.mount.offsety=Seat Y +role.mount.offsetz=Seat Z +role.mount.jumpheight=Jump +role.mount.sprint=Sprint role.mount.reset=Reset Position -role.mount.walkspeed=Walking speed: %s +role.mount.walkspeed=Walk Speed: %s +key.customnpcs.special=Special Key diff --git a/src/main/resources/assets/customnpcs/lang/zh_TW.lang b/src/main/resources/assets/customnpcs/lang/zh_TW.lang index a4f3d4ae5..b7f2c04f5 100644 --- a/src/main/resources/assets/customnpcs/lang/zh_TW.lang +++ b/src/main/resources/assets/customnpcs/lang/zh_TW.lang @@ -314,10 +314,11 @@ item.npcSaphire.name=藍寶石 item.npcAmethyst.name=紫水晶 item.npcRuby.name=紅寶石 role.mount=Mount -role.mount.offsetx=Seat Offset X -role.mount.offsety=Seat Offset Y -role.mount.offsetz=Seat Offset Z -role.mount.jumpheight=Jump Strength -role.mount.sprint=Allow Sprinting +role.mount.offsetx=Seat X +role.mount.offsety=Seat Y +role.mount.offsetz=Seat Z +role.mount.jumpheight=Jump +role.mount.sprint=Sprint role.mount.reset=Reset Position -role.mount.walkspeed=Walking speed: %s +role.mount.walkspeed=Walk Speed: %s +key.customnpcs.special=Special Key From c33ac04d11f21dead944968e8445128cf84cc3e0 Mon Sep 17 00:00:00 2001 From: Kam Date: Fri, 7 Nov 2025 22:44:22 -0500 Subject: [PATCH 17/38] Refine mount GUI layout and flight handling --- .../npcs/client/gui/roles/GuiNpcMount.java | 7 +-- .../noppes/npcs/entity/EntityNPCFlying.java | 8 +++ .../npcs/entity/EntityNPCInterface.java | 54 +++++++++++++++++-- .../java/noppes/npcs/util/NPCMountUtil.java | 3 ++ 4 files changed, 63 insertions(+), 9 deletions(-) diff --git a/src/main/java/noppes/npcs/client/gui/roles/GuiNpcMount.java b/src/main/java/noppes/npcs/client/gui/roles/GuiNpcMount.java index 8c63d2c99..0639113af 100644 --- a/src/main/java/noppes/npcs/client/gui/roles/GuiNpcMount.java +++ b/src/main/java/noppes/npcs/client/gui/roles/GuiNpcMount.java @@ -4,7 +4,6 @@ import kamkeel.npcs.network.PacketClient; import kamkeel.npcs.network.packets.request.role.RoleSavePacket; import net.minecraft.client.gui.GuiButton; import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.util.StatCollector; import noppes.npcs.client.gui.util.GuiNPCInterface2; import noppes.npcs.client.gui.util.GuiNpcButton; import noppes.npcs.client.gui.util.GuiNpcLabel; @@ -31,7 +30,7 @@ public class GuiNpcMount extends GuiNPCInterface2 implements ITextfieldListener } int leftLabelX = guiLeft + 4; - int leftFieldX = guiLeft + 140; + int leftFieldX = guiLeft + 116; int leftY = guiTop + 6; leftY = addOffsetRow(0, 10, "role.mount.offsetx", role.getOffsetX(), leftLabelX, leftFieldX, leftY); @@ -43,10 +42,6 @@ public class GuiNpcMount extends GuiNPCInterface2 implements ITextfieldListener addButton(new GuiNpcButton(101, leftFieldX, leftY, 60, 20, new String[]{"gui.no", "gui.yes"}, role.isSprintAllowed() ? 1 : 0)); leftY += 24; - GuiNpcLabel speedLabel = new GuiNpcLabel(5, "", leftLabelX, leftY + 5); - speedLabel.label = StatCollector.translateToLocalFormatted("role.mount.walkspeed", npc.ais.getWalkingSpeed()); - addLabel(speedLabel); - int rightLabelX = guiLeft + 214; int rightFieldX = guiLeft + 320; int rightY = guiTop + 6; diff --git a/src/main/java/noppes/npcs/entity/EntityNPCFlying.java b/src/main/java/noppes/npcs/entity/EntityNPCFlying.java index 2996ca7f3..3f57ce85f 100644 --- a/src/main/java/noppes/npcs/entity/EntityNPCFlying.java +++ b/src/main/java/noppes/npcs/entity/EntityNPCFlying.java @@ -107,6 +107,8 @@ public abstract class EntityNPCFlying extends EntityNPCInterface { return; } + boolean flightModeActive = isMountFlightModeActive(); + boolean aboveLimit = false; double heightOffGround = this.posY - this.worldObj.getTopSolidOrLiquidBlock((int) this.posX, (int) this.posZ); if (heightOffGround < 0) { @@ -132,6 +134,12 @@ public abstract class EntityNPCFlying extends EntityNPCInterface { this.flyLimitAllow = true; setNpcFlyingState(true); + if (flightModeActive) { + super.performMountedMovement(strafe, forward, moveSpeed); + this.velocityChanged = true; + return; + } + double d3 = this.motionY; super.performMountedMovement(strafe, forward, moveSpeed); this.motionY = d3; diff --git a/src/main/java/noppes/npcs/entity/EntityNPCInterface.java b/src/main/java/noppes/npcs/entity/EntityNPCInterface.java index 305a9098b..ff269b964 100644 --- a/src/main/java/noppes/npcs/entity/EntityNPCInterface.java +++ b/src/main/java/noppes/npcs/entity/EntityNPCInterface.java @@ -2128,11 +2128,59 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti public void performMountedMovement(float strafe, float forward, float moveSpeed) { this.moveStrafing = strafe; this.moveForward = forward; - if (!worldObj.isRemote) { - this.setAIMoveSpeed(moveSpeed); - super.moveEntityWithHeading(strafe, forward); + + if (isMountFlightModeActive()) { + performMountedFlightMovement(strafe, forward, moveSpeed); + return; } + if (!worldObj.isRemote) { + this.setAIMoveSpeed(moveSpeed); + } + super.moveEntityWithHeading(strafe, forward); + updateMountedLimbSwing(); + } + + protected boolean isMountFlightModeActive() { + return NPCMountUtil.isMountInFlightMode(mountState); + } + + private void performMountedFlightMovement(float strafe, float forward, float moveSpeed) { + if (!worldObj.isRemote) { + this.setAIMoveSpeed(moveSpeed); + } + + float inputMagnitude = MathHelper.sqrt_float(strafe * strafe + forward * forward); + if (inputMagnitude < 1.0E-3F) { + this.motionX *= 0.6D; + this.motionZ *= 0.6D; + if (Math.abs(this.motionX) < 0.003D) { + this.motionX = 0.0D; + } + if (Math.abs(this.motionZ) < 0.003D) { + this.motionZ = 0.0D; + } + } else { + float normStrafe = strafe / inputMagnitude; + float normForward = forward / inputMagnitude; + float yawRad = this.rotationYaw * (float) Math.PI / 180.0F; + double sinYaw = Math.sin(yawRad); + double cosYaw = Math.cos(yawRad); + double targetMotionX = (normStrafe * cosYaw - normForward * sinYaw) * moveSpeed; + double targetMotionZ = (normForward * cosYaw + normStrafe * sinYaw) * moveSpeed; + this.motionX = targetMotionX; + this.motionZ = targetMotionZ; + } + + this.moveEntity(this.motionX, this.motionY, this.motionZ); + this.velocityChanged = true; + if (!this.onGround) { + this.isAirBorne = true; + } + updateMountedLimbSwing(); + } + + private void updateMountedLimbSwing() { this.prevLimbSwingAmount = this.limbSwingAmount; double deltaX = this.posX - this.prevPosX; double deltaZ = this.posZ - this.prevPosZ; diff --git a/src/main/java/noppes/npcs/util/NPCMountUtil.java b/src/main/java/noppes/npcs/util/NPCMountUtil.java index fc04248a8..1caa4ba14 100644 --- a/src/main/java/noppes/npcs/util/NPCMountUtil.java +++ b/src/main/java/noppes/npcs/util/NPCMountUtil.java @@ -236,6 +236,9 @@ public final class NPCMountUtil { npc.setNpcJumpingState(false); return; } + if (npc.canFly()) { + return; + } if (npc.onGround) { npc.motionY = 0.0D; npc.setNpcFlyingState(false); From b13be2969ac550e2418839252b0c89172b2d2c4b Mon Sep 17 00:00:00 2001 From: Kam Date: Thu, 13 Nov 2025 16:47:20 -0500 Subject: [PATCH 18/38] 1.10.3 --- build.gradle | 2 +- src/main/java/noppes/npcs/CustomNpcs.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index dc3a7dd3b..e0873b3e9 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ plugins { id 'idea' } -version = "1.10.2" +version = "1.10.3-beta1" group= "kamkeel.customnpc-plus" archivesBaseName = "CustomNPC-Plus" diff --git a/src/main/java/noppes/npcs/CustomNpcs.java b/src/main/java/noppes/npcs/CustomNpcs.java index 68eaa60b7..4b5750c6a 100644 --- a/src/main/java/noppes/npcs/CustomNpcs.java +++ b/src/main/java/noppes/npcs/CustomNpcs.java @@ -100,7 +100,7 @@ import noppes.npcs.scripted.NpcAPI; import java.io.File; import java.util.Set; -@Mod(modid = "customnpcs", name = "CustomNPC+", version = "1.10.2") +@Mod(modid = "customnpcs", name = "CustomNPC+", version = "1.10.3") public class CustomNpcs { @SidedProxy(clientSide = "noppes.npcs.client.ClientProxy", serverSide = "noppes.npcs.CommonProxy") From ee413c31b95957f16306166817d21193dc4c9b4f Mon Sep 17 00:00:00 2001 From: Kam Date: Thu, 13 Nov 2025 19:54:10 -0500 Subject: [PATCH 19/38] Improve mount client sync and flight speed scaling --- .../npcs/entity/EntityNPCInterface.java | 90 ++++++++++++------- .../java/noppes/npcs/util/NPCMountUtil.java | 61 ++++++++++--- 2 files changed, 103 insertions(+), 48 deletions(-) diff --git a/src/main/java/noppes/npcs/entity/EntityNPCInterface.java b/src/main/java/noppes/npcs/entity/EntityNPCInterface.java index ff269b964..58aadb253 100644 --- a/src/main/java/noppes/npcs/entity/EntityNPCInterface.java +++ b/src/main/java/noppes/npcs/entity/EntityNPCInterface.java @@ -2129,15 +2129,38 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti this.moveStrafing = strafe; this.moveForward = forward; - if (isMountFlightModeActive()) { - performMountedFlightMovement(strafe, forward, moveSpeed); - return; - } + boolean flightMode = isMountFlightModeActive(); + double prevMotionX = this.motionX; + double prevMotionY = this.motionY; + double prevMotionZ = this.motionZ; + float previousJumpFactor = this.jumpMovementFactor; + float appliedJumpFactor = getMountedAirStrafeFactor(moveSpeed, flightMode); + + this.jumpMovementFactor = appliedJumpFactor; if (!worldObj.isRemote) { this.setAIMoveSpeed(moveSpeed); } + super.moveEntityWithHeading(strafe, forward); + + if (flightMode) { + this.motionY = prevMotionY; + this.isAirBorne = true; + } + + this.jumpMovementFactor = previousJumpFactor; + + if (!worldObj.isRemote) { + double newMotionX = this.motionX; + double newMotionY = this.motionY; + double newMotionZ = this.motionZ; + if (Math.abs(newMotionX - prevMotionX) > 1.0E-5D || Math.abs(newMotionY - prevMotionY) > 1.0E-5D || Math.abs(newMotionZ - prevMotionZ) > 1.0E-5D) { + this.velocityChanged = true; + } + } + + syncMountedRiderVelocity(); updateMountedLimbSwing(); } @@ -2145,39 +2168,38 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti return NPCMountUtil.isMountInFlightMode(mountState); } - private void performMountedFlightMovement(float strafe, float forward, float moveSpeed) { + private float getMountedAirStrafeFactor(float moveSpeed, boolean flightMode) { + if (moveSpeed <= 0.0F) { + return 0.0F; + } + if (!flightMode) { + return moveSpeed * 0.1F; + } + final float AIR_FRICTION = 0.91F; + final float BASE = 0.16277136F; + return moveSpeed * (BASE / (AIR_FRICTION * AIR_FRICTION * AIR_FRICTION)); + } + + private void syncMountedRiderVelocity() { + if (!(this.riddenByEntity instanceof EntityLivingBase)) { + return; + } + EntityLivingBase rider = (EntityLivingBase) this.riddenByEntity; + double riderPrevMotionX = rider.motionX; + double riderPrevMotionY = rider.motionY; + double riderPrevMotionZ = rider.motionZ; + + rider.motionX = this.motionX; + rider.motionY = this.motionY; + rider.motionZ = this.motionZ; + rider.fallDistance = 0.0F; + rider.isAirBorne = !this.onGround; + if (!worldObj.isRemote) { - this.setAIMoveSpeed(moveSpeed); - } - - float inputMagnitude = MathHelper.sqrt_float(strafe * strafe + forward * forward); - if (inputMagnitude < 1.0E-3F) { - this.motionX *= 0.6D; - this.motionZ *= 0.6D; - if (Math.abs(this.motionX) < 0.003D) { - this.motionX = 0.0D; + if (Math.abs(rider.motionX - riderPrevMotionX) > 1.0E-5D || Math.abs(rider.motionY - riderPrevMotionY) > 1.0E-5D || Math.abs(rider.motionZ - riderPrevMotionZ) > 1.0E-5D) { + rider.velocityChanged = true; } - if (Math.abs(this.motionZ) < 0.003D) { - this.motionZ = 0.0D; - } - } else { - float normStrafe = strafe / inputMagnitude; - float normForward = forward / inputMagnitude; - float yawRad = this.rotationYaw * (float) Math.PI / 180.0F; - double sinYaw = Math.sin(yawRad); - double cosYaw = Math.cos(yawRad); - double targetMotionX = (normStrafe * cosYaw - normForward * sinYaw) * moveSpeed; - double targetMotionZ = (normForward * cosYaw + normStrafe * sinYaw) * moveSpeed; - this.motionX = targetMotionX; - this.motionZ = targetMotionZ; } - - this.moveEntity(this.motionX, this.motionY, this.motionZ); - this.velocityChanged = true; - if (!this.onGround) { - this.isAirBorne = true; - } - updateMountedLimbSwing(); } private void updateMountedLimbSwing() { diff --git a/src/main/java/noppes/npcs/util/NPCMountUtil.java b/src/main/java/noppes/npcs/util/NPCMountUtil.java index 1caa4ba14..708bf3b76 100644 --- a/src/main/java/noppes/npcs/util/NPCMountUtil.java +++ b/src/main/java/noppes/npcs/util/NPCMountUtil.java @@ -54,7 +54,7 @@ public final class NPCMountUtil { controlledForward *= 0.25F; } - float moveSpeed = Math.max(0.0F, npc.getSpeed()); + float moveSpeed = getMountMoveSpeed(npc); boolean riderSprinting = rider.isSprinting() && mount.isSprintAllowed(); if (riderSprinting) { moveSpeed *= 1.2F; @@ -71,7 +71,6 @@ public final class NPCMountUtil { npc.setSprinting(riderSprinting); npc.stepHeight = 1.0F; - npc.jumpMovementFactor = moveSpeed * 0.1F; npc.getNavigator().clearPathEntity(); npc.setAttackTarget(null); @@ -86,6 +85,10 @@ public final class NPCMountUtil { return true; } + private static float getMountMoveSpeed(EntityNPCInterface npc) { + return Math.max(0.0F, npc.getSpeed()); + } + private static void applyMountedVerticalMotion(EntityNPCInterface npc, MountState state, RoleMount mount, EntityLivingBase rider, float moveSpeed, float forward) { if (mount == null) { return; @@ -103,46 +106,65 @@ public final class NPCMountUtil { boolean descendPressed = rider instanceof EntityPlayer && isSpecialKeyDown((EntityPlayer) rider); if (flightMode) { - npc.motionY = 0.0D; + double previousMotionY = npc.motionY; + double newMotionY = 0.0D; if (jumpPressed) { - npc.motionY += ascendSpeed; + newMotionY += ascendSpeed; } else if (descendPressed) { - npc.motionY -= descendSpeed; + newMotionY -= descendSpeed; } - npc.motionY = MathHelper.clamp_double(npc.motionY, -descendSpeed, ascendSpeed); - + newMotionY = MathHelper.clamp_double(newMotionY, -descendSpeed, ascendSpeed); + npc.motionY = newMotionY; npc.setNpcFlyingState(true); npc.setNpcJumpingState(jumpPressed); npc.isAirBorne = true; - npc.velocityChanged = true; npc.fallDistance = 0.0F; + if (!npc.worldObj.isRemote && Math.abs(previousMotionY - newMotionY) > 1.0E-4D) { + npc.velocityChanged = true; + } + if (!npc.worldObj.isRemote && rider instanceof EntityPlayer) { EntityPlayer player = (EntityPlayer) rider; if (!player.capabilities.isFlying) { + double playerPrevMotion = player.motionY; player.motionY = npc.motionY; - player.velocityChanged = true; + if (Math.abs(playerPrevMotion - player.motionY) > 1.0E-4D) { + player.velocityChanged = true; + } } } return; } if (jumpPressed && npc.onGround) { + double previousMotionY = npc.motionY; npc.motionY = jumpFactor; npc.setNpcJumpingState(true); npc.isAirBorne = true; - npc.velocityChanged = true; npc.fallDistance = 0.0F; if (!npc.worldObj.isRemote) { + if (Math.abs(previousMotionY - npc.motionY) > 1.0E-4D) { + npc.velocityChanged = true; + } net.minecraftforge.common.ForgeHooks.onLivingJump(npc); } return; } if (!npc.onGround) { - npc.motionY = Math.max(npc.motionY, -descendSpeed); + double previousMotionY = npc.motionY; + double limitedMotionY = Math.max(npc.motionY, -descendSpeed); + if (Math.abs(previousMotionY - limitedMotionY) > 1.0E-4D) { + npc.motionY = limitedMotionY; + if (!npc.worldObj.isRemote) { + npc.velocityChanged = true; + } + } else { + npc.motionY = limitedMotionY; + } npc.setNpcFlyingState(!npc.isInWater()); } else { npc.setNpcFlyingState(false); @@ -152,13 +174,16 @@ public final class NPCMountUtil { } if (npc.onGround) { + double previousMotionY = npc.motionY; if (rider.isJumping) { npc.motionY = jumpFactor; npc.setNpcJumpingState(true); npc.isAirBorne = true; - npc.velocityChanged = true; npc.fallDistance = 0.0F; if (!npc.worldObj.isRemote) { + if (Math.abs(previousMotionY - npc.motionY) > 1.0E-4D) { + npc.velocityChanged = true; + } net.minecraftforge.common.ForgeHooks.onLivingJump(npc); } } else { @@ -214,10 +239,12 @@ public final class NPCMountUtil { } public static void haltMountedMotion(EntityNPCInterface npc, MountState state) { + double prevMotionX = npc.motionX; + double prevMotionY = npc.motionY; + double prevMotionZ = npc.motionZ; npc.motionX = 0.0D; npc.motionY = 0.0D; npc.motionZ = 0.0D; - npc.velocityChanged = true; npc.isAirBorne = false; npc.setSprinting(false); npc.moveForward = 0.0F; @@ -227,6 +254,9 @@ public final class NPCMountUtil { npc.limbSwing = 0.0F; npc.getNavigator().clearPathEntity(); npc.fallDistance = 0.0F; + if (!npc.worldObj.isRemote && (Math.abs(prevMotionX) > 1.0E-4D || Math.abs(prevMotionY) > 1.0E-4D || Math.abs(prevMotionZ) > 1.0E-4D)) { + npc.velocityChanged = true; + } resetMountedFlightState(npc, state); } @@ -254,12 +284,15 @@ public final class NPCMountUtil { } double desired = -descendSpeed; + double previousMotionY = npc.motionY; npc.motionY = desired; npc.isAirBorne = true; - npc.velocityChanged = true; npc.fallDistance = 0.0F; npc.setNpcFlyingState(true); npc.setNpcJumpingState(false); + if (!npc.worldObj.isRemote && Math.abs(previousMotionY - desired) > 1.0E-4D) { + npc.velocityChanged = true; + } } public static void stabilizeDismountedRider(Entity rider) { From bdf3791e38169c6d9119d0c079d536958cc12b50 Mon Sep 17 00:00:00 2001 From: lonewolf8351 <122708538+lonewolf8351@users.noreply.github.com> Date: Wed, 3 Dec 2025 13:43:11 -0500 Subject: [PATCH 20/38] Added setHurtTime function to IEntityLivingBase --- .../java/noppes/npcs/scripted/entity/ScriptLivingBase.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/noppes/npcs/scripted/entity/ScriptLivingBase.java b/src/main/java/noppes/npcs/scripted/entity/ScriptLivingBase.java index edf9d2d29..15459194e 100644 --- a/src/main/java/noppes/npcs/scripted/entity/ScriptLivingBase.java +++ b/src/main/java/noppes/npcs/scripted/entity/ScriptLivingBase.java @@ -374,4 +374,10 @@ public class ScriptLivingBase extends ScriptEntity Date: Wed, 3 Dec 2025 13:44:56 -0500 Subject: [PATCH 21/38] added attack speed parameter to linked Items --- .../java/noppes/npcs/ServerEventsHandler.java | 15 +++++++++++++++ .../npcs/client/gui/item/SubGuiLinkedItem.java | 14 +++++++++++++- .../noppes/npcs/controllers/data/LinkedItem.java | 9 +++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/main/java/noppes/npcs/ServerEventsHandler.java b/src/main/java/noppes/npcs/ServerEventsHandler.java index a41a828e6..a00274beb 100644 --- a/src/main/java/noppes/npcs/ServerEventsHandler.java +++ b/src/main/java/noppes/npcs/ServerEventsHandler.java @@ -51,6 +51,7 @@ import noppes.npcs.controllers.ServerCloneController; import noppes.npcs.controllers.data.Animation; import noppes.npcs.controllers.data.AnimationData; import noppes.npcs.controllers.data.Line; +import noppes.npcs.controllers.data.LinkedItem; import noppes.npcs.controllers.data.MarkData; import noppes.npcs.controllers.data.Party; import noppes.npcs.controllers.data.PlayerData; @@ -59,6 +60,7 @@ import noppes.npcs.controllers.data.Quest; import noppes.npcs.controllers.data.QuestData; import noppes.npcs.entity.EntityNPCInterface; import noppes.npcs.items.ItemExcalibur; +import noppes.npcs.items.ItemLinked; import noppes.npcs.items.ItemShield; import noppes.npcs.items.ItemSoulstoneEmpty; import noppes.npcs.quests.QuestKill; @@ -171,6 +173,19 @@ public class ServerEventsHandler { @SubscribeEvent public void invoke(LivingHurtEvent event) { + + + + if(event.source.getEntity() instanceof EntityPlayerMP && event.entity instanceof EntityLivingBase ){ + EntityPlayerMP ep = (EntityPlayerMP) event.source.getEntity(); + if(ep.getHeldItem() != null && ep.getHeldItem().getItem() instanceof ItemLinked){ + int time = ep.getHeldItem().stackTagCompound.getCompoundTag("ItemData").getCompoundTag("LinkedData").getInteger("AttackSpeed"); + event.entity.hurtResistantTime = time; + ((EntityLivingBase) event.entity).hurtTime = time; + } + } + + if (!(event.entityLiving instanceof EntityPlayer)) return; diff --git a/src/main/java/noppes/npcs/client/gui/item/SubGuiLinkedItem.java b/src/main/java/noppes/npcs/client/gui/item/SubGuiLinkedItem.java index 9bf537a69..f4296a882 100644 --- a/src/main/java/noppes/npcs/client/gui/item/SubGuiLinkedItem.java +++ b/src/main/java/noppes/npcs/client/gui/item/SubGuiLinkedItem.java @@ -115,13 +115,22 @@ public class SubGuiLinkedItem extends SubGuiInterface implements ITextfieldListe y += spacing; // Dig Speed field (0 to 20) - addLabel(new GuiNpcLabel(11, "display.digSpeed", x, y + 5, CustomNpcResourceListener.DefaultTextColor)); + addLabel(new GuiNpcLabel(11, "Dig & Attack Speed", x, y + 5, CustomNpcResourceListener.DefaultTextColor)); GuiNpcTextField digSpeedField = new GuiNpcTextField(12, this, x + 110, y, 50, 20, "" + linkedItem.digSpeed); digSpeedField.setIntegersOnly().setMinMaxDefault(0, 20, 1); addTextField(digSpeedField); + GuiNpcTextField attackSpeedField = new GuiNpcTextField(14, this, x +165, y, 50, 20, "" +linkedItem.attackSpeed); + attackSpeedField.setIntegersOnly().setMinMaxDefault(0,999999999,20); + addTextField(attackSpeedField); y += spacing; + // Attack Speed Field (integers only) + //addLabel(new GuiNpcLabel(13,"Attack Speed",x,y + 5, CustomNpcResourceListener.DefaultTextColor)); + + + //y += spacing; + // TEXTURE field addLabel(new GuiNpcLabel(3, "display.texture", x, y + 5, CustomNpcResourceListener.DefaultTextColor)); addTextField(new GuiNpcTextField(2, this, x + 60, y, 200, 20, linkedItem.display.texture)); @@ -401,6 +410,9 @@ public class SubGuiLinkedItem extends SubGuiInterface implements ITextfieldListe } else if (id == 12) { // Dig Speed linkedItem.digSpeed = textField.getInteger(); + } else if (id == 14) { + // attack Speed + linkedItem.attackSpeed = textField.getInteger(); } else if (id == 31) { linkedItem.display.scaleX = textField.getFloat(); } else if (id == 32) { diff --git a/src/main/java/noppes/npcs/controllers/data/LinkedItem.java b/src/main/java/noppes/npcs/controllers/data/LinkedItem.java index 6144dc480..4fb5fc6e7 100644 --- a/src/main/java/noppes/npcs/controllers/data/LinkedItem.java +++ b/src/main/java/noppes/npcs/controllers/data/LinkedItem.java @@ -29,6 +29,7 @@ public class LinkedItem implements ILinkedItem { public boolean isNormalItem = false; public boolean isTool = false; public int digSpeed = 1; + public int attackSpeed = 20; public int armorType = -2; //-2: Fits in no armor slot, -1: Fits in all slots, 0 - 3: Fits in Head -> Boots slot respectively public int enchantability; @@ -58,6 +59,7 @@ public class LinkedItem implements ILinkedItem { compound.setBoolean("IsTool", this.isTool); compound.setBoolean("IsNormalItem", this.isNormalItem); compound.setInteger("DigSpeed", this.digSpeed); + compound.setInteger("AttackSpeed",this.attackSpeed); compound.setInteger("ArmorType", this.armorType); compound.setInteger("Enchantability", this.enchantability); @@ -87,6 +89,7 @@ public class LinkedItem implements ILinkedItem { this.isTool = compound.getBoolean("IsTool"); this.isNormalItem = compound.getBoolean("IsNormalItem"); this.digSpeed = compound.getInteger("DigSpeed"); + this.attackSpeed = compound.getInteger("AttackSpeed"); this.armorType = compound.getInteger("ArmorType"); this.enchantability = compound.getInteger("Enchantability"); @@ -251,4 +254,10 @@ public class LinkedItem implements ILinkedItem { setScriptHandler(data = new LinkedItemScript()); return data; } + + @Override + public int getAttackSpeed() {return attackSpeed;} + + @Override + public void setAttackSpeed(int time) {this.attackSpeed = time;} } From 000cefdab93f6d4d4a74db1410d82b8d3609a403 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 6 Dec 2025 01:27:27 +0000 Subject: [PATCH 22/38] Fix mount movement sync and speed issues - Remove server-side check for setAIMoveSpeed to fix client prediction desync on ground mounts - Increase air strafe factor for flying mounts to match ground speed feel --- src/main/java/noppes/npcs/entity/EntityNPCInterface.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main/java/noppes/npcs/entity/EntityNPCInterface.java b/src/main/java/noppes/npcs/entity/EntityNPCInterface.java index 58aadb253..8285cf213 100644 --- a/src/main/java/noppes/npcs/entity/EntityNPCInterface.java +++ b/src/main/java/noppes/npcs/entity/EntityNPCInterface.java @@ -2138,9 +2138,7 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti this.jumpMovementFactor = appliedJumpFactor; - if (!worldObj.isRemote) { - this.setAIMoveSpeed(moveSpeed); - } + this.setAIMoveSpeed(moveSpeed); super.moveEntityWithHeading(strafe, forward); @@ -2175,9 +2173,7 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti if (!flightMode) { return moveSpeed * 0.1F; } - final float AIR_FRICTION = 0.91F; - final float BASE = 0.16277136F; - return moveSpeed * (BASE / (AIR_FRICTION * AIR_FRICTION * AIR_FRICTION)); + return moveSpeed * 0.5F; } private void syncMountedRiderVelocity() { From 1337aba841fc44aa020d93d009e588f56b86ccca Mon Sep 17 00:00:00 2001 From: bigguy345 Date: Tue, 9 Dec 2025 11:11:32 +0200 Subject: [PATCH 23/38] Added GuiTextureSelection.setLocation --- .../noppes/npcs/client/gui/select/GuiTextureSelection.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/noppes/npcs/client/gui/select/GuiTextureSelection.java b/src/main/java/noppes/npcs/client/gui/select/GuiTextureSelection.java index eb8af18d0..62d3ef035 100644 --- a/src/main/java/noppes/npcs/client/gui/select/GuiTextureSelection.java +++ b/src/main/java/noppes/npcs/client/gui/select/GuiTextureSelection.java @@ -364,6 +364,10 @@ public class GuiTextureSelection extends SubGuiInterface implements ICustomScrol list.add(new TextureData(domain, name)); } + public void setLocation(String domain, String location) { + selectedDomain = domain; + this.location = location; + } static class TextureData { String domain; String absoluteName; From 0ef48c5477c35019f7bb4b2225173687dbfd6ee7 Mon Sep 17 00:00:00 2001 From: bigguy345 Date: Tue, 9 Dec 2025 11:12:30 +0200 Subject: [PATCH 24/38] Better fix for ESC closing all sub guis at once --- .../npcs/client/gui/util/GuiNPCInterface.java | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/main/java/noppes/npcs/client/gui/util/GuiNPCInterface.java b/src/main/java/noppes/npcs/client/gui/util/GuiNPCInterface.java index 069f81764..b678eaac0 100644 --- a/src/main/java/noppes/npcs/client/gui/util/GuiNPCInterface.java +++ b/src/main/java/noppes/npcs/client/gui/util/GuiNPCInterface.java @@ -51,7 +51,6 @@ public abstract class GuiNPCInterface extends GuiScreen { public float bgScaleX = 1; public float bgScaleY = 1; public float bgScaleZ = 1; - public long timeClosedSubGui; public GuiNPCInterface(EntityNPCInterface npc) { this.player = Minecraft.getMinecraft().thePlayer; @@ -206,11 +205,18 @@ public abstract class GuiNPCInterface extends GuiScreen { guiScrollableComponent.keyTyped(c, i); } - // Fixes closing sub with escape closes all of its parents - boolean enoughTimeSinceSubClosed = Minecraft.getSystemTime() - timeClosedSubGui > 50; + /* + Fixes closing sub with escape closes all of its parents. + The outermost GUI (root of all subs) closes the deepest open sub on ESC. + */ + boolean isSub = this instanceof SubGuiInterface; - if (closeOnEsc && enoughTimeSinceSubClosed && (i == 1 || !GuiNpcTextField.isFieldActive() && isInventoryKey(i))) { - close(); + if (closeOnEsc && !isSub && (i == 1 || !GuiNpcTextField.isFieldActive() && isInventoryKey(i))) { + SubGuiInterface sub = getSubGui(); + if (sub != null) + sub.close(); + else + close(); } } @@ -405,7 +411,6 @@ public abstract class GuiNPCInterface extends GuiScreen { public void closeSubGui(SubGuiInterface gui) { subgui = null; - timeClosedSubGui = Minecraft.getSystemTime(); initGui(); } @@ -413,10 +418,15 @@ public abstract class GuiNPCInterface extends GuiScreen { return subgui != null; } + /** + * @return The deepest open subgui + */ public SubGuiInterface getSubGui() { - if (hasSubGui() && subgui.hasSubGui()) - return subgui.getSubGui(); - return subgui; + SubGuiInterface sub = subgui; + if (sub != null) + while (sub.hasSubGui()) + sub = sub.getSubGui(); + return sub; } public void drawNpc(int x, int y) { From cca69d8e1d7ce7bfbd22f899a3de04f97ed19b06 Mon Sep 17 00:00:00 2001 From: bigguy345 Date: Tue, 9 Dec 2025 12:49:40 +0200 Subject: [PATCH 25/38] Added GUI NPC rendering utilities --- .../npcs/client/ClientEventHandler.java | 1 + .../companion/GuiNpcCompanionStats.java | 5 +- .../gui/select/GuiTextureSelection.java | 6 +- .../npcs/client/gui/util/GuiNPCInterface.java | 79 +++++++++++++++++-- 4 files changed, 82 insertions(+), 9 deletions(-) diff --git a/src/main/java/noppes/npcs/client/ClientEventHandler.java b/src/main/java/noppes/npcs/client/ClientEventHandler.java index deb374ab2..7a839fbb5 100644 --- a/src/main/java/noppes/npcs/client/ClientEventHandler.java +++ b/src/main/java/noppes/npcs/client/ClientEventHandler.java @@ -55,6 +55,7 @@ public class ClientEventHandler { public static float partialRenderTick; public static RendererLivingEntity renderer; + public static boolean renderingEntityInGUI; public static EntityNPCInterface renderingNpc; public static EntityPlayer renderingPlayer; public static HashMap partNames = new HashMap<>(); diff --git a/src/main/java/noppes/npcs/client/gui/player/companion/GuiNpcCompanionStats.java b/src/main/java/noppes/npcs/client/gui/player/companion/GuiNpcCompanionStats.java index a7d4c953d..ad79fbefd 100644 --- a/src/main/java/noppes/npcs/client/gui/player/companion/GuiNpcCompanionStats.java +++ b/src/main/java/noppes/npcs/client/gui/player/companion/GuiNpcCompanionStats.java @@ -34,6 +34,9 @@ public class GuiNpcCompanionStats extends GuiNPCInterface implements IGuiData { xSize = 171; ySize = 166; PacketClient.sendClient(new GetNPCRole()); + + xOffsetNpc = 34; + yOffsetNpc = 150; } @Override @@ -96,7 +99,7 @@ public class GuiNpcCompanionStats extends GuiNPCInterface implements IGuiData { } isEating = role.isEating(); - super.drawNpc(34, 150); + super.drawNpc(npc, i, j, f); int y = drawHealth(guiTop + 88); } diff --git a/src/main/java/noppes/npcs/client/gui/select/GuiTextureSelection.java b/src/main/java/noppes/npcs/client/gui/select/GuiTextureSelection.java index 62d3ef035..7655d30cb 100644 --- a/src/main/java/noppes/npcs/client/gui/select/GuiTextureSelection.java +++ b/src/main/java/noppes/npcs/client/gui/select/GuiTextureSelection.java @@ -145,6 +145,10 @@ public class GuiTextureSelection extends SubGuiInterface implements ICustomScrol int i = selectedResource.getResourcePath().lastIndexOf('/'); location = selectedResource.getResourcePath().substring(0, i + 1); } + + xOffsetNpc = 307; + yOffsetNpc = 150; + zoomed = 1.5f; } private File decodeFile(String url) { @@ -250,7 +254,7 @@ public class GuiTextureSelection extends SubGuiInterface implements ICustomScrol public void drawScreen(int i, int j, float f) { super.drawScreen(i, j, f); npc.textureLocation = selectedResource; - drawNpc(npc, 307, 150, 1.5F, 0); + drawNpc(npc, i, j, f); } @Override diff --git a/src/main/java/noppes/npcs/client/gui/util/GuiNPCInterface.java b/src/main/java/noppes/npcs/client/gui/util/GuiNPCInterface.java index b678eaac0..607a69af8 100644 --- a/src/main/java/noppes/npcs/client/gui/util/GuiNPCInterface.java +++ b/src/main/java/noppes/npcs/client/gui/util/GuiNPCInterface.java @@ -93,6 +93,13 @@ public abstract class GuiNPCInterface extends GuiScreen { scrollWindows.clear(); diagrams.clear(); Keyboard.enableRepeatEvents(true); + + if (drawRenderButtons) { + addButton(zoomIn = new GuiNpcButton(0, guiLeft + xOffsetNpc + xOffsetButton, guiTop + yOffsetNpc + yOffsetButton, 20, 20, "-")); + addButton(zoomOut = new GuiNpcButton(0, guiLeft + 22 + xOffsetNpc + xOffsetButton, guiTop + yOffsetNpc + yOffsetButton, 20, 20, "+")); + addButton(rotateLeft = new GuiNpcButton(0, guiLeft + 44 + xOffsetNpc + xOffsetButton, guiTop + yOffsetNpc + yOffsetButton, 20, 20, "<")); + addButton(rotateRight = new GuiNpcButton(0, guiLeft + 66 + xOffsetNpc + xOffsetButton, guiTop + yOffsetNpc + yOffsetButton, 20, 20, ">")); + } } @Override @@ -429,11 +436,65 @@ public abstract class GuiNPCInterface extends GuiScreen { return sub; } - public void drawNpc(int x, int y) { - drawNpc(npc, x, y, 1, 0); + private GuiNpcButton rotateLeft, rotateRight, zoomOut, zoomIn; + + public int xOffsetNpc = 0, xOffsetButton = 0, xMouseRange = 50; + public int yOffsetNpc = 0, yOffsetButton = 0, yMouseRange = 150; + + public float zoomed = 1, rotation; + public float minZoom = 1, maxZoom = 2.5f; + + public boolean followMouse = true, allowRotate = true, drawNPConSub = false; + public boolean drawRenderButtons = false; + + public boolean isMouseOverRenderer(int x, int y) { + if (!allowRotate) { + return false; + } + // Center of the entity rendering + int centerX = guiLeft + xOffsetNpc; // Matches l in drawScreen() + int centerY = guiTop + yOffsetNpc; // Matches i1 in drawScreen() + + // Range from the center to start considering mouse is over renderer. + int xRange = xMouseRange; // Horizontal range (Left and right of center) + int yRange = yMouseRange; // Vertical range (Up and down of center) + + // Check if the mouse is within the range area + return mouseX >= centerX - xRange && mouseX <= centerX + xRange && mouseY >= centerY - yRange && mouseY <= centerY + yRange; } - public void drawNpc(EntityLivingBase entity, int x, int y, float zoomed, int rotation) { + public void drawNpc(EntityLivingBase entity, int mouseX, int mouseY, float partialTicks) { + if (hasSubGui() && !drawNPConSub) + return; + + if (Mouse.isButtonDown(0) && drawRenderButtons) { + if (this.rotateLeft.mousePressed(this.mc, mouseX, mouseY)) { + rotation += partialTicks * 1.5F; + } else if (this.rotateRight.mousePressed(this.mc, mouseX, mouseY)) { + rotation -= partialTicks * 1.5F; + } else if (this.zoomOut.mousePressed(this.mc, mouseX, mouseY) && zoomed < maxZoom) { + zoomed += partialTicks * 0.05F; + } else if (this.zoomIn.mousePressed(this.mc, mouseX, mouseY) && zoomed > minZoom) { + zoomed -= partialTicks * 0.05F; + } + } + + if (isMouseOverRenderer(mouseX, mouseY)) { + zoomed += Mouse.getDWheel() * 0.001f; + if (Mouse.isButtonDown(0) || Mouse.isButtonDown(1)) { + rotation -= Mouse.getDX() * 0.75f; + } + } + + if (zoomed > maxZoom) + zoomed = maxZoom; + if (zoomed < minZoom) + zoomed = minZoom; + + drawNpc(entity, mouseX, mouseY, xOffsetNpc, yOffsetNpc, zoomed, rotation, partialTicks); + } + + public void drawNpc(EntityLivingBase entity, int mouseX, int mouseY, int x, int y, float zoomed, float rotation, float partialTicks) { EntityNPCInterface npc = null; if (entity instanceof EntityNPCInterface) npc = (EntityNPCInterface) entity; @@ -460,7 +521,7 @@ public abstract class GuiNPCInterface extends GuiScreen { int orientation = 0; if (npc != null) { orientation = npc.ais.orientation; - npc.ais.orientation = rotation; + npc.ais.orientation = (int) rotation; } GL11.glRotatef(135F, 0.0F, 1.0F, 0.0F); @@ -468,12 +529,16 @@ public abstract class GuiNPCInterface extends GuiScreen { GL11.glRotatef(-135F, 0.0F, 1.0F, 0.0F); GL11.glRotatef(-(float) Math.atan(f6 / 40F) * 20F, 1.0F, 0.0F, 0.0F); entity.renderYawOffset = rotation; - entity.rotationYaw = (float) Math.atan(f5 / 80F) * 40F + rotation; - entity.rotationPitch = -(float) Math.atan(f6 / 40F) * 20F; + entity.rotationYaw = followMouse ? (float) Math.atan(f5 / 80F) * 40F + rotation : 0; + entity.rotationPitch = followMouse ? -(float) Math.atan(f6 / 40F) * 20F : 0; entity.rotationYawHead = entity.rotationYaw; GL11.glTranslatef(0.0F, entity.yOffset, 0.0F); RenderManager.instance.playerViewY = 180F; - RenderManager.instance.renderEntityWithPosYaw(entity, 0, 0, 0, 0, 1); + + try { + RenderManager.instance.renderEntityWithPosYaw(entity, 0, 0, 0, 0, 1); + } catch (Exception e) { + } entity.prevRenderYawOffset = entity.renderYawOffset = f2; entity.prevRotationYaw = entity.rotationYaw = f3; entity.prevRotationPitch = entity.rotationPitch = f4; From 06483566cf25ed0fed6849d04014b5ae09826fa6 Mon Sep 17 00:00:00 2001 From: bigguy345 Date: Tue, 9 Dec 2025 13:44:42 +0200 Subject: [PATCH 26/38] Improved GuiTextureSelection --- .../npcs/client/gui/select/GuiTextureSelection.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/noppes/npcs/client/gui/select/GuiTextureSelection.java b/src/main/java/noppes/npcs/client/gui/select/GuiTextureSelection.java index 7655d30cb..a2093ec3e 100644 --- a/src/main/java/noppes/npcs/client/gui/select/GuiTextureSelection.java +++ b/src/main/java/noppes/npcs/client/gui/select/GuiTextureSelection.java @@ -48,6 +48,7 @@ public class GuiTextureSelection extends SubGuiInterface implements ICustomScrol private String selectedDomain; public ResourceLocation selectedResource; + public boolean setNPCSkin = true; // Instance maps to be populated private final HashMap> domains = new HashMap<>(); private final HashMap textures = new HashMap<>(); @@ -242,8 +243,9 @@ public class GuiTextureSelection extends SubGuiInterface implements ICustomScrol @Override protected void actionPerformed(GuiButton guibutton) { super.actionPerformed(guibutton); - if (guibutton.id == 2) { - npc.display.setSkinTexture(selectedResource.toString()); + if (guibutton.id == 2 && selectedResource != null) { + if (setNPCSkin) + npc.display.setSkinTexture(selectedResource.toString()); } npc.textureLocation = null; close(); @@ -253,7 +255,6 @@ public class GuiTextureSelection extends SubGuiInterface implements ICustomScrol @Override public void drawScreen(int i, int j, float f) { super.drawScreen(i, j, f); - npc.textureLocation = selectedResource; drawNpc(npc, i, j, f); } @@ -263,6 +264,8 @@ public class GuiTextureSelection extends SubGuiInterface implements ICustomScrol if (scroll.id == 1) { TextureData data = textures.get(scroll.getSelected()); selectedResource = new ResourceLocation(selectedDomain, data.absoluteName); + if (setNPCSkin) + npc.textureLocation = selectedResource; } } else { initGui(); @@ -292,7 +295,8 @@ public class GuiTextureSelection extends SubGuiInterface implements ICustomScrol scrollTextures.selected = -1; initGui(); } else { - npc.display.setSkinTexture(selectedResource.toString()); + if (setNPCSkin) + npc.display.setSkinTexture(selectedResource.toString()); close(); parent.initGui(); } From 00ea0200c4ada8e62e029d3b8c29929fdb854b54 Mon Sep 17 00:00:00 2001 From: bigguy345 Date: Tue, 9 Dec 2025 13:45:07 +0200 Subject: [PATCH 27/38] Fixed GuiScrollWindow scrolling when a subgui is open --- src/main/java/noppes/npcs/client/gui/util/GuiScrollWindow.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/noppes/npcs/client/gui/util/GuiScrollWindow.java b/src/main/java/noppes/npcs/client/gui/util/GuiScrollWindow.java index 6c81a0b86..4dc3bcda3 100644 --- a/src/main/java/noppes/npcs/client/gui/util/GuiScrollWindow.java +++ b/src/main/java/noppes/npcs/client/gui/util/GuiScrollWindow.java @@ -292,7 +292,7 @@ public class GuiScrollWindow extends GuiScreen implements ITextfieldListener, IC public void adjustScroll(int mouseX, int mouseY, int mouseScroll) { mouseX -= xPos; mouseY -= yPos; - if (Mouse.isButtonDown(0)) { + if (Mouse.isButtonDown(0) && !parent.hasSubGui()) { if (mouseX >= clipWidth - 9 && mouseX < clipWidth - 4 && mouseY >= 4 && mouseY < clipHeight) { isScrolling = true; } From 27de3c77a4dc4a9ccd917a229b1aba6347f26121 Mon Sep 17 00:00:00 2001 From: bigguy345 Date: Tue, 9 Dec 2025 13:45:38 +0200 Subject: [PATCH 28/38] Hooked renderingEntityInGUI accordingly --- .../java/noppes/npcs/client/gui/util/GuiModelInterface.java | 4 +++- .../java/noppes/npcs/client/gui/util/GuiModelInterface2.java | 4 +++- .../java/noppes/npcs/client/gui/util/GuiNPCInterface.java | 4 ++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/noppes/npcs/client/gui/util/GuiModelInterface.java b/src/main/java/noppes/npcs/client/gui/util/GuiModelInterface.java index ed4fd384f..82bc71a4a 100644 --- a/src/main/java/noppes/npcs/client/gui/util/GuiModelInterface.java +++ b/src/main/java/noppes/npcs/client/gui/util/GuiModelInterface.java @@ -5,6 +5,7 @@ import net.minecraft.client.renderer.OpenGlHelper; import net.minecraft.client.renderer.RenderHelper; import net.minecraft.client.renderer.entity.RenderManager; import net.minecraft.entity.EntityLivingBase; +import noppes.npcs.client.ClientEventHandler; import noppes.npcs.client.EntityUtil; import noppes.npcs.entity.EntityCustomNpc; import noppes.npcs.entity.data.ModelData; @@ -159,12 +160,13 @@ public class GuiModelInterface extends GuiNPCInterface { entity.prevRotationYawHead = entity.rotationYawHead = followMouse ? entity.rotationYaw : rotation; GL11.glTranslatef(0.0F, entity.yOffset, 1F); RenderManager.instance.playerViewY = 180F; - + ClientEventHandler.renderingEntityInGUI = true; try { RenderManager.instance.renderEntityWithPosYaw(entity, 0.0D, 0.0D, 0.0D, 0.0F, 1.0F); } catch (Exception e) { playerdata.setEntityClass(null); } + ClientEventHandler.renderingEntityInGUI = false; entity.prevRenderYawOffset = entity.renderYawOffset = f2; entity.prevRotationYaw = entity.rotationYaw = f3; entity.rotationPitch = f4; diff --git a/src/main/java/noppes/npcs/client/gui/util/GuiModelInterface2.java b/src/main/java/noppes/npcs/client/gui/util/GuiModelInterface2.java index 16d3af9d7..8a1d015d0 100644 --- a/src/main/java/noppes/npcs/client/gui/util/GuiModelInterface2.java +++ b/src/main/java/noppes/npcs/client/gui/util/GuiModelInterface2.java @@ -6,6 +6,7 @@ import net.minecraft.client.renderer.OpenGlHelper; import net.minecraft.client.renderer.RenderHelper; import net.minecraft.client.renderer.entity.RenderManager; import net.minecraft.entity.EntityLivingBase; +import noppes.npcs.client.ClientEventHandler; import noppes.npcs.client.EntityUtil; import noppes.npcs.entity.EntityCustomNpc; import noppes.npcs.entity.EntityNPCInterface; @@ -109,12 +110,13 @@ public class GuiModelInterface2 extends GuiNPCInterface2 { entity.prevRotationYawHead = entity.rotationYawHead = entity.rotationYaw; GL11.glTranslatef(0.0F, entity.yOffset, 0.0F); RenderManager.instance.playerViewY = 180F; - + ClientEventHandler.renderingEntityInGUI = true; try { RenderManager.instance.renderEntityWithPosYaw(entity, 0.0D, 0.0D, 0.0D, 0.0F, 1.0F); } catch (Exception e) { playerdata.setEntityClass(null); } + ClientEventHandler.renderingEntityInGUI = false; entity.prevRenderYawOffset = entity.renderYawOffset = f2; entity.prevRotationYaw = entity.rotationYaw = f3; entity.rotationPitch = f4; diff --git a/src/main/java/noppes/npcs/client/gui/util/GuiNPCInterface.java b/src/main/java/noppes/npcs/client/gui/util/GuiNPCInterface.java index 607a69af8..cbd661b69 100644 --- a/src/main/java/noppes/npcs/client/gui/util/GuiNPCInterface.java +++ b/src/main/java/noppes/npcs/client/gui/util/GuiNPCInterface.java @@ -11,6 +11,7 @@ import net.minecraft.client.renderer.entity.RenderManager; import net.minecraft.entity.EntityLivingBase; import net.minecraft.util.ResourceLocation; import net.minecraft.util.StatCollector; +import noppes.npcs.client.ClientEventHandler; import noppes.npcs.client.CustomNpcResourceListener; import noppes.npcs.client.TextBlockClient; import noppes.npcs.entity.EntityNPCInterface; @@ -534,11 +535,14 @@ public abstract class GuiNPCInterface extends GuiScreen { entity.rotationYawHead = entity.rotationYaw; GL11.glTranslatef(0.0F, entity.yOffset, 0.0F); RenderManager.instance.playerViewY = 180F; + ClientEventHandler.renderingEntityInGUI = true; try { RenderManager.instance.renderEntityWithPosYaw(entity, 0, 0, 0, 0, 1); } catch (Exception e) { } + + ClientEventHandler.renderingEntityInGUI = false; entity.prevRenderYawOffset = entity.renderYawOffset = f2; entity.prevRotationYaw = entity.rotationYaw = f3; entity.prevRotationPitch = entity.rotationPitch = f4; From 7adbdd30e4c515e7253289dae4c2109d816d9521 Mon Sep 17 00:00:00 2001 From: bigguy345 Date: Tue, 9 Dec 2025 15:01:01 +0200 Subject: [PATCH 29/38] More GUI NPC rendering improvements --- .../companion/GuiNpcCompanionStats.java | 2 +- .../gui/select/GuiTextureSelection.java | 9 +-- .../npcs/client/gui/util/GuiNPCInterface.java | 61 ++++++++++++------- 3 files changed, 43 insertions(+), 29 deletions(-) diff --git a/src/main/java/noppes/npcs/client/gui/player/companion/GuiNpcCompanionStats.java b/src/main/java/noppes/npcs/client/gui/player/companion/GuiNpcCompanionStats.java index ad79fbefd..30f8e422d 100644 --- a/src/main/java/noppes/npcs/client/gui/player/companion/GuiNpcCompanionStats.java +++ b/src/main/java/noppes/npcs/client/gui/player/companion/GuiNpcCompanionStats.java @@ -35,6 +35,7 @@ public class GuiNpcCompanionStats extends GuiNPCInterface implements IGuiData { ySize = 166; PacketClient.sendClient(new GetNPCRole()); + drawNpc = true; xOffsetNpc = 34; yOffsetNpc = 150; } @@ -99,7 +100,6 @@ public class GuiNpcCompanionStats extends GuiNPCInterface implements IGuiData { } isEating = role.isEating(); - super.drawNpc(npc, i, j, f); int y = drawHealth(guiTop + 88); } diff --git a/src/main/java/noppes/npcs/client/gui/select/GuiTextureSelection.java b/src/main/java/noppes/npcs/client/gui/select/GuiTextureSelection.java index a2093ec3e..4f1bccfaf 100644 --- a/src/main/java/noppes/npcs/client/gui/select/GuiTextureSelection.java +++ b/src/main/java/noppes/npcs/client/gui/select/GuiTextureSelection.java @@ -147,9 +147,10 @@ public class GuiTextureSelection extends SubGuiInterface implements ICustomScrol location = selectedResource.getResourcePath().substring(0, i + 1); } + drawNpc = true; xOffsetNpc = 307; yOffsetNpc = 150; - zoomed = 1.5f; + defaultZoom = zoom = 1.5f; } private File decodeFile(String url) { @@ -252,12 +253,6 @@ public class GuiTextureSelection extends SubGuiInterface implements ICustomScrol parent.initGui(); } - @Override - public void drawScreen(int i, int j, float f) { - super.drawScreen(i, j, f); - drawNpc(npc, i, j, f); - } - @Override public void customScrollClicked(int i, int j, int k, GuiCustomScroll scroll) { if (scroll == scrollTextures) { diff --git a/src/main/java/noppes/npcs/client/gui/util/GuiNPCInterface.java b/src/main/java/noppes/npcs/client/gui/util/GuiNPCInterface.java index cbd661b69..ad28c1e07 100644 --- a/src/main/java/noppes/npcs/client/gui/util/GuiNPCInterface.java +++ b/src/main/java/noppes/npcs/client/gui/util/GuiNPCInterface.java @@ -96,10 +96,10 @@ public abstract class GuiNPCInterface extends GuiScreen { Keyboard.enableRepeatEvents(true); if (drawRenderButtons) { - addButton(zoomIn = new GuiNpcButton(0, guiLeft + xOffsetNpc + xOffsetButton, guiTop + yOffsetNpc + yOffsetButton, 20, 20, "-")); - addButton(zoomOut = new GuiNpcButton(0, guiLeft + 22 + xOffsetNpc + xOffsetButton, guiTop + yOffsetNpc + yOffsetButton, 20, 20, "+")); - addButton(rotateLeft = new GuiNpcButton(0, guiLeft + 44 + xOffsetNpc + xOffsetButton, guiTop + yOffsetNpc + yOffsetButton, 20, 20, "<")); - addButton(rotateRight = new GuiNpcButton(0, guiLeft + 66 + xOffsetNpc + xOffsetButton, guiTop + yOffsetNpc + yOffsetButton, 20, 20, ">")); + zoomIn = new GuiNpcButton(0, guiLeft + xOffsetNpc + xOffsetButton, guiTop + yOffsetNpc + yOffsetButton, 20, 20, "-"); + zoomOut = new GuiNpcButton(0, guiLeft + 22 + xOffsetNpc + xOffsetButton, guiTop + yOffsetNpc + yOffsetButton, 20, 20, "+"); + rotateLeft = new GuiNpcButton(0, guiLeft + 44 + xOffsetNpc + xOffsetButton, guiTop + yOffsetNpc + yOffsetButton, 20, 20, "<"); + rotateRight = new GuiNpcButton(0, guiLeft + 66 + xOffsetNpc + xOffsetButton, guiTop + yOffsetNpc + yOffsetButton, 20, 20, ">"); } } @@ -356,6 +356,9 @@ public abstract class GuiNPCInterface extends GuiScreen { if (subgui != null) { subgui.drawScreen(i, j, f); } + + if (drawNpc) + drawNpcWithExtras(npc, i, j, f); } protected void drawBackground() { @@ -442,11 +445,11 @@ public abstract class GuiNPCInterface extends GuiScreen { public int xOffsetNpc = 0, xOffsetButton = 0, xMouseRange = 50; public int yOffsetNpc = 0, yOffsetButton = 0, yMouseRange = 150; - public float zoomed = 1, rotation; + public float defaultZoom = 1, zoom = 1, rotation; public float minZoom = 1, maxZoom = 2.5f; - public boolean followMouse = true, allowRotate = true, drawNPConSub = false; - public boolean drawRenderButtons = false; + public boolean followMouse = true, allowRotate = true, drawNPConSub; + public boolean drawNpc, drawRenderButtons; public boolean isMouseOverRenderer(int x, int y) { if (!allowRotate) { @@ -464,35 +467,51 @@ public abstract class GuiNPCInterface extends GuiScreen { return mouseX >= centerX - xRange && mouseX <= centerX + xRange && mouseY >= centerY - yRange && mouseY <= centerY + yRange; } + + public void drawNpcWithExtras(EntityLivingBase entity, int mouseX, int mouseY, float partialTicks) { + drawNpc(entity, mouseX, mouseY, partialTicks); + } public void drawNpc(EntityLivingBase entity, int mouseX, int mouseY, float partialTicks) { if (hasSubGui() && !drawNPConSub) return; + + if (drawRenderButtons) { + rotateLeft.drawButton(mc, mouseX, mouseY); + rotateRight.drawButton(mc, mouseX, mouseY); + zoomOut.drawButton(mc, mouseX, mouseY); + zoomIn.drawButton(mc, mouseX, mouseY); + } + if (Mouse.isButtonDown(0) && drawRenderButtons) { if (this.rotateLeft.mousePressed(this.mc, mouseX, mouseY)) { rotation += partialTicks * 1.5F; } else if (this.rotateRight.mousePressed(this.mc, mouseX, mouseY)) { rotation -= partialTicks * 1.5F; - } else if (this.zoomOut.mousePressed(this.mc, mouseX, mouseY) && zoomed < maxZoom) { - zoomed += partialTicks * 0.05F; - } else if (this.zoomIn.mousePressed(this.mc, mouseX, mouseY) && zoomed > minZoom) { - zoomed -= partialTicks * 0.05F; + } else if (this.zoomOut.mousePressed(this.mc, mouseX, mouseY) && zoom < maxZoom) { + zoom += partialTicks * 0.05F; + } else if (this.zoomIn.mousePressed(this.mc, mouseX, mouseY) && zoom > minZoom) { + zoom -= partialTicks * 0.05F; } } + if (isMouseOverRenderer(mouseX, mouseY)) { - zoomed += Mouse.getDWheel() * 0.001f; - if (Mouse.isButtonDown(0) || Mouse.isButtonDown(1)) { + zoom += Mouse.getDWheel() * 0.001f; + if (Mouse.isButtonDown(0)) { rotation -= Mouse.getDX() * 0.75f; + } else if (Mouse.isButtonDown(1)) { + rotation = 0; + zoom = defaultZoom; } } - if (zoomed > maxZoom) - zoomed = maxZoom; - if (zoomed < minZoom) - zoomed = minZoom; + if (zoom > maxZoom) + zoom = maxZoom; + if (zoom < minZoom) + zoom = minZoom; - drawNpc(entity, mouseX, mouseY, xOffsetNpc, yOffsetNpc, zoomed, rotation, partialTicks); + drawNpc(entity, mouseX, mouseY, xOffsetNpc, yOffsetNpc, zoom, rotation, partialTicks); } public void drawNpc(EntityLivingBase entity, int mouseX, int mouseY, int x, int y, float zoomed, float rotation, float partialTicks) { @@ -505,10 +524,10 @@ public abstract class GuiNPCInterface extends GuiScreen { npc.isDrawn = true; GL11.glEnable(GL11.GL_COLOR_MATERIAL); GL11.glPushMatrix(); - GL11.glTranslatef(guiLeft + x, guiTop + y, 50F); + GL11.glTranslatef(guiLeft + x, guiTop + y, 90F); float scale = 1; if (entity.height > 2.4) - scale = 2 / npc.height; + scale = 2 / entity.height; GL11.glScalef(-30 * scale * zoomed, 30 * scale * zoomed, 30 * scale * zoomed); GL11.glRotatef(180F, 0.0F, 0.0F, 1.0F); @@ -528,7 +547,7 @@ public abstract class GuiNPCInterface extends GuiScreen { GL11.glRotatef(135F, 0.0F, 1.0F, 0.0F); RenderHelper.enableStandardItemLighting(); GL11.glRotatef(-135F, 0.0F, 1.0F, 0.0F); - GL11.glRotatef(-(float) Math.atan(f6 / 40F) * 20F, 1.0F, 0.0F, 0.0F); + GL11.glRotatef(-(float) Math.atan(f6 / 400F) * 20F, 1.0F, 0.0F, 0.0F); entity.renderYawOffset = rotation; entity.rotationYaw = followMouse ? (float) Math.atan(f5 / 80F) * 40F + rotation : 0; entity.rotationPitch = followMouse ? -(float) Math.atan(f6 / 40F) * 20F : 0; From ddee62f2ce8ef4558ba3a1d73e52983912510374 Mon Sep 17 00:00:00 2001 From: bigguy345 Date: Tue, 9 Dec 2025 15:01:19 +0200 Subject: [PATCH 30/38] API --- src/api | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api b/src/api index 4c1728670..305a3778c 160000 --- a/src/api +++ b/src/api @@ -1 +1 @@ -Subproject commit 4c172867018c74ce2cb49582f6486f3510e1e43d +Subproject commit 305a3778c86fed2680b8cad173b7e5fb0f3c98e6 From 93e6a92b59bbfced39e85c474ab4aa2d4a46fae0 Mon Sep 17 00:00:00 2001 From: bigguy345 Date: Tue, 9 Dec 2025 17:46:07 +0200 Subject: [PATCH 31/38] Made GuiNpcButtons with multiple displays i.e ("Hair","Eyes","Fur") right clickable to select in reverse --- .../gui/util/GuiButtonBiDirectional.java | 2 ++ .../npcs/client/gui/util/GuiNPCInterface.java | 35 ++++++++++++++++++- .../npcs/client/gui/util/GuiNpcButton.java | 20 ++++++++++- .../npcs/client/gui/util/GuiScrollWindow.java | 35 ++++++++++++++++++- src/main/resources/META-INF/customnpcs_at.cfg | 2 ++ 5 files changed, 91 insertions(+), 3 deletions(-) diff --git a/src/main/java/noppes/npcs/client/gui/util/GuiButtonBiDirectional.java b/src/main/java/noppes/npcs/client/gui/util/GuiButtonBiDirectional.java index c1d60fc66..0ebc41d64 100644 --- a/src/main/java/noppes/npcs/client/gui/util/GuiButtonBiDirectional.java +++ b/src/main/java/noppes/npcs/client/gui/util/GuiButtonBiDirectional.java @@ -61,6 +61,8 @@ public class GuiButtonBiDirectional extends GuiNpcButton { boolean bo = super.mousePressed(minecraft, mouseX, mouseY); if (bo && display != null && display.length != 0) { boolean hoverL = mouseX >= this.xPosition && mouseY >= this.yPosition && mouseX < this.xPosition + 11 && mouseY < this.yPosition + this.height; + if (rightClicked.get()) + hoverL = true; boolean hoverR = !hoverL && mouseX >= this.xPosition + 11 && mouseY >= this.yPosition && mouseX < this.xPosition + this.width && mouseY < this.yPosition + this.height; diff --git a/src/main/java/noppes/npcs/client/gui/util/GuiNPCInterface.java b/src/main/java/noppes/npcs/client/gui/util/GuiNPCInterface.java index ad28c1e07..1d453a395 100644 --- a/src/main/java/noppes/npcs/client/gui/util/GuiNPCInterface.java +++ b/src/main/java/noppes/npcs/client/gui/util/GuiNPCInterface.java @@ -11,6 +11,8 @@ import net.minecraft.client.renderer.entity.RenderManager; import net.minecraft.entity.EntityLivingBase; import net.minecraft.util.ResourceLocation; import net.minecraft.util.StatCollector; +import net.minecraftforge.client.event.GuiScreenEvent; +import net.minecraftforge.common.MinecraftForge; import noppes.npcs.client.ClientEventHandler; import noppes.npcs.client.CustomNpcResourceListener; import noppes.npcs.client.TextBlockClient; @@ -24,6 +26,7 @@ import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; public abstract class GuiNPCInterface extends GuiScreen { public EntityClientPlayerMP player; @@ -165,10 +168,40 @@ public abstract class GuiNPCInterface extends GuiScreen { } } mouseEvent(i, j, k); - super.mouseClicked(i, j, k); + vanillaMouseClicked(i, j, k); } } + protected void vanillaMouseClicked(int mouseX, int mouseY, int mouseButton) { + if (mouseButton == 0 || mouseButton == 1) { + for (int l = 0; l < this.buttonList.size(); ++l) { + GuiButton guibutton = (GuiButton) this.buttonList.get(l); + + AtomicBoolean rightClicked = null; + if (mouseButton == 1) { + if (guibutton instanceof GuiNpcButton && ((GuiNpcButton) guibutton).rightClickable) { + rightClicked = ((GuiNpcButton) guibutton).rightClicked; + rightClicked.set(true); + } else + continue; + } + + if (guibutton.mousePressed(this.mc, mouseX, mouseY)) { + GuiScreenEvent.ActionPerformedEvent.Pre event = new GuiScreenEvent.ActionPerformedEvent.Pre(this, guibutton, this.buttonList); + if (MinecraftForge.EVENT_BUS.post(event)) + break; + this.selectedButton = event.button; + event.button.func_146113_a(this.mc.getSoundHandler()); + this.actionPerformed(event.button); + if (this.equals(this.mc.currentScreen)) + MinecraftForge.EVENT_BUS.post(new GuiScreenEvent.ActionPerformedEvent.Post(this, event.button, this.buttonList)); + } + + if (rightClicked != null) + rightClicked.set(false); + } + } + } @Override public void mouseClickMove(int mouseX, int mouseY, int clickedMouseButton, long timeSinceLastClick) { if (subgui != null) { diff --git a/src/main/java/noppes/npcs/client/gui/util/GuiNpcButton.java b/src/main/java/noppes/npcs/client/gui/util/GuiNpcButton.java index 1052b9c21..37ffdb807 100644 --- a/src/main/java/noppes/npcs/client/gui/util/GuiNpcButton.java +++ b/src/main/java/noppes/npcs/client/gui/util/GuiNpcButton.java @@ -12,6 +12,7 @@ import org.lwjgl.opengl.GL12; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; public class GuiNpcButton extends GuiButton { @@ -31,6 +32,8 @@ public class GuiNpcButton extends GuiButton { protected int iconPosX = 0; protected int iconPosY = 0; + public boolean rightClickable; + public GuiNpcButton(int i, int j, int k, String s) { super(i, j, k, StatCollector.translateToLocal(s)); id = i; @@ -40,6 +43,9 @@ public class GuiNpcButton extends GuiButton { this(i, j, k, display[val]); this.display = display; this.displayValue = val; + + if (display.length > 1) + rightClickable = true; } public GuiNpcButton(int i, int j, int k, int l, int m, String string) { @@ -51,6 +57,9 @@ public class GuiNpcButton extends GuiButton { this(i, j, k, l, m, display.length == 0 ? "" : display[val % display.length]); this.display = display; this.displayValue = display.length == 0 ? 0 : val % display.length; + + if (display.length > 1) + rightClickable = true; } public GuiNpcButton(int i, int j, int k, int l, int m, Enum[] displayEnums, int val) { @@ -61,6 +70,9 @@ public class GuiNpcButton extends GuiButton { } this.display = strings.toArray(new String[0]); this.displayValue = display.length == 0 ? 0 : val % display.length; + + if (display.length > 1) + rightClickable = true; } public void setDisplayText(String text) { @@ -113,11 +125,17 @@ public class GuiNpcButton extends GuiButton { return this; } + public AtomicBoolean rightClicked = new AtomicBoolean(); @Override public boolean mousePressed(Minecraft minecraft, int i, int j) { boolean bo = super.mousePressed(minecraft, i, j); if (bo && display != null && display.length != 0) { - displayValue = (displayValue + 1) % display.length; + if (rightClicked.get()) { + if (displayValue <= 0) + displayValue = display.length; + displayValue--; + } else + displayValue = (displayValue + 1) % display.length; this.setDisplayText(display[displayValue]); } return bo; diff --git a/src/main/java/noppes/npcs/client/gui/util/GuiScrollWindow.java b/src/main/java/noppes/npcs/client/gui/util/GuiScrollWindow.java index 4dc3bcda3..3555b30fd 100644 --- a/src/main/java/noppes/npcs/client/gui/util/GuiScrollWindow.java +++ b/src/main/java/noppes/npcs/client/gui/util/GuiScrollWindow.java @@ -5,6 +5,8 @@ import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.gui.GuiYesNoCallback; import net.minecraft.client.gui.ScaledResolution; import net.minecraft.util.ResourceLocation; +import net.minecraftforge.client.event.GuiScreenEvent; +import net.minecraftforge.common.MinecraftForge; import noppes.npcs.entity.EntityNPCInterface; import org.lwjgl.input.Keyboard; import org.lwjgl.input.Mouse; @@ -12,6 +14,7 @@ import org.lwjgl.opengl.GL11; import java.util.ArrayList; import java.util.HashMap; +import java.util.concurrent.atomic.AtomicBoolean; public class GuiScrollWindow extends GuiScreen implements ITextfieldListener, ICustomScrollListener, GuiYesNoCallback { public static final ResourceLocation resource = new ResourceLocation("customnpcs", "textures/gui/misc.png"); @@ -107,9 +110,39 @@ public class GuiScrollWindow extends GuiScreen implements ITextfieldListener, IC } } mouseEvent(i, j, k); - super.mouseClicked(i, j, k); + vanillaMouseClicked(i, j, k); } + protected void vanillaMouseClicked(int mouseX, int mouseY, int mouseButton) { + if (mouseButton == 0 || mouseButton == 1) { + for (int l = 0; l < this.buttonList.size(); ++l) { + GuiButton guibutton = (GuiButton) this.buttonList.get(l); + + AtomicBoolean rightClicked = null; + if (mouseButton == 1) { + if (guibutton instanceof GuiNpcButton && ((GuiNpcButton) guibutton).rightClickable) { + rightClicked = ((GuiNpcButton) guibutton).rightClicked; + rightClicked.set(true); + } else + continue; + } + + if (guibutton.mousePressed(this.mc, mouseX, mouseY)) { + GuiScreenEvent.ActionPerformedEvent.Pre event = new GuiScreenEvent.ActionPerformedEvent.Pre(this, guibutton, this.buttonList); + if (MinecraftForge.EVENT_BUS.post(event)) + break; + this.selectedButton = event.button; + event.button.func_146113_a(this.mc.getSoundHandler()); + this.actionPerformed(event.button); + if (this.equals(this.mc.currentScreen)) + MinecraftForge.EVENT_BUS.post(new GuiScreenEvent.ActionPerformedEvent.Post(this, event.button, this.buttonList)); + } + + if (rightClicked != null) + rightClicked.set(false); + } + } + } public void mouseEvent(int i, int j, int k) { } diff --git a/src/main/resources/META-INF/customnpcs_at.cfg b/src/main/resources/META-INF/customnpcs_at.cfg index 5597bbba8..dbcb46481 100644 --- a/src/main/resources/META-INF/customnpcs_at.cfg +++ b/src/main/resources/META-INF/customnpcs_at.cfg @@ -22,3 +22,5 @@ public net.minecraft.entity.EntityLivingBase field_70703_bu # isJumping public net.minecraft.client.Minecraft field_147126_aw # mcMusicTicker # MusicTicker public net.minecraft.client.audio.MusicTicker field_147676_d # timeUntilNextMusic +#GuiScreen +public net.minecraft.client.gui.GuiScreen field_146290_a # selectedButton From c1cd8b0386274145c6165368ddbb18f0b6a57249 Mon Sep 17 00:00:00 2001 From: bigguy345 Date: Sun, 14 Dec 2025 13:36:37 +0200 Subject: [PATCH 32/38] public EntityCustomNpc npc field to ModelMPM --- src/main/java/noppes/npcs/client/model/ModelMPM.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/noppes/npcs/client/model/ModelMPM.java b/src/main/java/noppes/npcs/client/model/ModelMPM.java index 6c6cc623d..7a8273a77 100644 --- a/src/main/java/noppes/npcs/client/model/ModelMPM.java +++ b/src/main/java/noppes/npcs/client/model/ModelMPM.java @@ -82,6 +82,7 @@ public class ModelMPM extends ModelNPCMale { public ModelTail tail; public ModelBase entityModel; public EntityLivingBase entity; + public EntityCustomNpc npc; public boolean currentlyPlayerTexture; @@ -354,7 +355,7 @@ public class ModelMPM extends ModelNPCMale { @Override public void render(Entity par1Entity, float par2, float par3, float par4, float par5, float par6, float par7) { - EntityCustomNpc npc = (EntityCustomNpc) par1Entity; + npc = (EntityCustomNpc) par1Entity; EntityPlayer player = Minecraft.getMinecraft().thePlayer; if (npc.scriptInvisibleToPlayer(Minecraft.getMinecraft().thePlayer) && (player.getHeldItem() == null || player.getHeldItem().getItem() != CustomItems.wand)) return; From c291ab174f11b34b7ce8b71d140d13d947d5789f Mon Sep 17 00:00:00 2001 From: bigguy345 Date: Sun, 14 Dec 2025 13:36:58 +0200 Subject: [PATCH 33/38] Made npc left/right leg model access public --- src/main/java/noppes/npcs/client/model/part/ModelLegs.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/noppes/npcs/client/model/part/ModelLegs.java b/src/main/java/noppes/npcs/client/model/part/ModelLegs.java index fe9325b4d..e5f8b5937 100644 --- a/src/main/java/noppes/npcs/client/model/part/ModelLegs.java +++ b/src/main/java/noppes/npcs/client/model/part/ModelLegs.java @@ -18,7 +18,7 @@ import org.lwjgl.opengl.GL11; public class ModelLegs extends ModelScaleRenderer { private EntityCustomNpc entity; - private ModelScaleRenderer leg1, leg2; + public ModelScaleRenderer leg1, leg2; private ModelRenderer spider; private ModelRenderer horse; private ModelNagaLegs naga; From e5e897ab1eea0384a6467cbcaecce9b309f33a4a Mon Sep 17 00:00:00 2001 From: bigguy345 Date: Mon, 15 Dec 2025 17:33:58 +0200 Subject: [PATCH 34/38] Completely remove CustomNPC-Plus/src/api submodule --- .gitmodules | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitmodules b/.gitmodules index d8252cb6a..4723bbcd2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,3 +2,6 @@ path = src/api url = https://github.com/KAMKEEL/CustomNPC-Plus-API.git branch = dev +[submodule "CustomNPC-Plus/src/api"] + path = CustomNPC-Plus/src/api + url = https://github.com/KAMKEEL/CustomNPC-Plus-API.git From 2c91cd4286ef9b9811e71677269476ada994399e Mon Sep 17 00:00:00 2001 From: bigguy345 Date: Mon, 15 Dec 2025 17:46:46 +0200 Subject: [PATCH 35/38] oops --- .gitmodules | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitmodules b/.gitmodules index 4723bbcd2..d8252cb6a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,6 +2,3 @@ path = src/api url = https://github.com/KAMKEEL/CustomNPC-Plus-API.git branch = dev -[submodule "CustomNPC-Plus/src/api"] - path = CustomNPC-Plus/src/api - url = https://github.com/KAMKEEL/CustomNPC-Plus-API.git From e767f4f46c3dba89ea2f1a3a67850419a83a75f0 Mon Sep 17 00:00:00 2001 From: somehussar <148253652+somehussar@users.noreply.github.com> Date: Fri, 19 Dec 2025 20:39:54 +0100 Subject: [PATCH 36/38] Fix Party Crash --- src/main/java/noppes/npcs/ServerEventsHandler.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/noppes/npcs/ServerEventsHandler.java b/src/main/java/noppes/npcs/ServerEventsHandler.java index a00274beb..1bd184a4a 100644 --- a/src/main/java/noppes/npcs/ServerEventsHandler.java +++ b/src/main/java/noppes/npcs/ServerEventsHandler.java @@ -433,6 +433,8 @@ public class ServerEventsHandler { return; String name = EntityList.getEntityString(entity); + if (entity instanceof EntityPlayer) + name = "Player"; QuestKill quest = (QuestKill) data.quest.questInterface; if (data.quest.partyOptions.objectiveRequirement == EnumPartyObjectives.Leader && !party.getLeaderUUID().equals(player.getUniqueID())) return; From 63ca8da22cc5a9da9c702567454b3b81cb81f2da Mon Sep 17 00:00:00 2001 From: PeTcHeNkA <60981731+PeTcHeNkA@users.noreply.github.com> Date: Sat, 20 Dec 2025 02:43:42 +0300 Subject: [PATCH 37/38] check: throw GradleException if api folder is not exist --- build.gradle | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index dc3a7dd3b..5ae3b3b5c 100644 --- a/build.gradle +++ b/build.gradle @@ -31,12 +31,14 @@ tasks.create('buildNoMixin', Exec) { commandLine gradleWrapper, 'build', '-Pnomixin' } -sourceSets { - named("main").configure { - java { - srcDirs += ['src/api/java'] - } +sourceSets.main.java { + def apiDir = file('src/api/java') + + if (!apiDir.exists()) { + throw new GradleException("Missing API sources: Run './gradlew updateAPI' or enable checkout submodules in CI.") } + + srcDirs += apiDir } // Modify the existing 'build' task to depend on 'updateAPI' From de094ea0a23805ea2e13abf94953811800a4cb47 Mon Sep 17 00:00:00 2001 From: Kam Date: Tue, 30 Dec 2025 17:50:29 -0500 Subject: [PATCH 38/38] 1.10.3 Setting --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 950d6d3d6..b76ad9d0c 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ plugins { id 'idea' } -version = "1.10.3-beta1" +version = "1.10.3" group= "kamkeel.customnpc-plus" archivesBaseName = "CustomNPC-Plus" @@ -33,7 +33,7 @@ tasks.create('buildNoMixin', Exec) { sourceSets.main.java { def apiDir = file('src/api/java') - + if (!apiDir.exists()) { throw new GradleException("Missing API sources: Run './gradlew updateAPI' or enable checkout submodules in CI.") }