mirror of
https://github.com/Melledy/Grasscutter.git
synced 2024-11-25 10:51:47 +00:00
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 <AnimeGitB@bigblueball.in>
This commit is contained in:
parent
957296fa2d
commit
abd1e7569e
@ -113,6 +113,7 @@ public class GameData {
|
||||
@Getter private static final Int2ObjectMap<TriggerExcelConfigData> triggerExcelConfigDataMap = new Int2ObjectOpenHashMap<>();
|
||||
@Getter private static final Map<String,ScriptSceneData> scriptSceneDataMap = new HashMap<>();
|
||||
@Getter private static final Map<Integer, List<Integer>> scenePointsPerScene = new HashMap<>();
|
||||
@Getter private static final Int2ObjectMap<BlossomRefreshExcelConfigData> blossomRefreshExcelConfigDataMap = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
@Getter private static final Int2ObjectMap<OpenStateData> openStateDataMap = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
@ -124,7 +125,7 @@ public class GameData {
|
||||
|
||||
|
||||
@Getter private static final List<OpenStateData> openStateList = new ArrayList<>();
|
||||
|
||||
|
||||
|
||||
public static Int2ObjectMap<?> getMapByResourceDef(Class<?> resourceDefinition) {
|
||||
Int2ObjectMap<?> map = null;
|
||||
|
@ -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<List<ReliquaryMainPropData>> relicMainPropDepot = new Int2ObjectOpenHashMap<>();
|
||||
private static Int2ObjectMap<List<ReliquaryAffixData>> relicAffixDepot = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
private static Map<String, AvatarConfig> playerAbilities = new HashMap<>();
|
||||
private static HashMap<SpawnDataEntry.GridBlockId, ArrayList<SpawnDataEntry>> spawnLists = new HashMap<>();
|
||||
@Getter @Setter private static Map<String, AvatarConfig> playerAbilities = new HashMap<>();
|
||||
@Getter private static HashMap<SpawnDataEntry.GridBlockId, ArrayList<SpawnDataEntry>> 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<SpawnDataEntry.GridBlockId, ArrayList<SpawnDataEntry>> getSpawnLists() {
|
||||
return spawnLists;
|
||||
}
|
||||
|
||||
public static void addSpawnListById(HashMap<SpawnDataEntry.GridBlockId, ArrayList<SpawnDataEntry>> data) {
|
||||
spawnLists.putAll(data);
|
||||
}
|
||||
|
||||
public static void setPlayerAbilities(Map<String, AvatarConfig> playerAbilities) {
|
||||
GameDepot.playerAbilities = playerAbilities;
|
||||
}
|
||||
|
||||
public static Map<String, AvatarConfig> getPlayerAbilities() {
|
||||
return playerAbilities;
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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<Integer> param;
|
||||
}
|
||||
}
|
@ -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<GameItem> 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));
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
@ -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) {
|
||||
|
@ -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<EntityMonster> activeMonsters = new ArrayList<>();
|
||||
private final Queue<Integer> candidateMonsters = new ArrayDeque<>();
|
||||
private static final int BLOOMING_GADGET_ID = 70210109;
|
||||
public BlossomActivity(EntityGadget entityGadget, List<Integer> 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<ChallengeTrigger> 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<EntityMonster> 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<goal){
|
||||
step++;
|
||||
|
||||
WorldLevelData worldLevelData = GameData.getWorldLevelDataMap().get(worldLevel);
|
||||
int worldLevelOverride = 0;
|
||||
if (worldLevelData != null) {
|
||||
worldLevelOverride = worldLevelData.getMonsterLevel();
|
||||
}
|
||||
|
||||
List<EntityMonster> 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;
|
||||
}
|
||||
}
|
@ -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<IntList> monsterIdsPerDifficulty; // Need to wrangle Gson for this
|
||||
@Getter private Map<Integer, List<Integer>> monsterIdsPerDifficulty;
|
||||
}
|
@ -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<BlossomActivity> blossomActivities = new ArrayList<>();
|
||||
private final List<BlossomActivity> activeChests = new ArrayList<>();
|
||||
private final List<EntityGadget> createdEntity = new ArrayList<>();
|
||||
|
||||
private final List<SpawnDataEntry> 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<GameEntity> 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<BlossomBriefInfoOuterClass.BlossomBriefInfo> 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<GameItem> 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<GameItem> 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<Integer> monsters = GameDepot.getBlossomConfig().getMonsterIdsPerDifficulty().get(difficulty);
|
||||
for(int i=0; i<count; i++){
|
||||
result.add((int) monsters.get(Utils.randomRange(0, monsters.size()-1)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package emu.grasscutter.game.managers.blossom;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
|
||||
public enum BlossomType {
|
||||
GOLD(70360056, 101001001, 1),
|
||||
BLUE(70360057, 101002003, 2);
|
||||
|
||||
@Getter private final int gadgetId;
|
||||
@Getter private final int circleCampId;
|
||||
@Getter private final int blossomChestId;
|
||||
|
||||
BlossomType(int gadgetId, int circleCampId, int blossomChestId) {
|
||||
this.gadgetId = gadgetId;
|
||||
this.circleCampId = circleCampId;
|
||||
this.blossomChestId = blossomChestId;
|
||||
}
|
||||
|
||||
private static final Int2ObjectMap<BlossomType> 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)];
|
||||
}
|
||||
}
|
@ -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<Player> players;
|
||||
private final Map<Integer, GameEntity> entities;
|
||||
private final Set<SpawnDataEntry> spawnedEntities;
|
||||
private final Set<SpawnDataEntry> deadSpawnedEntities;
|
||||
private final Set<SceneBlock> loadedBlocks;
|
||||
@Getter private final World world;
|
||||
@Getter private final SceneData sceneData;
|
||||
@Getter private final List<Player> players;
|
||||
@Getter private final Map<Integer, GameEntity> entities;
|
||||
@Getter private final Set<SpawnDataEntry> spawnedEntities;
|
||||
@Getter private final Set<SpawnDataEntry> deadSpawnedEntities;
|
||||
@Getter private final Set<SceneBlock> loadedBlocks;
|
||||
@Getter private final BlossomManager blossomManager;
|
||||
private Set<SpawnDataEntry.GridBlockId> 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<DungeonSettleListener> 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<DungeonSettleListener> dungeonSettleListeners;
|
||||
@Getter private DungeonData dungeonData;
|
||||
@Getter @Setter private int prevScene; // Id of the previous scene
|
||||
@Getter @Setter private int prevScenePoint;
|
||||
private Set<SceneNpcBornEntry> 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<Player> getPlayers() {
|
||||
return players;
|
||||
}
|
||||
|
||||
public int getPlayerCount() {
|
||||
return this.getPlayers().size();
|
||||
}
|
||||
|
||||
public Map<Integer, GameEntity> 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<SceneBlock> getLoadedBlocks() {
|
||||
return loadedBlocks;
|
||||
}
|
||||
|
||||
public Set<SpawnDataEntry> getSpawnedEntities() {
|
||||
return spawnedEntities;
|
||||
}
|
||||
|
||||
public Set<SpawnDataEntry> 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<DungeonSettleListener> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<SpawnDataEntry> 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<SpawnDataEntry> getSpawns() {
|
||||
return spawns;
|
||||
}
|
||||
@Getter private int sceneId;
|
||||
@Getter private int groupId;
|
||||
@Getter private int blockId;
|
||||
@Getter @Setter private List<SpawnDataEntry> 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;
|
||||
|
@ -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 {
|
||||
|
@ -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<BlossomBriefInfoOuterClass.BlossomBriefInfo> blossoms) {
|
||||
super(PacketOpcodes.BlossomBriefInfoNotify);
|
||||
this.setData(BlossomBriefInfoNotifyOuterClass.BlossomBriefInfoNotify.newBuilder().addAllBriefInfoList(blossoms));
|
||||
}
|
||||
}
|
@ -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())
|
||||
|
8
src/main/resources/defaults/data/BlossomConfig.json
Normal file
8
src/main/resources/defaults/data/BlossomConfig.json
Normal file
@ -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]
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user