mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-01-11 05:52:58 +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;
|
package emu.grasscutter.data.excels;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
import emu.grasscutter.data.GameResource;
|
import emu.grasscutter.data.GameResource;
|
||||||
import emu.grasscutter.data.ResourceType;
|
import emu.grasscutter.data.ResourceType;
|
||||||
import emu.grasscutter.data.common.ItemParamData;
|
import emu.grasscutter.data.common.ItemParamData;
|
||||||
@ -14,7 +15,8 @@ public class CompoundData extends GameResource {
|
|||||||
@Getter(onMethod_ = @Override)
|
@Getter(onMethod_ = @Override)
|
||||||
private int id;
|
private int id;
|
||||||
|
|
||||||
private int groupID;
|
@SerializedName("groupID")
|
||||||
|
private int groupId;
|
||||||
private int rankLevel;
|
private int rankLevel;
|
||||||
private boolean isDefaultUnlocked;
|
private boolean isDefaultUnlocked;
|
||||||
private int costTime;
|
private int costTime;
|
||||||
|
@ -116,7 +116,7 @@ public class WorldChallenge {
|
|||||||
|
|
||||||
public void fail() {
|
public void fail() {
|
||||||
if (!this.inProgress()) return;
|
if (!this.inProgress()) return;
|
||||||
this.finish(true);
|
this.finish(false);
|
||||||
|
|
||||||
this.getScene()
|
this.getScene()
|
||||||
.getScriptManager()
|
.getScriptManager()
|
||||||
@ -127,7 +127,7 @@ public class WorldChallenge {
|
|||||||
private void finish(boolean success) {
|
private void finish(boolean success) {
|
||||||
this.progress = false;
|
this.progress = false;
|
||||||
this.success = success;
|
this.success = success;
|
||||||
this.finishedTime = (int) ((System.currentTimeMillis() - this.startedAt) / 1000L);
|
this.finishedTime = (int) ((this.scene.getSceneTimeSeconds() - this.startedAt));
|
||||||
getScene().broadcastPacket(new PacketDungeonChallengeFinishNotify(this));
|
getScene().broadcastPacket(new PacketDungeonChallengeFinishNotify(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,16 +55,13 @@ public class EntityAvatar extends GameEntity {
|
|||||||
|
|
||||||
this.avatar = avatar;
|
this.avatar = avatar;
|
||||||
this.avatar.setCurrentEnergy();
|
this.avatar.setCurrentEnergy();
|
||||||
|
if (scene != null) this.id = getScene().getWorld().getNextEntityId(EntityIdType.AVATAR);
|
||||||
|
|
||||||
if (getScene() != null) {
|
GameItem weapon = this.getAvatar().getWeapon();
|
||||||
this.id = getScene().getWorld().getNextEntityId(EntityIdType.AVATAR);
|
|
||||||
|
|
||||||
var weapon = getAvatar().getWeapon();
|
|
||||||
if (weapon != null) {
|
if (weapon != null) {
|
||||||
weapon.setWeaponEntityId(getScene().getWorld().getNextEntityId(EntityIdType.WEAPON));
|
weapon.setWeaponEntityId(getScene().getWorld().getNextEntityId(EntityIdType.WEAPON));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getEntityTypeId() {
|
public int getEntityTypeId() {
|
||||||
|
@ -2,10 +2,15 @@ package emu.grasscutter.game.entity;
|
|||||||
|
|
||||||
import emu.grasscutter.data.binout.config.ConfigEntityGadget;
|
import emu.grasscutter.data.binout.config.ConfigEntityGadget;
|
||||||
import emu.grasscutter.game.props.FightProperty;
|
import emu.grasscutter.game.props.FightProperty;
|
||||||
|
import emu.grasscutter.game.quest.enums.QuestContent;
|
||||||
import emu.grasscutter.game.world.Scene;
|
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 emu.grasscutter.utils.Position;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import static emu.grasscutter.scripts.constants.EventType.EVENT_SPECIFIC_GADGET_HP_CHANGE;
|
||||||
|
|
||||||
public abstract class EntityBaseGadget extends GameEntity {
|
public abstract class EntityBaseGadget extends GameEntity {
|
||||||
@Getter(onMethod_ = @Override)
|
@Getter(onMethod_ = @Override)
|
||||||
protected final Position position;
|
protected final Position position;
|
||||||
@ -33,6 +38,29 @@ public abstract class EntityBaseGadget extends GameEntity {
|
|||||||
@Override
|
@Override
|
||||||
public void onDeath(int killerId) {
|
public void onDeath(int killerId) {
|
||||||
super.onDeath(killerId); // Invoke super class's onDeath() method.
|
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) {
|
protected void fillFightProps(ConfigEntityGadget configGadget) {
|
||||||
|
@ -157,6 +157,10 @@ public abstract class GameEntity {
|
|||||||
this.damage(amount, 0, ElementType.None);
|
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) {
|
public void damage(float amount, int killerId, ElementType attackType) {
|
||||||
// Check if the entity has properties.
|
// Check if the entity has properties.
|
||||||
if (this.getFightProperties() == null || !hasFightProperty(FightProperty.FIGHT_PROP_CUR_HP)) {
|
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.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, -(event.getDamage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.lastAttackType = attackType;
|
||||||
|
|
||||||
// Check if dead
|
// Check if dead
|
||||||
boolean isDead = false;
|
boolean isDead = false;
|
||||||
if (this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) <= 0f) {
|
if (this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) <= 0f) {
|
||||||
this.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0f);
|
this.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0f);
|
||||||
isDead = true;
|
isDead = true;
|
||||||
}
|
}
|
||||||
|
this.runLuaCallbacks(event);
|
||||||
|
|
||||||
// Packets
|
// Packets
|
||||||
this.getScene()
|
this.getScene()
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package emu.grasscutter.game.entity.gadget;
|
package emu.grasscutter.game.entity.gadget;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.data.GameData;
|
import emu.grasscutter.data.GameData;
|
||||||
|
import emu.grasscutter.data.excels.GatherData;
|
||||||
import emu.grasscutter.data.excels.ItemData;
|
import emu.grasscutter.data.excels.ItemData;
|
||||||
import emu.grasscutter.game.entity.EntityGadget;
|
import emu.grasscutter.game.entity.EntityGadget;
|
||||||
import emu.grasscutter.game.entity.EntityItem;
|
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.server.packet.send.PacketGadgetInteractRsp;
|
||||||
import emu.grasscutter.utils.Utils;
|
import emu.grasscutter.utils.Utils;
|
||||||
|
|
||||||
public class GadgetGatherObject extends GadgetContent {
|
public final class GadgetGatherObject extends GadgetContent {
|
||||||
private int itemId;
|
private int itemId;
|
||||||
private boolean isForbidGuest;
|
private boolean isForbidGuest;
|
||||||
|
|
||||||
public GadgetGatherObject(EntityGadget gadget) {
|
public GadgetGatherObject(EntityGadget gadget) {
|
||||||
super(gadget);
|
super(gadget);
|
||||||
|
|
||||||
|
// overwrites the default spawn handling
|
||||||
if (gadget.getSpawnEntry() != null) {
|
if (gadget.getSpawnEntry() != null) {
|
||||||
this.itemId = gadget.getSpawnEntry().getGatherItemId();
|
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.GameData;
|
||||||
import emu.grasscutter.data.excels.GatherData;
|
import emu.grasscutter.data.excels.GatherData;
|
||||||
import emu.grasscutter.game.entity.EntityGadget;
|
import emu.grasscutter.game.entity.EntityGadget;
|
||||||
import emu.grasscutter.game.entity.EntityItem;
|
|
||||||
import emu.grasscutter.game.inventory.GameItem;
|
import emu.grasscutter.game.inventory.GameItem;
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.game.props.ActionReason;
|
import emu.grasscutter.game.props.ActionReason;
|
||||||
import emu.grasscutter.game.world.Scene;
|
|
||||||
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
||||||
import emu.grasscutter.net.proto.GatherGadgetInfoOuterClass.GatherGadgetInfo;
|
import emu.grasscutter.net.proto.GatherGadgetInfoOuterClass.GatherGadgetInfo;
|
||||||
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
||||||
import emu.grasscutter.utils.Utils;
|
|
||||||
|
|
||||||
public class GadgetGatherPoint extends GadgetContent {
|
/** Spawner for the gather objects */
|
||||||
private final int itemId;
|
public final class GadgetGatherPoint extends GadgetContent {
|
||||||
private boolean isForbidGuest;
|
private final GatherData gatherData;
|
||||||
|
private final EntityGadget gatherObjectChild;
|
||||||
|
|
||||||
public GadgetGatherPoint(EntityGadget gadget) {
|
public GadgetGatherPoint(EntityGadget gadget) {
|
||||||
super(gadget);
|
super(gadget);
|
||||||
|
|
||||||
if (gadget.getSpawnEntry() != null) {
|
this.gatherData = GameData.getGatherDataMap().get(gadget.getPointType());
|
||||||
this.itemId = gadget.getSpawnEntry().getGatherItemId();
|
|
||||||
} else {
|
var scene = gadget.getScene();
|
||||||
GatherData gatherData = GameData.getGatherDataMap().get(gadget.getPointType());
|
gatherObjectChild = new EntityGadget(scene, gatherData.getGadgetId(), gadget.getBornPos());
|
||||||
this.itemId = gatherData.getItemId();
|
|
||||||
this.isForbidGuest = gatherData.isForbidGuest();
|
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() {
|
public int getItemId() {
|
||||||
return this.itemId;
|
return this.gatherData.getItemId();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isForbidGuest() {
|
public boolean isForbidGuest() {
|
||||||
return isForbidGuest;
|
return this.gatherData.isForbidGuest();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean onInteract(Player player, GadgetInteractReq req) {
|
public boolean onInteract(Player player, GadgetInteractReq req) {
|
||||||
@ -46,6 +53,7 @@ public class GadgetGatherPoint extends GadgetContent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
|
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
|
||||||
|
// todo does official use this for the spawners?
|
||||||
GatherGadgetInfo gatherGadgetInfo =
|
GatherGadgetInfo gatherGadgetInfo =
|
||||||
GatherGadgetInfo.newBuilder()
|
GatherGadgetInfo.newBuilder()
|
||||||
.setItemId(this.getItemId())
|
.setItemId(this.getItemId())
|
||||||
@ -54,30 +62,4 @@ public class GadgetGatherPoint extends GadgetContent {
|
|||||||
|
|
||||||
gadgetInfo.setGatherGadget(gatherGadgetInfo);
|
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 it.unimi.dsi.fastutil.ints.IntSet;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
public class GadgetWorktop extends GadgetContent {
|
public final class GadgetWorktop extends GadgetContent {
|
||||||
private IntSet worktopOptions;
|
private IntSet worktopOptions;
|
||||||
private WorktopWorktopOptionHandler handler;
|
private WorktopWorktopOptionHandler handler;
|
||||||
|
|
||||||
@ -20,6 +20,9 @@ public class GadgetWorktop extends GadgetContent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public IntSet getWorktopOptions() {
|
public IntSet getWorktopOptions() {
|
||||||
|
if (this.worktopOptions == null) {
|
||||||
|
this.worktopOptions = new IntOpenHashSet();
|
||||||
|
}
|
||||||
return worktopOptions;
|
return worktopOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +44,12 @@ public class BossChestInteractHandler implements ChestInteractHandler {
|
|||||||
var reward = worldDataManager.getRewardByBossId(monster.monster_id);
|
var reward = worldDataManager.getRewardByBossId(monster.monster_id);
|
||||||
|
|
||||||
if (reward == null) {
|
if (reward == null) {
|
||||||
|
var dungeonManager = player.getScene().getDungeonManager();
|
||||||
|
|
||||||
|
if (dungeonManager != null) {
|
||||||
|
return dungeonManager.getStatueDrops(
|
||||||
|
player, useCondensedResin, chest.getGadget().getGroupId());
|
||||||
|
}
|
||||||
Grasscutter.getLogger()
|
Grasscutter.getLogger()
|
||||||
.warn("Could not found the reward of boss monster {}", monster.monster_id);
|
.warn("Could not found the reward of boss monster {}", monster.monster_id);
|
||||||
return false;
|
return false;
|
||||||
|
@ -2,6 +2,8 @@ package emu.grasscutter.game.inventory;
|
|||||||
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
@ -27,7 +29,7 @@ public enum EquipType {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private final int value;
|
@Getter private final int value;
|
||||||
|
|
||||||
EquipType(int value) {
|
EquipType(int value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
@ -40,8 +42,4 @@ public enum EquipType {
|
|||||||
public static EquipType getTypeByName(String name) {
|
public static EquipType getTypeByName(String name) {
|
||||||
return stringMap.getOrDefault(name, EQUIP_NONE);
|
return stringMap.getOrDefault(name, EQUIP_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -57,6 +57,7 @@ public class GameItem {
|
|||||||
|
|
||||||
@Getter @Setter private int equipCharacter;
|
@Getter @Setter private int equipCharacter;
|
||||||
@Transient @Getter @Setter private int weaponEntityId;
|
@Transient @Getter @Setter private int weaponEntityId;
|
||||||
|
@Transient @Getter private boolean newItem = false;
|
||||||
|
|
||||||
public GameItem() {
|
public GameItem() {
|
||||||
// Morphia only
|
// 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) {
|
public static int getMinPromoteLevel(int level) {
|
||||||
if (level > 80) {
|
if (level > 80) {
|
||||||
return 6;
|
return 6;
|
||||||
@ -133,23 +158,6 @@ public class GameItem {
|
|||||||
return 0;
|
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() {
|
public int getEquipSlot() {
|
||||||
return this.getItemData().getEquipType().getValue();
|
return this.getItemData().getEquipType().getValue();
|
||||||
}
|
}
|
||||||
@ -353,7 +361,7 @@ public class GameItem {
|
|||||||
return ItemHint.newBuilder()
|
return ItemHint.newBuilder()
|
||||||
.setItemId(getItemId())
|
.setItemId(getItemId())
|
||||||
.setCount(getCount())
|
.setCount(getCount())
|
||||||
.setIsNew(false)
|
.setIsNew(this.isNewItem())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,11 +15,9 @@ import emu.grasscutter.game.props.ActionReason;
|
|||||||
import emu.grasscutter.game.props.ItemUseAction.UseItemParams;
|
import emu.grasscutter.game.props.ItemUseAction.UseItemParams;
|
||||||
import emu.grasscutter.game.props.PlayerProperty;
|
import emu.grasscutter.game.props.PlayerProperty;
|
||||||
import emu.grasscutter.game.props.WatcherTriggerType;
|
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||||
|
import emu.grasscutter.game.quest.enums.QuestContent;
|
||||||
import emu.grasscutter.net.proto.ItemParamOuterClass.ItemParam;
|
import emu.grasscutter.net.proto.ItemParamOuterClass.ItemParam;
|
||||||
import emu.grasscutter.server.packet.send.PacketAvatarEquipChangeNotify;
|
import emu.grasscutter.server.packet.send.*;
|
||||||
import emu.grasscutter.server.packet.send.PacketItemAddHintNotify;
|
|
||||||
import emu.grasscutter.server.packet.send.PacketStoreItemChangeNotify;
|
|
||||||
import emu.grasscutter.server.packet.send.PacketStoreItemDelNotify;
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||||
@ -96,12 +94,7 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
|
|||||||
GameItem result = putItem(item);
|
GameItem result = putItem(item);
|
||||||
|
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
getPlayer()
|
this.triggerAddItemEvents(result);
|
||||||
.getBattlePassManager()
|
|
||||||
.triggerMission(
|
|
||||||
WatcherTriggerType.TRIGGER_OBTAIN_MATERIAL_NUM,
|
|
||||||
result.getItemId(),
|
|
||||||
result.getCount());
|
|
||||||
getPlayer().sendPacket(new PacketStoreItemChangeNotify(result));
|
getPlayer().sendPacket(new PacketStoreItemChangeNotify(result));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -110,18 +103,19 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean addItem(GameItem item, ActionReason reason) {
|
public boolean addItem(GameItem item, ActionReason reason) {
|
||||||
boolean result = addItem(item);
|
return addItem(item, reason, false);
|
||||||
|
|
||||||
if (result && reason != null) {
|
|
||||||
getPlayer().sendPacket(new PacketItemAddHintNotify(item, reason));
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean addItem(GameItem item, ActionReason reason, boolean forceNotify) {
|
public boolean addItem(GameItem item, ActionReason reason, boolean forceNotify) {
|
||||||
boolean result = addItem(item);
|
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)) {
|
if (reason != null && (forceNotify || result)) {
|
||||||
getPlayer().sendPacket(new PacketItemAddHintNotify(item, reason));
|
getPlayer().sendPacket(new PacketItemAddHintNotify(item, reason));
|
||||||
}
|
}
|
||||||
@ -155,12 +149,7 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
getPlayer()
|
this.triggerAddItemEvents(result);
|
||||||
.getBattlePassManager()
|
|
||||||
.triggerMission(
|
|
||||||
WatcherTriggerType.TRIGGER_OBTAIN_MATERIAL_NUM,
|
|
||||||
result.getItemId(),
|
|
||||||
result.getCount());
|
|
||||||
changedItems.add(result);
|
changedItems.add(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -168,11 +157,30 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (reason != null) {
|
if (reason != null) {
|
||||||
getPlayer().sendPacket(new PacketItemAddHintNotify(changedItems, reason));
|
getPlayer().sendPacket(new PacketItemAddHintNotify(items, reason));
|
||||||
}
|
}
|
||||||
getPlayer().sendPacket(new PacketStoreItemChangeNotify(changedItems));
|
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) {
|
public void addItemParams(Collection<ItemParam> items) {
|
||||||
addItems(
|
addItems(
|
||||||
items.stream().map(param -> new GameItem(param.getItemId(), param.getCount())).toList(),
|
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();
|
var data = item.getItemData();
|
||||||
if (data == null) return null;
|
if (data == null) return null;
|
||||||
|
|
||||||
|
this.player.getProgressManager().addItemObtainedHistory(item.getItemId(), item.getCount());
|
||||||
|
|
||||||
if (data.isUseOnGain()) {
|
if (data.isUseOnGain()) {
|
||||||
var params = new UseItemParams(this.player, data.getUseTarget());
|
var params = new UseItemParams(this.player, data.getUseTarget());
|
||||||
params.usedItemId = data.getId();
|
params.usedItemId = data.getId();
|
||||||
@ -267,6 +277,7 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
|
|||||||
this.player.getCodex().checkAddedItem(item);
|
this.player.getCodex().checkAddedItem(item);
|
||||||
// Set owner and guid FIRST!
|
// Set owner and guid FIRST!
|
||||||
item.setOwner(this.player);
|
item.setOwner(this.player);
|
||||||
|
item.checkIsNew(this);
|
||||||
// Put in item store
|
// Put in item store
|
||||||
getItems().put(item.getGuid(), item);
|
getItems().put(item.getGuid(), item);
|
||||||
if (tab != null) {
|
if (tab != null) {
|
||||||
@ -467,9 +478,7 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
|
|||||||
|
|
||||||
// Battle pass trigger
|
// Battle pass trigger
|
||||||
int removeCount = Math.min(count, item.getCount());
|
int removeCount = Math.min(count, item.getCount());
|
||||||
getPlayer()
|
this.triggerRemItemEvents(item, removeCount);
|
||||||
.getBattlePassManager()
|
|
||||||
.triggerMission(WatcherTriggerType.TRIGGER_COST_MATERIAL, item.getItemId(), removeCount);
|
|
||||||
|
|
||||||
// Update in db
|
// Update in db
|
||||||
item.save();
|
item.save();
|
||||||
|
@ -2,6 +2,8 @@ package emu.grasscutter.game.inventory;
|
|||||||
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
@ -27,7 +29,7 @@ public enum ItemQuality {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private final int value;
|
@Getter private final int value;
|
||||||
|
|
||||||
ItemQuality(int value) {
|
ItemQuality(int value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
@ -40,8 +42,4 @@ public enum ItemQuality {
|
|||||||
public static ItemQuality getTypeByName(String name) {
|
public static ItemQuality getTypeByName(String name) {
|
||||||
return stringMap.getOrDefault(name, QUALITY_NONE);
|
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.Int2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
@ -27,7 +29,7 @@ public enum ItemType {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private final int value;
|
@Getter private final int value;
|
||||||
|
|
||||||
ItemType(int value) {
|
ItemType(int value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
@ -40,8 +42,4 @@ public enum ItemType {
|
|||||||
public static ItemType getTypeByName(String name) {
|
public static ItemType getTypeByName(String name) {
|
||||||
return stringMap.getOrDefault(name, ITEM_NONE);
|
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.Int2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
@ -61,7 +63,7 @@ public enum MaterialType {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private final int value;
|
@Getter private final int value;
|
||||||
|
|
||||||
MaterialType(int value) {
|
MaterialType(int value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
@ -74,8 +76,4 @@ public enum MaterialType {
|
|||||||
public static MaterialType getTypeByName(String name) {
|
public static MaterialType getTypeByName(String name) {
|
||||||
return stringMap.getOrDefault(name, MATERIAL_NONE);
|
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> blossomActivities = new ArrayList<>();
|
||||||
private final List<BlossomActivity> activeChests = new ArrayList<>();
|
private final List<BlossomActivity> activeChests = new ArrayList<>();
|
||||||
private final List<EntityGadget> createdEntity = new ArrayList<>();
|
private final List<EntityGadget> createdEntity = new ArrayList<>();
|
||||||
|
|
||||||
private final List<SpawnDataEntry> blossomConsumed = new ArrayList<>();
|
private final List<SpawnDataEntry> blossomConsumed = new ArrayList<>();
|
||||||
|
|
||||||
public BlossomManager(Scene scene) {
|
public BlossomManager(Scene scene) {
|
||||||
this.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() {
|
public void onTick() {
|
||||||
synchronized (blossomActivities) {
|
synchronized (blossomActivities) {
|
||||||
var it = blossomActivities.iterator();
|
var it = blossomActivities.iterator();
|
||||||
@ -201,6 +163,35 @@ public class BlossomManager {
|
|||||||
return scene.getWorld().getWorldLevel();
|
return scene.getWorld().getWorldLevel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Integer getPreviewReward(BlossomType type, int worldLevel) {
|
||||||
|
// TODO: blossoms should be based on their city
|
||||||
|
if (type == null) {
|
||||||
|
Grasscutter.getLogger().error("Illegal blossom type {}", type);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int blossomChestId = type.getBlossomChestId();
|
||||||
|
var dataMap = GameData.getBlossomRefreshExcelConfigDataMap();
|
||||||
|
for (var data : dataMap.values()) {
|
||||||
|
if (blossomChestId == data.getBlossomChestId()) {
|
||||||
|
var dropVecList = data.getDropVec();
|
||||||
|
if (worldLevel > dropVecList.length) {
|
||||||
|
Grasscutter.getLogger().error("Illegal world level {}", worldLevel);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return dropVecList[worldLevel].getPreviewReward();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Grasscutter.getLogger().error("Cannot find blossom type {}", type);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static RewardPreviewData getRewardList(BlossomType type, int worldLevel) {
|
||||||
|
Integer previewReward = getPreviewReward(type, worldLevel);
|
||||||
|
if (previewReward == null) return null;
|
||||||
|
return GameData.getRewardPreviewDataMap().get((int) previewReward);
|
||||||
|
}
|
||||||
|
|
||||||
public List<GameItem> onReward(Player player, EntityGadget chest, boolean useCondensedResin) {
|
public List<GameItem> onReward(Player player, EntityGadget chest, boolean useCondensedResin) {
|
||||||
var resinManager = player.getResinManager();
|
var resinManager = player.getResinManager();
|
||||||
synchronized (activeChests) {
|
synchronized (activeChests) {
|
||||||
@ -240,4 +231,14 @@ public class BlossomManager {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IntList getRandomMonstersID(int difficulty, int count) {
|
||||||
|
IntList result = new IntArrayList();
|
||||||
|
List<Integer> monsters =
|
||||||
|
GameDepot.getBlossomConfig().getMonsterIdsPerDifficulty().get(difficulty);
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
result.add((int) monsters.get(Utils.randomRange(0, monsters.size() - 1)));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ public class CookingCompoundManager extends BasePlayerManager {
|
|||||||
if (compound.isDefaultUnlocked()) {
|
if (compound.isDefaultUnlocked()) {
|
||||||
defaultUnlockedCompounds.add(id);
|
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
|
// TODO:Because we haven't implemented fishing feature,unlock all compounds related to
|
||||||
// fish.Besides,it should be bound to player rather than manager.
|
// 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) {
|
public void setEnergyUsage(boolean energyUsage) {
|
||||||
this.energyUsage = energyUsage;
|
this.energyUsage = energyUsage;
|
||||||
if (!energyUsage) { // Refill team energy if usage is disabled
|
if (!energyUsage) { // Refill team energy if usage is disabled
|
||||||
for (EntityAvatar entityAvatar : this.player.getTeamManager().getActiveTeam()) {
|
this.refillTeamEnergy(PropChangeReason.PROP_CHANGE_REASON_GM, true);
|
||||||
entityAvatar.addEnergy(1000, PropChangeReason.PROP_CHANGE_REASON_GM, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,6 +141,7 @@ public class StaminaManager extends BasePlayerManager {
|
|||||||
put(242301, 0.8f);
|
put(242301, 0.8f);
|
||||||
put(542301, 0.8f);
|
put(542301, 0.8f);
|
||||||
}};
|
}};
|
||||||
|
|
||||||
private final Logger logger = Grasscutter.getLogger();
|
private final Logger logger = Grasscutter.getLogger();
|
||||||
private final HashMap<String, BeforeUpdateStaminaListener> beforeUpdateStaminaListeners = new HashMap<>();
|
private final HashMap<String, BeforeUpdateStaminaListener> beforeUpdateStaminaListeners = new HashMap<>();
|
||||||
private final HashMap<String, AfterUpdateStaminaListener> afterUpdateStaminaListeners = new HashMap<>();
|
private final HashMap<String, AfterUpdateStaminaListener> afterUpdateStaminaListeners = new HashMap<>();
|
||||||
@ -414,13 +415,7 @@ public class StaminaManager extends BasePlayerManager {
|
|||||||
// Internal handler
|
// Internal handler
|
||||||
|
|
||||||
private void handleImmediateStamina(GameSession session, @NotNull MotionState motionState) {
|
private void handleImmediateStamina(GameSession session, @NotNull MotionState motionState) {
|
||||||
if (currentState == motionState) {
|
if (currentState == motionState) return;
|
||||||
if (motionState.equals(MotionState.MOTION_STATE_CLIMB_JUMP)) {
|
|
||||||
updateStaminaRelative(session, new Consumption(ConsumptionType.CLIMB_JUMP), true);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (motionState) {
|
switch (motionState) {
|
||||||
case MOTION_STATE_CLIMB ->
|
case MOTION_STATE_CLIMB ->
|
||||||
updateStaminaRelative(session, new Consumption(ConsumptionType.CLIMB_START), true);
|
updateStaminaRelative(session, new Consumption(ConsumptionType.CLIMB_START), true);
|
||||||
@ -440,6 +435,73 @@ public class StaminaManager extends BasePlayerManager {
|
|||||||
updateStaminaRelative(session, consumption, true);
|
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() {
|
private void handleDrowning() {
|
||||||
// TODO: fix drowning waverider entity
|
// TODO: fix drowning waverider entity
|
||||||
int stamina = getCurrentCharacterStamina();
|
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) {
|
private Consumption getFightConsumption(int skillCasting) {
|
||||||
// Talent moving
|
// Talent moving
|
||||||
if (TalentMovements.contains(skillCasting)) {
|
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() {
|
private Consumption getClimbConsumption() {
|
||||||
Consumption consumption = new Consumption();
|
Consumption consumption = new Consumption();
|
||||||
if (currentState == MotionState.MOTION_STATE_CLIMB && isPlayerMoving()) {
|
if (currentState == MotionState.MOTION_STATE_CLIMB && isPlayerMoving()) {
|
||||||
@ -552,6 +614,8 @@ public class StaminaManager extends BasePlayerManager {
|
|||||||
return new Consumption();
|
return new Consumption();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reduction getter
|
||||||
|
|
||||||
private float getTalentCostReductionFactor(HashMap<Integer, Float> talentReductionMap) {
|
private float getTalentCostReductionFactor(HashMap<Integer, Float> talentReductionMap) {
|
||||||
// All known talents reductions are not stackable
|
// All known talents reductions are not stackable
|
||||||
float reduction = 1;
|
float reduction = 1;
|
||||||
@ -568,8 +632,6 @@ public class StaminaManager extends BasePlayerManager {
|
|||||||
return reduction;
|
return reduction;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reduction getter
|
|
||||||
|
|
||||||
private float getFoodCostReductionFactor(HashMap<Integer, Float> foodReductionMap) {
|
private float getFoodCostReductionFactor(HashMap<Integer, Float> foodReductionMap) {
|
||||||
// All known food reductions are not stackable
|
// All known food reductions are not stackable
|
||||||
// TODO: Check consumed food (buff?) and return proper factor
|
// TODO: Check consumed food (buff?) and return proper factor
|
||||||
@ -633,76 +695,11 @@ public class StaminaManager extends BasePlayerManager {
|
|||||||
private Consumption getSwordCost(int skillId) {
|
private Consumption getSwordCost(int skillId) {
|
||||||
Consumption consumption = new Consumption(ConsumptionType.FIGHT, -2000);
|
Consumption consumption = new Consumption(ConsumptionType.FIGHT, -2000);
|
||||||
// Character specific handling
|
// Character specific handling
|
||||||
if (skillId == 10421) {
|
switch (skillId) {
|
||||||
|
case 10421:
|
||||||
consumption.amount = -2500;
|
consumption.amount = -2500;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return consumption;
|
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;
|
||||||
import emu.grasscutter.data.excels.OpenStateData.OpenStateCondType;
|
import emu.grasscutter.data.excels.OpenStateData.OpenStateCondType;
|
||||||
import emu.grasscutter.game.props.ActionReason;
|
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.QuestCond;
|
||||||
import emu.grasscutter.game.quest.enums.QuestContent;
|
import emu.grasscutter.game.quest.enums.QuestContent;
|
||||||
import emu.grasscutter.game.quest.enums.QuestState;
|
import emu.grasscutter.game.quest.enums.QuestState;
|
||||||
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
|
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
|
||||||
|
import emu.grasscutter.scripts.data.ScriptArgs;
|
||||||
import emu.grasscutter.server.packet.send.*;
|
import emu.grasscutter.server.packet.send.*;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static emu.grasscutter.scripts.constants.EventType.EVENT_UNLOCK_TRANS_POINT;
|
||||||
|
|
||||||
// @Entity
|
// @Entity
|
||||||
public final class PlayerProgressManager extends BasePlayerDataManager {
|
public final class PlayerProgressManager extends BasePlayerDataManager {
|
||||||
/******************************************************************************************************************
|
/******************************************************************************************************************
|
||||||
@ -33,22 +37,25 @@ public final class PlayerProgressManager extends BasePlayerDataManager {
|
|||||||
GameData.getOpenStateList().stream()
|
GameData.getOpenStateList().stream()
|
||||||
.filter(
|
.filter(
|
||||||
s ->
|
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.
|
// All states whose unlock we don't handle correctly yet.
|
||||||
|| (s.getCond().stream()
|
|| (s.getCond().stream()
|
||||||
.filter(
|
.anyMatch(
|
||||||
c ->
|
c ->
|
||||||
c.getCondType()
|
c.getCondType() == OpenStateCondType.OPEN_STATE_OFFERING_LEVEL
|
||||||
== OpenStateCondType.OPEN_STATE_COND_PLAYER_LEVEL)
|
|| c.getCondType()
|
||||||
.count()
|
== OpenStateCondType.OPEN_STATE_CITY_REPUTATION_LEVEL))
|
||||||
== 0)
|
|
||||||
// Always unlock OPEN_STATE_PAIMON, otherwise the player will not have a
|
// Always unlock OPEN_STATE_PAIMON, otherwise the player will not have a
|
||||||
// working chat.
|
// working chat.
|
||||||
|| s.getId() == 1)
|
|| s.getId() == 1)
|
||||||
.filter(
|
.filter(
|
||||||
s ->
|
s ->
|
||||||
!BLACKLIST_OPEN_STATES.contains(s.getId())) // Filter out states in the blacklist.
|
!BLACKLIST_OPEN_STATES.contains(s.getId())) // Filter out states in the blacklist.
|
||||||
.map(s -> s.getId())
|
.map(OpenStateData::getId)
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
public PlayerProgressManager(Player player) {
|
public PlayerProgressManager(Player player) {
|
||||||
@ -88,6 +95,10 @@ public final class PlayerProgressManager extends BasePlayerDataManager {
|
|||||||
if (value != previousValue) {
|
if (value != previousValue) {
|
||||||
this.player.getOpenStates().put(openState, value);
|
this.player.getOpenStates().put(openState, value);
|
||||||
|
|
||||||
|
this.player
|
||||||
|
.getQuestManager()
|
||||||
|
.queueEvent(QuestCond.QUEST_COND_OPEN_STATE_EQUAL, openState, value);
|
||||||
|
|
||||||
if (sendNotify) {
|
if (sendNotify) {
|
||||||
player.getSession().send(new PacketOpenStateChangeNotify(openState, value));
|
player.getSession().send(new PacketOpenStateChangeNotify(openState, value));
|
||||||
}
|
}
|
||||||
@ -104,19 +115,31 @@ public final class PlayerProgressManager extends BasePlayerDataManager {
|
|||||||
private boolean areConditionsMet(OpenStateData openState) {
|
private boolean areConditionsMet(OpenStateData openState) {
|
||||||
// Check all conditions and test if at least one of them is violated.
|
// Check all conditions and test if at least one of them is violated.
|
||||||
for (var condition : openState.getCond()) {
|
for (var condition : openState.getCond()) {
|
||||||
|
switch (condition.getCondType()) {
|
||||||
// For level conditions, check if the player has reached the necessary level.
|
// 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()) {
|
if (this.player.getLevel() < condition.getParam()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (condition.getCondType() == OpenStateCondType.OPEN_STATE_COND_QUEST) {
|
}
|
||||||
// ToDo: Implement.
|
case OPEN_STATE_COND_QUEST -> {
|
||||||
} else if (condition.getCondType() == OpenStateCondType.OPEN_STATE_COND_PARENT_QUEST) {
|
// check sub quest id for quest finished met requirements
|
||||||
// ToDo: Implement.
|
var quest = this.player.getQuestManager().getQuestById(condition.getParam());
|
||||||
} else if (condition.getCondType() == OpenStateCondType.OPEN_STATE_OFFERING_LEVEL) {
|
if (quest == null || quest.getState() != QuestState.QUEST_STATE_FINISHED) {
|
||||||
// ToDo: Implement.
|
return false;
|
||||||
} else if (condition.getCondType() == OpenStateCondType.OPEN_STATE_CITY_REPUTATION_LEVEL) {
|
}
|
||||||
|
}
|
||||||
|
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.
|
// ToDo: Implement.
|
||||||
|
case OPEN_STATE_OFFERING_LEVEL, OPEN_STATE_CITY_REPUTATION_LEVEL -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,6 +252,10 @@ public final class PlayerProgressManager extends BasePlayerDataManager {
|
|||||||
this.player
|
this.player
|
||||||
.getQuestManager()
|
.getQuestManager()
|
||||||
.queueEvent(QuestContent.QUEST_CONTENT_UNLOCK_TRANS_POINT, sceneId, pointId);
|
.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.
|
// Send packet.
|
||||||
this.player.sendPacket(new PacketScenePointUnlockNotify(sceneId, pointId));
|
this.player.sendPacket(new PacketScenePointUnlockNotify(sceneId, pointId));
|
||||||
|
@ -68,6 +68,8 @@ public final class TeamManager extends BasePlayerDataManager {
|
|||||||
this.gadgets = new HashSet<>();
|
this.gadgets = new HashSet<>();
|
||||||
this.teamResonances = new IntOpenHashSet();
|
this.teamResonances = new IntOpenHashSet();
|
||||||
this.teamResonancesConfig = new IntOpenHashSet();
|
this.teamResonancesConfig = new IntOpenHashSet();
|
||||||
|
this.trialAvatars = new HashMap<>();
|
||||||
|
this.trialAvatarTeam = new TeamInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
public TeamManager(Player player) {
|
public TeamManager(Player player) {
|
||||||
|
@ -14,12 +14,6 @@ public class ItemUseUnlockHomeModule extends ItemUseInt {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean useItem(UseItemParams params) {
|
public boolean useItem(UseItemParams params) {
|
||||||
return true;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean postUseItem(UseItemParams params) {
|
|
||||||
params.player.addRealmList(this.i);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import emu.grasscutter.data.excels.*;
|
|||||||
import emu.grasscutter.data.excels.codex.CodexAnimalData;
|
import emu.grasscutter.data.excels.codex.CodexAnimalData;
|
||||||
import emu.grasscutter.data.excels.monster.MonsterData;
|
import emu.grasscutter.data.excels.monster.MonsterData;
|
||||||
import emu.grasscutter.data.excels.world.WorldLevelData;
|
import emu.grasscutter.data.excels.world.WorldLevelData;
|
||||||
|
import emu.grasscutter.data.server.Grid;
|
||||||
import emu.grasscutter.game.avatar.Avatar;
|
import emu.grasscutter.game.avatar.Avatar;
|
||||||
import emu.grasscutter.game.dungeons.DungeonManager;
|
import emu.grasscutter.game.dungeons.DungeonManager;
|
||||||
import emu.grasscutter.game.dungeons.DungeonSettleListener;
|
import emu.grasscutter.game.dungeons.DungeonSettleListener;
|
||||||
@ -46,6 +47,8 @@ import lombok.Getter;
|
|||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
public final class Scene {
|
public final class Scene {
|
||||||
@Getter private final World world;
|
@Getter private final World world;
|
||||||
@Getter private final SceneData sceneData;
|
@Getter private final SceneData sceneData;
|
||||||
@ -126,6 +129,10 @@ public final class Scene {
|
|||||||
.orElse(null);
|
.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.
|
* 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
|
// Deregister scene if not in use
|
||||||
if (this.getPlayerCount() <= 0 && !this.dontDestroyWhenEmpty) {
|
if (this.getPlayerCount() <= 0 && !this.dontDestroyWhenEmpty) {
|
||||||
|
this.getScriptManager().onDestroy();
|
||||||
this.getWorld().deregisterScene(this);
|
this.getWorld().deregisterScene(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.saveGroups();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupPlayerAvatars(Player player) {
|
private void setupPlayerAvatars(Player player) {
|
||||||
@ -229,9 +239,14 @@ public final class Scene {
|
|||||||
// Add new entities for player
|
// Add new entities for player
|
||||||
TeamInfo teamInfo = player.getTeamManager().getCurrentTeamInfo();
|
TeamInfo teamInfo = player.getTeamManager().getCurrentTeamInfo();
|
||||||
for (int avatarId : teamInfo.getAvatars()) {
|
for (int avatarId : teamInfo.getAvatars()) {
|
||||||
EntityAvatar entity =
|
Avatar avatar = player.getAvatars().getAvatarById(avatarId);
|
||||||
new EntityAvatar(player.getScene(), player.getAvatars().getAvatarById(avatarId));
|
if (avatar == null) {
|
||||||
player.getTeamManager().getActiveTeam().add(entity);
|
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
|
// 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) {
|
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) {
|
if (toRemove.size() > 0) {
|
||||||
this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, visionType));
|
this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, visionType));
|
||||||
}
|
}
|
||||||
@ -408,17 +428,21 @@ public final class Scene {
|
|||||||
|
|
||||||
// Death event
|
// Death event
|
||||||
target.onDeath(attackerId);
|
target.onDeath(attackerId);
|
||||||
|
this.triggerDungeonEvent(
|
||||||
|
DungeonPassConditionType.DUNGEON_COND_KILL_MONSTER_COUNT, ++killedMonsterCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onTick() {
|
public void onTick() {
|
||||||
// Disable ticking for the player's home world.
|
// Disable ticking for the player's home world.
|
||||||
if (this.getSceneType() == SceneType.SCENE_HOME_WORLD
|
if (this.getSceneType() == SceneType.SCENE_HOME_WORLD
|
||||||
|| this.getSceneType() == SceneType.SCENE_HOME_ROOM) {
|
|| this.getSceneType() == SceneType.SCENE_HOME_ROOM) {
|
||||||
|
this.finishLoading();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.getScriptManager().isInit()) {
|
if (this.getScriptManager().isInit()) {
|
||||||
this.checkBlocks();
|
// this.checkBlocks();
|
||||||
|
this.checkGroups();
|
||||||
} else {
|
} else {
|
||||||
// TEMPORARY
|
// TEMPORARY
|
||||||
this.checkSpawns();
|
this.checkSpawns();
|
||||||
@ -431,7 +455,10 @@ public final class Scene {
|
|||||||
challenge.onCheckTimeOut();
|
challenge.onCheckTimeOut();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.blossomManager.onTick();
|
var sceneTime = getSceneTimeSeconds();
|
||||||
|
getEntities().forEach((eid, e) -> e.onTick(sceneTime));
|
||||||
|
|
||||||
|
blossomManager.onTick();
|
||||||
|
|
||||||
checkNpcGroup();
|
checkNpcGroup();
|
||||||
|
|
||||||
@ -701,10 +728,17 @@ public final class Scene {
|
|||||||
Grasscutter.getConfig().server.game.loadEntitiesForPlayerRange);
|
Grasscutter.getConfig().server.game.loadEntitiesForPlayerRange);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean unloadBlockIfNotVisible(Collection<SceneBlock> visible, SceneBlock block) {
|
public Set<Integer> getPlayerActiveGroups(Player player) {
|
||||||
if (visible.contains(block)) return false;
|
// consider the borders' entities of blocks, so we check if contains by index
|
||||||
this.onUnloadBlock(block);
|
Position playerPosition = player.getPosition();
|
||||||
return true;
|
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) {
|
public synchronized boolean loadBlock(SceneBlock block) {
|
||||||
@ -715,63 +749,44 @@ public final class Scene {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void checkBlocks() {
|
public synchronized void checkGroups() {
|
||||||
Set<SceneBlock> visible =
|
Set<Integer> visible =
|
||||||
this.players.stream()
|
this.players.stream()
|
||||||
.map(this::getPlayerActiveBlocks)
|
.map(player -> this.getPlayerActiveGroups(player))
|
||||||
.flatMap(Collection::stream)
|
.flatMap(Collection::stream)
|
||||||
.collect(Collectors.toSet());
|
.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()
|
visible.stream()
|
||||||
.filter(block -> !this.loadBlock(block))
|
.filter(g -> this.loadedGroups.stream().filter(gr -> gr.id == g).count() == 0)
|
||||||
.forEach(
|
.map(
|
||||||
block -> {
|
g -> {
|
||||||
// dynamic load the groups for players in a loaded block
|
for (var b : scriptManager.getBlocks().values()) {
|
||||||
var toLoad =
|
loadBlock(b);
|
||||||
this.players.stream()
|
SceneGroup group = b.groups.getOrDefault(g, null);
|
||||||
.filter(p -> block.contains(p.getPosition()))
|
if (group != null && !group.dynamic_load) return group;
|
||||||
.map(p -> this.playerMeetGroups(p, block))
|
}
|
||||||
.flatMap(Collection::stream)
|
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.filter(Objects::nonNull)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
this.onLoadGroup(toLoad);
|
this.onLoadGroup(toLoad);
|
||||||
});
|
if (!toLoad.isEmpty()) this.onRegisterGroups();
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onLoadBlock(SceneBlock block, List<Player> players) {
|
public void onLoadBlock(SceneBlock block, List<Player> players) {
|
||||||
this.getScriptManager().loadBlockFromScript(block);
|
this.getScriptManager().loadBlockFromScript(block);
|
||||||
scriptManager.getLoadedGroupSetPerBlock().put(block.id, new HashSet<>());
|
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);
|
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)
|
if (group == null || getScriptManager().getGroupInstanceById(group_id) != null)
|
||||||
return -1; // Group not found or already instanced
|
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();
|
if (GameData.getGroupReplacements().containsKey(group_id)) onRegisterGroups();
|
||||||
|
|
||||||
@ -824,7 +839,6 @@ public final class Scene {
|
|||||||
|
|
||||||
KahnsSort.Graph graph = new KahnsSort.Graph(nodes, groupList);
|
KahnsSort.Graph graph = new KahnsSort.Graph(nodes, groupList);
|
||||||
List<Integer> dynamicGroupsOrdered = KahnsSort.doSort(graph);
|
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
|
// Now we can start unloading and loading groups :D
|
||||||
dynamicGroupsOrdered.forEach(
|
dynamicGroupsOrdered.forEach(
|
||||||
@ -929,38 +943,10 @@ public final class Scene {
|
|||||||
Grasscutter.getLogger().info("Scene {} loaded {} group(s)", this.getId(), groups.size());
|
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 =
|
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()
|
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();
|
.toList();
|
||||||
|
|
||||||
if (toRemove.size() > 0) {
|
if (toRemove.size() > 0) {
|
||||||
@ -969,15 +955,15 @@ public final class Scene {
|
|||||||
new PacketSceneEntityDisappearNotify(toRemove, VisionType.VISION_TYPE_REMOVE));
|
new PacketSceneEntityDisappearNotify(toRemove, VisionType.VISION_TYPE_REMOVE));
|
||||||
}
|
}
|
||||||
|
|
||||||
var group = block.groups.get(groupId);
|
var group = block.groups.get(group_id);
|
||||||
if (group.triggers != null) {
|
if (group.triggers != null) {
|
||||||
group.triggers.values().forEach(this.getScriptManager()::deregisterTrigger);
|
group.triggers.values().forEach(getScriptManager()::deregisterTrigger);
|
||||||
}
|
}
|
||||||
if (group.regions != null) {
|
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);
|
this.loadedGroups.remove(group);
|
||||||
|
|
||||||
if (this.scriptManager.getLoadedGroupSetPerBlock().get(block.id).isEmpty()) {
|
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);
|
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);
|
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) {
|
public void deregisterScene(Scene scene) {
|
||||||
|
scene.saveGroups();
|
||||||
this.getScenes().remove(scene.getId());
|
this.getScenes().remove(scene.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void save() {
|
||||||
|
this.getScenes().values().forEach(Scene::saveGroups);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean transferPlayerToScene(Player player, int sceneId, Position pos) {
|
public boolean transferPlayerToScene(Player player, int sceneId, Position pos) {
|
||||||
return this.transferPlayerToScene(player, sceneId, TeleportType.INTERNAL, null, pos);
|
return this.transferPlayerToScene(player, sceneId, TeleportType.INTERNAL, null, pos);
|
||||||
}
|
}
|
||||||
@ -299,6 +304,7 @@ public class World implements Iterable<Player> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Scene oldScene = null;
|
Scene oldScene = null;
|
||||||
|
|
||||||
if (player.getScene() != null) {
|
if (player.getScene() != null) {
|
||||||
oldScene = player.getScene();
|
oldScene = player.getScene();
|
||||||
|
|
||||||
@ -312,8 +318,13 @@ public class World implements Iterable<Player> {
|
|||||||
|
|
||||||
var newScene = this.getSceneById(teleportProperties.getSceneId());
|
var newScene = this.getSceneById(teleportProperties.getSceneId());
|
||||||
newScene.addPlayer(player);
|
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();
|
SceneConfig config = newScene.getScriptManager().getConfig();
|
||||||
if (teleportProperties.getTeleportTo() == null && config != null) {
|
if (teleportProperties.getTeleportTo() == null && config != null) {
|
||||||
if (config.born_pos != null) {
|
if (config.born_pos != null) {
|
||||||
|
Loading…
Reference in New Issue
Block a user