From 770cd62370b4b18391293a3641e07ece1a5a852a Mon Sep 17 00:00:00 2001 From: longfruit <147137915+longfruit@users.noreply.github.com> Date: Mon, 16 Oct 2023 22:41:04 -0700 Subject: [PATCH] Fix daily dungeon flow (#2398) * Fix dungeon entry, daily changes, replay flow; fix Mond's weapon mats domain unlock * add note to DungeonEntryToBeExploreNotify --- .../command/commands/DebugCommand.java | 5 +- .../command/commands/EntityCommand.java | 5 +- .../grasscutter/data/common/PointData.java | 3 +- .../data/excels/dungeon/DungeonEntryData.java | 42 ++++++++++++ .../game/dungeons/DungeonSystem.java | 25 +++++++ .../enums/DungeonEntryCondCombType.java | 7 ++ .../entity/gadget/GadgetRewardStatue.java | 4 +- .../emu/grasscutter/game/player/Player.java | 25 +++++++ .../emu/grasscutter/game/world/Scene.java | 2 +- .../scripts/SceneScriptManager.java | 18 +++-- .../emu/grasscutter/scripts/ScriptLib.java | 68 ++++++++++++------- .../packet/recv/HandlerDungeonRestartReq.java | 15 ++++ ...lerGetDungeonEntryExploreConditionReq.java | 27 ++++++++ .../PacketDungeonEntryToBeExploreNotify.java | 20 ++++++ ...ketGetDungeonEntryExploreConditionRsp.java | 31 +++++++++ .../packet/send/PacketPostEnterSceneRsp.java | 5 ++ .../send/PacketUnfreezeGroupLimitNotify.java | 14 ++++ 17 files changed, 278 insertions(+), 38 deletions(-) create mode 100644 src/main/java/emu/grasscutter/game/dungeons/enums/DungeonEntryCondCombType.java create mode 100644 src/main/java/emu/grasscutter/server/packet/recv/HandlerDungeonRestartReq.java create mode 100644 src/main/java/emu/grasscutter/server/packet/recv/HandlerGetDungeonEntryExploreConditionReq.java create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketDungeonEntryToBeExploreNotify.java create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketGetDungeonEntryExploreConditionRsp.java create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketUnfreezeGroupLimitNotify.java diff --git a/src/main/java/emu/grasscutter/command/commands/DebugCommand.java b/src/main/java/emu/grasscutter/command/commands/DebugCommand.java index c4aac96af..47d916059 100644 --- a/src/main/java/emu/grasscutter/command/commands/DebugCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/DebugCommand.java @@ -32,9 +32,12 @@ public final class DebugCommand implements CommandHandler { var scene = sender.getScene(); var entityId = Integer.parseInt(args.get(0)); + // TODO Might want to allow groupId specification, + // because there can be more than one entity with + // the given config ID. var entity = args.size() > 1 && args.get(1).equals("config") - ? scene.getEntityByConfigId(entityId) + ? scene.getFirstEntityByConfigId(entityId) : scene.getEntityById(entityId); if (entity == null) { sender.dropMessage("Entity not found."); diff --git a/src/main/java/emu/grasscutter/command/commands/EntityCommand.java b/src/main/java/emu/grasscutter/command/commands/EntityCommand.java index 677a9a429..0537d8ff1 100644 --- a/src/main/java/emu/grasscutter/command/commands/EntityCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/EntityCommand.java @@ -51,7 +51,10 @@ public final class EntityCommand implements CommandHandler { } param.scene = targetPlayer.getScene(); - var entity = param.scene.getEntityByConfigId(param.configId); + // TODO Might want to allow groupId specification, + // because there can be more than one entity with + // the given config ID. + var entity = param.scene.getFirstEntityByConfigId(param.configId); if (entity == null) { CommandHandler.sendMessage(sender, translate(sender, "commands.entity.not_found_error")); diff --git a/src/main/java/emu/grasscutter/data/common/PointData.java b/src/main/java/emu/grasscutter/data/common/PointData.java index 67dfc8c73..6c9260b8b 100644 --- a/src/main/java/emu/grasscutter/data/common/PointData.java +++ b/src/main/java/emu/grasscutter/data/common/PointData.java @@ -19,6 +19,7 @@ public final class PointData { @Getter private Position size; @Getter private boolean forbidSimpleUnlock; @Getter private boolean unlocked; + @Getter private boolean groupLimit; @SerializedName( value = "dungeonIds", @@ -28,7 +29,7 @@ public final class PointData { @SerializedName( value = "dungeonRandomList", - alternate = {"OIBKFJNBLHO"}) + alternate = {"GLEKJMEEOMH"}) @Getter private int[] dungeonRandomList; diff --git a/src/main/java/emu/grasscutter/data/excels/dungeon/DungeonEntryData.java b/src/main/java/emu/grasscutter/data/excels/dungeon/DungeonEntryData.java index bfb93b512..82a10b47b 100644 --- a/src/main/java/emu/grasscutter/data/excels/dungeon/DungeonEntryData.java +++ b/src/main/java/emu/grasscutter/data/excels/dungeon/DungeonEntryData.java @@ -1,6 +1,8 @@ package emu.grasscutter.data.excels.dungeon; +import emu.grasscutter.game.dungeons.enums.*; import emu.grasscutter.data.*; +import java.util.List; import lombok.*; @ResourceType(name = "DungeonEntryExcelConfigData.json") @@ -12,4 +14,44 @@ public class DungeonEntryData extends GameResource { private int dungeonEntryId; private int sceneId; + private DungunEntryType type; + private DungeonEntryCondCombType condComb; + private List satisfiedCond; + + public static class DungeonEntryCondition { + private DungeonEntrySatisfiedConditionType type; + private int param1; + } + + public DungunEntryType getType() { + if (type == null) { + return DungunEntryType.DUNGEN_ENTRY_TYPE_NONE; + } + return type; + } + + public DungeonEntryCondCombType getCondComb() { + if (condComb == null) { + return DungeonEntryCondCombType.DUNGEON_ENTRY_COND_COMB_NONE; + } + return condComb; + } + + public int getLevelCondition() { + for (var cond : satisfiedCond) { + if (cond.type != null && cond.type.equals(DungeonEntrySatisfiedConditionType.DUNGEON_ENTRY_CONDITION_LEVEL)) { + return cond.param1; + } + } + return 0; + } + + public int getQuestCondition() { + for (var cond : satisfiedCond) { + if (cond.type != null && cond.type.equals(DungeonEntrySatisfiedConditionType.DUNGEON_ENTRY_CONDITION_QUEST)) { + return cond.param1; + } + } + return 0; + } } diff --git a/src/main/java/emu/grasscutter/game/dungeons/DungeonSystem.java b/src/main/java/emu/grasscutter/game/dungeons/DungeonSystem.java index c254e2cd2..fd26c724c 100644 --- a/src/main/java/emu/grasscutter/game/dungeons/DungeonSystem.java +++ b/src/main/java/emu/grasscutter/game/dungeons/DungeonSystem.java @@ -7,10 +7,14 @@ import emu.grasscutter.data.excels.dungeon.*; import emu.grasscutter.game.dungeons.handlers.DungeonBaseHandler; import emu.grasscutter.game.player.Player; import emu.grasscutter.game.props.SceneType; +import emu.grasscutter.game.props.EnterReason; import emu.grasscutter.game.world.*; import emu.grasscutter.net.packet.*; import emu.grasscutter.server.game.*; import emu.grasscutter.server.packet.send.PacketDungeonEntryInfoRsp; +import emu.grasscutter.game.world.data.TeleportProperties; +import emu.grasscutter.server.event.player.PlayerTeleportEvent.TeleportType; +import emu.grasscutter.net.proto.EnterTypeOuterClass.EnterType; import it.unimi.dsi.fastutil.ints.*; import java.util.List; import lombok.val; @@ -173,4 +177,25 @@ public final class DungeonSystem extends BaseGameSystem { player.getWorld().transferPlayerToScene(player, prevScene, prevPos); player.sendPacket(new BasePacket(PacketOpcodes.PlayerQuitDungeonRsp)); } + + public void restartDungeon(Player player) { + var scene = player.getScene(); + var dungeonManager = scene.getDungeonManager(); + var dungeonData = dungeonManager.getDungeonData(); + var sceneId = dungeonData.getSceneId(); + + // Forward over previous scene and scene point + var prevScene = scene.getPrevScene(); + var pointId = scene.getPrevScenePoint(); + + // Destroy then create scene again to reinitialize script state + scene.getPlayers().forEach(scene::removePlayer); + if (player.getWorld().transferPlayerToScene(player, sceneId, dungeonData)) { + scene = player.getScene(); + scene.setPrevScene(prevScene); + scene.setPrevScenePoint(pointId); + scene.setDungeonManager(new DungeonManager(scene, dungeonData)); + scene.addDungeonSettleObserver(basicDungeonSettleObserver); + } + } } diff --git a/src/main/java/emu/grasscutter/game/dungeons/enums/DungeonEntryCondCombType.java b/src/main/java/emu/grasscutter/game/dungeons/enums/DungeonEntryCondCombType.java new file mode 100644 index 000000000..7aed2ff2e --- /dev/null +++ b/src/main/java/emu/grasscutter/game/dungeons/enums/DungeonEntryCondCombType.java @@ -0,0 +1,7 @@ +package emu.grasscutter.game.dungeons.enums; + +public enum DungeonEntryCondCombType { + DUNGEON_ENTRY_COND_COMB_NONE, + DUNGEON_ENTRY_COND_COMB_LOGIC_OR, + DUNGEON_ENTRY_COND_COMB_LOGIC_AND +} diff --git a/src/main/java/emu/grasscutter/game/entity/gadget/GadgetRewardStatue.java b/src/main/java/emu/grasscutter/game/entity/gadget/GadgetRewardStatue.java index 742e79281..a266442ab 100644 --- a/src/main/java/emu/grasscutter/game/entity/gadget/GadgetRewardStatue.java +++ b/src/main/java/emu/grasscutter/game/entity/gadget/GadgetRewardStatue.java @@ -1,6 +1,6 @@ package emu.grasscutter.game.entity.gadget; -import emu.grasscutter.game.dungeons.challenge.DungeonChallenge; +import emu.grasscutter.game.dungeons.challenge.WorldChallenge; import emu.grasscutter.game.entity.EntityGadget; import emu.grasscutter.game.player.Player; import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq; @@ -18,7 +18,7 @@ public final class GadgetRewardStatue extends GadgetContent { public boolean onInteract(Player player, GadgetInteractReq req) { var dungeonManager = player.getScene().getDungeonManager(); - if (player.getScene().getChallenge() instanceof DungeonChallenge) { + if (player.getScene().getChallenge() instanceof WorldChallenge) { var useCondensed = req.getResinCostType() == ResinCostTypeOuterClass.ResinCostType.RESIN_COST_TYPE_CONDENSE; dungeonManager.getStatueDrops(player, useCondensed, getGadget().getGroupId()); diff --git a/src/main/java/emu/grasscutter/game/player/Player.java b/src/main/java/emu/grasscutter/game/player/Player.java index e7b50c46f..fc50aa29e 100644 --- a/src/main/java/emu/grasscutter/game/player/Player.java +++ b/src/main/java/emu/grasscutter/game/player/Player.java @@ -1530,6 +1530,31 @@ public class Player implements PlayerHook, FieldFetch { getServer().getPlayers().values().removeIf(player1 -> player1 == this); } + public void unfreezeUnlockedScenePoints(int sceneId) { + // Unfreeze previously unlocked scene points. For example, + // the first weapon mats domain needs some script interaction + // to unlock. It needs to be unfrozen when GetScenePointReq + // comes in to be interactable again. + GameData.getScenePointEntryMap().values().stream() + .filter(scenePointEntry -> + // Note: Only DungeonEntry scene points need to be unfrozen + scenePointEntry.getPointData().getType().equals("DungeonEntry") + // groupLimit says this scene point needs to be unfrozen + && scenePointEntry.getPointData().isGroupLimit()) + .forEach(scenePointEntry -> { + // If this is a previously unlocked scene point, + // send unfreeze packet. + val pointId = scenePointEntry.getPointData().getId(); + if (unlockedScenePoints.get(sceneId).contains(pointId)) { + this.sendPacket(new PacketUnfreezeGroupLimitNotify(pointId, sceneId)); + } + }); + } + + public void unfreezeUnlockedScenePoints() { + unlockedScenePoints.keySet().forEach(sceneId -> unfreezeUnlockedScenePoints(sceneId)); + } + public int getLegendaryKey() { return this.getProperty(PlayerProperty.PROP_PLAYER_LEGENDARY_KEY); } diff --git a/src/main/java/emu/grasscutter/game/world/Scene.java b/src/main/java/emu/grasscutter/game/world/Scene.java index 9e7a1c7db..efcaa14ea 100644 --- a/src/main/java/emu/grasscutter/game/world/Scene.java +++ b/src/main/java/emu/grasscutter/game/world/Scene.java @@ -158,7 +158,7 @@ public class Scene { return entity; } - public GameEntity getEntityByConfigId(int configId) { + public GameEntity getFirstEntityByConfigId(int configId) { return this.entities.values().stream() .filter(x -> x.getConfigId() == configId) .findFirst() diff --git a/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java b/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java index 868208c2d..2363e577b 100644 --- a/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java +++ b/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java @@ -265,6 +265,10 @@ public class SceneScriptManager { groupInstance.setActiveSuiteId(suiteIndex); groupInstance.setLastTimeRefreshed(getScene().getWorld().getGameTime()); + + // Call EVENT_GROUP_REFRESH for any action trigger waiting for it + callEvent(new ScriptArgs(groupInstance.getGroupId(), EventType.EVENT_GROUP_REFRESH)); + return suiteIndex; } @@ -323,7 +327,7 @@ public class SceneScriptManager { group.monsters.values().stream() .filter( m -> { - var entity = scene.getEntityByConfigId(m.config_id); + var entity = scene.getEntityByConfigId(m.config_id, groupId); return (entity == null || entity.getGroupId() != group @@ -694,7 +698,7 @@ public class SceneScriptManager { return suite.sceneGadgets.stream() .filter( m -> { - var entity = scene.getEntityByConfigId(m.config_id); + var entity = scene.getEntityByConfigId(m.config_id, group.id); return (entity == null || entity.getGroupId() != group.id) && (!m.isOneoff || !m.persistent @@ -712,7 +716,7 @@ public class SceneScriptManager { return suite.sceneMonsters.stream() .filter( m -> { - var entity = scene.getEntityByConfigId(m.config_id); + var entity = scene.getEntityByConfigId(m.config_id, group.id); return (entity == null || entity.getGroupId() != group @@ -788,7 +792,7 @@ public class SceneScriptManager { public void spawnMonstersByConfigId(SceneGroup group, int configId, int delayTime) { // TODO delay - var entity = scene.getEntityByConfigId(configId); + var entity = scene.getEntityByConfigId(configId, group.id); if (entity != null && entity.getGroupId() == group.id) { Grasscutter.getLogger() .debug("entity already exists failed in group {} with config {}", group.id, configId); @@ -884,9 +888,11 @@ public class SceneScriptManager { private boolean evaluateTriggerCondition(SceneTrigger trigger, ScriptArgs params) { Grasscutter.getLogger() .trace( - "Call Condition Trigger {}, [{},{},{}]", + "Call Condition Trigger {}, [{},{},{}], source_eid {}, target_eid {}", trigger.getCondition(), params.param1, + params.param2, + params.param3, params.source_eid, params.target_eid); LuaValue ret = this.callScriptFunc(trigger.getCondition(), trigger.currentGroup, params); @@ -1194,7 +1200,7 @@ public class SceneScriptManager { return monsters.values().stream() .noneMatch( m -> { - val entity = scene.getEntityByConfigId(m.config_id); + val entity = scene.getEntityByConfigId(m.config_id, groupId); return entity != null && entity.getGroupId() == groupId; }); } diff --git a/src/main/java/emu/grasscutter/scripts/ScriptLib.java b/src/main/java/emu/grasscutter/scripts/ScriptLib.java index 0209c8c9f..dc1b574ff 100644 --- a/src/main/java/emu/grasscutter/scripts/ScriptLib.java +++ b/src/main/java/emu/grasscutter/scripts/ScriptLib.java @@ -31,7 +31,7 @@ import static emu.grasscutter.scripts.constants.GroupKillPolicy.*; @SuppressWarnings("unused") public class ScriptLib { - public static final Logger logger = LoggerFactory.getLogger(ScriptLib.class); + public static final Logger logger = Grasscutter.getLogger(); private final FastThreadLocal sceneScriptManager; private final FastThreadLocal currentGroup; private final FastThreadLocal callParams; @@ -128,7 +128,7 @@ public class ScriptLib { // kill targets if exists targets.forEach(o -> { - var entity = getSceneScriptManager().getScene().getEntityByConfigId(o.config_id); + var entity = getSceneScriptManager().getScene().getEntityByConfigId(o.config_id, getCurrentGroup().get().id); if (entity == null) { return; } @@ -153,7 +153,7 @@ public class ScriptLib { // kill targets if exists for (int cfgId : targets) { - var entity = getSceneScriptManager().getScene().getEntityByConfigId(cfgId); + var entity = getSceneScriptManager().getScene().getEntityByConfigId(cfgId, getCurrentGroup().get().id); if (entity == null || cfgId == 0) { continue; } @@ -402,11 +402,11 @@ public class ScriptLib { // TODO: ChangeDeathZone public int ChangeGroupGadget(LuaTable table) { - logger.debug("[LUA] Call ChangeGroupGadget with {}", printTable(table)); + logger.debug("[LUA] Call ChangeGroupGadget with {}, current group {}", printTable(table), getCurrentGroup().get().id); var configId = table.get("config_id").toint(); var state = table.get("state").toint(); - var entity = getSceneScriptManager().getScene().getEntityByConfigId(configId); + var entity = getSceneScriptManager().getScene().getEntityByConfigId(configId, getCurrentGroup().get().id); if (entity == null) { return 1; } @@ -601,7 +601,7 @@ public class ScriptLib { return 1; } var configId = callParams.param1; - var entity = getSceneScriptManager().getScene().getEntityByConfigId(configId); + var entity = getSceneScriptManager().getScene().getEntityByConfigId(configId, getCurrentGroup().get().id); if (!(entity instanceof EntityGadget gadget)) { return 1; } @@ -785,7 +785,7 @@ public class ScriptLib { public int GetEntityIdByConfigId(int configId) { logger.warn("[LUA] Call GetEntityIdByConfigId with {}", configId); // TODO check - var entity = getSceneScriptManager().getScene().getEntityByConfigId(configId); + var entity = getSceneScriptManager().getScene().getEntityByConfigId(configId, getCurrentGroup().get().id); return entity != null ? entity.getId() : 0; } @@ -811,7 +811,7 @@ public class ScriptLib { public int GetGadgetStateByConfigId(int groupId, int configId) { logger.debug("[LUA] Call GetGadgetStateByConfigId with {},{}", groupId, configId); val scene = getSceneScriptManager().getScene(); - val gadget = groupId == 0 ? scene.getEntityByConfigId(configId) : scene.getEntityByConfigId(configId, groupId); + val gadget = groupId == 0 ? scene.getEntityByConfigId(configId, getCurrentGroup().get().id) : scene.getEntityByConfigId(configId, groupId); if (!(gadget instanceof EntityGadget)) { return -1; } @@ -833,17 +833,19 @@ public class ScriptLib { // TODO: GetGroupLogicStateValue public int GetGroupMonsterCount() { - logger.debug("[LUA] Call GetGroupMonsterCount "); - return (int) getSceneScriptManager().getScene().getEntities().values().stream() + int returnValue = (int) getSceneScriptManager().getScene().getEntities().values().stream() .filter(e -> e instanceof EntityMonster && e.getGroupId() == currentGroup.get().id) .count(); + logger.debug("[LUA] Call GetGroupMonsterCount = {}", returnValue); + return returnValue; } public int GetGroupMonsterCountByGroupId(int groupId) { - logger.debug("[LUA] Call GetGroupMonsterCountByGroupId with {}", groupId); - return (int) getSceneScriptManager().getScene().getEntities().values().stream() + int returnValue = (int) getSceneScriptManager().getScene().getEntities().values().stream() .filter(e -> e instanceof EntityMonster && e.getGroupId() == groupId) .count(); + logger.debug("[LUA] Call GetGroupMonsterCountByGroupId with {} = {}", groupId, returnValue); + return returnValue; } public int GetGroupSuite(int groupId) { @@ -860,13 +862,15 @@ public class ScriptLib { } public int GetGroupVariableValue(String var) { - logger.debug("[LUA] Call GetGroupVariableValue with {}", var); - return getSceneScriptManager().getVariables(currentGroup.get().id).getOrDefault(var, 0); + int returnValue = getSceneScriptManager().getVariables(currentGroup.get().id).getOrDefault(var, 0); + logger.debug("[LUA] Call GetGroupVariableValue with {} = {}", var, returnValue); + return returnValue; } public int GetGroupVariableValueByGroup(String var, int groupId) { - logger.debug("[LUA] Call GetGroupVariableValueByGroup with {},{}", var, groupId); - return getSceneScriptManager().getVariables(groupId).getOrDefault(var, 0); + int returnValue = getSceneScriptManager().getVariables(groupId).getOrDefault(var, 0); + logger.debug("[LUA] Call GetGroupVariableValueByGroup with {},{} = {}", var, groupId, returnValue); + return returnValue; } // TODO: GetHideAndSeekHunter @@ -1105,7 +1109,7 @@ public class ScriptLib { if (configId == LuaValue.NIL) { return 1; } - var entity = getSceneScriptManager().getScene().getEntityByConfigId(configId.toint()); + var entity = getSceneScriptManager().getScene().getEntityByConfigId(configId.toint(), getCurrentGroup().get().id); if (entity == null) { return 0; } @@ -1342,7 +1346,7 @@ public class ScriptLib { var scriptManager = this.getSceneScriptManager(); if (scriptManager == null) return 1; var scene = scriptManager.getScene(); - var entity = scene.getEntityByConfigId(cfgId); + var entity = scene.getEntityByConfigId(cfgId, getCurrentGroup().get().id); if (entity == null) return 2; scene.runWhenHostInitialized(() -> scene.broadcastPacket( new PacketServerGlobalValueChangeNotify(entity, sgvName, value))); @@ -1380,8 +1384,8 @@ public class ScriptLib { // TODO: SetGadgetHp public int SetGadgetStateByConfigId(int configId, int gadgetState) { - logger.debug("[LUA] Call SetGadgetStateByConfigId with {},{}", configId, gadgetState); - GameEntity entity = getSceneScriptManager().getScene().getEntityByConfigId(configId); + logger.debug("[LUA] Call SetGadgetStateByConfigId with {},{}, current group {}", configId, gadgetState, getCurrentGroup().get().id); + GameEntity entity = getSceneScriptManager().getScene().getEntityByConfigId(configId, getCurrentGroup().get().id); if (!(entity instanceof EntityGadget gadget)) { return 1; } @@ -1477,7 +1481,7 @@ public class ScriptLib { // var3 might contain the next point, sometimes is a single int, sometimes multiple ints as array // var4 has RouteType route_type, bool turn_mode - val entity = getSceneScriptManager().getScene().getEntityByConfigId(entityConfigId); + val entity = getSceneScriptManager().getScene().getEntityByConfigId(entityConfigId, getCurrentGroup().get().id); if (entity == null) { return 1; } @@ -1507,7 +1511,7 @@ public class ScriptLib { public int SetPlatformRouteId(int entityConfigId, int routeId) { logger.debug("[LUA] Call SetPlatformRouteId {} {}", entityConfigId, routeId); - val entity = getSceneScriptManager().getScene().getEntityByConfigId(entityConfigId); + val entity = getSceneScriptManager().getScene().getEntityByConfigId(entityConfigId, getCurrentGroup().get().id); if (entity == null) { return 1; } @@ -1584,7 +1588,7 @@ public class ScriptLib { return 1; } var configId = callParams.param1; - var entity = getSceneScriptManager().getScene().getEntityByConfigId(configId); + var entity = getSceneScriptManager().getScene().getEntityByConfigId(configId, getCurrentGroup().get().id); var worktopOptions = new int[table.length()]; for (int i = 1; i <= table.length(); i++) { @@ -1679,7 +1683,7 @@ public class ScriptLib { public int StartPlatform(int configId) { logger.debug("[LUA] Call StartPlatform {} ", configId); - val entity = sceneScriptManager.get().getScene().getEntityByConfigId(configId); + val entity = sceneScriptManager.get().getScene().getEntityByConfigId(configId, getCurrentGroup().get().id); if (!(entity instanceof EntityGadget entityGadget)) { return 1; } @@ -1714,7 +1718,7 @@ public class ScriptLib { public int StopPlatform(int configId) { logger.debug("[LUA] Call StopPlatform {} ", configId); - val entity = sceneScriptManager.get().getScene().getEntityByConfigId(configId); + val entity = sceneScriptManager.get().getScene().getEntityByConfigId(configId, getCurrentGroup().get().id); if (!(entity instanceof EntityGadget entityGadget)) { return 1; } @@ -1787,7 +1791,19 @@ public class ScriptLib { // TODO: TryRecordActivityPushTips // TODO: TrySetPlayerEyePoint - // TODO: UnfreezeGroupLimit + + public int UnfreezeGroupLimit(int dungeonEntryId) { + // Note: dungeonEntryId is also named pointId elsewhere in GC. + logger.debug("[LUA] Call UnfreezeGroupLimit with {}", dungeonEntryId); + + var scene = sceneScriptManager.get().getScene(); + scene.getPlayers().get(0).sendPacket(new PacketUnfreezeGroupLimitNotify( + dungeonEntryId, + scene.getId())); + + return 0; + } + // TODO: UnhideScenePoint public int UnlockFloatSignal(int groupId, int gadgetSignalId) { diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerDungeonRestartReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerDungeonRestartReq.java new file mode 100644 index 000000000..aabde3ff9 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerDungeonRestartReq.java @@ -0,0 +1,15 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.net.packet.*; +import emu.grasscutter.net.proto.DungeonRestartReqOuterClass.DungeonRestartReq; +import emu.grasscutter.net.proto.DungeonRestartRspOuterClass.DungeonRestartRsp; +import emu.grasscutter.server.game.GameSession; + +@Opcodes(PacketOpcodes.DungeonRestartReq) +public class HandlerDungeonRestartReq extends PacketHandler { + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + session.getPlayer().getServer().getDungeonSystem().restartDungeon(session.getPlayer()); + session.getPlayer().sendPacket(new BasePacket(PacketOpcodes.DungeonRestartRsp)); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetDungeonEntryExploreConditionReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetDungeonEntryExploreConditionReq.java new file mode 100644 index 000000000..e16d6623d --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetDungeonEntryExploreConditionReq.java @@ -0,0 +1,27 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.net.packet.*; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketGetDungeonEntryExploreConditionRsp; +import emu.grasscutter.server.packet.send.PacketDungeonEntryToBeExploreNotify; +import emu.grasscutter.net.proto.GetDungeonEntryExploreConditionReqOuterClass.GetDungeonEntryExploreConditionReq; + +@Opcodes(PacketOpcodes.GetDungeonEntryExploreConditionReq) +public class HandlerGetDungeonEntryExploreConditionReq extends PacketHandler { + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + var req = GetDungeonEntryExploreConditionReq.parseFrom(payload); + + // TODO Send GetDungeonEntryExploreConditionRsp if condition + // (adventurer rank or quest completion) is not met. Parse + // dungeon entry conditions from DungeonEntryExcelConfigData.json. + //session.send(new PacketGetDungeonEntryExploreConditionRsp(req.getDungeonEntryConfigId())); + + // For now, just unlock any domain the player touches. + session.send(new PacketDungeonEntryToBeExploreNotify( + req.getDungeonEntryScenePointId(), + req.getSceneId(), + req.getDungeonEntryConfigId())); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonEntryToBeExploreNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonEntryToBeExploreNotify.java new file mode 100644 index 000000000..3919a79b7 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonEntryToBeExploreNotify.java @@ -0,0 +1,20 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.*; +import emu.grasscutter.net.proto.DungeonEntryToBeExploreNotifyOuterClass.DungeonEntryToBeExploreNotify; + +public class PacketDungeonEntryToBeExploreNotify extends BasePacket { + + /** + * Marks the dungeon as pending exploration. + * This creates the "Unknown" text bubble above the dungeon entry in the world map. + */ + public PacketDungeonEntryToBeExploreNotify(int dungeonEntryScenePointId, int sceneId, int dungeonEntryConfigId) { + super(PacketOpcodes.DungeonEntryToBeExploreNotify); + this.setData(DungeonEntryToBeExploreNotify.newBuilder() + .setDungeonEntryScenePointId(dungeonEntryScenePointId) + .setSceneId(sceneId) + .setDungeonEntryConfigId(dungeonEntryConfigId) + ); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketGetDungeonEntryExploreConditionRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketGetDungeonEntryExploreConditionRsp.java new file mode 100644 index 000000000..61c23d14d --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketGetDungeonEntryExploreConditionRsp.java @@ -0,0 +1,31 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.data.GameData; +import emu.grasscutter.net.packet.*; +import emu.grasscutter.net.proto.GetDungeonEntryExploreConditionRspOuterClass.GetDungeonEntryExploreConditionRsp; +import emu.grasscutter.net.proto.DungeonEntryCondOuterClass.DungeonEntryCond; +import emu.grasscutter.net.proto.DungeonEntryBlockReasonOuterClass.DungeonEntryBlockReason; + +public class PacketGetDungeonEntryExploreConditionRsp extends BasePacket { + public PacketGetDungeonEntryExploreConditionRsp(int dungeonId) { + super(PacketOpcodes.GetDungeonEntryExploreConditionRsp); + + var data = GameData.getDungeonEntryDataMap().values().stream() + .filter(d -> d.getId() == dungeonId) + .toList().get(0); + + var level = data.getLevelCondition(); + var quest = data.getQuestCondition(); + var proto = GetDungeonEntryExploreConditionRsp.newBuilder() + .setRetcode(0) + .setDungeonEntryCond(DungeonEntryCond.newBuilder() + // There is also a DUNGEON_ENTRY_REASON_MULIPLE but only one param1 + // field to put values in. Only report the required level for now, then. + .setCondReason(DungeonEntryBlockReason.DUNGEON_ENTRY_REASON_LEVEL) + .setParam1(level) + ) + .build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPostEnterSceneRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPostEnterSceneRsp.java index 67a94d4ba..d0893de01 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketPostEnterSceneRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPostEnterSceneRsp.java @@ -12,6 +12,11 @@ public class PacketPostEnterSceneRsp extends BasePacket { PostEnterSceneRsp p = PostEnterSceneRsp.newBuilder().setEnterSceneToken(player.getEnterSceneToken()).build(); + // + // On moving to new scene: + // Unfreeze dungeon entry points that have already been unlocked in this scene. + player.unfreezeUnlockedScenePoints(); + this.setData(p); } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketUnfreezeGroupLimitNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketUnfreezeGroupLimitNotify.java new file mode 100644 index 000000000..a6993c04d --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketUnfreezeGroupLimitNotify.java @@ -0,0 +1,14 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.*; +import emu.grasscutter.net.proto.UnfreezeGroupLimitNotifyOuterClass.UnfreezeGroupLimitNotify; + +public class PacketUnfreezeGroupLimitNotify extends BasePacket { + public PacketUnfreezeGroupLimitNotify(int pointId, int sceneId) { + super(PacketOpcodes.UnfreezeGroupLimitNotify); + this.setData(UnfreezeGroupLimitNotify.newBuilder() + .setPointId(pointId) + .setSceneId(sceneId) + ); + } +}