From 53cc1822f6f65250ac5da6d00735ce797659df49 Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Thu, 28 Apr 2022 08:20:37 -0700 Subject: [PATCH 01/14] Implement dungeon entry --- .../java/emu/grasscutter/data/GameData.java | 10 +++ .../emu/grasscutter/data/ResourceLoader.java | 1 + .../grasscutter/data/common/PointData.java | 59 +++++++---------- .../emu/grasscutter/data/def/DungeonData.java | 28 +++++++++ .../emu/grasscutter/data/def/SceneData.java | 7 ++- .../game/dungeons/DungeonManager.java | 63 +++++++++++++++++++ .../emu/grasscutter/game/world/Scene.java | 24 +++++++ .../emu/grasscutter/game/world/World.java | 43 ++++++++++--- .../recv/HandlerDungeonEntryInfoReq.java | 3 + .../recv/HandlerPlayerEnterDungeonReq.java | 20 ++++++ .../recv/HandlerPlayerQuitDungeonReq.java | 16 +++++ .../send/PacketDungeonEntryInfoRsp.java | 37 +++++++++++ .../send/PacketPathfindingEnterSceneRsp.java | 2 - .../send/PacketPlayerEnterDungeonRsp.java | 19 ++++++ .../java/emu/grasscutter/utils/Utils.java | 1 + 15 files changed, 285 insertions(+), 48 deletions(-) create mode 100644 src/main/java/emu/grasscutter/data/def/DungeonData.java create mode 100644 src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerEnterDungeonReq.java create mode 100644 src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerQuitDungeonReq.java create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketDungeonEntryInfoRsp.java create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketPlayerEnterDungeonRsp.java diff --git a/src/main/java/emu/grasscutter/data/GameData.java b/src/main/java/emu/grasscutter/data/GameData.java index 4c7363054..5ac8ae444 100644 --- a/src/main/java/emu/grasscutter/data/GameData.java +++ b/src/main/java/emu/grasscutter/data/GameData.java @@ -61,6 +61,7 @@ public class GameData { private static final Int2ObjectMap fetterCharacterCardDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap rewardDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap worldLevelDataMap = new Int2ObjectOpenHashMap<>(); + private static final Int2ObjectMap dungeonDataMap = new Int2ObjectOpenHashMap<>(); // Cache private static Map> fetters = new HashMap<>(); @@ -97,6 +98,11 @@ public class GameData { public static Map getScenePointEntries() { return scenePointEntries; } + + // TODO optimize + public static ScenePointEntry getScenePointEntryById(int sceneId, int pointId) { + return getScenePointEntries().get(sceneId + "_" + pointId); + } public static Int2ObjectMap getAvatarDataMap() { return avatarDataMap; @@ -265,4 +271,8 @@ public class GameData { public static Int2ObjectMap getWorldLevelDataMap() { return worldLevelDataMap; } + + public static Int2ObjectMap getDungeonDataMap() { + return dungeonDataMap; + } } diff --git a/src/main/java/emu/grasscutter/data/ResourceLoader.java b/src/main/java/emu/grasscutter/data/ResourceLoader.java index 589f6aca8..6b48645d8 100644 --- a/src/main/java/emu/grasscutter/data/ResourceLoader.java +++ b/src/main/java/emu/grasscutter/data/ResourceLoader.java @@ -164,6 +164,7 @@ public class ResourceLoader { for (Map.Entry entry : config.points.entrySet()) { PointData pointData = Grasscutter.getGsonFactory().fromJson(entry.getValue(), PointData.class); + pointData.setId(Integer.parseInt(entry.getKey())); ScenePointEntry sl = new ScenePointEntry(sceneId + "_" + entry.getKey(), pointData); scenePointList.add(sl); diff --git a/src/main/java/emu/grasscutter/data/common/PointData.java b/src/main/java/emu/grasscutter/data/common/PointData.java index 7c31d2f06..147eee81a 100644 --- a/src/main/java/emu/grasscutter/data/common/PointData.java +++ b/src/main/java/emu/grasscutter/data/common/PointData.java @@ -1,43 +1,30 @@ package emu.grasscutter.data.common; -public class PointData { - private pos tranPos; +import emu.grasscutter.utils.Position; - public pos getTranPos() { +public class PointData { + private int id; + private String $type; + private Position tranPos; + private int[] dungeonIds; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getType() { + return $type; + } + + public Position getTranPos() { return tranPos; } - public void setTranPos(pos tranPos) { - this.tranPos = tranPos; - } - - public class pos { - private float x; - private float y; - private float z; - - public float getX() { - return x; - } - - public void setX(float x) { - this.x = x; - } - - public float getY() { - return y; - } - - public void setY(float y) { - this.y = y; - } - - public float getZ() { - return z; - } - - public void setZ(float z) { - this.z = z; - } - } + public int[] getDungeonIds() { + return dungeonIds; + } } diff --git a/src/main/java/emu/grasscutter/data/def/DungeonData.java b/src/main/java/emu/grasscutter/data/def/DungeonData.java new file mode 100644 index 000000000..3239d30fb --- /dev/null +++ b/src/main/java/emu/grasscutter/data/def/DungeonData.java @@ -0,0 +1,28 @@ +package emu.grasscutter.data.def; + +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; + +import emu.grasscutter.game.props.SceneType; + +@ResourceType(name = "DungeonExcelConfigData.json") +public class DungeonData extends GameResource { + private int Id; + private int SceneId; + private String InvolveType; // TODO enum + + @Override + public int getId() { + return this.Id; + } + + public int getSceneId() { + return SceneId; + } + + @Override + public void onLoad() { + + } +} diff --git a/src/main/java/emu/grasscutter/data/def/SceneData.java b/src/main/java/emu/grasscutter/data/def/SceneData.java index 220579faf..cd1510e1d 100644 --- a/src/main/java/emu/grasscutter/data/def/SceneData.java +++ b/src/main/java/emu/grasscutter/data/def/SceneData.java @@ -9,8 +9,9 @@ import emu.grasscutter.game.props.SceneType; @ResourceType(name = "SceneExcelConfigData.json") public class SceneData extends GameResource { private int Id; - private SceneType SceneType; + private SceneType Type; private String ScriptData; + @Override public int getId() { @@ -18,7 +19,7 @@ public class SceneData extends GameResource { } public SceneType getSceneType() { - return SceneType; + return Type; } public String getScriptData() { @@ -27,6 +28,6 @@ public class SceneData extends GameResource { @Override public void onLoad() { - + } } diff --git a/src/main/java/emu/grasscutter/game/dungeons/DungeonManager.java b/src/main/java/emu/grasscutter/game/dungeons/DungeonManager.java index 33bac7d09..3c5b61903 100644 --- a/src/main/java/emu/grasscutter/game/dungeons/DungeonManager.java +++ b/src/main/java/emu/grasscutter/game/dungeons/DungeonManager.java @@ -1,6 +1,18 @@ package emu.grasscutter.game.dungeons; +import emu.grasscutter.GameConstants; +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.custom.ScenePointEntry; +import emu.grasscutter.data.def.DungeonData; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.props.SceneType; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.server.game.GameServer; +import emu.grasscutter.server.packet.send.PacketDungeonEntryInfoRsp; +import emu.grasscutter.server.packet.send.PacketPlayerEnterDungeonRsp; +import emu.grasscutter.utils.Position; public class DungeonManager { private final GameServer server; @@ -12,4 +24,55 @@ public class DungeonManager { public GameServer getServer() { return server; } + + public void getEntryInfo(Player player, int pointId) { + ScenePointEntry entry = GameData.getScenePointEntryById(player.getScene().getId(), pointId); + + if (entry == null || entry.getPointData().getDungeonIds() == null) { + // Error + player.sendPacket(new PacketDungeonEntryInfoRsp()); + return; + } + + player.sendPacket(new PacketDungeonEntryInfoRsp(player, entry.getPointData())); + } + + public void enterDungeon(Player player, int pointId, int dungeonId) { + DungeonData data = GameData.getDungeonDataMap().get(dungeonId); + + if (data == null) { + return; + } + + int sceneId = data.getSceneId(); + + player.getWorld().transferPlayerToScene(player, sceneId, data); + + player.sendPacket(new PacketPlayerEnterDungeonRsp(pointId, dungeonId)); + } + + public void exitDungeon(Player player) { + if (player.getScene().getSceneType() != SceneType.SCENE_DUNGEON) { + return; + } + + // Get previous scene + int prevScene = player.getScene().getPrevScene() > 0 ? player.getScene().getPrevScene() : 3; + + // Get previous position + DungeonData dungeonData = player.getScene().getDungeonData(); + Position prevPos = new Position(GameConstants.START_POSITION); + + if (dungeonData != null) { + ScenePointEntry entry = GameData.getScenePointEntryById(prevScene, dungeonData.getId()); + + if (entry != null) { + prevPos.set(entry.getPointData().getTranPos()); + } + } + + // Transfer player back to world + player.getWorld().transferPlayerToScene(player, prevScene, prevPos); + player.sendPacket(new BasePacket(PacketOpcodes.PlayerQuitDungeonRsp)); + } } diff --git a/src/main/java/emu/grasscutter/game/world/Scene.java b/src/main/java/emu/grasscutter/game/world/Scene.java index 543079153..f3ede3e73 100644 --- a/src/main/java/emu/grasscutter/game/world/Scene.java +++ b/src/main/java/emu/grasscutter/game/world/Scene.java @@ -3,6 +3,7 @@ package emu.grasscutter.game.world; import emu.grasscutter.Grasscutter; import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameDepot; +import emu.grasscutter.data.def.DungeonData; import emu.grasscutter.data.def.MonsterData; import emu.grasscutter.data.def.SceneData; import emu.grasscutter.data.def.WorldLevelData; @@ -41,6 +42,9 @@ public class Scene { private int time; private ClimateType climate; private int weather; + + private DungeonData dungeonData; + private int prevScene; // Id of the previous scene public Scene(World world, SceneData sceneData) { this.world = world; @@ -50,6 +54,7 @@ public class Scene { this.time = 8 * 60; this.climate = ClimateType.CLIMATE_SUNNY; + this.prevScene = 3; this.spawnedEntities = new HashSet<>(); this.deadSpawnedEntities = new HashSet<>(); @@ -111,6 +116,14 @@ public class Scene { this.weather = weather; } + public int getPrevScene() { + return prevScene; + } + + public void setPrevScene(int prevScene) { + this.prevScene = prevScene; + } + public boolean dontDestroyWhenEmpty() { return dontDestroyWhenEmpty; } @@ -127,6 +140,17 @@ public class Scene { return deadSpawnedEntities; } + public DungeonData getDungeonData() { + return dungeonData; + } + + public void setDungeonData(DungeonData dungeonData) { + if (this.dungeonData != null || this.getSceneType() != SceneType.SCENE_DUNGEON || dungeonData.getSceneId() != this.getId()) { + return; + } + this.dungeonData = dungeonData; + } + public boolean isInScene(GameEntity entity) { return this.entities.containsKey(entity.getId()); } diff --git a/src/main/java/emu/grasscutter/game/world/World.java b/src/main/java/emu/grasscutter/game/world/World.java index 292e63d32..2a9324e40 100644 --- a/src/main/java/emu/grasscutter/game/world/World.java +++ b/src/main/java/emu/grasscutter/game/world/World.java @@ -17,6 +17,7 @@ import emu.grasscutter.game.props.EntityIdType; import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.LifeState; import emu.grasscutter.data.GameData; +import emu.grasscutter.data.def.DungeonData; import emu.grasscutter.data.def.SceneData; import emu.grasscutter.game.entity.EntityAvatar; import emu.grasscutter.game.entity.EntityClientGadget; @@ -212,6 +213,14 @@ public class World implements Iterable { } public boolean transferPlayerToScene(Player player, int sceneId, Position pos) { + return transferPlayerToScene(player, sceneId, null, pos); + } + + public boolean transferPlayerToScene(Player player, int sceneId, DungeonData data) { + return transferPlayerToScene(player, sceneId, data, null); + } + + public boolean transferPlayerToScene(Player player, int sceneId, DungeonData dungeonData, Position pos) { if (GameData.getSceneDataMap().get(sceneId) == null) { return false; } @@ -224,25 +233,45 @@ public class World implements Iterable { // Dont deregister scenes if the player is going to tp back into them if (oldScene.getId() == sceneId) { oldScene.setDontDestroyWhenEmpty(true); - } + } oldScene.removePlayer(player); } Scene newScene = this.getSceneById(sceneId); + newScene.setDungeonData(dungeonData); newScene.addPlayer(player); - player.getPos().set(pos); + // Dungeon + if (dungeonData != null) { + // TODO set position + } + + // Set player position + if (pos != null) { + player.getPos().set(pos); + } else { + pos = player.getPos(); + } + if (oldScene != null) { + newScene.setPrevScene(oldScene.getId()); oldScene.setDontDestroyWhenEmpty(false); } - // Teleport packet - if (oldScene == newScene) { - player.sendPacket(new PacketPlayerEnterSceneNotify(player, EnterType.ENTER_GOTO, EnterReason.TransPoint, sceneId, pos)); - } else { - player.sendPacket(new PacketPlayerEnterSceneNotify(player, EnterType.ENTER_JUMP, EnterReason.TransPoint, sceneId, pos)); + // Get enter types + EnterType enterType = EnterType.ENTER_JUMP; + EnterReason enterReason = EnterReason.TransPoint; + + if (dungeonData != null) { + enterType = EnterType.ENTER_DUNGEON; + enterReason = EnterReason.DungeonEnter; + } else if (oldScene == newScene) { + enterType = EnterType.ENTER_GOTO; } + + // Teleport packet + player.sendPacket(new PacketPlayerEnterSceneNotify(player, enterType, enterReason, sceneId, pos)); return true; } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerDungeonEntryInfoReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerDungeonEntryInfoReq.java index a490e9d73..286fbdc67 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerDungeonEntryInfoReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerDungeonEntryInfoReq.java @@ -2,6 +2,7 @@ package emu.grasscutter.server.packet.recv; import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.DungeonEntryInfoReqOuterClass.DungeonEntryInfoReq; import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.server.game.GameSession; @@ -10,7 +11,9 @@ public class HandlerDungeonEntryInfoReq extends PacketHandler { @Override public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + DungeonEntryInfoReq req = DungeonEntryInfoReq.parseFrom(payload); + session.getServer().getDungeonManager().getEntryInfo(session.getPlayer(), req.getPointId()); } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerEnterDungeonReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerEnterDungeonReq.java new file mode 100644 index 000000000..ce05c8ccf --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerEnterDungeonReq.java @@ -0,0 +1,20 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.PlayerEnterDungeonReqOuterClass.PlayerEnterDungeonReq; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.server.game.GameSession; + +@Opcodes(PacketOpcodes.PlayerEnterDungeonReq) +public class HandlerPlayerEnterDungeonReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + // Auto template + PlayerEnterDungeonReq req = PlayerEnterDungeonReq.parseFrom(payload); + + session.getServer().getDungeonManager().enterDungeon(session.getPlayer(), req.getPointId(), req.getDungeonId()); + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerQuitDungeonReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerQuitDungeonReq.java new file mode 100644 index 000000000..e33190847 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerQuitDungeonReq.java @@ -0,0 +1,16 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.server.game.GameSession; + +@Opcodes(PacketOpcodes.PlayerQuitDungeonReq) +public class HandlerPlayerQuitDungeonReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + session.getPlayer().getServer().getDungeonManager().exitDungeon(session.getPlayer()); + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonEntryInfoRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonEntryInfoRsp.java new file mode 100644 index 000000000..a2cc052bb --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonEntryInfoRsp.java @@ -0,0 +1,37 @@ +package emu.grasscutter.server.packet.send; + +import java.util.Arrays; + +import emu.grasscutter.data.common.PointData; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.DungeonEntryInfoOuterClass.DungeonEntryInfo; +import emu.grasscutter.net.proto.DungeonEntryInfoRspOuterClass.DungeonEntryInfoRsp; + +public class PacketDungeonEntryInfoRsp extends BasePacket { + + public PacketDungeonEntryInfoRsp(Player player, PointData pointData) { + super(PacketOpcodes.DungeonEntryInfoRsp); + + DungeonEntryInfoRsp.Builder proto = DungeonEntryInfoRsp.newBuilder() + .setPointId(pointData.getId()); + + for (int dungeonId : pointData.getDungeonIds()) { + DungeonEntryInfo info = DungeonEntryInfo.newBuilder().setDungeonId(dungeonId).build(); + proto.addDungeonEntryList(info); + } + + this.setData(proto); + } + + public PacketDungeonEntryInfoRsp() { + super(PacketOpcodes.DungeonEntryInfoRsp); + + DungeonEntryInfoRsp proto = DungeonEntryInfoRsp.newBuilder() + .setRetcode(1) + .build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPathfindingEnterSceneRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPathfindingEnterSceneRsp.java index 16caca296..9c96888ea 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketPathfindingEnterSceneRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPathfindingEnterSceneRsp.java @@ -7,7 +7,5 @@ public class PacketPathfindingEnterSceneRsp extends BasePacket { public PacketPathfindingEnterSceneRsp(int clientSequence) { super(PacketOpcodes.PathfindingEnterSceneRsp); - - this.buildHeader(clientSequence); } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerEnterDungeonRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerEnterDungeonRsp.java new file mode 100644 index 000000000..913ca109e --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerEnterDungeonRsp.java @@ -0,0 +1,19 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.PlayerEnterDungeonRspOuterClass.PlayerEnterDungeonRsp; + +public class PacketPlayerEnterDungeonRsp extends BasePacket { + + public PacketPlayerEnterDungeonRsp(int pointId, int dungeonId) { + super(PacketOpcodes.PlayerEnterDungeonRsp); + + PlayerEnterDungeonRsp proto = PlayerEnterDungeonRsp.newBuilder() + .setPointId(pointId) + .setDungeonId(dungeonId) + .build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/utils/Utils.java b/src/main/java/emu/grasscutter/utils/Utils.java index 8129a1188..b6c965c6b 100644 --- a/src/main/java/emu/grasscutter/utils/Utils.java +++ b/src/main/java/emu/grasscutter/utils/Utils.java @@ -65,6 +65,7 @@ public final class Utils { private static final char[] HEX_ARRAY = "0123456789abcdef".toCharArray(); public static String bytesToHex(byte[] bytes) { + if (bytes == null) return ""; char[] hexChars = new char[bytes.length * 2]; for (int j = 0; j < bytes.length; j++) { int v = bytes[j] & 0xFF; From d71b7abfc305fb7ff4b6ccafbc4c3e0c8696b837 Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Thu, 28 Apr 2022 22:19:14 -0700 Subject: [PATCH 02/14] Implement script support needed for dungeons Only a few are supported right now You will need certain script files in ./resources/Scripts --- build.gradle | 5 +- src/main/java/emu/grasscutter/Config.java | 1 + .../java/emu/grasscutter/Grasscutter.java | 2 + .../emu/grasscutter/data/def/GadgetData.java | 5 +- .../game/dungeons/DungeonChallenge.java | 90 ++++++ .../game/dungeons/DungeonManager.java | 4 +- .../game/entity/EntityBaseGadget.java | 18 ++ .../game/entity/EntityClientGadget.java | 2 +- .../grasscutter/game/entity/EntityGadget.java | 145 ++++++++- .../grasscutter/game/entity/EntityItem.java | 2 +- .../game/entity/EntityMonster.java | 25 +- .../grasscutter/game/entity/GameEntity.java | 28 ++ .../grasscutter/game/player/TeamManager.java | 6 +- .../grasscutter/game/props/EntityType.java | 93 ++++++ .../emu/grasscutter/game/world/Scene.java | 118 +++++++- .../emu/grasscutter/game/world/World.java | 19 +- .../scripts/SceneScriptManager.java | 283 ++++++++++++++++++ .../emu/grasscutter/scripts/ScriptLib.java | 165 ++++++++++ .../emu/grasscutter/scripts/ScriptLoader.java | 74 +++++ .../scripts/constants/ScriptEventType.java | 82 +++++ .../scripts/constants/ScriptGadgetState.java | 24 ++ .../scripts/constants/ScriptRegionShape.java | 7 + .../grasscutter/scripts/data/SceneBlock.java | 17 ++ .../grasscutter/scripts/data/SceneConfig.java | 11 + .../grasscutter/scripts/data/SceneGadget.java | 12 + .../grasscutter/scripts/data/SceneGroup.java | 17 ++ .../scripts/data/SceneInitConfig.java | 9 + .../scripts/data/SceneMonster.java | 11 + .../grasscutter/scripts/data/SceneSuite.java | 10 + .../scripts/data/SceneTrigger.java | 10 + .../scripts/serializer/LuaSerializer.java | 108 +++++++ .../scripts/serializer/Serializer.java | 12 + .../recv/HandlerSelectWorktopOptionReq.java | 22 ++ .../PacketDungeonChallengeBeginNotify.java | 20 ++ .../PacketDungeonChallengeFinishNotify.java | 20 ++ .../packet/send/PacketGadgetInteractRsp.java | 4 +- .../packet/send/PacketGadgetStateNotify.java | 21 ++ .../send/PacketSelectWorktopOptionRsp.java | 19 ++ .../send/PacketWorktopOptionNotify.java | 22 ++ 39 files changed, 1499 insertions(+), 44 deletions(-) create mode 100644 src/main/java/emu/grasscutter/game/dungeons/DungeonChallenge.java create mode 100644 src/main/java/emu/grasscutter/game/entity/EntityBaseGadget.java create mode 100644 src/main/java/emu/grasscutter/game/props/EntityType.java create mode 100644 src/main/java/emu/grasscutter/scripts/SceneScriptManager.java create mode 100644 src/main/java/emu/grasscutter/scripts/ScriptLib.java create mode 100644 src/main/java/emu/grasscutter/scripts/ScriptLoader.java create mode 100644 src/main/java/emu/grasscutter/scripts/constants/ScriptEventType.java create mode 100644 src/main/java/emu/grasscutter/scripts/constants/ScriptGadgetState.java create mode 100644 src/main/java/emu/grasscutter/scripts/constants/ScriptRegionShape.java create mode 100644 src/main/java/emu/grasscutter/scripts/data/SceneBlock.java create mode 100644 src/main/java/emu/grasscutter/scripts/data/SceneConfig.java create mode 100644 src/main/java/emu/grasscutter/scripts/data/SceneGadget.java create mode 100644 src/main/java/emu/grasscutter/scripts/data/SceneGroup.java create mode 100644 src/main/java/emu/grasscutter/scripts/data/SceneInitConfig.java create mode 100644 src/main/java/emu/grasscutter/scripts/data/SceneMonster.java create mode 100644 src/main/java/emu/grasscutter/scripts/data/SceneSuite.java create mode 100644 src/main/java/emu/grasscutter/scripts/data/SceneTrigger.java create mode 100644 src/main/java/emu/grasscutter/scripts/serializer/LuaSerializer.java create mode 100644 src/main/java/emu/grasscutter/scripts/serializer/Serializer.java create mode 100644 src/main/java/emu/grasscutter/server/packet/recv/HandlerSelectWorktopOptionReq.java create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketDungeonChallengeBeginNotify.java create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketDungeonChallengeFinishNotify.java create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketGadgetStateNotify.java create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketSelectWorktopOptionRsp.java create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketWorktopOptionNotify.java diff --git a/build.gradle b/build.gradle index 1ac88d0fa..b32491b2a 100644 --- a/build.gradle +++ b/build.gradle @@ -71,9 +71,10 @@ dependencies { implementation group: 'org.quartz-scheduler', name: 'quartz', version: '2.3.2' implementation group: 'org.quartz-scheduler', name: 'quartz-jobs', version: '2.3.2' - + + implementation group: 'org.luaj', name: 'luaj-jse', version: '3.0.1' + protobuf files('proto/') - } application { diff --git a/src/main/java/emu/grasscutter/Config.java b/src/main/java/emu/grasscutter/Config.java index 3e6e16d20..495035324 100644 --- a/src/main/java/emu/grasscutter/Config.java +++ b/src/main/java/emu/grasscutter/Config.java @@ -10,6 +10,7 @@ public final class Config { public String PACKETS_FOLDER = "./packets/"; public String DUMPS_FOLDER = "./dumps/"; public String KEY_FOLDER = "./keys/"; + public String SCRIPTS_FOLDER = "./resources/Scripts/"; public String PLUGINS_FOLDER = "./plugins/"; public String RunMode = "HYBRID"; // HYBRID, DISPATCH_ONLY, GAME_ONLY diff --git a/src/main/java/emu/grasscutter/Grasscutter.java b/src/main/java/emu/grasscutter/Grasscutter.java index dcd6a3b6f..f7669d30b 100644 --- a/src/main/java/emu/grasscutter/Grasscutter.java +++ b/src/main/java/emu/grasscutter/Grasscutter.java @@ -9,6 +9,7 @@ import java.net.InetSocketAddress; import emu.grasscutter.command.CommandMap; import emu.grasscutter.plugin.PluginManager; +import emu.grasscutter.scripts.ScriptLoader; import emu.grasscutter.utils.Utils; import org.reflections.Reflections; import org.slf4j.LoggerFactory; @@ -67,6 +68,7 @@ public final class Grasscutter { // Load all resources. ResourceLoader.loadAll(); + ScriptLoader.init(); // Database DatabaseManager.initialize(); diff --git a/src/main/java/emu/grasscutter/data/def/GadgetData.java b/src/main/java/emu/grasscutter/data/def/GadgetData.java index 7a071972b..85854eb88 100644 --- a/src/main/java/emu/grasscutter/data/def/GadgetData.java +++ b/src/main/java/emu/grasscutter/data/def/GadgetData.java @@ -2,12 +2,13 @@ package emu.grasscutter.data.def; import emu.grasscutter.data.GameResource; import emu.grasscutter.data.ResourceType; +import emu.grasscutter.game.props.EntityType; @ResourceType(name = "GadgetExcelConfigData.json") public class GadgetData extends GameResource { private int Id; - private String Type; + private EntityType Type; private String JsonName; private boolean IsInteractive; private String[] Tags; @@ -21,7 +22,7 @@ public class GadgetData extends GameResource { return this.Id; } - public String getType() { + public EntityType getType() { return Type; } diff --git a/src/main/java/emu/grasscutter/game/dungeons/DungeonChallenge.java b/src/main/java/emu/grasscutter/game/dungeons/DungeonChallenge.java new file mode 100644 index 000000000..66d02221a --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/DungeonChallenge.java @@ -0,0 +1,90 @@ +package emu.grasscutter.game.dungeons; + +import java.util.ArrayList; +import java.util.List; + +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.def.MonsterData; +import emu.grasscutter.game.entity.EntityMonster; +import emu.grasscutter.game.entity.GameEntity; +import emu.grasscutter.game.world.Scene; +import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType; +import emu.grasscutter.scripts.data.SceneGroup; +import emu.grasscutter.scripts.data.SceneMonster; +import emu.grasscutter.server.packet.send.PacketDungeonChallengeBeginNotify; +import emu.grasscutter.server.packet.send.PacketDungeonChallengeFinishNotify; +import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify; + +public class DungeonChallenge { + private final Scene scene; + private final SceneGroup group; + + private int challengeIndex; + private int challengeId; + private boolean isSuccess; + + private int score; + private int objective = 0; + + public DungeonChallenge(Scene scene, SceneGroup group) { + this.scene = scene; + this.group = group; + + objective += group.monsters.size(); + } + + public Scene getScene() { + return scene; + } + + public SceneGroup getGroup() { + return group; + } + + public int getChallengeIndex() { + return challengeIndex; + } + + public void setChallengeIndex(int challengeIndex) { + this.challengeIndex = challengeIndex; + } + + public int getChallengeId() { + return challengeId; + } + + public void setChallengeId(int challengeId) { + this.challengeId = challengeId; + } + + public boolean isSuccess() { + return isSuccess; + } + + public void setSuccess(boolean isSuccess) { + this.isSuccess = isSuccess; + } + + public void start() { + getScene().broadcastPacket(new PacketDungeonChallengeBeginNotify(this)); + } + + public void finish() { + getScene().broadcastPacket(new PacketDungeonChallengeFinishNotify(this)); + + if (this.isSuccess()) { + this.getScene().getScriptManager().onChallengeSuccess(); + } else { + this.getScene().getScriptManager().onChallengeFailure(); + } + } + + public void onMonsterDie(EntityMonster entity) { + score++; + + if (score >= objective) { + this.setSuccess(true); + finish(); + } + } +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/DungeonManager.java b/src/main/java/emu/grasscutter/game/dungeons/DungeonManager.java index 3c5b61903..5662abf4d 100644 --- a/src/main/java/emu/grasscutter/game/dungeons/DungeonManager.java +++ b/src/main/java/emu/grasscutter/game/dungeons/DungeonManager.java @@ -45,9 +45,11 @@ public class DungeonManager { } int sceneId = data.getSceneId(); + player.getScene().setPrevScene(sceneId); player.getWorld().transferPlayerToScene(player, sceneId, data); + player.getScene().setPrevScenePoint(pointId); player.sendPacket(new PacketPlayerEnterDungeonRsp(pointId, dungeonId)); } @@ -64,7 +66,7 @@ public class DungeonManager { Position prevPos = new Position(GameConstants.START_POSITION); if (dungeonData != null) { - ScenePointEntry entry = GameData.getScenePointEntryById(prevScene, dungeonData.getId()); + ScenePointEntry entry = GameData.getScenePointEntryById(prevScene, player.getScene().getPrevScenePoint()); if (entry != null) { prevPos.set(entry.getPointData().getTranPos()); diff --git a/src/main/java/emu/grasscutter/game/entity/EntityBaseGadget.java b/src/main/java/emu/grasscutter/game/entity/EntityBaseGadget.java new file mode 100644 index 000000000..a5b2cb6c5 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/entity/EntityBaseGadget.java @@ -0,0 +1,18 @@ +package emu.grasscutter.game.entity; + +import emu.grasscutter.game.world.Scene; +import emu.grasscutter.game.world.World; + +public abstract class EntityBaseGadget extends GameEntity { + + public EntityBaseGadget(Scene scene) { + super(scene); + } + + public abstract int getGadgetId(); + + @Override + public void onDeath(int killerId) { + + } +} diff --git a/src/main/java/emu/grasscutter/game/entity/EntityClientGadget.java b/src/main/java/emu/grasscutter/game/entity/EntityClientGadget.java index 464789426..77b76566a 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityClientGadget.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityClientGadget.java @@ -23,7 +23,7 @@ import emu.grasscutter.utils.Position; import emu.grasscutter.utils.ProtoHelper; import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap; -public class EntityClientGadget extends EntityGadget { +public class EntityClientGadget extends EntityBaseGadget { private final Player owner; private final Position pos; diff --git a/src/main/java/emu/grasscutter/game/entity/EntityGadget.java b/src/main/java/emu/grasscutter/game/entity/EntityGadget.java index c09504c21..640f93f22 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityGadget.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityGadget.java @@ -1,18 +1,157 @@ package emu.grasscutter.game.entity; +import java.util.Arrays; +import java.util.List; + +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.def.GadgetData; +import emu.grasscutter.game.props.EntityIdType; +import emu.grasscutter.game.props.EntityType; +import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.World; +import emu.grasscutter.net.proto.ClientGadgetInfoOuterClass; +import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo; +import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair; +import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo; +import emu.grasscutter.net.proto.EntityClientDataOuterClass.EntityClientData; +import emu.grasscutter.net.proto.EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo; +import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo; +import emu.grasscutter.net.proto.PropPairOuterClass.PropPair; +import emu.grasscutter.net.proto.ProtEntityTypeOuterClass.ProtEntityType; +import emu.grasscutter.net.proto.SceneEntityAiInfoOuterClass.SceneEntityAiInfo; +import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo; +import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo; +import emu.grasscutter.net.proto.VectorOuterClass.Vector; +import emu.grasscutter.net.proto.WorktopInfoOuterClass.WorktopInfo; +import emu.grasscutter.utils.Position; +import emu.grasscutter.utils.ProtoHelper; +import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntSet; -public abstract class EntityGadget extends GameEntity { +public class EntityGadget extends EntityBaseGadget { + private final GadgetData data; + private final Position pos; + private final Position rot; + private int gadgetId; + + private int state; + private IntSet worktopOptions; - public EntityGadget(Scene scene) { + public EntityGadget(Scene scene, int gadgetId, Position pos) { super(scene); + this.data = GameData.getGadgetDataMap().get(gadgetId); + this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET); + this.gadgetId = gadgetId; + this.pos = pos.clone(); + this.rot = new Position(); } - public abstract int getGadgetId(); + public GadgetData getGadgetData() { + return data; + } + + @Override + public Position getPosition() { + // TODO Auto-generated method stub + return this.pos; + } + + @Override + public Position getRotation() { + // TODO Auto-generated method stub + return this.rot; + } + public int getGadgetId() { + return gadgetId; + } + + public void setGadgetId(int gadgetId) { + this.gadgetId = gadgetId; + } + + public int getState() { + return state; + } + + public void setState(int state) { + this.state = state; + } + + public IntSet getWorktopOptions() { + return worktopOptions; + } + + public void addWorktopOptions(int[] options) { + if (this.worktopOptions == null) { + this.worktopOptions = new IntOpenHashSet(); + } + Arrays.stream(options).forEach(this.worktopOptions::add); + } + + public void removeWorktopOption(int option) { + if (this.worktopOptions == null) { + return; + } + this.worktopOptions.remove(option); + } + + @Override + public Int2FloatOpenHashMap getFightProperties() { + // TODO Auto-generated method stub + return null; + } + @Override public void onDeath(int killerId) { } + + @Override + public SceneEntityInfo toProto() { + EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder() + .setAbilityInfo(AbilitySyncStateInfo.newBuilder()) + .setRendererChangedInfo(EntityRendererChangedInfo.newBuilder()) + .setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(Vector.newBuilder())) + .setBornPos(Vector.newBuilder()) + .build(); + + SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder() + .setEntityId(getId()) + .setEntityType(ProtEntityType.PROT_ENTITY_GADGET) + .setMotionInfo(MotionInfo.newBuilder().setPos(getPosition().toProto()).setRot(getRotation().toProto()).setSpeed(Vector.newBuilder())) + .addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder()) + .setEntityClientData(EntityClientData.newBuilder()) + .setEntityAuthorityInfo(authority) + .setLifeState(1); + + PropPair pair = PropPair.newBuilder() + .setType(PlayerProperty.PROP_LEVEL.getId()) + .setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 1)) + .build(); + entityInfo.addPropList(pair); + + SceneGadgetInfo.Builder gadgetInfo = SceneGadgetInfo.newBuilder() + .setGadgetId(this.getGadgetId()) + .setGroupId(this.getGroupId()) + .setConfigId(this.getConfigId()) + .setGadgetState(this.getState()) + .setIsEnableInteract(true) + .setAuthorityPeerId(this.getScene().getWorld().getHostPeerId()); + + if (this.getGadgetData().getType() == EntityType.Worktop && this.getWorktopOptions() != null) { + WorktopInfo worktop = WorktopInfo.newBuilder() + .addAllOptionList(this.getWorktopOptions()) + .build(); + gadgetInfo.setWorktop(worktop); + } + + entityInfo.setGadget(gadgetInfo); + + return entityInfo.build(); + } } diff --git a/src/main/java/emu/grasscutter/game/entity/EntityItem.java b/src/main/java/emu/grasscutter/game/entity/EntityItem.java index e23c1cb33..f2b7386e1 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityItem.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityItem.java @@ -23,7 +23,7 @@ import emu.grasscutter.utils.Position; import emu.grasscutter.utils.ProtoHelper; import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap; -public class EntityItem extends EntityGadget { +public class EntityItem extends EntityBaseGadget { private final Position pos; private final Position rot; diff --git a/src/main/java/emu/grasscutter/game/entity/EntityMonster.java b/src/main/java/emu/grasscutter/game/entity/EntityMonster.java index b9f212fe1..be5812a1d 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityMonster.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityMonster.java @@ -4,6 +4,7 @@ import emu.grasscutter.data.GameData; import emu.grasscutter.data.common.PropGrowCurve; import emu.grasscutter.data.def.MonsterCurveData; import emu.grasscutter.data.def.MonsterData; +import emu.grasscutter.game.dungeons.DungeonChallenge; import emu.grasscutter.game.props.EntityIdType; import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.PlayerProperty; @@ -36,9 +37,6 @@ public class EntityMonster extends GameEntity { private final Position bornPos; private final int level; private int weaponEntityId; - - private int groupId; - private int configId; private int poseId; public EntityMonster(Scene scene, MonsterData monsterData, Position pos, int level) { @@ -103,22 +101,6 @@ public class EntityMonster extends GameEntity { public boolean isAlive() { return this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) > 0f; } - - public int getGroupId() { - return groupId; - } - - public void setGroupId(int groupId) { - this.groupId = groupId; - } - - public int getConfigId() { - return configId; - } - - public void setConfigId(int configId) { - this.configId = configId; - } public int getPoseId() { return poseId; @@ -127,12 +109,15 @@ public class EntityMonster extends GameEntity { public void setPoseId(int poseId) { this.poseId = poseId; } - + @Override public void onDeath(int killerId) { if (this.getSpawnEntry() != null) { this.getScene().getDeadSpawnedEntities().add(getSpawnEntry()); } + if (getScene().getChallenge() != null && getScene().getChallenge().getGroup().id == this.getGroupId()) { + getScene().getChallenge().onMonsterDie(this); + } } public void recalcStats() { diff --git a/src/main/java/emu/grasscutter/game/entity/GameEntity.java b/src/main/java/emu/grasscutter/game/entity/GameEntity.java index aef3378b6..24598a652 100644 --- a/src/main/java/emu/grasscutter/game/entity/GameEntity.java +++ b/src/main/java/emu/grasscutter/game/entity/GameEntity.java @@ -17,6 +17,10 @@ public abstract class GameEntity { private final Scene scene; private SpawnDataEntry spawnEntry; + private int blockId; + private int configId; + private int groupId; + private MotionState moveState; private int lastMoveSceneTimeMs; private int lastMoveReliableSeq; @@ -96,6 +100,30 @@ public abstract class GameEntity { return getFightProperties().getOrDefault(prop.getId(), 0f); } + public int getBlockId() { + return blockId; + } + + public void setBlockId(int blockId) { + this.blockId = blockId; + } + + public int getConfigId() { + return configId; + } + + public void setConfigId(int configId) { + this.configId = configId; + } + + public int getGroupId() { + return groupId; + } + + public void setGroupId(int groupId) { + this.groupId = groupId; + } + protected MotionInfo getMotionInfo() { MotionInfo proto = MotionInfo.newBuilder() .setPos(getPosition().toProto()) diff --git a/src/main/java/emu/grasscutter/game/player/TeamManager.java b/src/main/java/emu/grasscutter/game/player/TeamManager.java index b942604f5..84ee38424 100644 --- a/src/main/java/emu/grasscutter/game/player/TeamManager.java +++ b/src/main/java/emu/grasscutter/game/player/TeamManager.java @@ -15,7 +15,7 @@ import emu.grasscutter.Grasscutter; import emu.grasscutter.data.def.AvatarSkillDepotData; import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.entity.EntityAvatar; -import emu.grasscutter.game.entity.EntityGadget; +import emu.grasscutter.game.entity.EntityBaseGadget; import emu.grasscutter.game.props.ElementType; import emu.grasscutter.game.props.EnterReason; import emu.grasscutter.game.props.FightProperty; @@ -54,7 +54,7 @@ public class TeamManager { @Transient private TeamInfo mpTeam; @Transient private int entityId; @Transient private final List avatars; - @Transient private final Set gadgets; + @Transient private final Set gadgets; @Transient private final IntSet teamResonances; @Transient private final IntSet teamResonancesConfig; @@ -141,7 +141,7 @@ public class TeamManager { this.entityId = entityId; } - public Set getGadgets() { + public Set getGadgets() { return gadgets; } diff --git a/src/main/java/emu/grasscutter/game/props/EntityType.java b/src/main/java/emu/grasscutter/game/props/EntityType.java new file mode 100644 index 000000000..efe6694c4 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/props/EntityType.java @@ -0,0 +1,93 @@ +package emu.grasscutter.game.props; + +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; + +public enum EntityType { + None (0), + Avatar (1), + Monster (2), + Bullet (3), + AttackPhyisicalUnit (4), + AOE (5), + Camera (6), + EnviroArea (7), + Equip (8), + MonsterEquip (9), + Grass (10), + Level (11), + NPC (12), + TransPointFirst (13), + TransPointFirstGadget (14), + TransPointSecond (15), + TransPointSecondGadget (16), + DropItem (17), + Field (18), + Gadget (19), + Water (20), + GatherPoint (21), + GatherObject (22), + AirflowField (23), + SpeedupField (24), + Gear (25), + Chest (26), + EnergyBall (27), + ElemCrystal (28), + Timeline (29), + Worktop (30), + Team (31), + Platform (32), + AmberWind (33), + EnvAnimal (34), + SealGadget (35), + Tree (36), + Bush (37), + QuestGadget (38), + Lightning (39), + RewardPoint (40), + RewardStatue (41), + MPLevel (42), + WindSeed (43), + MpPlayRewardPoint (44), + ViewPoint (45), + RemoteAvatar (46), + GeneralRewardPoint (47), + PlayTeam (48), + OfferingGadget (49), + EyePoint (50), + MiracleRing (51), + Foundation (52), + WidgetGadget (53), + PlaceHolder (99); + + private final int value; + private static final Int2ObjectMap map = new Int2ObjectOpenHashMap<>(); + private static final Map stringMap = new HashMap<>(); + + static { + Stream.of(values()).forEach(e -> { + map.put(e.getValue(), e); + stringMap.put(e.name(), e); + }); + } + + private EntityType(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static EntityType getTypeByValue(int value) { + return map.getOrDefault(value, None); + } + + public static EntityType getTypeByName(String name) { + return stringMap.getOrDefault(name, None); + } +} diff --git a/src/main/java/emu/grasscutter/game/world/Scene.java b/src/main/java/emu/grasscutter/game/world/Scene.java index f3ede3e73..44c5be1d2 100644 --- a/src/main/java/emu/grasscutter/game/world/Scene.java +++ b/src/main/java/emu/grasscutter/game/world/Scene.java @@ -7,6 +7,7 @@ import emu.grasscutter.data.def.DungeonData; import emu.grasscutter.data.def.MonsterData; import emu.grasscutter.data.def.SceneData; import emu.grasscutter.data.def.WorldLevelData; +import emu.grasscutter.game.dungeons.DungeonChallenge; import emu.grasscutter.game.entity.*; import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.TeamInfo; @@ -18,6 +19,10 @@ import emu.grasscutter.game.world.SpawnDataEntry.SpawnGroupEntry; import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult; import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType; +import emu.grasscutter.scripts.SceneScriptManager; +import emu.grasscutter.scripts.data.SceneBlock; +import emu.grasscutter.scripts.data.SceneGadget; +import emu.grasscutter.scripts.data.SceneGroup; import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify; import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify; @@ -37,15 +42,19 @@ public class Scene { private final Set spawnedEntities; private final Set deadSpawnedEntities; + private final Set loadedBlocks; private boolean dontDestroyWhenEmpty; private int time; private ClimateType climate; private int weather; + private SceneScriptManager scriptManager; + private DungeonChallenge challenge; private DungeonData dungeonData; private int prevScene; // Id of the previous scene - + private int prevScenePoint; + public Scene(World world, SceneData sceneData) { this.world = world; this.sceneData = sceneData; @@ -58,6 +67,8 @@ public class Scene { this.spawnedEntities = new HashSet<>(); this.deadSpawnedEntities = new HashSet<>(); + this.loadedBlocks = new HashSet<>(); + this.scriptManager = new SceneScriptManager(this); } public int getId() { @@ -124,6 +135,14 @@ public class Scene { this.prevScene = prevScene; } + public int getPrevScenePoint() { + return prevScenePoint; + } + + public void setPrevScenePoint(int prevPoint) { + this.prevScenePoint = prevPoint; + } + public boolean dontDestroyWhenEmpty() { return dontDestroyWhenEmpty; } @@ -132,6 +151,10 @@ public class Scene { this.dontDestroyWhenEmpty = dontDestroyWhenEmpty; } + public Set getLoadedBlocks() { + return loadedBlocks; + } + public Set getSpawnedEntities() { return spawnedEntities; } @@ -140,6 +163,10 @@ public class Scene { return deadSpawnedEntities; } + public SceneScriptManager getScriptManager() { + return scriptManager; + } + public DungeonData getDungeonData() { return dungeonData; } @@ -151,6 +178,14 @@ public class Scene { this.dungeonData = dungeonData; } + public DungeonChallenge getChallenge() { + return challenge; + } + + public void setChallenge(DungeonChallenge challenge) { + this.challenge = challenge; + } + public boolean isInScene(GameEntity entity) { return this.entities.containsKey(entity.getId()); } @@ -183,7 +218,7 @@ public class Scene { this.removePlayerAvatars(player); // Remove player gadgets - for (EntityGadget gadget : player.getTeamManager().getGadgets()) { + for (EntityBaseGadget gadget : player.getTeamManager().getGadgets()) { this.removeEntity(gadget); } @@ -338,7 +373,15 @@ public class Scene { } public void onTick() { - this.checkSpawns(); + if (this.getScriptManager().isInit()) { + this.checkBlocks(); + } else { + // TEMPORARY + this.checkSpawns(); + } + + // Triggers + this.getScriptManager().onTick(); } // TODO - Test @@ -411,6 +454,75 @@ public class Scene { } } + public void checkBlocks() { + Set visible = new HashSet<>(); + + for (Player player : this.getPlayers()) { + for (SceneBlock block : getScriptManager().getBlocks()) { + if (!block.contains(player.getPos())) { + continue; + } + + visible.add(block); + } + } + + Iterator it = this.getLoadedBlocks().iterator(); + while (it.hasNext()) { + SceneBlock block = it.next(); + + if (!visible.contains(block)) { + it.remove(); + + onUnloadBlock(block); + } + } + + for (SceneBlock block : visible) { + if (!this.getLoadedBlocks().contains(block)) { + this.getLoadedBlocks().add(block); + this.onLoadBlock(block); + } + } + } + + // TODO optimize + public void onLoadBlock(SceneBlock block) { + for (SceneGroup group : block.groups) { + group.triggers.forEach(getScriptManager()::registerTrigger); + } + + for (SceneGroup group : block.groups) { + for (SceneGadget g : group.gadgets) { + EntityGadget entity = new EntityGadget(this, g.gadget_id, g.pos); + + if (entity.getGadgetData() == null) continue; + + entity.setBlockId(block.id); + entity.setConfigId(g.config_id); + entity.setGroupId(group.id); + entity.getRotation().set(g.rot); + entity.setState(g.state); + + this.addEntity(entity); + this.getScriptManager().onGadgetCreate(entity); + } + } + } + + public void onUnloadBlock(SceneBlock block) { + List toRemove = this.getEntities().values().stream().filter(e -> e.getBlockId() == block.id).toList(); + + if (toRemove.size() > 0) { + toRemove.stream().forEach(this::removeEntityDirectly); + this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, VisionType.VISION_REMOVE)); + } + + for (SceneGroup group : block.groups) { + group.triggers.forEach(getScriptManager()::deregisterTrigger); + } + } + // Gadgets public void onPlayerCreateGadget(EntityClientGadget gadget) { diff --git a/src/main/java/emu/grasscutter/game/world/World.java b/src/main/java/emu/grasscutter/game/world/World.java index 2a9324e40..215896fea 100644 --- a/src/main/java/emu/grasscutter/game/world/World.java +++ b/src/main/java/emu/grasscutter/game/world/World.java @@ -21,11 +21,12 @@ import emu.grasscutter.data.def.DungeonData; import emu.grasscutter.data.def.SceneData; import emu.grasscutter.game.entity.EntityAvatar; import emu.grasscutter.game.entity.EntityClientGadget; -import emu.grasscutter.game.entity.EntityGadget; +import emu.grasscutter.game.entity.EntityBaseGadget; import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult; import emu.grasscutter.net.proto.EnterTypeOuterClass.EnterType; import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType; +import emu.grasscutter.scripts.data.SceneConfig; import emu.grasscutter.server.packet.send.PacketDelTeamEntityNotify; import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify; @@ -243,16 +244,22 @@ public class World implements Iterable { newScene.addPlayer(player); // Dungeon - if (dungeonData != null) { - // TODO set position + SceneConfig config = newScene.getScriptManager().getConfig(); + if (pos == null && config != null) { + if (config.born_pos != null) { + pos = newScene.getScriptManager().getConfig().born_pos; + } + if (config.born_rot != null) { + player.getRotation().set(config.born_rot); + } } // Set player position - if (pos != null) { - player.getPos().set(pos); - } else { + if (pos == null) { pos = player.getPos(); } + + player.getPos().set(pos); if (oldScene != null) { newScene.setPrevScene(oldScene.getId()); diff --git a/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java b/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java new file mode 100644 index 000000000..c5de4807f --- /dev/null +++ b/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java @@ -0,0 +1,283 @@ +package emu.grasscutter.scripts; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.script.Bindings; +import javax.script.CompiledScript; +import javax.script.ScriptException; + +import org.luaj.vm2.LuaTable; +import org.luaj.vm2.LuaValue; +import org.luaj.vm2.lib.jse.CoerceJavaToLua; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.game.entity.EntityGadget; +import emu.grasscutter.game.entity.EntityMonster; +import emu.grasscutter.game.entity.GameEntity; +import emu.grasscutter.game.world.Scene; +import emu.grasscutter.scripts.constants.ScriptEventType; +import emu.grasscutter.scripts.constants.ScriptGadgetState; +import emu.grasscutter.scripts.constants.ScriptRegionShape; +import emu.grasscutter.scripts.data.SceneBlock; +import emu.grasscutter.scripts.data.SceneConfig; +import emu.grasscutter.scripts.data.SceneGadget; +import emu.grasscutter.scripts.data.SceneGroup; +import emu.grasscutter.scripts.data.SceneInitConfig; +import emu.grasscutter.scripts.data.SceneMonster; +import emu.grasscutter.scripts.data.SceneSuite; +import emu.grasscutter.scripts.data.SceneTrigger; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; + +public class SceneScriptManager { + private final Scene scene; + private final ScriptLib scriptLib; + private final LuaValue scriptLibLua; + private Bindings bindings; + + private SceneConfig config; + private List blocks; + private Int2ObjectOpenHashMap> triggers; + private boolean isInit; + + public SceneScriptManager(Scene scene) { + this.scene = scene; + this.scriptLib = new ScriptLib(this); + this.scriptLibLua = CoerceJavaToLua.coerce(this.scriptLib); + this.triggers = new Int2ObjectOpenHashMap<>(); + + // TEMPORARY + if (this.getScene().getId() < 10) { + return; + } + + // Create + this.init(); + } + + public Scene getScene() { + return scene; + } + + public ScriptLib getScriptLib() { + return scriptLib; + } + + public LuaValue getScriptLibLua() { + return scriptLibLua; + } + + public Bindings getBindings() { + return bindings; + } + + public SceneConfig getConfig() { + return config; + } + + public List getBlocks() { + return blocks; + } + + public Set getTriggersByEvent(int eventId) { + return triggers.computeIfAbsent(eventId, e -> new HashSet<>()); + } + + public void registerTrigger(SceneTrigger trigger) { + getTriggersByEvent(trigger.event).add(trigger); + } + + public void deregisterTrigger(SceneTrigger trigger) { + getTriggersByEvent(trigger.event).remove(trigger); + } + + // TODO optimize + public SceneGroup getGroupById(int groupId) { + for (SceneBlock block : this.getScene().getLoadedBlocks()) { + for (SceneGroup group : block.groups) { + if (group.id == groupId) { + return group; + } + } + } + return null; + } + + private void init() { + // Get compiled script if cached + CompiledScript cs = ScriptLoader.getScriptByPath( + Grasscutter.getConfig().SCRIPTS_FOLDER + "Scene/" + getScene().getId() + "/scene" + getScene().getId() + "." + ScriptLoader.getScriptType()); + + if (cs == null) { + Grasscutter.getLogger().warn("No script found for scene " + getScene().getId()); + return; + } + + // Create bindings + bindings = ScriptLoader.getEngine().createBindings(); + + // Set variables + bindings.put("EventType", new ScriptEventType()); // TODO - make static class to avoid instantiating a new class every scene + bindings.put("GadgetState", new ScriptGadgetState()); + bindings.put("RegionShape", new ScriptRegionShape()); + bindings.put("ScriptLib", getScriptLib()); + + // Eval script + try { + cs.eval(getBindings()); + + this.config = ScriptLoader.getSerializer().toObject(SceneConfig.class, bindings.get("scene_config")); + + // TODO optimize later + // Create blocks + List blockIds = ScriptLoader.getSerializer().toList(Integer.class, bindings.get("blocks")); + List blocks = ScriptLoader.getSerializer().toList(SceneBlock.class, bindings.get("block_rects")); + + for (int i = 0; i < blocks.size(); i++) { + SceneBlock block = blocks.get(0); + block.id = blockIds.get(i); + + loadBlock(block); + } + + this.blocks = blocks; + } catch (ScriptException e) { + Grasscutter.getLogger().error("Error running script", e); + } + // TEMP + this.isInit = true; + } + + public boolean isInit() { + return isInit; + } + + private void loadBlock(SceneBlock block) { + CompiledScript cs = ScriptLoader.getScriptByPath( + Grasscutter.getConfig().SCRIPTS_FOLDER + "Scene/" + getScene().getId() + "/scene" + getScene().getId() + "_block" + block.id + "." + ScriptLoader.getScriptType()); + + if (cs == null) { + return; + } + + // Eval script + try { + cs.eval(getBindings()); + + // Set groups + block.groups = ScriptLoader.getSerializer().toList(SceneGroup.class, bindings.get("groups")); + block.groups.forEach(this::loadGroup); + } catch (ScriptException e) { + Grasscutter.getLogger().error("Error loading block " + block.id + " in scene " + getScene().getId(), e); + } + } + + private void loadGroup(SceneGroup group) { + CompiledScript cs = ScriptLoader.getScriptByPath( + Grasscutter.getConfig().SCRIPTS_FOLDER + "Scene/" + getScene().getId() + "/scene" + getScene().getId() + "_group" + group.id + "." + ScriptLoader.getScriptType()); + + if (cs == null) { + return; + } + + // Eval script + try { + cs.eval(getBindings()); + + // Set + group.monsters = ScriptLoader.getSerializer().toList(SceneMonster.class, bindings.get("monsters")); + group.gadgets = ScriptLoader.getSerializer().toList(SceneGadget.class, bindings.get("gadgets")); + group.triggers = ScriptLoader.getSerializer().toList(SceneTrigger.class, bindings.get("triggers")); + group.suites = ScriptLoader.getSerializer().toList(SceneSuite.class, bindings.get("suites")); + group.init_config = ScriptLoader.getSerializer().toObject(SceneInitConfig.class, bindings.get("init_config")); + } catch (ScriptException e) { + Grasscutter.getLogger().error("Error loading group " + group.id + " in scene " + getScene().getId(), e); + } + } + + public void onTick() { + checkTriggers(); + } + + public void checkTriggers() { + + } + + // Events + + private LuaValue getFunc(String name) { + return (LuaValue) this.getBindings().get(name); + } + + public void onGadgetCreate(EntityGadget gadget) { + LuaTable params = new LuaTable(); + params.set("param1", gadget.getConfigId()); + + for (SceneTrigger trigger : this.getTriggersByEvent(ScriptEventType.EVENT_GADGET_CREATE)) { + LuaValue condition = getFunc(trigger.condition); + + LuaValue ret = condition.call(this.getScriptLibLua(), params); + + if (ret.checkboolean() == true) { + LuaValue action = getFunc(trigger.action); + action.call(this.getScriptLibLua(), LuaValue.NIL); + } + } + } + + public void onOptionSelect(int entityId, int optionId) { + GameEntity entity = this.getScene().getEntityById(entityId); + + if (entity == null || !(entity instanceof EntityGadget)) { + return; + } + + LuaTable params = new LuaTable(); + params.set("param1", entity.getConfigId()); + params.set("param2", optionId); + + for (SceneTrigger trigger : this.getTriggersByEvent(ScriptEventType.EVENT_SELECT_OPTION)) { + LuaValue condition = getFunc(trigger.condition); + + LuaValue ret = condition.call(this.getScriptLibLua(), params); + + if (ret.checkboolean() == true) { + LuaValue action = getFunc(trigger.action); + action.call(this.getScriptLibLua(), LuaValue.NIL); + } + } + } + + public void onMonsterSpawn(EntityMonster entity) { + LuaTable params = new LuaTable(); + params.set("param1", entity.getConfigId()); + + for (SceneTrigger trigger : this.getTriggersByEvent(ScriptEventType.EVENT_ANY_MONSTER_LIVE)) { + LuaValue condition = getFunc(trigger.condition); + + LuaValue ret = condition.call(this.getScriptLibLua(), params); + + if (ret.checkboolean() == true) { + LuaValue action = getFunc(trigger.action); + action.call(this.getScriptLibLua(), LuaValue.NIL); + } + } + } + + public void onChallengeSuccess() { + for (SceneTrigger trigger : this.getTriggersByEvent(ScriptEventType.EVENT_CHALLENGE_SUCCESS)) { + LuaValue action = getFunc(trigger.action); + action.call(this.getScriptLibLua(), LuaValue.NIL); + } + } + + public void onChallengeFailure() { + for (SceneTrigger trigger : this.getTriggersByEvent(ScriptEventType.EVENT_CHALLENGE_FAIL)) { + LuaValue action = getFunc(trigger.action); + action.call(this.getScriptLibLua(), LuaValue.NIL); + } + } +} diff --git a/src/main/java/emu/grasscutter/scripts/ScriptLib.java b/src/main/java/emu/grasscutter/scripts/ScriptLib.java new file mode 100644 index 000000000..e44367e6b --- /dev/null +++ b/src/main/java/emu/grasscutter/scripts/ScriptLib.java @@ -0,0 +1,165 @@ +package emu.grasscutter.scripts; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.luaj.vm2.LuaTable; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.def.MonsterData; +import emu.grasscutter.game.dungeons.DungeonChallenge; +import emu.grasscutter.game.entity.EntityGadget; +import emu.grasscutter.game.entity.EntityMonster; +import emu.grasscutter.game.entity.GameEntity; +import emu.grasscutter.scripts.data.SceneGroup; +import emu.grasscutter.scripts.data.SceneMonster; +import emu.grasscutter.server.packet.send.PacketGadgetStateNotify; +import emu.grasscutter.server.packet.send.PacketWorktopOptionNotify; + +public class ScriptLib { + private final SceneScriptManager sceneScriptManager; + + public ScriptLib(SceneScriptManager sceneScriptManager) { + this.sceneScriptManager = sceneScriptManager; + } + + public SceneScriptManager getSceneScriptManager() { + return sceneScriptManager; + } + + public int SetGadgetStateByConfigId(int configId, int gadgetState) { + Optional entity = getSceneScriptManager().getScene().getEntities().values().stream() + .filter(e -> e.getConfigId() == configId).findFirst(); + + if (entity.isEmpty()) { + return 1; + } + + if (!(entity.get() instanceof EntityGadget)) { + return 1; + } + + EntityGadget gadget = (EntityGadget) entity.get(); + gadget.setState(gadgetState); + + getSceneScriptManager().getScene().broadcastPacket(new PacketGadgetStateNotify(gadget, gadgetState)); + return 0; + } + + public int SetGroupGadgetStateByConfigId(int groupId, int configId, int gadgetState) { + List list = getSceneScriptManager().getScene().getEntities().values().stream() + .filter(e -> e.getGroupId() == groupId).toList(); + + for (GameEntity entity : list) { + EntityGadget gadget = (EntityGadget) entity; + gadget.setState(gadgetState); + + getSceneScriptManager().getScene().broadcastPacket(new PacketGadgetStateNotify(gadget, gadgetState)); + } + + return 0; + } + + public int SetWorktopOptionsByGroupId(int groupId, int configId, int[] options) { + Optional entity = getSceneScriptManager().getScene().getEntities().values().stream() + .filter(e -> e.getConfigId() == configId && e.getGroupId() == groupId).findFirst(); + + if (entity.isEmpty()) { + return 1; + } + + if (!(entity.get() instanceof EntityGadget)) { + return 1; + } + + EntityGadget gadget = (EntityGadget) entity.get(); + gadget.addWorktopOptions(options); + + getSceneScriptManager().getScene().broadcastPacket(new PacketWorktopOptionNotify(gadget)); + return 0; + } + + public int DelWorktopOptionByGroupId(int groupId, int configId, int option) { + Optional entity = getSceneScriptManager().getScene().getEntities().values().stream() + .filter(e -> e.getConfigId() == configId && e.getGroupId() == groupId).findFirst(); + + if (entity.isEmpty()) { + return 1; + } + + if (!(entity.get() instanceof EntityGadget)) { + return 1; + } + + EntityGadget gadget = (EntityGadget) entity.get(); + gadget.removeWorktopOption(option); + + getSceneScriptManager().getScene().broadcastPacket(new PacketWorktopOptionNotify(gadget)); + return 0; + } + + // Some fields are guessed + public int AutoMonsterTide(int challengeIndex, int groupId, int[] config_ids, int param4, int param5, int param6) { + SceneGroup group = getSceneScriptManager().getGroupById(groupId); + + if (group == null || group.monsters == null) { + return 1; + } + + // TODO just spawn all from group for now + List toAdd = new ArrayList<>(); + + for (SceneMonster monster : group.monsters) { + MonsterData data = GameData.getMonsterDataMap().get(monster.monster_id); + + if (data == null) { + continue; + } + + EntityMonster entity = new EntityMonster(getSceneScriptManager().getScene(), data, monster.pos, monster.level); + entity.getRotation().set(monster.rot); + entity.setGroupId(group.id); + entity.setConfigId(monster.config_id); + + toAdd.add(entity); + } + + if (toAdd.size() > 0) { + getSceneScriptManager().getScene().addEntities(toAdd); + + for (GameEntity entity : toAdd) { + this.getSceneScriptManager().onMonsterSpawn((EntityMonster) entity); + } + } + + return 0; + } + + public int ActiveChallenge(int challengeId, int challengeIndex, int param3, int groupId, int param4, int param5) { + SceneGroup group = getSceneScriptManager().getGroupById(groupId); + + if (group == null || group.monsters == null) { + return 1; + } + + DungeonChallenge challenge = new DungeonChallenge(getSceneScriptManager().getScene(), group); + challenge.setChallengeId(challengeId); + challenge.setChallengeIndex(challengeIndex); + + getSceneScriptManager().getScene().setChallenge(challenge); + + challenge.start(); + return 0; + } + + public int RefreshGroup(LuaTable table) { + // Add group back to suite + return 0; + } + + public void PrintContextLog(String msg) { + Grasscutter.getLogger().info("[LUA] " + msg); + } +} diff --git a/src/main/java/emu/grasscutter/scripts/ScriptLoader.java b/src/main/java/emu/grasscutter/scripts/ScriptLoader.java new file mode 100644 index 000000000..c4157c690 --- /dev/null +++ b/src/main/java/emu/grasscutter/scripts/ScriptLoader.java @@ -0,0 +1,74 @@ +package emu.grasscutter.scripts; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import javax.script.Compilable; +import javax.script.CompiledScript; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineFactory; +import javax.script.ScriptEngineManager; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.scripts.serializer.LuaSerializer; +import emu.grasscutter.scripts.serializer.Serializer; + +public class ScriptLoader { + private static ScriptEngineManager sm; + private static ScriptEngine engine; + private static ScriptEngineFactory factory; + private static String fileType; + private static Serializer serializer; + + private static Map scripts = new HashMap<>(); + + public synchronized static void init() throws Exception { + if (sm != null) { + throw new Exception("Script loader already initialized"); + } + + sm = new ScriptEngineManager(); + engine = sm.getEngineByName("luaj"); + factory = getEngine().getFactory(); + fileType = "lua"; + serializer = new LuaSerializer(); + } + + public static ScriptEngine getEngine() { + return engine; + } + + public static String getScriptType() { + return fileType; + } + + public static Serializer getSerializer() { + return serializer; + } + + public static CompiledScript getScriptByPath(String path) { + CompiledScript sc = scripts.get(path); + + Grasscutter.getLogger().info("Loaded " + path); + + if (sc == null) { + File file = new File(path); + + if (!file.exists()) return null; + + try (FileReader fr = new FileReader(file)) { + sc = ((Compilable) getEngine()).compile(fr); + scripts.put(path, sc); + } catch (Exception e) { + //e.printStackTrace(); + return null; + } + } + + return sc; + } +} diff --git a/src/main/java/emu/grasscutter/scripts/constants/ScriptEventType.java b/src/main/java/emu/grasscutter/scripts/constants/ScriptEventType.java new file mode 100644 index 000000000..1ec2b4bf0 --- /dev/null +++ b/src/main/java/emu/grasscutter/scripts/constants/ScriptEventType.java @@ -0,0 +1,82 @@ +package emu.grasscutter.scripts.constants; + +public class ScriptEventType { + public static final int EVENT_NONE = 0; + public static final int EVENT_ANY_MONSTER_DIE = 1; + public static final int EVENT_ANY_GADGET_DIE = 2; + public static final int EVENT_VARIABLE_CHANGE = 3; + public static final int EVENT_ENTER_REGION = 4; + public static final int EVENT_LEAVE_REGION = 5; + public static final int EVENT_GADGET_CREATE = 6; + public static final int EVENT_GADGET_STATE_CHANGE = 7; + public static final int EVENT_DUNGEON_SETTLE = 8; + public static final int EVENT_SELECT_OPTION = 9; + public static final int EVENT_CLIENT_EXECUTE = 10; + public static final int EVENT_ANY_MONSTER_LIVE = 11; + public static final int EVENT_SPECIFIC_MONSTER_HP_CHANGE = 12; + public static final int EVENT_CITY_LEVELUP_UNLOCK_DUNGEON_ENTRY = 13; + public static final int EVENT_DUNGEON_BROADCAST_ONTIMER = 14; + public static final int EVENT_TIMER_EVENT = 15; + public static final int EVENT_CHALLENGE_SUCCESS = 16; + public static final int EVENT_CHALLENGE_FAIL = 17; + public static final int EVENT_SEAL_BATTLE_BEGIN = 18; + public static final int EVENT_SEAL_BATTLE_END = 19; + public static final int EVENT_GATHER = 20; + public static final int EVENT_QUEST_FINISH = 21; + public static final int EVENT_MONSTER_BATTLE = 22; + public static final int EVENT_CITY_LEVELUP = 23; + public static final int EVENT_CUTSCENE_END = 24; + public static final int EVENT_AVATAR_NEAR_PLATFORM = 25; + public static final int EVENT_PLATFORM_REACH_POINT = 26; + public static final int EVENT_UNLOCK_TRANS_POINT = 27; + public static final int EVENT_QUEST_START = 28; + public static final int EVENT_GROUP_LOAD = 29; + public static final int EVENT_GROUP_WILL_UNLOAD = 30; + public static final int EVENT_GROUP_WILL_REFRESH = 31; + public static final int EVENT_GROUP_REFRESH = 32; + public static final int EVENT_DUNGEON_REWARD_GET = 33; + public static final int EVENT_SPECIFIC_GADGET_HP_CHANGE = 34; + public static final int EVENT_MONSTER_TIDE_OVER = 35; + public static final int EVENT_MONSTER_TIDE_CREATE = 36; + public static final int EVENT_MONSTER_TIDE_DIE = 37; + public static final int EVENT_SEALAMP_PHASE_CHANGE = 38; + public static final int EVENT_BLOSSOM_PROGRESS_FINISH = 39; + public static final int EVENT_BLOSSOM_CHEST_DIE = 40; + public static final int EVENT_GADGET_PLAY_START = 41; + public static final int EVENT_GADGET_PLAY_START_CD = 42; + public static final int EVENT_GADGET_PLAY_STOP = 43; + public static final int EVENT_GADGET_LUA_NOTIFY = 44; + public static final int EVENT_MP_PLAY_PREPARE = 45; + public static final int EVENT_MP_PLAY_BATTLE = 46; + public static final int EVENT_MP_PLAY_PREPARE_INTERRUPT = 47; + public static final int EVENT_SELECT_DIFFICULTY = 48; + public static final int EVENT_SCENE_MP_PLAY_BATTLE_STATE = 49; + public static final int EVENT_SCENE_MP_PLAY_BATTLE_STAGE_CHANGE = 50; + public static final int EVENT_SCENE_MP_PLAY_BATTLE_RESULT = 51; + public static final int EVENT_SEAL_BATTLE_PROGRESS_DECREASE = 52; + public static final int EVENT_GENERAL_REWARD_DIE = 53; + public static final int EVENT_SCENE_MP_PLAY_BATTLE_INTERRUPT = 54; + public static final int EVENT_MONSTER_DIE_BEFORE_LEAVE_SCENE = 55; + public static final int EVENT_SCENE_MP_PLAY_OPEN = 56; + public static final int EVENT_OFFERING_LEVELUP = 57; + public static final int EVENT_DUNGEON_REVIVE = 58; + public static final int EVENT_SCENE_MP_PLAY_ALL_AVATAR_DIE = 59; + public static final int EVENT_DUNGEON_ALL_AVATAR_DIE = 60; + public static final int EVENT_GENERAL_REWARD_TAKEN = 61; + public static final int EVENT_PLATFORM_REACH_ARRAYPOINT = 62; + public static final int EVENT_SCENE_MULTISTAGE_PLAY_STAGE_END = 63; + public static final int EVENT_SCENE_MULTISTAGE_PLAY_END_STAGE_REQ = 64; + public static final int EVENT_MECHANICUS_PICKED_CARD = 65; + public static final int EVENT_POOL_MONSTER_TIDE_OVER = 66; + public static final int EVENT_POOL_MONSTER_TIDE_CREATE = 67; + public static final int EVENT_POOL_MONSTER_TIDE_DIE = 68; + public static final int EVENT_DUNGEON_AVATAR_SLIP_DIE = 69; + public static final int EVENT_GALLERY_START = 70; + public static final int EVENT_GALLERY_STOP = 71; + public static final int EVENT_TIME_AXIS_PASS = 72; + public static final int EVENT_FLEUR_FAIR_DUNGEON_ALL_PLAYER_ENTER = 73; + public static final int EVENT_GADGETTALK_DONE = 74; + public static final int EVENT_SET_GAME_TIME = 75; + public static final int EVENT_HIDE_AND_SEEK_PLAYER_QUIT = 76; + public static final int EVENT_AVATAR_DIE = 77; +} diff --git a/src/main/java/emu/grasscutter/scripts/constants/ScriptGadgetState.java b/src/main/java/emu/grasscutter/scripts/constants/ScriptGadgetState.java new file mode 100644 index 000000000..a36340ab6 --- /dev/null +++ b/src/main/java/emu/grasscutter/scripts/constants/ScriptGadgetState.java @@ -0,0 +1,24 @@ +package emu.grasscutter.scripts.constants; + +public class ScriptGadgetState { + public static final int Default = 0; + public static final int GatherDrop = 1; + public static final int ChestLocked = 101; + public static final int ChestOpened = 102; + public static final int ChestTrap = 103; + public static final int ChestBramble = 104; + public static final int ChestFrozen = 105; + public static final int ChestRock = 106; + public static final int GearStart = 201; + public static final int GearStop = 202; + public static final int GearAction1 = 203; + public static final int GearAction2 = 204; + public static final int CrystalResonate1 = 301; + public static final int CrystalResonate2 = 302; + public static final int CrystalExplode = 303; + public static final int CrystalDrain = 304; + public static final int StatueActive = 401; + public static final int Action01 = 901; + public static final int Action02 = 902; + public static final int Action03 = 903; +} diff --git a/src/main/java/emu/grasscutter/scripts/constants/ScriptRegionShape.java b/src/main/java/emu/grasscutter/scripts/constants/ScriptRegionShape.java new file mode 100644 index 000000000..abb19387f --- /dev/null +++ b/src/main/java/emu/grasscutter/scripts/constants/ScriptRegionShape.java @@ -0,0 +1,7 @@ +package emu.grasscutter.scripts.constants; + +public class ScriptRegionShape { + public static final int NONE = 0; + public static final int SPHERE = 1; + public static final int CUBIC = 2; +} diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneBlock.java b/src/main/java/emu/grasscutter/scripts/data/SceneBlock.java new file mode 100644 index 000000000..11a930fdd --- /dev/null +++ b/src/main/java/emu/grasscutter/scripts/data/SceneBlock.java @@ -0,0 +1,17 @@ +package emu.grasscutter.scripts.data; + +import java.util.List; + +import emu.grasscutter.utils.Position; + +public class SceneBlock { + public int id; + public Position max; + public Position min; + public List groups; + + public boolean contains(Position pos) { + return pos.getX() <= max.getX() && pos.getX() >= min.getX() && + pos.getZ() <= max.getZ() && pos.getZ() >= min.getZ(); + } +} diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneConfig.java b/src/main/java/emu/grasscutter/scripts/data/SceneConfig.java new file mode 100644 index 000000000..3a1ca60ea --- /dev/null +++ b/src/main/java/emu/grasscutter/scripts/data/SceneConfig.java @@ -0,0 +1,11 @@ +package emu.grasscutter.scripts.data; + +import emu.grasscutter.utils.Position; + +public class SceneConfig { + public Position vision_anchor; + public Position born_pos; + public Position born_rot; + public Position begin_pos; + public Position size; +} diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneGadget.java b/src/main/java/emu/grasscutter/scripts/data/SceneGadget.java new file mode 100644 index 000000000..e5340ae55 --- /dev/null +++ b/src/main/java/emu/grasscutter/scripts/data/SceneGadget.java @@ -0,0 +1,12 @@ +package emu.grasscutter.scripts.data; + +import emu.grasscutter.utils.Position; + +public class SceneGadget { + public int level; + public int config_id; + public int gadget_id; + public int state; + public Position pos; + public Position rot; +} diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneGroup.java b/src/main/java/emu/grasscutter/scripts/data/SceneGroup.java new file mode 100644 index 000000000..e227b1853 --- /dev/null +++ b/src/main/java/emu/grasscutter/scripts/data/SceneGroup.java @@ -0,0 +1,17 @@ +package emu.grasscutter.scripts.data; + +import java.util.List; + +import emu.grasscutter.utils.Position; + +public class SceneGroup { + public int id; + public int refresh_id; + public Position pos; + + public List monsters; + public List gadgets; + public List triggers; + public List suites; + public SceneInitConfig init_config; +} diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneInitConfig.java b/src/main/java/emu/grasscutter/scripts/data/SceneInitConfig.java new file mode 100644 index 000000000..fcdc92c87 --- /dev/null +++ b/src/main/java/emu/grasscutter/scripts/data/SceneInitConfig.java @@ -0,0 +1,9 @@ +package emu.grasscutter.scripts.data; + +import emu.grasscutter.utils.Position; + +public class SceneInitConfig { + public int suite; + public int end_suite; + public int rand_suite; +} diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneMonster.java b/src/main/java/emu/grasscutter/scripts/data/SceneMonster.java new file mode 100644 index 000000000..56d2e3d3d --- /dev/null +++ b/src/main/java/emu/grasscutter/scripts/data/SceneMonster.java @@ -0,0 +1,11 @@ +package emu.grasscutter.scripts.data; + +import emu.grasscutter.utils.Position; + +public class SceneMonster { + public int level; + public int config_id; + public int monster_id; + public Position pos; + public Position rot; +} diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneSuite.java b/src/main/java/emu/grasscutter/scripts/data/SceneSuite.java new file mode 100644 index 000000000..be9bc0f08 --- /dev/null +++ b/src/main/java/emu/grasscutter/scripts/data/SceneSuite.java @@ -0,0 +1,10 @@ +package emu.grasscutter.scripts.data; + +import java.util.List; + +import emu.grasscutter.utils.Position; + +public class SceneSuite { + public List triggers; + public int rand_weight; +} diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneTrigger.java b/src/main/java/emu/grasscutter/scripts/data/SceneTrigger.java new file mode 100644 index 000000000..a1603b1e6 --- /dev/null +++ b/src/main/java/emu/grasscutter/scripts/data/SceneTrigger.java @@ -0,0 +1,10 @@ +package emu.grasscutter.scripts.data; + +public class SceneTrigger { + public String name; + public int config_id; + public int event; + public String source; + public String condition; + public String action; +} diff --git a/src/main/java/emu/grasscutter/scripts/serializer/LuaSerializer.java b/src/main/java/emu/grasscutter/scripts/serializer/LuaSerializer.java new file mode 100644 index 000000000..a63328b55 --- /dev/null +++ b/src/main/java/emu/grasscutter/scripts/serializer/LuaSerializer.java @@ -0,0 +1,108 @@ +package emu.grasscutter.scripts.serializer; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +import org.luaj.vm2.LuaTable; +import org.luaj.vm2.LuaValue; + +public class LuaSerializer implements Serializer { + + @Override + public List toList(Class type, Object obj) { + return serializeList(type, (LuaTable) obj); + } + + @Override + public T toObject(Class type, Object obj) { + return serialize(type, (LuaTable) obj); + } + + public List serializeList(Class type, LuaTable table) { + List list = new ArrayList(); + + try { + LuaValue[] keys = table.keys(); + for (LuaValue k : keys) { + try { + LuaValue keyValue = table.get(k); + + T object = null; + + if (keyValue.istable()) { + object = serialize(type, keyValue.checktable()); + } else if (keyValue.isint()) { + object = (T) (Integer) keyValue.toint(); + } else if (keyValue.isnumber()) { + object = (T) (Float) keyValue.tofloat(); // terrible... + } else if (keyValue.isstring()) { + object = (T) keyValue.tojstring(); + } else { + object = (T) keyValue; + } + + if (object != null) { + list.add(object); + } + } catch (Exception ex) { + + } + } + } catch (Exception e) { + e.printStackTrace(); + } + + return list; + } + + public T serialize(Class type, LuaTable table) { + T object = null; + + if (type == List.class) { + try { + Class listType = (Class) type.getTypeParameters()[0].getClass(); + return (T) serializeList(listType, table); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + try { + object = type.getDeclaredConstructor().newInstance(null); + + LuaValue[] keys = table.keys(); + for (LuaValue k : keys) { + try { + Field field = object.getClass().getDeclaredField(k.checkjstring()); + if (field == null) { + continue; + } + + field.setAccessible(true); + LuaValue keyValue = table.get(k); + + if (keyValue.istable()) { + field.set(object, serialize(field.getType(), keyValue.checktable())); + } else if (field.getType().equals(float.class)) { + field.setFloat(object, keyValue.tofloat()); + } else if (field.getType().equals(int.class)) { + field.setInt(object, keyValue.toint()); + } else if (field.getType().equals(String.class)) { + field.set(object, keyValue.tojstring()); + } else { + field.set(object, keyValue); + } + } catch (Exception ex) { + //ex.printStackTrace(); + continue; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + + return object; + } +} diff --git a/src/main/java/emu/grasscutter/scripts/serializer/Serializer.java b/src/main/java/emu/grasscutter/scripts/serializer/Serializer.java new file mode 100644 index 000000000..64bd266d2 --- /dev/null +++ b/src/main/java/emu/grasscutter/scripts/serializer/Serializer.java @@ -0,0 +1,12 @@ +package emu.grasscutter.scripts.serializer; + +import java.util.List; + +import org.luaj.vm2.LuaTable; + +public interface Serializer { + + public List toList(Class type, Object obj); + + public T toObject(Class type, Object obj); +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSelectWorktopOptionReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSelectWorktopOptionReq.java new file mode 100644 index 000000000..23f3af4cc --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSelectWorktopOptionReq.java @@ -0,0 +1,22 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.SelectWorktopOptionReqOuterClass.SelectWorktopOptionReq; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketSelectWorktopOptionRsp; + +@Opcodes(PacketOpcodes.SelectWorktopOptionReq) +public class HandlerSelectWorktopOptionReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + SelectWorktopOptionReq req = SelectWorktopOptionReq.parseFrom(payload); + + session.getPlayer().getScene().getScriptManager().onOptionSelect(req.getGadgetEntityId(), req.getOptionId()); + + session.send(new PacketSelectWorktopOptionRsp(req.getGadgetEntityId(), req.getOptionId())); + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonChallengeBeginNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonChallengeBeginNotify.java new file mode 100644 index 000000000..2dd734cf5 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonChallengeBeginNotify.java @@ -0,0 +1,20 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.game.dungeons.DungeonChallenge; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.DungeonChallengeBeginNotifyOuterClass.DungeonChallengeBeginNotify; + +public class PacketDungeonChallengeBeginNotify extends BasePacket { + + public PacketDungeonChallengeBeginNotify(DungeonChallenge challenge) { + super(PacketOpcodes.DungeonChallengeBeginNotify); + + DungeonChallengeBeginNotify proto = DungeonChallengeBeginNotify.newBuilder() + .setChallengeId(challenge.getChallengeId()) + .setChallengeIndex(challenge.getChallengeIndex()) + .build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonChallengeFinishNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonChallengeFinishNotify.java new file mode 100644 index 000000000..323528aae --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonChallengeFinishNotify.java @@ -0,0 +1,20 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.game.dungeons.DungeonChallenge; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.DungeonChallengeFinishNotifyOuterClass.DungeonChallengeFinishNotify; + +public class PacketDungeonChallengeFinishNotify extends BasePacket { + + public PacketDungeonChallengeFinishNotify(DungeonChallenge challenge) { + super(PacketOpcodes.DungeonChallengeFinishNotify); + + DungeonChallengeFinishNotify proto = DungeonChallengeFinishNotify.newBuilder() + .setChallengeIndex(challenge.getChallengeId()) + .setIsSuccess(challenge.isSuccess()) + .build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketGadgetInteractRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketGadgetInteractRsp.java index d15cca8a8..c5e5d723e 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketGadgetInteractRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketGadgetInteractRsp.java @@ -1,6 +1,6 @@ package emu.grasscutter.server.packet.send; -import emu.grasscutter.game.entity.EntityGadget; +import emu.grasscutter.game.entity.EntityBaseGadget; import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.GadgetInteractRspOuterClass.GadgetInteractRsp; @@ -8,7 +8,7 @@ import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType; import emu.grasscutter.net.proto.RetcodeOuterClass; public class PacketGadgetInteractRsp extends BasePacket { - public PacketGadgetInteractRsp(EntityGadget gadget, InteractType interact) { + public PacketGadgetInteractRsp(EntityBaseGadget gadget, InteractType interact) { super(PacketOpcodes.GadgetInteractRsp); GadgetInteractRsp proto = GadgetInteractRsp.newBuilder() diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketGadgetStateNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketGadgetStateNotify.java new file mode 100644 index 000000000..e2af45633 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketGadgetStateNotify.java @@ -0,0 +1,21 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.game.entity.EntityGadget; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.GadgetStateNotifyOuterClass.GadgetStateNotify; + +public class PacketGadgetStateNotify extends BasePacket { + + public PacketGadgetStateNotify(EntityGadget gadget, int newState) { + super(PacketOpcodes.GadgetStateNotify); + + GadgetStateNotify proto = GadgetStateNotify.newBuilder() + .setGadgetEntityId(gadget.getId()) + .setGadgetState(newState) + .setIsEnableInteract(true) + .build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketSelectWorktopOptionRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketSelectWorktopOptionRsp.java new file mode 100644 index 000000000..72d77e583 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketSelectWorktopOptionRsp.java @@ -0,0 +1,19 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.SelectWorktopOptionRspOuterClass.SelectWorktopOptionRsp; + +public class PacketSelectWorktopOptionRsp extends BasePacket { + + public PacketSelectWorktopOptionRsp(int entityId, int optionId) { + super(PacketOpcodes.SelectWorktopOptionRsp); + + SelectWorktopOptionRsp proto = SelectWorktopOptionRsp.newBuilder() + .setGadgetEntityId(entityId) + .setOptionId(optionId) + .build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketWorktopOptionNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketWorktopOptionNotify.java new file mode 100644 index 000000000..14648a618 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketWorktopOptionNotify.java @@ -0,0 +1,22 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.game.entity.EntityGadget; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.WorktopOptionNotifyOuterClass.WorktopOptionNotify; + +public class PacketWorktopOptionNotify extends BasePacket { + + public PacketWorktopOptionNotify(EntityGadget gadget) { + super(PacketOpcodes.WorktopOptionNotify); + + WorktopOptionNotify.Builder proto = WorktopOptionNotify.newBuilder() + .setGadgetEntityId(gadget.getId()); + + if (gadget.getWorktopOptions() != null) { + proto.addAllOptionList(gadget.getWorktopOptions()); + } + + this.setData(proto); + } +} From 30886da0ad6770572244cbedc4e95dd4f0c0aaea Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Thu, 28 Apr 2022 23:09:07 -0700 Subject: [PATCH 03/14] Fix compile error caused by merge --- src/main/java/emu/grasscutter/game/entity/EntityVehicle.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/game/entity/EntityVehicle.java b/src/main/java/emu/grasscutter/game/entity/EntityVehicle.java index 06e5ee14a..c7609f89b 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityVehicle.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityVehicle.java @@ -25,7 +25,7 @@ import emu.grasscutter.utils.ProtoHelper; import it.unimi.dsi.fastutil.ints.Int2FloatMap; import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap; -public class EntityVehicle extends EntityGadget { +public class EntityVehicle extends EntityBaseGadget { private final Player owner; private final Int2FloatOpenHashMap fightProp; From 8ab2b446cd8c163ee32d25bec27463cf08c345e9 Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Fri, 29 Apr 2022 00:00:23 -0700 Subject: [PATCH 04/14] Clean script events --- .../game/dungeons/DungeonChallenge.java | 5 +- .../emu/grasscutter/game/world/Scene.java | 4 +- .../scripts/SceneScriptManager.java | 91 +++++-------------- .../emu/grasscutter/scripts/ScriptLib.java | 4 +- .../{ScriptEventType.java => EventType.java} | 2 +- .../grasscutter/scripts/data/ScriptArgs.java | 47 ++++++++++ .../recv/HandlerSelectWorktopOptionReq.java | 22 ++++- 7 files changed, 99 insertions(+), 76 deletions(-) rename src/main/java/emu/grasscutter/scripts/constants/{ScriptEventType.java => EventType.java} (99%) create mode 100644 src/main/java/emu/grasscutter/scripts/data/ScriptArgs.java diff --git a/src/main/java/emu/grasscutter/game/dungeons/DungeonChallenge.java b/src/main/java/emu/grasscutter/game/dungeons/DungeonChallenge.java index 66d02221a..1b595723a 100644 --- a/src/main/java/emu/grasscutter/game/dungeons/DungeonChallenge.java +++ b/src/main/java/emu/grasscutter/game/dungeons/DungeonChallenge.java @@ -9,6 +9,7 @@ import emu.grasscutter.game.entity.EntityMonster; import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.game.world.Scene; import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType; +import emu.grasscutter.scripts.constants.EventType; import emu.grasscutter.scripts.data.SceneGroup; import emu.grasscutter.scripts.data.SceneMonster; import emu.grasscutter.server.packet.send.PacketDungeonChallengeBeginNotify; @@ -73,9 +74,9 @@ public class DungeonChallenge { getScene().broadcastPacket(new PacketDungeonChallengeFinishNotify(this)); if (this.isSuccess()) { - this.getScene().getScriptManager().onChallengeSuccess(); + this.getScene().getScriptManager().callEvent(EventType.EVENT_CHALLENGE_SUCCESS, null); } else { - this.getScene().getScriptManager().onChallengeFailure(); + this.getScene().getScriptManager().callEvent(EventType.EVENT_CHALLENGE_FAIL, null); } } diff --git a/src/main/java/emu/grasscutter/game/world/Scene.java b/src/main/java/emu/grasscutter/game/world/Scene.java index 44c5be1d2..93bfab9db 100644 --- a/src/main/java/emu/grasscutter/game/world/Scene.java +++ b/src/main/java/emu/grasscutter/game/world/Scene.java @@ -20,9 +20,11 @@ import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult; import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType; import emu.grasscutter.scripts.SceneScriptManager; +import emu.grasscutter.scripts.constants.EventType; import emu.grasscutter.scripts.data.SceneBlock; import emu.grasscutter.scripts.data.SceneGadget; import emu.grasscutter.scripts.data.SceneGroup; +import emu.grasscutter.scripts.data.ScriptArgs; import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify; import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify; @@ -505,7 +507,7 @@ public class Scene { entity.setState(g.state); this.addEntity(entity); - this.getScriptManager().onGadgetCreate(entity); + this.getScriptManager().callEvent(EventType.EVENT_GADGET_CREATE, new ScriptArgs(entity.getConfigId())); } } } diff --git a/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java b/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java index c5de4807f..09104f63d 100644 --- a/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java +++ b/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java @@ -18,7 +18,7 @@ import emu.grasscutter.game.entity.EntityGadget; import emu.grasscutter.game.entity.EntityMonster; import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.game.world.Scene; -import emu.grasscutter.scripts.constants.ScriptEventType; +import emu.grasscutter.scripts.constants.EventType; import emu.grasscutter.scripts.constants.ScriptGadgetState; import emu.grasscutter.scripts.constants.ScriptRegionShape; import emu.grasscutter.scripts.data.SceneBlock; @@ -29,6 +29,7 @@ import emu.grasscutter.scripts.data.SceneInitConfig; import emu.grasscutter.scripts.data.SceneMonster; import emu.grasscutter.scripts.data.SceneSuite; import emu.grasscutter.scripts.data.SceneTrigger; +import emu.grasscutter.scripts.data.ScriptArgs; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; @@ -120,7 +121,7 @@ public class SceneScriptManager { bindings = ScriptLoader.getEngine().createBindings(); // Set variables - bindings.put("EventType", new ScriptEventType()); // TODO - make static class to avoid instantiating a new class every scene + bindings.put("EventType", new EventType()); // TODO - make static class to avoid instantiating a new class every scene bindings.put("GadgetState", new ScriptGadgetState()); bindings.put("RegionShape", new ScriptRegionShape()); bindings.put("ScriptLib", getScriptLib()); @@ -208,76 +209,30 @@ public class SceneScriptManager { // Events - private LuaValue getFunc(String name) { - return (LuaValue) this.getBindings().get(name); - } - - public void onGadgetCreate(EntityGadget gadget) { - LuaTable params = new LuaTable(); - params.set("param1", gadget.getConfigId()); - - for (SceneTrigger trigger : this.getTriggersByEvent(ScriptEventType.EVENT_GADGET_CREATE)) { - LuaValue condition = getFunc(trigger.condition); - - LuaValue ret = condition.call(this.getScriptLibLua(), params); + public void callEvent(int eventType, ScriptArgs params) { + for (SceneTrigger trigger : this.getTriggersByEvent(eventType)) { + LuaValue condition = null; + + if (trigger.condition != null && !trigger.condition.isEmpty()) { + condition = (LuaValue) this.getBindings().get(trigger.condition); + } + + LuaValue ret = LuaValue.TRUE; + + if (condition != null) { + LuaValue args = LuaValue.NIL; + + if (params != null) { + args = CoerceJavaToLua.coerce(params); + } + + ret = condition.call(this.getScriptLibLua(), args); + } if (ret.checkboolean() == true) { - LuaValue action = getFunc(trigger.action); + LuaValue action = (LuaValue) this.getBindings().get(trigger.action); action.call(this.getScriptLibLua(), LuaValue.NIL); } } } - - public void onOptionSelect(int entityId, int optionId) { - GameEntity entity = this.getScene().getEntityById(entityId); - - if (entity == null || !(entity instanceof EntityGadget)) { - return; - } - - LuaTable params = new LuaTable(); - params.set("param1", entity.getConfigId()); - params.set("param2", optionId); - - for (SceneTrigger trigger : this.getTriggersByEvent(ScriptEventType.EVENT_SELECT_OPTION)) { - LuaValue condition = getFunc(trigger.condition); - - LuaValue ret = condition.call(this.getScriptLibLua(), params); - - if (ret.checkboolean() == true) { - LuaValue action = getFunc(trigger.action); - action.call(this.getScriptLibLua(), LuaValue.NIL); - } - } - } - - public void onMonsterSpawn(EntityMonster entity) { - LuaTable params = new LuaTable(); - params.set("param1", entity.getConfigId()); - - for (SceneTrigger trigger : this.getTriggersByEvent(ScriptEventType.EVENT_ANY_MONSTER_LIVE)) { - LuaValue condition = getFunc(trigger.condition); - - LuaValue ret = condition.call(this.getScriptLibLua(), params); - - if (ret.checkboolean() == true) { - LuaValue action = getFunc(trigger.action); - action.call(this.getScriptLibLua(), LuaValue.NIL); - } - } - } - - public void onChallengeSuccess() { - for (SceneTrigger trigger : this.getTriggersByEvent(ScriptEventType.EVENT_CHALLENGE_SUCCESS)) { - LuaValue action = getFunc(trigger.action); - action.call(this.getScriptLibLua(), LuaValue.NIL); - } - } - - public void onChallengeFailure() { - for (SceneTrigger trigger : this.getTriggersByEvent(ScriptEventType.EVENT_CHALLENGE_FAIL)) { - LuaValue action = getFunc(trigger.action); - action.call(this.getScriptLibLua(), LuaValue.NIL); - } - } } diff --git a/src/main/java/emu/grasscutter/scripts/ScriptLib.java b/src/main/java/emu/grasscutter/scripts/ScriptLib.java index e44367e6b..87881db51 100644 --- a/src/main/java/emu/grasscutter/scripts/ScriptLib.java +++ b/src/main/java/emu/grasscutter/scripts/ScriptLib.java @@ -13,8 +13,10 @@ import emu.grasscutter.game.dungeons.DungeonChallenge; import emu.grasscutter.game.entity.EntityGadget; import emu.grasscutter.game.entity.EntityMonster; import emu.grasscutter.game.entity.GameEntity; +import emu.grasscutter.scripts.constants.EventType; import emu.grasscutter.scripts.data.SceneGroup; import emu.grasscutter.scripts.data.SceneMonster; +import emu.grasscutter.scripts.data.ScriptArgs; import emu.grasscutter.server.packet.send.PacketGadgetStateNotify; import emu.grasscutter.server.packet.send.PacketWorktopOptionNotify; @@ -130,7 +132,7 @@ public class ScriptLib { getSceneScriptManager().getScene().addEntities(toAdd); for (GameEntity entity : toAdd) { - this.getSceneScriptManager().onMonsterSpawn((EntityMonster) entity); + this.getSceneScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_LIVE, new ScriptArgs(entity.getConfigId())); } } diff --git a/src/main/java/emu/grasscutter/scripts/constants/ScriptEventType.java b/src/main/java/emu/grasscutter/scripts/constants/EventType.java similarity index 99% rename from src/main/java/emu/grasscutter/scripts/constants/ScriptEventType.java rename to src/main/java/emu/grasscutter/scripts/constants/EventType.java index 1ec2b4bf0..58d8dc3ab 100644 --- a/src/main/java/emu/grasscutter/scripts/constants/ScriptEventType.java +++ b/src/main/java/emu/grasscutter/scripts/constants/EventType.java @@ -1,6 +1,6 @@ package emu.grasscutter.scripts.constants; -public class ScriptEventType { +public class EventType { public static final int EVENT_NONE = 0; public static final int EVENT_ANY_MONSTER_DIE = 1; public static final int EVENT_ANY_GADGET_DIE = 2; diff --git a/src/main/java/emu/grasscutter/scripts/data/ScriptArgs.java b/src/main/java/emu/grasscutter/scripts/data/ScriptArgs.java new file mode 100644 index 000000000..3073f7322 --- /dev/null +++ b/src/main/java/emu/grasscutter/scripts/data/ScriptArgs.java @@ -0,0 +1,47 @@ +package emu.grasscutter.scripts.data; + +public class ScriptArgs { + public int param1; + public int param2; + public int param3; + + public ScriptArgs() { + + } + + public ScriptArgs(int param1) { + this.param1 = param1; + } + + public ScriptArgs(int param1, int param2) { + this.param1 = param1; + this.param2 = param2; + } + + public int getParam1() { + return param1; + } + + public ScriptArgs setParam1(int param1) { + this.param1 = param1; + return this; + } + + public int getParam2() { + return param2; + } + + public ScriptArgs setParam2(int param2) { + this.param2 = param2; + return this; + } + + public int getParam3() { + return param3; + } + + public ScriptArgs setParam3(int param3) { + this.param3 = param3; + return this; + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSelectWorktopOptionReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSelectWorktopOptionReq.java index 23f3af4cc..82980c452 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSelectWorktopOptionReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSelectWorktopOptionReq.java @@ -1,8 +1,12 @@ package emu.grasscutter.server.packet.recv; +import emu.grasscutter.game.entity.EntityGadget; +import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.SelectWorktopOptionReqOuterClass.SelectWorktopOptionReq; +import emu.grasscutter.scripts.constants.EventType; +import emu.grasscutter.scripts.data.ScriptArgs; import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.packet.send.PacketSelectWorktopOptionRsp; @@ -14,9 +18,21 @@ public class HandlerSelectWorktopOptionReq extends PacketHandler { public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { SelectWorktopOptionReq req = SelectWorktopOptionReq.parseFrom(payload); - session.getPlayer().getScene().getScriptManager().onOptionSelect(req.getGadgetEntityId(), req.getOptionId()); - - session.send(new PacketSelectWorktopOptionRsp(req.getGadgetEntityId(), req.getOptionId())); + try { + GameEntity entity = session.getPlayer().getScene().getEntityById(req.getGadgetEntityId()); + + if (entity == null || !(entity instanceof EntityGadget)) { + return; + } + + session.getPlayer().getScene().getScriptManager().callEvent( + EventType.EVENT_SELECT_OPTION, + new ScriptArgs(entity.getConfigId(), req.getOptionId()) + ); + } finally { + // Always send packet + session.send(new PacketSelectWorktopOptionRsp(req.getGadgetEntityId(), req.getOptionId())); + } } } From 72e9a21ce3cc7c113fc60d9514eb30d5fc8bffbf Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Fri, 29 Apr 2022 00:49:05 -0700 Subject: [PATCH 05/14] Allow the player to finish dungeons --- .../game/dungeons/DungeonChallenge.java | 11 ++++++++-- .../game/dungeons/DungeonManager.java | 2 ++ .../emu/grasscutter/scripts/ScriptLib.java | 4 ++++ .../send/PacketChallengeDataNotify.java | 21 +++++++++++++++++++ .../PacketDungeonChallengeBeginNotify.java | 1 + .../PacketDungeonChallengeFinishNotify.java | 4 +++- 6 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketChallengeDataNotify.java diff --git a/src/main/java/emu/grasscutter/game/dungeons/DungeonChallenge.java b/src/main/java/emu/grasscutter/game/dungeons/DungeonChallenge.java index 1b595723a..686f6023e 100644 --- a/src/main/java/emu/grasscutter/game/dungeons/DungeonChallenge.java +++ b/src/main/java/emu/grasscutter/game/dungeons/DungeonChallenge.java @@ -12,6 +12,7 @@ import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType; import emu.grasscutter.scripts.constants.EventType; import emu.grasscutter.scripts.data.SceneGroup; import emu.grasscutter.scripts.data.SceneMonster; +import emu.grasscutter.server.packet.send.PacketChallengeDataNotify; import emu.grasscutter.server.packet.send.PacketDungeonChallengeBeginNotify; import emu.grasscutter.server.packet.send.PacketDungeonChallengeFinishNotify; import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify; @@ -66,6 +67,10 @@ public class DungeonChallenge { this.isSuccess = isSuccess; } + public int getScore() { + return score; + } + public void start() { getScene().broadcastPacket(new PacketDungeonChallengeBeginNotify(this)); } @@ -81,9 +86,11 @@ public class DungeonChallenge { } public void onMonsterDie(EntityMonster entity) { - score++; + score = getScore() + 1; - if (score >= objective) { + getScene().broadcastPacket(new PacketChallengeDataNotify(this, 1, getScore())); + + if (getScore() >= objective) { this.setSuccess(true); finish(); } diff --git a/src/main/java/emu/grasscutter/game/dungeons/DungeonManager.java b/src/main/java/emu/grasscutter/game/dungeons/DungeonManager.java index 5662abf4d..6011c1f7c 100644 --- a/src/main/java/emu/grasscutter/game/dungeons/DungeonManager.java +++ b/src/main/java/emu/grasscutter/game/dungeons/DungeonManager.java @@ -44,6 +44,8 @@ public class DungeonManager { return; } + Grasscutter.getLogger().info(player.getNickname() + " is trying to enter dungeon " + dungeonId); + int sceneId = data.getSceneId(); player.getScene().setPrevScene(sceneId); diff --git a/src/main/java/emu/grasscutter/scripts/ScriptLib.java b/src/main/java/emu/grasscutter/scripts/ScriptLib.java index 87881db51..05b129f2d 100644 --- a/src/main/java/emu/grasscutter/scripts/ScriptLib.java +++ b/src/main/java/emu/grasscutter/scripts/ScriptLib.java @@ -139,6 +139,10 @@ public class ScriptLib { return 0; } + public int AddExtraGroupSuite(int groupId, int param2) { + return 0; + } + public int ActiveChallenge(int challengeId, int challengeIndex, int param3, int groupId, int param4, int param5) { SceneGroup group = getSceneScriptManager().getGroupById(groupId); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketChallengeDataNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketChallengeDataNotify.java new file mode 100644 index 000000000..452ca7118 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketChallengeDataNotify.java @@ -0,0 +1,21 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.game.dungeons.DungeonChallenge; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.ChallengeDataNotifyOuterClass.ChallengeDataNotify; + +public class PacketChallengeDataNotify extends BasePacket { + + public PacketChallengeDataNotify(DungeonChallenge challenge, int index, int value) { + super(PacketOpcodes.ChallengeDataNotify); + + ChallengeDataNotify proto = ChallengeDataNotify.newBuilder() + .setChallengeIndex(challenge.getChallengeIndex()) + .setParamIndex(index) + .setValue(value) + .build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonChallengeBeginNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonChallengeBeginNotify.java index 2dd734cf5..2b8112728 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonChallengeBeginNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonChallengeBeginNotify.java @@ -13,6 +13,7 @@ public class PacketDungeonChallengeBeginNotify extends BasePacket { DungeonChallengeBeginNotify proto = DungeonChallengeBeginNotify.newBuilder() .setChallengeId(challenge.getChallengeId()) .setChallengeIndex(challenge.getChallengeIndex()) + .setGroupId(challenge.getGroup().id) .build(); this.setData(proto); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonChallengeFinishNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonChallengeFinishNotify.java index 323528aae..0fbcd9570 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonChallengeFinishNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonChallengeFinishNotify.java @@ -11,8 +11,10 @@ public class PacketDungeonChallengeFinishNotify extends BasePacket { super(PacketOpcodes.DungeonChallengeFinishNotify); DungeonChallengeFinishNotify proto = DungeonChallengeFinishNotify.newBuilder() - .setChallengeIndex(challenge.getChallengeId()) + .setChallengeIndex(challenge.getChallengeIndex()) .setIsSuccess(challenge.isSuccess()) + .setUnk1(challenge.getChallengeId()) + .setUnk2(30) .build(); this.setData(proto); From d690590ecff2f026d890f8085cc3aa4bb3470014 Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Fri, 29 Apr 2022 00:52:40 -0700 Subject: [PATCH 06/14] Implement AddExtraGroupSuite --- .../emu/grasscutter/scripts/ScriptLib.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/main/java/emu/grasscutter/scripts/ScriptLib.java b/src/main/java/emu/grasscutter/scripts/ScriptLib.java index 05b129f2d..42649448f 100644 --- a/src/main/java/emu/grasscutter/scripts/ScriptLib.java +++ b/src/main/java/emu/grasscutter/scripts/ScriptLib.java @@ -55,6 +55,10 @@ public class ScriptLib { .filter(e -> e.getGroupId() == groupId).toList(); for (GameEntity entity : list) { + if (!(entity instanceof EntityGadget)) { + continue; + } + EntityGadget gadget = (EntityGadget) entity; gadget.setState(gadgetState); @@ -140,9 +144,42 @@ public class ScriptLib { } public int AddExtraGroupSuite(int groupId, int param2) { + SceneGroup group = getSceneScriptManager().getGroupById(groupId); + + if (group == null || group.monsters == null) { + return 1; + } + + // TODO just spawn all from group for now + List toAdd = new ArrayList<>(); + + for (SceneMonster monster : group.monsters) { + MonsterData data = GameData.getMonsterDataMap().get(monster.monster_id); + + if (data == null) { + continue; + } + + EntityMonster entity = new EntityMonster(getSceneScriptManager().getScene(), data, monster.pos, monster.level); + entity.getRotation().set(monster.rot); + entity.setGroupId(group.id); + entity.setConfigId(monster.config_id); + + toAdd.add(entity); + } + + if (toAdd.size() > 0) { + getSceneScriptManager().getScene().addEntities(toAdd); + + for (GameEntity entity : toAdd) { + this.getSceneScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_LIVE, new ScriptArgs(entity.getConfigId())); + } + } + return 0; } + // param3 (probably time limit for timed dungeons) public int ActiveChallenge(int challengeId, int challengeIndex, int param3, int groupId, int param4, int param5) { SceneGroup group = getSceneScriptManager().getGroupById(groupId); From 14b6d3ce556dd668a0878b997893d6a2b38511ca Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Fri, 29 Apr 2022 01:03:16 -0700 Subject: [PATCH 07/14] Fix monster levels in dungeons --- .../emu/grasscutter/data/def/DungeonData.java | 5 ++ .../emu/grasscutter/game/world/Scene.java | 15 +---- .../scripts/SceneScriptManager.java | 62 +++++++++++++++++++ .../emu/grasscutter/scripts/ScriptLib.java | 50 +-------------- 4 files changed, 70 insertions(+), 62 deletions(-) diff --git a/src/main/java/emu/grasscutter/data/def/DungeonData.java b/src/main/java/emu/grasscutter/data/def/DungeonData.java index 3239d30fb..311286dca 100644 --- a/src/main/java/emu/grasscutter/data/def/DungeonData.java +++ b/src/main/java/emu/grasscutter/data/def/DungeonData.java @@ -10,6 +10,7 @@ import emu.grasscutter.game.props.SceneType; public class DungeonData extends GameResource { private int Id; private int SceneId; + private int ShowLevel; private String InvolveType; // TODO enum @Override @@ -21,6 +22,10 @@ public class DungeonData extends GameResource { return SceneId; } + public int getShowLevel() { + return ShowLevel; + } + @Override public void onLoad() { diff --git a/src/main/java/emu/grasscutter/game/world/Scene.java b/src/main/java/emu/grasscutter/game/world/Scene.java index 93bfab9db..aa85e2b61 100644 --- a/src/main/java/emu/grasscutter/game/world/Scene.java +++ b/src/main/java/emu/grasscutter/game/world/Scene.java @@ -495,20 +495,7 @@ public class Scene { } for (SceneGroup group : block.groups) { - for (SceneGadget g : group.gadgets) { - EntityGadget entity = new EntityGadget(this, g.gadget_id, g.pos); - - if (entity.getGadgetData() == null) continue; - - entity.setBlockId(block.id); - entity.setConfigId(g.config_id); - entity.setGroupId(group.id); - entity.getRotation().set(g.rot); - entity.setState(g.state); - - this.addEntity(entity); - this.getScriptManager().callEvent(EventType.EVENT_GADGET_CREATE, new ScriptArgs(entity.getConfigId())); - } + this.getScriptManager().spawnGadgetsInGroup(block, group); } } diff --git a/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java b/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java index 09104f63d..33acf4f45 100644 --- a/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java +++ b/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java @@ -1,5 +1,6 @@ package emu.grasscutter.scripts; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -14,6 +15,9 @@ import org.luaj.vm2.LuaValue; import org.luaj.vm2.lib.jse.CoerceJavaToLua; import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.def.MonsterData; +import emu.grasscutter.data.def.WorldLevelData; import emu.grasscutter.game.entity.EntityGadget; import emu.grasscutter.game.entity.EntityMonster; import emu.grasscutter.game.entity.GameEntity; @@ -207,6 +211,64 @@ public class SceneScriptManager { } + public void spawnGadgetsInGroup(SceneBlock block, SceneGroup group) { + for (SceneGadget g : group.gadgets) { + EntityGadget entity = new EntityGadget(getScene(), g.gadget_id, g.pos); + + if (entity.getGadgetData() == null) continue; + + entity.setBlockId(block.id); + entity.setConfigId(g.config_id); + entity.setGroupId(group.id); + entity.getRotation().set(g.rot); + entity.setState(g.state); + + getScene().addEntity(entity); + this.callEvent(EventType.EVENT_GADGET_CREATE, new ScriptArgs(entity.getConfigId())); + } + } + + public void spawnMonstersInGroup(SceneGroup group) { + List toAdd = new ArrayList<>(); + + for (SceneMonster monster : group.monsters) { + MonsterData data = GameData.getMonsterDataMap().get(monster.monster_id); + + if (data == null) { + continue; + } + + // Calculate level + int level = monster.level; + + if (getScene().getDungeonData() != null) { + level = getScene().getDungeonData().getShowLevel(); + } else if (getScene().getWorld().getWorldLevel() > 0) { + WorldLevelData worldLevelData = GameData.getWorldLevelDataMap().get(getScene().getWorld().getWorldLevel()); + + if (worldLevelData != null) { + level = worldLevelData.getMonsterLevel(); + } + } + + // Spawn mob + EntityMonster entity = new EntityMonster(getScene(), data, monster.pos, level); + entity.getRotation().set(monster.rot); + entity.setGroupId(group.id); + entity.setConfigId(monster.config_id); + + toAdd.add(entity); + } + + if (toAdd.size() > 0) { + getScene().addEntities(toAdd); + + for (GameEntity entity : toAdd) { + callEvent(EventType.EVENT_ANY_MONSTER_LIVE, new ScriptArgs(entity.getConfigId())); + } + } + } + // Events public void callEvent(int eventType, ScriptArgs params) { diff --git a/src/main/java/emu/grasscutter/scripts/ScriptLib.java b/src/main/java/emu/grasscutter/scripts/ScriptLib.java index 42649448f..a4559086c 100644 --- a/src/main/java/emu/grasscutter/scripts/ScriptLib.java +++ b/src/main/java/emu/grasscutter/scripts/ScriptLib.java @@ -115,30 +115,7 @@ public class ScriptLib { } // TODO just spawn all from group for now - List toAdd = new ArrayList<>(); - - for (SceneMonster monster : group.monsters) { - MonsterData data = GameData.getMonsterDataMap().get(monster.monster_id); - - if (data == null) { - continue; - } - - EntityMonster entity = new EntityMonster(getSceneScriptManager().getScene(), data, monster.pos, monster.level); - entity.getRotation().set(monster.rot); - entity.setGroupId(group.id); - entity.setConfigId(monster.config_id); - - toAdd.add(entity); - } - - if (toAdd.size() > 0) { - getSceneScriptManager().getScene().addEntities(toAdd); - - for (GameEntity entity : toAdd) { - this.getSceneScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_LIVE, new ScriptArgs(entity.getConfigId())); - } - } + this.getSceneScriptManager().spawnMonstersInGroup(group); return 0; } @@ -151,30 +128,7 @@ public class ScriptLib { } // TODO just spawn all from group for now - List toAdd = new ArrayList<>(); - - for (SceneMonster monster : group.monsters) { - MonsterData data = GameData.getMonsterDataMap().get(monster.monster_id); - - if (data == null) { - continue; - } - - EntityMonster entity = new EntityMonster(getSceneScriptManager().getScene(), data, monster.pos, monster.level); - entity.getRotation().set(monster.rot); - entity.setGroupId(group.id); - entity.setConfigId(monster.config_id); - - toAdd.add(entity); - } - - if (toAdd.size() > 0) { - getSceneScriptManager().getScene().addEntities(toAdd); - - for (GameEntity entity : toAdd) { - this.getSceneScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_LIVE, new ScriptArgs(entity.getConfigId())); - } - } + this.getSceneScriptManager().spawnMonstersInGroup(group); return 0; } From 3af5d20473b44d969c9336f81139d68e1369e0fe Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Fri, 29 Apr 2022 01:15:40 -0700 Subject: [PATCH 08/14] Prevent weird crashing if there was an error loading scripts --- src/main/java/emu/grasscutter/scripts/SceneScriptManager.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java b/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java index 33acf4f45..0ab0c2332 100644 --- a/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java +++ b/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java @@ -151,7 +151,9 @@ public class SceneScriptManager { this.blocks = blocks; } catch (ScriptException e) { Grasscutter.getLogger().error("Error running script", e); + return; } + // TEMP this.isInit = true; } From 1a5d4cf46679e4921b89c9739a0c99a953f96a9e Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Fri, 29 Apr 2022 02:07:25 -0700 Subject: [PATCH 09/14] Implement lazy loading of scripts when they enter a new block --- .../java/emu/grasscutter/game/world/Scene.java | 10 ++++++++-- .../grasscutter/scripts/SceneScriptManager.java | 15 +++++++++------ .../emu/grasscutter/scripts/data/SceneGroup.java | 12 ++++++++++++ 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/main/java/emu/grasscutter/game/world/Scene.java b/src/main/java/emu/grasscutter/game/world/Scene.java index aa85e2b61..c791f981d 100644 --- a/src/main/java/emu/grasscutter/game/world/Scene.java +++ b/src/main/java/emu/grasscutter/game/world/Scene.java @@ -482,8 +482,8 @@ public class Scene { for (SceneBlock block : visible) { if (!this.getLoadedBlocks().contains(block)) { - this.getLoadedBlocks().add(block); this.onLoadBlock(block); + this.getLoadedBlocks().add(block); } } } @@ -491,11 +491,17 @@ public class Scene { // TODO optimize public void onLoadBlock(SceneBlock block) { for (SceneGroup group : block.groups) { + // We load the script files for the groups here + if (!group.isLoaded()) { + this.getScriptManager().loadGroupFromScript(group); + } + group.triggers.forEach(getScriptManager()::registerTrigger); } + // Spawn gadgets AFTER triggers are added for (SceneGroup group : block.groups) { - this.getScriptManager().spawnGadgetsInGroup(block, group); + this.getScriptManager().spawnGadgetsInGroup(group); } } diff --git a/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java b/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java index 0ab0c2332..6d0d1e845 100644 --- a/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java +++ b/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java @@ -145,7 +145,7 @@ public class SceneScriptManager { SceneBlock block = blocks.get(0); block.id = blockIds.get(i); - loadBlock(block); + loadBlockFromScript(block); } this.blocks = blocks; @@ -162,7 +162,7 @@ public class SceneScriptManager { return isInit; } - private void loadBlock(SceneBlock block) { + private void loadBlockFromScript(SceneBlock block) { CompiledScript cs = ScriptLoader.getScriptByPath( Grasscutter.getConfig().SCRIPTS_FOLDER + "Scene/" + getScene().getId() + "/scene" + getScene().getId() + "_block" + block.id + "." + ScriptLoader.getScriptType()); @@ -176,13 +176,16 @@ public class SceneScriptManager { // Set groups block.groups = ScriptLoader.getSerializer().toList(SceneGroup.class, bindings.get("groups")); - block.groups.forEach(this::loadGroup); + block.groups.forEach(g -> g.block_id = block.id); } catch (ScriptException e) { Grasscutter.getLogger().error("Error loading block " + block.id + " in scene " + getScene().getId(), e); } } - private void loadGroup(SceneGroup group) { + public void loadGroupFromScript(SceneGroup group) { + // Set flag here so if there is no script, we dont call this function over and over again. + group.setLoaded(true); + CompiledScript cs = ScriptLoader.getScriptByPath( Grasscutter.getConfig().SCRIPTS_FOLDER + "Scene/" + getScene().getId() + "/scene" + getScene().getId() + "_group" + group.id + "." + ScriptLoader.getScriptType()); @@ -213,13 +216,13 @@ public class SceneScriptManager { } - public void spawnGadgetsInGroup(SceneBlock block, SceneGroup group) { + public void spawnGadgetsInGroup(SceneGroup group) { for (SceneGadget g : group.gadgets) { EntityGadget entity = new EntityGadget(getScene(), g.gadget_id, g.pos); if (entity.getGadgetData() == null) continue; - entity.setBlockId(block.id); + entity.setBlockId(group.block_id); entity.setConfigId(g.config_id); entity.setGroupId(group.id); entity.getRotation().set(g.rot); diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneGroup.java b/src/main/java/emu/grasscutter/scripts/data/SceneGroup.java index e227b1853..833f4f9e2 100644 --- a/src/main/java/emu/grasscutter/scripts/data/SceneGroup.java +++ b/src/main/java/emu/grasscutter/scripts/data/SceneGroup.java @@ -5,6 +5,8 @@ import java.util.List; import emu.grasscutter.utils.Position; public class SceneGroup { + public transient int block_id; // Not an actual variable in the scripts but we will keep it here for reference + public int id; public int refresh_id; public Position pos; @@ -14,4 +16,14 @@ public class SceneGroup { public List triggers; public List suites; public SceneInitConfig init_config; + + private transient boolean isLoaded; // Not an actual variable in the scripts either + + public boolean isLoaded() { + return isLoaded; + } + + public boolean setLoaded(boolean loaded) { + return loaded; + } } From ae3d9a4dc1fdd7921e0acaf5a162b6f33bb71428 Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Fri, 29 Apr 2022 02:38:25 -0700 Subject: [PATCH 10/14] Add a few more functions for the script engine to call --- .../game/entity/EntityMonster.java | 4 +++ .../scripts/SceneScriptManager.java | 13 +++++++- .../emu/grasscutter/scripts/ScriptLib.java | 32 +++++++++++++++++-- .../grasscutter/scripts/data/SceneVar.java | 7 ++++ 4 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 src/main/java/emu/grasscutter/scripts/data/SceneVar.java diff --git a/src/main/java/emu/grasscutter/game/entity/EntityMonster.java b/src/main/java/emu/grasscutter/game/entity/EntityMonster.java index be5812a1d..c9d0c0982 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityMonster.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityMonster.java @@ -23,6 +23,7 @@ import emu.grasscutter.net.proto.SceneEntityAiInfoOuterClass.SceneEntityAiInfo; import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo; import emu.grasscutter.net.proto.SceneMonsterInfoOuterClass.SceneMonsterInfo; import emu.grasscutter.net.proto.SceneWeaponInfoOuterClass.SceneWeaponInfo; +import emu.grasscutter.scripts.constants.EventType; import emu.grasscutter.utils.Position; import emu.grasscutter.utils.ProtoHelper; import it.unimi.dsi.fastutil.ints.Int2FloatMap; @@ -115,6 +116,9 @@ public class EntityMonster extends GameEntity { if (this.getSpawnEntry() != null) { this.getScene().getDeadSpawnedEntities().add(getSpawnEntry()); } + if (getScene().getScriptManager().isInit() && this.getGroupId() > 0) { + getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_DIE, null); + } if (getScene().getChallenge() != null && getScene().getChallenge().getGroup().id == this.getGroupId()) { getScene().getChallenge().onMonsterDie(this); } diff --git a/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java b/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java index 6d0d1e845..5413021ea 100644 --- a/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java +++ b/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import javax.script.Bindings; @@ -33,6 +34,7 @@ import emu.grasscutter.scripts.data.SceneInitConfig; import emu.grasscutter.scripts.data.SceneMonster; import emu.grasscutter.scripts.data.SceneSuite; import emu.grasscutter.scripts.data.SceneTrigger; +import emu.grasscutter.scripts.data.SceneVar; import emu.grasscutter.scripts.data.ScriptArgs; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; @@ -41,8 +43,9 @@ public class SceneScriptManager { private final Scene scene; private final ScriptLib scriptLib; private final LuaValue scriptLibLua; - private Bindings bindings; + private final Map variables; + private Bindings bindings; private SceneConfig config; private List blocks; private Int2ObjectOpenHashMap> triggers; @@ -53,6 +56,7 @@ public class SceneScriptManager { this.scriptLib = new ScriptLib(this); this.scriptLibLua = CoerceJavaToLua.coerce(this.scriptLib); this.triggers = new Int2ObjectOpenHashMap<>(); + this.variables = new HashMap<>(); // TEMPORARY if (this.getScene().getId() < 10) { @@ -87,6 +91,10 @@ public class SceneScriptManager { return blocks; } + public Map getVariables() { + return variables; + } + public Set getTriggersByEvent(int eventId) { return triggers.computeIfAbsent(eventId, e -> new HashSet<>()); } @@ -203,6 +211,9 @@ public class SceneScriptManager { group.triggers = ScriptLoader.getSerializer().toList(SceneTrigger.class, bindings.get("triggers")); group.suites = ScriptLoader.getSerializer().toList(SceneSuite.class, bindings.get("suites")); group.init_config = ScriptLoader.getSerializer().toObject(SceneInitConfig.class, bindings.get("init_config")); + + List variables = ScriptLoader.getSerializer().toList(SceneVar.class, bindings.get("variables")); + variables.forEach(var -> this.getVariables().put(var.name, LuaValue.valueOf(var.value))); } catch (ScriptException e) { Grasscutter.getLogger().error("Error loading group " + group.id + " in scene " + getScene().getId(), e); } diff --git a/src/main/java/emu/grasscutter/scripts/ScriptLib.java b/src/main/java/emu/grasscutter/scripts/ScriptLib.java index a4559086c..9a20359e5 100644 --- a/src/main/java/emu/grasscutter/scripts/ScriptLib.java +++ b/src/main/java/emu/grasscutter/scripts/ScriptLib.java @@ -5,6 +5,7 @@ import java.util.List; import java.util.Optional; import org.luaj.vm2.LuaTable; +import org.luaj.vm2.LuaValue; import emu.grasscutter.Grasscutter; import emu.grasscutter.data.GameData; @@ -120,7 +121,7 @@ public class ScriptLib { return 0; } - public int AddExtraGroupSuite(int groupId, int param2) { + public int AddExtraGroupSuite(int groupId, int suite) { SceneGroup group = getSceneScriptManager().getGroupById(groupId); if (group == null || group.monsters == null) { @@ -151,8 +152,35 @@ public class ScriptLib { return 0; } + public int GetGroupMonsterCountByGroupId(int groupId) { + return (int) getSceneScriptManager().getScene().getEntities().values().stream() + .filter(e -> e instanceof EntityMonster && e.getGroupId() == groupId) + .count(); + } + + public LuaValue GetGroupVariableValue(String var) { + return getSceneScriptManager().getVariables().getOrDefault(var, LuaValue.NIL); + } + + public LuaValue ChangeGroupVariableValue(String var, LuaValue value) { + getSceneScriptManager().getVariables().put(var, value); + return LuaValue.ZERO; + } + public int RefreshGroup(LuaTable table) { - // Add group back to suite + // Kill and Respawn? + int groupId = table.get("group_id").toint(); + int suite = table.get("suite").toint(); + + SceneGroup group = getSceneScriptManager().getGroupById(groupId); + + if (group == null || group.monsters == null) { + return 1; + } + + // TODO just spawn all from group for now + this.getSceneScriptManager().spawnMonstersInGroup(group); + return 0; } diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneVar.java b/src/main/java/emu/grasscutter/scripts/data/SceneVar.java new file mode 100644 index 000000000..9ead6f474 --- /dev/null +++ b/src/main/java/emu/grasscutter/scripts/data/SceneVar.java @@ -0,0 +1,7 @@ +package emu.grasscutter.scripts.data; + +public class SceneVar { + public String name; + public int value; + public boolean no_refresh; +} From ffc1f801e6cab1bd470aa9c674532f108362c8ed Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Fri, 29 Apr 2022 03:06:33 -0700 Subject: [PATCH 11/14] Implement spawning monsters by suite --- .../scripts/SceneScriptManager.java | 37 +++++++++++++++++-- .../emu/grasscutter/scripts/ScriptLib.java | 10 ++--- .../grasscutter/scripts/data/SceneGroup.java | 4 ++ .../grasscutter/scripts/data/SceneSuite.java | 3 ++ 4 files changed, 45 insertions(+), 9 deletions(-) diff --git a/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java b/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java index 5413021ea..2f2f31219 100644 --- a/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java +++ b/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java @@ -6,6 +6,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import javax.script.Bindings; import javax.script.CompiledScript; @@ -43,7 +44,7 @@ public class SceneScriptManager { private final Scene scene; private final ScriptLib scriptLib; private final LuaValue scriptLibLua; - private final Map variables; + private final Map variables; private Bindings bindings; private SceneConfig config; @@ -91,7 +92,7 @@ public class SceneScriptManager { return blocks; } - public Map getVariables() { + public Map getVariables() { return variables; } @@ -212,8 +213,22 @@ public class SceneScriptManager { group.suites = ScriptLoader.getSerializer().toList(SceneSuite.class, bindings.get("suites")); group.init_config = ScriptLoader.getSerializer().toObject(SceneInitConfig.class, bindings.get("init_config")); + // Add variables to suite List variables = ScriptLoader.getSerializer().toList(SceneVar.class, bindings.get("variables")); - variables.forEach(var -> this.getVariables().put(var.name, LuaValue.valueOf(var.value))); + variables.forEach(var -> this.getVariables().put(var.name, var.value)); + + // Add monsters to suite TODO optimize + HashMap map = (HashMap) group.monsters.stream().collect(Collectors.toMap(m -> m.config_id, m -> m)); + + for (SceneSuite suite : group.suites) { + suite.sceneMonsters = new ArrayList<>(suite.monsters.size()); + for (int id : suite.monsters) { + SceneMonster monster = map.get(id); + if (monster != null) { + suite.sceneMonsters.add(monster); + } + } + } } catch (ScriptException e) { Grasscutter.getLogger().error("Error loading group " + group.id + " in scene " + getScene().getId(), e); } @@ -244,10 +259,24 @@ public class SceneScriptManager { } } + public void spawnMonstersInGroup(SceneGroup group, int suiteIndex) { + spawnMonstersInGroup(group, group.getSuiteByIndex(suiteIndex)); + } + public void spawnMonstersInGroup(SceneGroup group) { + spawnMonstersInGroup(group, null); + } + + public void spawnMonstersInGroup(SceneGroup group, SceneSuite suite) { + List monsters = group.monsters; + + if (suite != null) { + monsters = suite.sceneMonsters; + } + List toAdd = new ArrayList<>(); - for (SceneMonster monster : group.monsters) { + for (SceneMonster monster : monsters) { MonsterData data = GameData.getMonsterDataMap().get(monster.monster_id); if (data == null) { diff --git a/src/main/java/emu/grasscutter/scripts/ScriptLib.java b/src/main/java/emu/grasscutter/scripts/ScriptLib.java index 9a20359e5..57cd73dd3 100644 --- a/src/main/java/emu/grasscutter/scripts/ScriptLib.java +++ b/src/main/java/emu/grasscutter/scripts/ScriptLib.java @@ -129,7 +129,7 @@ public class ScriptLib { } // TODO just spawn all from group for now - this.getSceneScriptManager().spawnMonstersInGroup(group); + this.getSceneScriptManager().spawnMonstersInGroup(group, suite); return 0; } @@ -158,11 +158,11 @@ public class ScriptLib { .count(); } - public LuaValue GetGroupVariableValue(String var) { - return getSceneScriptManager().getVariables().getOrDefault(var, LuaValue.NIL); + public int GetGroupVariableValue(String var) { + return getSceneScriptManager().getVariables().getOrDefault(var, 0); } - public LuaValue ChangeGroupVariableValue(String var, LuaValue value) { + public LuaValue ChangeGroupVariableValue(String var, int value) { getSceneScriptManager().getVariables().put(var, value); return LuaValue.ZERO; } @@ -179,7 +179,7 @@ public class ScriptLib { } // TODO just spawn all from group for now - this.getSceneScriptManager().spawnMonstersInGroup(group); + this.getSceneScriptManager().spawnMonstersInGroup(group, suite); return 0; } diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneGroup.java b/src/main/java/emu/grasscutter/scripts/data/SceneGroup.java index 833f4f9e2..d72d02d53 100644 --- a/src/main/java/emu/grasscutter/scripts/data/SceneGroup.java +++ b/src/main/java/emu/grasscutter/scripts/data/SceneGroup.java @@ -26,4 +26,8 @@ public class SceneGroup { public boolean setLoaded(boolean loaded) { return loaded; } + + public SceneSuite getSuiteByIndex(int index) { + return suites.get(index - 1); + } } diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneSuite.java b/src/main/java/emu/grasscutter/scripts/data/SceneSuite.java index be9bc0f08..d84504569 100644 --- a/src/main/java/emu/grasscutter/scripts/data/SceneSuite.java +++ b/src/main/java/emu/grasscutter/scripts/data/SceneSuite.java @@ -5,6 +5,9 @@ import java.util.List; import emu.grasscutter.utils.Position; public class SceneSuite { + public List monsters; public List triggers; public int rand_weight; + + public transient List sceneMonsters; } From cb15a5dec90f46d697c8441a7a48a671db3f31c6 Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Fri, 29 Apr 2022 03:09:17 -0700 Subject: [PATCH 12/14] Deregister scenes if no one is in them instead of deregistering when no entities are in them --- src/main/java/emu/grasscutter/game/world/Scene.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/game/world/Scene.java b/src/main/java/emu/grasscutter/game/world/Scene.java index c791f981d..e886d135b 100644 --- a/src/main/java/emu/grasscutter/game/world/Scene.java +++ b/src/main/java/emu/grasscutter/game/world/Scene.java @@ -225,7 +225,7 @@ public class Scene { } // Deregister scene if not in use - if (this.getEntities().size() <= 0 && !this.dontDestroyWhenEmpty()) { + if (this.getPlayerCount() <= 0 && !this.dontDestroyWhenEmpty()) { this.getWorld().deregisterScene(this); } } From d877d7eebca6010a0e66931bfcb671b8cce5e759 Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Fri, 29 Apr 2022 03:24:36 -0700 Subject: [PATCH 13/14] Remove player from challenge if they are leaving the scene --- .../grasscutter/game/dungeons/DungeonChallenge.java | 13 ++++++++++--- src/main/java/emu/grasscutter/game/world/Scene.java | 6 ++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/main/java/emu/grasscutter/game/dungeons/DungeonChallenge.java b/src/main/java/emu/grasscutter/game/dungeons/DungeonChallenge.java index 686f6023e..608587765 100644 --- a/src/main/java/emu/grasscutter/game/dungeons/DungeonChallenge.java +++ b/src/main/java/emu/grasscutter/game/dungeons/DungeonChallenge.java @@ -23,7 +23,8 @@ public class DungeonChallenge { private int challengeIndex; private int challengeId; - private boolean isSuccess; + private boolean success; + private boolean progress; private int score; private int objective = 0; @@ -60,11 +61,15 @@ public class DungeonChallenge { } public boolean isSuccess() { - return isSuccess; + return success; } public void setSuccess(boolean isSuccess) { - this.isSuccess = isSuccess; + this.success = isSuccess; + } + + public boolean inProgress() { + return progress; } public int getScore() { @@ -72,10 +77,12 @@ public class DungeonChallenge { } public void start() { + this.progress = true; getScene().broadcastPacket(new PacketDungeonChallengeBeginNotify(this)); } public void finish() { + this.progress = false; getScene().broadcastPacket(new PacketDungeonChallengeFinishNotify(this)); if (this.isSuccess()) { diff --git a/src/main/java/emu/grasscutter/game/world/Scene.java b/src/main/java/emu/grasscutter/game/world/Scene.java index e886d135b..9166499be 100644 --- a/src/main/java/emu/grasscutter/game/world/Scene.java +++ b/src/main/java/emu/grasscutter/game/world/Scene.java @@ -25,6 +25,7 @@ import emu.grasscutter.scripts.data.SceneBlock; import emu.grasscutter.scripts.data.SceneGadget; import emu.grasscutter.scripts.data.SceneGroup; import emu.grasscutter.scripts.data.ScriptArgs; +import emu.grasscutter.server.packet.send.PacketDungeonChallengeFinishNotify; import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify; import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify; @@ -212,6 +213,11 @@ public class Scene { } public synchronized void removePlayer(Player player) { + // Remove from challenge if leaving + if (this.getChallenge() != null && this.getChallenge().inProgress()) { + player.sendPacket(new PacketDungeonChallengeFinishNotify(this.getChallenge())); + } + // Remove player from scene getPlayers().remove(player); player.setScene(null); From 6d9874302511c985e0aa820e00da1c94c14b644d Mon Sep 17 00:00:00 2001 From: Melledy <52122272+Melledy@users.noreply.github.com> Date: Fri, 29 Apr 2022 03:33:14 -0700 Subject: [PATCH 14/14] Prevent console error spam when attacking gadgets --- src/main/java/emu/grasscutter/game/world/Scene.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/emu/grasscutter/game/world/Scene.java b/src/main/java/emu/grasscutter/game/world/Scene.java index 9166499be..ee6b9a6a9 100644 --- a/src/main/java/emu/grasscutter/game/world/Scene.java +++ b/src/main/java/emu/grasscutter/game/world/Scene.java @@ -346,6 +346,11 @@ public class Scene { } } + // Sanity check + if (target.getFightProperties() == null) { + return; + } + // Lose hp target.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, -result.getDamage());