From 3fe800c67934771ff23adc2803c4f839fef2031f Mon Sep 17 00:00:00 2001 From: dragon Date: Fri, 18 Nov 2022 22:54:09 +0800 Subject: [PATCH 1/8] Implement new drop system. Full chest drop support. Implement new drop system. Provide a more official-like experience. --- .../command/commands/ReloadCommand.java | 2 +- .../java/emu/grasscutter/data/GameData.java | 2 + .../grasscutter/data/common/DropItemData.java | 12 + .../data/excels/DropMaterialData.java | 18 ++ .../data/excels/DropTableData.java | 25 ++ .../grasscutter/game/drop/ChestDropData.java | 13 + .../emu/grasscutter/game/drop/DropSystem.java | 235 +++++++++++++----- .../game/drop/DropSystemLegacy.java | 106 ++++++++ .../game/entity/gadget/GadgetChest.java | 31 ++- .../grasscutter/game/inventory/Inventory.java | 2 +- .../emu/grasscutter/game/world/Scene.java | 45 +++- .../grasscutter/scripts/data/SceneGadget.java | 10 + .../grasscutter/server/game/GameServer.java | 7 +- .../packet/send/PacketDropHintNotify.java | 23 ++ 14 files changed, 450 insertions(+), 81 deletions(-) create mode 100644 src/main/java/emu/grasscutter/data/common/DropItemData.java create mode 100644 src/main/java/emu/grasscutter/data/excels/DropMaterialData.java create mode 100644 src/main/java/emu/grasscutter/data/excels/DropTableData.java create mode 100644 src/main/java/emu/grasscutter/game/drop/ChestDropData.java create mode 100644 src/main/java/emu/grasscutter/game/drop/DropSystemLegacy.java create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketDropHintNotify.java diff --git a/src/main/java/emu/grasscutter/command/commands/ReloadCommand.java b/src/main/java/emu/grasscutter/command/commands/ReloadCommand.java index 713acccf..e9efb94e 100644 --- a/src/main/java/emu/grasscutter/command/commands/ReloadCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/ReloadCommand.java @@ -19,7 +19,7 @@ public final class ReloadCommand implements CommandHandler { Grasscutter.loadConfig(); Grasscutter.loadLanguage(); Grasscutter.getGameServer().getGachaSystem().load(); - Grasscutter.getGameServer().getDropSystem().load(); + Grasscutter.getGameServer().getDropSystemLegacy().load(); Grasscutter.getGameServer().getShopSystem().load(); CommandHandler.sendMessage(sender, translate(sender, "commands.reload.reload_done")); diff --git a/src/main/java/emu/grasscutter/data/GameData.java b/src/main/java/emu/grasscutter/data/GameData.java index ea0801d3..0f2196b3 100644 --- a/src/main/java/emu/grasscutter/data/GameData.java +++ b/src/main/java/emu/grasscutter/data/GameData.java @@ -70,6 +70,8 @@ public class GameData { @Getter private static final Int2ObjectMap cookRecipeDataMap = new Int2ObjectOpenHashMap<>(); @Getter private static final Int2ObjectMap compoundDataMap=new Int2ObjectOpenHashMap<>(); @Getter private static final Int2ObjectMap dailyDungeonDataMap = new Int2ObjectOpenHashMap<>(); + @Getter private static final Int2ObjectMap dropTableDataMap=new Int2ObjectOpenHashMap<>(); + @Getter private static final Int2ObjectMap dropMaterialDataMap=new Int2ObjectOpenHashMap<>(); @Getter private static final Int2ObjectMap dungeonDataMap = new Int2ObjectOpenHashMap<>(); @Getter private static final Int2ObjectMap dungeonEntryDataMap = new Int2ObjectOpenHashMap<>(); @Getter private static final Int2ObjectMap envAnimalGatherConfigDataMap = new Int2ObjectOpenHashMap<>(); diff --git a/src/main/java/emu/grasscutter/data/common/DropItemData.java b/src/main/java/emu/grasscutter/data/common/DropItemData.java new file mode 100644 index 00000000..6c384235 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/common/DropItemData.java @@ -0,0 +1,12 @@ +package emu.grasscutter.data.common; + +import com.google.gson.annotations.SerializedName; +import lombok.Getter; + +@Getter +public class DropItemData { + @SerializedName(value="itemId") + private int id; + private String countRange; + private int weight; +} diff --git a/src/main/java/emu/grasscutter/data/excels/DropMaterialData.java b/src/main/java/emu/grasscutter/data/excels/DropMaterialData.java new file mode 100644 index 00000000..c6991c83 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/DropMaterialData.java @@ -0,0 +1,18 @@ +package emu.grasscutter.data.excels; + +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; +import lombok.Getter; + +@ResourceType(name = "DropMaterialExcelConfigData.json") +@Getter +public class DropMaterialData extends GameResource { + @Getter(onMethod = @__(@Override)) + private int id; + private boolean useOnGain; + private boolean disableFirstGainHint; + private boolean autoPick; + private boolean dropSeparately; + private int groupId; + private boolean forceGainHint; +} diff --git a/src/main/java/emu/grasscutter/data/excels/DropTableData.java b/src/main/java/emu/grasscutter/data/excels/DropTableData.java new file mode 100644 index 00000000..ad600fca --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/DropTableData.java @@ -0,0 +1,25 @@ +package emu.grasscutter.data.excels; + +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; +import emu.grasscutter.data.ResourceType.LoadPriority; +import emu.grasscutter.data.common.DropItemData; +import lombok.Getter; + +import java.util.List; + +@ResourceType(name ={"DropTableExcelConfigData.json","DropSubTableExcelConfigData.json"} , loadPriority = LoadPriority.HIGH) +@Getter +public class DropTableData extends GameResource { + @Getter(onMethod = @__(@Override)) + private int id; + private int randomType; + private int dropLevel; + private List dropVec; + private int nodeType; + private boolean fallToGround; + private int sourceType; + private int everydayLimit; + private int historyLimit; + private int activityLimit; +} diff --git a/src/main/java/emu/grasscutter/game/drop/ChestDropData.java b/src/main/java/emu/grasscutter/game/drop/ChestDropData.java new file mode 100644 index 00000000..e2427a94 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/drop/ChestDropData.java @@ -0,0 +1,13 @@ +package emu.grasscutter.game.drop; + +import lombok.Getter; + +@Getter +public class ChestDropData { + private int minLevel; + private String index; + private int dropId; + private int dropCount; + private int sourceType; + private String type; +} diff --git a/src/main/java/emu/grasscutter/game/drop/DropSystem.java b/src/main/java/emu/grasscutter/game/drop/DropSystem.java index 053dcbed..d33f8c20 100644 --- a/src/main/java/emu/grasscutter/game/drop/DropSystem.java +++ b/src/main/java/emu/grasscutter/game/drop/DropSystem.java @@ -3,104 +3,203 @@ package emu.grasscutter.game.drop; import emu.grasscutter.Grasscutter; import emu.grasscutter.data.DataLoader; import emu.grasscutter.data.GameData; -import emu.grasscutter.data.excels.ItemData; -import emu.grasscutter.game.entity.EntityItem; -import emu.grasscutter.game.entity.EntityMonster; +import emu.grasscutter.data.common.DropItemData; +import emu.grasscutter.data.excels.DropMaterialData; +import emu.grasscutter.data.excels.DropTableData; +import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.inventory.ItemType; import emu.grasscutter.game.player.Player; import emu.grasscutter.game.props.ActionReason; -import emu.grasscutter.game.world.Scene; import emu.grasscutter.server.game.BaseGameSystem; import emu.grasscutter.server.game.GameServer; -import emu.grasscutter.utils.Position; -import emu.grasscutter.utils.Utils; +import emu.grasscutter.server.packet.send.PacketDropHintNotify; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import java.util.List; +import java.util.*; public class DropSystem extends BaseGameSystem { - private final Int2ObjectMap> dropData; + private final Int2ObjectMap dropTable; + private final Map> chestReward; + private final Random rand; public DropSystem(GameServer server) { super(server); - this.dropData = new Int2ObjectOpenHashMap<>(); - this.load(); - } - - public Int2ObjectMap> getDropData() { - return dropData; - } - - public synchronized void load() { - getDropData().clear(); + rand = new Random(); + dropTable = GameData.getDropTableDataMap(); + chestReward = new HashMap<>(); try { - List banners = DataLoader.loadList("Drop.json", DropInfo.class); - if (banners.size() > 0) { - for (DropInfo di : banners) { - getDropData().put(di.getMonsterId(), di.getDropDataList()); + List dataList = DataLoader.loadList("ChestDrop.json", ChestDropData.class); + for (var i : dataList) { + if (!chestReward.containsKey(i.getIndex())) { + chestReward.put(i.getIndex(), new ArrayList<>()); } - Grasscutter.getLogger().debug("Drop data successfully loaded."); - } else { - Grasscutter.getLogger().error("Unable to load drop data. Drop data size is 0."); + chestReward.get(i.getIndex()).add(i); } } catch (Exception e) { - Grasscutter.getLogger().error("Unable to load drop data.", e); + Grasscutter.getLogger().error("Unable to load chest drop data.Please place ChestDrop.json in data folder."); } + } - private void addDropEntity(DropData dd, Scene dropScene, ItemData itemData, Position pos, int num, Player target) { - if (!dd.isGive() && (itemData.getItemType() != ItemType.ITEM_VIRTUAL || itemData.getGadgetId() != 0)) { - EntityItem entity = new EntityItem(dropScene, target, itemData, pos, num, dd.isShare()); - if (!dd.isShare()) - dropScene.addEntityToSingleClient(target, entity); - else - dropScene.addEntity(entity); + + public boolean handleChestDrop(int chestDropId, int dropCount, GameEntity bornFrom) { + Grasscutter.getLogger().info("ChestDrop:chest_drop_id={},drop_count={}", chestDropId, dropCount); + return processDrop(chestDropId, dropCount, ActionReason.OpenChest, bornFrom, bornFrom.getWorld().getHost(), false); + } + + public boolean handleChestDrop(String dropTag, int level, GameEntity bornFrom) { + Grasscutter.getLogger().info("ChestDrop:drop_tag={},level={}", dropTag, level); + if (!chestReward.containsKey(dropTag)) return false; + var rewardList = chestReward.get(dropTag); + ChestDropData dropData = null; + int minLevel = 0; + for (var i : rewardList) { + if (level >= i.getMinLevel() && i.getMinLevel() > minLevel) { + minLevel = i.getMinLevel(); + dropData = i; + } + } + if (dropData == null) return false; + return processDrop(dropData.getDropId(), dropData.getDropCount(), ActionReason.OpenChest, bornFrom, bornFrom.getWorld().getHost(), false); + } + + private boolean processDrop(int dropId, int count, ActionReason reason, GameEntity bornFrom, Player player, boolean share) { + if (!dropTable.containsKey(dropId)) return false; + var dropData = dropTable.get(dropId); + if (dropData.getNodeType() != 1) return false; + List items = new ArrayList<>(); + processSubDrop(dropData, count, items); + if (dropData.isFallToGround()) { + dropItems(items, reason, bornFrom, player, share); } else { - if (target != null) { - target.getInventory().addItem(new GameItem(itemData, num), ActionReason.SubfieldDrop, true); - } else { - // target is null if items will be added are shared. no one could pick it up because of the combination(give + shared) - // so it will be sent to all players' inventories directly. - dropScene.getPlayers().forEach(x -> x.getInventory().addItem(new GameItem(itemData, num), ActionReason.SubfieldDrop, true)); - } + giveItems(items, reason, player, share); } + return true; } - private void processDrop(DropData dd, EntityMonster em, Player gp) { - int target = Utils.randomRange(1, 10000); - if (target >= dd.getMinWeight() && target < dd.getMaxWeight()) { - ItemData itemData = GameData.getItemDataMap().get(dd.getItemId()); - int num = Utils.randomRange(dd.getMinCount(), dd.getMaxCount()); + private void processSubDrop(DropTableData dropData, int count, List items) { + //TODO:Not clear on the meaning of some fields,like "dropLevel".Will ignore them. + //TODO:solve drop limits,like everydayLimit. - if (itemData == null) { - return; + if (dropData.getRandomType() == 0) { + int weightSum = 0; + for (var i : dropData.getDropVec()) { + int id = i.getId(); + if (id == 0) continue; + weightSum += i.getWeight(); } - if (itemData.isEquip()) { - for (int i = 0; i < num; i++) { - float range = (2.5f + (.05f * num)); - Position pos = em.getPosition().nearby2d(range).addY(3f); - addDropEntity(dd, em.getScene(), itemData, pos, num, gp); + if (weightSum == 0) return; + int weight = rand.nextInt(weightSum); + int sum = 0; + for (var i : dropData.getDropVec()) { + int id = i.getId(); + if (id == 0) continue; + sum += i.getWeight(); + if (weight < sum) { + //win the item + int amount = calculateDropAmount(i) * count; + if (dropTable.containsKey(id)) { + processSubDrop(dropTable.get(id), amount, items); + } else { + items.add(new GameItem(id, amount)); + } + break; } - } else { - Position pos = em.getPosition().clone().addY(3f); - addDropEntity(dd, em.getScene(), itemData, pos, num, gp); } - } - } - - public void callDrop(EntityMonster em) { - int id = em.getMonsterData().getId(); - if (getDropData().containsKey(id)) { - for (DropData dd : getDropData().get(id)) { - if (dd.isShare()) - processDrop(dd, em, null); - else { - for (Player gp : em.getScene().getPlayers()) { - processDrop(dd, em, gp); + } else if (dropData.getRandomType() == 1) { + for (var i : dropData.getDropVec()) { + int id = i.getId(); + if (id == 0) continue; + if (rand.nextInt(10000) < i.getWeight()) { + int amount = calculateDropAmount(i) * count; + if (dropTable.containsKey(id)) { + processSubDrop(dropTable.get(id), amount, items); + } else { + items.add(new GameItem(id, amount)); } } } } } + + private int calculateDropAmount(DropItemData i) { + int amount = 0; + if (i.getCountRange().contains(";")) { + String[] ranges = i.getCountRange().split(";"); + amount = rand.nextInt(Integer.parseInt(ranges[0]), Integer.parseInt(ranges[1]) + 1); + } else if (i.getCountRange().contains(".")) { + double expectAmount = Double.parseDouble(i.getCountRange()); + int chance = (int) expectAmount + 1; + for (int k = 0; k < chance; k++) { + if (rand.nextDouble() < expectAmount / chance) amount++; + } + } else { + amount = Integer.parseInt(i.getCountRange()); + } + return amount; + } + + /** + * @param share Whether other players in the scene could see the drop items. + */ + private void dropItem(int itemId, int amount, ActionReason reason, Player player, GameEntity bornFrom, boolean share) { + DropMaterialData drop = GameData.getDropMaterialDataMap().get(itemId); + if (GameData.getItemDataMap().get(itemId).getItemType() == ItemType.ITEM_VIRTUAL || (drop != null && drop.isAutoPick())) { + if (share) { + for (var p : player.getScene().getPlayers()) { + p.sendPacket(new PacketDropHintNotify(itemId, bornFrom.getPosition().toProto())); + } + } else { + player.sendPacket(new PacketDropHintNotify(itemId, bornFrom.getPosition().toProto())); + } + } else { + //TODO:solve share problem + player.getScene().addDropEntity(new GameItem(itemId, amount), bornFrom, player, share); + } + } + + private void dropItems(List items, ActionReason reason, GameEntity bornFrom, Player player, boolean share) { + for (var i : items) { + DropMaterialData drop = GameData.getDropMaterialDataMap().get(i.getItemId()); + if (i.getItemData().getItemType() == ItemType.ITEM_VIRTUAL || (drop != null && drop.isAutoPick())) { + giveItem(i,reason,player,share); + } + } + //TODO:solve share problem + player.getScene().addDropEntities(items, bornFrom, player, share); + } + + private void giveItem(GameItem item, ActionReason reason, Player player, boolean share) { + if (share) { + for (var p : player.getScene().getPlayers()) { + p.sendPacket(new PacketDropHintNotify(item.getItemId(), player.getPosition().toProto())); + p.getInventory().addItem(item, reason); + } + } else { + player.sendPacket(new PacketDropHintNotify(item.getItemId(), player.getPosition().toProto())); + player.getInventory().addItem(item, reason); + } + } + + private void giveItems(List items, ActionReason reason, Player player, boolean share) { + for (var i : items) { + DropMaterialData drop = GameData.getDropMaterialDataMap().get(i.getItemId()); + if (i.getItemData().getItemType() == ItemType.ITEM_VIRTUAL || (drop != null && drop.isAutoPick())) { + if (share) { + for (var p : player.getScene().getPlayers()) { + p.sendPacket(new PacketDropHintNotify(i.getItemId(), player.getPosition().toProto())); + } + } else { + player.sendPacket(new PacketDropHintNotify(i.getItemId(), player.getPosition().toProto())); + } + } + } + if (share) { + for (var p : player.getScene().getPlayers()) { + p.getInventory().addItems(items, reason); + } + } else { + player.getInventory().addItems(items, reason); + } + } } diff --git a/src/main/java/emu/grasscutter/game/drop/DropSystemLegacy.java b/src/main/java/emu/grasscutter/game/drop/DropSystemLegacy.java new file mode 100644 index 00000000..93c88a7b --- /dev/null +++ b/src/main/java/emu/grasscutter/game/drop/DropSystemLegacy.java @@ -0,0 +1,106 @@ +package emu.grasscutter.game.drop; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.DataLoader; +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.excels.ItemData; +import emu.grasscutter.game.entity.EntityItem; +import emu.grasscutter.game.entity.EntityMonster; +import emu.grasscutter.game.inventory.GameItem; +import emu.grasscutter.game.inventory.ItemType; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.props.ActionReason; +import emu.grasscutter.game.world.Scene; +import emu.grasscutter.server.game.BaseGameSystem; +import emu.grasscutter.server.game.GameServer; +import emu.grasscutter.utils.Position; +import emu.grasscutter.utils.Utils; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; + +import java.util.List; + +public class DropSystemLegacy extends BaseGameSystem { + private final Int2ObjectMap> dropData; + + public DropSystemLegacy(GameServer server) { + super(server); + this.dropData = new Int2ObjectOpenHashMap<>(); + this.load(); + } + + public Int2ObjectMap> getDropData() { + return dropData; + } + + public synchronized void load() { + getDropData().clear(); + try { + List banners = DataLoader.loadList("Drop.json", DropInfo.class); + if (banners.size() > 0) { + for (DropInfo di : banners) { + getDropData().put(di.getMonsterId(), di.getDropDataList()); + } + Grasscutter.getLogger().debug("Drop data successfully loaded."); + } else { + Grasscutter.getLogger().error("Unable to load drop data. Drop data size is 0."); + } + } catch (Exception e) { + Grasscutter.getLogger().error("Unable to load drop data.", e); + } + } + private void addDropEntity(DropData dd, Scene dropScene, ItemData itemData, Position pos, int num, Player target) { + if (!dd.isGive() && (itemData.getItemType() != ItemType.ITEM_VIRTUAL || itemData.getGadgetId() != 0)) { + EntityItem entity = new EntityItem(dropScene, target, itemData, pos, num, dd.isShare()); + if (!dd.isShare()) + dropScene.addEntityToSingleClient(target, entity); + else + dropScene.addEntity(entity); + } else { + if (target != null) { + target.getInventory().addItem(new GameItem(itemData, num), ActionReason.SubfieldDrop, true); + } else { + // target is null if items will be added are shared. no one could pick it up because of the combination(give + shared) + // so it will be sent to all players' inventories directly. + dropScene.getPlayers().forEach(x -> x.getInventory().addItem(new GameItem(itemData, num), ActionReason.SubfieldDrop, true)); + } + } + } + + private void processDrop(DropData dd, EntityMonster em, Player gp) { + int target = Utils.randomRange(1, 10000); + if (target >= dd.getMinWeight() && target < dd.getMaxWeight()) { + ItemData itemData = GameData.getItemDataMap().get(dd.getItemId()); + int num = Utils.randomRange(dd.getMinCount(), dd.getMaxCount()); + + if (itemData == null) { + return; + } + if (itemData.isEquip()) { + for (int i = 0; i < num; i++) { + float range = (2.5f + (.05f * num)); + Position pos = em.getPosition().nearby2d(range).addY(3f); + addDropEntity(dd, em.getScene(), itemData, pos, num, gp); + } + } else { + Position pos = em.getPosition().clone().addY(3f); + addDropEntity(dd, em.getScene(), itemData, pos, num, gp); + } + } + } + + public void callDrop(EntityMonster em) { + int id = em.getMonsterData().getId(); + if (getDropData().containsKey(id)) { + for (DropData dd : getDropData().get(id)) { + if (dd.isShare()) + processDrop(dd, em, null); + else { + for (Player gp : em.getScene().getPlayers()) { + processDrop(dd, em, gp); + } + } + } + } + } +} diff --git a/src/main/java/emu/grasscutter/game/entity/gadget/GadgetChest.java b/src/main/java/emu/grasscutter/game/entity/gadget/GadgetChest.java index 6cbc4b64..7d721d88 100644 --- a/src/main/java/emu/grasscutter/game/entity/gadget/GadgetChest.java +++ b/src/main/java/emu/grasscutter/game/entity/gadget/GadgetChest.java @@ -1,10 +1,10 @@ package emu.grasscutter.game.entity.gadget; import emu.grasscutter.Grasscutter; +import emu.grasscutter.game.drop.DropSystem; import emu.grasscutter.game.entity.EntityGadget; import emu.grasscutter.game.entity.gadget.chest.BossChestInteractHandler; import emu.grasscutter.game.player.Player; -import emu.grasscutter.game.props.LifeState; import emu.grasscutter.net.proto.BossChestInfoOuterClass.BossChestInfo; import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq; import emu.grasscutter.net.proto.InterOpTypeOuterClass.InterOpType; @@ -13,8 +13,8 @@ import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType; import emu.grasscutter.net.proto.ResinCostTypeOuterClass; import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo; import emu.grasscutter.scripts.constants.ScriptGadgetState; +import emu.grasscutter.scripts.data.SceneGadget; import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp; -import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify; public class GadgetChest extends GadgetContent { @@ -22,7 +22,34 @@ public class GadgetChest extends GadgetContent { super(gadget); } + /** + * @return Whether we should remove the gadget. + */ public boolean onInteract(Player player, GadgetInteractReq req) { + //TODO:handle boss chests + //If bigWorldScript enabled,use new drop system. + if (Grasscutter.getConfig().server.game.enableScriptInBigWorld) { + //only the owner of the world can open chests. + if (player != player.getWorld().getHost()) return false; + SceneGadget chest = getGadget().getMetaGadget(); + Grasscutter.getLogger().info("OpenChest:chest_drop_id={},drop_tag={}", chest.chest_drop_id, chest.drop_tag); + DropSystem dropSystem = player.getServer().getDropSystem(); + boolean status = false; + if (chest.drop_tag != null) { + status = dropSystem.handleChestDrop(chest.drop_tag, chest.level, getGadget()); + } else if (chest.chest_drop_id != 0) { + status = dropSystem.handleChestDrop(chest.chest_drop_id, chest.drop_count, getGadget()); + } + if (status) { + getGadget().updateState(ScriptGadgetState.ChestOpened); + player.sendPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_TYPE_OPEN_CHEST,InterOpType.INTER_OP_TYPE_FINISH)); + return chest.isOneoff; + } + //if failed,fallback to legacy drop system. + Grasscutter.getLogger().warn("Can not solve chest drop:chest_drop_id={},drop_tag={}.Fallback to legacy drop system.", chest.chest_drop_id, chest.drop_tag); + } + + //Legacy chest drop system var chestInteractHandlerMap = getGadget().getScene().getWorld().getServer().getWorldDataSystem().getChestInteractHandlerMap(); var handler = chestInteractHandlerMap.get(getGadget().getGadgetData().getJsonName()); if (handler == null) { diff --git a/src/main/java/emu/grasscutter/game/inventory/Inventory.java b/src/main/java/emu/grasscutter/game/inventory/Inventory.java index b82e9a96..cf9a970d 100644 --- a/src/main/java/emu/grasscutter/game/inventory/Inventory.java +++ b/src/main/java/emu/grasscutter/game/inventory/Inventory.java @@ -142,7 +142,7 @@ public class Inventory extends BasePlayerManager implements Iterable { if (item.getItemId() == 0) continue; GameItem result = null; try { - // putItem might throws exception + // putItem might throw exception // ignore that exception and continue result = putItem(item); } catch (Exception e) { diff --git a/src/main/java/emu/grasscutter/game/world/Scene.java b/src/main/java/emu/grasscutter/game/world/Scene.java index 7ca42ce4..0e737485 100644 --- a/src/main/java/emu/grasscutter/game/world/Scene.java +++ b/src/main/java/emu/grasscutter/game/world/Scene.java @@ -7,8 +7,10 @@ import emu.grasscutter.data.binout.SceneNpcBornEntry; import emu.grasscutter.data.excels.*; import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.dungeons.DungeonSettleListener; +import emu.grasscutter.game.dungeons.challenge.WorldChallenge; import emu.grasscutter.game.entity.*; import emu.grasscutter.game.entity.gadget.GadgetWorktop; +import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.managers.blossom.BlossomManager; import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.TeamInfo; @@ -16,7 +18,7 @@ import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.LifeState; import emu.grasscutter.game.props.SceneType; import emu.grasscutter.game.quest.QuestGroupSuite; -import emu.grasscutter.game.dungeons.challenge.WorldChallenge; +import emu.grasscutter.game.world.SpawnDataEntry.GridBlockId; import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult; import emu.grasscutter.net.proto.SelectWorktopOptionReqOuterClass; @@ -50,7 +52,7 @@ public class Scene { @Getter @Setter private int autoCloseTime; @Getter private int time; - private long startTime; + private final long startTime; @Getter private SceneScriptManager scriptManager; @Getter @Setter private WorldChallenge challenge; @@ -327,7 +329,7 @@ public class Scene { // Reward drop if (target instanceof EntityMonster && this.getSceneType() != SceneType.SCENE_DUNGEON) { - getWorld().getServer().getDropSystem().callDrop((EntityMonster) target); + getWorld().getServer().getDropSystemLegacy().callDrop((EntityMonster) target); } // Remove entity from world @@ -390,8 +392,7 @@ public class Scene { public synchronized void checkSpawns() { Set loadedGridBlocks = new HashSet<>(); for (Player player : this.getPlayers()) { - for (SpawnDataEntry.GridBlockId block : SpawnDataEntry.GridBlockId.getAdjacentGridBlockIds(player.getSceneId(), player.getPosition())) - loadedGridBlocks.add(block); + Collections.addAll(loadedGridBlocks, GridBlockId.getAdjacentGridBlockIds(player.getSceneId(), player.getPosition())); } if (this.loadedGridBlocks.containsAll(loadedGridBlocks)) { // Don't recalculate static spawns if nothing has changed return; @@ -700,18 +701,48 @@ public class Scene { if (itemData.isEquip()) { float range = (1.5f + (.05f * amount)); for (int i = 0; i < amount; i++) { - Position pos = bornForm.getPosition().nearby2d(range).addZ(.9f); // Why Z? + Position pos = bornForm.getPosition().nearby2d(range).addY(1.5f); // Why Z? EntityItem entity = new EntityItem(this, null, itemData, pos, 1); addEntity(entity); } } else { - EntityItem entity = new EntityItem(this, null, itemData, bornForm.getPosition().clone().addZ(.9f), amount); // Why Z? + EntityItem entity = new EntityItem(this, null, itemData, bornForm.getPosition().clone().addY(1.5f), amount); // Why Z? addEntity(entity); } } + + public void addDropEntity(GameItem item, GameEntity bornForm, Player player, boolean share) { + //TODO:optimize EntityItem.java. Maybe we should make other players can't see the ItemEntity. + ItemData itemData = GameData.getItemDataMap().get(item.getItemId()); + if (itemData == null) return; + if (itemData.isEquip()) { + float range = (1.5f + (.05f * item.getCount())); + for (int j = 0; j < item.getCount(); j++) { + Position pos = bornForm.getPosition().nearby2d(range).addY(1.5f); //TODO + EntityItem entity = new EntityItem(this, player, itemData, pos, item.getCount(), share); + addEntity(entity); + } + } else { + EntityItem entity = new EntityItem(this, player, itemData, bornForm.getPosition().clone().addY(1.5f), item.getCount(), share); //TODO:improve + addEntity(entity); + } + + } + + /** + * @param share If false,only the player can see the items but others can't.If true,all players will see. + */ + public void addDropEntities(List items, GameEntity bornForm, Player player, boolean share) { + //TODO:optimize EntityItem.java. Maybe we should make other players can't see the ItemEntity. + for (var i : items) { + addDropEntity(i, bornForm, player, share); + } + } + public void loadNpcForPlayerEnter(Player player) { this.npcBornEntrySet.addAll(loadNpcForPlayer(player)); } + private List loadNpcForPlayer(Player player) { var pos = player.getPosition(); var data = GameData.getSceneNpcBornData().get(getId()); diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneGadget.java b/src/main/java/emu/grasscutter/scripts/data/SceneGadget.java index 78bf4326..fb9c901a 100644 --- a/src/main/java/emu/grasscutter/scripts/data/SceneGadget.java +++ b/src/main/java/emu/grasscutter/scripts/data/SceneGadget.java @@ -6,12 +6,22 @@ import lombok.ToString; @ToString @Setter public class SceneGadget extends SceneObject{ + public int config_id; public int gadget_id; + public int level; + public int chest_drop_id; + public int drop_count; + public String drop_tag; + boolean showcutscene; + boolean persistence; public int state; + public int point_type; + public int owner; public SceneBossChest boss_chest; public int interact_id; public boolean isOneoff; + public int area_id; public int draft_id; public void setIsOneoff(boolean isOneoff) { diff --git a/src/main/java/emu/grasscutter/server/game/GameServer.java b/src/main/java/emu/grasscutter/server/game/GameServer.java index 09e4e347..968347c4 100644 --- a/src/main/java/emu/grasscutter/server/game/GameServer.java +++ b/src/main/java/emu/grasscutter/server/game/GameServer.java @@ -9,6 +9,7 @@ import emu.grasscutter.game.chat.ChatSystem; import emu.grasscutter.game.chat.ChatSystemHandler; import emu.grasscutter.game.combine.CombineManger; import emu.grasscutter.game.drop.DropSystem; +import emu.grasscutter.game.drop.DropSystemLegacy; import emu.grasscutter.game.dungeons.DungeonSystem; import emu.grasscutter.game.dungeons.challenge.DungeonChallenge; import emu.grasscutter.game.expedition.ExpeditionSystem; @@ -28,10 +29,10 @@ import emu.grasscutter.game.world.World; import emu.grasscutter.game.world.WorldDataSystem; import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail; -import emu.grasscutter.server.event.types.ServerEvent; import emu.grasscutter.server.event.game.ServerTickEvent; import emu.grasscutter.server.event.internal.ServerStartEvent; import emu.grasscutter.server.event.internal.ServerStopEvent; +import emu.grasscutter.server.event.types.ServerEvent; import emu.grasscutter.server.scheduler.ServerTaskScheduler; import emu.grasscutter.task.TaskMap; import kcp.highway.ChannelConfig; @@ -44,7 +45,7 @@ import java.time.OffsetDateTime; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import static emu.grasscutter.config.Configuration.*; +import static emu.grasscutter.config.Configuration.GAME_INFO; import static emu.grasscutter.utils.Language.translate; @Getter @@ -63,6 +64,7 @@ public final class GameServer extends KcpServer { private final DungeonSystem dungeonSystem; private final ExpeditionSystem expeditionSystem; private final DropSystem dropSystem; + private final DropSystemLegacy dropSystemLegacy; private final WorldDataSystem worldDataSystem; private final BattlePassSystem battlePassSystem; private final CombineManger combineSystem; @@ -116,6 +118,7 @@ public final class GameServer extends KcpServer { this.multiplayerSystem = new MultiplayerSystem(this); this.dungeonSystem = new DungeonSystem(this); this.dropSystem = new DropSystem(this); + this.dropSystemLegacy = new DropSystemLegacy(this); this.expeditionSystem = new ExpeditionSystem(this); this.combineSystem = new CombineManger(this); this.towerSystem = new TowerSystem(this); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketDropHintNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketDropHintNotify.java new file mode 100644 index 00000000..3c3f4e90 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketDropHintNotify.java @@ -0,0 +1,23 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.DropHintNotifyOuterClass.DropHintNotify; +import emu.grasscutter.net.proto.VectorOuterClass.Vector; + +public class PacketDropHintNotify extends BasePacket { + public PacketDropHintNotify(int itemId, Vector position){ + super(PacketOpcodes.DropHintNotify); + var proto= DropHintNotify.newBuilder() + .addItemIdList(itemId) + .setPosition(position); + setData(proto.build()); + } + public PacketDropHintNotify(Iterable items, Vector position){ + super(PacketOpcodes.DropHintNotify); + var proto= DropHintNotify.newBuilder() + .addAllItemIdList(items) + .setPosition(position); + setData(proto.build()); + } +} From d86597d360e1c75c2fb2ff930be7c48afee2efa0 Mon Sep 17 00:00:00 2001 From: dragon Date: Sat, 19 Nov 2022 15:37:47 +0800 Subject: [PATCH 2/8] chest drop bug fix. --- .../proto/WorldChestOpenNotifyOuterClass.java | 689 ++++++++++++++++++ .../emu/grasscutter/game/drop/DropSystem.java | 40 +- .../grasscutter/game/entity/EntityItem.java | 3 + .../game/entity/gadget/GadgetChest.java | 2 + .../grasscutter/net/packet/PacketOpcodes.java | 2 +- .../send/PacketWorldChestOpenNotify.java | 17 + 6 files changed, 721 insertions(+), 32 deletions(-) create mode 100644 src/generated/main/java/emu/grasscutter/net/proto/WorldChestOpenNotifyOuterClass.java create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketWorldChestOpenNotify.java diff --git a/src/generated/main/java/emu/grasscutter/net/proto/WorldChestOpenNotifyOuterClass.java b/src/generated/main/java/emu/grasscutter/net/proto/WorldChestOpenNotifyOuterClass.java new file mode 100644 index 00000000..f1e1555b --- /dev/null +++ b/src/generated/main/java/emu/grasscutter/net/proto/WorldChestOpenNotifyOuterClass.java @@ -0,0 +1,689 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: WorldChestOpenNotify.proto + +package emu.grasscutter.net.proto; + +public final class WorldChestOpenNotifyOuterClass { + private WorldChestOpenNotifyOuterClass() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistryLite registry) { + } + + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + registerAllExtensions( + (com.google.protobuf.ExtensionRegistryLite) registry); + } + public interface WorldChestOpenNotifyOrBuilder extends + // @@protoc_insertion_point(interface_extends:WorldChestOpenNotify) + com.google.protobuf.MessageOrBuilder { + + /** + * uint32 group_id = 6; + * @return The groupId. + */ + int getGroupId(); + + /** + * uint32 scene_id = 9; + * @return The sceneId. + */ + int getSceneId(); + + /** + * uint32 config_id = 12; + * @return The configId. + */ + int getConfigId(); + } + /** + *
+   * CmdId: 3295
+   * EnetChannelId: 0
+   * EnetIsReliable: true
+   * 
+ * + * Protobuf type {@code WorldChestOpenNotify} + */ + public static final class WorldChestOpenNotify extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:WorldChestOpenNotify) + WorldChestOpenNotifyOrBuilder { + private static final long serialVersionUID = 0L; + // Use WorldChestOpenNotify.newBuilder() to construct. + private WorldChestOpenNotify(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + private WorldChestOpenNotify() { + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new WorldChestOpenNotify(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private WorldChestOpenNotify( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 48: { + + groupId_ = input.readUInt32(); + break; + } + case 72: { + + sceneId_ = input.readUInt32(); + break; + } + case 96: { + + configId_ = input.readUInt32(); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.internal_static_WorldChestOpenNotify_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.internal_static_WorldChestOpenNotify_fieldAccessorTable + .ensureFieldAccessorsInitialized( + emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify.class, emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify.Builder.class); + } + + public static final int GROUP_ID_FIELD_NUMBER = 6; + private int groupId_; + /** + * uint32 group_id = 6; + * @return The groupId. + */ + @java.lang.Override + public int getGroupId() { + return groupId_; + } + + public static final int SCENE_ID_FIELD_NUMBER = 9; + private int sceneId_; + /** + * uint32 scene_id = 9; + * @return The sceneId. + */ + @java.lang.Override + public int getSceneId() { + return sceneId_; + } + + public static final int CONFIG_ID_FIELD_NUMBER = 12; + private int configId_; + /** + * uint32 config_id = 12; + * @return The configId. + */ + @java.lang.Override + public int getConfigId() { + return configId_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (groupId_ != 0) { + output.writeUInt32(6, groupId_); + } + if (sceneId_ != 0) { + output.writeUInt32(9, sceneId_); + } + if (configId_ != 0) { + output.writeUInt32(12, configId_); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (groupId_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(6, groupId_); + } + if (sceneId_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(9, sceneId_); + } + if (configId_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(12, configId_); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify)) { + return super.equals(obj); + } + emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify other = (emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify) obj; + + if (getGroupId() + != other.getGroupId()) return false; + if (getSceneId() + != other.getSceneId()) return false; + if (getConfigId() + != other.getConfigId()) return false; + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + GROUP_ID_FIELD_NUMBER; + hash = (53 * hash) + getGroupId(); + hash = (37 * hash) + SCENE_ID_FIELD_NUMBER; + hash = (53 * hash) + getSceneId(); + hash = (37 * hash) + CONFIG_ID_FIELD_NUMBER; + hash = (53 * hash) + getConfigId(); + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+     * CmdId: 3295
+     * EnetChannelId: 0
+     * EnetIsReliable: true
+     * 
+ * + * Protobuf type {@code WorldChestOpenNotify} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:WorldChestOpenNotify) + emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotifyOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.internal_static_WorldChestOpenNotify_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.internal_static_WorldChestOpenNotify_fieldAccessorTable + .ensureFieldAccessorsInitialized( + emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify.class, emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify.Builder.class); + } + + // Construct using emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + groupId_ = 0; + + sceneId_ = 0; + + configId_ = 0; + + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.internal_static_WorldChestOpenNotify_descriptor; + } + + @java.lang.Override + public emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify getDefaultInstanceForType() { + return emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify.getDefaultInstance(); + } + + @java.lang.Override + public emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify build() { + emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify buildPartial() { + emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify result = new emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify(this); + result.groupId_ = groupId_; + result.sceneId_ = sceneId_; + result.configId_ = configId_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify) { + return mergeFrom((emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify other) { + if (other == emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify.getDefaultInstance()) return this; + if (other.getGroupId() != 0) { + setGroupId(other.getGroupId()); + } + if (other.getSceneId() != 0) { + setSceneId(other.getSceneId()); + } + if (other.getConfigId() != 0) { + setConfigId(other.getConfigId()); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + + private int groupId_ ; + /** + * uint32 group_id = 6; + * @return The groupId. + */ + @java.lang.Override + public int getGroupId() { + return groupId_; + } + /** + * uint32 group_id = 6; + * @param value The groupId to set. + * @return This builder for chaining. + */ + public Builder setGroupId(int value) { + + groupId_ = value; + onChanged(); + return this; + } + /** + * uint32 group_id = 6; + * @return This builder for chaining. + */ + public Builder clearGroupId() { + + groupId_ = 0; + onChanged(); + return this; + } + + private int sceneId_ ; + /** + * uint32 scene_id = 9; + * @return The sceneId. + */ + @java.lang.Override + public int getSceneId() { + return sceneId_; + } + /** + * uint32 scene_id = 9; + * @param value The sceneId to set. + * @return This builder for chaining. + */ + public Builder setSceneId(int value) { + + sceneId_ = value; + onChanged(); + return this; + } + /** + * uint32 scene_id = 9; + * @return This builder for chaining. + */ + public Builder clearSceneId() { + + sceneId_ = 0; + onChanged(); + return this; + } + + private int configId_ ; + /** + * uint32 config_id = 12; + * @return The configId. + */ + @java.lang.Override + public int getConfigId() { + return configId_; + } + /** + * uint32 config_id = 12; + * @param value The configId to set. + * @return This builder for chaining. + */ + public Builder setConfigId(int value) { + + configId_ = value; + onChanged(); + return this; + } + /** + * uint32 config_id = 12; + * @return This builder for chaining. + */ + public Builder clearConfigId() { + + configId_ = 0; + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:WorldChestOpenNotify) + } + + // @@protoc_insertion_point(class_scope:WorldChestOpenNotify) + private static final emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify(); + } + + public static emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public WorldChestOpenNotify parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new WorldChestOpenNotify(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + } + + private static final com.google.protobuf.Descriptors.Descriptor + internal_static_WorldChestOpenNotify_descriptor; + private static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_WorldChestOpenNotify_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n\032WorldChestOpenNotify.proto\"M\n\024WorldChe" + + "stOpenNotify\022\020\n\010group_id\030\006 \001(\r\022\020\n\010scene_" + + "id\030\t \001(\r\022\021\n\tconfig_id\030\014 \001(\rB\033\n\031emu.grass" + + "cutter.net.protob\006proto3" + }; + descriptor = com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }); + internal_static_WorldChestOpenNotify_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_WorldChestOpenNotify_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_WorldChestOpenNotify_descriptor, + new java.lang.String[] { "GroupId", "SceneId", "ConfigId", }); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/src/main/java/emu/grasscutter/game/drop/DropSystem.java b/src/main/java/emu/grasscutter/game/drop/DropSystem.java index d33f8c20..b8eaacba 100644 --- a/src/main/java/emu/grasscutter/game/drop/DropSystem.java +++ b/src/main/java/emu/grasscutter/game/drop/DropSystem.java @@ -142,58 +142,36 @@ public class DropSystem extends BaseGameSystem { /** * @param share Whether other players in the scene could see the drop items. */ - private void dropItem(int itemId, int amount, ActionReason reason, Player player, GameEntity bornFrom, boolean share) { - DropMaterialData drop = GameData.getDropMaterialDataMap().get(itemId); - if (GameData.getItemDataMap().get(itemId).getItemType() == ItemType.ITEM_VIRTUAL || (drop != null && drop.isAutoPick())) { - if (share) { - for (var p : player.getScene().getPlayers()) { - p.sendPacket(new PacketDropHintNotify(itemId, bornFrom.getPosition().toProto())); - } - } else { - player.sendPacket(new PacketDropHintNotify(itemId, bornFrom.getPosition().toProto())); - } + private void dropItem(GameItem item, ActionReason reason, Player player, GameEntity bornFrom, boolean share) { + DropMaterialData drop = GameData.getDropMaterialDataMap().get(item.getItemId()); + if ((drop != null && drop.isAutoPick()) || item.getItemId() == 102) { + giveItem(item, reason, player, share); } else { //TODO:solve share problem - player.getScene().addDropEntity(new GameItem(itemId, amount), bornFrom, player, share); + player.getScene().addDropEntity(item, bornFrom, player, share); } } private void dropItems(List items, ActionReason reason, GameEntity bornFrom, Player player, boolean share) { for (var i : items) { - DropMaterialData drop = GameData.getDropMaterialDataMap().get(i.getItemId()); - if (i.getItemData().getItemType() == ItemType.ITEM_VIRTUAL || (drop != null && drop.isAutoPick())) { - giveItem(i,reason,player,share); - } + dropItem(i,reason,player,bornFrom,share); } - //TODO:solve share problem - player.getScene().addDropEntities(items, bornFrom, player, share); } private void giveItem(GameItem item, ActionReason reason, Player player, boolean share) { if (share) { for (var p : player.getScene().getPlayers()) { - p.sendPacket(new PacketDropHintNotify(item.getItemId(), player.getPosition().toProto())); p.getInventory().addItem(item, reason); + p.sendPacket(new PacketDropHintNotify(item.getItemId(), player.getPosition().toProto())); } } else { - player.sendPacket(new PacketDropHintNotify(item.getItemId(), player.getPosition().toProto())); player.getInventory().addItem(item, reason); + player.sendPacket(new PacketDropHintNotify(item.getItemId(), player.getPosition().toProto())); } } private void giveItems(List items, ActionReason reason, Player player, boolean share) { - for (var i : items) { - DropMaterialData drop = GameData.getDropMaterialDataMap().get(i.getItemId()); - if (i.getItemData().getItemType() == ItemType.ITEM_VIRTUAL || (drop != null && drop.isAutoPick())) { - if (share) { - for (var p : player.getScene().getPlayers()) { - p.sendPacket(new PacketDropHintNotify(i.getItemId(), player.getPosition().toProto())); - } - } else { - player.sendPacket(new PacketDropHintNotify(i.getItemId(), player.getPosition().toProto())); - } - } - } + //don't know whether we need PacketDropHintNotify. if (share) { for (var p : player.getScene().getPlayers()) { p.getInventory().addItems(items, reason); diff --git a/src/main/java/emu/grasscutter/game/entity/EntityItem.java b/src/main/java/emu/grasscutter/game/entity/EntityItem.java index cd3d81d6..a1fac1ee 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityItem.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityItem.java @@ -22,6 +22,7 @@ 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.server.packet.send.PacketDropHintNotify; import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp; import emu.grasscutter.utils.Position; import emu.grasscutter.utils.ProtoHelper; @@ -66,6 +67,7 @@ public class EntityItem extends EntityBaseGadget { @Override public void onInteract(Player player, GadgetInteractReq interactReq) { // check drop owner to avoid someone picked up item in others' world + //TODO:improve it if (!this.isShare()) { int dropOwner = (int) (this.getGuid() >> 32); if (dropOwner != player.getUid()) { @@ -79,6 +81,7 @@ public class EntityItem extends EntityBaseGadget { // Add to inventory boolean success = player.getInventory().addItem(item, ActionReason.SubfieldDrop); if (success) { + player.sendPacket(new PacketDropHintNotify(item.getItemId(),getPosition().toProto())); if (!this.isShare()) { // not shared drop player.sendPacket(new PacketGadgetInteractRsp(this, InteractType.INTERACT_TYPE_PICK_ITEM)); } else { diff --git a/src/main/java/emu/grasscutter/game/entity/gadget/GadgetChest.java b/src/main/java/emu/grasscutter/game/entity/gadget/GadgetChest.java index 7d721d88..5c870c35 100644 --- a/src/main/java/emu/grasscutter/game/entity/gadget/GadgetChest.java +++ b/src/main/java/emu/grasscutter/game/entity/gadget/GadgetChest.java @@ -15,6 +15,7 @@ import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo; import emu.grasscutter.scripts.constants.ScriptGadgetState; import emu.grasscutter.scripts.data.SceneGadget; import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp; +import emu.grasscutter.server.packet.send.PacketWorldChestOpenNotify; public class GadgetChest extends GadgetContent { @@ -43,6 +44,7 @@ public class GadgetChest extends GadgetContent { if (status) { getGadget().updateState(ScriptGadgetState.ChestOpened); player.sendPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_TYPE_OPEN_CHEST,InterOpType.INTER_OP_TYPE_FINISH)); + player.sendPacket(new PacketWorldChestOpenNotify(getGadget().getGroupId(), player.getSceneId(), chest.config_id)); return chest.isOneoff; } //if failed,fallback to legacy drop system. diff --git a/src/main/java/emu/grasscutter/net/packet/PacketOpcodes.java b/src/main/java/emu/grasscutter/net/packet/PacketOpcodes.java index 507029fa..28761b89 100644 --- a/src/main/java/emu/grasscutter/net/packet/PacketOpcodes.java +++ b/src/main/java/emu/grasscutter/net/packet/PacketOpcodes.java @@ -1800,7 +1800,7 @@ public class PacketOpcodes { public static final int Unk3000_FIPHHGCJIMO = 23678; public static final int Unk3000_FPDBJJJLKEP = 6103; public static final int Unk3000_GCBMILHPIKA = 4659; - public static final int Unk3000_GDMEIKLAMIB = 3295; + public static final int WorldChestOpenNotify = 3295; public static final int Unk3000_GMLAHHCDKOI = 841; public static final int Unk3000_GNLFOLGMEPN = 21208; public static final int Unk3000_HBIPKOBMGGD = 5995; diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketWorldChestOpenNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketWorldChestOpenNotify.java new file mode 100644 index 00000000..03a0a543 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketWorldChestOpenNotify.java @@ -0,0 +1,17 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.WorldChestOpenNotifyOuterClass.WorldChestOpenNotify; + +public class PacketWorldChestOpenNotify extends BasePacket { + public PacketWorldChestOpenNotify(int groupId,int sceneId,int configId){ + super(PacketOpcodes.WorldChestOpenNotify); + WorldChestOpenNotify proto= WorldChestOpenNotify.newBuilder() + .setGroupId(groupId) + .setSceneId(sceneId) + .setConfigId(configId) + .build(); + setData(proto); + } +} From d0cccde6b2d032a158df3e055dbafb5875b21d99 Mon Sep 17 00:00:00 2001 From: dragon Date: Sat, 19 Nov 2022 23:53:06 +0800 Subject: [PATCH 3/8] Boss chest interact implementation --- .../emu/grasscutter/game/drop/DropSystem.java | 72 ++++++++++++------- .../game/entity/gadget/GadgetChest.java | 56 ++++++++++----- 2 files changed, 85 insertions(+), 43 deletions(-) diff --git a/src/main/java/emu/grasscutter/game/drop/DropSystem.java b/src/main/java/emu/grasscutter/game/drop/DropSystem.java index b8eaacba..10b06539 100644 --- a/src/main/java/emu/grasscutter/game/drop/DropSystem.java +++ b/src/main/java/emu/grasscutter/game/drop/DropSystem.java @@ -8,12 +8,13 @@ import emu.grasscutter.data.excels.DropMaterialData; import emu.grasscutter.data.excels.DropTableData; import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.game.inventory.GameItem; -import emu.grasscutter.game.inventory.ItemType; import emu.grasscutter.game.player.Player; import emu.grasscutter.game.props.ActionReason; +import emu.grasscutter.scripts.data.SceneBossChest; import emu.grasscutter.server.game.BaseGameSystem; import emu.grasscutter.server.game.GameServer; import emu.grasscutter.server.packet.send.PacketDropHintNotify; +import emu.grasscutter.server.packet.send.PacketGadgetAutoPickDropInfoNotify; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import java.util.*; @@ -42,14 +43,8 @@ public class DropSystem extends BaseGameSystem { } - public boolean handleChestDrop(int chestDropId, int dropCount, GameEntity bornFrom) { - Grasscutter.getLogger().info("ChestDrop:chest_drop_id={},drop_count={}", chestDropId, dropCount); - return processDrop(chestDropId, dropCount, ActionReason.OpenChest, bornFrom, bornFrom.getWorld().getHost(), false); - } - - public boolean handleChestDrop(String dropTag, int level, GameEntity bornFrom) { - Grasscutter.getLogger().info("ChestDrop:drop_tag={},level={}", dropTag, level); - if (!chestReward.containsKey(dropTag)) return false; + private int queryDropData(String dropTag, int level) { + if (!chestReward.containsKey(dropTag)) return 0; var rewardList = chestReward.get(dropTag); ChestDropData dropData = null; int minLevel = 0; @@ -59,25 +54,44 @@ public class DropSystem extends BaseGameSystem { dropData = i; } } - if (dropData == null) return false; - return processDrop(dropData.getDropId(), dropData.getDropCount(), ActionReason.OpenChest, bornFrom, bornFrom.getWorld().getHost(), false); + if (dropData == null) return 0; + return dropData.getDropId(); } - private boolean processDrop(int dropId, int count, ActionReason reason, GameEntity bornFrom, Player player, boolean share) { - if (!dropTable.containsKey(dropId)) return false; - var dropData = dropTable.get(dropId); - if (dropData.getNodeType() != 1) return false; + public boolean handleChestDrop(int chestDropId, int dropCount, GameEntity bornFrom) { + Grasscutter.getLogger().info("ChestDrop:chest_drop_id={},drop_count={}", chestDropId, dropCount); + if (!dropTable.containsKey(chestDropId)) return false; + var dropData = dropTable.get(chestDropId); List items = new ArrayList<>(); - processSubDrop(dropData, count, items); + processDrop(dropData, dropCount, items); if (dropData.isFallToGround()) { - dropItems(items, reason, bornFrom, player, share); + dropItems(items, ActionReason.OpenChest, bornFrom, bornFrom.getWorld().getHost(), false); } else { - giveItems(items, reason, player, share); + bornFrom.getWorld().getHost().getInventory().addItems(items, ActionReason.OpenChest); } return true; } - private void processSubDrop(DropTableData dropData, int count, List items) { + public boolean handleChestDrop(String dropTag, int level, GameEntity bornFrom) { + Grasscutter.getLogger().info("ChestDrop:drop_tag={},level={}", dropTag, level); + int dropId = queryDropData(dropTag, level); + if (dropId == 0) return false; + return handleChestDrop(dropId, 1, bornFrom); + } + + public boolean handleBossChestDrop(String dropTag, int level, SceneBossChest bossChest, Player player) { + int dropId = queryDropData(dropTag, level); + if (!dropTable.containsKey(dropId)) return false; + var dropData = dropTable.get(dropId); + List items = new ArrayList<>(); + processDrop(dropData, 1, items); + //TODO:test if there still need some packets. + player.getInventory().addItems(items, ActionReason.OpenWorldBossChest); + player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(items)); + return true; + } + + private void processDrop(DropTableData dropData, int count, List items) { //TODO:Not clear on the meaning of some fields,like "dropLevel".Will ignore them. //TODO:solve drop limits,like everydayLimit. @@ -98,10 +112,12 @@ public class DropSystem extends BaseGameSystem { if (weight < sum) { //win the item int amount = calculateDropAmount(i) * count; - if (dropTable.containsKey(id)) { - processSubDrop(dropTable.get(id), amount, items); - } else { - items.add(new GameItem(id, amount)); + if (amount > 0) { + if (dropTable.containsKey(id)) { + processDrop(dropTable.get(id), amount, items); + } else { + items.add(new GameItem(id, amount)); + } } break; } @@ -112,10 +128,12 @@ public class DropSystem extends BaseGameSystem { if (id == 0) continue; if (rand.nextInt(10000) < i.getWeight()) { int amount = calculateDropAmount(i) * count; - if (dropTable.containsKey(id)) { - processSubDrop(dropTable.get(id), amount, items); - } else { - items.add(new GameItem(id, amount)); + if (amount > 0) { + if (dropTable.containsKey(id)) { + processDrop(dropTable.get(id), amount, items); + } else { + items.add(new GameItem(id, amount)); + } } } } diff --git a/src/main/java/emu/grasscutter/game/entity/gadget/GadgetChest.java b/src/main/java/emu/grasscutter/game/entity/gadget/GadgetChest.java index 5c870c35..8cd0abb0 100644 --- a/src/main/java/emu/grasscutter/game/entity/gadget/GadgetChest.java +++ b/src/main/java/emu/grasscutter/game/entity/gadget/GadgetChest.java @@ -1,6 +1,8 @@ package emu.grasscutter.game.entity.gadget; import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.excels.WorldLevelData; import emu.grasscutter.game.drop.DropSystem; import emu.grasscutter.game.entity.EntityGadget; import emu.grasscutter.game.entity.gadget.chest.BossChestInteractHandler; @@ -27,27 +29,49 @@ public class GadgetChest extends GadgetContent { * @return Whether we should remove the gadget. */ public boolean onInteract(Player player, GadgetInteractReq req) { - //TODO:handle boss chests //If bigWorldScript enabled,use new drop system. if (Grasscutter.getConfig().server.game.enableScriptInBigWorld) { - //only the owner of the world can open chests. - if (player != player.getWorld().getHost()) return false; SceneGadget chest = getGadget().getMetaGadget(); Grasscutter.getLogger().info("OpenChest:chest_drop_id={},drop_tag={}", chest.chest_drop_id, chest.drop_tag); DropSystem dropSystem = player.getServer().getDropSystem(); - boolean status = false; - if (chest.drop_tag != null) { - status = dropSystem.handleChestDrop(chest.drop_tag, chest.level, getGadget()); - } else if (chest.chest_drop_id != 0) { - status = dropSystem.handleChestDrop(chest.chest_drop_id, chest.drop_count, getGadget()); + if (chest.boss_chest != null && chest.drop_tag != null) { + //Boss chest drop + //TODO:check for blossom chests + if (req.getOpType() == InterOpType.INTER_OP_TYPE_START) { + //Two steps + player.sendPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_TYPE_OPEN_CHEST, InterOpType.INTER_OP_TYPE_START)); + return false; + } + //TODO:check for take_num.(some boss rewards can only be claimed once a week.) + //TODO:should return Retcode.RET_RESIN_NOT_ENOUGH ? + //not sure whether the level calculation is correct. + WorldLevelData worldLevelData = GameData.getWorldLevelDataMap().get(player.getWorldLevel()); + int level = worldLevelData.getMonsterLevel() + player.getLevel() - worldLevelData.getId() + chest.level; + if (player.getResinManager().useResin(chest.boss_chest.resin) && dropSystem.handleBossChestDrop(chest.drop_tag, level, chest.boss_chest, player)) { + getGadget().updateState(ScriptGadgetState.ChestOpened); + player.sendPacket(new PacketGadgetInteractRsp(this.getGadget(), InteractTypeOuterClass.InteractType.INTERACT_TYPE_OPEN_CHEST, InterOpType.INTER_OP_TYPE_FINISH)); + //TODO:need world chest notify? + return true; + } + //if failed,fallback to legacy drop system. + } else { + //Normal chest drop + //only the owner of the world can open chests. + if (player != player.getWorld().getHost()) return false; + boolean status = false; + if (chest.drop_tag != null) { + status = dropSystem.handleChestDrop(chest.drop_tag, chest.level, getGadget()); + } else if (chest.chest_drop_id != 0) { + status = dropSystem.handleChestDrop(chest.chest_drop_id, chest.drop_count, getGadget()); + } + if (status) { + getGadget().updateState(ScriptGadgetState.ChestOpened); + player.sendPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_TYPE_OPEN_CHEST, InterOpType.INTER_OP_TYPE_FINISH)); + player.sendPacket(new PacketWorldChestOpenNotify(getGadget().getGroupId(), player.getSceneId(), chest.config_id)); + return chest.isOneoff; + } + //if failed,fallback to legacy drop system. } - if (status) { - getGadget().updateState(ScriptGadgetState.ChestOpened); - player.sendPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_TYPE_OPEN_CHEST,InterOpType.INTER_OP_TYPE_FINISH)); - player.sendPacket(new PacketWorldChestOpenNotify(getGadget().getGroupId(), player.getSceneId(), chest.config_id)); - return chest.isOneoff; - } - //if failed,fallback to legacy drop system. Grasscutter.getLogger().warn("Can not solve chest drop:chest_drop_id={},drop_tag={}.Fallback to legacy drop system.", chest.chest_drop_id, chest.drop_tag); } @@ -75,7 +99,7 @@ public class GadgetChest extends GadgetContent { } getGadget().updateState(ScriptGadgetState.ChestOpened); - player.sendPacket(new PacketGadgetInteractRsp(this.getGadget(), InteractTypeOuterClass.InteractType.INTERACT_TYPE_OPEN_CHEST)); + player.sendPacket(new PacketGadgetInteractRsp(this.getGadget(), InteractTypeOuterClass.InteractType.INTERACT_TYPE_OPEN_CHEST, InterOpType.INTER_OP_TYPE_FINISH)); return true; } From eb84f3f4266ed6a57ad7bbd89d649f981cfeb8ec Mon Sep 17 00:00:00 2001 From: dragon Date: Sun, 20 Nov 2022 11:48:16 +0800 Subject: [PATCH 4/8] Optimize boss chest rewards calculation; bug fix. --- .../emu/grasscutter/game/drop/DropSystem.java | 18 ++++++++++-------- .../game/entity/gadget/GadgetChest.java | 9 ++------- .../grasscutter/game/inventory/Inventory.java | 2 +- .../grasscutter/scripts/data/SceneMonster.java | 7 +++++-- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/main/java/emu/grasscutter/game/drop/DropSystem.java b/src/main/java/emu/grasscutter/game/drop/DropSystem.java index 10b06539..6973f380 100644 --- a/src/main/java/emu/grasscutter/game/drop/DropSystem.java +++ b/src/main/java/emu/grasscutter/game/drop/DropSystem.java @@ -10,7 +10,6 @@ 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.scripts.data.SceneBossChest; import emu.grasscutter.server.game.BaseGameSystem; import emu.grasscutter.server.game.GameServer; import emu.grasscutter.server.packet.send.PacketDropHintNotify; @@ -23,6 +22,8 @@ public class DropSystem extends BaseGameSystem { private final Int2ObjectMap dropTable; private final Map> chestReward; private final Random rand; + //TODO:don't know how to determine boss level.Have to hard-code the data from wiki. + private final int[] bossLevel = {36, 37, 41, 50, 62, 72, 83, 91, 93}; public DropSystem(GameServer server) { super(server); @@ -79,8 +80,8 @@ public class DropSystem extends BaseGameSystem { return handleChestDrop(dropId, 1, bornFrom); } - public boolean handleBossChestDrop(String dropTag, int level, SceneBossChest bossChest, Player player) { - int dropId = queryDropData(dropTag, level); + public boolean handleBossChestDrop(String dropTag, Player player) { + int dropId = queryDropData(dropTag, bossLevel[player.getWorldLevel()]); if (!dropTable.containsKey(dropId)) return false; var dropData = dropTable.get(dropId); List items = new ArrayList<>(); @@ -94,7 +95,10 @@ public class DropSystem extends BaseGameSystem { private void processDrop(DropTableData dropData, int count, List items) { //TODO:Not clear on the meaning of some fields,like "dropLevel".Will ignore them. //TODO:solve drop limits,like everydayLimit. - + if (count > 1) { + for (int i = 0; i < count; i++) processDrop(dropData, 1, items); + return; + } if (dropData.getRandomType() == 0) { int weightSum = 0; for (var i : dropData.getDropVec()) { @@ -147,10 +151,8 @@ public class DropSystem extends BaseGameSystem { amount = rand.nextInt(Integer.parseInt(ranges[0]), Integer.parseInt(ranges[1]) + 1); } else if (i.getCountRange().contains(".")) { double expectAmount = Double.parseDouble(i.getCountRange()); - int chance = (int) expectAmount + 1; - for (int k = 0; k < chance; k++) { - if (rand.nextDouble() < expectAmount / chance) amount++; - } + amount = (int) expectAmount; + if (rand.nextDouble() < expectAmount - amount) amount++; } else { amount = Integer.parseInt(i.getCountRange()); } diff --git a/src/main/java/emu/grasscutter/game/entity/gadget/GadgetChest.java b/src/main/java/emu/grasscutter/game/entity/gadget/GadgetChest.java index 8cd0abb0..860ce78b 100644 --- a/src/main/java/emu/grasscutter/game/entity/gadget/GadgetChest.java +++ b/src/main/java/emu/grasscutter/game/entity/gadget/GadgetChest.java @@ -1,8 +1,6 @@ package emu.grasscutter.game.entity.gadget; import emu.grasscutter.Grasscutter; -import emu.grasscutter.data.GameData; -import emu.grasscutter.data.excels.WorldLevelData; import emu.grasscutter.game.drop.DropSystem; import emu.grasscutter.game.entity.EntityGadget; import emu.grasscutter.game.entity.gadget.chest.BossChestInteractHandler; @@ -42,12 +40,9 @@ public class GadgetChest extends GadgetContent { player.sendPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_TYPE_OPEN_CHEST, InterOpType.INTER_OP_TYPE_START)); return false; } - //TODO:check for take_num.(some boss rewards can only be claimed once a week.) + //TODO:check for take_num.(some boss rewards can only be claimed once a week.). Handle boss respawn. //TODO:should return Retcode.RET_RESIN_NOT_ENOUGH ? - //not sure whether the level calculation is correct. - WorldLevelData worldLevelData = GameData.getWorldLevelDataMap().get(player.getWorldLevel()); - int level = worldLevelData.getMonsterLevel() + player.getLevel() - worldLevelData.getId() + chest.level; - if (player.getResinManager().useResin(chest.boss_chest.resin) && dropSystem.handleBossChestDrop(chest.drop_tag, level, chest.boss_chest, player)) { + if (player.getResinManager().useResin(chest.boss_chest.resin) && dropSystem.handleBossChestDrop(chest.drop_tag, player)) { getGadget().updateState(ScriptGadgetState.ChestOpened); player.sendPacket(new PacketGadgetInteractRsp(this.getGadget(), InteractTypeOuterClass.InteractType.INTERACT_TYPE_OPEN_CHEST, InterOpType.INTER_OP_TYPE_FINISH)); //TODO:need world chest notify? diff --git a/src/main/java/emu/grasscutter/game/inventory/Inventory.java b/src/main/java/emu/grasscutter/game/inventory/Inventory.java index cf9a970d..67e12edc 100644 --- a/src/main/java/emu/grasscutter/game/inventory/Inventory.java +++ b/src/main/java/emu/grasscutter/game/inventory/Inventory.java @@ -157,7 +157,7 @@ public class Inventory extends BasePlayerManager implements Iterable { return; } if (reason != null) { - getPlayer().sendPacket(new PacketItemAddHintNotify(changedItems, reason)); + getPlayer().sendPacket(new PacketItemAddHintNotify(items, reason)); } getPlayer().sendPacket(new PacketStoreItemChangeNotify(changedItems)); } diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneMonster.java b/src/main/java/emu/grasscutter/scripts/data/SceneMonster.java index 070a87fe..31ed1127 100644 --- a/src/main/java/emu/grasscutter/scripts/data/SceneMonster.java +++ b/src/main/java/emu/grasscutter/scripts/data/SceneMonster.java @@ -6,7 +6,10 @@ import lombok.ToString; @ToString @Setter public class SceneMonster extends SceneObject{ - public int monster_id; + public int config_id; + public int monster_id; public int pose_id; + public int level; public int drop_id; -} \ No newline at end of file + public String drop_tag; +} From cacd6c70a6c1d30c35f25ff172ef695026997e5b Mon Sep 17 00:00:00 2001 From: dragon Date: Mon, 21 Nov 2022 16:22:02 +0800 Subject: [PATCH 5/8] Implement monster drop.Optimize monster level calculation. --- .../grasscutter/game/drop/BaseDropData.java | 11 ++++ .../grasscutter/game/drop/ChestDropData.java | 6 +- .../emu/grasscutter/game/drop/DropData.java | 1 + .../emu/grasscutter/game/drop/DropSystem.java | 64 ++++++++++++++++--- .../game/entity/EntityMonster.java | 3 +- .../emu/grasscutter/game/world/Scene.java | 11 ++-- .../game/world/WorldDataSystem.java | 4 +- .../scripts/SceneScriptManager.java | 7 +- .../packet/send/PacketDropHintNotify.java | 14 ++-- 9 files changed, 89 insertions(+), 32 deletions(-) create mode 100644 src/main/java/emu/grasscutter/game/drop/BaseDropData.java diff --git a/src/main/java/emu/grasscutter/game/drop/BaseDropData.java b/src/main/java/emu/grasscutter/game/drop/BaseDropData.java new file mode 100644 index 00000000..021e2f47 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/drop/BaseDropData.java @@ -0,0 +1,11 @@ +package emu.grasscutter.game.drop; + +import lombok.Getter; + +@Getter +public class BaseDropData { + private int minLevel; + private String index; + private int dropId; + private int dropCount; +} diff --git a/src/main/java/emu/grasscutter/game/drop/ChestDropData.java b/src/main/java/emu/grasscutter/game/drop/ChestDropData.java index e2427a94..0ba263b7 100644 --- a/src/main/java/emu/grasscutter/game/drop/ChestDropData.java +++ b/src/main/java/emu/grasscutter/game/drop/ChestDropData.java @@ -3,11 +3,7 @@ package emu.grasscutter.game.drop; import lombok.Getter; @Getter -public class ChestDropData { - private int minLevel; - private String index; - private int dropId; - private int dropCount; +public class ChestDropData extends BaseDropData { private int sourceType; private String type; } diff --git a/src/main/java/emu/grasscutter/game/drop/DropData.java b/src/main/java/emu/grasscutter/game/drop/DropData.java index 601ae048..9c22c629 100644 --- a/src/main/java/emu/grasscutter/game/drop/DropData.java +++ b/src/main/java/emu/grasscutter/game/drop/DropData.java @@ -1,5 +1,6 @@ package emu.grasscutter.game.drop; +@Deprecated public class DropData { private int minWeight; private int maxWeight; diff --git a/src/main/java/emu/grasscutter/game/drop/DropSystem.java b/src/main/java/emu/grasscutter/game/drop/DropSystem.java index 6973f380..8d78097f 100644 --- a/src/main/java/emu/grasscutter/game/drop/DropSystem.java +++ b/src/main/java/emu/grasscutter/game/drop/DropSystem.java @@ -6,10 +6,12 @@ import emu.grasscutter.data.GameData; import emu.grasscutter.data.common.DropItemData; import emu.grasscutter.data.excels.DropMaterialData; import emu.grasscutter.data.excels.DropTableData; +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.scripts.data.SceneMonster; import emu.grasscutter.server.game.BaseGameSystem; import emu.grasscutter.server.game.GameServer; import emu.grasscutter.server.packet.send.PacketDropHintNotify; @@ -20,7 +22,8 @@ import java.util.*; public class DropSystem extends BaseGameSystem { private final Int2ObjectMap dropTable; - private final Map> chestReward; + private final Map> chestReward; + private final Map> monsterDrop; private final Random rand; //TODO:don't know how to determine boss level.Have to hard-code the data from wiki. private final int[] bossLevel = {36, 37, 41, 50, 62, 72, 83, 91, 93}; @@ -30,6 +33,7 @@ public class DropSystem extends BaseGameSystem { rand = new Random(); dropTable = GameData.getDropTableDataMap(); chestReward = new HashMap<>(); + monsterDrop = new HashMap<>(); try { List dataList = DataLoader.loadList("ChestDrop.json", ChestDropData.class); for (var i : dataList) { @@ -41,13 +45,23 @@ public class DropSystem extends BaseGameSystem { } catch (Exception e) { Grasscutter.getLogger().error("Unable to load chest drop data.Please place ChestDrop.json in data folder."); } - + try { + List dataList = DataLoader.loadList("MonsterDrop.json", BaseDropData.class); + for (var i : dataList) { + if (!monsterDrop.containsKey(i.getIndex())) { + monsterDrop.put(i.getIndex(), new ArrayList<>()); + } + monsterDrop.get(i.getIndex()).add(i); + } + } catch (Exception e) { + Grasscutter.getLogger().error("Unable to load monster drop data.Please place MonsterDrop.json in data folder."); + } } - private int queryDropData(String dropTag, int level) { - if (!chestReward.containsKey(dropTag)) return 0; - var rewardList = chestReward.get(dropTag); - ChestDropData dropData = null; + private int queryDropData(String dropTag, int level, Map> rewards) { + if (!rewards.containsKey(dropTag)) return 0; + var rewardList = rewards.get(dropTag); + BaseDropData dropData = null; int minLevel = 0; for (var i : rewardList) { if (level >= i.getMinLevel() && i.getMinLevel() > minLevel) { @@ -59,6 +73,33 @@ public class DropSystem extends BaseGameSystem { return dropData.getDropId(); } + public boolean handleMonsterDrop(EntityMonster monster) { + int dropId; + int level = monster.getLevel(); + SceneMonster sceneMonster = monster.getMetaMonster(); + if (sceneMonster != null) { + if (sceneMonster.drop_tag != null) { + dropId = queryDropData(sceneMonster.drop_tag, level, monsterDrop); + } else { + dropId = sceneMonster.drop_id; + } + } else { + dropId = monster.getMonsterData().getKillDropId(); + } + if (!dropTable.containsKey(dropId)) return false; + var dropData = dropTable.get(dropId); + List items = new ArrayList<>(); + processDrop(dropData, 1, items); + if (dropData.isFallToGround()) { + dropItems(items, ActionReason.MonsterDie, monster, monster.getScene().getPlayers().get(0), true); + } else { + for (Player p : monster.getScene().getPlayers()) { + p.getInventory().addItems(items, ActionReason.MonsterDie); + } + } + return true; + } + public boolean handleChestDrop(int chestDropId, int dropCount, GameEntity bornFrom) { Grasscutter.getLogger().info("ChestDrop:chest_drop_id={},drop_count={}", chestDropId, dropCount); if (!dropTable.containsKey(chestDropId)) return false; @@ -75,13 +116,13 @@ public class DropSystem extends BaseGameSystem { public boolean handleChestDrop(String dropTag, int level, GameEntity bornFrom) { Grasscutter.getLogger().info("ChestDrop:drop_tag={},level={}", dropTag, level); - int dropId = queryDropData(dropTag, level); + int dropId = queryDropData(dropTag, level, chestReward); if (dropId == 0) return false; return handleChestDrop(dropId, 1, bornFrom); } public boolean handleBossChestDrop(String dropTag, Player player) { - int dropId = queryDropData(dropTag, bossLevel[player.getWorldLevel()]); + int dropId = queryDropData(dropTag, bossLevel[player.getWorldLevel()], chestReward); if (!dropTable.containsKey(dropId)) return false; var dropData = dropTable.get(dropId); List items = new ArrayList<>(); @@ -145,7 +186,7 @@ public class DropSystem extends BaseGameSystem { } private int calculateDropAmount(DropItemData i) { - int amount = 0; + int amount; if (i.getCountRange().contains(";")) { String[] ranges = i.getCountRange().split(";"); amount = rand.nextInt(Integer.parseInt(ranges[0]), Integer.parseInt(ranges[1]) + 1); @@ -164,7 +205,7 @@ public class DropSystem extends BaseGameSystem { */ private void dropItem(GameItem item, ActionReason reason, Player player, GameEntity bornFrom, boolean share) { DropMaterialData drop = GameData.getDropMaterialDataMap().get(item.getItemId()); - if ((drop != null && drop.isAutoPick()) || item.getItemId() == 102) { + if ((drop != null && drop.isAutoPick()) || item.getItemId() == 101 || item.getItemId() == 102) { giveItem(item, reason, player, share); } else { //TODO:solve share problem @@ -195,9 +236,12 @@ public class DropSystem extends BaseGameSystem { if (share) { for (var p : player.getScene().getPlayers()) { p.getInventory().addItems(items, reason); + p.sendPacket(new PacketDropHintNotify(items, player.getPosition().toProto())); } } else { player.getInventory().addItems(items, reason); + player.sendPacket(new PacketDropHintNotify(items, player.getPosition().toProto())); } } + } diff --git a/src/main/java/emu/grasscutter/game/entity/EntityMonster.java b/src/main/java/emu/grasscutter/game/entity/EntityMonster.java index 19727b11..42ce0ac1 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityMonster.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityMonster.java @@ -28,6 +28,7 @@ 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.scripts.data.SceneMonster; import emu.grasscutter.scripts.data.ScriptArgs; import emu.grasscutter.utils.Position; import emu.grasscutter.utils.ProtoHelper; @@ -49,7 +50,7 @@ public class EntityMonster extends GameEntity { private int weaponEntityId; @Getter @Setter private int poseId; @Getter @Setter private int aiId = -1; - + @Getter @Setter private SceneMonster metaMonster; public EntityMonster(Scene scene, MonsterData monsterData, Position pos, int level) { super(scene); this.id = getWorld().getNextEntityId(EntityIdType.MONSTER); diff --git a/src/main/java/emu/grasscutter/game/world/Scene.java b/src/main/java/emu/grasscutter/game/world/Scene.java index 0e737485..da1cde6b 100644 --- a/src/main/java/emu/grasscutter/game/world/Scene.java +++ b/src/main/java/emu/grasscutter/game/world/Scene.java @@ -329,7 +329,8 @@ public class Scene { // Reward drop if (target instanceof EntityMonster && this.getSceneType() != SceneType.SCENE_DUNGEON) { - getWorld().getServer().getDropSystemLegacy().callDrop((EntityMonster) target); + if (!getWorld().getServer().getDropSystem().handleMonsterDrop((EntityMonster) target)) + getWorld().getServer().getDropSystemLegacy().callDrop((EntityMonster) target); } // Remove entity from world @@ -701,12 +702,12 @@ public class Scene { if (itemData.isEquip()) { float range = (1.5f + (.05f * amount)); for (int i = 0; i < amount; i++) { - Position pos = bornForm.getPosition().nearby2d(range).addY(1.5f); // Why Z? + Position pos = bornForm.getPosition().nearby2d(range).addY(1.5f); EntityItem entity = new EntityItem(this, null, itemData, pos, 1); addEntity(entity); } } else { - EntityItem entity = new EntityItem(this, null, itemData, bornForm.getPosition().clone().addY(1.5f), amount); // Why Z? + EntityItem entity = new EntityItem(this, null, itemData, bornForm.getPosition().clone().addY(1.5f), amount); addEntity(entity); } } @@ -718,12 +719,12 @@ public class Scene { if (itemData.isEquip()) { float range = (1.5f + (.05f * item.getCount())); for (int j = 0; j < item.getCount(); j++) { - Position pos = bornForm.getPosition().nearby2d(range).addY(1.5f); //TODO + Position pos = bornForm.getPosition().nearby2d(range).addY(1.5f); EntityItem entity = new EntityItem(this, player, itemData, pos, item.getCount(), share); addEntity(entity); } } else { - EntityItem entity = new EntityItem(this, player, itemData, bornForm.getPosition().clone().addY(1.5f), item.getCount(), share); //TODO:improve + EntityItem entity = new EntityItem(this, player, itemData, bornForm.getPosition().clone().addY(1.5f), item.getCount(), share); addEntity(entity); } diff --git a/src/main/java/emu/grasscutter/game/world/WorldDataSystem.java b/src/main/java/emu/grasscutter/game/world/WorldDataSystem.java index 0df4c69c..674d5230 100644 --- a/src/main/java/emu/grasscutter/game/world/WorldDataSystem.java +++ b/src/main/java/emu/grasscutter/game/world/WorldDataSystem.java @@ -51,7 +51,7 @@ public class WorldDataSystem extends BaseGameSystem { public Map getChestInteractHandlerMap() { return chestInteractHandlerMap; } - + @Deprecated public RewardPreviewData getRewardByBossId(int monsterId) { var investigationMonsterData = GameData.getInvestigationMonsterDataMap().values().parallelStream() .filter(imd -> imd.getMonsterIdList() != null && !imd.getMonsterIdList().isEmpty()) @@ -80,7 +80,7 @@ public class WorldDataSystem extends BaseGameSystem { WorldLevelData worldLevelData = GameData.getWorldLevelDataMap().get(world.getWorldLevel()); if (worldLevelData != null) { - level = worldLevelData.getMonsterLevel(); + level = Math.max(level, worldLevelData.getMonsterLevel()); } return level; } diff --git a/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java b/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java index 02ebfbec..6385c525 100644 --- a/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java +++ b/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java @@ -433,12 +433,12 @@ public class SceneScriptManager { int level = monster.level; if (getScene().getDungeonData() != null) { - level = getScene().getDungeonData().getShowLevel(); - } else if (getScene().getWorld().getWorldLevel() > 0) { + level = Math.max(level, getScene().getDungeonData().getShowLevel()); + } else { WorldLevelData worldLevelData = GameData.getWorldLevelDataMap().get(getScene().getWorld().getWorldLevel()); if (worldLevelData != null) { - level = worldLevelData.getMonsterLevel(); + level = Math.max(level, worldLevelData.getMonsterLevel()); } } @@ -449,6 +449,7 @@ public class SceneScriptManager { entity.setBlockId(blockId); entity.setConfigId(monster.config_id); entity.setPoseId(monster.pose_id); + entity.setMetaMonster(monster); this.getScriptMonsterSpawnService() .onMonsterCreatedListener.forEach(action -> action.onNotify(entity)); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketDropHintNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketDropHintNotify.java index 3c3f4e90..1251b68f 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketDropHintNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketDropHintNotify.java @@ -1,23 +1,25 @@ package emu.grasscutter.server.packet.send; +import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.DropHintNotifyOuterClass.DropHintNotify; import emu.grasscutter.net.proto.VectorOuterClass.Vector; public class PacketDropHintNotify extends BasePacket { - public PacketDropHintNotify(int itemId, Vector position){ + public PacketDropHintNotify(int itemId, Vector position) { super(PacketOpcodes.DropHintNotify); - var proto= DropHintNotify.newBuilder() + var proto = DropHintNotify.newBuilder() .addItemIdList(itemId) .setPosition(position); setData(proto.build()); } - public PacketDropHintNotify(Iterable items, Vector position){ + + public PacketDropHintNotify(Iterable items, Vector position) { super(PacketOpcodes.DropHintNotify); - var proto= DropHintNotify.newBuilder() - .addAllItemIdList(items) - .setPosition(position); + var proto = DropHintNotify.newBuilder(); + items.forEach(i -> proto.addItemIdList(i.getItemId())); + proto.setPosition(position); setData(proto.build()); } } From fe175c5337a47db8202a00ddd3c7c0bbbc0026ef Mon Sep 17 00:00:00 2001 From: dragon Date: Wed, 23 Nov 2022 11:46:29 +0800 Subject: [PATCH 6/8] remove test logs; bug fix --- .../emu/grasscutter/game/drop/DropSystem.java | 40 ++++++++++++------- .../game/entity/gadget/GadgetChest.java | 7 ++-- .../emu/grasscutter/game/world/Scene.java | 13 ++---- 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/src/main/java/emu/grasscutter/game/drop/DropSystem.java b/src/main/java/emu/grasscutter/game/drop/DropSystem.java index 8d78097f..ca2a1986 100644 --- a/src/main/java/emu/grasscutter/game/drop/DropSystem.java +++ b/src/main/java/emu/grasscutter/game/drop/DropSystem.java @@ -9,6 +9,7 @@ import emu.grasscutter.data.excels.DropTableData; import emu.grasscutter.game.entity.EntityMonster; import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.game.inventory.GameItem; +import emu.grasscutter.game.inventory.ItemType; import emu.grasscutter.game.player.Player; import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.scripts.data.SceneMonster; @@ -101,7 +102,6 @@ public class DropSystem extends BaseGameSystem { } public boolean handleChestDrop(int chestDropId, int dropCount, GameEntity bornFrom) { - Grasscutter.getLogger().info("ChestDrop:chest_drop_id={},drop_count={}", chestDropId, dropCount); if (!dropTable.containsKey(chestDropId)) return false; var dropData = dropTable.get(chestDropId); List items = new ArrayList<>(); @@ -115,7 +115,6 @@ public class DropSystem extends BaseGameSystem { } public boolean handleChestDrop(String dropTag, int level, GameEntity bornFrom) { - Grasscutter.getLogger().info("ChestDrop:drop_tag={},level={}", dropTag, level); int dropId = queryDropData(dropTag, level, chestReward); if (dropId == 0) return false; return handleChestDrop(dropId, 1, bornFrom); @@ -127,7 +126,6 @@ public class DropSystem extends BaseGameSystem { var dropData = dropTable.get(dropId); List items = new ArrayList<>(); processDrop(dropData, 1, items); - //TODO:test if there still need some packets. player.getInventory().addItems(items, ActionReason.OpenWorldBossChest); player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(items)); return true; @@ -157,12 +155,19 @@ public class DropSystem extends BaseGameSystem { if (weight < sum) { //win the item int amount = calculateDropAmount(i) * count; - if (amount > 0) { - if (dropTable.containsKey(id)) { - processDrop(dropTable.get(id), amount, items); - } else { - items.add(new GameItem(id, amount)); + if (amount <= 0) break; + if (dropTable.containsKey(id)) { + processDrop(dropTable.get(id), amount, items); + } else { + boolean flag = true; + for (var j : items) { + if (j.getItemId() == id) { + j.setCount(j.getCount() + amount); + flag = false; + break; + } } + if (flag) items.add(new GameItem(id, amount)); } break; } @@ -173,12 +178,19 @@ public class DropSystem extends BaseGameSystem { if (id == 0) continue; if (rand.nextInt(10000) < i.getWeight()) { int amount = calculateDropAmount(i) * count; - if (amount > 0) { - if (dropTable.containsKey(id)) { - processDrop(dropTable.get(id), amount, items); - } else { - items.add(new GameItem(id, amount)); + if (amount <= 0) continue; + if (dropTable.containsKey(id)) { + processDrop(dropTable.get(id), amount, items); + } else { + boolean flag = true; + for (var j : items) { + if (j.getItemId() == id) { + j.setCount(j.getCount() + amount); + flag = false; + break; + } } + if (flag) items.add(new GameItem(id, amount)); } } } @@ -205,7 +217,7 @@ public class DropSystem extends BaseGameSystem { */ private void dropItem(GameItem item, ActionReason reason, Player player, GameEntity bornFrom, boolean share) { DropMaterialData drop = GameData.getDropMaterialDataMap().get(item.getItemId()); - if ((drop != null && drop.isAutoPick()) || item.getItemId() == 101 || item.getItemId() == 102) { + if ((drop != null && drop.isAutoPick()) || (item.getItemData().getItemType() == ItemType.ITEM_VIRTUAL && item.getItemData().getGadgetId() == 0)) { giveItem(item, reason, player, share); } else { //TODO:solve share problem diff --git a/src/main/java/emu/grasscutter/game/entity/gadget/GadgetChest.java b/src/main/java/emu/grasscutter/game/entity/gadget/GadgetChest.java index 860ce78b..2b6b0ea1 100644 --- a/src/main/java/emu/grasscutter/game/entity/gadget/GadgetChest.java +++ b/src/main/java/emu/grasscutter/game/entity/gadget/GadgetChest.java @@ -5,6 +5,7 @@ import emu.grasscutter.game.drop.DropSystem; import emu.grasscutter.game.entity.EntityGadget; import emu.grasscutter.game.entity.gadget.chest.BossChestInteractHandler; import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.props.WatcherTriggerType; import emu.grasscutter.net.proto.BossChestInfoOuterClass.BossChestInfo; import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq; import emu.grasscutter.net.proto.InterOpTypeOuterClass.InterOpType; @@ -30,7 +31,6 @@ public class GadgetChest extends GadgetContent { //If bigWorldScript enabled,use new drop system. if (Grasscutter.getConfig().server.game.enableScriptInBigWorld) { SceneGadget chest = getGadget().getMetaGadget(); - Grasscutter.getLogger().info("OpenChest:chest_drop_id={},drop_tag={}", chest.chest_drop_id, chest.drop_tag); DropSystem dropSystem = player.getServer().getDropSystem(); if (chest.boss_chest != null && chest.drop_tag != null) { //Boss chest drop @@ -43,9 +43,10 @@ public class GadgetChest extends GadgetContent { //TODO:check for take_num.(some boss rewards can only be claimed once a week.). Handle boss respawn. //TODO:should return Retcode.RET_RESIN_NOT_ENOUGH ? if (player.getResinManager().useResin(chest.boss_chest.resin) && dropSystem.handleBossChestDrop(chest.drop_tag, player)) { + //Is it correct? + player.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_WORLD_BOSS_REWARD,chest.boss_chest.monster_config_id,1); getGadget().updateState(ScriptGadgetState.ChestOpened); player.sendPacket(new PacketGadgetInteractRsp(this.getGadget(), InteractTypeOuterClass.InteractType.INTERACT_TYPE_OPEN_CHEST, InterOpType.INTER_OP_TYPE_FINISH)); - //TODO:need world chest notify? return true; } //if failed,fallback to legacy drop system. @@ -67,7 +68,7 @@ public class GadgetChest extends GadgetContent { } //if failed,fallback to legacy drop system. } - Grasscutter.getLogger().warn("Can not solve chest drop:chest_drop_id={},drop_tag={}.Fallback to legacy drop system.", chest.chest_drop_id, chest.drop_tag); + Grasscutter.getLogger().warn("Can not solve chest drop: chest_drop_id = {} , drop_tag = {}.Fallback to legacy drop system.", chest.chest_drop_id, chest.drop_tag); } //Legacy chest drop system diff --git a/src/main/java/emu/grasscutter/game/world/Scene.java b/src/main/java/emu/grasscutter/game/world/Scene.java index da1cde6b..974813f7 100644 --- a/src/main/java/emu/grasscutter/game/world/Scene.java +++ b/src/main/java/emu/grasscutter/game/world/Scene.java @@ -329,8 +329,10 @@ public class Scene { // Reward drop if (target instanceof EntityMonster && this.getSceneType() != SceneType.SCENE_DUNGEON) { - if (!getWorld().getServer().getDropSystem().handleMonsterDrop((EntityMonster) target)) + if (!getWorld().getServer().getDropSystem().handleMonsterDrop((EntityMonster) target)) { + Grasscutter.getLogger().warn("Can not solve monster drop: drop_id = {} , drop_tag = {}.Fallback to legacy drop system.", ((EntityMonster) target).getMetaMonster().drop_id, ((EntityMonster) target).getMetaMonster().drop_tag); getWorld().getServer().getDropSystemLegacy().callDrop((EntityMonster) target); + } } // Remove entity from world @@ -730,15 +732,6 @@ public class Scene { } - /** - * @param share If false,only the player can see the items but others can't.If true,all players will see. - */ - public void addDropEntities(List items, GameEntity bornForm, Player player, boolean share) { - //TODO:optimize EntityItem.java. Maybe we should make other players can't see the ItemEntity. - for (var i : items) { - addDropEntity(i, bornForm, player, share); - } - } public void loadNpcForPlayerEnter(Player player) { this.npcBornEntrySet.addAll(loadNpcForPlayer(player)); From eb3ef1038bee388e89d7bae35ee8c630d92e36ad Mon Sep 17 00:00:00 2001 From: dragon Date: Wed, 23 Nov 2022 12:08:01 +0800 Subject: [PATCH 7/8] fix isOneOff improper usage. --- .../java/emu/grasscutter/game/entity/gadget/GadgetChest.java | 2 +- src/main/java/emu/grasscutter/scripts/data/SceneGadget.java | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/emu/grasscutter/game/entity/gadget/GadgetChest.java b/src/main/java/emu/grasscutter/game/entity/gadget/GadgetChest.java index 2b6b0ea1..7b43ad28 100644 --- a/src/main/java/emu/grasscutter/game/entity/gadget/GadgetChest.java +++ b/src/main/java/emu/grasscutter/game/entity/gadget/GadgetChest.java @@ -64,7 +64,7 @@ public class GadgetChest extends GadgetContent { getGadget().updateState(ScriptGadgetState.ChestOpened); player.sendPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_TYPE_OPEN_CHEST, InterOpType.INTER_OP_TYPE_FINISH)); player.sendPacket(new PacketWorldChestOpenNotify(getGadget().getGroupId(), player.getSceneId(), chest.config_id)); - return chest.isOneoff; + return true; } //if failed,fallback to legacy drop system. } diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneGadget.java b/src/main/java/emu/grasscutter/scripts/data/SceneGadget.java index fb9c901a..71d9b10e 100644 --- a/src/main/java/emu/grasscutter/scripts/data/SceneGadget.java +++ b/src/main/java/emu/grasscutter/scripts/data/SceneGadget.java @@ -20,6 +20,11 @@ public class SceneGadget extends SceneObject{ public int owner; public SceneBossChest boss_chest; public int interact_id; + /** + * Note: this field indicates whether the gadget should disappear permanently. + * For example, if isOneOff=true, like most chests, it will disappear permanently after interacted. + * If isOneOff=false, like investigation points, it will disappear temporarily, and appear again in next big world resource refresh routine. + */ public boolean isOneoff; public int area_id; public int draft_id; From c9cf52de22cafdc1559bd21d1fa97de42b0fc32b Mon Sep 17 00:00:00 2001 From: dragon Date: Thu, 24 Nov 2022 18:21:19 +0800 Subject: [PATCH 8/8] change load path of drop data --- src/main/java/emu/grasscutter/utils/FileUtils.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/emu/grasscutter/utils/FileUtils.java b/src/main/java/emu/grasscutter/utils/FileUtils.java index 574bfd6c..e316448b 100644 --- a/src/main/java/emu/grasscutter/utils/FileUtils.java +++ b/src/main/java/emu/grasscutter/utils/FileUtils.java @@ -8,7 +8,10 @@ import java.io.IOException; import java.io.InputStream; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; -import java.nio.file.*; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -113,7 +116,9 @@ public final class FileUtils { } public static Path getExcelPath(String filename) { - return getTsjJsonTsv(RESOURCES_PATH.resolve("ExcelBinOutput"), filename); + Path p = getTsjJsonTsv(RESOURCES_PATH.resolve("Server"), filename); + if (Files.exists(p)) return p; + else return getTsjJsonTsv(RESOURCES_PATH.resolve("ExcelBinOutput"), filename); } // Gets path of a resource.