From abd1e7569e4a59897f2365ff7c99f9e4638e97ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E6=80=A1=E7=84=B6?= <63996691+zhaodice@users.noreply.github.com> Date: Sun, 21 Aug 2022 14:19:59 +0800 Subject: [PATCH] Blossom Implement (#1606) * Blossom! * rename * delete SpawnBlossomEntry.java * use MAP * use List * use LIST * use List * useCondensedResin * useCondensedResin * fix build * enhance * fix bug * REMOVE BOSS * fix condensed resin * fix condensed resin * use POSITIVE_INFINITY * use RewardPreviewData * fix build * fix resources * add BLOSSOM_MONSTER_FIGHTING_VOLUME * edit monster score * edit monster score * fix bug * fix bug * improve logic * fix monsters level * Deleted comment blocks * nitpick * Fix compilation problems * nitpick * Refactor + nitpick * Clean up overall diff to develop * Clean up other usage of condensed resin * Clean up overall diff to develop * Lombokify Scene.java * Missed an odd getter name * Unhardcode reward previews * EDIT NAME * remove leyline 1 * remove leyline 2 * Update BlossomManager.java Co-authored-by: AnimeGitB --- .../java/emu/grasscutter/data/GameData.java | 3 +- .../java/emu/grasscutter/data/GameDepot.java | 20 +- .../emu/grasscutter/data/ResourceLoader.java | 8 + .../excels/BlossomRefreshExcelConfigData.java | 46 ++++ .../dungeons/challenge/DungeonChallenge.java | 29 +-- .../grasscutter/game/entity/GameEntity.java | 11 +- .../game/entity/gadget/GadgetChest.java | 10 +- .../game/entity/gadget/GadgetWorktop.java | 24 +- .../chest/BossChestInteractHandler.java | 11 + .../worktop/WorktopWorktopOptionHandler.java | 6 + .../game/managers/ResinManager.java | 6 + .../managers/blossom/BlossomActivity.java | 135 ++++++++++ .../game/managers/blossom/BlossomConfig.java | 12 + .../game/managers/blossom/BlossomManager.java | 237 ++++++++++++++++++ .../game/managers/blossom/BlossomType.java | 37 +++ .../emu/grasscutter/game/world/Scene.java | 156 ++++-------- .../game/world/SpawnDataEntry.java | 102 ++------ .../recv/HandlerSelectWorktopOptionReq.java | 12 +- .../send/PacketBlossomBriefInfoNotify.java | 13 + .../java/emu/grasscutter/utils/Position.java | 45 ++-- .../defaults/data/BlossomConfig.json | 8 + 21 files changed, 670 insertions(+), 261 deletions(-) create mode 100644 src/main/java/emu/grasscutter/data/excels/BlossomRefreshExcelConfigData.java create mode 100644 src/main/java/emu/grasscutter/game/entity/gadget/worktop/WorktopWorktopOptionHandler.java create mode 100644 src/main/java/emu/grasscutter/game/managers/blossom/BlossomActivity.java create mode 100644 src/main/java/emu/grasscutter/game/managers/blossom/BlossomConfig.java create mode 100644 src/main/java/emu/grasscutter/game/managers/blossom/BlossomManager.java create mode 100644 src/main/java/emu/grasscutter/game/managers/blossom/BlossomType.java create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketBlossomBriefInfoNotify.java create mode 100644 src/main/resources/defaults/data/BlossomConfig.json diff --git a/src/main/java/emu/grasscutter/data/GameData.java b/src/main/java/emu/grasscutter/data/GameData.java index e56140b83..b63a7906d 100644 --- a/src/main/java/emu/grasscutter/data/GameData.java +++ b/src/main/java/emu/grasscutter/data/GameData.java @@ -113,6 +113,7 @@ public class GameData { @Getter private static final Int2ObjectMap triggerExcelConfigDataMap = new Int2ObjectOpenHashMap<>(); @Getter private static final Map scriptSceneDataMap = new HashMap<>(); @Getter private static final Map> scenePointsPerScene = new HashMap<>(); + @Getter private static final Int2ObjectMap blossomRefreshExcelConfigDataMap = new Int2ObjectOpenHashMap<>(); @Getter private static final Int2ObjectMap openStateDataMap = new Int2ObjectOpenHashMap<>(); @@ -124,7 +125,7 @@ public class GameData { @Getter private static final List openStateList = new ArrayList<>(); - + public static Int2ObjectMap getMapByResourceDef(Class resourceDefinition) { Int2ObjectMap map = null; diff --git a/src/main/java/emu/grasscutter/data/GameDepot.java b/src/main/java/emu/grasscutter/data/GameDepot.java index a809105fa..f9d9bfcd1 100644 --- a/src/main/java/emu/grasscutter/data/GameDepot.java +++ b/src/main/java/emu/grasscutter/data/GameDepot.java @@ -10,10 +10,13 @@ import emu.grasscutter.Grasscutter; import emu.grasscutter.data.ResourceLoader.AvatarConfig; import emu.grasscutter.data.excels.ReliquaryAffixData; import emu.grasscutter.data.excels.ReliquaryMainPropData; +import emu.grasscutter.game.managers.blossom.BlossomConfig; import emu.grasscutter.game.world.SpawnDataEntry; import emu.grasscutter.utils.WeightedList; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import lombok.Getter; +import lombok.Setter; public class GameDepot { public static final int[] BLOCK_SIZE = new int[]{50,500};//Scales @@ -22,8 +25,9 @@ public class GameDepot { private static Int2ObjectMap> relicMainPropDepot = new Int2ObjectOpenHashMap<>(); private static Int2ObjectMap> relicAffixDepot = new Int2ObjectOpenHashMap<>(); - private static Map playerAbilities = new HashMap<>(); - private static HashMap> spawnLists = new HashMap<>(); + @Getter @Setter private static Map playerAbilities = new HashMap<>(); + @Getter private static HashMap> spawnLists = new HashMap<>(); + @Getter @Setter private static BlossomConfig blossomConfig; public static void load() { for (ReliquaryMainPropData data : GameData.getReliquaryMainPropDataMap().values()) { @@ -64,19 +68,7 @@ public class GameDepot { return relicAffixDepot.get(depot); } - public static HashMap> getSpawnLists() { - return spawnLists; - } - public static void addSpawnListById(HashMap> data) { spawnLists.putAll(data); } - - public static void setPlayerAbilities(Map playerAbilities) { - GameDepot.playerAbilities = playerAbilities; - } - - public static Map getPlayerAbilities() { - return playerAbilities; - } } diff --git a/src/main/java/emu/grasscutter/data/ResourceLoader.java b/src/main/java/emu/grasscutter/data/ResourceLoader.java index b5d517078..dd15e2059 100644 --- a/src/main/java/emu/grasscutter/data/ResourceLoader.java +++ b/src/main/java/emu/grasscutter/data/ResourceLoader.java @@ -9,6 +9,7 @@ import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction; import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierActionType; import emu.grasscutter.data.common.PointData; import emu.grasscutter.data.common.ScenePointConfig; +import emu.grasscutter.game.managers.blossom.BlossomConfig; import emu.grasscutter.game.quest.QuestEncryptionKey; import emu.grasscutter.game.world.SpawnDataEntry; import emu.grasscutter.game.world.SpawnDataEntry.GridBlockId; @@ -75,6 +76,7 @@ public class ResourceLoader { // Load default home layout loadHomeworldDefaultSaveData(); loadNpcBornData(); + loadBlossomResources(); Grasscutter.getLogger().info(translate("messages.status.resources.finish")); loadedAll = true; @@ -486,6 +488,12 @@ public class ResourceLoader { Grasscutter.getLogger().debug("Loaded " + GameData.getSceneNpcBornData().size() + " SceneNpcBornDatas."); } + @SneakyThrows + private static void loadBlossomResources() { + GameDepot.setBlossomConfig(DataLoader.loadClass("BlossomConfig.json", BlossomConfig.class)); + Grasscutter.getLogger().debug("Loaded BlossomConfig."); + } + // BinOutput configs public static class AvatarConfig { diff --git a/src/main/java/emu/grasscutter/data/excels/BlossomRefreshExcelConfigData.java b/src/main/java/emu/grasscutter/data/excels/BlossomRefreshExcelConfigData.java new file mode 100644 index 000000000..1640cc50a --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/BlossomRefreshExcelConfigData.java @@ -0,0 +1,46 @@ +package emu.grasscutter.data.excels; + +import java.util.List; + +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; +import lombok.Getter; + +@ResourceType(name = "BlossomRefreshExcelConfigData.json") +public class BlossomRefreshExcelConfigData extends GameResource { + private int id; + // Map details + @Getter private long nameTextMapHash; + @Getter private long descTextMapHash; + @Getter private String icon; + @Getter private String clientShowType; // BLOSSOM_SHOWTYPE_CHALLENGE, BLOSSOM_SHOWTYPE_NPCTALK + + // Refresh details + @Getter private String refreshType; // Leyline blossoms, magical ore outcrops + @Getter private int refreshCount; // Number of entries to spawn at refresh (1 for each leyline type for each city, 4 for magical ore for each city) + @Getter private String refreshTime; // Server time-of-day to refresh at + @Getter private RefreshCond[] refreshCondVec; // AR requirements etc. + + @Getter private int cityId; + @Getter private int blossomChestId; // 1 for mora, 2 for exp + @Getter private Drop[] dropVec; + + // Unknown details + // @Getter private int reviseLevel; + // @Getter private int campUpdateNeedCount; // Always 1 if specified + + @Override + public int getId() { + return id; + } + + public static class Drop { + @Getter int dropId; + @Getter int previewReward; + } + + public static class RefreshCond { + @Getter String type; + @Getter List param; + } +} diff --git a/src/main/java/emu/grasscutter/game/dungeons/challenge/DungeonChallenge.java b/src/main/java/emu/grasscutter/game/dungeons/challenge/DungeonChallenge.java index 3455b4add..3238dc9bd 100644 --- a/src/main/java/emu/grasscutter/game/dungeons/challenge/DungeonChallenge.java +++ b/src/main/java/emu/grasscutter/game/dungeons/challenge/DungeonChallenge.java @@ -8,12 +8,12 @@ import emu.grasscutter.game.dungeons.DungeonDrop; import emu.grasscutter.game.dungeons.DungeonDropEntry; import emu.grasscutter.game.dungeons.challenge.trigger.ChallengeTrigger; 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.props.WatcherTriggerType; import emu.grasscutter.game.world.Scene; import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq; +import emu.grasscutter.net.proto.ResinCostTypeOuterClass; import emu.grasscutter.scripts.constants.EventType; import emu.grasscutter.scripts.data.SceneGroup; import emu.grasscutter.scripts.data.ScriptArgs; @@ -88,11 +88,12 @@ public class DungeonChallenge extends WorldChallenge { private void settle() { if (!stage) { - getScene().getDungeonSettleObservers().forEach(o -> o.onDungeonSettle(getScene())); - getScene().getScriptManager().callEvent(EventType.EVENT_DUNGEON_SETTLE, + var scene = this.getScene(); + scene.getDungeonSettleListeners().forEach(o -> o.onDungeonSettle(getScene())); + scene.getScriptManager().callEvent(EventType.EVENT_DUNGEON_SETTLE, new ScriptArgs(this.isSuccess() ? 1 : 0)); // Battle pass trigger - this.getScene().getPlayers().forEach(p -> p.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_FINISH_DUNGEON)); + scene.getPlayers().forEach(p -> p.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_FINISH_DUNGEON)); } } @@ -166,7 +167,7 @@ public class DungeonChallenge extends WorldChallenge { // Get rewards. List rewards = new ArrayList<>(); - if (request.getIsUseCondenseResin()) { + if (request.getResinCostType() == ResinCostTypeOuterClass.ResinCostType.RESIN_COST_TYPE_CONDENSE) { // Check if condensed resin is usable here. // For this, we use the following logic for now: // The normal resin cost of the dungeon has to be 20. @@ -174,25 +175,15 @@ public class DungeonChallenge extends WorldChallenge { return; } - // Make sure the player has condensed resin. - GameItem condensedResin = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(220007); - if (condensedResin == null || condensedResin.getCount() <= 0) { - return; - } - - // Deduct. - player.getInventory().removeItem(condensedResin, 1); + // Spend the condensed resin and only proceed if the transaction succeeds. + if (!player.getResinManager().useCondensedResin(1)) return; // Roll rewards. rewards.addAll(this.rollRewards(true)); } else { - // If the player used regular resin, try to deduct. - // Stop if insufficient resin. - boolean success = player.getResinManager().useResin(resinCost); - if (!success) { - return; - } + // Spend the resin and only proceed if the transaction succeeds. + if (!player.getResinManager().useResin(resinCost)) return; // Roll rewards. rewards.addAll(this.rollRewards(false)); diff --git a/src/main/java/emu/grasscutter/game/entity/GameEntity.java b/src/main/java/emu/grasscutter/game/entity/GameEntity.java index c08590649..2e06d5def 100644 --- a/src/main/java/emu/grasscutter/game/entity/GameEntity.java +++ b/src/main/java/emu/grasscutter/game/entity/GameEntity.java @@ -215,12 +215,15 @@ public abstract class GameEntity { // Invoke entity damage event. EntityDamageEvent event = new EntityDamageEvent(this, amount, this.getScene().getEntityById(killerId)); - event.call(); if (event.isCanceled()) { + event.call(); + if (event.isCanceled()) { return; // If the event is canceled, do not damage the entity. } - - // Add negative HP to the current HP property. - this.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, -(event.getDamage())); + + if(getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) != Float.POSITIVE_INFINITY){ + // Add negative HP to the current HP property. + this.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, -(event.getDamage())); + } // Check if dead boolean isDead = false; 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 43e6e0b80..afee4d332 100644 --- a/src/main/java/emu/grasscutter/game/entity/gadget/GadgetChest.java +++ b/src/main/java/emu/grasscutter/game/entity/gadget/GadgetChest.java @@ -2,6 +2,7 @@ package emu.grasscutter.game.entity.gadget; import emu.grasscutter.Grasscutter; 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; @@ -9,6 +10,7 @@ import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq; import emu.grasscutter.net.proto.InterOpTypeOuterClass.InterOpType; import emu.grasscutter.net.proto.InteractTypeOuterClass; 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.server.packet.send.PacketGadgetInteractRsp; @@ -32,7 +34,13 @@ public class GadgetChest extends GadgetContent { player.sendPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_TYPE_OPEN_CHEST, InterOpType.INTER_OP_TYPE_START)); return false; }else { - var success = handler.onInteract(this, player); + boolean success; + if(handler instanceof BossChestInteractHandler bossChestInteractHandler){ + success = bossChestInteractHandler.onInteract(this, player, + req.getResinCostType()== ResinCostTypeOuterClass.ResinCostType.RESIN_COST_TYPE_CONDENSE); + }else{ + success = handler.onInteract(this, player); + } if (!success) { return false; } diff --git a/src/main/java/emu/grasscutter/game/entity/gadget/GadgetWorktop.java b/src/main/java/emu/grasscutter/game/entity/gadget/GadgetWorktop.java index 0bdf321ec..cc4669a22 100644 --- a/src/main/java/emu/grasscutter/game/entity/gadget/GadgetWorktop.java +++ b/src/main/java/emu/grasscutter/game/entity/gadget/GadgetWorktop.java @@ -3,31 +3,34 @@ package emu.grasscutter.game.entity.gadget; import java.util.Arrays; import emu.grasscutter.game.entity.EntityGadget; +import emu.grasscutter.game.entity.gadget.worktop.WorktopWorktopOptionHandler; import emu.grasscutter.game.player.Player; import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq; import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo; +import emu.grasscutter.net.proto.SelectWorktopOptionReqOuterClass.SelectWorktopOptionReq; import emu.grasscutter.net.proto.WorktopInfoOuterClass.WorktopInfo; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; public class GadgetWorktop extends GadgetContent { private IntSet worktopOptions; - + private WorktopWorktopOptionHandler handler; + public GadgetWorktop(EntityGadget gadget) { super(gadget); } - + public IntSet getWorktopOptions() { return worktopOptions; } - + public void addWorktopOptions(int[] options) { if (this.worktopOptions == null) { this.worktopOptions = new IntOpenHashSet(); } Arrays.stream(options).forEach(this.worktopOptions::add); } - + public void removeWorktopOption(int option) { if (this.worktopOptions == null) { return; @@ -43,11 +46,20 @@ public class GadgetWorktop extends GadgetContent { if (this.worktopOptions == null) { return; } - + WorktopInfo worktop = WorktopInfo.newBuilder() .addAllOptionList(this.getWorktopOptions()) .build(); - + gadgetInfo.setWorktop(worktop); } + + public void setOnSelectWorktopOptionEvent(WorktopWorktopOptionHandler handler) { + this.handler = handler; + } + public boolean onSelectWorktopOption(SelectWorktopOptionReq req) { + this.handler.onSelectWorktopOption(this,req.getOptionId()); + return false; + } + } diff --git a/src/main/java/emu/grasscutter/game/entity/gadget/chest/BossChestInteractHandler.java b/src/main/java/emu/grasscutter/game/entity/gadget/chest/BossChestInteractHandler.java index d02a3760d..a740ff18c 100644 --- a/src/main/java/emu/grasscutter/game/entity/gadget/chest/BossChestInteractHandler.java +++ b/src/main/java/emu/grasscutter/game/entity/gadget/chest/BossChestInteractHandler.java @@ -19,6 +19,17 @@ public class BossChestInteractHandler implements ChestInteractHandler{ @Override public boolean onInteract(GadgetChest chest, Player player) { + return this.onInteract(chest,player,false); + } + + public boolean onInteract(GadgetChest chest, Player player,boolean useCondensedResin) { + var blossomRewards = player.getScene().getBlossomManager().onReward(player,chest.getGadget(),useCondensedResin); + if(blossomRewards!=null) { + player.getInventory().addItems(blossomRewards, ActionReason.OpenWorldBossChest); + player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(blossomRewards)); + return true; + } + var worldDataManager = chest.getGadget().getScene().getWorld().getServer().getWorldDataSystem(); var monster = chest.getGadget().getMetaGadget().group.monsters.get(chest.getGadget().getMetaGadget().boss_chest.monster_config_id); var reward = worldDataManager.getRewardByBossId(monster.monster_id); diff --git a/src/main/java/emu/grasscutter/game/entity/gadget/worktop/WorktopWorktopOptionHandler.java b/src/main/java/emu/grasscutter/game/entity/gadget/worktop/WorktopWorktopOptionHandler.java new file mode 100644 index 000000000..f6aa1a92d --- /dev/null +++ b/src/main/java/emu/grasscutter/game/entity/gadget/worktop/WorktopWorktopOptionHandler.java @@ -0,0 +1,6 @@ +package emu.grasscutter.game.entity.gadget.worktop; + +import emu.grasscutter.game.entity.gadget.GadgetWorktop; +public interface WorktopWorktopOptionHandler { + boolean onSelectWorktopOption(GadgetWorktop gadgetWorktop,int option); +} \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/game/managers/ResinManager.java b/src/main/java/emu/grasscutter/game/managers/ResinManager.java index aa85f61ac..d6056571e 100644 --- a/src/main/java/emu/grasscutter/game/managers/ResinManager.java +++ b/src/main/java/emu/grasscutter/game/managers/ResinManager.java @@ -52,6 +52,12 @@ public class ResinManager extends BasePlayerManager { return true; } + public synchronized boolean useCondensedResin(int amount) { + // Don't deduct if resin disabled. + if (!GAME_OPTIONS.resinOptions.resinUsage) return true; + return this.player.getInventory().payItem(220007, amount); + } + public synchronized void addResin(int amount) { // Check if resin enabled. if (!GAME_OPTIONS.resinOptions.resinUsage) { diff --git a/src/main/java/emu/grasscutter/game/managers/blossom/BlossomActivity.java b/src/main/java/emu/grasscutter/game/managers/blossom/BlossomActivity.java new file mode 100644 index 000000000..4e50262ac --- /dev/null +++ b/src/main/java/emu/grasscutter/game/managers/blossom/BlossomActivity.java @@ -0,0 +1,135 @@ +package emu.grasscutter.game.managers.blossom; + +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.excels.MonsterData; +import emu.grasscutter.data.excels.WorldLevelData; +import emu.grasscutter.game.dungeons.challenge.WorldChallenge; +import emu.grasscutter.game.dungeons.challenge.trigger.ChallengeTrigger; +import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterTrigger; +import emu.grasscutter.game.entity.EntityGadget; +import emu.grasscutter.game.entity.EntityMonster; +import emu.grasscutter.game.props.FightProperty; +import emu.grasscutter.game.world.Scene; +import emu.grasscutter.scripts.data.SceneBossChest; +import emu.grasscutter.scripts.data.SceneGadget; +import emu.grasscutter.scripts.data.SceneGroup; +import emu.grasscutter.utils.Position; +import emu.grasscutter.utils.Utils; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.List; +import java.util.Queue; + +public class BlossomActivity { + + private final SceneGroup tempSceneGroup; + private final WorldChallenge challenge; + private final EntityGadget gadget; + private EntityGadget chest; + private int step; + private final int goal; + private int generatedCount; + private final int worldLevel; + private boolean pass=false; + private final List activeMonsters = new ArrayList<>(); + private final Queue candidateMonsters = new ArrayDeque<>(); + private static final int BLOOMING_GADGET_ID = 70210109; + public BlossomActivity(EntityGadget entityGadget, List monsters, int timeout, int worldLevel) { + this.tempSceneGroup = new SceneGroup(); + this.tempSceneGroup.id = entityGadget.getId(); + this.gadget=entityGadget; + this.step=0; + this.goal = monsters.size(); + this.candidateMonsters.addAll(monsters); + this.worldLevel = worldLevel; + ArrayList challengeTriggers = new ArrayList<>(); + this.challenge = new WorldChallenge(entityGadget.getScene(), + tempSceneGroup, + 1, + 1, + List.of(goal, timeout), + timeout, + goal, challengeTriggers); + challengeTriggers.add(new KillMonsterTrigger()); + //this.challengeTriggers.add(new InTimeTrigger()); + } + public WorldChallenge getChallenge(){ + return this.challenge; + } + public void setMonsters(List monsters) { + this.activeMonsters.clear(); + this.activeMonsters.addAll(monsters); + for(EntityMonster monster : monsters){ + monster.setGroupId(this.tempSceneGroup.id); + } + } + public int getAliveMonstersCount(){ + int count=0; + for(EntityMonster monster: activeMonsters) { + if(monster.isAlive()){ + count++; + } + } + return count; + } + public boolean getPass(){ + return pass; + } + public void start(){ + challenge.start(); + } + public void onTick() { + Scene scene = gadget.getScene(); + Position pos = gadget.getPosition(); + if(getAliveMonstersCount() <= 2){ + if(generatedCount newMonsters = new ArrayList<>(); + int willSpawn = Utils.randomRange(3,5); + if(generatedCount+willSpawn>goal){ + willSpawn = goal - generatedCount; + } + generatedCount+=willSpawn; + for (int i = 0; i < willSpawn; i++) { + MonsterData monsterData = GameData.getMonsterDataMap().get(candidateMonsters.poll()); + int level = scene.getEntityLevel(1, worldLevelOverride); + EntityMonster entity = new EntityMonster(scene, monsterData, pos.nearby2d(40), level); + scene.addEntity(entity); + newMonsters.add(entity); + } + setMonsters(newMonsters); + }else{ + if(getAliveMonstersCount() == 0) { + this.pass = true; + this.challenge.done(); + } + } + } + } + public EntityGadget getGadget(){ + return gadget; + } + public EntityGadget getChest(){ + if(chest==null) { + EntityGadget rewardGadget = new EntityGadget(gadget.getScene(), BLOOMING_GADGET_ID, gadget.getPosition()); + SceneGadget metaGadget = new SceneGadget(); + metaGadget.boss_chest = new SceneBossChest(); + metaGadget.boss_chest.resin = 20; + rewardGadget.setFightProperty(FightProperty.FIGHT_PROP_BASE_HP, Float.POSITIVE_INFINITY); + rewardGadget.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, Float.POSITIVE_INFINITY); + rewardGadget.setFightProperty(FightProperty.FIGHT_PROP_MAX_HP, Float.POSITIVE_INFINITY); + rewardGadget.setMetaGadget(metaGadget); + rewardGadget.buildContent(); + chest = rewardGadget; + } + return chest; + } +} diff --git a/src/main/java/emu/grasscutter/game/managers/blossom/BlossomConfig.java b/src/main/java/emu/grasscutter/game/managers/blossom/BlossomConfig.java new file mode 100644 index 000000000..22046ef42 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/managers/blossom/BlossomConfig.java @@ -0,0 +1,12 @@ +package emu.grasscutter.game.managers.blossom; + +import java.util.List; +import java.util.Map; + +import lombok.Getter; + +public class BlossomConfig { + @Getter private int monsterFightingVolume; + // @Getter private Int2ObjectMap monsterIdsPerDifficulty; // Need to wrangle Gson for this + @Getter private Map> monsterIdsPerDifficulty; +} diff --git a/src/main/java/emu/grasscutter/game/managers/blossom/BlossomManager.java b/src/main/java/emu/grasscutter/game/managers/blossom/BlossomManager.java new file mode 100644 index 000000000..131d6f2cd --- /dev/null +++ b/src/main/java/emu/grasscutter/game/managers/blossom/BlossomManager.java @@ -0,0 +1,237 @@ +package emu.grasscutter.game.managers.blossom; + +import java.util.ArrayList; +import java.util.List; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.GameDepot; +import emu.grasscutter.data.common.ItemParamData; +import emu.grasscutter.data.excels.RewardPreviewData; +import emu.grasscutter.game.entity.EntityGadget; +import emu.grasscutter.game.entity.GameEntity; +import emu.grasscutter.game.entity.gadget.GadgetWorktop; +import emu.grasscutter.game.inventory.GameItem; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.world.Scene; +import emu.grasscutter.game.world.SpawnDataEntry; +import emu.grasscutter.game.world.SpawnDataEntry.SpawnGroupEntry; +import emu.grasscutter.net.proto.BlossomBriefInfoOuterClass; +import emu.grasscutter.net.proto.VisionTypeOuterClass; +import emu.grasscutter.server.packet.send.PacketBlossomBriefInfoNotify; +import emu.grasscutter.utils.Utils; +import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; + +public class BlossomManager { + public BlossomManager(Scene scene) { + this.scene = scene; + } + + private final Scene scene; + private final List blossomActivities = new ArrayList<>(); + private final List activeChests = new ArrayList<>(); + private final List createdEntity = new ArrayList<>(); + + private final List blossomConsumed = new ArrayList<>(); + + public void onTick(){ + synchronized (blossomActivities){ + var it = blossomActivities.iterator(); + while(it.hasNext()){ + var active = it.next(); + active.onTick(); + if (active.getPass()) { + EntityGadget chest = active.getChest(); + scene.addEntity(chest); + scene.setChallenge(null); + activeChests.add(active); + it.remove(); + } + } + } + } + + public void recycleGadgetEntity(List entities){ + for(var entity : entities){ + if(entity instanceof EntityGadget gadget){ + createdEntity.remove(gadget); + } + } + notifyIcon(); + } + + public void initBlossom(EntityGadget gadget){ + if(createdEntity.contains(gadget)){ + return; + } + if(blossomConsumed.contains(gadget.getSpawnEntry())){ + return; + } + var id = gadget.getGadgetId(); + if(BlossomType.valueOf(id)==null){ + return; + } + gadget.buildContent(); + gadget.setState(204); + int worldLevel = getWorldLevel(); + GadgetWorktop gadgetWorktop = ((GadgetWorktop) gadget.getContent()); + gadgetWorktop.addWorktopOptions(new int[]{187}); + gadgetWorktop.setOnSelectWorktopOptionEvent((GadgetWorktop context, int option) -> { + BlossomActivity activity; + EntityGadget entityGadget = context.getGadget(); + synchronized (blossomActivities) { + for (BlossomActivity i : this.blossomActivities) { + if (i.getGadget() == entityGadget) { + return false; + } + } + + int volume=0; + IntList monsters = new IntArrayList(); + while(true){ + var remain = GameDepot.getBlossomConfig().getMonsterFightingVolume() - volume; + if(remain<=0){ + break; + } + var rand = Utils.randomRange(1,100); + if(rand>85 && remain>=50){//15% ,generate strong monster + monsters.addAll(getRandomMonstersID(2,1)); + volume+=50; + }else if(rand>50 && remain>=20) {//35% ,generate normal monster + monsters.addAll(getRandomMonstersID(1,1)); + volume+=20; + }else{//50% ,generate weak monster + monsters.addAll(getRandomMonstersID(0,1)); + volume+=10; + } + } + + Grasscutter.getLogger().info("Blossom Monsters:"+monsters); + + activity = new BlossomActivity(entityGadget, monsters, -1, worldLevel); + blossomActivities.add(activity); + } + entityGadget.updateState(201); + scene.setChallenge(activity.getChallenge()); + scene.removeEntity(entityGadget, VisionTypeOuterClass.VisionType.VISION_TYPE_REMOVE); + activity.start(); + return true; + }); + createdEntity.add(gadget); + notifyIcon(); + } + + public void notifyIcon() { + final int wl = getWorldLevel(); + final int worldLevel = (wl < 0) ? 0 : ((wl > 8) ? 8 : wl); + final int monsterLevel = GameData.getWorldLevelDataMap().get(worldLevel).getMonsterLevel(); + List blossoms = new ArrayList<>(); + GameDepot.getSpawnLists().forEach((gridBlockId, spawnDataEntryList) -> { + int sceneId = gridBlockId.getSceneId(); + spawnDataEntryList.stream() + .map(SpawnDataEntry::getGroup) + .map(SpawnGroupEntry::getSpawns) + .flatMap(List::stream) + .filter(spawn -> !blossomConsumed.contains(spawn)) + .filter(spawn -> BlossomType.valueOf(spawn.getGadgetId()) != null) + .forEach(spawn -> { + var type = BlossomType.valueOf(spawn.getGadgetId()); + int previewReward = getPreviewReward(type, worldLevel); + blossoms.add(BlossomBriefInfoOuterClass.BlossomBriefInfo.newBuilder() + .setSceneId(sceneId) + .setPos(spawn.getPos().toProto()) + .setResin(20) + .setMonsterLevel(monsterLevel) + .setRewardId(previewReward) + .setCircleCampId(type.getCircleCampId()) + .setRefreshId(type.getBlossomChestId()) // TODO: replace when using actual leylines + .build() + ); + }); + }); + scene.broadcastPacket(new PacketBlossomBriefInfoNotify(blossoms)); + } + + public int getWorldLevel(){ + return scene.getWorld().getWorldLevel(); + } + + private static Integer getPreviewReward(BlossomType type, int worldLevel) { + // TODO: blossoms should be based on their city + if (type == null) { + Grasscutter.getLogger().error("Illegal blossom type {}",type); + return null; + } + + int blossomChestId = type.getBlossomChestId(); + var dataMap = GameData.getBlossomRefreshExcelConfigDataMap(); + for (var data : dataMap.values()) { + if (blossomChestId == data.getBlossomChestId()) { + var dropVecList = data.getDropVec(); + if (worldLevel > dropVecList.length) { + Grasscutter.getLogger().error("Illegal world level {}", worldLevel); + return null; + } + return dropVecList[worldLevel].getPreviewReward(); + } + } + Grasscutter.getLogger().error("Cannot find blossom type {}",type); + return null; + } + + private static RewardPreviewData getRewardList(BlossomType type, int worldLevel) { + Integer previewReward = getPreviewReward(type, worldLevel); + if (previewReward == null) return null; + return GameData.getRewardPreviewDataMap().get((int) previewReward); + } + + public List onReward(Player player, EntityGadget chest, boolean useCondensedResin) { + var resinManager = player.getResinManager(); + synchronized (activeChests) { + var it = activeChests.iterator(); + while (it.hasNext()) { + var activeChest = it.next(); + if (activeChest.getChest() == chest) { + boolean pay = useCondensedResin ? resinManager.useCondensedResin(1) : resinManager.useResin(20); + if (pay) { + int worldLevel = getWorldLevel(); + List items = new ArrayList<>(); + var gadget = activeChest.getGadget(); + var type = BlossomType.valueOf(gadget.getGadgetId()); + RewardPreviewData blossomRewards = getRewardList(type, worldLevel); + if (blossomRewards == null) { + Grasscutter.getLogger().error("Blossom could not support world level : "+worldLevel); + return null; + } + var rewards = blossomRewards.getPreviewItems(); + for (ItemParamData blossomReward : rewards) { + int rewardCount = blossomReward.getCount(); + if (useCondensedResin) { + rewardCount += blossomReward.getCount(); // Double! + } + items.add(new GameItem(blossomReward.getItemId(),rewardCount)); + } + it.remove(); + recycleGadgetEntity(List.of(gadget)); + blossomConsumed.add(gadget.getSpawnEntry()); + return items; + } + return null; + } + } + } + return null; + } + + public static IntList getRandomMonstersID(int difficulty,int count){ + IntList result = new IntArrayList(); + List monsters = GameDepot.getBlossomConfig().getMonsterIdsPerDifficulty().get(difficulty); + for(int i=0; i map = new Int2ObjectOpenHashMap<>( + Stream.of(values()).collect(Collectors.toMap(x -> x.getGadgetId(), x -> x)) + ); + + public static BlossomType valueOf(int i) { + return map.get(i); + } + + public static BlossomType random() { + BlossomType[] values = values(); + return values[Utils.randomRange(0, values.length)]; + } +} diff --git a/src/main/java/emu/grasscutter/game/world/Scene.java b/src/main/java/emu/grasscutter/game/world/Scene.java index 8e6e05a9c..54433d571 100644 --- a/src/main/java/emu/grasscutter/game/world/Scene.java +++ b/src/main/java/emu/grasscutter/game/world/Scene.java @@ -8,6 +8,8 @@ import emu.grasscutter.data.excels.*; import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.dungeons.DungeonSettleListener; import emu.grasscutter.game.entity.*; +import emu.grasscutter.game.entity.gadget.GadgetWorktop; +import emu.grasscutter.game.managers.blossom.BlossomManager; import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.TeamInfo; import emu.grasscutter.game.props.FightProperty; @@ -17,6 +19,7 @@ import emu.grasscutter.game.quest.QuestGroupSuite; import emu.grasscutter.game.dungeons.challenge.WorldChallenge; import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult; +import emu.grasscutter.net.proto.SelectWorktopOptionReqOuterClass; import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType; import emu.grasscutter.scripts.SceneIndexManager; import emu.grasscutter.scripts.SceneScriptManager; @@ -25,6 +28,8 @@ import emu.grasscutter.scripts.data.SceneGadget; import emu.grasscutter.scripts.data.SceneGroup; import emu.grasscutter.server.packet.send.*; import emu.grasscutter.utils.Position; +import lombok.Getter; +import lombok.Setter; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -32,25 +37,26 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.stream.Collectors; public class Scene { - private final World world; - private final SceneData sceneData; - private final List players; - private final Map entities; - private final Set spawnedEntities; - private final Set deadSpawnedEntities; - private final Set loadedBlocks; + @Getter private final World world; + @Getter private final SceneData sceneData; + @Getter private final List players; + @Getter private final Map entities; + @Getter private final Set spawnedEntities; + @Getter private final Set deadSpawnedEntities; + @Getter private final Set loadedBlocks; + @Getter private final BlossomManager blossomManager; private Set loadedGridBlocks; - private boolean dontDestroyWhenEmpty; + @Getter @Setter private boolean dontDestroyWhenEmpty; - private int autoCloseTime; - private int time; + @Getter @Setter private int autoCloseTime; + @Getter private int time; - private SceneScriptManager scriptManager; - private WorldChallenge challenge; - private List dungeonSettleListeners; - private DungeonData dungeonData; - private int prevScene; // Id of the previous scene - private int prevScenePoint; + @Getter private SceneScriptManager scriptManager; + @Getter @Setter private WorldChallenge challenge; + @Getter private List dungeonSettleListeners; + @Getter private DungeonData dungeonData; + @Getter @Setter private int prevScene; // Id of the previous scene + @Getter @Setter private int prevScenePoint; private Set npcBornEntrySet; public Scene(World world, SceneData sceneData) { this.world = world; @@ -67,36 +73,21 @@ public class Scene { this.loadedGridBlocks = new HashSet<>(); this.npcBornEntrySet = ConcurrentHashMap.newKeySet(); this.scriptManager = new SceneScriptManager(this); + this.blossomManager = new BlossomManager(this); } public int getId() { return sceneData.getId(); } - public World getWorld() { - return world; - } - - public SceneData getSceneData() { - return this.sceneData; - } - public SceneType getSceneType() { return getSceneData().getSceneType(); } - public List getPlayers() { - return players; - } - public int getPlayerCount() { return this.getPlayers().size(); } - public Map getEntities() { - return entities; - } - public GameEntity getEntityById(int id) { return this.entities.get(id); } @@ -107,72 +98,11 @@ public class Scene { .findFirst() .orElse(null); } - /** - * @return the autoCloseTime - */ - public int getAutoCloseTime() { - return autoCloseTime; - } - - /** - * @param autoCloseTime the autoCloseTime to set - */ - public void setAutoCloseTime(int autoCloseTime) { - this.autoCloseTime = autoCloseTime; - } - - public int getTime() { - return time; - } public void changeTime(int time) { this.time = time % 1440; } - public int getPrevScene() { - return prevScene; - } - - public void setPrevScene(int prevScene) { - this.prevScene = prevScene; - } - - public int getPrevScenePoint() { - return prevScenePoint; - } - - public void setPrevScenePoint(int prevPoint) { - this.prevScenePoint = prevPoint; - } - - public boolean dontDestroyWhenEmpty() { - return dontDestroyWhenEmpty; - } - - public void setDontDestroyWhenEmpty(boolean dontDestroyWhenEmpty) { - this.dontDestroyWhenEmpty = dontDestroyWhenEmpty; - } - - public Set getLoadedBlocks() { - return loadedBlocks; - } - - public Set getSpawnedEntities() { - return spawnedEntities; - } - - public Set getDeadSpawnedEntities() { - return deadSpawnedEntities; - } - - public SceneScriptManager getScriptManager() { - return scriptManager; - } - - public DungeonData getDungeonData() { - return dungeonData; - } - public void setDungeonData(DungeonData dungeonData) { if (dungeonData == null || this.dungeonData != null || this.getSceneType() != SceneType.SCENE_DUNGEON || dungeonData.getSceneId() != this.getId()) { return; @@ -180,14 +110,6 @@ public class Scene { this.dungeonData = dungeonData; } - public WorldChallenge getChallenge() { - return challenge; - } - - public void setChallenge(WorldChallenge challenge) { - this.challenge = challenge; - } - public void addDungeonSettleObserver(DungeonSettleListener dungeonSettleListener) { if (dungeonSettleListeners == null) { dungeonSettleListeners = new ArrayList<>(); @@ -195,10 +117,6 @@ public class Scene { dungeonSettleListeners.add(dungeonSettleListener); } - public List getDungeonSettleObservers() { - return dungeonSettleListeners; - } - public boolean isInScene(GameEntity entity) { return this.entities.containsKey(entity.getId()); } @@ -241,7 +159,7 @@ public class Scene { } // Deregister scene if not in use - if (this.getPlayerCount() <= 0 && !this.dontDestroyWhenEmpty()) { + if (this.getPlayerCount() <= 0 && !this.dontDestroyWhenEmpty) { this.getWorld().deregisterScene(this); } } @@ -434,6 +352,8 @@ public class Scene { challenge.onCheckTimeOut(); } + blossomManager.onTick(); + checkNpcGroup(); } @@ -527,11 +447,12 @@ public class Scene { } gadget.buildContent(); - gadget.setFightProperty(FightProperty.FIGHT_PROP_BASE_HP, 99999); - gadget.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 99999); - gadget.setFightProperty(FightProperty.FIGHT_PROP_MAX_HP, 99999); + gadget.setFightProperty(FightProperty.FIGHT_PROP_BASE_HP, Float.POSITIVE_INFINITY); + gadget.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, Float.POSITIVE_INFINITY); + gadget.setFightProperty(FightProperty.FIGHT_PROP_MAX_HP, Float.POSITIVE_INFINITY); entity = gadget; + blossomManager.initBlossom(gadget); } if (entity == null) continue; @@ -557,6 +478,7 @@ public class Scene { if (toRemove.size() > 0) { toRemove.stream().forEach(this::removeEntityDirectly); this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, VisionType.VISION_TYPE_REMOVE)); + blossomManager.recycleGadgetEntity(toRemove); } } @@ -825,4 +747,20 @@ public class Scene { scriptManager.addGroupSuite(group, suite); }); } + + public void selectWorktopOptionWith(SelectWorktopOptionReqOuterClass.SelectWorktopOptionReq req) { + GameEntity entity = getEntityById(req.getGadgetEntityId()); + if (entity == null) { + return; + } + // Handle + if (entity instanceof EntityGadget gadget) { + if (gadget.getContent() instanceof GadgetWorktop worktop) { + boolean shouldDelete = worktop.onSelectWorktopOption(req); + if (shouldDelete) { + entity.getScene().removeEntity(entity, VisionType.VISION_TYPE_REMOVE); + } + } + } + } } diff --git a/src/main/java/emu/grasscutter/game/world/SpawnDataEntry.java b/src/main/java/emu/grasscutter/game/world/SpawnDataEntry.java index f4d5efd4f..22f853776 100644 --- a/src/main/java/emu/grasscutter/game/world/SpawnDataEntry.java +++ b/src/main/java/emu/grasscutter/game/world/SpawnDataEntry.java @@ -5,62 +5,20 @@ import java.util.Objects; import emu.grasscutter.data.GameDepot; import emu.grasscutter.utils.Position; +import lombok.Getter; +import lombok.Setter; public class SpawnDataEntry { - private transient SpawnGroupEntry group; - private int monsterId; - private int gadgetId; - private int configId; - private int level; - private int poseId; - private int gatherItemId; - private int gadgetState; - private Position pos; - private Position rot; - - public SpawnGroupEntry getGroup() { - return group; - } - - public void setGroup(SpawnGroupEntry group) { - this.group = group; - } - - public int getMonsterId() { - return monsterId; - } - - public int getGadgetId() { - return gadgetId; - } - - public int getGadgetState() { - return gadgetState; - } - - public int getConfigId() { - return configId; - } - - public int getLevel() { - return level; - } - - public int getPoseId() { - return poseId; - } - - public int getGatherItemId() { - return gatherItemId; - } - - public Position getPos() { - return pos; - } - - public Position getRot() { - return rot; - } + @Getter @Setter private transient SpawnGroupEntry group; + @Getter private int monsterId; + @Getter private int gadgetId; + @Getter private int configId; + @Getter private int level; + @Getter private int poseId; + @Getter private int gatherItemId; + @Getter private int gadgetState; + @Getter private Position pos; + @Getter private Position rot; public GridBlockId getBlockId() { int scale = GridBlockId.getScale(gadgetId); @@ -71,37 +29,17 @@ public class SpawnDataEntry { } public static class SpawnGroupEntry { - private int sceneId; - private int groupId; - private int blockId; - private List spawns; - - public int getSceneId() { - return sceneId; - } - - public int getGroupId() { - return groupId; - } - - public int getBlockId() { - return blockId; - } - - public void setBlockId(int blockId) { - this.blockId = blockId; - } - - public List getSpawns() { - return spawns; - } + @Getter private int sceneId; + @Getter private int groupId; + @Getter private int blockId; + @Getter @Setter private List spawns; } public static class GridBlockId { - int sceneId; - int scale; - int x; - int z; + @Getter private int sceneId; + @Getter private int scale; + @Getter private int x; + @Getter private int z; public GridBlockId(int sceneId, int scale, int x, int z) { this.sceneId = sceneId; diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSelectWorktopOptionReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSelectWorktopOptionReq.java index 82980c452..aa8f9a86b 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSelectWorktopOptionReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSelectWorktopOptionReq.java @@ -13,20 +13,20 @@ import emu.grasscutter.server.packet.send.PacketSelectWorktopOptionRsp; @Opcodes(PacketOpcodes.SelectWorktopOptionReq) public class HandlerSelectWorktopOptionReq extends PacketHandler { - + @Override public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { SelectWorktopOptionReq req = SelectWorktopOptionReq.parseFrom(payload); - + try { GameEntity entity = session.getPlayer().getScene().getEntityById(req.getGadgetEntityId()); - - if (entity == null || !(entity instanceof EntityGadget)) { + + if (!(entity instanceof EntityGadget)) { return; } - + session.getPlayer().getScene().selectWorktopOptionWith(req); session.getPlayer().getScene().getScriptManager().callEvent( - EventType.EVENT_SELECT_OPTION, + EventType.EVENT_SELECT_OPTION, new ScriptArgs(entity.getConfigId(), req.getOptionId()) ); } finally { diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketBlossomBriefInfoNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketBlossomBriefInfoNotify.java new file mode 100644 index 000000000..14890058c --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketBlossomBriefInfoNotify.java @@ -0,0 +1,13 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.BlossomBriefInfoNotifyOuterClass; +import emu.grasscutter.net.proto.BlossomBriefInfoOuterClass; + +public class PacketBlossomBriefInfoNotify extends BasePacket { + public PacketBlossomBriefInfoNotify(Iterable blossoms) { + super(PacketOpcodes.BlossomBriefInfoNotify); + this.setData(BlossomBriefInfoNotifyOuterClass.BlossomBriefInfoNotify.newBuilder().addAllBriefInfoList(blossoms)); + } +} \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/utils/Position.java b/src/main/java/emu/grasscutter/utils/Position.java index 627fcf7b9..419278a13 100644 --- a/src/main/java/emu/grasscutter/utils/Position.java +++ b/src/main/java/emu/grasscutter/utils/Position.java @@ -10,28 +10,28 @@ import emu.grasscutter.net.proto.VectorOuterClass.Vector; @Entity public class Position implements Serializable { private static final long serialVersionUID = -2001232313615923575L; - + @SerializedName(value="x", alternate={"_x", "X"}) private float x; - + @SerializedName(value="y", alternate={"_y", "Y"}) private float y; - + @SerializedName(value="z", alternate={"_z", "Z"}) private float z; - + public Position() { } - + public Position(float x, float y) { set(x, y); } - + public Position(float x, float y, float z) { set(x, y, z); } - + public Position(String p) { String[] split = p.split(","); if (split.length >= 2) { @@ -42,7 +42,7 @@ public class Position implements Serializable { this.z = Float.parseFloat(split[2]); } } - + public Position(Vector vector) { this.set(vector); } @@ -66,7 +66,7 @@ public class Position implements Serializable { public void setZ(float z) { this.z = z; } - + public float getY() { return y; } @@ -74,22 +74,22 @@ public class Position implements Serializable { public void setY(float y) { this.y = y; } - + public Position set(float x, float y) { this.x = x; this.y = y; return this; } - + // Deep copy public Position set(Position pos) { return this.set(pos.getX(), pos.getY(), pos.getZ()); } - + public Position set(Vector pos) { return this.set(pos.getX(), pos.getY(), pos.getZ()); } - + public Position set(float x, float y, float z) { this.x = x; this.y = y; @@ -103,17 +103,17 @@ public class Position implements Serializable { this.z += add.getZ(); return this; } - + public Position addX(float d) { this.x += d; return this; } - + public Position addY(float d) { this.y += d; return this; } - + public Position addZ(float d) { this.z += d; return this; @@ -125,7 +125,7 @@ public class Position implements Serializable { this.z -= sub.getZ(); return this; } - + /** In radians * */ public Position translate(float dist, float angle) { @@ -149,13 +149,20 @@ public class Position implements Serializable { double detZ = getZ()-b.getZ(); return Math.sqrt(detX*detX+detY*detY+detZ*detZ); } + + public Position nearby2d(int range){ + Position position = clone(); + position.z += (float)Utils.randomRange(-range,range)/10; + position.x += (float)Utils.randomRange(-range,range)/10; + return position; + } public Position translateWithDegrees(float dist, float angle) { angle = (float) Math.toRadians(angle); this.x += dist * Math.sin(angle); this.y += -dist * Math.cos(angle); return this; } - + @Override public Position clone() { return new Position(x, y, z); @@ -165,7 +172,7 @@ public class Position implements Serializable { public String toString() { return "(" + this.getX() + ", " + this.getY() + ", " + this.getZ() + ")"; } - + public Vector toProto() { return Vector.newBuilder() .setX(this.getX()) diff --git a/src/main/resources/defaults/data/BlossomConfig.json b/src/main/resources/defaults/data/BlossomConfig.json new file mode 100644 index 000000000..2e99de1ad --- /dev/null +++ b/src/main/resources/defaults/data/BlossomConfig.json @@ -0,0 +1,8 @@ +{ + "monsterFightingVolume": 100, + "monsterIdsPerDifficulty": { + "0": [21010101, 20010101, 20010201, 20010202, 20010301, 20010401, 20010403, 20010501, 20010601, 20010604, 20010701, 20010702, 20010801, 20010802, 20010901, 20010902, 20010904, 20011001, 20011101, 20011103, 20011601, 20011701, 20011801, 20011901, 20050201, 20050202, 20050203, 20050301, 20050302, 20050401, 20050402, 20050403, 20050501, 20050502, 20050601, 20050602, 20050603, 20050701, 20050702, 20050703, 20050801, 20050802, 20050901, 21010201, 21010301, 21010401, 21010402, 21010501, 21010502, 21010601, 21010603, 21010701, 21010901, 21010902, 21011001, 21011002, 21011201, 21011301, 21011302, 21011401, 21011403, 21011501, 21011601, 21011602, 20011201, 20011202, 20011301, 20011304, 20011401, 20011501, 20011502, 25010101, 25010102, 25010103, 25010104, 25010105, 25010106, 25010201, 25010203, 25010204, 25010205, 25010206, 25010207, 25010208, 25010701, 25020101, 25020102, 25020201, 25020204, 25030101, 25030102, 25030103, 25030201, 25030301, 25040101, 25040102, 25040103, 25050101, 25050201, 25050301, 25050401, 25050402, 25050501, 25050502, 25060101, 25060102, 25070101, 25070201, 25070202, 21010102, 20010302, 20010402, 20010502, 20010602, 20010703, 20010803, 20010903, 20011002, 20011102, 21010302, 21010702, 21011202, 21011402, 20011203, 20011302, 20011402, 20011503, 21030102, 21030202, 21030302, 21030403, 21030502, 21030602], + "1": [21020101, 21020201, 21020202, 21020301, 21020601, 21020701, 21020703, 21030101, 21030103, 21030201, 21030203, 21030301, 21030303, 21030304, 21030401, 21030402, 21030501, 21030601, 21030603, 25010301, 25010302, 25010401, 25010501, 25010601, 26010101, 26010102, 26010201, 26010301, 21020102, 21020203, 21020702, 21020302, 21020602], + "2": [21020401, 21020501, 21020801, 20020101, 20070101, 22010101, 22010102, 22010103, 22010104, 22010201, 22010202, 22010203, 22010204, 22010301, 22010302, 22010303, 22010304, 22010401, 22010403, 22010404, 24010101, 24010201, 23010101, 23010201, 23010301, 23010401, 23010501, 23010601, 23020101, 23020102, 23030101, 23030102, 23040101, 23040102, 23050101, 20060101, 20060201, 20060301, 20060401, 20060501, 20060601, 21020402, 21020502, 21020802, 22010105, 22010205, 22010305, 22010402] + } +}