diff --git a/.gitignore b/.gitignore index 23d9047f1..ed907b3d2 100644 --- a/.gitignore +++ b/.gitignore @@ -6,10 +6,13 @@ /out/ /build/ /eclipse/ +/gradle-plugins/ .classpath .project /bin/ /config/ +/copilot/ +/buildSrc/build/ /crash-reports/ /logs/ options.txt @@ -39,3 +42,4 @@ addon.late.local.gradle.kts layout.json out run +src/main/resources/assets/customnpcs/api \ No newline at end of file diff --git a/src/main/java/noppes/npcs/client/gui/GuiScript.java b/src/main/java/noppes/npcs/client/gui/GuiScript.java index a8017222d..4349b1ef6 100644 --- a/src/main/java/noppes/npcs/client/gui/GuiScript.java +++ b/src/main/java/noppes/npcs/client/gui/GuiScript.java @@ -13,6 +13,7 @@ import noppes.npcs.client.NoppesUtil; import noppes.npcs.client.gui.script.GuiNPCEventScripts; import noppes.npcs.client.gui.script.GuiScriptInterface; import noppes.npcs.client.gui.util.*; +import noppes.npcs.constants.ScriptContext; import noppes.npcs.controllers.ScriptContainer; import noppes.npcs.controllers.ScriptController; import noppes.npcs.controllers.data.DataScript; @@ -166,8 +167,10 @@ public class GuiScript extends GuiScriptInterface { int scrollbarOffset = activeArea.hasVerticalScrollbar() ? -8 : -2; fullscreenButton.initGui(editorX + editorWidth, editorY, scrollbarOffset); - // ==================== RIGHT PANEL BUTTONS (hidden in fullscreen) ==================== - if (!isFullscreen) { + // Set language and script context for proper syntax highlighting and autocomplete + activeArea.setLanguage(script.getLanguage()); + activeArea.setScriptContext(ScriptContext.NPC); + addButton(new GuiNpcButton(102, guiLeft + 315, guiTop + 4, 50, 20, "gui.clear")); addButton(new GuiNpcButton(101, guiLeft + 366, guiTop + 4, 50, 20, "gui.paste")); addButton(new GuiNpcButton(100, guiLeft + 315, guiTop + 25, 50, 20, "gui.copy")); diff --git a/src/main/java/noppes/npcs/client/gui/script/GuiScriptBlock.java b/src/main/java/noppes/npcs/client/gui/script/GuiScriptBlock.java index c95c2c443..5b9762a96 100644 --- a/src/main/java/noppes/npcs/client/gui/script/GuiScriptBlock.java +++ b/src/main/java/noppes/npcs/client/gui/script/GuiScriptBlock.java @@ -5,7 +5,6 @@ import net.minecraft.nbt.NBTTagCompound; import noppes.npcs.api.handler.IScriptHookHandler; import noppes.npcs.blocks.tiles.TileScripted; import noppes.npcs.constants.ScriptContext; -import noppes.npcs.controllers.ScriptHookController; import java.util.ArrayList; @@ -25,6 +24,11 @@ public class GuiScriptBlock extends GuiScriptInterface { loaded = true; } + @Override + protected ScriptContext getScriptContext() { + return ScriptContext.BLOCK; + } + public void save() { if (loaded) { super.save(); 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 5f180e578..11c326880 100644 --- a/src/main/java/noppes/npcs/client/gui/script/GuiScriptInterface.java +++ b/src/main/java/noppes/npcs/client/gui/script/GuiScriptInterface.java @@ -23,6 +23,7 @@ import noppes.npcs.client.gui.util.ITextChangeListener; import noppes.npcs.client.gui.util.ITextfieldListener; import noppes.npcs.controllers.ScriptContainer; import noppes.npcs.controllers.ScriptController; +import noppes.npcs.constants.ScriptContext; import noppes.npcs.controllers.data.ForgeDataScript; import noppes.npcs.controllers.data.IScriptHandler; import noppes.npcs.controllers.data.IScriptUnit; @@ -225,6 +226,9 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb // Set the script context for context-aware hook autocomplete activeArea.setScriptContext(getScriptContext()); + // Set the script context for context-aware hook autocomplete + activeArea.setScriptContext(getScriptContext()); + // Setup fullscreen key binding GuiScriptTextArea.KEYS.FULLSCREEN.setTask(e -> { if (e.isPress()) { @@ -627,96 +631,6 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb return ScriptContext.GLOBAL; } - // ==================== UNIFIED SCRIPT DATA HANDLING ==================== - - /** - * Unified setGuiData for script GUIs with old container system. - * Handles both language data and tab-specific script loading. - */ - protected void setGuiDataWithOldContainer(NBTTagCompound compound) { - if (compound.hasKey("LoadComplete")) { - loaded = true; - return; - } - - if (!compound.hasKey("Tab")) { - this.handler.setLanguage(compound.getString("ScriptLanguage")); - this.handler.setEnabled(compound.getBoolean("ScriptEnabled")); - this.loadLanguagesData(compound); - } else { - int tab = compound.getInteger("Tab"); - ScriptContainer container = new ScriptContainer(this.handler); - container.readFromNBT(compound.getCompoundTag("Script")); - this.setHandlerContainer(container); - this.initGui(); - } - } - - /** - * Load languages data from NBT. - * Separated for potential override in subclasses if needed. - */ - protected void loadLanguagesData(NBTTagCompound compound) { - NBTTagList data = compound.getTagList("Languages", 10); - HashMap languages = new HashMap(); - - for (int i = 0; i < data.tagCount(); ++i) { - NBTTagCompound comp = data.getCompoundTagAt(i); - java.util.ArrayList scripts = new java.util.ArrayList(); - NBTTagList list = comp.getTagList("Scripts", 8); - - for (int j = 0; j < list.tagCount(); ++j) { - scripts.add(list.getStringTagAt(j)); - } - - languages.put(comp.getString("Language"), scripts); - } - - this.languages = languages; - this.initGui(); - } - - /** - * Set the handler's container. Override if handler is not IScriptHandler. - */ - protected void setHandlerContainer(IScriptUnit container) { - // Default implementation - subclasses may need to cast and set differently - // e.g., ((LinkedItemScript) handler).container = container; - } - - /** - * Unified save method for script GUIs with packet-based saving. - * Subclasses only need to override sendScriptPackets() and sendMetadataPacket(). - */ - protected void saveWithPackets() { - if (loaded) { - this.setScript(); - - List containers = this.handler.getScripts(); - for (int i = 0; i < containers.size(); i++) { - IScriptUnit container = containers.get(i); - sendSavePacket(i, containers.size(), container.writeToNBT(new NBTTagCompound())); - } - - NBTTagCompound scriptData = new NBTTagCompound(); - scriptData.setString("ScriptLanguage", this.handler.getLanguage()); - scriptData.setBoolean("ScriptEnabled", this.handler.getEnabled()); - scriptData.setTag("ScriptConsole", noppes.npcs.NBTTags.NBTLongStringMap(this.handler.getConsoleText())); - - sendSavePacket(-1, containers.size(), scriptData); - } - } - - /** - * Send a script container packet. Override in subclasses. - * @param index The index of this script - * @param totalCount Total number of scripts - * @param scriptNBT The script container NBT data - */ - protected void sendSavePacket(int index, int totalCount, NBTTagCompound scriptNBT) { - // Default: do nothing (for non-packet-based GUIs like GuiScriptItem) - } - public void save() { if (loaded) this.setScript(); diff --git a/src/main/java/noppes/npcs/client/gui/script/GuiScriptItem.java b/src/main/java/noppes/npcs/client/gui/script/GuiScriptItem.java index cb5b807f2..5d23aca89 100644 --- a/src/main/java/noppes/npcs/client/gui/script/GuiScriptItem.java +++ b/src/main/java/noppes/npcs/client/gui/script/GuiScriptItem.java @@ -6,9 +6,7 @@ import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import noppes.npcs.CustomItems; import noppes.npcs.NBTTags; -import noppes.npcs.api.handler.IScriptHookHandler; import noppes.npcs.constants.ScriptContext; -import noppes.npcs.controllers.ScriptHookController; import noppes.npcs.scripted.item.ScriptCustomItem; import java.util.*; @@ -37,6 +35,11 @@ public class GuiScriptItem extends GuiScriptInterface { } } + @Override + protected ScriptContext getScriptContext() { + return ScriptContext.ITEM; + } + public void save() { if (loaded) { super.save(); diff --git a/src/main/java/noppes/npcs/client/gui/script/GuiScriptPlayers.java b/src/main/java/noppes/npcs/client/gui/script/GuiScriptPlayers.java index 2befb8d74..755f56f56 100644 --- a/src/main/java/noppes/npcs/client/gui/script/GuiScriptPlayers.java +++ b/src/main/java/noppes/npcs/client/gui/script/GuiScriptPlayers.java @@ -4,7 +4,8 @@ import kamkeel.npcs.network.packets.request.script.PlayerScriptPacket; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.nbt.NBTTagCompound; import noppes.npcs.NBTTags; -import noppes.npcs.api.handler.IScriptHookHandler; +import noppes.npcs.constants.ScriptContext; +import noppes.npcs.constants.EnumScriptType; import noppes.npcs.controllers.ScriptContainer; import noppes.npcs.controllers.data.EffectScript; import noppes.npcs.controllers.data.IScriptUnit; @@ -47,6 +48,11 @@ public class GuiScriptPlayers extends GuiScriptInterface { } } + @Override + protected ScriptContext getScriptContext() { + return ScriptContext.PLAYER; + } + public void save() { if (loaded) { super.save(); diff --git a/src/main/java/noppes/npcs/client/gui/util/GuiScriptTextArea.java b/src/main/java/noppes/npcs/client/gui/util/GuiScriptTextArea.java index 3426bcae8..5f3e590f0 100644 --- a/src/main/java/noppes/npcs/client/gui/util/GuiScriptTextArea.java +++ b/src/main/java/noppes/npcs/client/gui/util/GuiScriptTextArea.java @@ -11,6 +11,7 @@ import noppes.npcs.client.gui.util.key.OverlayKeyPresetViewer; import noppes.npcs.client.gui.util.script.*; import noppes.npcs.client.gui.util.script.JavaTextContainer.LineData; import noppes.npcs.client.key.impl.ScriptEditorKeys; +import noppes.npcs.constants.ScriptContext; import noppes.npcs.util.ValueUtil; import org.lwjgl.input.Keyboard; import org.lwjgl.input.Mouse; @@ -2159,6 +2160,27 @@ public class GuiScriptTextArea extends GuiNpcTextField { this.container.formatCodeText(); } + /** + * Set the script context (NPC, PLAYER, BLOCK, ITEM, etc.). + * This determines which hooks and event types are available for autocomplete. + * + * @param context The script context + */ + public void setScriptContext(ScriptContext context) { + if (this.container != null) { + this.container.setScriptContext(context); + } + } + + /** + * Get the current script context. + * + * @return The script context (NPC, PLAYER, BLOCK, ITEM, etc.) + */ + public ScriptContext getScriptContext() { + return this.container != null ? this.container.getScriptContext() : ScriptContext.GLOBAL; + } + public void setListener(ITextChangeListener listener) { this.listener = listener; } diff --git a/src/main/java/noppes/npcs/constants/ScriptContext.java b/src/main/java/noppes/npcs/constants/ScriptContext.java new file mode 100644 index 000000000..a4567955b --- /dev/null +++ b/src/main/java/noppes/npcs/constants/ScriptContext.java @@ -0,0 +1,201 @@ +package noppes.npcs.constants; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Represents a script context that maps to one or more event interface namespaces. + * + * A script context can have multiple namespaces because script editors often + * support hooks from multiple event types. For example, Player scripts support: + * - IPlayerEvent (core player hooks) + * - IAnimationEvent (animation hooks) + * - IPartyEvent (party hooks) + * - ICustomGuiEvent (custom GUI hooks) + * - etc. + * + * This is a registerable system - any mod can register their own contexts: + * + * // Register a new context with multiple namespaces: + * ScriptContext.register("DBC", "IDBCEvent", "IDBCPlayerEvent", "IDBCFormEvent"); + * + * // Or add namespaces to an existing context: + * ScriptContext.PLAYER.addNamespace("IDBCPlayerEvent"); + */ +public class ScriptContext { + + // Registry of all script contexts + private static final Map REGISTRY = new ConcurrentHashMap<>(); + + // ==================== BUILT-IN CONTEXTS ==================== + + public static final ScriptContext NPC = register("NPC", + "INpcEvent", + "IProjectileEvent" + ); + + public static final ScriptContext PLAYER = register("PLAYER", + "IPlayerEvent", + "IAnimationEvent", + "IPartyEvent", + "IDialogEvent", + "IQuestEvent", + "IFactionEvent", + "ICustomGuiEvent", + "IEffectEvent" + ); + + public static final ScriptContext BLOCK = register("BLOCK", + "IBlockEvent" + ); + + public static final ScriptContext ITEM = register("ITEM", + "IItemEvent" + ); + + public static final ScriptContext FORGE = register("FORGE", + "IForgeEvent" + ); + + public static final ScriptContext GLOBAL = register("GLOBAL", + "Global" + ); + + // ==================== INSTANCE FIELDS ==================== + + /** Unique identifier for this context (e.g., "NPC", "PLAYER", "DBC") */ + public final String id; + + /** The event interface namespaces this context supports */ + private final List namespaces; + + // ==================== CONSTRUCTOR ==================== + + private ScriptContext(String id, String... namespaces) { + this.id = id; + this.namespaces = new ArrayList<>(Arrays.asList(namespaces)); + } + + // ==================== NAMESPACE ACCESS ==================== + + /** + * Get all namespaces this context supports. + * + * @return Unmodifiable list of namespace strings + */ + public List getNamespaces() { + return Collections.unmodifiableList(namespaces); + } + + /** + * Check if this context includes a specific namespace. + * + * @param namespace The namespace to check (e.g., "IPlayerEvent") + * @return true if this context supports hooks from that namespace + */ + public boolean hasNamespace(String namespace) { + return namespaces.contains(namespace); + } + + /** + * Add a namespace to this context. + * Mods can use this to extend built-in contexts with their own event types. + * + * @param namespace The namespace to add (e.g., "IDBCPlayerEvent") + */ + public void addNamespace(String namespace) { + if (!namespaces.contains(namespace)) { + namespaces.add(namespace); + } + } + + /** + * Get the primary namespace (first one registered). + * This is mainly for backward compatibility. + * + * @return The first namespace, or "Global" if none + */ + public String getPrimaryNamespace() { + return namespaces.isEmpty() ? "Global" : namespaces.get(0); + } + + // ==================== REGISTRATION API ==================== + + /** + * Register a new script context with one or more namespaces. + * + * @param id Unique identifier (e.g., "DBC", "CUSTOM") + * @param namespaces The event interface names (e.g., "IDBCEvent", "IDBCPlayerEvent") + * @return The registered ScriptContext + */ + public static ScriptContext register(String id, String... namespaces) { + ScriptContext context = new ScriptContext(id, namespaces); + REGISTRY.put(id, context); + return context; + } + + /** + * Get a script context by its ID. + * + * @param id The context ID (e.g., "NPC", "PLAYER", "DBC") + * @return The ScriptContext, or GLOBAL if not found + */ + public static ScriptContext byId(String id) { + return REGISTRY.getOrDefault(id, GLOBAL); + } + + /** + * Find a script context that contains a specific namespace. + * + * @param namespace The event interface namespace (e.g., "INpcEvent") + * @return The first ScriptContext that contains this namespace, or GLOBAL if not found + */ + public static ScriptContext byNamespace(String namespace) { + if (namespace == null) return GLOBAL; + for (ScriptContext ctx : REGISTRY.values()) { + if (ctx.hasNamespace(namespace)) { + return ctx; + } + } + return GLOBAL; + } + + /** + * Get all registered script contexts. + * + * @return Unmodifiable collection of all registered contexts + */ + public static Collection values() { + return Collections.unmodifiableCollection(REGISTRY.values()); + } + + /** + * Check if a context with the given ID exists. + * + * @param id The context ID to check + * @return true if registered + */ + public static boolean exists(String id) { + return REGISTRY.containsKey(id); + } + + // ==================== OBJECT METHODS ==================== + + @Override + public String toString() { + return id + " -> " + namespaces; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof ScriptContext)) return false; + ScriptContext other = (ScriptContext) obj; + return id.equals(other.id); + } + + @Override + public int hashCode() { + return id.hashCode(); + } +}