mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-01-10 22:23:20 +08:00
Continue merging quests
(pt. 1)
Finished last at: `World.java`, line `player.setAvatarsAbilityForScene(newScene);`
This commit is contained in:
parent
c64cc7d5e2
commit
97ee71bcf4
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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) {
|
||||
|
@ -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()
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user