diff --git a/src/main/java/emu/grasscutter/data/binout/AbilityModifier.java b/src/main/java/emu/grasscutter/data/binout/AbilityModifier.java index a1de94e92..ff5c26eff 100644 --- a/src/main/java/emu/grasscutter/data/binout/AbilityModifier.java +++ b/src/main/java/emu/grasscutter/data/binout/AbilityModifier.java @@ -345,6 +345,7 @@ public class AbilityModifier implements Serializable { public DynamicFloat valueRangeMax; public String overrideMapKey; + public int paramNum; public DynamicFloat param1 = DynamicFloat.ZERO, param2 = DynamicFloat.ZERO, @@ -359,12 +360,18 @@ public class AbilityModifier implements Serializable { public String content; public enum LuaCallType { + Gadget, + @SerializedName(value = "OwnerGadegt", alternate = "OwnerGadget") + OwnerGadget, FromGroup, - CurGalleryControlGroup, - CurChallengeGroup, + OwnerFromGroup, SpecificGroup, + CurScenePlay, + CurChallengeGroup, + CurRogueBossGroup, + CurGalleryControlGroup, AbilityGroupSourceGroup, - CurScenePlay + LevelBankZoneContainsGroup } public enum DropType { diff --git a/src/main/java/emu/grasscutter/game/ability/actions/ActionServerLuaCall.java b/src/main/java/emu/grasscutter/game/ability/actions/ActionServerLuaCall.java index 26f9134ad..e0ce5c7f5 100644 --- a/src/main/java/emu/grasscutter/game/ability/actions/ActionServerLuaCall.java +++ b/src/main/java/emu/grasscutter/game/ability/actions/ActionServerLuaCall.java @@ -8,6 +8,7 @@ import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.scripts.ScriptLoader; import javax.script.Bindings; import org.luaj.vm2.LuaFunction; +import org.luaj.vm2.LuaValue; @AbilityAction(AbilityModifierAction.Type.ServerLuaCall) public final class ActionServerLuaCall extends AbilityActionHandler { @@ -16,12 +17,14 @@ public final class ActionServerLuaCall extends AbilityActionHandler { Ability ability, AbilityModifierAction action, ByteString abilityData, GameEntity target) { var scene = target.getScene(); var scriptManager = scene.getScriptManager(); + var functionName = action.funcName; // Set the script library's manager. var scriptLib = ScriptLoader.getScriptLib(); scriptLib.setCurrentEntity(target); scriptLib.setSceneScriptManager(scriptManager); + // Attempt to call the function. return switch (action.luaCallType) { default -> false; @@ -33,7 +36,7 @@ public final class ActionServerLuaCall extends AbilityActionHandler { // Set the script library's group. scriptLib.setCurrentGroup(group); - yield ActionServerLuaCall.callFunction(script, functionName); + yield ActionServerLuaCall.callFunction(script, functionName, ability, action); } case SpecificGroup -> { var groupId = action.callParamList[0]; @@ -43,7 +46,16 @@ public final class ActionServerLuaCall extends AbilityActionHandler { // Set the script library's group. scriptLib.setCurrentGroup(group); - yield ActionServerLuaCall.callFunction(script, functionName); + yield ActionServerLuaCall.callFunction(script, functionName, ability, action); + } + case Gadget -> { + var controller = target.getEntityController(); + if (controller == null || functionName.isBlank()) yield false; + + // Hand off the function handling to the controller. + controller.callControllerScriptFunc(target, functionName, ability, action); + + yield true; } }; } @@ -53,17 +65,32 @@ public final class ActionServerLuaCall extends AbilityActionHandler { * * @param bindings The bindings to fetch the function from. * @param functionName The name of the function to call. + * @param ability The ability data. + * @param action The ability action data. * @return Whether the function was called successfully. */ - private static boolean callFunction(Bindings bindings, String functionName) { + private static boolean callFunction( + Bindings bindings, String functionName, + Ability ability, AbilityModifierAction action + ) { try { // Resolve the function from the script. var function = bindings.get(functionName); if (!(function instanceof LuaFunction luaFunction)) throw new Exception("Function is not a LuaFunction."); - // Attempt to invoke the function. - luaFunction.call(ScriptLoader.getScriptLibLua()); + // Convert parameters to Lua values. + var lParam1 = LuaValue.valueOf(action.param1.getInt(ability)); + var lParam2 = LuaValue.valueOf(action.param2.getInt(ability)); + var lParam3 = LuaValue.valueOf(action.param3.getInt(ability)); + + // Invoke the function with the parameters. + switch (action.paramNum) { + case 1 -> luaFunction.invoke(new LuaValue[] { lParam1 }); + case 2 -> luaFunction.invoke(new LuaValue[] { lParam1, lParam2 }); + case 3 -> luaFunction.invoke(new LuaValue[] { lParam1, lParam2, lParam3 }); + default -> luaFunction.invoke(new LuaValue[] { ScriptLoader.getScriptLibLua() }); + } return true; } catch (Exception exception) { diff --git a/src/main/java/emu/grasscutter/scripts/data/controller/EntityController.java b/src/main/java/emu/grasscutter/scripts/data/controller/EntityController.java index 82b7c2a65..7c9220bc4 100644 --- a/src/main/java/emu/grasscutter/scripts/data/controller/EntityController.java +++ b/src/main/java/emu/grasscutter/scripts/data/controller/EntityController.java @@ -1,6 +1,8 @@ package emu.grasscutter.scripts.data.controller; import emu.grasscutter.*; +import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction; +import emu.grasscutter.game.ability.Ability; import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.game.props.ElementType; import emu.grasscutter.scripts.*; @@ -58,6 +60,30 @@ public class EntityController { return 0; } + /** + * Invoked from {@link emu.grasscutter.game.ability.actions.ActionServerLuaCall} to call an entity controller function. + * + * @param entity The entity which called the function. + * @param funcName The name of the function to call. + * @param ability The ability that is calling the function. + * @param action The action that is calling the function. + * @return The return value of the function. + */ + public LuaValue callControllerScriptFunc( + GameEntity entity, String funcName, + Ability ability, AbilityModifierAction action) { + var lParam1 = LuaValue.valueOf(action.param1.getInt(ability)); + var lParam2 = LuaValue.valueOf(action.param2.getInt(ability)); + var lParam3 = LuaValue.valueOf(action.param3.getInt(ability)); + + return switch (action.paramNum) { + case 1 -> this.callControllerScriptFunc(entity, funcName, lParam1); + case 2 -> this.callControllerScriptFunc(entity, funcName, lParam1, lParam2); + case 3 -> this.callControllerScriptFunc(entity, funcName, lParam1, lParam2, lParam3); + default -> this.callControllerScriptFunc(entity, funcName, LuaValue.NIL); + }; + } + // TODO actual execution should probably be handle by EntityControllerScriptManager private LuaValue callControllerScriptFunc(GameEntity entity, String funcName, LuaValue arg1) { return callControllerScriptFunc(entity, funcName, arg1, LuaValue.NIL, LuaValue.NIL);