diff --git a/src/main/java/noppes/npcs/client/gui/GuiScript.java b/src/main/java/noppes/npcs/client/gui/GuiScript.java index 8d960e142..7420d4387 100644 --- a/src/main/java/noppes/npcs/client/gui/GuiScript.java +++ b/src/main/java/noppes/npcs/client/gui/GuiScript.java @@ -158,7 +158,9 @@ public class GuiScript extends GuiScriptInterface { container == null ? "" : container.script); } - activeArea.setLanguage(script.getLanguage()); + // Use the container's language for syntax highlighting, fall back to handler default + String language = (container != null) ? container.getLanguage() : script.getLanguage(); + activeArea.setLanguage(language); activeArea.setScriptContext(getScriptContext()); // Setup fullscreen key binding @@ -211,16 +213,19 @@ public class GuiScript extends GuiScriptInterface { addButton(new GuiNpcButton(105, guiLeft + 60, guiTop + 4, 80, 20, consoleOptions.toArray(new String[0]), activeConsole)); - addLabel(new GuiNpcLabel(1, "script.language", guiLeft + 232, guiTop + 30)); + addLabel(new GuiNpcLabel(1, "script.default", guiLeft + 232, guiTop + 30)); List languageOptions = getLanguageOptions(); addButton(new GuiNpcButton(103, guiLeft + 294, guiTop + 25, 80, 20, languageOptions.toArray(new String[0]), getLanguageIndex(languageOptions))); - getButton(103).enabled = languageOptions.size() > 0; addLabel(new GuiNpcLabel(2, "gui.enabled", guiLeft + 232, guiTop + 53)); addButton(new GuiNpcButton(104, guiLeft + 294, guiTop + 48, 50, 20, new String[]{"gui.no", "gui.yes"}, script.enabled ? 1 : 0)); + // Disable language/enabled controls until server data actually arrives + getButton(103).enabled = serverDataReceived && languageOptions.size() > 0; + getButton(104).enabled = serverDataReceived; + if (MinecraftServer.getServer() != null) addButton(new GuiNpcButton(106, guiLeft + 232, guiTop + 71, 150, 20, "script.openfolder")); } @@ -309,9 +314,13 @@ public class GuiScript extends GuiScriptInterface { } if (guibutton.id == 107) { ScriptContainer container = getCurrentContainer(); - if (container == null) - script.setNPCScript(activeTab, container = new ScriptContainer(this.script)); - setSubGui(new GuiScriptList(languages.get(script.scriptLanguage), container)); + if (container == null) { + container = new ScriptContainer(this.script); + container.setLanguage(script.getLanguage()); + script.setNPCScript(activeTab, container); + } + String lang = container.getLanguage(); + setSubGui(new GuiScriptList(languages.get(lang), container)); } } @@ -319,8 +328,11 @@ public class GuiScript extends GuiScriptInterface { protected void setScript() { if (showScript) { ScriptContainer container = getCurrentContainer(); - if (container == null) - script.setNPCScript(activeTab, container = new ScriptContainer(this.script)); + if (container == null) { + container = new ScriptContainer(this.script); + container.setLanguage(script.getLanguage()); + script.setNPCScript(activeTab, container); + } String text = getTextField(2).getText(); text = text.replace("\r\n", "\n"); text = text.replace("\r", "\n"); diff --git a/src/main/java/noppes/npcs/client/gui/script/GuiScriptInterface.java b/src/main/java/noppes/npcs/client/gui/script/GuiScriptInterface.java index 82012429f..3d91446c9 100644 --- a/src/main/java/noppes/npcs/client/gui/script/GuiScriptInterface.java +++ b/src/main/java/noppes/npcs/client/gui/script/GuiScriptInterface.java @@ -50,6 +50,7 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb protected int scriptLimit = 1; public List hookList = new ArrayList(); protected boolean loaded = false; + protected boolean serverDataReceived = false; protected Map textAreas = new HashMap<>(); protected GuiScreen parent; @@ -300,9 +301,12 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb this.addButton(new GuiNpcButton(100, left1, this.guiTop + 21 + yoffset, 60, 20, "gui.copy")); this.addButton(new GuiNpcButton(105, left1 + 61, this.guiTop + 21 + yoffset, 60, 20, "gui.remove")); - // Language toggle button (only if handler supports Janino and there is more than one option) - if (handler.supportsJanino() && getLanguageOptions().size() > 1 && container != null) { - this.addButton(new GuiNpcButton(113, left1, this.guiTop + 42 + yoffset, 121, 20, container.getLanguage())); + // Per-tab language button — cycles through all available languages + List langOptions = getLanguageOptions(); + if (langOptions.size() > 1 && container != null) { + int langIdx = getContainerLanguageIndex(langOptions, container); + this.addButton(new GuiNpcButton(113, left1, this.guiTop + 42 + yoffset, 121, 20, + langOptions.toArray(new String[0]), langIdx)); } this.addButton(new GuiNpcButton(107, left1, this.guiTop + 66 + yoffset, 80, 20, "script.loadscript")); @@ -331,16 +335,20 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb int var9 = this.guiLeft + this.xSize - 150; this.addButton(new GuiNpcButton(100, var9, this.guiTop + 125, 60, 20, "gui.copy")); this.addButton(new GuiNpcButton(102, var9, this.guiTop + 146, 60, 20, "gui.clear")); - this.addLabel(new GuiNpcLabel(1, "script.language", var9, this.guiTop + 15)); + this.addLabel(new GuiNpcLabel(1, "script.default", var9, this.guiTop + 15)); List languageOptions = getLanguageOptions(); this.addButton(new GuiNpcButton(103, var9 + 60, this.guiTop + 10, 80, 20, languageOptions.toArray(new String[0]), this.getLanguageIndex(languageOptions))); - this.getButton(103).enabled = languageOptions.size() > 0; this.addLabel(new GuiNpcLabel(2, "gui.enabled", var9, this.guiTop + 36)); this.addButton(new GuiNpcButton(104, var9 + 60, this.guiTop + 31, 50, 20, new String[]{"gui.no", "gui.yes"}, this.handler.getEnabled() ? 1 : 0)); + // Disable language/enabled controls until server data actually arrives + // (serverDataReceived is only set in loadLanguagesData, not by getLanguageOptions injecting Java) + this.getButton(103).enabled = serverDataReceived && languageOptions.size() > 0; + this.getButton(104).enabled = serverDataReceived; + if (this.player.worldObj.isRemote) { this.addButton(new GuiNpcButton(106, var9, this.guiTop + 55, 150, 20, "script.openfolder")); } @@ -467,6 +475,26 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb return 0; } + /** + * Finds the index of a container's language within the available options. + * Used for the per-tab language button (113). + * + * @param languageOptions The list of available languages. + * @param container The script unit whose language to find. + * @return The 0-based index, or 0 if not found. + */ + protected int getContainerLanguageIndex(List languageOptions, IScriptUnit container) { + if (languageOptions == null || languageOptions.isEmpty() || container == null) + return 0; + + String current = container.getLanguage(); + for (int i = 0; i < languageOptions.size(); i++) { + if (current != null && languageOptions.get(i).equalsIgnoreCase(current)) + return i; + } + return 0; + } + /** * Retrieves the list of available scripting languages for this handler. *

@@ -591,13 +619,13 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb if (guibutton.id == scriptLimit) { if (handler.isSingleContainer()) { if (handler.getSingleScript() == null) { - handler.addScriptUnit(new ScriptContainer(handler)); + handler.addScriptUnit(createDefaultScriptUnit()); } else { this.setScript(); } this.activeTab = 1; } else { - handler.addScriptUnit(new ScriptContainer(this.handler)); + handler.addScriptUnit(createDefaultScriptUnit()); this.activeTab = this.handler.getScripts().size(); } this.initGui(); @@ -655,43 +683,47 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb if (guibutton.id == 107) { IScriptUnit container = getCurrentContainer(); if (container == null) { - container = new ScriptContainer(this.handler); + container = createDefaultScriptUnit(); handler.addScriptUnit(container); } - String language = container != null ? container.getLanguage() : this.handler.getLanguage(); + String language = container.getLanguage(); this.setSubGui(new EventGuiScriptList((List) this.languages.get(language), container)); } - // Language toggle button - switch between ECMAScript and Java + // Per-tab language cycling button if (guibutton.id == 113) { + String selectedLanguage = ((GuiNpcButton) guibutton).displayString; int idx = getActiveScriptIndex(); if (idx >= 0 && idx < handler.getScripts().size()) { IScriptUnit currentUnit = handler.getScripts().get(idx); - IScriptUnit newUnit; + boolean currentIsJanino = currentUnit.isJanino(); + boolean targetIsJanino = "Java".equals(selectedLanguage); - if (currentUnit.isJanino()) { - // Switch from Java to ECMAScript - newUnit = new ScriptContainer(handler); + if (currentIsJanino && !targetIsJanino) { + // Switch from Java to a non-Java language + IScriptUnit newUnit = new ScriptContainer(handler); newUnit.setScript(currentUnit.getScript()); newUnit.setExternalScripts(new ArrayList<>(currentUnit.getExternalScripts())); - } else { - // Switch from ECMAScript to Java - newUnit = handler.createJaninoScriptUnit(); + handler.replaceScriptUnit(idx, newUnit); + textAreas.remove(idx); + initGui(); + } else if (!currentIsJanino && targetIsJanino) { + // Switch from non-Java to Java + IScriptUnit newUnit = handler.createJaninoScriptUnit(); if (newUnit != null) { newUnit.setScript(currentUnit.getScript()); newUnit.setExternalScripts(new ArrayList<>(currentUnit.getExternalScripts())); - } else { - return; // Handler doesn't support Janino + handler.replaceScriptUnit(idx, newUnit); + textAreas.remove(idx); + initGui(); } + } else if (!currentIsJanino) { + // Switch between non-Java languages (e.g., ECMAScript to Lua) + currentUnit.setLanguage(selectedLanguage); + GuiScriptTextArea area = getActiveScriptArea(); + if (area != null) area.setLanguage(selectedLanguage); } - - handler.replaceScriptUnit(idx, newUnit); - // Clear the cached text area so it recreates with new language - textAreas.remove(idx); - - // Reinitialize the GUI - initGui(); } } } @@ -701,7 +733,7 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb IScriptUnit container = getCurrentContainer(); if (container == null) { - container = new ScriptContainer(this.handler); + container = createDefaultScriptUnit(); handler.addScriptUnit(container); } @@ -771,6 +803,7 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb } this.languages = languages; + this.serverDataReceived = true; this.initGui(); } @@ -820,6 +853,22 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb super.close(); } + /** + * Create a new script unit using the handler's default language. + * If default is Java, creates a JaninoScript; otherwise a ScriptContainer + * with its language set to the handler's default. + */ + protected IScriptUnit createDefaultScriptUnit() { + String defaultLang = handler.getLanguage(); + if ("Java".equals(defaultLang)) { + IScriptUnit unit = handler.createJaninoScriptUnit(); + if (unit != null) return unit; + } + ScriptContainer sc = new ScriptContainer(handler); + sc.setLanguage(defaultLang); + return sc; + } + // ==================== FULLSCREEN METHODS ==================== /** diff --git a/src/main/java/noppes/npcs/controllers/ScriptContainer.java b/src/main/java/noppes/npcs/controllers/ScriptContainer.java index efdbc5b2d..c090b2a75 100644 --- a/src/main/java/noppes/npcs/controllers/ScriptContainer.java +++ b/src/main/java/noppes/npcs/controllers/ScriptContainer.java @@ -46,6 +46,12 @@ public class ScriptContainer implements IScriptUnit { private CompiledScript compScript = null; private final HashMap cachedFunctions = new HashMap<>(); + /** + * Persisted per-container language. Null means inherit from handler default. + * Separate from currentScriptLanguage which is the engine cache. + */ + private String language = null; + public ScriptContainer(IScriptHandler handler) { this.handler = handler; } @@ -68,6 +74,12 @@ public class ScriptContainer implements IScriptUnit { this.console = NBTTags.GetLongStringMap(compound.getTagList("Console", 10)); this.scripts = NBTTags.getStringList(compound.getTagList("ScriptList", 10)); this.lastCreated = 0L; + + // Read per-container language (empty string or missing = inherit from handler) + if (compound.hasKey("ContainerLanguage")) { + String lang = compound.getString("ContainerLanguage"); + this.language = lang.isEmpty() ? null : lang; + } } public NBTTagCompound writeToNBT(NBTTagCompound compound) { @@ -98,6 +110,11 @@ public class ScriptContainer implements IScriptUnit { //compound.setString("Type", this.type); compound.setTag("Console", NBTTags.NBTLongStringMap(this.console)); compound.setTag("ScriptList", NBTTags.nbtStringList(this.scripts)); + + // Persist per-container language if explicitly set + if (this.language != null) { + compound.setString("ContainerLanguage", this.language); + } return compound; } @@ -169,7 +186,7 @@ public class ScriptContainer implements IScriptUnit { if (!CustomNpcs.proxy.isScriptingEnabled() || errored || !hasCode() || unknownFunctions.contains(type)) return; - this.setEngine(handler.getLanguage()); + this.setEngine(this.getLanguage()); if (engine == null) return; @@ -308,18 +325,15 @@ public class ScriptContainer implements IScriptUnit { @Override public String getLanguage() { - // If a specific language is set for this container, use it - // Otherwise fall back to the handler's language - if (currentScriptLanguage != null) { - return currentScriptLanguage; - } - return handler != null ? handler.getLanguage() : "ECMAScript"; + // Return persisted per-container language, defaulting to ECMAScript. + // This is separate from currentScriptLanguage which is the engine cache. + return this.language != null ? this.language : "ECMAScript"; } @Override public void setLanguage(String language) { - if (!Objects.equals(this.currentScriptLanguage, language)) { - this.currentScriptLanguage = language; + if (!Objects.equals(this.language, language)) { + this.language = language; this.evaluated = false; } } diff --git a/src/main/java/noppes/npcs/controllers/data/ForgeDataScript.java b/src/main/java/noppes/npcs/controllers/data/ForgeDataScript.java index f8c961db7..26685aee8 100644 --- a/src/main/java/noppes/npcs/controllers/data/ForgeDataScript.java +++ b/src/main/java/noppes/npcs/controllers/data/ForgeDataScript.java @@ -18,6 +18,7 @@ import noppes.npcs.constants.ScriptContext; import noppes.npcs.controllers.ScriptContainer; import noppes.npcs.controllers.ScriptController; import noppes.npcs.controllers.ScriptHookController; +import noppes.npcs.janino.EventJaninoScript; import org.apache.commons.lang3.StringUtils; import java.io.IOException; @@ -161,6 +162,11 @@ public class ForgeDataScript extends MultiScriptHandler { ForgeScriptPacket.Save(index, totalCount, nbt); } + @Override + public IScriptUnit createJaninoScriptUnit() { + return new EventJaninoScript(ScriptContext.FORGE); + } + @Override public boolean isClient() { return false; diff --git a/src/main/java/noppes/npcs/controllers/data/GlobalNPCDataScript.java b/src/main/java/noppes/npcs/controllers/data/GlobalNPCDataScript.java index 4654ff68f..6f8a057cd 100644 --- a/src/main/java/noppes/npcs/controllers/data/GlobalNPCDataScript.java +++ b/src/main/java/noppes/npcs/controllers/data/GlobalNPCDataScript.java @@ -8,6 +8,7 @@ import noppes.npcs.api.entity.ICustomNpc; import noppes.npcs.constants.ScriptContext; import noppes.npcs.controllers.ScriptController; import noppes.npcs.entity.EntityNPCInterface; +import noppes.npcs.janino.EventJaninoScript; import noppes.npcs.scripted.NpcAPI; import noppes.npcs.util.ScriptToStringHelper; @@ -47,6 +48,11 @@ public class GlobalNPCDataScript extends MultiScriptHandler { lastNpcUpdate = ScriptController.Instance.lastGlobalNpcUpdate; } + @Override + public IScriptUnit createJaninoScriptUnit() { + return new EventJaninoScript(ScriptContext.NPC); + } + @Override public void requestData() { GlobalNPCScriptPacket.Get(); diff --git a/src/main/java/noppes/npcs/controllers/data/PlayerDataScript.java b/src/main/java/noppes/npcs/controllers/data/PlayerDataScript.java index 95f8bcb8e..7307333e0 100644 --- a/src/main/java/noppes/npcs/controllers/data/PlayerDataScript.java +++ b/src/main/java/noppes/npcs/controllers/data/PlayerDataScript.java @@ -167,6 +167,12 @@ public class PlayerDataScript extends MultiScriptHandler { return ScriptController.Instance.playerScripts.scriptLanguage; } + @Override + public void setLanguage(String lang) { + ScriptController.Instance.playerScripts.scriptLanguage = lang; + this.scriptLanguage = lang; + } + @Override public String noticeString() { if (this.player == null) { diff --git a/src/main/resources/assets/customnpcs/lang/de_DE.lang b/src/main/resources/assets/customnpcs/lang/de_DE.lang index 32170875b..3ae606484 100644 --- a/src/main/resources/assets/customnpcs/lang/de_DE.lang +++ b/src/main/resources/assets/customnpcs/lang/de_DE.lang @@ -432,6 +432,7 @@ script.interact=interagieren script.update=Update script.console=Konsole script.language=Sprache +script.default=Standard script.damaged=Schaden script.killed=Sterben script.attack=Angriff diff --git a/src/main/resources/assets/customnpcs/lang/en_US.lang b/src/main/resources/assets/customnpcs/lang/en_US.lang index e7683fc77..82f58177f 100644 --- a/src/main/resources/assets/customnpcs/lang/en_US.lang +++ b/src/main/resources/assets/customnpcs/lang/en_US.lang @@ -861,6 +861,7 @@ script.interact=Interact script.update=Update script.console=Console script.language=Language +script.default=Default script.damaged=Damaged script.killed=Killed script.attack=Attack diff --git a/src/main/resources/assets/customnpcs/lang/ko_KR.lang b/src/main/resources/assets/customnpcs/lang/ko_KR.lang index e3a672289..e0c124dee 100644 --- a/src/main/resources/assets/customnpcs/lang/ko_KR.lang +++ b/src/main/resources/assets/customnpcs/lang/ko_KR.lang @@ -423,6 +423,7 @@ script.interact=상호작용 script.update=업데이트 script.console=콘솔 script.language=언어 +script.default=기본 script.damaged=데미지 script.killed=사망 script.attack=공격 diff --git a/src/main/resources/assets/customnpcs/lang/ru_RU.lang b/src/main/resources/assets/customnpcs/lang/ru_RU.lang index 2c6f76422..22dd39276 100644 --- a/src/main/resources/assets/customnpcs/lang/ru_RU.lang +++ b/src/main/resources/assets/customnpcs/lang/ru_RU.lang @@ -461,6 +461,7 @@ script.interact=Взаимодействие script.update=Обновление script.console=Консоль script.language=Язык +script.default=По умолч. script.damaged=Получ. урон script.killed=Убит script.attack=Атакует diff --git a/src/main/resources/assets/customnpcs/lang/zh_CN.lang b/src/main/resources/assets/customnpcs/lang/zh_CN.lang index 32105c7db..53793cecb 100644 --- a/src/main/resources/assets/customnpcs/lang/zh_CN.lang +++ b/src/main/resources/assets/customnpcs/lang/zh_CN.lang @@ -641,6 +641,7 @@ script.interact=对话 script.update=更新 script.console=控制台 script.language=语言 +script.default=默认 script.damaged=伤害 script.killed=杀死 script.attack=攻击