Continue merging quests (pt. 1)

Finished last at: `World.java`, line `player.setAvatarsAbilityForScene(newScene);`
This commit is contained in:
KingRainbow44 2023-04-09 13:25:16 -04:00
parent c64cc7d5e2
commit 97ee71bcf4
No known key found for this signature in database
GPG Key ID: FC2CB64B00D257BE
26 changed files with 6545 additions and 6479 deletions

View File

@ -1,5 +1,6 @@
package emu.grasscutter.data.excels;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.ItemParamData;
@ -14,7 +15,8 @@ public class CompoundData extends GameResource {
@Getter(onMethod_ = @Override)
private int id;
private int groupID;
@SerializedName("groupID")
private int groupId;
private int rankLevel;
private boolean isDefaultUnlocked;
private int costTime;

View File

@ -116,7 +116,7 @@ public class WorldChallenge {
public void fail() {
if (!this.inProgress()) return;
this.finish(true);
this.finish(false);
this.getScene()
.getScriptManager()
@ -127,7 +127,7 @@ public class WorldChallenge {
private void finish(boolean success) {
this.progress = false;
this.success = success;
this.finishedTime = (int) ((System.currentTimeMillis() - this.startedAt) / 1000L);
this.finishedTime = (int) ((this.scene.getSceneTimeSeconds() - this.startedAt));
getScene().broadcastPacket(new PacketDungeonChallengeFinishNotify(this));
}

View File

@ -55,16 +55,13 @@ public class EntityAvatar extends GameEntity {
this.avatar = avatar;
this.avatar.setCurrentEnergy();
if (scene != null) this.id = getScene().getWorld().getNextEntityId(EntityIdType.AVATAR);
if (getScene() != null) {
this.id = getScene().getWorld().getNextEntityId(EntityIdType.AVATAR);
var weapon = getAvatar().getWeapon();
GameItem weapon = this.getAvatar().getWeapon();
if (weapon != null) {
weapon.setWeaponEntityId(getScene().getWorld().getNextEntityId(EntityIdType.WEAPON));
}
}
}
@Override
public int getEntityTypeId() {

View File

@ -2,10 +2,15 @@ package emu.grasscutter.game.entity;
import emu.grasscutter.data.binout.config.ConfigEntityGadget;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.quest.enums.QuestContent;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.server.event.entity.EntityDamageEvent;
import emu.grasscutter.utils.Position;
import lombok.Getter;
import static emu.grasscutter.scripts.constants.EventType.EVENT_SPECIFIC_GADGET_HP_CHANGE;
public abstract class EntityBaseGadget extends GameEntity {
@Getter(onMethod_ = @Override)
protected final Position position;
@ -33,6 +38,29 @@ public abstract class EntityBaseGadget extends GameEntity {
@Override
public void onDeath(int killerId) {
super.onDeath(killerId); // Invoke super class's onDeath() method.
getScene()
.getPlayers()
.forEach(
p ->
p.getQuestManager()
.queueEvent(QuestContent.QUEST_CONTENT_DESTROY_GADGET, this.getGadgetId()));
}
@Override
public void runLuaCallbacks(EntityDamageEvent event) {
super.runLuaCallbacks(event);
getScene()
.getScriptManager()
.callEvent(
new ScriptArgs(
this.getGroupId(),
EVENT_SPECIFIC_GADGET_HP_CHANGE,
getConfigId(),
getGadgetId())
.setSourceEntityId(getId())
.setParam3((int) this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP))
.setEventSource(Integer.toString(getConfigId())));
}
protected void fillFightProps(ConfigEntityGadget configGadget) {

View File

@ -157,6 +157,10 @@ public abstract class GameEntity {
this.damage(amount, 0, ElementType.None);
}
public void damage(float amount, ElementType attackType) {
this.damage(amount, 0, attackType);
}
public void damage(float amount, int killerId, ElementType attackType) {
// Check if the entity has properties.
if (this.getFightProperties() == null || !hasFightProperty(FightProperty.FIGHT_PROP_CUR_HP)) {
@ -177,12 +181,15 @@ public abstract class GameEntity {
this.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, -(event.getDamage()));
}
this.lastAttackType = attackType;
// Check if dead
boolean isDead = false;
if (this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) <= 0f) {
this.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0f);
isDead = true;
}
this.runLuaCallbacks(event);
// Packets
this.getScene()

View File

@ -1,6 +1,8 @@
package emu.grasscutter.game.entity.gadget;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.GatherData;
import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.EntityItem;
@ -15,15 +17,25 @@ import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
import emu.grasscutter.utils.Utils;
public class GadgetGatherObject extends GadgetContent {
public final class GadgetGatherObject extends GadgetContent {
private int itemId;
private boolean isForbidGuest;
public GadgetGatherObject(EntityGadget gadget) {
super(gadget);
// overwrites the default spawn handling
if (gadget.getSpawnEntry() != null) {
this.itemId = gadget.getSpawnEntry().getGatherItemId();
return;
}
GatherData gatherData = GameData.getGatherDataMap().get(gadget.getPointType());
if (gatherData != null) {
this.itemId = gatherData.getItemId();
this.isForbidGuest = gatherData.isForbidGuest();
} else {
Grasscutter.getLogger().error("invalid gather object: {}", gadget.getConfigId());
}
}

View File

@ -3,38 +3,45 @@ package emu.grasscutter.game.entity.gadget;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.GatherData;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.EntityItem;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
import emu.grasscutter.net.proto.GatherGadgetInfoOuterClass.GatherGadgetInfo;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
import emu.grasscutter.utils.Utils;
public class GadgetGatherPoint extends GadgetContent {
private final int itemId;
private boolean isForbidGuest;
/** Spawner for the gather objects */
public final class GadgetGatherPoint extends GadgetContent {
private final GatherData gatherData;
private final EntityGadget gatherObjectChild;
public GadgetGatherPoint(EntityGadget gadget) {
super(gadget);
if (gadget.getSpawnEntry() != null) {
this.itemId = gadget.getSpawnEntry().getGatherItemId();
} else {
GatherData gatherData = GameData.getGatherDataMap().get(gadget.getPointType());
this.itemId = gatherData.getItemId();
this.isForbidGuest = gatherData.isForbidGuest();
}
this.gatherData = GameData.getGatherDataMap().get(gadget.getPointType());
var scene = gadget.getScene();
gatherObjectChild = new EntityGadget(scene, gatherData.getGadgetId(), gadget.getBornPos());
gatherObjectChild.setBlockId(gadget.getBlockId());
gatherObjectChild.setConfigId(gadget.getConfigId());
gatherObjectChild.setGroupId(gadget.getGroupId());
gatherObjectChild.getRotation().set(gadget.getRotation());
gatherObjectChild.setState(gadget.getState());
gatherObjectChild.setPointType(gadget.getPointType());
gatherObjectChild.setMetaGadget(gadget.getMetaGadget());
gatherObjectChild.buildContent();
gadget.getChildren().add(gatherObjectChild);
scene.addEntity(gatherObjectChild);
}
public int getItemId() {
return this.itemId;
return this.gatherData.getItemId();
}
public boolean isForbidGuest() {
return isForbidGuest;
return this.gatherData.isForbidGuest();
}
public boolean onInteract(Player player, GadgetInteractReq req) {
@ -46,6 +53,7 @@ public class GadgetGatherPoint extends GadgetContent {
}
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
// todo does official use this for the spawners?
GatherGadgetInfo gatherGadgetInfo =
GatherGadgetInfo.newBuilder()
.setItemId(this.getItemId())
@ -54,30 +62,4 @@ public class GadgetGatherPoint extends GadgetContent {
gadgetInfo.setGatherGadget(gatherGadgetInfo);
}
public void dropItems(Player player) {
Scene scene = getGadget().getScene();
int times = Utils.randomRange(1, 2);
for (int i = 0; i < times; i++) {
EntityItem item =
new EntityItem(
scene,
player,
GameData.getItemDataMap().get(itemId),
getGadget()
.getPosition()
.clone()
.addY(2f)
.addX(Utils.randomFloatRange(-1f, 1f))
.addZ(Utils.randomFloatRange(-1f, 1f)),
1,
true);
scene.addEntity(item);
}
scene.killEntity(this.getGadget(), player.getTeamManager().getCurrentAvatarEntity().getId());
// Todo: add record
}
}

View File

@ -11,7 +11,7 @@ import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import java.util.Arrays;
public class GadgetWorktop extends GadgetContent {
public final class GadgetWorktop extends GadgetContent {
private IntSet worktopOptions;
private WorktopWorktopOptionHandler handler;
@ -20,6 +20,9 @@ public class GadgetWorktop extends GadgetContent {
}
public IntSet getWorktopOptions() {
if (this.worktopOptions == null) {
this.worktopOptions = new IntOpenHashSet();
}
return worktopOptions;
}

View File

@ -44,6 +44,12 @@ public class BossChestInteractHandler implements ChestInteractHandler {
var reward = worldDataManager.getRewardByBossId(monster.monster_id);
if (reward == null) {
var dungeonManager = player.getScene().getDungeonManager();
if (dungeonManager != null) {
return dungeonManager.getStatueDrops(
player, useCondensedResin, chest.getGadget().getGroupId());
}
Grasscutter.getLogger()
.warn("Could not found the reward of boss monster {}", monster.monster_id);
return false;

View File

@ -2,6 +2,8 @@ package emu.grasscutter.game.inventory;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.Getter;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
@ -27,7 +29,7 @@ public enum EquipType {
});
}
private final int value;
@Getter private final int value;
EquipType(int value) {
this.value = value;
@ -40,8 +42,4 @@ public enum EquipType {
public static EquipType getTypeByName(String name) {
return stringMap.getOrDefault(name, EQUIP_NONE);
}
public int getValue() {
return value;
}
}

View File

@ -57,6 +57,7 @@ public class GameItem {
@Getter @Setter private int equipCharacter;
@Transient @Getter @Setter private int weaponEntityId;
@Transient @Getter private boolean newItem = false;
public GameItem() {
// Morphia only
@ -116,6 +117,30 @@ public class GameItem {
}
}
public int getOwnerId() {
return ownerId;
}
public void setOwner(Player player) {
this.ownerId = player.getUid();
this.guid = player.getNextGameGuid();
}
public void checkIsNew(Inventory inventory) {
// display notification when player obtain new item
if (inventory.getItemByGuid(this.itemId) == null) {
this.newItem = true;
}
}
public ObjectId getObjectId() {
return id;
}
public ItemType getItemType() {
return this.itemData.getItemType();
}
public static int getMinPromoteLevel(int level) {
if (level > 80) {
return 6;
@ -133,23 +158,6 @@ public class GameItem {
return 0;
}
public int getOwnerId() {
return ownerId;
}
public void setOwner(Player player) {
this.ownerId = player.getUid();
this.guid = player.getNextGameGuid();
}
public ObjectId getObjectId() {
return id;
}
public ItemType getItemType() {
return this.itemData.getItemType();
}
public int getEquipSlot() {
return this.getItemData().getEquipType().getValue();
}
@ -353,7 +361,7 @@ public class GameItem {
return ItemHint.newBuilder()
.setItemId(getItemId())
.setCount(getCount())
.setIsNew(false)
.setIsNew(this.isNewItem())
.build();
}

View File

@ -15,11 +15,9 @@ import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.props.ItemUseAction.UseItemParams;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.game.quest.enums.QuestContent;
import emu.grasscutter.net.proto.ItemParamOuterClass.ItemParam;
import emu.grasscutter.server.packet.send.PacketAvatarEquipChangeNotify;
import emu.grasscutter.server.packet.send.PacketItemAddHintNotify;
import emu.grasscutter.server.packet.send.PacketStoreItemChangeNotify;
import emu.grasscutter.server.packet.send.PacketStoreItemDelNotify;
import emu.grasscutter.server.packet.send.*;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
@ -96,12 +94,7 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
GameItem result = putItem(item);
if (result != null) {
getPlayer()
.getBattlePassManager()
.triggerMission(
WatcherTriggerType.TRIGGER_OBTAIN_MATERIAL_NUM,
result.getItemId(),
result.getCount());
this.triggerAddItemEvents(result);
getPlayer().sendPacket(new PacketStoreItemChangeNotify(result));
return true;
}
@ -110,18 +103,19 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
}
public boolean addItem(GameItem item, ActionReason reason) {
boolean result = addItem(item);
if (result && reason != null) {
getPlayer().sendPacket(new PacketItemAddHintNotify(item, reason));
}
return result;
return addItem(item, reason, false);
}
public boolean addItem(GameItem item, ActionReason reason, boolean forceNotify) {
boolean result = addItem(item);
if (item.getItemData().getMaterialType() == MaterialType.MATERIAL_AVATAR) {
getPlayer()
.sendPacket(
new PacketAddNoGachaAvatarCardNotify(
(item.getItemId() % 1000) + 10000000, reason, item));
}
if (reason != null && (forceNotify || result)) {
getPlayer().sendPacket(new PacketItemAddHintNotify(item, reason));
}
@ -155,12 +149,7 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
e.printStackTrace();
}
if (result != null) {
getPlayer()
.getBattlePassManager()
.triggerMission(
WatcherTriggerType.TRIGGER_OBTAIN_MATERIAL_NUM,
result.getItemId(),
result.getCount());
this.triggerAddItemEvents(result);
changedItems.add(result);
}
}
@ -168,11 +157,30 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
return;
}
if (reason != null) {
getPlayer().sendPacket(new PacketItemAddHintNotify(changedItems, reason));
getPlayer().sendPacket(new PacketItemAddHintNotify(items, reason));
}
getPlayer().sendPacket(new PacketStoreItemChangeNotify(changedItems));
}
private void triggerAddItemEvents(GameItem result) {
getPlayer()
.getBattlePassManager()
.triggerMission(
WatcherTriggerType.TRIGGER_OBTAIN_MATERIAL_NUM, result.getItemId(), result.getCount());
getPlayer()
.getQuestManager()
.queueEvent(QuestContent.QUEST_CONTENT_OBTAIN_ITEM, result.getItemId(), result.getCount());
}
private void triggerRemItemEvents(GameItem item, int removeCount) {
getPlayer()
.getBattlePassManager()
.triggerMission(WatcherTriggerType.TRIGGER_COST_MATERIAL, item.getItemId(), removeCount);
getPlayer()
.getQuestManager()
.queueEvent(QuestContent.QUEST_CONTENT_ITEM_LESS_THAN, item.getItemId(), item.getCount());
}
public void addItemParams(Collection<ItemParam> items) {
addItems(
items.stream().map(param -> new GameItem(param.getItemId(), param.getCount())).toList(),
@ -194,6 +202,8 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
var data = item.getItemData();
if (data == null) return null;
this.player.getProgressManager().addItemObtainedHistory(item.getItemId(), item.getCount());
if (data.isUseOnGain()) {
var params = new UseItemParams(this.player, data.getUseTarget());
params.usedItemId = data.getId();
@ -267,6 +277,7 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
this.player.getCodex().checkAddedItem(item);
// Set owner and guid FIRST!
item.setOwner(this.player);
item.checkIsNew(this);
// Put in item store
getItems().put(item.getGuid(), item);
if (tab != null) {
@ -467,9 +478,7 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
// Battle pass trigger
int removeCount = Math.min(count, item.getCount());
getPlayer()
.getBattlePassManager()
.triggerMission(WatcherTriggerType.TRIGGER_COST_MATERIAL, item.getItemId(), removeCount);
this.triggerRemItemEvents(item, removeCount);
// Update in db
item.save();

View File

@ -2,6 +2,8 @@ package emu.grasscutter.game.inventory;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.Getter;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
@ -27,7 +29,7 @@ public enum ItemQuality {
});
}
private final int value;
@Getter private final int value;
ItemQuality(int value) {
this.value = value;
@ -40,8 +42,4 @@ public enum ItemQuality {
public static ItemQuality getTypeByName(String name) {
return stringMap.getOrDefault(name, QUALITY_NONE);
}
public int getValue() {
return value;
}
}

View File

@ -2,6 +2,8 @@ package emu.grasscutter.game.inventory;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.Getter;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
@ -27,7 +29,7 @@ public enum ItemType {
});
}
private final int value;
@Getter private final int value;
ItemType(int value) {
this.value = value;
@ -40,8 +42,4 @@ public enum ItemType {
public static ItemType getTypeByName(String name) {
return stringMap.getOrDefault(name, ITEM_NONE);
}
public int getValue() {
return value;
}
}

View File

@ -2,6 +2,8 @@ package emu.grasscutter.game.inventory;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.Getter;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
@ -61,7 +63,7 @@ public enum MaterialType {
});
}
private final int value;
@Getter private final int value;
MaterialType(int value) {
this.value = value;
@ -74,8 +76,4 @@ public enum MaterialType {
public static MaterialType getTypeByName(String name) {
return stringMap.getOrDefault(name, MATERIAL_NONE);
}
public int getValue() {
return value;
}
}

View File

@ -27,51 +27,13 @@ public class BlossomManager {
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 BlossomManager(Scene scene) {
this.scene = scene;
}
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 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;
}
public void onTick() {
synchronized (blossomActivities) {
var it = blossomActivities.iterator();
@ -201,6 +163,35 @@ public class BlossomManager {
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) {
@ -240,4 +231,14 @@ public class BlossomManager {
}
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;
}
}

View File

@ -39,7 +39,7 @@ public class CookingCompoundManager extends BasePlayerManager {
if (compound.isDefaultUnlocked()) {
defaultUnlockedCompounds.add(id);
}
compoundGroups.computeIfAbsent(compound.getGroupID(), gid -> new HashSet<>()).add(id);
compoundGroups.computeIfAbsent(compound.getGroupId(), gid -> new HashSet<>()).add(id);
});
// TODO:Because we haven't implemented fishing feature,unlock all compounds related to
// fish.Besides,it should be bound to player rather than manager.

View File

@ -412,9 +412,7 @@ public class EnergyManager extends BasePlayerManager {
public void setEnergyUsage(boolean energyUsage) {
this.energyUsage = energyUsage;
if (!energyUsage) { // Refill team energy if usage is disabled
for (EntityAvatar entityAvatar : this.player.getTeamManager().getActiveTeam()) {
entityAvatar.addEnergy(1000, PropChangeReason.PROP_CHANGE_REASON_GM, true);
}
this.refillTeamEnergy(PropChangeReason.PROP_CHANGE_REASON_GM, true);
}
}
}

View File

@ -141,6 +141,7 @@ public class StaminaManager extends BasePlayerManager {
put(242301, 0.8f);
put(542301, 0.8f);
}};
private final Logger logger = Grasscutter.getLogger();
private final HashMap<String, BeforeUpdateStaminaListener> beforeUpdateStaminaListeners = new HashMap<>();
private final HashMap<String, AfterUpdateStaminaListener> afterUpdateStaminaListeners = new HashMap<>();
@ -414,13 +415,7 @@ public class StaminaManager extends BasePlayerManager {
// Internal handler
private void handleImmediateStamina(GameSession session, @NotNull MotionState motionState) {
if (currentState == motionState) {
if (motionState.equals(MotionState.MOTION_STATE_CLIMB_JUMP)) {
updateStaminaRelative(session, new Consumption(ConsumptionType.CLIMB_JUMP), true);
}
return;
}
if (currentState == motionState) return;
switch (motionState) {
case MOTION_STATE_CLIMB ->
updateStaminaRelative(session, new Consumption(ConsumptionType.CLIMB_START), true);
@ -440,6 +435,73 @@ public class StaminaManager extends BasePlayerManager {
updateStaminaRelative(session, consumption, true);
}
private class SustainedStaminaHandler extends TimerTask {
public void run() {
boolean moving = isPlayerMoving();
int currentCharacterStamina = getCurrentCharacterStamina();
int maxCharacterStamina = getMaxCharacterStamina();
int currentVehicleStamina = getCurrentVehicleStamina();
int maxVehicleStamina = getMaxVehicleStamina();
if (moving || (currentCharacterStamina < maxCharacterStamina) || (currentVehicleStamina < maxVehicleStamina)) {
logger.trace("Player moving: " + moving + ", stamina full: " +
(currentCharacterStamina >= maxCharacterStamina) + ", recalculate stamina");
boolean isCharacterStamina = true;
Consumption consumption;
if (MotionStatesCategorized.get("CLIMB").contains(currentState)) {
consumption = getClimbConsumption();
} else if (MotionStatesCategorized.get("DASH").contains(currentState)) {
consumption = getDashConsumption();
} else if (MotionStatesCategorized.get("FLY").contains(currentState)) {
consumption = getFlyConsumption();
} else if (MotionStatesCategorized.get("RUN").contains(currentState)) {
consumption = new Consumption(ConsumptionType.RUN);
} else if (MotionStatesCategorized.get("SKIFF").contains(currentState)) {
consumption = getSkiffConsumption();
isCharacterStamina = false;
} else if (MotionStatesCategorized.get("STANDBY").contains(currentState)) {
consumption = new Consumption(ConsumptionType.STANDBY);
} else if (MotionStatesCategorized.get("SWIM").contains(currentState)) {
consumption = getSwimConsumptions();
} else if (MotionStatesCategorized.get("WALK").contains(currentState)) {
consumption = new Consumption(ConsumptionType.WALK);
} else if (MotionStatesCategorized.get("NOCOST_NORECOVER").contains(currentState)) {
consumption = new Consumption();
} else if (MotionStatesCategorized.get("OTHER").contains(currentState)) {
consumption = getOtherConsumptions();
} else { // ignore
return;
}
if (consumption.amount < 0 && isCharacterStamina) {
// Do not apply reduction factor when recovering stamina
if (player.getTeamManager().getTeamResonances().contains(10301)) {
consumption.amount *= 0.85f;
}
}
// Delay 1 seconds before starts recovering stamina
if (consumption.amount != 0 && cachedSession != null) {
if (consumption.amount < 0) {
staminaRecoverDelay = 0;
}
if (consumption.amount > 0
&& consumption.type != ConsumptionType.POWERED_FLY
&& consumption.type != ConsumptionType.POWERED_SKIFF) {
// For POWERED_* recover immediately - things like Amber's gliding exam and skiff challenges may require this.
if (staminaRecoverDelay < 5) {
// For others recover after 1 seconds (5 ticks) - as official server does.
staminaRecoverDelay++;
consumption.amount = 0;
logger.trace("Delaying recovery: " + staminaRecoverDelay);
}
}
updateStaminaRelative(cachedSession, consumption, isCharacterStamina);
}
}
previousState = currentState;
previousCoordinates = currentCoordinates.clone();
}
}
private void handleDrowning() {
// TODO: fix drowning waverider entity
int stamina = getCurrentCharacterStamina();
@ -452,6 +514,10 @@ public class StaminaManager extends BasePlayerManager {
}
}
// Consumption Calculators
// Stamina Consumption Reduction: https://genshin-impact.fandom.com/wiki/Stamina
private Consumption getFightConsumption(int skillCasting) {
// Talent moving
if (TalentMovements.contains(skillCasting)) {
@ -471,10 +537,6 @@ public class StaminaManager extends BasePlayerManager {
};
}
// Consumption Calculators
// Stamina Consumption Reduction: https://genshin-impact.fandom.com/wiki/Stamina
private Consumption getClimbConsumption() {
Consumption consumption = new Consumption();
if (currentState == MotionState.MOTION_STATE_CLIMB && isPlayerMoving()) {
@ -552,6 +614,8 @@ public class StaminaManager extends BasePlayerManager {
return new Consumption();
}
// Reduction getter
private float getTalentCostReductionFactor(HashMap<Integer, Float> talentReductionMap) {
// All known talents reductions are not stackable
float reduction = 1;
@ -568,8 +632,6 @@ public class StaminaManager extends BasePlayerManager {
return reduction;
}
// Reduction getter
private float getFoodCostReductionFactor(HashMap<Integer, Float> foodReductionMap) {
// All known food reductions are not stackable
// TODO: Check consumed food (buff?) and return proper factor
@ -633,76 +695,11 @@ public class StaminaManager extends BasePlayerManager {
private Consumption getSwordCost(int skillId) {
Consumption consumption = new Consumption(ConsumptionType.FIGHT, -2000);
// Character specific handling
if (skillId == 10421) {
switch (skillId) {
case 10421:
consumption.amount = -2500;
break;
}
return consumption;
}
private class SustainedStaminaHandler extends TimerTask {
public void run() {
boolean moving = isPlayerMoving();
int currentCharacterStamina = getCurrentCharacterStamina();
int maxCharacterStamina = getMaxCharacterStamina();
int currentVehicleStamina = getCurrentVehicleStamina();
int maxVehicleStamina = getMaxVehicleStamina();
if (moving || (currentCharacterStamina < maxCharacterStamina) || (currentVehicleStamina < maxVehicleStamina)) {
logger.trace("Player moving: " + moving + ", stamina full: " +
(currentCharacterStamina >= maxCharacterStamina) + ", recalculate stamina");
boolean isCharacterStamina = true;
Consumption consumption;
if (MotionStatesCategorized.get("CLIMB").contains(currentState)) {
consumption = getClimbConsumption();
} else if (MotionStatesCategorized.get("DASH").contains(currentState)) {
consumption = getDashConsumption();
} else if (MotionStatesCategorized.get("FLY").contains(currentState)) {
consumption = getFlyConsumption();
} else if (MotionStatesCategorized.get("RUN").contains(currentState)) {
consumption = new Consumption(ConsumptionType.RUN);
} else if (MotionStatesCategorized.get("SKIFF").contains(currentState)) {
consumption = getSkiffConsumption();
isCharacterStamina = false;
} else if (MotionStatesCategorized.get("STANDBY").contains(currentState)) {
consumption = new Consumption(ConsumptionType.STANDBY);
} else if (MotionStatesCategorized.get("SWIM").contains(currentState)) {
consumption = getSwimConsumptions();
} else if (MotionStatesCategorized.get("WALK").contains(currentState)) {
consumption = new Consumption(ConsumptionType.WALK);
} else if (MotionStatesCategorized.get("NOCOST_NORECOVER").contains(currentState)) {
consumption = new Consumption();
} else if (MotionStatesCategorized.get("OTHER").contains(currentState)) {
consumption = getOtherConsumptions();
} else { // ignore
return;
}
if (consumption.amount < 0 && isCharacterStamina) {
// Do not apply reduction factor when recovering stamina
if (player.getTeamManager().getTeamResonances().contains(10301)) {
consumption.amount *= 0.85f;
}
}
// Delay 1 seconds before starts recovering stamina
if (consumption.amount != 0 && cachedSession != null) {
if (consumption.amount < 0) {
staminaRecoverDelay = 0;
}
if (consumption.amount > 0
&& consumption.type != ConsumptionType.POWERED_FLY
&& consumption.type != ConsumptionType.POWERED_SKIFF) {
// For POWERED_* recover immediately - things like Amber's gliding exam and skiff challenges may require this.
if (staminaRecoverDelay < 5) {
// For others recover after 1 seconds (5 ticks) - as official server does.
staminaRecoverDelay++;
consumption.amount = 0;
logger.trace("Delaying recovery: " + staminaRecoverDelay);
}
}
updateStaminaRelative(cachedSession, consumption, isCharacterStamina);
}
}
previousState = currentState;
previousCoordinates = currentCoordinates.clone();
}
}
}

View File

@ -5,14 +5,18 @@ import emu.grasscutter.data.binout.ScenePointEntry;
import emu.grasscutter.data.excels.OpenStateData;
import emu.grasscutter.data.excels.OpenStateData.OpenStateCondType;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.quest.enums.ParentQuestState;
import emu.grasscutter.game.quest.enums.QuestCond;
import emu.grasscutter.game.quest.enums.QuestContent;
import emu.grasscutter.game.quest.enums.QuestState;
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.server.packet.send.*;
import java.util.Set;
import java.util.stream.Collectors;
import static emu.grasscutter.scripts.constants.EventType.EVENT_UNLOCK_TRANS_POINT;
// @Entity
public final class PlayerProgressManager extends BasePlayerDataManager {
/******************************************************************************************************************
@ -33,22 +37,25 @@ public final class PlayerProgressManager extends BasePlayerDataManager {
GameData.getOpenStateList().stream()
.filter(
s ->
s.isDefaultState() // Actual default-opened states.
s.isDefaultState() && !s.isAllowClientOpen() // Actual default-opened states.
|| ((s.getCond().size() == 1)
&& (s.getCond().get(0).getCondType()
== OpenStateCondType.OPEN_STATE_COND_PLAYER_LEVEL)
&& (s.getCond().get(0).getParam() == 1))
// All states whose unlock we don't handle correctly yet.
|| (s.getCond().stream()
.filter(
.anyMatch(
c ->
c.getCondType()
== OpenStateCondType.OPEN_STATE_COND_PLAYER_LEVEL)
.count()
== 0)
c.getCondType() == OpenStateCondType.OPEN_STATE_OFFERING_LEVEL
|| c.getCondType()
== OpenStateCondType.OPEN_STATE_CITY_REPUTATION_LEVEL))
// Always unlock OPEN_STATE_PAIMON, otherwise the player will not have a
// working chat.
|| s.getId() == 1)
.filter(
s ->
!BLACKLIST_OPEN_STATES.contains(s.getId())) // Filter out states in the blacklist.
.map(s -> s.getId())
.map(OpenStateData::getId)
.collect(Collectors.toSet());
public PlayerProgressManager(Player player) {
@ -88,6 +95,10 @@ public final class PlayerProgressManager extends BasePlayerDataManager {
if (value != previousValue) {
this.player.getOpenStates().put(openState, value);
this.player
.getQuestManager()
.queueEvent(QuestCond.QUEST_COND_OPEN_STATE_EQUAL, openState, value);
if (sendNotify) {
player.getSession().send(new PacketOpenStateChangeNotify(openState, value));
}
@ -104,19 +115,31 @@ public final class PlayerProgressManager extends BasePlayerDataManager {
private boolean areConditionsMet(OpenStateData openState) {
// Check all conditions and test if at least one of them is violated.
for (var condition : openState.getCond()) {
switch (condition.getCondType()) {
// For level conditions, check if the player has reached the necessary level.
if (condition.getCondType() == OpenStateCondType.OPEN_STATE_COND_PLAYER_LEVEL) {
case OPEN_STATE_COND_PLAYER_LEVEL -> {
if (this.player.getLevel() < condition.getParam()) {
return false;
}
} else if (condition.getCondType() == OpenStateCondType.OPEN_STATE_COND_QUEST) {
// ToDo: Implement.
} else if (condition.getCondType() == OpenStateCondType.OPEN_STATE_COND_PARENT_QUEST) {
// ToDo: Implement.
} else if (condition.getCondType() == OpenStateCondType.OPEN_STATE_OFFERING_LEVEL) {
// ToDo: Implement.
} else if (condition.getCondType() == OpenStateCondType.OPEN_STATE_CITY_REPUTATION_LEVEL) {
}
case OPEN_STATE_COND_QUEST -> {
// check sub quest id for quest finished met requirements
var quest = this.player.getQuestManager().getQuestById(condition.getParam());
if (quest == null || quest.getState() != QuestState.QUEST_STATE_FINISHED) {
return false;
}
}
case OPEN_STATE_COND_PARENT_QUEST -> {
// check main quest id for quest finished met requirements
// TODO not sure if its having or finished quest
var mainQuest = this.player.getQuestManager().getMainQuestById(condition.getParam());
if (mainQuest == null
|| mainQuest.getState() != ParentQuestState.PARENT_QUEST_STATE_FINISHED) {
return false;
}
}
// ToDo: Implement.
case OPEN_STATE_OFFERING_LEVEL, OPEN_STATE_CITY_REPUTATION_LEVEL -> {}
}
}
@ -229,6 +252,10 @@ public final class PlayerProgressManager extends BasePlayerDataManager {
this.player
.getQuestManager()
.queueEvent(QuestContent.QUEST_CONTENT_UNLOCK_TRANS_POINT, sceneId, pointId);
this.player
.getScene()
.getScriptManager()
.callEvent(new ScriptArgs(0, EVENT_UNLOCK_TRANS_POINT, sceneId, pointId));
// Send packet.
this.player.sendPacket(new PacketScenePointUnlockNotify(sceneId, pointId));

View File

@ -68,6 +68,8 @@ public final class TeamManager extends BasePlayerDataManager {
this.gadgets = new HashSet<>();
this.teamResonances = new IntOpenHashSet();
this.teamResonancesConfig = new IntOpenHashSet();
this.trialAvatars = new HashMap<>();
this.trialAvatarTeam = new TeamInfo();
}
public TeamManager(Player player) {

View File

@ -14,12 +14,6 @@ public class ItemUseUnlockHomeModule extends ItemUseInt {
@Override
public boolean useItem(UseItemParams params) {
return true;
}
@Override
public boolean postUseItem(UseItemParams params) {
params.player.addRealmList(this.i);
return true;
return false;
}
}

View File

@ -9,6 +9,7 @@ import emu.grasscutter.data.excels.*;
import emu.grasscutter.data.excels.codex.CodexAnimalData;
import emu.grasscutter.data.excels.monster.MonsterData;
import emu.grasscutter.data.excels.world.WorldLevelData;
import emu.grasscutter.data.server.Grid;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.dungeons.DungeonManager;
import emu.grasscutter.game.dungeons.DungeonSettleListener;
@ -46,6 +47,8 @@ import lombok.Getter;
import lombok.Setter;
import lombok.val;
import javax.annotation.Nullable;
public final class Scene {
@Getter private final World world;
@Getter private final SceneData sceneData;
@ -126,6 +129,10 @@ public final class Scene {
.orElse(null);
}
@Nullable public Route getSceneRouteById(int routeId) {
return sceneRoutes.get(routeId);
}
/**
* Sets the scene's pause state. Sends the current scene's time to all players.
*
@ -218,8 +225,11 @@ public final class Scene {
// Deregister scene if not in use
if (this.getPlayerCount() <= 0 && !this.dontDestroyWhenEmpty) {
this.getScriptManager().onDestroy();
this.getWorld().deregisterScene(this);
}
this.saveGroups();
}
private void setupPlayerAvatars(Player player) {
@ -229,9 +239,14 @@ public final class Scene {
// Add new entities for player
TeamInfo teamInfo = player.getTeamManager().getCurrentTeamInfo();
for (int avatarId : teamInfo.getAvatars()) {
EntityAvatar entity =
new EntityAvatar(player.getScene(), player.getAvatars().getAvatarById(avatarId));
player.getTeamManager().getActiveTeam().add(entity);
Avatar avatar = player.getAvatars().getAvatarById(avatarId);
if (avatar == null) {
if (player.getTeamManager().isUsingTrialTeam()) {
avatar = player.getTeamManager().getTrialAvatars().get(avatarId);
}
if (avatar == null) continue;
}
player.getTeamManager().getActiveTeam().add(new EntityAvatar(player.getScene(), avatar));
}
// Limit character index in case its out of bounds
@ -322,7 +337,12 @@ public final class Scene {
}
public synchronized void removeEntities(List<GameEntity> entity, VisionType visionType) {
var toRemove = entity.stream().map(this::removeEntityDirectly).toList();
var toRemove =
entity.stream()
.filter(Objects::nonNull)
.map(this::removeEntityDirectly)
.filter(Objects::nonNull)
.toList();
if (toRemove.size() > 0) {
this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, visionType));
}
@ -408,17 +428,21 @@ public final class Scene {
// Death event
target.onDeath(attackerId);
this.triggerDungeonEvent(
DungeonPassConditionType.DUNGEON_COND_KILL_MONSTER_COUNT, ++killedMonsterCount);
}
public void onTick() {
// Disable ticking for the player's home world.
if (this.getSceneType() == SceneType.SCENE_HOME_WORLD
|| this.getSceneType() == SceneType.SCENE_HOME_ROOM) {
this.finishLoading();
return;
}
if (this.getScriptManager().isInit()) {
this.checkBlocks();
// this.checkBlocks();
this.checkGroups();
} else {
// TEMPORARY
this.checkSpawns();
@ -431,7 +455,10 @@ public final class Scene {
challenge.onCheckTimeOut();
}
this.blossomManager.onTick();
var sceneTime = getSceneTimeSeconds();
getEntities().forEach((eid, e) -> e.onTick(sceneTime));
blossomManager.onTick();
checkNpcGroup();
@ -701,10 +728,17 @@ public final class Scene {
Grasscutter.getConfig().server.game.loadEntitiesForPlayerRange);
}
private boolean unloadBlockIfNotVisible(Collection<SceneBlock> visible, SceneBlock block) {
if (visible.contains(block)) return false;
this.onUnloadBlock(block);
return true;
public Set<Integer> getPlayerActiveGroups(Player player) {
// consider the borders' entities of blocks, so we check if contains by index
Position playerPosition = player.getPosition();
Set<Integer> activeGroups = new HashSet<>();
for (int i = 0; i < 4; i++) {
Grid grid = getScriptManager().getGroupGrids().get(i);
activeGroups.addAll(grid.getNearbyGroups(i, playerPosition));
}
return activeGroups;
}
public synchronized boolean loadBlock(SceneBlock block) {
@ -715,63 +749,44 @@ public final class Scene {
return true;
}
public synchronized void checkBlocks() {
Set<SceneBlock> visible =
public synchronized void checkGroups() {
Set<Integer> visible =
this.players.stream()
.map(this::getPlayerActiveBlocks)
.map(player -> this.getPlayerActiveGroups(player))
.flatMap(Collection::stream)
.collect(Collectors.toSet());
this.loadedBlocks.removeIf(block -> unloadBlockIfNotVisible(visible, block));
Iterator<SceneGroup> it = this.loadedGroups.iterator();
while (it.hasNext()) {
SceneGroup group = it.next();
if (!visible.contains(group.id) && !group.dynamic_load)
unloadGroup(scriptManager.getBlocks().get(group.block_id), group.id);
}
List<SceneGroup> toLoad =
visible.stream()
.filter(block -> !this.loadBlock(block))
.forEach(
block -> {
// dynamic load the groups for players in a loaded block
var toLoad =
this.players.stream()
.filter(p -> block.contains(p.getPosition()))
.map(p -> this.playerMeetGroups(p, block))
.flatMap(Collection::stream)
.filter(g -> this.loadedGroups.stream().filter(gr -> gr.id == g).count() == 0)
.map(
g -> {
for (var b : scriptManager.getBlocks().values()) {
loadBlock(b);
SceneGroup group = b.groups.getOrDefault(g, null);
if (group != null && !group.dynamic_load) return group;
}
return null;
})
.filter(Objects::nonNull)
.toList();
this.onLoadGroup(toLoad);
});
}
public List<SceneGroup> playerMeetGroups(Player player, SceneBlock block) {
List<SceneGroup> sceneGroups =
SceneIndexManager.queryNeighbors(
block.sceneGroupIndex,
player.getPosition().toDoubleArray(),
Grasscutter.getConfig().server.game.loadEntitiesForPlayerRange);
List<SceneGroup> groups =
sceneGroups.stream()
.filter(
group -> !scriptManager.getLoadedGroupSetPerBlock().get(block.id).contains(group))
.peek(group -> scriptManager.getLoadedGroupSetPerBlock().get(block.id).add(group))
.toList();
if (groups.size() == 0) {
return List.of();
}
return groups;
if (!toLoad.isEmpty()) this.onRegisterGroups();
}
public void onLoadBlock(SceneBlock block, List<Player> players) {
this.getScriptManager().loadBlockFromScript(block);
scriptManager.getLoadedGroupSetPerBlock().put(block.id, new HashSet<>());
// the groups form here is not added in current scene
var groups =
players.stream()
.filter(player -> block.contains(player.getPosition()))
.map(p -> playerMeetGroups(p, block))
.flatMap(Collection::stream)
.toList();
onLoadGroup(groups);
Grasscutter.getLogger().info("Scene {} Block {} loaded.", this.getId(), block.id);
}
@ -780,7 +795,7 @@ public final class Scene {
if (group == null || getScriptManager().getGroupInstanceById(group_id) != null)
return -1; // Group not found or already instanced
onLoadGroup(new ArrayList<>(List.of(group)));
this.onLoadGroup(new ArrayList<>(List.of(group)));
if (GameData.getGroupReplacements().containsKey(group_id)) onRegisterGroups();
@ -824,7 +839,6 @@ public final class Scene {
KahnsSort.Graph graph = new KahnsSort.Graph(nodes, groupList);
List<Integer> dynamicGroupsOrdered = KahnsSort.doSort(graph);
if (dynamicGroupsOrdered == null) throw new RuntimeException("Invalid group replacement graph");
// Now we can start unloading and loading groups :D
dynamicGroupsOrdered.forEach(
@ -929,38 +943,10 @@ public final class Scene {
Grasscutter.getLogger().info("Scene {} loaded {} group(s)", this.getId(), groups.size());
}
public void onUnloadBlock(SceneBlock block) {
public void unloadGroup(SceneBlock block, int group_id) {
List<GameEntity> toRemove =
this.getEntities().values().stream().filter(e -> e.getBlockId() == block.id).toList();
if (toRemove.size() > 0) {
toRemove.forEach(this::removeEntityDirectly);
this.broadcastPacket(
new PacketSceneEntityDisappearNotify(toRemove, VisionType.VISION_TYPE_REMOVE));
}
for (SceneGroup group : block.groups.values()) {
if (group.triggers != null) {
group.triggers.values().forEach(getScriptManager()::deregisterTrigger);
}
if (group.regions != null) {
group.regions.values().forEach(getScriptManager()::deregisterRegion);
}
}
scriptManager.getLoadedGroupSetPerBlock().remove(block.id);
Grasscutter.getLogger().info("Scene {} Block {} is unloaded.", this.getId(), block.id);
}
/**
* Unloads a Lua group.
*
* @param block The block that contains the group.
* @param groupId The group ID.
*/
public void unloadGroup(SceneBlock block, int groupId) {
var toRemove =
this.getEntities().values().stream()
.filter(e -> e != null && (e.getBlockId() == block.id && e.getGroupId() == groupId))
.filter(e -> e != null && (e.getBlockId() == block.id && e.getGroupId() == group_id))
.toList();
if (toRemove.size() > 0) {
@ -969,15 +955,15 @@ public final class Scene {
new PacketSceneEntityDisappearNotify(toRemove, VisionType.VISION_TYPE_REMOVE));
}
var group = block.groups.get(groupId);
var group = block.groups.get(group_id);
if (group.triggers != null) {
group.triggers.values().forEach(this.getScriptManager()::deregisterTrigger);
group.triggers.values().forEach(getScriptManager()::deregisterTrigger);
}
if (group.regions != null) {
group.regions.values().forEach(this.getScriptManager()::deregisterRegion);
group.regions.values().forEach(getScriptManager()::deregisterRegion);
}
this.scriptManager.getLoadedGroupSetPerBlock().get(block.id).remove(group);
scriptManager.getLoadedGroupSetPerBlock().get(block.id).remove(group);
this.loadedGroups.remove(group);
if (this.scriptManager.getLoadedGroupSetPerBlock().get(block.id).isEmpty()) {
@ -985,7 +971,7 @@ public final class Scene {
Grasscutter.getLogger().info("Scene {} Block {} is unloaded.", this.getId(), block.id);
}
this.broadcastPacket(new PacketGroupUnloadNotify(List.of(groupId)));
this.broadcastPacket(new PacketGroupUnloadNotify(List.of(group_id)));
this.scriptManager.unregisterGroup(group);
}
@ -1155,4 +1141,8 @@ public final class Scene {
}
}
}
public void saveGroups() {
this.getScriptManager().getCachedGroupInstances().values().forEach(SceneGroupInstance::save);
}
}

View File

@ -217,9 +217,14 @@ public class World implements Iterable<Player> {
}
public void deregisterScene(Scene scene) {
scene.saveGroups();
this.getScenes().remove(scene.getId());
}
public void save() {
this.getScenes().values().forEach(Scene::saveGroups);
}
public boolean transferPlayerToScene(Player player, int sceneId, Position pos) {
return this.transferPlayerToScene(player, sceneId, TeleportType.INTERNAL, null, pos);
}
@ -299,6 +304,7 @@ public class World implements Iterable<Player> {
}
Scene oldScene = null;
if (player.getScene() != null) {
oldScene = player.getScene();
@ -312,8 +318,13 @@ public class World implements Iterable<Player> {
var newScene = this.getSceneById(teleportProperties.getSceneId());
newScene.addPlayer(player);
player.getTeamManager().applyAbilities(newScene);
player.setAvatarsAbilityForScene(newScene);
// Dungeon
// Dungeon system is handling this already
// if(dungeonData!=null){
// var dungeonManager = new DungeonManager(newScene, dungeonData);
// dungeonManager.startDungeon();
// }
SceneConfig config = newScene.getScriptManager().getConfig();
if (teleportProperties.getTeleportTo() == null && config != null) {
if (config.born_pos != null) {