diff --git a/src/main/java/emu/grasscutter/data/GameData.java b/src/main/java/emu/grasscutter/data/GameData.java index c187a1819..6572ab673 100644 --- a/src/main/java/emu/grasscutter/data/GameData.java +++ b/src/main/java/emu/grasscutter/data/GameData.java @@ -66,7 +66,8 @@ public class GameData { private static final Int2ObjectMap dailyDungeonDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap dungeonDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap shopGoodsDataMap = new Int2ObjectOpenHashMap<>(); - + private static final Int2ObjectMap rewardPreviewDataMap = new Int2ObjectOpenHashMap<>(); + // Cache private static Map> fetters = new HashMap<>(); private static Map> shopGoods = new HashMap<>(); @@ -300,6 +301,13 @@ public class GameData { return shopGoods; } + /** + * @return the rewardpreviewdatamap + */ + public static Int2ObjectMap getRewardPreviewDataMap() { + return rewardPreviewDataMap; + } + public static IntList getScenePointIdList() { return scenePointIdList; } diff --git a/src/main/java/emu/grasscutter/data/common/ItemParamStringData.java b/src/main/java/emu/grasscutter/data/common/ItemParamStringData.java new file mode 100644 index 000000000..5fad12511 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/common/ItemParamStringData.java @@ -0,0 +1,26 @@ +package emu.grasscutter.data.common; + +public class ItemParamStringData { + private int Id; + private String Count; + + public ItemParamStringData() {} + + public int getId() { + return Id; + } + + public String getCount() { + return Count; + } + + public ItemParamData toItemParamData() { + if (Count.contains(";")) { + String[] split = Count.split(";"); + Count = Count.split(";")[split.length - 1]; + } else if (Count.contains(".")) { + return new ItemParamData(Id, (int) Math.ceil(Double.parseDouble(Count))); + } + return new ItemParamData(Id, Integer.parseInt(Count)); + } +} diff --git a/src/main/java/emu/grasscutter/data/def/DungeonData.java b/src/main/java/emu/grasscutter/data/def/DungeonData.java index 311286dca..086cbb6bf 100644 --- a/src/main/java/emu/grasscutter/data/def/DungeonData.java +++ b/src/main/java/emu/grasscutter/data/def/DungeonData.java @@ -11,7 +11,10 @@ public class DungeonData extends GameResource { private int Id; private int SceneId; private int ShowLevel; + private int PassRewardPreviewID; private String InvolveType; // TODO enum + + private RewardPreviewData previewData; @Override public int getId() { @@ -26,8 +29,14 @@ public class DungeonData extends GameResource { return ShowLevel; } + public RewardPreviewData getRewardPreview() { + return previewData; + } + @Override public void onLoad() { - + if (this.PassRewardPreviewID > 0) { + this.previewData = GameData.getRewardPreviewDataMap().get(this.PassRewardPreviewID); + } } } diff --git a/src/main/java/emu/grasscutter/data/def/RewardPreviewData.java b/src/main/java/emu/grasscutter/data/def/RewardPreviewData.java new file mode 100644 index 000000000..f971ee1cf --- /dev/null +++ b/src/main/java/emu/grasscutter/data/def/RewardPreviewData.java @@ -0,0 +1,43 @@ +package emu.grasscutter.data.def; + +import java.util.Arrays; +import java.util.List; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; + +import emu.grasscutter.game.props.SceneType; + +import emu.grasscutter.data.ResourceType.LoadPriority; +import emu.grasscutter.data.common.ItemParamData; +import emu.grasscutter.data.common.ItemParamStringData; + +@ResourceType(name = "RewardPreviewExcelConfigData.json", loadPriority = LoadPriority.HIGH) +public class RewardPreviewData extends GameResource { + private int Id; + private ItemParamStringData[] PreviewItems; + private ItemParamData[] PreviewItemsArray; + + @Override + public int getId() { + return this.Id; + } + + public ItemParamData[] getPreviewItems() { + return PreviewItemsArray; + } + + @Override + public void onLoad() { + if (this.PreviewItems != null && this.PreviewItems.length > 0) { + this.PreviewItemsArray = Arrays.stream(this.PreviewItems) + .filter(d -> d.getId() > 0 && d.getCount() != null && !d.getCount().isEmpty()) + .map(ItemParamStringData::toItemParamData) + .toArray(size -> new ItemParamData[size]); + } else { + this.PreviewItemsArray = new ItemParamData[0]; + } + } +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/DungeonChallenge.java b/src/main/java/emu/grasscutter/game/dungeons/DungeonChallenge.java index 608587765..f468fcd23 100644 --- a/src/main/java/emu/grasscutter/game/dungeons/DungeonChallenge.java +++ b/src/main/java/emu/grasscutter/game/dungeons/DungeonChallenge.java @@ -4,18 +4,29 @@ import java.util.ArrayList; import java.util.List; import emu.grasscutter.data.GameData; +import emu.grasscutter.data.common.ItemParamData; +import emu.grasscutter.data.def.DungeonData; import emu.grasscutter.data.def.MonsterData; import emu.grasscutter.game.entity.EntityMonster; import emu.grasscutter.game.entity.GameEntity; +import emu.grasscutter.game.inventory.GameItem; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.props.ActionReason; 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.scripts.data.ScriptArgs; 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.PacketDungeonSettleNotify; +import emu.grasscutter.server.packet.send.PacketGadgetAutoPickDropInfoNotify; import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify; +import emu.grasscutter.utils.Utils; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntSet; public class DungeonChallenge { private final Scene scene; @@ -28,12 +39,12 @@ public class DungeonChallenge { private int score; private int objective = 0; + private IntSet rewardedPlayers; public DungeonChallenge(Scene scene, SceneGroup group) { this.scene = scene; this.group = group; - - objective += group.monsters.size(); + this.setRewardedPlayers(new IntOpenHashSet()); } public Scene getScene() { @@ -60,6 +71,14 @@ public class DungeonChallenge { this.challengeId = challengeId; } + public int getObjective() { + return objective; + } + + public void setObjective(int objective) { + this.objective = objective; + } + public boolean isSuccess() { return success; } @@ -75,6 +94,18 @@ public class DungeonChallenge { public int getScore() { return score; } + + public int getTimeLimit() { + return 600; + } + + public IntSet getRewardedPlayers() { + return rewardedPlayers; + } + + public void setRewardedPlayers(IntSet rewardedPlayers) { + this.rewardedPlayers = rewardedPlayers; + } public void start() { this.progress = true; @@ -83,23 +114,57 @@ public class DungeonChallenge { public void finish() { this.progress = false; + getScene().broadcastPacket(new PacketDungeonChallengeFinishNotify(this)); if (this.isSuccess()) { + // Call success script event this.getScene().getScriptManager().callEvent(EventType.EVENT_CHALLENGE_SUCCESS, null); + + // Settle + settle(); } else { this.getScene().getScriptManager().callEvent(EventType.EVENT_CHALLENGE_FAIL, null); } } + + private void settle() { + getScene().setAutoCloseTime(Utils.getCurrentSeconds() + 1000); + getScene().broadcastPacket(new PacketDungeonSettleNotify(this)); + + getScene().getScriptManager().callEvent(EventType.EVENT_DUNGEON_SETTLE, new ScriptArgs(this.isSuccess() ? 1 : 0)); + } public void onMonsterDie(EntityMonster entity) { score = getScore() + 1; getScene().broadcastPacket(new PacketChallengeDataNotify(this, 1, getScore())); - if (getScore() >= objective) { + if (getScore() >= getObjective()) { this.setSuccess(true); finish(); } } + + public void getStatueDrops(Player player) { + DungeonData dungeonData = getScene().getDungeonData(); + if (!isSuccess() || dungeonData == null || dungeonData.getRewardPreview() == null || dungeonData.getRewardPreview().getPreviewItems().length == 0) { + return; + } + + // Already rewarded + if (getRewardedPlayers().contains(player.getUid())) { + return; + } + + List rewards = new ArrayList<>(); + for (ItemParamData param : getScene().getDungeonData().getRewardPreview().getPreviewItems()) { + rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1))); + } + + player.getInventory().addItems(rewards, ActionReason.DungeonStatueDrop); + player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(rewards)); + + getRewardedPlayers().add(player.getUid()); + } } diff --git a/src/main/java/emu/grasscutter/game/inventory/GameItem.java b/src/main/java/emu/grasscutter/game/inventory/GameItem.java index ddc61988a..9fbc1e8c1 100644 --- a/src/main/java/emu/grasscutter/game/inventory/GameItem.java +++ b/src/main/java/emu/grasscutter/game/inventory/GameItem.java @@ -415,12 +415,6 @@ public class GameItem { Reliquary relic = this.toReliquaryProto(); proto.setEquip(Equip.newBuilder().setReliquary(relic).setIsLocked(this.isLocked()).build()); break; - case ITEM_MATERIAL: - Material material = Material.newBuilder() - .setCount(getCount()) - .build(); - proto.setMaterial(material); - break; case ITEM_FURNITURE: Furniture furniture = Furniture.newBuilder() .setCount(getCount()) @@ -428,6 +422,10 @@ public class GameItem { proto.setFurniture(furniture); break; default: + Material material = Material.newBuilder() + .setCount(getCount()) + .build(); + proto.setMaterial(material); break; } diff --git a/src/main/java/emu/grasscutter/game/player/Player.java b/src/main/java/emu/grasscutter/game/player/Player.java index 93182b15a..e4c59e38f 100644 --- a/src/main/java/emu/grasscutter/game/player/Player.java +++ b/src/main/java/emu/grasscutter/game/player/Player.java @@ -11,6 +11,7 @@ import emu.grasscutter.game.CoopRequest; import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.avatar.AvatarProfileData; import emu.grasscutter.game.avatar.AvatarStorage; +import emu.grasscutter.game.entity.EntityGadget; import emu.grasscutter.game.entity.EntityItem; import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.game.friends.FriendsList; @@ -21,6 +22,7 @@ import emu.grasscutter.game.inventory.Inventory; import emu.grasscutter.game.mail.Mail; import emu.grasscutter.game.mail.MailHandler; import emu.grasscutter.game.props.ActionReason; +import emu.grasscutter.game.props.EntityType; import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.game.shop.ShopLimit; import emu.grasscutter.game.world.Scene; @@ -785,6 +787,16 @@ public class Player { else this.getScene().broadcastPacket(new PacketGadgetInteractRsp(drop, InteractType.INTERACT_PICK_ITEM)); } + } else if (entity instanceof EntityGadget) { + EntityGadget gadget = (EntityGadget) entity; + + if (gadget.getGadgetData().getType() == EntityType.RewardStatue) { + if (scene.getChallenge() != null) { + scene.getChallenge().getStatueDrops(this); + } + + this.sendPacket(new PacketGadgetInteractRsp(gadget, InteractType.INTERACT_OPEN_STATUE)); + } } else { // Delete directly entity.getScene().removeEntity(entity); diff --git a/src/main/java/emu/grasscutter/game/world/Scene.java b/src/main/java/emu/grasscutter/game/world/Scene.java index 2e3afc087..7aa4d3f8a 100644 --- a/src/main/java/emu/grasscutter/game/world/Scene.java +++ b/src/main/java/emu/grasscutter/game/world/Scene.java @@ -49,6 +49,7 @@ public class Scene { private final Set loadedBlocks; private boolean dontDestroyWhenEmpty; + private int autoCloseTime; private int time; private ClimateType climate; private int weather; @@ -107,6 +108,20 @@ public class Scene { return this.entities.get(id); } + /** + * @return the autoCloseTime + */ + public int getAutoCloseTime() { + return autoCloseTime; + } + + /** + * @param autoCloseTime the autoCloseTime to set + */ + public void setAutoCloseTime(int autoCloseTime) { + this.autoCloseTime = autoCloseTime; + } + public int getTime() { return time; } @@ -520,8 +535,22 @@ public class Scene { } // Spawn gadgets AFTER triggers are added + // TODO for (SceneGroup group : block.groups) { - this.getScriptManager().spawnGadgetsInGroup(group); + if (group.init_config == null) { + continue; + } + + int suite = group.init_config.suite; + + if (suite == 0) { + continue; + } + + do { + this.getScriptManager().spawnGadgetsInGroup(group, suite); + suite++; + } while (suite < group.init_config.end_suite); } } diff --git a/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java b/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java index f2f892de3..1784c827b 100644 --- a/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java +++ b/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java @@ -234,14 +234,31 @@ public class SceneScriptManager { 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)); + Int2ObjectMap map = new Int2ObjectOpenHashMap<>(); + group.monsters.forEach(m -> map.put(m.config_id, m)); + group.gadgets.forEach(m -> map.put(m.config_id, 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); + try { + SceneMonster monster = (SceneMonster) map.get(id); + if (monster != null) { + suite.sceneMonsters.add(monster); + } + } catch (Exception e) { + continue; + } + } + suite.sceneGadgets = new ArrayList<>(suite.gadgets.size()); + for (int id : suite.gadgets) { + try { + SceneGadget gadget = (SceneGadget) map.get(id); + if (gadget != null) { + suite.sceneGadgets.add(gadget); + } + } catch (Exception e) { + continue; } } } @@ -274,8 +291,22 @@ public class SceneScriptManager { } } + public void spawnGadgetsInGroup(SceneGroup group, int suiteIndex) { + spawnGadgetsInGroup(group, group.getSuiteByIndex(suiteIndex)); + } + public void spawnGadgetsInGroup(SceneGroup group) { - for (SceneGadget g : group.gadgets) { + spawnGadgetsInGroup(group, null); + } + + public void spawnGadgetsInGroup(SceneGroup group, SceneSuite suite) { + List gadgets = group.gadgets; + + if (suite != null) { + gadgets = suite.sceneGadgets; + } + + for (SceneGadget g : gadgets) { EntityGadget entity = new EntityGadget(getScene(), g.gadget_id, g.pos); if (entity.getGadgetData() == null) continue; diff --git a/src/main/java/emu/grasscutter/scripts/ScriptLib.java b/src/main/java/emu/grasscutter/scripts/ScriptLib.java index 5dfe644be..e75920579 100644 --- a/src/main/java/emu/grasscutter/scripts/ScriptLib.java +++ b/src/main/java/emu/grasscutter/scripts/ScriptLib.java @@ -136,7 +136,7 @@ public class ScriptLib { } // param3 (probably time limit for timed dungeons) - public int ActiveChallenge(int challengeId, int challengeIndex, int param3, int groupId, int param4, int param5) { + public int ActiveChallenge(int challengeId, int challengeIndex, int param3, int groupId, int objectiveKills, int param5) { SceneGroup group = getSceneScriptManager().getGroupById(groupId); if (group == null || group.monsters == null) { @@ -146,6 +146,7 @@ public class ScriptLib { DungeonChallenge challenge = new DungeonChallenge(getSceneScriptManager().getScene(), group); challenge.setChallengeId(challengeId); challenge.setChallengeIndex(challengeIndex); + challenge.setObjective(objectiveKills); getSceneScriptManager().getScene().setChallenge(challenge); @@ -163,8 +164,13 @@ public class ScriptLib { return getSceneScriptManager().getVariables().getOrDefault(var, 0); } - public LuaValue ChangeGroupVariableValue(String var, int value) { + public int SetGroupVariableValue(String var, int value) { getSceneScriptManager().getVariables().put(var, value); + return 0; + } + + public LuaValue ChangeGroupVariableValue(String var, int value) { + getSceneScriptManager().getVariables().put(var, getSceneScriptManager().getVariables().get(var) + value); return LuaValue.ZERO; } @@ -179,8 +185,8 @@ public class ScriptLib { return 1; } - // TODO just spawn all from group for now this.getSceneScriptManager().spawnMonstersInGroup(group, suite); + this.getSceneScriptManager().spawnGadgetsInGroup(group, suite); return 0; } diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneSuite.java b/src/main/java/emu/grasscutter/scripts/data/SceneSuite.java index d84504569..90433b3d4 100644 --- a/src/main/java/emu/grasscutter/scripts/data/SceneSuite.java +++ b/src/main/java/emu/grasscutter/scripts/data/SceneSuite.java @@ -6,8 +6,10 @@ import emu.grasscutter.utils.Position; public class SceneSuite { public List monsters; + public List gadgets; public List triggers; public int rand_weight; public transient List sceneMonsters; + public transient List sceneGadgets; } 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 2b8112728..0c52c9d04 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonChallengeBeginNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonChallengeBeginNotify.java @@ -8,12 +8,14 @@ import emu.grasscutter.net.proto.DungeonChallengeBeginNotifyOuterClass.DungeonCh public class PacketDungeonChallengeBeginNotify extends BasePacket { public PacketDungeonChallengeBeginNotify(DungeonChallenge challenge) { - super(PacketOpcodes.DungeonChallengeBeginNotify); + super(PacketOpcodes.DungeonChallengeBeginNotify, true); DungeonChallengeBeginNotify proto = DungeonChallengeBeginNotify.newBuilder() .setChallengeId(challenge.getChallengeId()) .setChallengeIndex(challenge.getChallengeIndex()) .setGroupId(challenge.getGroup().id) + .addParamList(challenge.getObjective()) + .addParamList(challenge.getTimeLimit()) .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 0fbcd9570..a44c16778 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonChallengeFinishNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonChallengeFinishNotify.java @@ -8,13 +8,12 @@ import emu.grasscutter.net.proto.DungeonChallengeFinishNotifyOuterClass.DungeonC public class PacketDungeonChallengeFinishNotify extends BasePacket { public PacketDungeonChallengeFinishNotify(DungeonChallenge challenge) { - super(PacketOpcodes.DungeonChallengeFinishNotify); + super(PacketOpcodes.DungeonChallengeFinishNotify, true); DungeonChallengeFinishNotify proto = DungeonChallengeFinishNotify.newBuilder() .setChallengeIndex(challenge.getChallengeIndex()) .setIsSuccess(challenge.isSuccess()) - .setUnk1(challenge.getChallengeId()) - .setUnk2(30) + .setUnk1(2) .build(); this.setData(proto); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonSettleNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonSettleNotify.java new file mode 100644 index 000000000..411be291a --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonSettleNotify.java @@ -0,0 +1,22 @@ +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.DungeonSettleNotifyOuterClass.DungeonSettleNotify; + +public class PacketDungeonSettleNotify extends BasePacket { + + public PacketDungeonSettleNotify(DungeonChallenge challenge) { + super(PacketOpcodes.DungeonSettleNotify); + + DungeonSettleNotify proto = DungeonSettleNotify.newBuilder() + .setDungeonId(challenge.getScene().getDungeonData().getId()) + .setIsSuccess(challenge.isSuccess()) + .setCloseTime(challenge.getScene().getAutoCloseTime()) + .setResult(challenge.isSuccess() ? 1 : 0) + .build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketGadgetAutoPickDropInfoNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketGadgetAutoPickDropInfoNotify.java new file mode 100644 index 000000000..1e0034713 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketGadgetAutoPickDropInfoNotify.java @@ -0,0 +1,22 @@ +package emu.grasscutter.server.packet.send; + +import java.util.Collection; + +import emu.grasscutter.game.inventory.GameItem; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.GadgetAutoPickDropInfoNotifyOuterClass.GadgetAutoPickDropInfoNotify; +import emu.grasscutter.net.proto.GadgetAutoPickDropInfoNotifyOuterClass.GadgetAutoPickDropInfoNotify.Builder; + +public class PacketGadgetAutoPickDropInfoNotify extends BasePacket { + + public PacketGadgetAutoPickDropInfoNotify(Collection items) { + super(PacketOpcodes.GadgetAutoPickDropInfoNotify); + + GadgetAutoPickDropInfoNotify.Builder proto = GadgetAutoPickDropInfoNotify.newBuilder(); + + items.forEach(item -> proto.addItemList(item.toProto())); + + this.setData(proto); + } +}