diff --git a/assets/Scripts/client/clienthooks.ts b/assets/Scripts/client/clienthooks.ts new file mode 100644 index 00000000..1f061856 --- /dev/null +++ b/assets/Scripts/client/clienthooks.ts @@ -0,0 +1,14 @@ +import { Engine } from "/Scripts/types/engine"; +import { Hook } from "/Scripts/types/hook"; + +/** + * The client-wide hooks + */ +export const clientHooks: Hook[] = [ + { + signal: "OPEN_VOXEL", + callback: (engine: Engine) => { + engine.classes.menuUtils.static.openVoxel() + } + } +] \ No newline at end of file diff --git a/assets/Scripts/client/entity/inventory.ts b/assets/Scripts/client/entity/inventory.ts index 3ab708fd..a983f551 100644 --- a/assets/Scripts/client/entity/inventory.ts +++ b/assets/Scripts/client/entity/inventory.ts @@ -6,6 +6,9 @@ import { Entity } from "/Scripts/types/host/entity/entity" */ export type Item = Entity +/** + * An inventory + */ export interface Inventory { } diff --git a/assets/Scripts/engine/engine-init.ts b/assets/Scripts/engine/engine-init.ts index baaffa23..f7a4b4ca 100644 --- a/assets/Scripts/engine/engine-init.ts +++ b/assets/Scripts/engine/engine-init.ts @@ -3,6 +3,7 @@ import { Client, NamespaceClient } from '/Scripts/client/client' import { HookManager } from '/Scripts/engine/hooks/hook-manager' import { SceneLoader } from '/Scripts/engine/scene/scene-loader' import { Engine } from '/Scripts/types/engine' +import { clientHooks } from '/Scripts/client/clienthooks' /** * The core engine values @@ -25,5 +26,10 @@ export const ENGINE_onInit = () => { engine.sceneLoader.engine = engine engine.hookManager.engine = engine + //load global hooks + clientHooks.forEach(hook => { + engine.hookManager.registerGlobalHook(hook,false) + }) + loggerScripts.INFO('Script Engine Initialized') } diff --git a/assets/Scripts/engine/hooks/hook-manager.ts b/assets/Scripts/engine/hooks/hook-manager.ts index d2cf747a..4f817d49 100644 --- a/assets/Scripts/engine/hooks/hook-manager.ts +++ b/assets/Scripts/engine/hooks/hook-manager.ts @@ -95,6 +95,28 @@ export class HookManager { scene.signalHookMap[hookSignal] = sceneSignalArray } + /** + * Registers a global hook + * @param hook The hook + */ + registerGlobalHook(hook: Hook, isServerScene: boolean){ + const trackedHook: TrackedHook = { + ...hook, + scope: isServerScene ? HookScope.SCENE_SERVER : HookScope.SCENE_CLIENT, + } + //add to flat array + this.hooks.push(trackedHook) + //add to signal array + const hookSignal: string = hook.signal + const signalArray: Array = this.signalHookMap?.[hookSignal] ? this.signalHookMap?.[hookSignal] : [] + signalArray.push(trackedHook) + this.signalHookMap[hookSignal] = signalArray + loggerScripts.DEBUG('register signal hook map') + loggerScripts.DEBUG(hookSignal) + loggerScripts.DEBUG(Object.keys(this.signalHookMap) + '') + loggerScripts.DEBUG(this.signalHookMap[hookSignal] + '') + } + /** * Deregisters a hook * @param scene The scene which introduced the hook @@ -118,7 +140,8 @@ export class HookManager { const globalHooks: Array = this.signalHookMap[signal] if(!!globalHooks){ globalHooks.forEach(trackedHook => { - trackedHook.callback(...argsRaw) + loggerScripts.DEBUG("Called global hook") + trackedHook.callback(this.engine, ...argsRaw) }) } else { //There isn't a hook registered for this signal at the global level @@ -131,7 +154,8 @@ export class HookManager { const sceneHooks: Array = scene.signalHookMap[signal] if(!!sceneHooks){ sceneHooks.forEach(trackeHook => { - trackeHook.callback(...argsRaw) + loggerScripts.DEBUG("Called local hook") + trackeHook.callback(this.engine, ...argsRaw) }) } else { //There isn't a hook registered for this signal at the scene level diff --git a/assets/Scripts/types/hook.ts b/assets/Scripts/types/hook.ts index c9d494be..b981ab77 100644 --- a/assets/Scripts/types/hook.ts +++ b/assets/Scripts/types/hook.ts @@ -1,3 +1,4 @@ +import { Engine } from "/Scripts/types/engine"; /** @@ -13,6 +14,6 @@ export interface Hook { /** * The function to call when the signal is fired */ - readonly callback: (...value: any) => void, + readonly callback: (engine: Engine, ...value: any) => void, } diff --git a/assets/Scripts/types/host/renderer/ui/menus.ts b/assets/Scripts/types/host/renderer/ui/menus.ts new file mode 100644 index 00000000..e19d27cc --- /dev/null +++ b/assets/Scripts/types/host/renderer/ui/menus.ts @@ -0,0 +1,11 @@ +/** + * Utilities for ui menu interactions + */ +export interface MenuUtils { + + /** + * Opens the voxel selection menu + */ + readonly openVoxel: () => void + +} \ No newline at end of file diff --git a/assets/Scripts/types/host/static-classes.ts b/assets/Scripts/types/host/static-classes.ts index e8ece579..1ee3b9a1 100644 --- a/assets/Scripts/types/host/static-classes.ts +++ b/assets/Scripts/types/host/static-classes.ts @@ -1,4 +1,5 @@ import { Entity } from "/Scripts/types/host/entity/entity"; +import { MenuUtils } from "/Scripts/types/host/renderer/ui/menus"; import { TutorialUtils } from "/Scripts/types/host/renderer/ui/tutorial"; import { Vector } from "/Scripts/types/spatial"; @@ -28,6 +29,10 @@ export interface StaticClasses { */ readonly serverUtils?: Class, + /** + * Utilities for interacting with menus on client + */ + readonly menuUtils?: Class, } diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 80ddf74d..d59eafb1 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -857,6 +857,7 @@ Filter toolbar slots out of equip menu Fix attack tree checks Disable client equip tests until can review Toolbar scrolling +Items executing script engine hooks on usage # TODO diff --git a/src/main/java/electrosphere/client/item/ItemActions.java b/src/main/java/electrosphere/client/item/ItemActions.java index 315e9eb9..56c0a4c6 100644 --- a/src/main/java/electrosphere/client/item/ItemActions.java +++ b/src/main/java/electrosphere/client/item/ItemActions.java @@ -1,5 +1,6 @@ package electrosphere.client.item; +import electrosphere.client.script.ClientScriptUtils; import electrosphere.client.ui.menu.WindowStrings; import electrosphere.client.ui.menu.WindowUtils; import electrosphere.client.ui.menu.ingame.MenuGeneratorsTerrainEditing; @@ -48,6 +49,14 @@ public class ItemActions { if(shooterTree != null){ shooterTree.fire(); } + ClientToolbarState clientToolbarState = ClientToolbarState.getClientToolbarState(Globals.playerEntity); + Entity primaryEntity = clientToolbarState.getCurrentPrimaryItem(); + if(primaryEntity != null && Globals.gameConfigCurrent.getItemMap().getItem(primaryEntity) != null){ + Item data = Globals.gameConfigCurrent.getItemMap().getItem(primaryEntity); + if(data.getClientSidePrimary() != null){ + ClientScriptUtils.fireSignal(data.getClientSidePrimary()); + } + } } } diff --git a/src/main/java/electrosphere/client/script/ClientScriptUtils.java b/src/main/java/electrosphere/client/script/ClientScriptUtils.java new file mode 100644 index 00000000..55d53ab1 --- /dev/null +++ b/src/main/java/electrosphere/client/script/ClientScriptUtils.java @@ -0,0 +1,22 @@ +package electrosphere.client.script; + +import electrosphere.engine.Globals; +import electrosphere.script.ScriptEngine; + +/** + * Utilities for dealing with the scripting engine from the client's perspective + */ +public class ClientScriptUtils { + + /** + * Fires a signal + * @param signalName The name of the signal + * @param args The arguments provided alongside the signal + */ + public static void fireSignal(String signalName, Object ... args){ + if(Globals.scriptEngine != null && Globals.scriptEngine.isInitialized()){ + Globals.scriptEngine.fireSignal(signalName, ScriptEngine.GLOBAL_SCENE, args); + } + } + +} diff --git a/src/main/java/electrosphere/client/ui/menu/script/ScriptMenuUtils.java b/src/main/java/electrosphere/client/ui/menu/script/ScriptMenuUtils.java new file mode 100644 index 00000000..be80eb24 --- /dev/null +++ b/src/main/java/electrosphere/client/ui/menu/script/ScriptMenuUtils.java @@ -0,0 +1,25 @@ +package electrosphere.client.ui.menu.script; + +import org.graalvm.polyglot.HostAccess.Export; + +import electrosphere.client.ui.menu.WindowStrings; +import electrosphere.client.ui.menu.WindowUtils; +import electrosphere.client.ui.menu.ingame.MenuGeneratorsTerrainEditing; +import electrosphere.controls.ControlHandler.ControlsState; +import electrosphere.engine.Globals; + +/** + * Utilities provided to the scripting interface that allow creating/destroying menus + */ +public class ScriptMenuUtils { + + /** + * Opens the voxel selection menu + */ + @Export + public static void openVoxel(){ + WindowUtils.replaceWindow(WindowStrings.VOXEL_TYPE_SELECTION,MenuGeneratorsTerrainEditing.createVoxelTypeSelectionPanel()); + Globals.controlHandler.hintUpdateControlState(ControlsState.IN_GAME_MAIN_MENU); + } + +} diff --git a/src/main/java/electrosphere/script/ScriptEngine.java b/src/main/java/electrosphere/script/ScriptEngine.java index 82c25c08..9e4c8ffb 100644 --- a/src/main/java/electrosphere/script/ScriptEngine.java +++ b/src/main/java/electrosphere/script/ScriptEngine.java @@ -13,6 +13,7 @@ import org.graalvm.polyglot.Source; import org.graalvm.polyglot.Source.Builder; import org.graalvm.polyglot.Value; +import electrosphere.client.ui.menu.script.ScriptMenuUtils; import electrosphere.client.ui.menu.tutorial.TutorialMenus; import electrosphere.engine.Globals; import electrosphere.engine.Main; @@ -92,6 +93,7 @@ public class ScriptEngine { {"simulation",Main.class}, {"tutorialUtils",TutorialMenus.class}, {"serverUtils",JSServerUtils.class}, + {"menuUtils",ScriptMenuUtils.class}, }; //singletons from the host that are provided to the javascript context