mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2026-02-06 10:06:51 +01:00
Continue merging quests (pt. 1)
Finished last at: `World.java`, line `player.setAvatarsAbilityForScene(newScene);`
This commit is contained in:
@@ -1,24 +1,26 @@
|
|||||||
package emu.grasscutter.data.excels;
|
package emu.grasscutter.data.excels;
|
||||||
|
|
||||||
import emu.grasscutter.data.GameResource;
|
import com.google.gson.annotations.SerializedName;
|
||||||
import emu.grasscutter.data.ResourceType;
|
import emu.grasscutter.data.GameResource;
|
||||||
import emu.grasscutter.data.common.ItemParamData;
|
import emu.grasscutter.data.ResourceType;
|
||||||
import java.util.List;
|
import emu.grasscutter.data.common.ItemParamData;
|
||||||
import lombok.Getter;
|
import java.util.List;
|
||||||
|
import lombok.Getter;
|
||||||
@ResourceType(
|
|
||||||
name = {"CompoundExcelConfigData.json"},
|
@ResourceType(
|
||||||
loadPriority = ResourceType.LoadPriority.LOW)
|
name = {"CompoundExcelConfigData.json"},
|
||||||
@Getter
|
loadPriority = ResourceType.LoadPriority.LOW)
|
||||||
public class CompoundData extends GameResource {
|
@Getter
|
||||||
@Getter(onMethod_ = @Override)
|
public class CompoundData extends GameResource {
|
||||||
private int id;
|
@Getter(onMethod_ = @Override)
|
||||||
|
private int id;
|
||||||
private int groupID;
|
|
||||||
private int rankLevel;
|
@SerializedName("groupID")
|
||||||
private boolean isDefaultUnlocked;
|
private int groupId;
|
||||||
private int costTime;
|
private int rankLevel;
|
||||||
private int queueSize;
|
private boolean isDefaultUnlocked;
|
||||||
private List<ItemParamData> inputVec;
|
private int costTime;
|
||||||
private List<ItemParamData> outputVec;
|
private int queueSize;
|
||||||
}
|
private List<ItemParamData> inputVec;
|
||||||
|
private List<ItemParamData> outputVec;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,178 +1,178 @@
|
|||||||
package emu.grasscutter.game.dungeons.challenge;
|
package emu.grasscutter.game.dungeons.challenge;
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.game.dungeons.challenge.trigger.ChallengeTrigger;
|
import emu.grasscutter.game.dungeons.challenge.trigger.ChallengeTrigger;
|
||||||
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
|
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
|
||||||
import emu.grasscutter.game.entity.EntityGadget;
|
import emu.grasscutter.game.entity.EntityGadget;
|
||||||
import emu.grasscutter.game.entity.EntityMonster;
|
import emu.grasscutter.game.entity.EntityMonster;
|
||||||
import emu.grasscutter.game.props.WatcherTriggerType;
|
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||||
import emu.grasscutter.game.world.Scene;
|
import emu.grasscutter.game.world.Scene;
|
||||||
import emu.grasscutter.scripts.constants.EventType;
|
import emu.grasscutter.scripts.constants.EventType;
|
||||||
import emu.grasscutter.scripts.data.SceneGroup;
|
import emu.grasscutter.scripts.data.SceneGroup;
|
||||||
import emu.grasscutter.scripts.data.SceneTrigger;
|
import emu.grasscutter.scripts.data.SceneTrigger;
|
||||||
import emu.grasscutter.scripts.data.ScriptArgs;
|
import emu.grasscutter.scripts.data.ScriptArgs;
|
||||||
import emu.grasscutter.server.packet.send.PacketDungeonChallengeBeginNotify;
|
import emu.grasscutter.server.packet.send.PacketDungeonChallengeBeginNotify;
|
||||||
import emu.grasscutter.server.packet.send.PacketDungeonChallengeFinishNotify;
|
import emu.grasscutter.server.packet.send.PacketDungeonChallengeFinishNotify;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
public class WorldChallenge {
|
public class WorldChallenge {
|
||||||
private final Scene scene;
|
private final Scene scene;
|
||||||
private final SceneGroup group;
|
private final SceneGroup group;
|
||||||
private final int challengeId;
|
private final int challengeId;
|
||||||
private final int challengeIndex;
|
private final int challengeIndex;
|
||||||
private final List<Integer> paramList;
|
private final List<Integer> paramList;
|
||||||
private final int timeLimit;
|
private final int timeLimit;
|
||||||
private final List<ChallengeTrigger> challengeTriggers;
|
private final List<ChallengeTrigger> challengeTriggers;
|
||||||
private final int goal;
|
private final int goal;
|
||||||
private final AtomicInteger score;
|
private final AtomicInteger score;
|
||||||
private boolean progress;
|
private boolean progress;
|
||||||
private boolean success;
|
private boolean success;
|
||||||
private long startedAt;
|
private long startedAt;
|
||||||
private int finishedTime;
|
private int finishedTime;
|
||||||
|
|
||||||
public WorldChallenge(
|
public WorldChallenge(
|
||||||
Scene scene,
|
Scene scene,
|
||||||
SceneGroup group,
|
SceneGroup group,
|
||||||
int challengeId,
|
int challengeId,
|
||||||
int challengeIndex,
|
int challengeIndex,
|
||||||
List<Integer> paramList,
|
List<Integer> paramList,
|
||||||
int timeLimit,
|
int timeLimit,
|
||||||
int goal,
|
int goal,
|
||||||
List<ChallengeTrigger> challengeTriggers) {
|
List<ChallengeTrigger> challengeTriggers) {
|
||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
this.group = group;
|
this.group = group;
|
||||||
this.challengeId = challengeId;
|
this.challengeId = challengeId;
|
||||||
this.challengeIndex = challengeIndex;
|
this.challengeIndex = challengeIndex;
|
||||||
this.paramList = paramList;
|
this.paramList = paramList;
|
||||||
this.timeLimit = timeLimit;
|
this.timeLimit = timeLimit;
|
||||||
this.challengeTriggers = challengeTriggers;
|
this.challengeTriggers = challengeTriggers;
|
||||||
this.goal = goal;
|
this.goal = goal;
|
||||||
this.score = new AtomicInteger(0);
|
this.score = new AtomicInteger(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean inProgress() {
|
public boolean inProgress() {
|
||||||
return this.progress;
|
return this.progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onCheckTimeOut() {
|
public void onCheckTimeOut() {
|
||||||
if (!inProgress()) {
|
if (!inProgress()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (timeLimit <= 0) {
|
if (timeLimit <= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
challengeTriggers.forEach(t -> t.onCheckTimeout(this));
|
challengeTriggers.forEach(t -> t.onCheckTimeout(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
if (inProgress()) {
|
if (inProgress()) {
|
||||||
Grasscutter.getLogger().info("Could not start a in progress challenge.");
|
Grasscutter.getLogger().info("Could not start a in progress challenge.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.progress = true;
|
this.progress = true;
|
||||||
this.startedAt = System.currentTimeMillis();
|
this.startedAt = System.currentTimeMillis();
|
||||||
getScene().broadcastPacket(new PacketDungeonChallengeBeginNotify(this));
|
getScene().broadcastPacket(new PacketDungeonChallengeBeginNotify(this));
|
||||||
challengeTriggers.forEach(t -> t.onBegin(this));
|
challengeTriggers.forEach(t -> t.onBegin(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void done() {
|
public void done() {
|
||||||
if (!this.inProgress()) return;
|
if (!this.inProgress()) return;
|
||||||
this.finish(true);
|
this.finish(true);
|
||||||
|
|
||||||
var scene = this.getScene();
|
var scene = this.getScene();
|
||||||
var dungeonManager = scene.getDungeonManager();
|
var dungeonManager = scene.getDungeonManager();
|
||||||
if (dungeonManager != null && dungeonManager.getDungeonData() != null) {
|
if (dungeonManager != null && dungeonManager.getDungeonData() != null) {
|
||||||
scene
|
scene
|
||||||
.getPlayers()
|
.getPlayers()
|
||||||
.forEach(
|
.forEach(
|
||||||
p ->
|
p ->
|
||||||
p.getActivityManager()
|
p.getActivityManager()
|
||||||
.triggerWatcher(
|
.triggerWatcher(
|
||||||
WatcherTriggerType.TRIGGER_FINISH_CHALLENGE,
|
WatcherTriggerType.TRIGGER_FINISH_CHALLENGE,
|
||||||
String.valueOf(dungeonManager.getDungeonData().getId()),
|
String.valueOf(dungeonManager.getDungeonData().getId()),
|
||||||
String.valueOf(this.getGroup().id),
|
String.valueOf(this.getGroup().id),
|
||||||
String.valueOf(this.getChallengeId())));
|
String.valueOf(this.getChallengeId())));
|
||||||
}
|
}
|
||||||
|
|
||||||
scene
|
scene
|
||||||
.getScriptManager()
|
.getScriptManager()
|
||||||
.callEvent(
|
.callEvent(
|
||||||
// TODO record the time in PARAM2 and used in action
|
// TODO record the time in PARAM2 and used in action
|
||||||
new ScriptArgs(this.getGroup().id, EventType.EVENT_CHALLENGE_SUCCESS)
|
new ScriptArgs(this.getGroup().id, EventType.EVENT_CHALLENGE_SUCCESS)
|
||||||
.setParam2(finishedTime));
|
.setParam2(finishedTime));
|
||||||
this.getScene()
|
this.getScene()
|
||||||
.triggerDungeonEvent(
|
.triggerDungeonEvent(
|
||||||
DungeonPassConditionType.DUNGEON_COND_FINISH_CHALLENGE,
|
DungeonPassConditionType.DUNGEON_COND_FINISH_CHALLENGE,
|
||||||
getChallengeId(),
|
getChallengeId(),
|
||||||
getChallengeIndex());
|
getChallengeIndex());
|
||||||
|
|
||||||
this.challengeTriggers.forEach(t -> t.onFinish(this));
|
this.challengeTriggers.forEach(t -> t.onFinish(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
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()
|
||||||
.callEvent(new ScriptArgs(this.getGroup().id, EventType.EVENT_CHALLENGE_FAIL));
|
.callEvent(new ScriptArgs(this.getGroup().id, EventType.EVENT_CHALLENGE_FAIL));
|
||||||
challengeTriggers.forEach(t -> t.onFinish(this));
|
challengeTriggers.forEach(t -> t.onFinish(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
public int increaseScore() {
|
public int increaseScore() {
|
||||||
return score.incrementAndGet();
|
return score.incrementAndGet();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onMonsterDeath(EntityMonster monster) {
|
public void onMonsterDeath(EntityMonster monster) {
|
||||||
if (!inProgress()) {
|
if (!inProgress()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (monster.getGroupId() != getGroup().id) {
|
if (monster.getGroupId() != getGroup().id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.challengeTriggers.forEach(t -> t.onMonsterDeath(this, monster));
|
this.challengeTriggers.forEach(t -> t.onMonsterDeath(this, monster));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onGadgetDeath(EntityGadget gadget) {
|
public void onGadgetDeath(EntityGadget gadget) {
|
||||||
if (!inProgress()) {
|
if (!inProgress()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (gadget.getGroupId() != getGroup().id) {
|
if (gadget.getGroupId() != getGroup().id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.challengeTriggers.forEach(t -> t.onGadgetDeath(this, gadget));
|
this.challengeTriggers.forEach(t -> t.onGadgetDeath(this, gadget));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onGroupTriggerDeath(SceneTrigger trigger) {
|
public void onGroupTriggerDeath(SceneTrigger trigger) {
|
||||||
if (!this.inProgress()) return;
|
if (!this.inProgress()) return;
|
||||||
|
|
||||||
var triggerGroup = trigger.getCurrentGroup();
|
var triggerGroup = trigger.getCurrentGroup();
|
||||||
if (triggerGroup == null || triggerGroup.id != getGroup().id) {
|
if (triggerGroup == null || triggerGroup.id != getGroup().id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.challengeTriggers.forEach(t -> t.onGroupTrigger(this, trigger));
|
this.challengeTriggers.forEach(t -> t.onGroupTrigger(this, trigger));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onGadgetDamage(EntityGadget gadget) {
|
public void onGadgetDamage(EntityGadget gadget) {
|
||||||
if (!inProgress()) {
|
if (!inProgress()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (gadget.getGroupId() != getGroup().id) {
|
if (gadget.getGroupId() != getGroup().id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.challengeTriggers.forEach(t -> t.onGadgetDamage(this, gadget));
|
this.challengeTriggers.forEach(t -> t.onGadgetDamage(this, gadget));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,370 +1,367 @@
|
|||||||
package emu.grasscutter.game.entity;
|
package emu.grasscutter.game.entity;
|
||||||
|
|
||||||
import emu.grasscutter.GameConstants;
|
import emu.grasscutter.GameConstants;
|
||||||
import emu.grasscutter.data.GameData;
|
import emu.grasscutter.data.GameData;
|
||||||
import emu.grasscutter.data.excels.avatar.AvatarData;
|
import emu.grasscutter.data.excels.avatar.AvatarData;
|
||||||
import emu.grasscutter.data.excels.avatar.AvatarSkillDepotData;
|
import emu.grasscutter.data.excels.avatar.AvatarSkillDepotData;
|
||||||
import emu.grasscutter.game.avatar.Avatar;
|
import emu.grasscutter.game.avatar.Avatar;
|
||||||
import emu.grasscutter.game.inventory.EquipType;
|
import emu.grasscutter.game.inventory.EquipType;
|
||||||
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.EntityIdType;
|
import emu.grasscutter.game.props.EntityIdType;
|
||||||
import emu.grasscutter.game.props.FightProperty;
|
import emu.grasscutter.game.props.FightProperty;
|
||||||
import emu.grasscutter.game.props.PlayerProperty;
|
import emu.grasscutter.game.props.PlayerProperty;
|
||||||
import emu.grasscutter.game.world.Scene;
|
import emu.grasscutter.game.world.Scene;
|
||||||
import emu.grasscutter.net.proto.AbilityControlBlockOuterClass.AbilityControlBlock;
|
import emu.grasscutter.net.proto.AbilityControlBlockOuterClass.AbilityControlBlock;
|
||||||
import emu.grasscutter.net.proto.AbilityEmbryoOuterClass.AbilityEmbryo;
|
import emu.grasscutter.net.proto.AbilityEmbryoOuterClass.AbilityEmbryo;
|
||||||
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
|
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
|
||||||
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
|
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
|
||||||
import emu.grasscutter.net.proto.ChangeEnergyReasonOuterClass.ChangeEnergyReason;
|
import emu.grasscutter.net.proto.ChangeEnergyReasonOuterClass.ChangeEnergyReason;
|
||||||
import emu.grasscutter.net.proto.ChangeHpReasonOuterClass.ChangeHpReason;
|
import emu.grasscutter.net.proto.ChangeHpReasonOuterClass.ChangeHpReason;
|
||||||
import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo;
|
import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo;
|
||||||
import emu.grasscutter.net.proto.EntityClientDataOuterClass.EntityClientData;
|
import emu.grasscutter.net.proto.EntityClientDataOuterClass.EntityClientData;
|
||||||
import emu.grasscutter.net.proto.EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo;
|
import emu.grasscutter.net.proto.EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo;
|
||||||
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType;
|
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType;
|
||||||
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
|
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
|
||||||
import emu.grasscutter.net.proto.PropPairOuterClass.PropPair;
|
import emu.grasscutter.net.proto.PropPairOuterClass.PropPair;
|
||||||
import emu.grasscutter.net.proto.ProtEntityTypeOuterClass.ProtEntityType;
|
import emu.grasscutter.net.proto.ProtEntityTypeOuterClass.ProtEntityType;
|
||||||
import emu.grasscutter.net.proto.SceneAvatarInfoOuterClass.SceneAvatarInfo;
|
import emu.grasscutter.net.proto.SceneAvatarInfoOuterClass.SceneAvatarInfo;
|
||||||
import emu.grasscutter.net.proto.SceneEntityAiInfoOuterClass.SceneEntityAiInfo;
|
import emu.grasscutter.net.proto.SceneEntityAiInfoOuterClass.SceneEntityAiInfo;
|
||||||
import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
|
import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
|
||||||
import emu.grasscutter.net.proto.VectorOuterClass.Vector;
|
import emu.grasscutter.net.proto.VectorOuterClass.Vector;
|
||||||
import emu.grasscutter.server.event.player.PlayerMoveEvent;
|
import emu.grasscutter.server.event.player.PlayerMoveEvent;
|
||||||
import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify;
|
import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify;
|
||||||
import emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotify;
|
import emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotify;
|
||||||
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
|
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
|
||||||
import emu.grasscutter.utils.Position;
|
import emu.grasscutter.utils.Position;
|
||||||
import emu.grasscutter.utils.ProtoHelper;
|
import emu.grasscutter.utils.ProtoHelper;
|
||||||
import emu.grasscutter.utils.Utils;
|
import emu.grasscutter.utils.Utils;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
|
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
|
|
||||||
public class EntityAvatar extends GameEntity {
|
public class EntityAvatar extends GameEntity {
|
||||||
@Getter private final Avatar avatar;
|
@Getter private final Avatar avatar;
|
||||||
|
|
||||||
@Getter private PlayerDieType killedType;
|
@Getter private PlayerDieType killedType;
|
||||||
@Getter private int killedBy;
|
@Getter private int killedBy;
|
||||||
|
|
||||||
public EntityAvatar(Avatar avatar) {
|
public EntityAvatar(Avatar avatar) {
|
||||||
this(null, avatar);
|
this(null, avatar);
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntityAvatar(Scene scene, Avatar avatar) {
|
public EntityAvatar(Scene scene, Avatar avatar) {
|
||||||
super(scene);
|
super(scene);
|
||||||
|
|
||||||
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) {
|
|
||||||
this.id = getScene().getWorld().getNextEntityId(EntityIdType.AVATAR);
|
GameItem weapon = this.getAvatar().getWeapon();
|
||||||
|
if (weapon != null) {
|
||||||
var weapon = getAvatar().getWeapon();
|
weapon.setWeaponEntityId(getScene().getWorld().getNextEntityId(EntityIdType.WEAPON));
|
||||||
if (weapon != null) {
|
}
|
||||||
weapon.setWeaponEntityId(getScene().getWorld().getNextEntityId(EntityIdType.WEAPON));
|
}
|
||||||
}
|
|
||||||
}
|
@Override
|
||||||
}
|
public int getEntityTypeId() {
|
||||||
|
return this.getAvatar().getAvatarId();
|
||||||
@Override
|
}
|
||||||
public int getEntityTypeId() {
|
|
||||||
return this.getAvatar().getAvatarId();
|
public Player getPlayer() {
|
||||||
}
|
return this.avatar.getPlayer();
|
||||||
|
}
|
||||||
public Player getPlayer() {
|
|
||||||
return this.avatar.getPlayer();
|
@Override
|
||||||
}
|
public Position getPosition() {
|
||||||
|
return getPlayer().getPosition();
|
||||||
@Override
|
}
|
||||||
public Position getPosition() {
|
|
||||||
return getPlayer().getPosition();
|
@Override
|
||||||
}
|
public Position getRotation() {
|
||||||
|
return getPlayer().getRotation();
|
||||||
@Override
|
}
|
||||||
public Position getRotation() {
|
|
||||||
return getPlayer().getRotation();
|
@Override
|
||||||
}
|
public boolean isAlive() {
|
||||||
|
return this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) > 0f;
|
||||||
@Override
|
}
|
||||||
public boolean isAlive() {
|
|
||||||
return this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) > 0f;
|
@Override
|
||||||
}
|
public Int2FloatMap getFightProperties() {
|
||||||
|
return getAvatar().getFightProperties();
|
||||||
@Override
|
}
|
||||||
public Int2FloatMap getFightProperties() {
|
|
||||||
return getAvatar().getFightProperties();
|
public int getWeaponEntityId() {
|
||||||
}
|
if (getAvatar().getWeapon() != null) {
|
||||||
|
return getAvatar().getWeapon().getWeaponEntityId();
|
||||||
public int getWeaponEntityId() {
|
}
|
||||||
if (getAvatar().getWeapon() != null) {
|
return 0;
|
||||||
return getAvatar().getWeapon().getWeaponEntityId();
|
}
|
||||||
}
|
|
||||||
return 0;
|
@Override
|
||||||
}
|
public void onDeath(int killerId) {
|
||||||
|
super.onDeath(killerId); // Invoke super class's onDeath() method.
|
||||||
@Override
|
|
||||||
public void onDeath(int killerId) {
|
this.killedType = PlayerDieType.PLAYER_DIE_TYPE_KILL_BY_MONSTER;
|
||||||
super.onDeath(killerId); // Invoke super class's onDeath() method.
|
this.killedBy = killerId;
|
||||||
|
clearEnergy(ChangeEnergyReason.CHANGE_ENERGY_REASON_NONE);
|
||||||
this.killedType = PlayerDieType.PLAYER_DIE_TYPE_KILL_BY_MONSTER;
|
}
|
||||||
this.killedBy = killerId;
|
|
||||||
clearEnergy(ChangeEnergyReason.CHANGE_ENERGY_REASON_NONE);
|
public void onDeath(PlayerDieType dieType, int killerId) {
|
||||||
}
|
super.onDeath(killerId); // Invoke super class's onDeath() method.
|
||||||
|
|
||||||
public void onDeath(PlayerDieType dieType, int killerId) {
|
this.killedType = dieType;
|
||||||
super.onDeath(killerId); // Invoke super class's onDeath() method.
|
this.killedBy = killerId;
|
||||||
|
clearEnergy(ChangeEnergyReason.CHANGE_ENERGY_REASON_NONE);
|
||||||
this.killedType = dieType;
|
}
|
||||||
this.killedBy = killerId;
|
|
||||||
clearEnergy(ChangeEnergyReason.CHANGE_ENERGY_REASON_NONE);
|
@Override
|
||||||
}
|
public float heal(float amount) {
|
||||||
|
// Do not heal character if they are dead
|
||||||
@Override
|
if (!this.isAlive()) {
|
||||||
public float heal(float amount) {
|
return 0f;
|
||||||
// Do not heal character if they are dead
|
}
|
||||||
if (!this.isAlive()) {
|
|
||||||
return 0f;
|
float healed = super.heal(amount);
|
||||||
}
|
|
||||||
|
if (healed > 0f) {
|
||||||
float healed = super.heal(amount);
|
getScene()
|
||||||
|
.broadcastPacket(
|
||||||
if (healed > 0f) {
|
new PacketEntityFightPropChangeReasonNotify(
|
||||||
getScene()
|
this,
|
||||||
.broadcastPacket(
|
FightProperty.FIGHT_PROP_CUR_HP,
|
||||||
new PacketEntityFightPropChangeReasonNotify(
|
healed,
|
||||||
this,
|
PropChangeReason.PROP_CHANGE_REASON_ABILITY,
|
||||||
FightProperty.FIGHT_PROP_CUR_HP,
|
ChangeHpReason.CHANGE_HP_REASON_ADD_ABILITY));
|
||||||
healed,
|
}
|
||||||
PropChangeReason.PROP_CHANGE_REASON_ABILITY,
|
|
||||||
ChangeHpReason.CHANGE_HP_REASON_ADD_ABILITY));
|
return healed;
|
||||||
}
|
}
|
||||||
|
|
||||||
return healed;
|
public void clearEnergy(ChangeEnergyReason reason) {
|
||||||
}
|
// Fight props.
|
||||||
|
val curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp();
|
||||||
public void clearEnergy(ChangeEnergyReason reason) {
|
float curEnergy = this.getFightProperty(curEnergyProp);
|
||||||
// Fight props.
|
|
||||||
val curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp();
|
// Set energy to zero.
|
||||||
float curEnergy = this.getFightProperty(curEnergyProp);
|
this.avatar.setCurrentEnergy(curEnergyProp, 0);
|
||||||
|
|
||||||
// Set energy to zero.
|
// Send packets.
|
||||||
this.avatar.setCurrentEnergy(curEnergyProp, 0);
|
this.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, curEnergyProp));
|
||||||
|
|
||||||
// Send packets.
|
if (reason == ChangeEnergyReason.CHANGE_ENERGY_REASON_SKILL_START) {
|
||||||
this.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, curEnergyProp));
|
this.getScene()
|
||||||
|
.broadcastPacket(
|
||||||
if (reason == ChangeEnergyReason.CHANGE_ENERGY_REASON_SKILL_START) {
|
new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, -curEnergy, reason));
|
||||||
this.getScene()
|
}
|
||||||
.broadcastPacket(
|
}
|
||||||
new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, -curEnergy, reason));
|
|
||||||
}
|
/**
|
||||||
}
|
* Adds a fixed amount of energy to the current avatar.
|
||||||
|
*
|
||||||
/**
|
* @param amount The amount of energy to add.
|
||||||
* Adds a fixed amount of energy to the current avatar.
|
* @return True if the energy was added, false if the energy was not added.
|
||||||
*
|
*/
|
||||||
* @param amount The amount of energy to add.
|
public boolean addEnergy(float amount) {
|
||||||
* @return True if the energy was added, false if the energy was not added.
|
var curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp();
|
||||||
*/
|
var curEnergy = this.getFightProperty(curEnergyProp);
|
||||||
public boolean addEnergy(float amount) {
|
if (curEnergy == amount) return false;
|
||||||
var curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp();
|
|
||||||
var curEnergy = this.getFightProperty(curEnergyProp);
|
this.getAvatar().setCurrentEnergy(curEnergyProp, amount);
|
||||||
if (curEnergy == amount) return false;
|
this.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, curEnergyProp));
|
||||||
|
return true;
|
||||||
this.getAvatar().setCurrentEnergy(curEnergyProp, amount);
|
}
|
||||||
this.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, curEnergyProp));
|
|
||||||
return true;
|
public void addEnergy(float amount, PropChangeReason reason) {
|
||||||
}
|
this.addEnergy(amount, reason, false);
|
||||||
|
}
|
||||||
public void addEnergy(float amount, PropChangeReason reason) {
|
|
||||||
this.addEnergy(amount, reason, false);
|
public void addEnergy(float amount, PropChangeReason reason, boolean isFlat) {
|
||||||
}
|
// Get current and maximum energy for this avatar.
|
||||||
|
val elementType = this.getAvatar().getSkillDepot().getElementType();
|
||||||
public void addEnergy(float amount, PropChangeReason reason, boolean isFlat) {
|
val curEnergyProp = elementType.getCurEnergyProp();
|
||||||
// Get current and maximum energy for this avatar.
|
val maxEnergyProp = elementType.getMaxEnergyProp();
|
||||||
val elementType = this.getAvatar().getSkillDepot().getElementType();
|
|
||||||
val curEnergyProp = elementType.getCurEnergyProp();
|
float curEnergy = this.getFightProperty(curEnergyProp);
|
||||||
val maxEnergyProp = elementType.getMaxEnergyProp();
|
float maxEnergy = this.getFightProperty(maxEnergyProp);
|
||||||
|
|
||||||
float curEnergy = this.getFightProperty(curEnergyProp);
|
// Scale amount by energy recharge, if the amount is not flat.
|
||||||
float maxEnergy = this.getFightProperty(maxEnergyProp);
|
if (!isFlat) {
|
||||||
|
amount *= this.getFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY);
|
||||||
// Scale amount by energy recharge, if the amount is not flat.
|
}
|
||||||
if (!isFlat) {
|
|
||||||
amount *= this.getFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY);
|
// Determine the new energy value.
|
||||||
}
|
float newEnergy = Math.min(curEnergy + amount, maxEnergy);
|
||||||
|
|
||||||
// Determine the new energy value.
|
// Set energy and notify.
|
||||||
float newEnergy = Math.min(curEnergy + amount, maxEnergy);
|
if (newEnergy != curEnergy) {
|
||||||
|
this.avatar.setCurrentEnergy(curEnergyProp, newEnergy);
|
||||||
// Set energy and notify.
|
|
||||||
if (newEnergy != curEnergy) {
|
this.getScene()
|
||||||
this.avatar.setCurrentEnergy(curEnergyProp, newEnergy);
|
.broadcastPacket(new PacketAvatarFightPropUpdateNotify(this.getAvatar(), curEnergyProp));
|
||||||
|
this.getScene()
|
||||||
this.getScene()
|
.broadcastPacket(
|
||||||
.broadcastPacket(new PacketAvatarFightPropUpdateNotify(this.getAvatar(), curEnergyProp));
|
new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, newEnergy, reason));
|
||||||
this.getScene()
|
}
|
||||||
.broadcastPacket(
|
}
|
||||||
new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, newEnergy, reason));
|
|
||||||
}
|
public SceneAvatarInfo getSceneAvatarInfo() {
|
||||||
}
|
val avatar = this.getAvatar();
|
||||||
|
val player = this.getPlayer();
|
||||||
public SceneAvatarInfo getSceneAvatarInfo() {
|
SceneAvatarInfo.Builder avatarInfo =
|
||||||
val avatar = this.getAvatar();
|
SceneAvatarInfo.newBuilder()
|
||||||
val player = this.getPlayer();
|
.setUid(player.getUid())
|
||||||
SceneAvatarInfo.Builder avatarInfo =
|
.setAvatarId(avatar.getAvatarId())
|
||||||
SceneAvatarInfo.newBuilder()
|
.setGuid(avatar.getGuid())
|
||||||
.setUid(player.getUid())
|
.setPeerId(player.getPeerId())
|
||||||
.setAvatarId(avatar.getAvatarId())
|
.addAllTalentIdList(avatar.getTalentIdList())
|
||||||
.setGuid(avatar.getGuid())
|
.setCoreProudSkillLevel(avatar.getCoreProudSkillLevel())
|
||||||
.setPeerId(player.getPeerId())
|
.putAllSkillLevelMap(avatar.getSkillLevelMap())
|
||||||
.addAllTalentIdList(avatar.getTalentIdList())
|
.setSkillDepotId(avatar.getSkillDepotId())
|
||||||
.setCoreProudSkillLevel(avatar.getCoreProudSkillLevel())
|
.addAllInherentProudSkillList(avatar.getProudSkillList())
|
||||||
.putAllSkillLevelMap(avatar.getSkillLevelMap())
|
.putAllProudSkillExtraLevelMap(avatar.getProudSkillBonusMap())
|
||||||
.setSkillDepotId(avatar.getSkillDepotId())
|
.addAllTeamResonanceList(player.getTeamManager().getTeamResonances())
|
||||||
.addAllInherentProudSkillList(avatar.getProudSkillList())
|
.setWearingFlycloakId(avatar.getFlyCloak())
|
||||||
.putAllProudSkillExtraLevelMap(avatar.getProudSkillBonusMap())
|
.setCostumeId(avatar.getCostume())
|
||||||
.addAllTeamResonanceList(player.getTeamManager().getTeamResonances())
|
.setBornTime(avatar.getBornTime());
|
||||||
.setWearingFlycloakId(avatar.getFlyCloak())
|
|
||||||
.setCostumeId(avatar.getCostume())
|
for (GameItem item : avatar.getEquips().values()) {
|
||||||
.setBornTime(avatar.getBornTime());
|
if (item.getItemData().getEquipType() == EquipType.EQUIP_WEAPON) {
|
||||||
|
avatarInfo.setWeapon(item.createSceneWeaponInfo());
|
||||||
for (GameItem item : avatar.getEquips().values()) {
|
} else {
|
||||||
if (item.getItemData().getEquipType() == EquipType.EQUIP_WEAPON) {
|
avatarInfo.addReliquaryList(item.createSceneReliquaryInfo());
|
||||||
avatarInfo.setWeapon(item.createSceneWeaponInfo());
|
}
|
||||||
} else {
|
avatarInfo.addEquipIdList(item.getItemId());
|
||||||
avatarInfo.addReliquaryList(item.createSceneReliquaryInfo());
|
}
|
||||||
}
|
|
||||||
avatarInfo.addEquipIdList(item.getItemId());
|
return avatarInfo.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
return avatarInfo.build();
|
@Override
|
||||||
}
|
public SceneEntityInfo toProto() {
|
||||||
|
EntityAuthorityInfo authority =
|
||||||
@Override
|
EntityAuthorityInfo.newBuilder()
|
||||||
public SceneEntityInfo toProto() {
|
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
|
||||||
EntityAuthorityInfo authority =
|
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
|
||||||
EntityAuthorityInfo.newBuilder()
|
.setAiInfo(
|
||||||
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
|
SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(Vector.newBuilder()))
|
||||||
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
|
.setBornPos(Vector.newBuilder())
|
||||||
.setAiInfo(
|
.build();
|
||||||
SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(Vector.newBuilder()))
|
|
||||||
.setBornPos(Vector.newBuilder())
|
SceneEntityInfo.Builder entityInfo =
|
||||||
.build();
|
SceneEntityInfo.newBuilder()
|
||||||
|
.setEntityId(getId())
|
||||||
SceneEntityInfo.Builder entityInfo =
|
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_AVATAR)
|
||||||
SceneEntityInfo.newBuilder()
|
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
|
||||||
.setEntityId(getId())
|
.setEntityClientData(EntityClientData.newBuilder())
|
||||||
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_AVATAR)
|
.setEntityAuthorityInfo(authority)
|
||||||
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
|
.setLastMoveSceneTimeMs(this.getLastMoveSceneTimeMs())
|
||||||
.setEntityClientData(EntityClientData.newBuilder())
|
.setLastMoveReliableSeq(this.getLastMoveReliableSeq())
|
||||||
.setEntityAuthorityInfo(authority)
|
.setLifeState(this.getLifeState().getValue());
|
||||||
.setLastMoveSceneTimeMs(this.getLastMoveSceneTimeMs())
|
|
||||||
.setLastMoveReliableSeq(this.getLastMoveReliableSeq())
|
if (this.getScene() != null) {
|
||||||
.setLifeState(this.getLifeState().getValue());
|
entityInfo.setMotionInfo(this.getMotionInfo());
|
||||||
|
}
|
||||||
if (this.getScene() != null) {
|
|
||||||
entityInfo.setMotionInfo(this.getMotionInfo());
|
this.addAllFightPropsToEntityInfo(entityInfo);
|
||||||
}
|
|
||||||
|
PropPair pair =
|
||||||
this.addAllFightPropsToEntityInfo(entityInfo);
|
PropPair.newBuilder()
|
||||||
|
.setType(PlayerProperty.PROP_LEVEL.getId())
|
||||||
PropPair pair =
|
.setPropValue(
|
||||||
PropPair.newBuilder()
|
ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, getAvatar().getLevel()))
|
||||||
.setType(PlayerProperty.PROP_LEVEL.getId())
|
.build();
|
||||||
.setPropValue(
|
entityInfo.addPropList(pair);
|
||||||
ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, getAvatar().getLevel()))
|
|
||||||
.build();
|
entityInfo.setAvatar(this.getSceneAvatarInfo());
|
||||||
entityInfo.addPropList(pair);
|
|
||||||
|
return entityInfo.build();
|
||||||
entityInfo.setAvatar(this.getSceneAvatarInfo());
|
}
|
||||||
|
|
||||||
return entityInfo.build();
|
public AbilityControlBlock getAbilityControlBlock() {
|
||||||
}
|
AvatarData data = this.getAvatar().getAvatarData();
|
||||||
|
AbilityControlBlock.Builder abilityControlBlock = AbilityControlBlock.newBuilder();
|
||||||
public AbilityControlBlock getAbilityControlBlock() {
|
int embryoId = 0;
|
||||||
AvatarData data = this.getAvatar().getAvatarData();
|
|
||||||
AbilityControlBlock.Builder abilityControlBlock = AbilityControlBlock.newBuilder();
|
// Add avatar abilities
|
||||||
int embryoId = 0;
|
if (data.getAbilities() != null) {
|
||||||
|
for (int id : data.getAbilities()) {
|
||||||
// Add avatar abilities
|
AbilityEmbryo emb =
|
||||||
if (data.getAbilities() != null) {
|
AbilityEmbryo.newBuilder()
|
||||||
for (int id : data.getAbilities()) {
|
.setAbilityId(++embryoId)
|
||||||
AbilityEmbryo emb =
|
.setAbilityNameHash(id)
|
||||||
AbilityEmbryo.newBuilder()
|
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
|
||||||
.setAbilityId(++embryoId)
|
.build();
|
||||||
.setAbilityNameHash(id)
|
abilityControlBlock.addAbilityEmbryoList(emb);
|
||||||
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
|
}
|
||||||
.build();
|
}
|
||||||
abilityControlBlock.addAbilityEmbryoList(emb);
|
// Add default abilities
|
||||||
}
|
for (int id : GameConstants.DEFAULT_ABILITY_HASHES) {
|
||||||
}
|
AbilityEmbryo emb =
|
||||||
// Add default abilities
|
AbilityEmbryo.newBuilder()
|
||||||
for (int id : GameConstants.DEFAULT_ABILITY_HASHES) {
|
.setAbilityId(++embryoId)
|
||||||
AbilityEmbryo emb =
|
.setAbilityNameHash(id)
|
||||||
AbilityEmbryo.newBuilder()
|
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
|
||||||
.setAbilityId(++embryoId)
|
.build();
|
||||||
.setAbilityNameHash(id)
|
abilityControlBlock.addAbilityEmbryoList(emb);
|
||||||
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
|
}
|
||||||
.build();
|
// Add team resonances
|
||||||
abilityControlBlock.addAbilityEmbryoList(emb);
|
for (int id : this.getPlayer().getTeamManager().getTeamResonancesConfig()) {
|
||||||
}
|
AbilityEmbryo emb =
|
||||||
// Add team resonances
|
AbilityEmbryo.newBuilder()
|
||||||
for (int id : this.getPlayer().getTeamManager().getTeamResonancesConfig()) {
|
.setAbilityId(++embryoId)
|
||||||
AbilityEmbryo emb =
|
.setAbilityNameHash(id)
|
||||||
AbilityEmbryo.newBuilder()
|
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
|
||||||
.setAbilityId(++embryoId)
|
.build();
|
||||||
.setAbilityNameHash(id)
|
abilityControlBlock.addAbilityEmbryoList(emb);
|
||||||
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
|
}
|
||||||
.build();
|
// Add skill depot abilities
|
||||||
abilityControlBlock.addAbilityEmbryoList(emb);
|
AvatarSkillDepotData skillDepot =
|
||||||
}
|
GameData.getAvatarSkillDepotDataMap().get(this.getAvatar().getSkillDepotId());
|
||||||
// Add skill depot abilities
|
if (skillDepot != null && skillDepot.getAbilities() != null) {
|
||||||
AvatarSkillDepotData skillDepot =
|
for (int id : skillDepot.getAbilities()) {
|
||||||
GameData.getAvatarSkillDepotDataMap().get(this.getAvatar().getSkillDepotId());
|
AbilityEmbryo emb =
|
||||||
if (skillDepot != null && skillDepot.getAbilities() != null) {
|
AbilityEmbryo.newBuilder()
|
||||||
for (int id : skillDepot.getAbilities()) {
|
.setAbilityId(++embryoId)
|
||||||
AbilityEmbryo emb =
|
.setAbilityNameHash(id)
|
||||||
AbilityEmbryo.newBuilder()
|
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
|
||||||
.setAbilityId(++embryoId)
|
.build();
|
||||||
.setAbilityNameHash(id)
|
abilityControlBlock.addAbilityEmbryoList(emb);
|
||||||
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
|
}
|
||||||
.build();
|
}
|
||||||
abilityControlBlock.addAbilityEmbryoList(emb);
|
// Add equip abilities
|
||||||
}
|
if (this.getAvatar().getExtraAbilityEmbryos().size() > 0) {
|
||||||
}
|
for (String skill : this.getAvatar().getExtraAbilityEmbryos()) {
|
||||||
// Add equip abilities
|
AbilityEmbryo emb =
|
||||||
if (this.getAvatar().getExtraAbilityEmbryos().size() > 0) {
|
AbilityEmbryo.newBuilder()
|
||||||
for (String skill : this.getAvatar().getExtraAbilityEmbryos()) {
|
.setAbilityId(++embryoId)
|
||||||
AbilityEmbryo emb =
|
.setAbilityNameHash(Utils.abilityHash(skill))
|
||||||
AbilityEmbryo.newBuilder()
|
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
|
||||||
.setAbilityId(++embryoId)
|
.build();
|
||||||
.setAbilityNameHash(Utils.abilityHash(skill))
|
abilityControlBlock.addAbilityEmbryoList(emb);
|
||||||
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
|
}
|
||||||
.build();
|
}
|
||||||
abilityControlBlock.addAbilityEmbryoList(emb);
|
|
||||||
}
|
//
|
||||||
}
|
return abilityControlBlock.build();
|
||||||
|
}
|
||||||
//
|
|
||||||
return abilityControlBlock.build();
|
/**
|
||||||
}
|
* Move this entity to a new position. Additionally invoke player move event.
|
||||||
|
*
|
||||||
/**
|
* @param newPosition The new position.
|
||||||
* Move this entity to a new position. Additionally invoke player move event.
|
* @param rotation The new rotation.
|
||||||
*
|
*/
|
||||||
* @param newPosition The new position.
|
@Override
|
||||||
* @param rotation The new rotation.
|
public void move(Position newPosition, Position rotation) {
|
||||||
*/
|
// Invoke player move event.
|
||||||
@Override
|
PlayerMoveEvent event =
|
||||||
public void move(Position newPosition, Position rotation) {
|
new PlayerMoveEvent(
|
||||||
// Invoke player move event.
|
this.getPlayer(), PlayerMoveEvent.MoveType.PLAYER, this.getPosition(), newPosition);
|
||||||
PlayerMoveEvent event =
|
event.call();
|
||||||
new PlayerMoveEvent(
|
|
||||||
this.getPlayer(), PlayerMoveEvent.MoveType.PLAYER, this.getPosition(), newPosition);
|
// Set position and rotation.
|
||||||
event.call();
|
super.move(event.getDestination(), rotation);
|
||||||
|
}
|
||||||
// Set position and rotation.
|
}
|
||||||
super.move(event.getDestination(), rotation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,63 +1,91 @@
|
|||||||
package emu.grasscutter.game.entity;
|
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.world.Scene;
|
import emu.grasscutter.game.quest.enums.QuestContent;
|
||||||
import emu.grasscutter.utils.Position;
|
import emu.grasscutter.game.world.Scene;
|
||||||
import lombok.Getter;
|
import emu.grasscutter.scripts.data.ScriptArgs;
|
||||||
|
import emu.grasscutter.server.event.entity.EntityDamageEvent;
|
||||||
public abstract class EntityBaseGadget extends GameEntity {
|
import emu.grasscutter.utils.Position;
|
||||||
@Getter(onMethod_ = @Override)
|
import lombok.Getter;
|
||||||
protected final Position position;
|
|
||||||
|
import static emu.grasscutter.scripts.constants.EventType.EVENT_SPECIFIC_GADGET_HP_CHANGE;
|
||||||
@Getter(onMethod_ = @Override)
|
|
||||||
protected final Position rotation;
|
public abstract class EntityBaseGadget extends GameEntity {
|
||||||
|
@Getter(onMethod_ = @Override)
|
||||||
public EntityBaseGadget(Scene scene) {
|
protected final Position position;
|
||||||
this(scene, null, null);
|
|
||||||
}
|
@Getter(onMethod_ = @Override)
|
||||||
|
protected final Position rotation;
|
||||||
public EntityBaseGadget(Scene scene, Position position, Position rotation) {
|
|
||||||
super(scene);
|
public EntityBaseGadget(Scene scene) {
|
||||||
this.position = position != null ? position.clone() : new Position();
|
this(scene, null, null);
|
||||||
this.rotation = rotation != null ? rotation.clone() : new Position();
|
}
|
||||||
}
|
|
||||||
|
public EntityBaseGadget(Scene scene, Position position, Position rotation) {
|
||||||
public abstract int getGadgetId();
|
super(scene);
|
||||||
|
this.position = position != null ? position.clone() : new Position();
|
||||||
@Override
|
this.rotation = rotation != null ? rotation.clone() : new Position();
|
||||||
public int getEntityTypeId() {
|
}
|
||||||
return this.getGadgetId();
|
|
||||||
}
|
public abstract int getGadgetId();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDeath(int killerId) {
|
public int getEntityTypeId() {
|
||||||
super.onDeath(killerId); // Invoke super class's onDeath() method.
|
return this.getGadgetId();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void fillFightProps(ConfigEntityGadget configGadget) {
|
@Override
|
||||||
if (configGadget == null || configGadget.getCombat() == null) {
|
public void onDeath(int killerId) {
|
||||||
return;
|
super.onDeath(killerId); // Invoke super class's onDeath() method.
|
||||||
}
|
|
||||||
var combatData = configGadget.getCombat();
|
getScene()
|
||||||
var combatProperties = combatData.getProperty();
|
.getPlayers()
|
||||||
|
.forEach(
|
||||||
var targetHp = combatProperties.getHP();
|
p ->
|
||||||
setFightProperty(FightProperty.FIGHT_PROP_MAX_HP, targetHp);
|
p.getQuestManager()
|
||||||
setFightProperty(FightProperty.FIGHT_PROP_BASE_HP, targetHp);
|
.queueEvent(QuestContent.QUEST_CONTENT_DESTROY_GADGET, this.getGadgetId()));
|
||||||
if (combatProperties.isInvincible()) {
|
}
|
||||||
targetHp = Float.POSITIVE_INFINITY;
|
|
||||||
}
|
@Override
|
||||||
setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, targetHp);
|
public void runLuaCallbacks(EntityDamageEvent event) {
|
||||||
|
super.runLuaCallbacks(event);
|
||||||
var atk = combatProperties.getAttack();
|
getScene()
|
||||||
setFightProperty(FightProperty.FIGHT_PROP_BASE_ATTACK, atk);
|
.getScriptManager()
|
||||||
setFightProperty(FightProperty.FIGHT_PROP_CUR_ATTACK, atk);
|
.callEvent(
|
||||||
|
new ScriptArgs(
|
||||||
var def = combatProperties.getDefence();
|
this.getGroupId(),
|
||||||
setFightProperty(FightProperty.FIGHT_PROP_BASE_DEFENSE, def);
|
EVENT_SPECIFIC_GADGET_HP_CHANGE,
|
||||||
setFightProperty(FightProperty.FIGHT_PROP_CUR_DEFENSE, def);
|
getConfigId(),
|
||||||
|
getGadgetId())
|
||||||
setLockHP(combatProperties.isLockHP());
|
.setSourceEntityId(getId())
|
||||||
}
|
.setParam3((int) this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP))
|
||||||
}
|
.setEventSource(Integer.toString(getConfigId())));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void fillFightProps(ConfigEntityGadget configGadget) {
|
||||||
|
if (configGadget == null || configGadget.getCombat() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var combatData = configGadget.getCombat();
|
||||||
|
var combatProperties = combatData.getProperty();
|
||||||
|
|
||||||
|
var targetHp = combatProperties.getHP();
|
||||||
|
setFightProperty(FightProperty.FIGHT_PROP_MAX_HP, targetHp);
|
||||||
|
setFightProperty(FightProperty.FIGHT_PROP_BASE_HP, targetHp);
|
||||||
|
if (combatProperties.isInvincible()) {
|
||||||
|
targetHp = Float.POSITIVE_INFINITY;
|
||||||
|
}
|
||||||
|
setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, targetHp);
|
||||||
|
|
||||||
|
var atk = combatProperties.getAttack();
|
||||||
|
setFightProperty(FightProperty.FIGHT_PROP_BASE_ATTACK, atk);
|
||||||
|
setFightProperty(FightProperty.FIGHT_PROP_CUR_ATTACK, atk);
|
||||||
|
|
||||||
|
var def = combatProperties.getDefence();
|
||||||
|
setFightProperty(FightProperty.FIGHT_PROP_BASE_DEFENSE, def);
|
||||||
|
setFightProperty(FightProperty.FIGHT_PROP_CUR_DEFENSE, def);
|
||||||
|
|
||||||
|
setLockHP(combatProperties.isLockHP());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,96 +1,96 @@
|
|||||||
package emu.grasscutter.game.entity;
|
package emu.grasscutter.game.entity;
|
||||||
|
|
||||||
import emu.grasscutter.game.props.EntityIdType;
|
import emu.grasscutter.game.props.EntityIdType;
|
||||||
import emu.grasscutter.game.world.Scene;
|
import emu.grasscutter.game.world.Scene;
|
||||||
import emu.grasscutter.net.proto.SceneEntityInfoOuterClass;
|
import emu.grasscutter.net.proto.SceneEntityInfoOuterClass;
|
||||||
import emu.grasscutter.scripts.data.SceneRegion;
|
import emu.grasscutter.scripts.data.SceneRegion;
|
||||||
import emu.grasscutter.utils.Position;
|
import emu.grasscutter.utils.Position;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
|
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public class EntityRegion extends GameEntity {
|
public class EntityRegion extends GameEntity {
|
||||||
private final Position position;
|
private final Position position;
|
||||||
private final Set<Integer> entities; // Ids of entities inside this region
|
private final Set<Integer> entities; // Ids of entities inside this region
|
||||||
private final SceneRegion metaRegion;
|
private final SceneRegion metaRegion;
|
||||||
private boolean hasNewEntities;
|
private boolean hasNewEntities;
|
||||||
private boolean entityLeave;
|
private boolean entityLeave;
|
||||||
|
|
||||||
public EntityRegion(Scene scene, SceneRegion region) {
|
public EntityRegion(Scene scene, SceneRegion region) {
|
||||||
super(scene);
|
super(scene);
|
||||||
|
|
||||||
this.id = getScene().getWorld().getNextEntityId(EntityIdType.REGION);
|
this.id = getScene().getWorld().getNextEntityId(EntityIdType.REGION);
|
||||||
this.setGroupId(region.group.id);
|
this.setGroupId(region.group.id);
|
||||||
this.setBlockId(region.group.block_id);
|
this.setBlockId(region.group.block_id);
|
||||||
this.setConfigId(region.config_id);
|
this.setConfigId(region.config_id);
|
||||||
this.position = region.pos.clone();
|
this.position = region.pos.clone();
|
||||||
this.entities = ConcurrentHashMap.newKeySet();
|
this.entities = ConcurrentHashMap.newKeySet();
|
||||||
this.metaRegion = region;
|
this.metaRegion = region;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getEntityTypeId() {
|
public int getEntityTypeId() {
|
||||||
return this.metaRegion.config_id;
|
return this.metaRegion.config_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addEntity(GameEntity entity) {
|
public void addEntity(GameEntity entity) {
|
||||||
if (this.getEntities().contains(entity.getId())) {
|
if (this.getEntities().contains(entity.getId())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.getEntities().add(entity.getId());
|
this.getEntities().add(entity.getId());
|
||||||
this.hasNewEntities = true;
|
this.hasNewEntities = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasNewEntities() {
|
public boolean hasNewEntities() {
|
||||||
return hasNewEntities;
|
return hasNewEntities;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void resetNewEntities() {
|
public void resetNewEntities() {
|
||||||
hasNewEntities = false;
|
hasNewEntities = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeEntity(int entityId) {
|
public void removeEntity(int entityId) {
|
||||||
this.getEntities().remove(entityId);
|
this.getEntities().remove(entityId);
|
||||||
this.entityLeave = true;
|
this.entityLeave = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeEntity(GameEntity entity) {
|
public void removeEntity(GameEntity entity) {
|
||||||
this.getEntities().remove(entity.getId());
|
this.getEntities().remove(entity.getId());
|
||||||
this.entityLeave = true;
|
this.entityLeave = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean entityLeave() {
|
public boolean entityLeave() {
|
||||||
return this.entityLeave;
|
return this.entityLeave;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void resetEntityLeave() {
|
public void resetEntityLeave() {
|
||||||
this.entityLeave = false;
|
this.entityLeave = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Int2FloatMap getFightProperties() {
|
public Int2FloatMap getFightProperties() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Position getPosition() {
|
public Position getPosition() {
|
||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Position getRotation() {
|
public Position getRotation() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SceneEntityInfoOuterClass.SceneEntityInfo toProto() {
|
public SceneEntityInfoOuterClass.SceneEntityInfo toProto() {
|
||||||
/** The Region Entity would not be sent to client. */
|
/** The Region Entity would not be sent to client. */
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getFirstEntityId() {
|
public int getFirstEntityId() {
|
||||||
return entities.stream().findFirst().orElse(0);
|
return entities.stream().findFirst().orElse(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,264 +1,271 @@
|
|||||||
package emu.grasscutter.game.entity;
|
package emu.grasscutter.game.entity;
|
||||||
|
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.game.props.ElementType;
|
import emu.grasscutter.game.props.ElementType;
|
||||||
import emu.grasscutter.game.props.FightProperty;
|
import emu.grasscutter.game.props.FightProperty;
|
||||||
import emu.grasscutter.game.props.LifeState;
|
import emu.grasscutter.game.props.LifeState;
|
||||||
import emu.grasscutter.game.world.Scene;
|
import emu.grasscutter.game.world.Scene;
|
||||||
import emu.grasscutter.game.world.SpawnDataEntry;
|
import emu.grasscutter.game.world.SpawnDataEntry;
|
||||||
import emu.grasscutter.game.world.World;
|
import emu.grasscutter.game.world.World;
|
||||||
import emu.grasscutter.net.proto.FightPropPairOuterClass.FightPropPair;
|
import emu.grasscutter.net.proto.FightPropPairOuterClass.FightPropPair;
|
||||||
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
||||||
import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo;
|
import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo;
|
||||||
import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState;
|
import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState;
|
||||||
import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
|
import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
|
||||||
import emu.grasscutter.net.proto.VectorOuterClass.Vector;
|
import emu.grasscutter.net.proto.VectorOuterClass.Vector;
|
||||||
import emu.grasscutter.scripts.data.controller.EntityController;
|
import emu.grasscutter.scripts.data.controller.EntityController;
|
||||||
import emu.grasscutter.server.event.entity.EntityDamageEvent;
|
import emu.grasscutter.server.event.entity.EntityDamageEvent;
|
||||||
import emu.grasscutter.server.event.entity.EntityDeathEvent;
|
import emu.grasscutter.server.event.entity.EntityDeathEvent;
|
||||||
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
|
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
|
||||||
import emu.grasscutter.utils.Position;
|
import emu.grasscutter.utils.Position;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
|
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
|
||||||
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.objects.Object2FloatMap;
|
import it.unimi.dsi.fastutil.objects.Object2FloatMap;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2FloatOpenHashMap;
|
import it.unimi.dsi.fastutil.objects.Object2FloatOpenHashMap;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
public abstract class GameEntity {
|
public abstract class GameEntity {
|
||||||
@Getter private final Scene scene;
|
@Getter private final Scene scene;
|
||||||
@Getter protected int id;
|
@Getter protected int id;
|
||||||
@Getter @Setter private SpawnDataEntry spawnEntry;
|
@Getter @Setter private SpawnDataEntry spawnEntry;
|
||||||
|
|
||||||
@Getter @Setter private int blockId;
|
@Getter @Setter private int blockId;
|
||||||
@Getter @Setter private int configId;
|
@Getter @Setter private int configId;
|
||||||
@Getter @Setter private int groupId;
|
@Getter @Setter private int groupId;
|
||||||
|
|
||||||
@Getter @Setter private MotionState motionState;
|
@Getter @Setter private MotionState motionState;
|
||||||
@Getter @Setter private int lastMoveSceneTimeMs;
|
@Getter @Setter private int lastMoveSceneTimeMs;
|
||||||
@Getter @Setter private int lastMoveReliableSeq;
|
@Getter @Setter private int lastMoveReliableSeq;
|
||||||
|
|
||||||
@Getter @Setter private boolean lockHP;
|
@Getter @Setter private boolean lockHP;
|
||||||
|
|
||||||
// Lua controller for specific actions
|
// Lua controller for specific actions
|
||||||
@Getter @Setter private EntityController entityController;
|
@Getter @Setter private EntityController entityController;
|
||||||
@Getter private ElementType lastAttackType = ElementType.None;
|
@Getter private ElementType lastAttackType = ElementType.None;
|
||||||
|
|
||||||
// Abilities
|
// Abilities
|
||||||
private Object2FloatMap<String> metaOverrideMap;
|
private Object2FloatMap<String> metaOverrideMap;
|
||||||
private Int2ObjectMap<String> metaModifiers;
|
private Int2ObjectMap<String> metaModifiers;
|
||||||
|
|
||||||
public GameEntity(Scene scene) {
|
public GameEntity(Scene scene) {
|
||||||
this.scene = scene;
|
this.scene = scene;
|
||||||
this.motionState = MotionState.MOTION_STATE_NONE;
|
this.motionState = MotionState.MOTION_STATE_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getEntityType() {
|
public int getEntityType() {
|
||||||
return this.getId() >> 24;
|
return this.getId() >> 24;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract int getEntityTypeId();
|
public abstract int getEntityTypeId();
|
||||||
|
|
||||||
public World getWorld() {
|
public World getWorld() {
|
||||||
return this.getScene().getWorld();
|
return this.getScene().getWorld();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAlive() {
|
public boolean isAlive() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LifeState getLifeState() {
|
public LifeState getLifeState() {
|
||||||
return this.isAlive() ? LifeState.LIFE_ALIVE : LifeState.LIFE_DEAD;
|
return this.isAlive() ? LifeState.LIFE_ALIVE : LifeState.LIFE_DEAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object2FloatMap<String> getMetaOverrideMap() {
|
public Object2FloatMap<String> getMetaOverrideMap() {
|
||||||
if (this.metaOverrideMap == null) {
|
if (this.metaOverrideMap == null) {
|
||||||
this.metaOverrideMap = new Object2FloatOpenHashMap<>();
|
this.metaOverrideMap = new Object2FloatOpenHashMap<>();
|
||||||
}
|
}
|
||||||
return this.metaOverrideMap;
|
return this.metaOverrideMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Int2ObjectMap<String> getMetaModifiers() {
|
public Int2ObjectMap<String> getMetaModifiers() {
|
||||||
if (this.metaModifiers == null) {
|
if (this.metaModifiers == null) {
|
||||||
this.metaModifiers = new Int2ObjectOpenHashMap<>();
|
this.metaModifiers = new Int2ObjectOpenHashMap<>();
|
||||||
}
|
}
|
||||||
return this.metaModifiers;
|
return this.metaModifiers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract Int2FloatMap getFightProperties();
|
public abstract Int2FloatMap getFightProperties();
|
||||||
|
|
||||||
public abstract Position getPosition();
|
public abstract Position getPosition();
|
||||||
|
|
||||||
public abstract Position getRotation();
|
public abstract Position getRotation();
|
||||||
|
|
||||||
public void setFightProperty(FightProperty prop, float value) {
|
public void setFightProperty(FightProperty prop, float value) {
|
||||||
this.getFightProperties().put(prop.getId(), value);
|
this.getFightProperties().put(prop.getId(), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFightProperty(int id, float value) {
|
public void setFightProperty(int id, float value) {
|
||||||
this.getFightProperties().put(id, value);
|
this.getFightProperties().put(id, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addFightProperty(FightProperty prop, float value) {
|
public void addFightProperty(FightProperty prop, float value) {
|
||||||
this.getFightProperties().put(prop.getId(), this.getFightProperty(prop) + value);
|
this.getFightProperties().put(prop.getId(), this.getFightProperty(prop) + value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getFightProperty(FightProperty prop) {
|
public float getFightProperty(FightProperty prop) {
|
||||||
return this.getFightProperties().getOrDefault(prop.getId(), 0f);
|
return this.getFightProperties().getOrDefault(prop.getId(), 0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasFightProperty(FightProperty prop) {
|
public boolean hasFightProperty(FightProperty prop) {
|
||||||
return this.getFightProperties().containsKey(prop.getId());
|
return this.getFightProperties().containsKey(prop.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addAllFightPropsToEntityInfo(SceneEntityInfo.Builder entityInfo) {
|
public void addAllFightPropsToEntityInfo(SceneEntityInfo.Builder entityInfo) {
|
||||||
this.getFightProperties()
|
this.getFightProperties()
|
||||||
.forEach(
|
.forEach(
|
||||||
(key, value) -> {
|
(key, value) -> {
|
||||||
if (key == 0) return;
|
if (key == 0) return;
|
||||||
entityInfo.addFightPropList(
|
entityInfo.addFightPropList(
|
||||||
FightPropPair.newBuilder().setPropType(key).setPropValue(value).build());
|
FightPropPair.newBuilder().setPropType(key).setPropValue(value).build());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected MotionInfo getMotionInfo() {
|
protected MotionInfo getMotionInfo() {
|
||||||
return MotionInfo.newBuilder()
|
return MotionInfo.newBuilder()
|
||||||
.setPos(this.getPosition().toProto())
|
.setPos(this.getPosition().toProto())
|
||||||
.setRot(this.getRotation().toProto())
|
.setRot(this.getRotation().toProto())
|
||||||
.setSpeed(Vector.newBuilder())
|
.setSpeed(Vector.newBuilder())
|
||||||
.setState(this.getMotionState())
|
.setState(this.getMotionState())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public float heal(float amount) {
|
public float heal(float amount) {
|
||||||
if (this.getFightProperties() == null) {
|
if (this.getFightProperties() == null) {
|
||||||
return 0f;
|
return 0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
float curHp = this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
|
float curHp = this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
|
||||||
float maxHp = this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
|
float maxHp = this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
|
||||||
|
|
||||||
if (curHp >= maxHp) {
|
if (curHp >= maxHp) {
|
||||||
return 0f;
|
return 0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
float healed = Math.min(maxHp - curHp, amount);
|
float healed = Math.min(maxHp - curHp, amount);
|
||||||
this.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, healed);
|
this.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, healed);
|
||||||
|
|
||||||
this.getScene()
|
this.getScene()
|
||||||
.broadcastPacket(
|
.broadcastPacket(
|
||||||
new PacketEntityFightPropUpdateNotify(this, FightProperty.FIGHT_PROP_CUR_HP));
|
new PacketEntityFightPropUpdateNotify(this, FightProperty.FIGHT_PROP_CUR_HP));
|
||||||
|
|
||||||
return healed;
|
return healed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void damage(float amount) {
|
public void damage(float amount) {
|
||||||
this.damage(amount, 0, ElementType.None);
|
this.damage(amount, 0, ElementType.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void damage(float amount, int killerId, ElementType attackType) {
|
public void damage(float amount, ElementType attackType) {
|
||||||
// Check if the entity has properties.
|
this.damage(amount, 0, attackType);
|
||||||
if (this.getFightProperties() == null || !hasFightProperty(FightProperty.FIGHT_PROP_CUR_HP)) {
|
}
|
||||||
return;
|
|
||||||
}
|
public void damage(float amount, int killerId, ElementType attackType) {
|
||||||
|
// Check if the entity has properties.
|
||||||
// Invoke entity damage event.
|
if (this.getFightProperties() == null || !hasFightProperty(FightProperty.FIGHT_PROP_CUR_HP)) {
|
||||||
EntityDamageEvent event =
|
return;
|
||||||
new EntityDamageEvent(this, amount, attackType, this.getScene().getEntityById(killerId));
|
}
|
||||||
event.call();
|
|
||||||
if (event.isCanceled()) {
|
// Invoke entity damage event.
|
||||||
return; // If the event is canceled, do not damage the entity.
|
EntityDamageEvent event =
|
||||||
}
|
new EntityDamageEvent(this, amount, attackType, this.getScene().getEntityById(killerId));
|
||||||
|
event.call();
|
||||||
float curHp = getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
|
if (event.isCanceled()) {
|
||||||
if (curHp != Float.POSITIVE_INFINITY && !lockHP || lockHP && curHp <= event.getDamage()) {
|
return; // If the event is canceled, do not damage the entity.
|
||||||
// Add negative HP to the current HP property.
|
}
|
||||||
this.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, -(event.getDamage()));
|
|
||||||
}
|
float curHp = getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
|
||||||
|
if (curHp != Float.POSITIVE_INFINITY && !lockHP || lockHP && curHp <= event.getDamage()) {
|
||||||
// Check if dead
|
// Add negative HP to the current HP property.
|
||||||
boolean isDead = false;
|
this.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, -(event.getDamage()));
|
||||||
if (this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) <= 0f) {
|
}
|
||||||
this.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0f);
|
|
||||||
isDead = true;
|
this.lastAttackType = attackType;
|
||||||
}
|
|
||||||
|
// Check if dead
|
||||||
// Packets
|
boolean isDead = false;
|
||||||
this.getScene()
|
if (this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) <= 0f) {
|
||||||
.broadcastPacket(
|
this.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0f);
|
||||||
new PacketEntityFightPropUpdateNotify(this, FightProperty.FIGHT_PROP_CUR_HP));
|
isDead = true;
|
||||||
|
}
|
||||||
// Check if dead.
|
this.runLuaCallbacks(event);
|
||||||
if (isDead) {
|
|
||||||
this.getScene().killEntity(this, killerId);
|
// Packets
|
||||||
}
|
this.getScene()
|
||||||
}
|
.broadcastPacket(
|
||||||
|
new PacketEntityFightPropUpdateNotify(this, FightProperty.FIGHT_PROP_CUR_HP));
|
||||||
/**
|
|
||||||
* Runs the Lua callbacks for {@link EntityDamageEvent}.
|
// Check if dead.
|
||||||
*
|
if (isDead) {
|
||||||
* @param event The damage event.
|
this.getScene().killEntity(this, killerId);
|
||||||
*/
|
}
|
||||||
public void runLuaCallbacks(EntityDamageEvent event) {
|
}
|
||||||
if (entityController != null) {
|
|
||||||
entityController.onBeHurt(this, event.getAttackElementType(), true); // todo is host handling
|
/**
|
||||||
}
|
* Runs the Lua callbacks for {@link EntityDamageEvent}.
|
||||||
}
|
*
|
||||||
|
* @param event The damage event.
|
||||||
/**
|
*/
|
||||||
* Move this entity to a new position.
|
public void runLuaCallbacks(EntityDamageEvent event) {
|
||||||
*
|
if (entityController != null) {
|
||||||
* @param position The new position.
|
entityController.onBeHurt(this, event.getAttackElementType(), true); // todo is host handling
|
||||||
* @param rotation The new rotation.
|
}
|
||||||
*/
|
}
|
||||||
public void move(Position position, Position rotation) {
|
|
||||||
// Set the position and rotation.
|
/**
|
||||||
this.getPosition().set(position);
|
* Move this entity to a new position.
|
||||||
this.getRotation().set(rotation);
|
*
|
||||||
}
|
* @param position The new position.
|
||||||
|
* @param rotation The new rotation.
|
||||||
/**
|
*/
|
||||||
* Called when a player interacts with this entity
|
public void move(Position position, Position rotation) {
|
||||||
*
|
// Set the position and rotation.
|
||||||
* @param player Player that is interacting with this entity
|
this.getPosition().set(position);
|
||||||
* @param interactReq Interact request protobuf data
|
this.getRotation().set(rotation);
|
||||||
*/
|
}
|
||||||
public void onInteract(Player player, GadgetInteractReq interactReq) {}
|
|
||||||
|
/**
|
||||||
/** Called when this entity is added to the world */
|
* Called when a player interacts with this entity
|
||||||
public void onCreate() {}
|
*
|
||||||
|
* @param player Player that is interacting with this entity
|
||||||
public void onRemoved() {}
|
* @param interactReq Interact request protobuf data
|
||||||
|
*/
|
||||||
public void onTick(int sceneTime) {
|
public void onInteract(Player player, GadgetInteractReq interactReq) {}
|
||||||
if (entityController != null) {
|
|
||||||
entityController.onTimer(this, sceneTime);
|
/** Called when this entity is added to the world */
|
||||||
}
|
public void onCreate() {}
|
||||||
}
|
|
||||||
|
public void onRemoved() {}
|
||||||
public int onClientExecuteRequest(int param1, int param2, int param3) {
|
|
||||||
if (entityController != null) {
|
public void onTick(int sceneTime) {
|
||||||
return entityController.onClientExecuteRequest(this, param1, param2, param3);
|
if (entityController != null) {
|
||||||
}
|
entityController.onTimer(this, sceneTime);
|
||||||
return 0;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public int onClientExecuteRequest(int param1, int param2, int param3) {
|
||||||
* Called when this entity dies
|
if (entityController != null) {
|
||||||
*
|
return entityController.onClientExecuteRequest(this, param1, param2, param3);
|
||||||
* @param killerId Entity id of the entity that killed this entity
|
}
|
||||||
*/
|
return 0;
|
||||||
public void onDeath(int killerId) {
|
}
|
||||||
// Invoke entity death event.
|
|
||||||
EntityDeathEvent event = new EntityDeathEvent(this, killerId);
|
/**
|
||||||
event.call();
|
* Called when this entity dies
|
||||||
|
*
|
||||||
// Run Lua callbacks.
|
* @param killerId Entity id of the entity that killed this entity
|
||||||
if (entityController != null) {
|
*/
|
||||||
entityController.onDie(this, getLastAttackType());
|
public void onDeath(int killerId) {
|
||||||
}
|
// Invoke entity death event.
|
||||||
}
|
EntityDeathEvent event = new EntityDeathEvent(this, killerId);
|
||||||
|
event.call();
|
||||||
public abstract SceneEntityInfo toProto();
|
|
||||||
}
|
// Run Lua callbacks.
|
||||||
|
if (entityController != null) {
|
||||||
|
entityController.onDie(this, getLastAttackType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract SceneEntityInfo toProto();
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,86 +1,98 @@
|
|||||||
package emu.grasscutter.game.entity.gadget;
|
package emu.grasscutter.game.entity.gadget;
|
||||||
|
|
||||||
import emu.grasscutter.data.GameData;
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.data.excels.ItemData;
|
import emu.grasscutter.data.GameData;
|
||||||
import emu.grasscutter.game.entity.EntityGadget;
|
import emu.grasscutter.data.excels.GatherData;
|
||||||
import emu.grasscutter.game.entity.EntityItem;
|
import emu.grasscutter.data.excels.ItemData;
|
||||||
import emu.grasscutter.game.inventory.GameItem;
|
import emu.grasscutter.game.entity.EntityGadget;
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.entity.EntityItem;
|
||||||
import emu.grasscutter.game.props.ActionReason;
|
import emu.grasscutter.game.inventory.GameItem;
|
||||||
import emu.grasscutter.game.world.Scene;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
import emu.grasscutter.game.props.ActionReason;
|
||||||
import emu.grasscutter.net.proto.GatherGadgetInfoOuterClass.GatherGadgetInfo;
|
import emu.grasscutter.game.world.Scene;
|
||||||
import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType;
|
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
||||||
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
import emu.grasscutter.net.proto.GatherGadgetInfoOuterClass.GatherGadgetInfo;
|
||||||
import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
|
import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType;
|
||||||
import emu.grasscutter.utils.Utils;
|
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
|
||||||
public class GadgetGatherObject extends GadgetContent {
|
import emu.grasscutter.utils.Utils;
|
||||||
private int itemId;
|
|
||||||
private boolean isForbidGuest;
|
public final class GadgetGatherObject extends GadgetContent {
|
||||||
|
private int itemId;
|
||||||
public GadgetGatherObject(EntityGadget gadget) {
|
private boolean isForbidGuest;
|
||||||
super(gadget);
|
|
||||||
|
public GadgetGatherObject(EntityGadget gadget) {
|
||||||
if (gadget.getSpawnEntry() != null) {
|
super(gadget);
|
||||||
this.itemId = gadget.getSpawnEntry().getGatherItemId();
|
|
||||||
}
|
// overwrites the default spawn handling
|
||||||
}
|
if (gadget.getSpawnEntry() != null) {
|
||||||
|
this.itemId = gadget.getSpawnEntry().getGatherItemId();
|
||||||
public int getItemId() {
|
return;
|
||||||
return this.itemId;
|
}
|
||||||
}
|
|
||||||
|
GatherData gatherData = GameData.getGatherDataMap().get(gadget.getPointType());
|
||||||
public boolean isForbidGuest() {
|
if (gatherData != null) {
|
||||||
return isForbidGuest;
|
this.itemId = gatherData.getItemId();
|
||||||
}
|
this.isForbidGuest = gatherData.isForbidGuest();
|
||||||
|
} else {
|
||||||
public boolean onInteract(Player player, GadgetInteractReq req) {
|
Grasscutter.getLogger().error("invalid gather object: {}", gadget.getConfigId());
|
||||||
// Sanity check
|
}
|
||||||
ItemData itemData = GameData.getItemDataMap().get(getItemId());
|
}
|
||||||
if (itemData == null) {
|
|
||||||
return false;
|
public int getItemId() {
|
||||||
}
|
return this.itemId;
|
||||||
|
}
|
||||||
GameItem item = new GameItem(itemData, 1);
|
|
||||||
player.getInventory().addItem(item, ActionReason.Gather);
|
public boolean isForbidGuest() {
|
||||||
|
return isForbidGuest;
|
||||||
getGadget()
|
}
|
||||||
.getScene()
|
|
||||||
.broadcastPacket(
|
public boolean onInteract(Player player, GadgetInteractReq req) {
|
||||||
new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_TYPE_GATHER));
|
// Sanity check
|
||||||
|
ItemData itemData = GameData.getItemDataMap().get(getItemId());
|
||||||
return true;
|
if (itemData == null) {
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
|
|
||||||
GatherGadgetInfo gatherGadgetInfo =
|
GameItem item = new GameItem(itemData, 1);
|
||||||
GatherGadgetInfo.newBuilder()
|
player.getInventory().addItem(item, ActionReason.Gather);
|
||||||
.setItemId(this.getItemId())
|
|
||||||
.setIsForbidGuest(this.isForbidGuest())
|
getGadget()
|
||||||
.build();
|
.getScene()
|
||||||
|
.broadcastPacket(
|
||||||
gadgetInfo.setGatherGadget(gatherGadgetInfo);
|
new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_TYPE_GATHER));
|
||||||
}
|
|
||||||
|
return true;
|
||||||
public void dropItems(Player player) {
|
}
|
||||||
Scene scene = getGadget().getScene();
|
|
||||||
int times = Utils.randomRange(1, 2);
|
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
|
||||||
|
GatherGadgetInfo gatherGadgetInfo =
|
||||||
for (int i = 0; i < times; i++) {
|
GatherGadgetInfo.newBuilder()
|
||||||
EntityItem item =
|
.setItemId(this.getItemId())
|
||||||
new EntityItem(
|
.setIsForbidGuest(this.isForbidGuest())
|
||||||
scene,
|
.build();
|
||||||
player,
|
|
||||||
GameData.getItemDataMap().get(itemId),
|
gadgetInfo.setGatherGadget(gatherGadgetInfo);
|
||||||
getGadget().getPosition().nearby2d(1f).addY(2f),
|
}
|
||||||
1,
|
|
||||||
true);
|
public void dropItems(Player player) {
|
||||||
|
Scene scene = getGadget().getScene();
|
||||||
scene.addEntity(item);
|
int times = Utils.randomRange(1, 2);
|
||||||
}
|
|
||||||
|
for (int i = 0; i < times; i++) {
|
||||||
scene.killEntity(this.getGadget(), player.getTeamManager().getCurrentAvatarEntity().getId());
|
EntityItem item =
|
||||||
// Todo: add record
|
new EntityItem(
|
||||||
}
|
scene,
|
||||||
}
|
player,
|
||||||
|
GameData.getItemDataMap().get(itemId),
|
||||||
|
getGadget().getPosition().nearby2d(1f).addY(2f),
|
||||||
|
1,
|
||||||
|
true);
|
||||||
|
|
||||||
|
scene.addEntity(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
scene.killEntity(this.getGadget(), player.getTeamManager().getCurrentAvatarEntity().getId());
|
||||||
|
// Todo: add record
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,83 +1,65 @@
|
|||||||
package emu.grasscutter.game.entity.gadget;
|
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.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
||||||
import emu.grasscutter.game.world.Scene;
|
import emu.grasscutter.net.proto.GatherGadgetInfoOuterClass.GatherGadgetInfo;
|
||||||
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
||||||
import emu.grasscutter.net.proto.GatherGadgetInfoOuterClass.GatherGadgetInfo;
|
|
||||||
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
/** Spawner for the gather objects */
|
||||||
import emu.grasscutter.utils.Utils;
|
public final class GadgetGatherPoint extends GadgetContent {
|
||||||
|
private final GatherData gatherData;
|
||||||
public class GadgetGatherPoint extends GadgetContent {
|
private final EntityGadget gatherObjectChild;
|
||||||
private final int itemId;
|
|
||||||
private boolean isForbidGuest;
|
public GadgetGatherPoint(EntityGadget gadget) {
|
||||||
|
super(gadget);
|
||||||
public GadgetGatherPoint(EntityGadget gadget) {
|
|
||||||
super(gadget);
|
this.gatherData = GameData.getGatherDataMap().get(gadget.getPointType());
|
||||||
|
|
||||||
if (gadget.getSpawnEntry() != null) {
|
var scene = gadget.getScene();
|
||||||
this.itemId = gadget.getSpawnEntry().getGatherItemId();
|
gatherObjectChild = new EntityGadget(scene, gatherData.getGadgetId(), gadget.getBornPos());
|
||||||
} else {
|
|
||||||
GatherData gatherData = GameData.getGatherDataMap().get(gadget.getPointType());
|
gatherObjectChild.setBlockId(gadget.getBlockId());
|
||||||
this.itemId = gatherData.getItemId();
|
gatherObjectChild.setConfigId(gadget.getConfigId());
|
||||||
this.isForbidGuest = gatherData.isForbidGuest();
|
gatherObjectChild.setGroupId(gadget.getGroupId());
|
||||||
}
|
gatherObjectChild.getRotation().set(gadget.getRotation());
|
||||||
}
|
gatherObjectChild.setState(gadget.getState());
|
||||||
|
gatherObjectChild.setPointType(gadget.getPointType());
|
||||||
public int getItemId() {
|
gatherObjectChild.setMetaGadget(gadget.getMetaGadget());
|
||||||
return this.itemId;
|
gatherObjectChild.buildContent();
|
||||||
}
|
|
||||||
|
gadget.getChildren().add(gatherObjectChild);
|
||||||
public boolean isForbidGuest() {
|
scene.addEntity(gatherObjectChild);
|
||||||
return isForbidGuest;
|
}
|
||||||
}
|
|
||||||
|
public int getItemId() {
|
||||||
public boolean onInteract(Player player, GadgetInteractReq req) {
|
return this.gatherData.getItemId();
|
||||||
GameItem item = new GameItem(getItemId(), 1);
|
}
|
||||||
|
|
||||||
player.getInventory().addItem(item, ActionReason.Gather);
|
public boolean isForbidGuest() {
|
||||||
|
return this.gatherData.isForbidGuest();
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
public boolean onInteract(Player player, GadgetInteractReq req) {
|
||||||
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
|
GameItem item = new GameItem(getItemId(), 1);
|
||||||
GatherGadgetInfo gatherGadgetInfo =
|
|
||||||
GatherGadgetInfo.newBuilder()
|
player.getInventory().addItem(item, ActionReason.Gather);
|
||||||
.setItemId(this.getItemId())
|
|
||||||
.setIsForbidGuest(this.isForbidGuest())
|
return true;
|
||||||
.build();
|
}
|
||||||
|
|
||||||
gadgetInfo.setGatherGadget(gatherGadgetInfo);
|
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
|
||||||
}
|
// todo does official use this for the spawners?
|
||||||
|
GatherGadgetInfo gatherGadgetInfo =
|
||||||
public void dropItems(Player player) {
|
GatherGadgetInfo.newBuilder()
|
||||||
Scene scene = getGadget().getScene();
|
.setItemId(this.getItemId())
|
||||||
int times = Utils.randomRange(1, 2);
|
.setIsForbidGuest(this.isForbidGuest())
|
||||||
|
.build();
|
||||||
for (int i = 0; i < times; i++) {
|
|
||||||
EntityItem item =
|
gadgetInfo.setGatherGadget(gatherGadgetInfo);
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,65 +1,68 @@
|
|||||||
package emu.grasscutter.game.entity.gadget;
|
package emu.grasscutter.game.entity.gadget;
|
||||||
|
|
||||||
import emu.grasscutter.game.entity.EntityGadget;
|
import emu.grasscutter.game.entity.EntityGadget;
|
||||||
import emu.grasscutter.game.entity.gadget.worktop.WorktopWorktopOptionHandler;
|
import emu.grasscutter.game.entity.gadget.worktop.WorktopWorktopOptionHandler;
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
||||||
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
||||||
import emu.grasscutter.net.proto.SelectWorktopOptionReqOuterClass.SelectWorktopOptionReq;
|
import emu.grasscutter.net.proto.SelectWorktopOptionReqOuterClass.SelectWorktopOptionReq;
|
||||||
import emu.grasscutter.net.proto.WorktopInfoOuterClass.WorktopInfo;
|
import emu.grasscutter.net.proto.WorktopInfoOuterClass.WorktopInfo;
|
||||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
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;
|
||||||
|
|
||||||
public GadgetWorktop(EntityGadget gadget) {
|
public GadgetWorktop(EntityGadget gadget) {
|
||||||
super(gadget);
|
super(gadget);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IntSet getWorktopOptions() {
|
public IntSet getWorktopOptions() {
|
||||||
return worktopOptions;
|
if (this.worktopOptions == null) {
|
||||||
}
|
this.worktopOptions = new IntOpenHashSet();
|
||||||
|
}
|
||||||
public void addWorktopOptions(int[] options) {
|
return worktopOptions;
|
||||||
if (this.worktopOptions == null) {
|
}
|
||||||
this.worktopOptions = new IntOpenHashSet();
|
|
||||||
}
|
public void addWorktopOptions(int[] options) {
|
||||||
Arrays.stream(options).forEach(this.worktopOptions::add);
|
if (this.worktopOptions == null) {
|
||||||
}
|
this.worktopOptions = new IntOpenHashSet();
|
||||||
|
}
|
||||||
public void removeWorktopOption(int option) {
|
Arrays.stream(options).forEach(this.worktopOptions::add);
|
||||||
if (this.worktopOptions == null) {
|
}
|
||||||
return;
|
|
||||||
}
|
public void removeWorktopOption(int option) {
|
||||||
this.worktopOptions.remove(option);
|
if (this.worktopOptions == null) {
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
public boolean onInteract(Player player, GadgetInteractReq req) {
|
this.worktopOptions.remove(option);
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
|
public boolean onInteract(Player player, GadgetInteractReq req) {
|
||||||
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
|
return false;
|
||||||
if (this.worktopOptions == null) {
|
}
|
||||||
return;
|
|
||||||
}
|
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
|
||||||
|
if (this.worktopOptions == null) {
|
||||||
WorktopInfo worktop =
|
return;
|
||||||
WorktopInfo.newBuilder().addAllOptionList(this.getWorktopOptions()).build();
|
}
|
||||||
|
|
||||||
gadgetInfo.setWorktop(worktop);
|
WorktopInfo worktop =
|
||||||
}
|
WorktopInfo.newBuilder().addAllOptionList(this.getWorktopOptions()).build();
|
||||||
|
|
||||||
public void setOnSelectWorktopOptionEvent(WorktopWorktopOptionHandler handler) {
|
gadgetInfo.setWorktop(worktop);
|
||||||
this.handler = handler;
|
}
|
||||||
}
|
|
||||||
|
public void setOnSelectWorktopOptionEvent(WorktopWorktopOptionHandler handler) {
|
||||||
public boolean onSelectWorktopOption(SelectWorktopOptionReq req) {
|
this.handler = handler;
|
||||||
if (this.handler != null) {
|
}
|
||||||
this.handler.onSelectWorktopOption(this, req.getOptionId());
|
|
||||||
}
|
public boolean onSelectWorktopOption(SelectWorktopOptionReq req) {
|
||||||
return false;
|
if (this.handler != null) {
|
||||||
}
|
this.handler.onSelectWorktopOption(this, req.getOptionId());
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,61 +1,67 @@
|
|||||||
package emu.grasscutter.game.entity.gadget.chest;
|
package emu.grasscutter.game.entity.gadget.chest;
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.data.common.ItemParamData;
|
import emu.grasscutter.data.common.ItemParamData;
|
||||||
import emu.grasscutter.game.entity.gadget.GadgetChest;
|
import emu.grasscutter.game.entity.gadget.GadgetChest;
|
||||||
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.server.packet.send.PacketGadgetAutoPickDropInfoNotify;
|
import emu.grasscutter.server.packet.send.PacketGadgetAutoPickDropInfoNotify;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class BossChestInteractHandler implements ChestInteractHandler {
|
public class BossChestInteractHandler implements ChestInteractHandler {
|
||||||
@Override
|
@Override
|
||||||
public boolean isTwoStep() {
|
public boolean isTwoStep() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onInteract(GadgetChest chest, Player player) {
|
public boolean onInteract(GadgetChest chest, Player player) {
|
||||||
return this.onInteract(chest, player, false);
|
return this.onInteract(chest, player, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean onInteract(GadgetChest chest, Player player, boolean useCondensedResin) {
|
public boolean onInteract(GadgetChest chest, Player player, boolean useCondensedResin) {
|
||||||
var blossomRewards =
|
var blossomRewards =
|
||||||
player
|
player
|
||||||
.getScene()
|
.getScene()
|
||||||
.getBlossomManager()
|
.getBlossomManager()
|
||||||
.onReward(player, chest.getGadget(), useCondensedResin);
|
.onReward(player, chest.getGadget(), useCondensedResin);
|
||||||
if (blossomRewards != null) {
|
if (blossomRewards != null) {
|
||||||
player.getInventory().addItems(blossomRewards, ActionReason.OpenWorldBossChest);
|
player.getInventory().addItems(blossomRewards, ActionReason.OpenWorldBossChest);
|
||||||
player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(blossomRewards));
|
player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(blossomRewards));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var worldDataManager = chest.getGadget().getScene().getWorld().getServer().getWorldDataSystem();
|
var worldDataManager = chest.getGadget().getScene().getWorld().getServer().getWorldDataSystem();
|
||||||
var monster =
|
var monster =
|
||||||
chest
|
chest
|
||||||
.getGadget()
|
.getGadget()
|
||||||
.getMetaGadget()
|
.getMetaGadget()
|
||||||
.group
|
.group
|
||||||
.monsters
|
.monsters
|
||||||
.get(chest.getGadget().getMetaGadget().boss_chest.monster_config_id);
|
.get(chest.getGadget().getMetaGadget().boss_chest.monster_config_id);
|
||||||
var reward = worldDataManager.getRewardByBossId(monster.monster_id);
|
var reward = worldDataManager.getRewardByBossId(monster.monster_id);
|
||||||
|
|
||||||
if (reward == null) {
|
if (reward == null) {
|
||||||
Grasscutter.getLogger()
|
var dungeonManager = player.getScene().getDungeonManager();
|
||||||
.warn("Could not found the reward of boss monster {}", monster.monster_id);
|
|
||||||
return false;
|
if (dungeonManager != null) {
|
||||||
}
|
return dungeonManager.getStatueDrops(
|
||||||
List<GameItem> rewards = new ArrayList<>();
|
player, useCondensedResin, chest.getGadget().getGroupId());
|
||||||
for (ItemParamData param : reward.getPreviewItems()) {
|
}
|
||||||
rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1)));
|
Grasscutter.getLogger()
|
||||||
}
|
.warn("Could not found the reward of boss monster {}", monster.monster_id);
|
||||||
|
return false;
|
||||||
player.getInventory().addItems(rewards, ActionReason.OpenWorldBossChest);
|
}
|
||||||
player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(rewards));
|
List<GameItem> rewards = new ArrayList<>();
|
||||||
|
for (ItemParamData param : reward.getPreviewItems()) {
|
||||||
return true;
|
rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1)));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
player.getInventory().addItems(rewards, ActionReason.OpenWorldBossChest);
|
||||||
|
player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(rewards));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,47 +1,45 @@
|
|||||||
package emu.grasscutter.game.inventory;
|
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 java.util.HashMap;
|
import lombok.Getter;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
public enum EquipType {
|
import java.util.stream.Stream;
|
||||||
EQUIP_NONE(0),
|
|
||||||
EQUIP_BRACER(1),
|
public enum EquipType {
|
||||||
EQUIP_NECKLACE(2),
|
EQUIP_NONE(0),
|
||||||
EQUIP_SHOES(3),
|
EQUIP_BRACER(1),
|
||||||
EQUIP_RING(4),
|
EQUIP_NECKLACE(2),
|
||||||
EQUIP_DRESS(5),
|
EQUIP_SHOES(3),
|
||||||
EQUIP_WEAPON(6);
|
EQUIP_RING(4),
|
||||||
|
EQUIP_DRESS(5),
|
||||||
private static final Int2ObjectMap<EquipType> map = new Int2ObjectOpenHashMap<>();
|
EQUIP_WEAPON(6);
|
||||||
private static final Map<String, EquipType> stringMap = new HashMap<>();
|
|
||||||
|
private static final Int2ObjectMap<EquipType> map = new Int2ObjectOpenHashMap<>();
|
||||||
static {
|
private static final Map<String, EquipType> stringMap = new HashMap<>();
|
||||||
Stream.of(values())
|
|
||||||
.forEach(
|
static {
|
||||||
e -> {
|
Stream.of(values())
|
||||||
map.put(e.getValue(), e);
|
.forEach(
|
||||||
stringMap.put(e.name(), e);
|
e -> {
|
||||||
});
|
map.put(e.getValue(), e);
|
||||||
}
|
stringMap.put(e.name(), e);
|
||||||
|
});
|
||||||
private final int value;
|
}
|
||||||
|
|
||||||
EquipType(int value) {
|
@Getter private final int value;
|
||||||
this.value = value;
|
|
||||||
}
|
EquipType(int value) {
|
||||||
|
this.value = value;
|
||||||
public static EquipType getTypeByValue(int value) {
|
}
|
||||||
return map.getOrDefault(value, EQUIP_NONE);
|
|
||||||
}
|
public static EquipType getTypeByValue(int value) {
|
||||||
|
return map.getOrDefault(value, EQUIP_NONE);
|
||||||
public static EquipType getTypeByName(String name) {
|
}
|
||||||
return stringMap.getOrDefault(name, EQUIP_NONE);
|
|
||||||
}
|
public static EquipType getTypeByName(String name) {
|
||||||
|
return stringMap.getOrDefault(name, EQUIP_NONE);
|
||||||
public int getValue() {
|
}
|
||||||
return value;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,363 +1,371 @@
|
|||||||
package emu.grasscutter.game.inventory;
|
package emu.grasscutter.game.inventory;
|
||||||
|
|
||||||
import dev.morphia.annotations.*;
|
import dev.morphia.annotations.*;
|
||||||
import emu.grasscutter.data.GameData;
|
import emu.grasscutter.data.GameData;
|
||||||
import emu.grasscutter.data.GameDepot;
|
import emu.grasscutter.data.GameDepot;
|
||||||
import emu.grasscutter.data.common.ItemParamData;
|
import emu.grasscutter.data.common.ItemParamData;
|
||||||
import emu.grasscutter.data.excels.ItemData;
|
import emu.grasscutter.data.excels.ItemData;
|
||||||
import emu.grasscutter.data.excels.reliquary.ReliquaryAffixData;
|
import emu.grasscutter.data.excels.reliquary.ReliquaryAffixData;
|
||||||
import emu.grasscutter.data.excels.reliquary.ReliquaryMainPropData;
|
import emu.grasscutter.data.excels.reliquary.ReliquaryMainPropData;
|
||||||
import emu.grasscutter.database.DatabaseHelper;
|
import emu.grasscutter.database.DatabaseHelper;
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.game.props.FightProperty;
|
import emu.grasscutter.game.props.FightProperty;
|
||||||
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
|
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
|
||||||
import emu.grasscutter.net.proto.EquipOuterClass.Equip;
|
import emu.grasscutter.net.proto.EquipOuterClass.Equip;
|
||||||
import emu.grasscutter.net.proto.FurnitureOuterClass.Furniture;
|
import emu.grasscutter.net.proto.FurnitureOuterClass.Furniture;
|
||||||
import emu.grasscutter.net.proto.ItemHintOuterClass.ItemHint;
|
import emu.grasscutter.net.proto.ItemHintOuterClass.ItemHint;
|
||||||
import emu.grasscutter.net.proto.ItemOuterClass.Item;
|
import emu.grasscutter.net.proto.ItemOuterClass.Item;
|
||||||
import emu.grasscutter.net.proto.ItemParamOuterClass.ItemParam;
|
import emu.grasscutter.net.proto.ItemParamOuterClass.ItemParam;
|
||||||
import emu.grasscutter.net.proto.MaterialOuterClass.Material;
|
import emu.grasscutter.net.proto.MaterialOuterClass.Material;
|
||||||
import emu.grasscutter.net.proto.ReliquaryOuterClass.Reliquary;
|
import emu.grasscutter.net.proto.ReliquaryOuterClass.Reliquary;
|
||||||
import emu.grasscutter.net.proto.SceneReliquaryInfoOuterClass.SceneReliquaryInfo;
|
import emu.grasscutter.net.proto.SceneReliquaryInfoOuterClass.SceneReliquaryInfo;
|
||||||
import emu.grasscutter.net.proto.SceneWeaponInfoOuterClass.SceneWeaponInfo;
|
import emu.grasscutter.net.proto.SceneWeaponInfoOuterClass.SceneWeaponInfo;
|
||||||
import emu.grasscutter.net.proto.WeaponOuterClass.Weapon;
|
import emu.grasscutter.net.proto.WeaponOuterClass.Weapon;
|
||||||
import emu.grasscutter.utils.WeightedList;
|
import emu.grasscutter.utils.WeightedList;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import org.bson.types.ObjectId;
|
import org.bson.types.ObjectId;
|
||||||
|
|
||||||
@Entity(value = "items", useDiscriminator = false)
|
@Entity(value = "items", useDiscriminator = false)
|
||||||
public class GameItem {
|
public class GameItem {
|
||||||
@Id private ObjectId id;
|
@Id private ObjectId id;
|
||||||
@Indexed private int ownerId;
|
@Indexed private int ownerId;
|
||||||
@Getter @Setter private int itemId;
|
@Getter @Setter private int itemId;
|
||||||
@Getter @Setter private int count;
|
@Getter @Setter private int count;
|
||||||
|
|
||||||
@Transient @Getter private long guid; // Player unique id
|
@Transient @Getter private long guid; // Player unique id
|
||||||
@Transient @Getter @Setter private ItemData itemData;
|
@Transient @Getter @Setter private ItemData itemData;
|
||||||
|
|
||||||
// Equips
|
// Equips
|
||||||
@Getter @Setter private int level;
|
@Getter @Setter private int level;
|
||||||
@Getter @Setter private int exp;
|
@Getter @Setter private int exp;
|
||||||
@Getter @Setter private int totalExp;
|
@Getter @Setter private int totalExp;
|
||||||
@Getter @Setter private int promoteLevel;
|
@Getter @Setter private int promoteLevel;
|
||||||
@Getter @Setter private boolean locked;
|
@Getter @Setter private boolean locked;
|
||||||
|
|
||||||
// Weapon
|
// Weapon
|
||||||
@Getter private List<Integer> affixes;
|
@Getter private List<Integer> affixes;
|
||||||
@Getter @Setter private int refinement = 0;
|
@Getter @Setter private int refinement = 0;
|
||||||
|
|
||||||
// Relic
|
// Relic
|
||||||
@Getter @Setter private int mainPropId;
|
@Getter @Setter private int mainPropId;
|
||||||
@Getter private List<Integer> appendPropIdList;
|
@Getter private List<Integer> appendPropIdList;
|
||||||
|
|
||||||
@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() {
|
|
||||||
// Morphia only
|
public GameItem() {
|
||||||
}
|
// Morphia only
|
||||||
|
}
|
||||||
public GameItem(int itemId) {
|
|
||||||
this(GameData.getItemDataMap().get(itemId));
|
public GameItem(int itemId) {
|
||||||
}
|
this(GameData.getItemDataMap().get(itemId));
|
||||||
|
}
|
||||||
public GameItem(int itemId, int count) {
|
|
||||||
this(GameData.getItemDataMap().get(itemId), count);
|
public GameItem(int itemId, int count) {
|
||||||
}
|
this(GameData.getItemDataMap().get(itemId), count);
|
||||||
|
}
|
||||||
public GameItem(ItemParamData itemParamData) {
|
|
||||||
this(itemParamData.getId(), itemParamData.getCount());
|
public GameItem(ItemParamData itemParamData) {
|
||||||
}
|
this(itemParamData.getId(), itemParamData.getCount());
|
||||||
|
}
|
||||||
public GameItem(ItemData data) {
|
|
||||||
this(data, 1);
|
public GameItem(ItemData data) {
|
||||||
}
|
this(data, 1);
|
||||||
|
}
|
||||||
public GameItem(ItemData data, int count) {
|
|
||||||
this.itemId = data.getId();
|
public GameItem(ItemData data, int count) {
|
||||||
this.itemData = data;
|
this.itemId = data.getId();
|
||||||
|
this.itemData = data;
|
||||||
switch (data.getItemType()) {
|
|
||||||
case ITEM_VIRTUAL:
|
switch (data.getItemType()) {
|
||||||
this.count = count;
|
case ITEM_VIRTUAL:
|
||||||
break;
|
this.count = count;
|
||||||
case ITEM_WEAPON:
|
break;
|
||||||
this.count = 1;
|
case ITEM_WEAPON:
|
||||||
this.level = Math.max(this.count, 1); // ??????????????????
|
this.count = 1;
|
||||||
this.affixes = new ArrayList<>(2);
|
this.level = Math.max(this.count, 1); // ??????????????????
|
||||||
if (data.getSkillAffix() != null) {
|
this.affixes = new ArrayList<>(2);
|
||||||
for (int skillAffix : data.getSkillAffix()) {
|
if (data.getSkillAffix() != null) {
|
||||||
if (skillAffix > 0) {
|
for (int skillAffix : data.getSkillAffix()) {
|
||||||
this.affixes.add(skillAffix);
|
if (skillAffix > 0) {
|
||||||
}
|
this.affixes.add(skillAffix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case ITEM_RELIQUARY:
|
break;
|
||||||
this.count = 1;
|
case ITEM_RELIQUARY:
|
||||||
this.level = 1;
|
this.count = 1;
|
||||||
this.appendPropIdList = new ArrayList<>();
|
this.level = 1;
|
||||||
// Create main property
|
this.appendPropIdList = new ArrayList<>();
|
||||||
ReliquaryMainPropData mainPropData =
|
// Create main property
|
||||||
GameDepot.getRandomRelicMainProp(data.getMainPropDepotId());
|
ReliquaryMainPropData mainPropData =
|
||||||
if (mainPropData != null) {
|
GameDepot.getRandomRelicMainProp(data.getMainPropDepotId());
|
||||||
this.mainPropId = mainPropData.getId();
|
if (mainPropData != null) {
|
||||||
}
|
this.mainPropId = mainPropData.getId();
|
||||||
// Create extra stats
|
}
|
||||||
this.addAppendProps(data.getAppendPropNum());
|
// Create extra stats
|
||||||
break;
|
this.addAppendProps(data.getAppendPropNum());
|
||||||
default:
|
break;
|
||||||
this.count = Math.min(count, data.getStackLimit());
|
default:
|
||||||
}
|
this.count = Math.min(count, data.getStackLimit());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
public static int getMinPromoteLevel(int level) {
|
|
||||||
if (level > 80) {
|
public int getOwnerId() {
|
||||||
return 6;
|
return ownerId;
|
||||||
} else if (level > 70) {
|
}
|
||||||
return 5;
|
|
||||||
} else if (level > 60) {
|
public void setOwner(Player player) {
|
||||||
return 4;
|
this.ownerId = player.getUid();
|
||||||
} else if (level > 50) {
|
this.guid = player.getNextGameGuid();
|
||||||
return 3;
|
}
|
||||||
} else if (level > 40) {
|
|
||||||
return 2;
|
public void checkIsNew(Inventory inventory) {
|
||||||
} else if (level > 20) {
|
// display notification when player obtain new item
|
||||||
return 1;
|
if (inventory.getItemByGuid(this.itemId) == null) {
|
||||||
}
|
this.newItem = true;
|
||||||
return 0;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getOwnerId() {
|
public ObjectId getObjectId() {
|
||||||
return ownerId;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOwner(Player player) {
|
public ItemType getItemType() {
|
||||||
this.ownerId = player.getUid();
|
return this.itemData.getItemType();
|
||||||
this.guid = player.getNextGameGuid();
|
}
|
||||||
}
|
|
||||||
|
public static int getMinPromoteLevel(int level) {
|
||||||
public ObjectId getObjectId() {
|
if (level > 80) {
|
||||||
return id;
|
return 6;
|
||||||
}
|
} else if (level > 70) {
|
||||||
|
return 5;
|
||||||
public ItemType getItemType() {
|
} else if (level > 60) {
|
||||||
return this.itemData.getItemType();
|
return 4;
|
||||||
}
|
} else if (level > 50) {
|
||||||
|
return 3;
|
||||||
public int getEquipSlot() {
|
} else if (level > 40) {
|
||||||
return this.getItemData().getEquipType().getValue();
|
return 2;
|
||||||
}
|
} else if (level > 20) {
|
||||||
|
return 1;
|
||||||
public boolean isEquipped() {
|
}
|
||||||
return this.getEquipCharacter() > 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDestroyable() {
|
public int getEquipSlot() {
|
||||||
return !this.isLocked() && !this.isEquipped();
|
return this.getItemData().getEquipType().getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addAppendProp() {
|
public boolean isEquipped() {
|
||||||
if (this.appendPropIdList == null) {
|
return this.getEquipCharacter() > 0;
|
||||||
this.appendPropIdList = new ArrayList<>();
|
}
|
||||||
}
|
|
||||||
|
public boolean isDestroyable() {
|
||||||
if (this.appendPropIdList.size() < 4) {
|
return !this.isLocked() && !this.isEquipped();
|
||||||
this.addNewAppendProp();
|
}
|
||||||
} else {
|
|
||||||
this.upgradeRandomAppendProp();
|
public void addAppendProp() {
|
||||||
}
|
if (this.appendPropIdList == null) {
|
||||||
}
|
this.appendPropIdList = new ArrayList<>();
|
||||||
|
}
|
||||||
public void addAppendProps(int quantity) {
|
|
||||||
int num = Math.max(quantity, 0);
|
if (this.appendPropIdList.size() < 4) {
|
||||||
for (int i = 0; i < num; i++) {
|
this.addNewAppendProp();
|
||||||
this.addAppendProp();
|
} else {
|
||||||
}
|
this.upgradeRandomAppendProp();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private Set<FightProperty> getAppendFightProperties() {
|
|
||||||
Set<FightProperty> props = new HashSet<>();
|
public void addAppendProps(int quantity) {
|
||||||
// Previously this would check no more than the first four affixes, however custom artifacts may
|
int num = Math.max(quantity, 0);
|
||||||
// not respect this order.
|
for (int i = 0; i < num; i++) {
|
||||||
for (int appendPropId : this.appendPropIdList) {
|
this.addAppendProp();
|
||||||
ReliquaryAffixData affixData = GameData.getReliquaryAffixDataMap().get(appendPropId);
|
}
|
||||||
if (affixData != null) {
|
}
|
||||||
props.add(affixData.getFightProp());
|
|
||||||
}
|
private Set<FightProperty> getAppendFightProperties() {
|
||||||
}
|
Set<FightProperty> props = new HashSet<>();
|
||||||
return props;
|
// Previously this would check no more than the first four affixes, however custom artifacts may
|
||||||
}
|
// not respect this order.
|
||||||
|
for (int appendPropId : this.appendPropIdList) {
|
||||||
private void addNewAppendProp() {
|
ReliquaryAffixData affixData = GameData.getReliquaryAffixDataMap().get(appendPropId);
|
||||||
List<ReliquaryAffixData> affixList =
|
if (affixData != null) {
|
||||||
GameDepot.getRelicAffixList(this.itemData.getAppendPropDepotId());
|
props.add(affixData.getFightProp());
|
||||||
|
}
|
||||||
if (affixList == null) {
|
}
|
||||||
return;
|
return props;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build blacklist - Dont add same stat as main/sub stat
|
private void addNewAppendProp() {
|
||||||
Set<FightProperty> blacklist = this.getAppendFightProperties();
|
List<ReliquaryAffixData> affixList =
|
||||||
ReliquaryMainPropData mainPropData =
|
GameDepot.getRelicAffixList(this.itemData.getAppendPropDepotId());
|
||||||
GameData.getReliquaryMainPropDataMap().get(this.mainPropId);
|
|
||||||
if (mainPropData != null) {
|
if (affixList == null) {
|
||||||
blacklist.add(mainPropData.getFightProp());
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build random list
|
// Build blacklist - Dont add same stat as main/sub stat
|
||||||
WeightedList<ReliquaryAffixData> randomList = new WeightedList<>();
|
Set<FightProperty> blacklist = this.getAppendFightProperties();
|
||||||
for (ReliquaryAffixData affix : affixList) {
|
ReliquaryMainPropData mainPropData =
|
||||||
if (!blacklist.contains(affix.getFightProp())) {
|
GameData.getReliquaryMainPropDataMap().get(this.mainPropId);
|
||||||
randomList.add(affix.getWeight(), affix);
|
if (mainPropData != null) {
|
||||||
}
|
blacklist.add(mainPropData.getFightProp());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (randomList.size() == 0) {
|
// Build random list
|
||||||
return;
|
WeightedList<ReliquaryAffixData> randomList = new WeightedList<>();
|
||||||
}
|
for (ReliquaryAffixData affix : affixList) {
|
||||||
|
if (!blacklist.contains(affix.getFightProp())) {
|
||||||
// Add random stat
|
randomList.add(affix.getWeight(), affix);
|
||||||
ReliquaryAffixData affixData = randomList.next();
|
}
|
||||||
this.appendPropIdList.add(affixData.getId());
|
}
|
||||||
}
|
|
||||||
|
if (randomList.size() == 0) {
|
||||||
private void upgradeRandomAppendProp() {
|
return;
|
||||||
List<ReliquaryAffixData> affixList =
|
}
|
||||||
GameDepot.getRelicAffixList(this.itemData.getAppendPropDepotId());
|
|
||||||
|
// Add random stat
|
||||||
if (affixList == null) {
|
ReliquaryAffixData affixData = randomList.next();
|
||||||
return;
|
this.appendPropIdList.add(affixData.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build whitelist
|
private void upgradeRandomAppendProp() {
|
||||||
Set<FightProperty> whitelist = this.getAppendFightProperties();
|
List<ReliquaryAffixData> affixList =
|
||||||
|
GameDepot.getRelicAffixList(this.itemData.getAppendPropDepotId());
|
||||||
// Build random list
|
|
||||||
WeightedList<ReliquaryAffixData> randomList = new WeightedList<>();
|
if (affixList == null) {
|
||||||
for (ReliquaryAffixData affix : affixList) {
|
return;
|
||||||
if (whitelist.contains(affix.getFightProp())) {
|
}
|
||||||
randomList.add(affix.getUpgradeWeight(), affix);
|
|
||||||
}
|
// Build whitelist
|
||||||
}
|
Set<FightProperty> whitelist = this.getAppendFightProperties();
|
||||||
|
|
||||||
// Add random stat
|
// Build random list
|
||||||
ReliquaryAffixData affixData = randomList.next();
|
WeightedList<ReliquaryAffixData> randomList = new WeightedList<>();
|
||||||
this.appendPropIdList.add(affixData.getId());
|
for (ReliquaryAffixData affix : affixList) {
|
||||||
}
|
if (whitelist.contains(affix.getFightProp())) {
|
||||||
|
randomList.add(affix.getUpgradeWeight(), affix);
|
||||||
@PostLoad
|
}
|
||||||
public void onLoad() {
|
}
|
||||||
if (this.itemData == null) {
|
|
||||||
this.itemData = GameData.getItemDataMap().get(getItemId());
|
// Add random stat
|
||||||
}
|
ReliquaryAffixData affixData = randomList.next();
|
||||||
}
|
this.appendPropIdList.add(affixData.getId());
|
||||||
|
}
|
||||||
public void save() {
|
|
||||||
if (this.count > 0 && this.ownerId > 0) {
|
@PostLoad
|
||||||
DatabaseHelper.saveItem(this);
|
public void onLoad() {
|
||||||
} else if (this.getObjectId() != null) {
|
if (this.itemData == null) {
|
||||||
DatabaseHelper.deleteItem(this);
|
this.itemData = GameData.getItemDataMap().get(getItemId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public SceneWeaponInfo createSceneWeaponInfo() {
|
public void save() {
|
||||||
SceneWeaponInfo.Builder weaponInfo =
|
if (this.count > 0 && this.ownerId > 0) {
|
||||||
SceneWeaponInfo.newBuilder()
|
DatabaseHelper.saveItem(this);
|
||||||
.setEntityId(this.getWeaponEntityId())
|
} else if (this.getObjectId() != null) {
|
||||||
.setItemId(this.getItemId())
|
DatabaseHelper.deleteItem(this);
|
||||||
.setGuid(this.getGuid())
|
}
|
||||||
.setLevel(this.getLevel())
|
}
|
||||||
.setGadgetId(this.getItemData().getGadgetId())
|
|
||||||
.setAbilityInfo(AbilitySyncStateInfo.newBuilder().setIsInited(getAffixes().size() > 0));
|
public SceneWeaponInfo createSceneWeaponInfo() {
|
||||||
|
SceneWeaponInfo.Builder weaponInfo =
|
||||||
if (this.getAffixes() != null && this.getAffixes().size() > 0) {
|
SceneWeaponInfo.newBuilder()
|
||||||
for (int affix : this.getAffixes()) {
|
.setEntityId(this.getWeaponEntityId())
|
||||||
weaponInfo.putAffixMap(affix, this.getRefinement());
|
.setItemId(this.getItemId())
|
||||||
}
|
.setGuid(this.getGuid())
|
||||||
}
|
.setLevel(this.getLevel())
|
||||||
|
.setGadgetId(this.getItemData().getGadgetId())
|
||||||
return weaponInfo.build();
|
.setAbilityInfo(AbilitySyncStateInfo.newBuilder().setIsInited(getAffixes().size() > 0));
|
||||||
}
|
|
||||||
|
if (this.getAffixes() != null && this.getAffixes().size() > 0) {
|
||||||
public SceneReliquaryInfo createSceneReliquaryInfo() {
|
for (int affix : this.getAffixes()) {
|
||||||
SceneReliquaryInfo relicInfo =
|
weaponInfo.putAffixMap(affix, this.getRefinement());
|
||||||
SceneReliquaryInfo.newBuilder()
|
}
|
||||||
.setItemId(this.getItemId())
|
}
|
||||||
.setGuid(this.getGuid())
|
|
||||||
.setLevel(this.getLevel())
|
return weaponInfo.build();
|
||||||
.build();
|
}
|
||||||
|
|
||||||
return relicInfo;
|
public SceneReliquaryInfo createSceneReliquaryInfo() {
|
||||||
}
|
SceneReliquaryInfo relicInfo =
|
||||||
|
SceneReliquaryInfo.newBuilder()
|
||||||
public Weapon toWeaponProto() {
|
.setItemId(this.getItemId())
|
||||||
Weapon.Builder weapon =
|
.setGuid(this.getGuid())
|
||||||
Weapon.newBuilder()
|
.setLevel(this.getLevel())
|
||||||
.setLevel(this.getLevel())
|
.build();
|
||||||
.setExp(this.getExp())
|
|
||||||
.setPromoteLevel(this.getPromoteLevel());
|
return relicInfo;
|
||||||
|
}
|
||||||
if (this.getAffixes() != null && this.getAffixes().size() > 0) {
|
|
||||||
for (int affix : this.getAffixes()) {
|
public Weapon toWeaponProto() {
|
||||||
weapon.putAffixMap(affix, this.getRefinement());
|
Weapon.Builder weapon =
|
||||||
}
|
Weapon.newBuilder()
|
||||||
}
|
.setLevel(this.getLevel())
|
||||||
|
.setExp(this.getExp())
|
||||||
return weapon.build();
|
.setPromoteLevel(this.getPromoteLevel());
|
||||||
}
|
|
||||||
|
if (this.getAffixes() != null && this.getAffixes().size() > 0) {
|
||||||
public Reliquary toReliquaryProto() {
|
for (int affix : this.getAffixes()) {
|
||||||
Reliquary.Builder relic =
|
weapon.putAffixMap(affix, this.getRefinement());
|
||||||
Reliquary.newBuilder()
|
}
|
||||||
.setLevel(this.getLevel())
|
}
|
||||||
.setExp(this.getExp())
|
|
||||||
.setPromoteLevel(this.getPromoteLevel())
|
return weapon.build();
|
||||||
.setMainPropId(this.getMainPropId())
|
}
|
||||||
.addAllAppendPropIdList(this.getAppendPropIdList());
|
|
||||||
|
public Reliquary toReliquaryProto() {
|
||||||
return relic.build();
|
Reliquary.Builder relic =
|
||||||
}
|
Reliquary.newBuilder()
|
||||||
|
.setLevel(this.getLevel())
|
||||||
public Item toProto() {
|
.setExp(this.getExp())
|
||||||
Item.Builder proto = Item.newBuilder().setGuid(this.getGuid()).setItemId(this.getItemId());
|
.setPromoteLevel(this.getPromoteLevel())
|
||||||
|
.setMainPropId(this.getMainPropId())
|
||||||
switch (getItemType()) {
|
.addAllAppendPropIdList(this.getAppendPropIdList());
|
||||||
case ITEM_WEAPON:
|
|
||||||
Weapon weapon = this.toWeaponProto();
|
return relic.build();
|
||||||
proto.setEquip(Equip.newBuilder().setWeapon(weapon).setIsLocked(this.isLocked()).build());
|
}
|
||||||
break;
|
|
||||||
case ITEM_RELIQUARY:
|
public Item toProto() {
|
||||||
Reliquary relic = this.toReliquaryProto();
|
Item.Builder proto = Item.newBuilder().setGuid(this.getGuid()).setItemId(this.getItemId());
|
||||||
proto.setEquip(Equip.newBuilder().setReliquary(relic).setIsLocked(this.isLocked()).build());
|
|
||||||
break;
|
switch (getItemType()) {
|
||||||
case ITEM_FURNITURE:
|
case ITEM_WEAPON:
|
||||||
Furniture furniture = Furniture.newBuilder().setCount(getCount()).build();
|
Weapon weapon = this.toWeaponProto();
|
||||||
proto.setFurniture(furniture);
|
proto.setEquip(Equip.newBuilder().setWeapon(weapon).setIsLocked(this.isLocked()).build());
|
||||||
break;
|
break;
|
||||||
default:
|
case ITEM_RELIQUARY:
|
||||||
Material material = Material.newBuilder().setCount(getCount()).build();
|
Reliquary relic = this.toReliquaryProto();
|
||||||
proto.setMaterial(material);
|
proto.setEquip(Equip.newBuilder().setReliquary(relic).setIsLocked(this.isLocked()).build());
|
||||||
break;
|
break;
|
||||||
}
|
case ITEM_FURNITURE:
|
||||||
|
Furniture furniture = Furniture.newBuilder().setCount(getCount()).build();
|
||||||
return proto.build();
|
proto.setFurniture(furniture);
|
||||||
}
|
break;
|
||||||
|
default:
|
||||||
public ItemHint toItemHintProto() {
|
Material material = Material.newBuilder().setCount(getCount()).build();
|
||||||
return ItemHint.newBuilder()
|
proto.setMaterial(material);
|
||||||
.setItemId(getItemId())
|
break;
|
||||||
.setCount(getCount())
|
}
|
||||||
.setIsNew(false)
|
|
||||||
.build();
|
return proto.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ItemParam toItemParam() {
|
public ItemHint toItemHintProto() {
|
||||||
return ItemParam.newBuilder().setItemId(this.getItemId()).setCount(this.getCount()).build();
|
return ItemHint.newBuilder()
|
||||||
}
|
.setItemId(getItemId())
|
||||||
}
|
.setCount(getCount())
|
||||||
|
.setIsNew(this.isNewItem())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemParam toItemParam() {
|
||||||
|
return ItemParam.newBuilder().setItemId(this.getItemId()).setCount(this.getCount()).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,47 +1,45 @@
|
|||||||
package emu.grasscutter.game.inventory;
|
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 java.util.HashMap;
|
import lombok.Getter;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
public enum ItemQuality {
|
import java.util.stream.Stream;
|
||||||
QUALITY_NONE(0),
|
|
||||||
QUALITY_WHITE(1),
|
public enum ItemQuality {
|
||||||
QUALITY_GREEN(2),
|
QUALITY_NONE(0),
|
||||||
QUALITY_BLUE(3),
|
QUALITY_WHITE(1),
|
||||||
QUALITY_PURPLE(4),
|
QUALITY_GREEN(2),
|
||||||
QUALITY_ORANGE(5),
|
QUALITY_BLUE(3),
|
||||||
QUALITY_ORANGE_SP(105);
|
QUALITY_PURPLE(4),
|
||||||
|
QUALITY_ORANGE(5),
|
||||||
private static final Int2ObjectMap<ItemQuality> map = new Int2ObjectOpenHashMap<>();
|
QUALITY_ORANGE_SP(105);
|
||||||
private static final Map<String, ItemQuality> stringMap = new HashMap<>();
|
|
||||||
|
private static final Int2ObjectMap<ItemQuality> map = new Int2ObjectOpenHashMap<>();
|
||||||
static {
|
private static final Map<String, ItemQuality> stringMap = new HashMap<>();
|
||||||
Stream.of(values())
|
|
||||||
.forEach(
|
static {
|
||||||
e -> {
|
Stream.of(values())
|
||||||
map.put(e.getValue(), e);
|
.forEach(
|
||||||
stringMap.put(e.name(), e);
|
e -> {
|
||||||
});
|
map.put(e.getValue(), e);
|
||||||
}
|
stringMap.put(e.name(), e);
|
||||||
|
});
|
||||||
private final int value;
|
}
|
||||||
|
|
||||||
ItemQuality(int value) {
|
@Getter private final int value;
|
||||||
this.value = value;
|
|
||||||
}
|
ItemQuality(int value) {
|
||||||
|
this.value = value;
|
||||||
public static ItemQuality getTypeByValue(int value) {
|
}
|
||||||
return map.getOrDefault(value, QUALITY_NONE);
|
|
||||||
}
|
public static ItemQuality getTypeByValue(int value) {
|
||||||
|
return map.getOrDefault(value, QUALITY_NONE);
|
||||||
public static ItemQuality getTypeByName(String name) {
|
}
|
||||||
return stringMap.getOrDefault(name, QUALITY_NONE);
|
|
||||||
}
|
public static ItemQuality getTypeByName(String name) {
|
||||||
|
return stringMap.getOrDefault(name, QUALITY_NONE);
|
||||||
public int getValue() {
|
}
|
||||||
return value;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,47 +1,45 @@
|
|||||||
package emu.grasscutter.game.inventory;
|
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 java.util.HashMap;
|
import lombok.Getter;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
public enum ItemType {
|
import java.util.stream.Stream;
|
||||||
ITEM_NONE(0),
|
|
||||||
ITEM_VIRTUAL(1),
|
public enum ItemType {
|
||||||
ITEM_MATERIAL(2),
|
ITEM_NONE(0),
|
||||||
ITEM_RELIQUARY(3),
|
ITEM_VIRTUAL(1),
|
||||||
ITEM_WEAPON(4),
|
ITEM_MATERIAL(2),
|
||||||
ITEM_DISPLAY(5),
|
ITEM_RELIQUARY(3),
|
||||||
ITEM_FURNITURE(6);
|
ITEM_WEAPON(4),
|
||||||
|
ITEM_DISPLAY(5),
|
||||||
private static final Int2ObjectMap<ItemType> map = new Int2ObjectOpenHashMap<>();
|
ITEM_FURNITURE(6);
|
||||||
private static final Map<String, ItemType> stringMap = new HashMap<>();
|
|
||||||
|
private static final Int2ObjectMap<ItemType> map = new Int2ObjectOpenHashMap<>();
|
||||||
static {
|
private static final Map<String, ItemType> stringMap = new HashMap<>();
|
||||||
Stream.of(values())
|
|
||||||
.forEach(
|
static {
|
||||||
e -> {
|
Stream.of(values())
|
||||||
map.put(e.getValue(), e);
|
.forEach(
|
||||||
stringMap.put(e.name(), e);
|
e -> {
|
||||||
});
|
map.put(e.getValue(), e);
|
||||||
}
|
stringMap.put(e.name(), e);
|
||||||
|
});
|
||||||
private final int value;
|
}
|
||||||
|
|
||||||
ItemType(int value) {
|
@Getter private final int value;
|
||||||
this.value = value;
|
|
||||||
}
|
ItemType(int value) {
|
||||||
|
this.value = value;
|
||||||
public static ItemType getTypeByValue(int value) {
|
}
|
||||||
return map.getOrDefault(value, ITEM_NONE);
|
|
||||||
}
|
public static ItemType getTypeByValue(int value) {
|
||||||
|
return map.getOrDefault(value, ITEM_NONE);
|
||||||
public static ItemType getTypeByName(String name) {
|
}
|
||||||
return stringMap.getOrDefault(name, ITEM_NONE);
|
|
||||||
}
|
public static ItemType getTypeByName(String name) {
|
||||||
|
return stringMap.getOrDefault(name, ITEM_NONE);
|
||||||
public int getValue() {
|
}
|
||||||
return value;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,81 +1,79 @@
|
|||||||
package emu.grasscutter.game.inventory;
|
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 java.util.HashMap;
|
import lombok.Getter;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
public enum MaterialType {
|
import java.util.stream.Stream;
|
||||||
MATERIAL_NONE(0),
|
|
||||||
MATERIAL_FOOD(1),
|
public enum MaterialType {
|
||||||
MATERIAL_QUEST(2),
|
MATERIAL_NONE(0),
|
||||||
MATERIAL_EXCHANGE(4),
|
MATERIAL_FOOD(1),
|
||||||
MATERIAL_CONSUME(5),
|
MATERIAL_QUEST(2),
|
||||||
MATERIAL_EXP_FRUIT(6),
|
MATERIAL_EXCHANGE(4),
|
||||||
MATERIAL_AVATAR(7),
|
MATERIAL_CONSUME(5),
|
||||||
MATERIAL_ADSORBATE(8),
|
MATERIAL_EXP_FRUIT(6),
|
||||||
MATERIAL_CRICKET(9),
|
MATERIAL_AVATAR(7),
|
||||||
MATERIAL_ELEM_CRYSTAL(10),
|
MATERIAL_ADSORBATE(8),
|
||||||
MATERIAL_WEAPON_EXP_STONE(11),
|
MATERIAL_CRICKET(9),
|
||||||
MATERIAL_CHEST(12),
|
MATERIAL_ELEM_CRYSTAL(10),
|
||||||
MATERIAL_RELIQUARY_MATERIAL(13),
|
MATERIAL_WEAPON_EXP_STONE(11),
|
||||||
MATERIAL_AVATAR_MATERIAL(14),
|
MATERIAL_CHEST(12),
|
||||||
MATERIAL_NOTICE_ADD_HP(15),
|
MATERIAL_RELIQUARY_MATERIAL(13),
|
||||||
MATERIAL_SEA_LAMP(16),
|
MATERIAL_AVATAR_MATERIAL(14),
|
||||||
MATERIAL_SELECTABLE_CHEST(17),
|
MATERIAL_NOTICE_ADD_HP(15),
|
||||||
MATERIAL_FLYCLOAK(18),
|
MATERIAL_SEA_LAMP(16),
|
||||||
MATERIAL_NAMECARD(19),
|
MATERIAL_SELECTABLE_CHEST(17),
|
||||||
MATERIAL_TALENT(20),
|
MATERIAL_FLYCLOAK(18),
|
||||||
MATERIAL_WIDGET(21),
|
MATERIAL_NAMECARD(19),
|
||||||
MATERIAL_CHEST_BATCH_USE(22),
|
MATERIAL_TALENT(20),
|
||||||
MATERIAL_FAKE_ABSORBATE(23),
|
MATERIAL_WIDGET(21),
|
||||||
MATERIAL_CONSUME_BATCH_USE(24),
|
MATERIAL_CHEST_BATCH_USE(22),
|
||||||
MATERIAL_WOOD(25),
|
MATERIAL_FAKE_ABSORBATE(23),
|
||||||
MATERIAL_FURNITURE_FORMULA(27),
|
MATERIAL_CONSUME_BATCH_USE(24),
|
||||||
MATERIAL_CHANNELLER_SLAB_BUFF(28),
|
MATERIAL_WOOD(25),
|
||||||
MATERIAL_FURNITURE_SUITE_FORMULA(29),
|
MATERIAL_FURNITURE_FORMULA(27),
|
||||||
MATERIAL_COSTUME(30),
|
MATERIAL_CHANNELLER_SLAB_BUFF(28),
|
||||||
MATERIAL_HOME_SEED(31),
|
MATERIAL_FURNITURE_SUITE_FORMULA(29),
|
||||||
MATERIAL_FISH_BAIT(32),
|
MATERIAL_COSTUME(30),
|
||||||
MATERIAL_FISH_ROD(33),
|
MATERIAL_HOME_SEED(31),
|
||||||
MATERIAL_SUMO_BUFF(34),
|
MATERIAL_FISH_BAIT(32),
|
||||||
MATERIAL_FIREWORKS(35),
|
MATERIAL_FISH_ROD(33),
|
||||||
MATERIAL_BGM(36),
|
MATERIAL_SUMO_BUFF(34),
|
||||||
MATERIAL_SPICE_FOOD(37),
|
MATERIAL_FIREWORKS(35),
|
||||||
MATERIAL_ACTIVITY_ROBOT(38),
|
MATERIAL_BGM(36),
|
||||||
MATERIAL_ACTIVITY_GEAR(39),
|
MATERIAL_SPICE_FOOD(37),
|
||||||
MATERIAL_ACTIVITY_JIGSAW(40),
|
MATERIAL_ACTIVITY_ROBOT(38),
|
||||||
MATERIAL_ARANARA(41),
|
MATERIAL_ACTIVITY_GEAR(39),
|
||||||
MATERIAL_DESHRET_MANUAL(46);
|
MATERIAL_ACTIVITY_JIGSAW(40),
|
||||||
|
MATERIAL_ARANARA(41),
|
||||||
private static final Int2ObjectMap<MaterialType> map = new Int2ObjectOpenHashMap<>();
|
MATERIAL_DESHRET_MANUAL(46);
|
||||||
private static final Map<String, MaterialType> stringMap = new HashMap<>();
|
|
||||||
|
private static final Int2ObjectMap<MaterialType> map = new Int2ObjectOpenHashMap<>();
|
||||||
static {
|
private static final Map<String, MaterialType> stringMap = new HashMap<>();
|
||||||
Stream.of(values())
|
|
||||||
.forEach(
|
static {
|
||||||
e -> {
|
Stream.of(values())
|
||||||
map.put(e.getValue(), e);
|
.forEach(
|
||||||
stringMap.put(e.name(), e);
|
e -> {
|
||||||
});
|
map.put(e.getValue(), e);
|
||||||
}
|
stringMap.put(e.name(), e);
|
||||||
|
});
|
||||||
private final int value;
|
}
|
||||||
|
|
||||||
MaterialType(int value) {
|
@Getter private final int value;
|
||||||
this.value = value;
|
|
||||||
}
|
MaterialType(int value) {
|
||||||
|
this.value = value;
|
||||||
public static MaterialType getTypeByValue(int value) {
|
}
|
||||||
return map.getOrDefault(value, MATERIAL_NONE);
|
|
||||||
}
|
public static MaterialType getTypeByValue(int value) {
|
||||||
|
return map.getOrDefault(value, MATERIAL_NONE);
|
||||||
public static MaterialType getTypeByName(String name) {
|
}
|
||||||
return stringMap.getOrDefault(name, MATERIAL_NONE);
|
|
||||||
}
|
public static MaterialType getTypeByName(String name) {
|
||||||
|
return stringMap.getOrDefault(name, MATERIAL_NONE);
|
||||||
public int getValue() {
|
}
|
||||||
return value;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,243 +1,244 @@
|
|||||||
package emu.grasscutter.game.managers.blossom;
|
package emu.grasscutter.game.managers.blossom;
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.data.GameData;
|
import emu.grasscutter.data.GameData;
|
||||||
import emu.grasscutter.data.GameDepot;
|
import emu.grasscutter.data.GameDepot;
|
||||||
import emu.grasscutter.data.common.ItemParamData;
|
import emu.grasscutter.data.common.ItemParamData;
|
||||||
import emu.grasscutter.data.excels.RewardPreviewData;
|
import emu.grasscutter.data.excels.RewardPreviewData;
|
||||||
import emu.grasscutter.game.entity.EntityGadget;
|
import emu.grasscutter.game.entity.EntityGadget;
|
||||||
import emu.grasscutter.game.entity.GameEntity;
|
import emu.grasscutter.game.entity.GameEntity;
|
||||||
import emu.grasscutter.game.entity.gadget.GadgetWorktop;
|
import emu.grasscutter.game.entity.gadget.GadgetWorktop;
|
||||||
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.world.Scene;
|
import emu.grasscutter.game.world.Scene;
|
||||||
import emu.grasscutter.game.world.SpawnDataEntry;
|
import emu.grasscutter.game.world.SpawnDataEntry;
|
||||||
import emu.grasscutter.game.world.SpawnDataEntry.SpawnGroupEntry;
|
import emu.grasscutter.game.world.SpawnDataEntry.SpawnGroupEntry;
|
||||||
import emu.grasscutter.net.proto.BlossomBriefInfoOuterClass;
|
import emu.grasscutter.net.proto.BlossomBriefInfoOuterClass;
|
||||||
import emu.grasscutter.net.proto.VisionTypeOuterClass;
|
import emu.grasscutter.net.proto.VisionTypeOuterClass;
|
||||||
import emu.grasscutter.server.packet.send.PacketBlossomBriefInfoNotify;
|
import emu.grasscutter.server.packet.send.PacketBlossomBriefInfoNotify;
|
||||||
import emu.grasscutter.utils.Utils;
|
import emu.grasscutter.utils.Utils;
|
||||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
import it.unimi.dsi.fastutil.ints.IntList;
|
import it.unimi.dsi.fastutil.ints.IntList;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class BlossomManager {
|
public class BlossomManager {
|
||||||
private final Scene scene;
|
private final Scene scene;
|
||||||
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) {
|
|
||||||
this.scene = scene;
|
public BlossomManager(Scene scene) {
|
||||||
}
|
this.scene = scene;
|
||||||
|
}
|
||||||
private static Integer getPreviewReward(BlossomType type, int worldLevel) {
|
|
||||||
// TODO: blossoms should be based on their city
|
public void onTick() {
|
||||||
if (type == null) {
|
synchronized (blossomActivities) {
|
||||||
Grasscutter.getLogger().error("Illegal blossom type {}", type);
|
var it = blossomActivities.iterator();
|
||||||
return null;
|
while (it.hasNext()) {
|
||||||
}
|
var active = it.next();
|
||||||
|
active.onTick();
|
||||||
int blossomChestId = type.getBlossomChestId();
|
if (active.getPass()) {
|
||||||
var dataMap = GameData.getBlossomRefreshExcelConfigDataMap();
|
EntityGadget chest = active.getChest();
|
||||||
for (var data : dataMap.values()) {
|
scene.addEntity(chest);
|
||||||
if (blossomChestId == data.getBlossomChestId()) {
|
scene.setChallenge(null);
|
||||||
var dropVecList = data.getDropVec();
|
activeChests.add(active);
|
||||||
if (worldLevel > dropVecList.length) {
|
it.remove();
|
||||||
Grasscutter.getLogger().error("Illegal world level {}", worldLevel);
|
}
|
||||||
return null;
|
}
|
||||||
}
|
}
|
||||||
return dropVecList[worldLevel].getPreviewReward();
|
}
|
||||||
}
|
|
||||||
}
|
public void recycleGadgetEntity(List<GameEntity> entities) {
|
||||||
Grasscutter.getLogger().error("Cannot find blossom type {}", type);
|
for (var entity : entities) {
|
||||||
return null;
|
if (entity instanceof EntityGadget gadget) {
|
||||||
}
|
createdEntity.remove(gadget);
|
||||||
|
}
|
||||||
private static RewardPreviewData getRewardList(BlossomType type, int worldLevel) {
|
}
|
||||||
Integer previewReward = getPreviewReward(type, worldLevel);
|
notifyIcon();
|
||||||
if (previewReward == null) return null;
|
}
|
||||||
return GameData.getRewardPreviewDataMap().get((int) previewReward);
|
|
||||||
}
|
public void initBlossom(EntityGadget gadget) {
|
||||||
|
if (createdEntity.contains(gadget)) {
|
||||||
public static IntList getRandomMonstersID(int difficulty, int count) {
|
return;
|
||||||
IntList result = new IntArrayList();
|
}
|
||||||
List<Integer> monsters =
|
if (blossomConsumed.contains(gadget.getSpawnEntry())) {
|
||||||
GameDepot.getBlossomConfig().getMonsterIdsPerDifficulty().get(difficulty);
|
return;
|
||||||
for (int i = 0; i < count; i++) {
|
}
|
||||||
result.add((int) monsters.get(Utils.randomRange(0, monsters.size() - 1)));
|
var id = gadget.getGadgetId();
|
||||||
}
|
if (BlossomType.valueOf(id) == null) {
|
||||||
return result;
|
return;
|
||||||
}
|
}
|
||||||
|
gadget.buildContent();
|
||||||
public void onTick() {
|
gadget.setState(204);
|
||||||
synchronized (blossomActivities) {
|
int worldLevel = getWorldLevel();
|
||||||
var it = blossomActivities.iterator();
|
GadgetWorktop gadgetWorktop = ((GadgetWorktop) gadget.getContent());
|
||||||
while (it.hasNext()) {
|
gadgetWorktop.addWorktopOptions(new int[] {187});
|
||||||
var active = it.next();
|
gadgetWorktop.setOnSelectWorktopOptionEvent(
|
||||||
active.onTick();
|
(GadgetWorktop context, int option) -> {
|
||||||
if (active.getPass()) {
|
BlossomActivity activity;
|
||||||
EntityGadget chest = active.getChest();
|
EntityGadget entityGadget = context.getGadget();
|
||||||
scene.addEntity(chest);
|
synchronized (blossomActivities) {
|
||||||
scene.setChallenge(null);
|
for (BlossomActivity i : this.blossomActivities) {
|
||||||
activeChests.add(active);
|
if (i.getGadget() == entityGadget) {
|
||||||
it.remove();
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
int volume = 0;
|
||||||
|
IntList monsters = new IntArrayList();
|
||||||
public void recycleGadgetEntity(List<GameEntity> entities) {
|
while (true) {
|
||||||
for (var entity : entities) {
|
var remain = GameDepot.getBlossomConfig().getMonsterFightingVolume() - volume;
|
||||||
if (entity instanceof EntityGadget gadget) {
|
if (remain <= 0) {
|
||||||
createdEntity.remove(gadget);
|
break;
|
||||||
}
|
}
|
||||||
}
|
var rand = Utils.randomRange(1, 100);
|
||||||
notifyIcon();
|
if (rand > 85 && remain >= 50) { // 15% ,generate strong monster
|
||||||
}
|
monsters.addAll(getRandomMonstersID(2, 1));
|
||||||
|
volume += 50;
|
||||||
public void initBlossom(EntityGadget gadget) {
|
} else if (rand > 50 && remain >= 20) { // 35% ,generate normal monster
|
||||||
if (createdEntity.contains(gadget)) {
|
monsters.addAll(getRandomMonstersID(1, 1));
|
||||||
return;
|
volume += 20;
|
||||||
}
|
} else { // 50% ,generate weak monster
|
||||||
if (blossomConsumed.contains(gadget.getSpawnEntry())) {
|
monsters.addAll(getRandomMonstersID(0, 1));
|
||||||
return;
|
volume += 10;
|
||||||
}
|
}
|
||||||
var id = gadget.getGadgetId();
|
}
|
||||||
if (BlossomType.valueOf(id) == null) {
|
|
||||||
return;
|
Grasscutter.getLogger().info("Blossom Monsters:" + monsters);
|
||||||
}
|
|
||||||
gadget.buildContent();
|
activity = new BlossomActivity(entityGadget, monsters, -1, worldLevel);
|
||||||
gadget.setState(204);
|
blossomActivities.add(activity);
|
||||||
int worldLevel = getWorldLevel();
|
}
|
||||||
GadgetWorktop gadgetWorktop = ((GadgetWorktop) gadget.getContent());
|
entityGadget.updateState(201);
|
||||||
gadgetWorktop.addWorktopOptions(new int[] {187});
|
scene.setChallenge(activity.getChallenge());
|
||||||
gadgetWorktop.setOnSelectWorktopOptionEvent(
|
scene.removeEntity(entityGadget, VisionTypeOuterClass.VisionType.VISION_TYPE_REMOVE);
|
||||||
(GadgetWorktop context, int option) -> {
|
activity.start();
|
||||||
BlossomActivity activity;
|
return true;
|
||||||
EntityGadget entityGadget = context.getGadget();
|
});
|
||||||
synchronized (blossomActivities) {
|
createdEntity.add(gadget);
|
||||||
for (BlossomActivity i : this.blossomActivities) {
|
notifyIcon();
|
||||||
if (i.getGadget() == entityGadget) {
|
}
|
||||||
return false;
|
|
||||||
}
|
public void notifyIcon() {
|
||||||
}
|
final int wl = getWorldLevel();
|
||||||
|
final int worldLevel = (wl < 0) ? 0 : ((wl > 8) ? 8 : wl);
|
||||||
int volume = 0;
|
final var worldLevelData = GameData.getWorldLevelDataMap().get(worldLevel);
|
||||||
IntList monsters = new IntArrayList();
|
final int monsterLevel = (worldLevelData != null) ? worldLevelData.getMonsterLevel() : 1;
|
||||||
while (true) {
|
List<BlossomBriefInfoOuterClass.BlossomBriefInfo> blossoms = new ArrayList<>();
|
||||||
var remain = GameDepot.getBlossomConfig().getMonsterFightingVolume() - volume;
|
GameDepot.getSpawnLists()
|
||||||
if (remain <= 0) {
|
.forEach(
|
||||||
break;
|
(gridBlockId, spawnDataEntryList) -> {
|
||||||
}
|
int sceneId = gridBlockId.getSceneId();
|
||||||
var rand = Utils.randomRange(1, 100);
|
spawnDataEntryList.stream()
|
||||||
if (rand > 85 && remain >= 50) { // 15% ,generate strong monster
|
.map(SpawnDataEntry::getGroup)
|
||||||
monsters.addAll(getRandomMonstersID(2, 1));
|
.map(SpawnGroupEntry::getSpawns)
|
||||||
volume += 50;
|
.flatMap(List::stream)
|
||||||
} else if (rand > 50 && remain >= 20) { // 35% ,generate normal monster
|
.filter(spawn -> !blossomConsumed.contains(spawn))
|
||||||
monsters.addAll(getRandomMonstersID(1, 1));
|
.filter(spawn -> BlossomType.valueOf(spawn.getGadgetId()) != null)
|
||||||
volume += 20;
|
.forEach(
|
||||||
} else { // 50% ,generate weak monster
|
spawn -> {
|
||||||
monsters.addAll(getRandomMonstersID(0, 1));
|
var type = BlossomType.valueOf(spawn.getGadgetId());
|
||||||
volume += 10;
|
int previewReward = getPreviewReward(type, worldLevel);
|
||||||
}
|
blossoms.add(
|
||||||
}
|
BlossomBriefInfoOuterClass.BlossomBriefInfo.newBuilder()
|
||||||
|
.setSceneId(sceneId)
|
||||||
Grasscutter.getLogger().info("Blossom Monsters:" + monsters);
|
.setPos(spawn.getPos().toProto())
|
||||||
|
.setResin(20)
|
||||||
activity = new BlossomActivity(entityGadget, monsters, -1, worldLevel);
|
.setMonsterLevel(monsterLevel)
|
||||||
blossomActivities.add(activity);
|
.setRewardId(previewReward)
|
||||||
}
|
.setCircleCampId(type.getCircleCampId())
|
||||||
entityGadget.updateState(201);
|
.setRefreshId(
|
||||||
scene.setChallenge(activity.getChallenge());
|
type.getBlossomChestId()) // TODO: replace when using actual
|
||||||
scene.removeEntity(entityGadget, VisionTypeOuterClass.VisionType.VISION_TYPE_REMOVE);
|
// leylines
|
||||||
activity.start();
|
.build());
|
||||||
return true;
|
});
|
||||||
});
|
});
|
||||||
createdEntity.add(gadget);
|
scene.broadcastPacket(new PacketBlossomBriefInfoNotify(blossoms));
|
||||||
notifyIcon();
|
}
|
||||||
}
|
|
||||||
|
public int getWorldLevel() {
|
||||||
public void notifyIcon() {
|
return scene.getWorld().getWorldLevel();
|
||||||
final int wl = getWorldLevel();
|
}
|
||||||
final int worldLevel = (wl < 0) ? 0 : ((wl > 8) ? 8 : wl);
|
|
||||||
final var worldLevelData = GameData.getWorldLevelDataMap().get(worldLevel);
|
private static Integer getPreviewReward(BlossomType type, int worldLevel) {
|
||||||
final int monsterLevel = (worldLevelData != null) ? worldLevelData.getMonsterLevel() : 1;
|
// TODO: blossoms should be based on their city
|
||||||
List<BlossomBriefInfoOuterClass.BlossomBriefInfo> blossoms = new ArrayList<>();
|
if (type == null) {
|
||||||
GameDepot.getSpawnLists()
|
Grasscutter.getLogger().error("Illegal blossom type {}", type);
|
||||||
.forEach(
|
return null;
|
||||||
(gridBlockId, spawnDataEntryList) -> {
|
}
|
||||||
int sceneId = gridBlockId.getSceneId();
|
|
||||||
spawnDataEntryList.stream()
|
int blossomChestId = type.getBlossomChestId();
|
||||||
.map(SpawnDataEntry::getGroup)
|
var dataMap = GameData.getBlossomRefreshExcelConfigDataMap();
|
||||||
.map(SpawnGroupEntry::getSpawns)
|
for (var data : dataMap.values()) {
|
||||||
.flatMap(List::stream)
|
if (blossomChestId == data.getBlossomChestId()) {
|
||||||
.filter(spawn -> !blossomConsumed.contains(spawn))
|
var dropVecList = data.getDropVec();
|
||||||
.filter(spawn -> BlossomType.valueOf(spawn.getGadgetId()) != null)
|
if (worldLevel > dropVecList.length) {
|
||||||
.forEach(
|
Grasscutter.getLogger().error("Illegal world level {}", worldLevel);
|
||||||
spawn -> {
|
return null;
|
||||||
var type = BlossomType.valueOf(spawn.getGadgetId());
|
}
|
||||||
int previewReward = getPreviewReward(type, worldLevel);
|
return dropVecList[worldLevel].getPreviewReward();
|
||||||
blossoms.add(
|
}
|
||||||
BlossomBriefInfoOuterClass.BlossomBriefInfo.newBuilder()
|
}
|
||||||
.setSceneId(sceneId)
|
Grasscutter.getLogger().error("Cannot find blossom type {}", type);
|
||||||
.setPos(spawn.getPos().toProto())
|
return null;
|
||||||
.setResin(20)
|
}
|
||||||
.setMonsterLevel(monsterLevel)
|
|
||||||
.setRewardId(previewReward)
|
private static RewardPreviewData getRewardList(BlossomType type, int worldLevel) {
|
||||||
.setCircleCampId(type.getCircleCampId())
|
Integer previewReward = getPreviewReward(type, worldLevel);
|
||||||
.setRefreshId(
|
if (previewReward == null) return null;
|
||||||
type.getBlossomChestId()) // TODO: replace when using actual
|
return GameData.getRewardPreviewDataMap().get((int) previewReward);
|
||||||
// leylines
|
}
|
||||||
.build());
|
|
||||||
});
|
public List<GameItem> onReward(Player player, EntityGadget chest, boolean useCondensedResin) {
|
||||||
});
|
var resinManager = player.getResinManager();
|
||||||
scene.broadcastPacket(new PacketBlossomBriefInfoNotify(blossoms));
|
synchronized (activeChests) {
|
||||||
}
|
var it = activeChests.iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
public int getWorldLevel() {
|
var activeChest = it.next();
|
||||||
return scene.getWorld().getWorldLevel();
|
if (activeChest.getChest() == chest) {
|
||||||
}
|
boolean pay =
|
||||||
|
useCondensedResin ? resinManager.useCondensedResin(1) : resinManager.useResin(20);
|
||||||
public List<GameItem> onReward(Player player, EntityGadget chest, boolean useCondensedResin) {
|
if (pay) {
|
||||||
var resinManager = player.getResinManager();
|
int worldLevel = getWorldLevel();
|
||||||
synchronized (activeChests) {
|
List<GameItem> items = new ArrayList<>();
|
||||||
var it = activeChests.iterator();
|
var gadget = activeChest.getGadget();
|
||||||
while (it.hasNext()) {
|
var type = BlossomType.valueOf(gadget.getGadgetId());
|
||||||
var activeChest = it.next();
|
RewardPreviewData blossomRewards = getRewardList(type, worldLevel);
|
||||||
if (activeChest.getChest() == chest) {
|
if (blossomRewards == null) {
|
||||||
boolean pay =
|
Grasscutter.getLogger()
|
||||||
useCondensedResin ? resinManager.useCondensedResin(1) : resinManager.useResin(20);
|
.error("Blossom could not support world level : " + worldLevel);
|
||||||
if (pay) {
|
return null;
|
||||||
int worldLevel = getWorldLevel();
|
}
|
||||||
List<GameItem> items = new ArrayList<>();
|
var rewards = blossomRewards.getPreviewItems();
|
||||||
var gadget = activeChest.getGadget();
|
for (ItemParamData blossomReward : rewards) {
|
||||||
var type = BlossomType.valueOf(gadget.getGadgetId());
|
int rewardCount = blossomReward.getCount();
|
||||||
RewardPreviewData blossomRewards = getRewardList(type, worldLevel);
|
if (useCondensedResin) {
|
||||||
if (blossomRewards == null) {
|
rewardCount += blossomReward.getCount(); // Double!
|
||||||
Grasscutter.getLogger()
|
}
|
||||||
.error("Blossom could not support world level : " + worldLevel);
|
items.add(new GameItem(blossomReward.getItemId(), rewardCount));
|
||||||
return null;
|
}
|
||||||
}
|
it.remove();
|
||||||
var rewards = blossomRewards.getPreviewItems();
|
recycleGadgetEntity(List.of(gadget));
|
||||||
for (ItemParamData blossomReward : rewards) {
|
blossomConsumed.add(gadget.getSpawnEntry());
|
||||||
int rewardCount = blossomReward.getCount();
|
return items;
|
||||||
if (useCondensedResin) {
|
}
|
||||||
rewardCount += blossomReward.getCount(); // Double!
|
return null;
|
||||||
}
|
}
|
||||||
items.add(new GameItem(blossomReward.getItemId(), rewardCount));
|
}
|
||||||
}
|
}
|
||||||
it.remove();
|
return null;
|
||||||
recycleGadgetEntity(List.of(gadget));
|
}
|
||||||
blossomConsumed.add(gadget.getSpawnEntry());
|
|
||||||
return items;
|
public static IntList getRandomMonstersID(int difficulty, int count) {
|
||||||
}
|
IntList result = new IntArrayList();
|
||||||
return null;
|
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 null;
|
}
|
||||||
}
|
return result;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,163 +1,163 @@
|
|||||||
package emu.grasscutter.game.managers.cooking;
|
package emu.grasscutter.game.managers.cooking;
|
||||||
|
|
||||||
import emu.grasscutter.data.GameData;
|
import emu.grasscutter.data.GameData;
|
||||||
import emu.grasscutter.data.common.ItemParamData;
|
import emu.grasscutter.data.common.ItemParamData;
|
||||||
import emu.grasscutter.data.excels.CompoundData;
|
import emu.grasscutter.data.excels.CompoundData;
|
||||||
import emu.grasscutter.game.inventory.GameItem;
|
import emu.grasscutter.game.inventory.GameItem;
|
||||||
import emu.grasscutter.game.player.BasePlayerManager;
|
import emu.grasscutter.game.player.BasePlayerManager;
|
||||||
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.net.proto.CompoundQueueDataOuterClass.CompoundQueueData;
|
import emu.grasscutter.net.proto.CompoundQueueDataOuterClass.CompoundQueueData;
|
||||||
import emu.grasscutter.net.proto.GetCompoundDataReqOuterClass.GetCompoundDataReq;
|
import emu.grasscutter.net.proto.GetCompoundDataReqOuterClass.GetCompoundDataReq;
|
||||||
import emu.grasscutter.net.proto.ItemParamOuterClass.ItemParam;
|
import emu.grasscutter.net.proto.ItemParamOuterClass.ItemParam;
|
||||||
import emu.grasscutter.net.proto.PlayerCompoundMaterialReqOuterClass.PlayerCompoundMaterialReq;
|
import emu.grasscutter.net.proto.PlayerCompoundMaterialReqOuterClass.PlayerCompoundMaterialReq;
|
||||||
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
|
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
|
||||||
import emu.grasscutter.net.proto.TakeCompoundOutputReqOuterClass.TakeCompoundOutputReq;
|
import emu.grasscutter.net.proto.TakeCompoundOutputReqOuterClass.TakeCompoundOutputReq;
|
||||||
import emu.grasscutter.server.packet.send.PackageTakeCompoundOutputRsp;
|
import emu.grasscutter.server.packet.send.PackageTakeCompoundOutputRsp;
|
||||||
import emu.grasscutter.server.packet.send.PacketCompoundDataNotify;
|
import emu.grasscutter.server.packet.send.PacketCompoundDataNotify;
|
||||||
import emu.grasscutter.server.packet.send.PacketGetCompoundDataRsp;
|
import emu.grasscutter.server.packet.send.PacketGetCompoundDataRsp;
|
||||||
import emu.grasscutter.server.packet.send.PacketPlayerCompoundMaterialRsp;
|
import emu.grasscutter.server.packet.send.PacketPlayerCompoundMaterialRsp;
|
||||||
import emu.grasscutter.utils.Utils;
|
import emu.grasscutter.utils.Utils;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class CookingCompoundManager extends BasePlayerManager {
|
public class CookingCompoundManager extends BasePlayerManager {
|
||||||
private static Set<Integer> defaultUnlockedCompounds;
|
private static Set<Integer> defaultUnlockedCompounds;
|
||||||
private static Map<Integer, Set<Integer>> compoundGroups;
|
private static Map<Integer, Set<Integer>> compoundGroups;
|
||||||
// TODO:bind it to player
|
// TODO:bind it to player
|
||||||
private static Set<Integer> unlocked;
|
private static Set<Integer> unlocked;
|
||||||
|
|
||||||
public CookingCompoundManager(Player player) {
|
public CookingCompoundManager(Player player) {
|
||||||
super(player);
|
super(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void initialize() {
|
public static void initialize() {
|
||||||
defaultUnlockedCompounds = new HashSet<>();
|
defaultUnlockedCompounds = new HashSet<>();
|
||||||
compoundGroups = new HashMap<>();
|
compoundGroups = new HashMap<>();
|
||||||
GameData.getCompoundDataMap()
|
GameData.getCompoundDataMap()
|
||||||
.forEach(
|
.forEach(
|
||||||
(id, compound) -> {
|
(id, compound) -> {
|
||||||
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.
|
||||||
unlocked = new HashSet<>(defaultUnlockedCompounds);
|
unlocked = new HashSet<>(defaultUnlockedCompounds);
|
||||||
if (compoundGroups.containsKey(3)) // Avoid NPE from Resources error
|
if (compoundGroups.containsKey(3)) // Avoid NPE from Resources error
|
||||||
unlocked.addAll(compoundGroups.get(3));
|
unlocked.addAll(compoundGroups.get(3));
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized List<CompoundQueueData> getCompoundQueueData() {
|
private synchronized List<CompoundQueueData> getCompoundQueueData() {
|
||||||
List<CompoundQueueData> compoundQueueData =
|
List<CompoundQueueData> compoundQueueData =
|
||||||
new ArrayList<>(player.getActiveCookCompounds().size());
|
new ArrayList<>(player.getActiveCookCompounds().size());
|
||||||
int currentTime = Utils.getCurrentSeconds();
|
int currentTime = Utils.getCurrentSeconds();
|
||||||
for (var item : player.getActiveCookCompounds().values()) {
|
for (var item : player.getActiveCookCompounds().values()) {
|
||||||
var data =
|
var data =
|
||||||
CompoundQueueData.newBuilder()
|
CompoundQueueData.newBuilder()
|
||||||
.setCompoundId(item.getCompoundId())
|
.setCompoundId(item.getCompoundId())
|
||||||
.setOutputCount(item.getOutputCount(currentTime))
|
.setOutputCount(item.getOutputCount(currentTime))
|
||||||
.setOutputTime(item.getOutputTime(currentTime))
|
.setOutputTime(item.getOutputTime(currentTime))
|
||||||
.setWaitCount(item.getWaitCount(currentTime))
|
.setWaitCount(item.getWaitCount(currentTime))
|
||||||
.build();
|
.build();
|
||||||
compoundQueueData.add(data);
|
compoundQueueData.add(data);
|
||||||
}
|
}
|
||||||
return compoundQueueData;
|
return compoundQueueData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void handleGetCompoundDataReq(GetCompoundDataReq req) {
|
public synchronized void handleGetCompoundDataReq(GetCompoundDataReq req) {
|
||||||
player.sendPacket(new PacketGetCompoundDataRsp(unlocked, getCompoundQueueData()));
|
player.sendPacket(new PacketGetCompoundDataRsp(unlocked, getCompoundQueueData()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void handlePlayerCompoundMaterialReq(PlayerCompoundMaterialReq req) {
|
public synchronized void handlePlayerCompoundMaterialReq(PlayerCompoundMaterialReq req) {
|
||||||
int id = req.getCompoundId(), count = req.getCount();
|
int id = req.getCompoundId(), count = req.getCount();
|
||||||
CompoundData compound = GameData.getCompoundDataMap().get(id);
|
CompoundData compound = GameData.getCompoundDataMap().get(id);
|
||||||
var activeCompounds = player.getActiveCookCompounds();
|
var activeCompounds = player.getActiveCookCompounds();
|
||||||
|
|
||||||
// check whether the compound is available
|
// check whether the compound is available
|
||||||
// TODO:add other compounds,see my pr for detail
|
// TODO:add other compounds,see my pr for detail
|
||||||
if (!unlocked.contains(id)) {
|
if (!unlocked.contains(id)) {
|
||||||
player.sendPacket(new PacketPlayerCompoundMaterialRsp(Retcode.RET_FAIL_VALUE));
|
player.sendPacket(new PacketPlayerCompoundMaterialRsp(Retcode.RET_FAIL_VALUE));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// check whether the queue is full
|
// check whether the queue is full
|
||||||
if (activeCompounds.containsKey(id)
|
if (activeCompounds.containsKey(id)
|
||||||
&& activeCompounds.get(id).getTotalCount() + count > compound.getQueueSize()) {
|
&& activeCompounds.get(id).getTotalCount() + count > compound.getQueueSize()) {
|
||||||
player.sendPacket(new PacketPlayerCompoundMaterialRsp(Retcode.RET_COMPOUND_QUEUE_FULL_VALUE));
|
player.sendPacket(new PacketPlayerCompoundMaterialRsp(Retcode.RET_COMPOUND_QUEUE_FULL_VALUE));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// try to consume raw materials
|
// try to consume raw materials
|
||||||
if (!player.getInventory().payItems(compound.getInputVec(), count)) {
|
if (!player.getInventory().payItems(compound.getInputVec(), count)) {
|
||||||
// TODO:I'm not sure whether retcode is correct.
|
// TODO:I'm not sure whether retcode is correct.
|
||||||
player.sendPacket(
|
player.sendPacket(
|
||||||
new PacketPlayerCompoundMaterialRsp(Retcode.RET_ITEM_COUNT_NOT_ENOUGH_VALUE));
|
new PacketPlayerCompoundMaterialRsp(Retcode.RET_ITEM_COUNT_NOT_ENOUGH_VALUE));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ActiveCookCompoundData c;
|
ActiveCookCompoundData c;
|
||||||
int currentTime = Utils.getCurrentSeconds();
|
int currentTime = Utils.getCurrentSeconds();
|
||||||
if (activeCompounds.containsKey(id)) {
|
if (activeCompounds.containsKey(id)) {
|
||||||
c = activeCompounds.get(id);
|
c = activeCompounds.get(id);
|
||||||
c.addCompound(count, currentTime);
|
c.addCompound(count, currentTime);
|
||||||
} else {
|
} else {
|
||||||
c = new ActiveCookCompoundData(id, compound.getCostTime(), count, currentTime);
|
c = new ActiveCookCompoundData(id, compound.getCostTime(), count, currentTime);
|
||||||
activeCompounds.put(id, c);
|
activeCompounds.put(id, c);
|
||||||
}
|
}
|
||||||
var data =
|
var data =
|
||||||
CompoundQueueData.newBuilder()
|
CompoundQueueData.newBuilder()
|
||||||
.setCompoundId(id)
|
.setCompoundId(id)
|
||||||
.setOutputCount(c.getOutputCount(currentTime))
|
.setOutputCount(c.getOutputCount(currentTime))
|
||||||
.setOutputTime(c.getOutputTime(currentTime))
|
.setOutputTime(c.getOutputTime(currentTime))
|
||||||
.setWaitCount(c.getWaitCount(currentTime))
|
.setWaitCount(c.getWaitCount(currentTime))
|
||||||
.build();
|
.build();
|
||||||
player.sendPacket(new PacketPlayerCompoundMaterialRsp(data));
|
player.sendPacket(new PacketPlayerCompoundMaterialRsp(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void handleTakeCompoundOutputReq(TakeCompoundOutputReq req) {
|
public synchronized void handleTakeCompoundOutputReq(TakeCompoundOutputReq req) {
|
||||||
// Client won't set compound_id and will set group_id instead.
|
// Client won't set compound_id and will set group_id instead.
|
||||||
int groupId = req.getCompoundGroupId();
|
int groupId = req.getCompoundGroupId();
|
||||||
var activeCompounds = player.getActiveCookCompounds();
|
var activeCompounds = player.getActiveCookCompounds();
|
||||||
int now = Utils.getCurrentSeconds();
|
int now = Utils.getCurrentSeconds();
|
||||||
// check available queues
|
// check available queues
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
Map<Integer, GameItem> allRewards = new HashMap<>();
|
Map<Integer, GameItem> allRewards = new HashMap<>();
|
||||||
for (int id : compoundGroups.get(groupId)) {
|
for (int id : compoundGroups.get(groupId)) {
|
||||||
if (!activeCompounds.containsKey(id)) continue;
|
if (!activeCompounds.containsKey(id)) continue;
|
||||||
int quantity = activeCompounds.get(id).takeCompound(now);
|
int quantity = activeCompounds.get(id).takeCompound(now);
|
||||||
if (activeCompounds.get(id).getTotalCount() == 0) activeCompounds.remove(id);
|
if (activeCompounds.get(id).getTotalCount() == 0) activeCompounds.remove(id);
|
||||||
if (quantity == 0) continue;
|
if (quantity == 0) continue;
|
||||||
List<ItemParamData> rewards = GameData.getCompoundDataMap().get(id).getOutputVec();
|
List<ItemParamData> rewards = GameData.getCompoundDataMap().get(id).getOutputVec();
|
||||||
for (var i : rewards) {
|
for (var i : rewards) {
|
||||||
if (i.getId() == 0) continue;
|
if (i.getId() == 0) continue;
|
||||||
if (allRewards.containsKey(i.getId())) {
|
if (allRewards.containsKey(i.getId())) {
|
||||||
GameItem item = allRewards.get(i.getId());
|
GameItem item = allRewards.get(i.getId());
|
||||||
item.setCount(item.getCount() + i.getCount() * quantity);
|
item.setCount(item.getCount() + i.getCount() * quantity);
|
||||||
} else {
|
} else {
|
||||||
allRewards.put(i.getId(), new GameItem(i.getId(), i.getCount() * quantity));
|
allRewards.put(i.getId(), new GameItem(i.getId(), i.getCount() * quantity));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
// give player the rewards
|
// give player the rewards
|
||||||
if (success) {
|
if (success) {
|
||||||
player.getInventory().addItems(allRewards.values(), ActionReason.Compound);
|
player.getInventory().addItems(allRewards.values(), ActionReason.Compound);
|
||||||
player.sendPacket(
|
player.sendPacket(
|
||||||
new PackageTakeCompoundOutputRsp(
|
new PackageTakeCompoundOutputRsp(
|
||||||
allRewards.values().stream()
|
allRewards.values().stream()
|
||||||
.map(
|
.map(
|
||||||
i ->
|
i ->
|
||||||
ItemParam.newBuilder()
|
ItemParam.newBuilder()
|
||||||
.setItemId(i.getItemId())
|
.setItemId(i.getItemId())
|
||||||
.setCount(i.getCount())
|
.setCount(i.getCount())
|
||||||
.build())
|
.build())
|
||||||
.toList(),
|
.toList(),
|
||||||
Retcode.RET_SUCC_VALUE));
|
Retcode.RET_SUCC_VALUE));
|
||||||
} else {
|
} else {
|
||||||
player.sendPacket(
|
player.sendPacket(
|
||||||
new PackageTakeCompoundOutputRsp(null, Retcode.RET_COMPOUND_NOT_FINISH_VALUE));
|
new PackageTakeCompoundOutputRsp(null, Retcode.RET_COMPOUND_NOT_FINISH_VALUE));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onPlayerLogin() {
|
public void onPlayerLogin() {
|
||||||
player.sendPacket(new PacketCompoundDataNotify(unlocked, getCompoundQueueData()));
|
player.sendPacket(new PacketCompoundDataNotify(unlocked, getCompoundQueueData()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,420 +1,418 @@
|
|||||||
package emu.grasscutter.game.managers.energy;
|
package emu.grasscutter.game.managers.energy;
|
||||||
|
|
||||||
import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
|
import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
|
||||||
|
|
||||||
import com.google.protobuf.InvalidProtocolBufferException;
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.data.DataLoader;
|
import emu.grasscutter.data.DataLoader;
|
||||||
import emu.grasscutter.data.GameData;
|
import emu.grasscutter.data.GameData;
|
||||||
import emu.grasscutter.data.excels.ItemData;
|
import emu.grasscutter.data.excels.ItemData;
|
||||||
import emu.grasscutter.data.excels.avatar.AvatarSkillDepotData;
|
import emu.grasscutter.data.excels.avatar.AvatarSkillDepotData;
|
||||||
import emu.grasscutter.data.excels.monster.MonsterData.HpDrops;
|
import emu.grasscutter.data.excels.monster.MonsterData.HpDrops;
|
||||||
import emu.grasscutter.game.avatar.Avatar;
|
import emu.grasscutter.game.avatar.Avatar;
|
||||||
import emu.grasscutter.game.entity.*;
|
import emu.grasscutter.game.entity.*;
|
||||||
import emu.grasscutter.game.player.BasePlayerManager;
|
import emu.grasscutter.game.player.BasePlayerManager;
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.game.props.ElementType;
|
import emu.grasscutter.game.props.ElementType;
|
||||||
import emu.grasscutter.game.props.FightProperty;
|
import emu.grasscutter.game.props.FightProperty;
|
||||||
import emu.grasscutter.game.props.MonsterType;
|
import emu.grasscutter.game.props.MonsterType;
|
||||||
import emu.grasscutter.game.props.WeaponType;
|
import emu.grasscutter.game.props.WeaponType;
|
||||||
import emu.grasscutter.net.proto.AbilityActionGenerateElemBallOuterClass.AbilityActionGenerateElemBall;
|
import emu.grasscutter.net.proto.AbilityActionGenerateElemBallOuterClass.AbilityActionGenerateElemBall;
|
||||||
import emu.grasscutter.net.proto.AbilityIdentifierOuterClass.AbilityIdentifier;
|
import emu.grasscutter.net.proto.AbilityIdentifierOuterClass.AbilityIdentifier;
|
||||||
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry;
|
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry;
|
||||||
import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult;
|
import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult;
|
||||||
import emu.grasscutter.net.proto.ChangeEnergyReasonOuterClass.ChangeEnergyReason;
|
import emu.grasscutter.net.proto.ChangeEnergyReasonOuterClass.ChangeEnergyReason;
|
||||||
import emu.grasscutter.net.proto.EvtBeingHitInfoOuterClass.EvtBeingHitInfo;
|
import emu.grasscutter.net.proto.EvtBeingHitInfoOuterClass.EvtBeingHitInfo;
|
||||||
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
|
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
|
||||||
import emu.grasscutter.server.game.GameSession;
|
import emu.grasscutter.server.game.GameSession;
|
||||||
import emu.grasscutter.utils.Position;
|
import emu.grasscutter.utils.Position;
|
||||||
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.objects.Object2IntMap;
|
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.ThreadLocalRandom;
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
public class EnergyManager extends BasePlayerManager {
|
public class EnergyManager extends BasePlayerManager {
|
||||||
private static final Int2ObjectMap<List<EnergyDropInfo>> energyDropData =
|
private static final Int2ObjectMap<List<EnergyDropInfo>> energyDropData =
|
||||||
new Int2ObjectOpenHashMap<>();
|
new Int2ObjectOpenHashMap<>();
|
||||||
private static final Int2ObjectMap<List<SkillParticleGenerationInfo>>
|
private static final Int2ObjectMap<List<SkillParticleGenerationInfo>>
|
||||||
skillParticleGenerationData = new Int2ObjectOpenHashMap<>();
|
skillParticleGenerationData = new Int2ObjectOpenHashMap<>();
|
||||||
private final Object2IntMap<EntityAvatar> avatarNormalProbabilities;
|
private final Object2IntMap<EntityAvatar> avatarNormalProbabilities;
|
||||||
@Getter private boolean energyUsage; // Should energy usage be enabled for this player?
|
@Getter private boolean energyUsage; // Should energy usage be enabled for this player?
|
||||||
|
|
||||||
public EnergyManager(Player player) {
|
public EnergyManager(Player player) {
|
||||||
super(player);
|
super(player);
|
||||||
this.avatarNormalProbabilities = new Object2IntOpenHashMap<>();
|
this.avatarNormalProbabilities = new Object2IntOpenHashMap<>();
|
||||||
this.energyUsage = GAME_OPTIONS.energyUsage;
|
this.energyUsage = GAME_OPTIONS.energyUsage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void initialize() {
|
public static void initialize() {
|
||||||
// Read the data we need for monster energy drops.
|
// Read the data we need for monster energy drops.
|
||||||
try {
|
try {
|
||||||
DataLoader.loadList("EnergyDrop.json", EnergyDropEntry.class)
|
DataLoader.loadList("EnergyDrop.json", EnergyDropEntry.class)
|
||||||
.forEach(
|
.forEach(
|
||||||
entry -> {
|
entry -> {
|
||||||
energyDropData.put(entry.getDropId(), entry.getDropList());
|
energyDropData.put(entry.getDropId(), entry.getDropList());
|
||||||
});
|
});
|
||||||
|
|
||||||
Grasscutter.getLogger().debug("Energy drop data successfully loaded.");
|
Grasscutter.getLogger().debug("Energy drop data successfully loaded.");
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Grasscutter.getLogger().error("Unable to load energy drop data.", ex);
|
Grasscutter.getLogger().error("Unable to load energy drop data.", ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the data for particle generation from skills
|
// Read the data for particle generation from skills
|
||||||
try {
|
try {
|
||||||
DataLoader.loadList("SkillParticleGeneration.json", SkillParticleGenerationEntry.class)
|
DataLoader.loadList("SkillParticleGeneration.json", SkillParticleGenerationEntry.class)
|
||||||
.forEach(
|
.forEach(
|
||||||
entry -> {
|
entry -> {
|
||||||
skillParticleGenerationData.put(entry.getAvatarId(), entry.getAmountList());
|
skillParticleGenerationData.put(entry.getAvatarId(), entry.getAmountList());
|
||||||
});
|
});
|
||||||
|
|
||||||
Grasscutter.getLogger().debug("Skill particle generation data successfully loaded.");
|
Grasscutter.getLogger().debug("Skill particle generation data successfully loaded.");
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Grasscutter.getLogger().error("Unable to load skill particle generation data data.", ex);
|
Grasscutter.getLogger().error("Unable to load skill particle generation data data.", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Particle creation for elemental skills. */
|
/** Particle creation for elemental skills. */
|
||||||
private int getBallCountForAvatar(int avatarId) {
|
private int getBallCountForAvatar(int avatarId) {
|
||||||
// We default to two particles.
|
// We default to two particles.
|
||||||
int count = 2;
|
int count = 2;
|
||||||
|
|
||||||
// If we don't have any data for this avatar, stop.
|
// If we don't have any data for this avatar, stop.
|
||||||
if (!skillParticleGenerationData.containsKey(avatarId)) {
|
if (!skillParticleGenerationData.containsKey(avatarId)) {
|
||||||
Grasscutter.getLogger().warn("No particle generation data for avatarId {} found.", avatarId);
|
Grasscutter.getLogger().warn("No particle generation data for avatarId {} found.", avatarId);
|
||||||
}
|
}
|
||||||
// If we do have data, roll for how many particles we should generate.
|
// If we do have data, roll for how many particles we should generate.
|
||||||
else {
|
else {
|
||||||
int roll = ThreadLocalRandom.current().nextInt(0, 100);
|
int roll = ThreadLocalRandom.current().nextInt(0, 100);
|
||||||
int percentageStack = 0;
|
int percentageStack = 0;
|
||||||
for (SkillParticleGenerationInfo info : skillParticleGenerationData.get(avatarId)) {
|
for (SkillParticleGenerationInfo info : skillParticleGenerationData.get(avatarId)) {
|
||||||
int chance = info.getChance();
|
int chance = info.getChance();
|
||||||
percentageStack += chance;
|
percentageStack += chance;
|
||||||
if (roll < percentageStack) {
|
if (roll < percentageStack) {
|
||||||
count = info.getValue();
|
count = info.getValue();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Done.
|
// Done.
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getBallIdForElement(ElementType element) {
|
private int getBallIdForElement(ElementType element) {
|
||||||
// If we have no element, we default to an element-less particle.
|
// If we have no element, we default to an element-less particle.
|
||||||
if (element == null) {
|
if (element == null) {
|
||||||
return 2024;
|
return 2024;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, we determine the particle's ID based on the element.
|
// Otherwise, we determine the particle's ID based on the element.
|
||||||
return switch (element) {
|
return switch (element) {
|
||||||
case Fire -> 2017;
|
case Fire -> 2017;
|
||||||
case Water -> 2018;
|
case Water -> 2018;
|
||||||
case Grass -> 2019;
|
case Grass -> 2019;
|
||||||
case Electric -> 2020;
|
case Electric -> 2020;
|
||||||
case Wind -> 2021;
|
case Wind -> 2021;
|
||||||
case Ice -> 2022;
|
case Ice -> 2022;
|
||||||
case Rock -> 2023;
|
case Rock -> 2023;
|
||||||
default -> 2024;
|
default -> 2024;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleGenerateElemBall(AbilityInvokeEntry invoke)
|
public void handleGenerateElemBall(AbilityInvokeEntry invoke)
|
||||||
throws InvalidProtocolBufferException {
|
throws InvalidProtocolBufferException {
|
||||||
// ToDo:
|
// ToDo:
|
||||||
// This is also called when a weapon like Favonius Warbow etc. creates energy through its
|
// This is also called when a weapon like Favonius Warbow etc. creates energy through its
|
||||||
// passive.
|
// passive.
|
||||||
// We are not handling this correctly at the moment.
|
// We are not handling this correctly at the moment.
|
||||||
|
|
||||||
// Get action info.
|
// Get action info.
|
||||||
AbilityActionGenerateElemBall action =
|
AbilityActionGenerateElemBall action =
|
||||||
AbilityActionGenerateElemBall.parseFrom(invoke.getAbilityData());
|
AbilityActionGenerateElemBall.parseFrom(invoke.getAbilityData());
|
||||||
if (action == null) {
|
if (action == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default to an elementless particle.
|
// Default to an elementless particle.
|
||||||
int itemId = 2024;
|
int itemId = 2024;
|
||||||
|
|
||||||
// Generate 2 particles by default.
|
// Generate 2 particles by default.
|
||||||
int amount = 2;
|
int amount = 2;
|
||||||
|
|
||||||
// Try to get the casting avatar from the player's party.
|
// Try to get the casting avatar from the player's party.
|
||||||
Optional<EntityAvatar> avatarEntity =
|
Optional<EntityAvatar> avatarEntity =
|
||||||
this.getCastingAvatarEntityForEnergy(invoke.getEntityId());
|
this.getCastingAvatarEntityForEnergy(invoke.getEntityId());
|
||||||
|
|
||||||
// Bug: invokes twice sometimes, Ayato, Keqing
|
// Bug: invokes twice sometimes, Ayato, Keqing
|
||||||
// ToDo: deal with press, hold difference. deal with charge(Beidou, Yunjin)
|
// ToDo: deal with press, hold difference. deal with charge(Beidou, Yunjin)
|
||||||
if (avatarEntity.isPresent()) {
|
if (avatarEntity.isPresent()) {
|
||||||
Avatar avatar = avatarEntity.get().getAvatar();
|
Avatar avatar = avatarEntity.get().getAvatar();
|
||||||
|
|
||||||
if (avatar != null) {
|
if (avatar != null) {
|
||||||
int avatarId = avatar.getAvatarId();
|
int avatarId = avatar.getAvatarId();
|
||||||
AvatarSkillDepotData skillDepotData = avatar.getSkillDepot();
|
AvatarSkillDepotData skillDepotData = avatar.getSkillDepot();
|
||||||
|
|
||||||
// Determine how many particles we need to create for this avatar.
|
// Determine how many particles we need to create for this avatar.
|
||||||
amount = this.getBallCountForAvatar(avatarId);
|
amount = this.getBallCountForAvatar(avatarId);
|
||||||
|
|
||||||
// Determine the avatar's element, and based on that the ID of the
|
// Determine the avatar's element, and based on that the ID of the
|
||||||
// particles we have to generate.
|
// particles we have to generate.
|
||||||
if (skillDepotData != null) {
|
if (skillDepotData != null) {
|
||||||
ElementType element = skillDepotData.getElementType();
|
ElementType element = skillDepotData.getElementType();
|
||||||
itemId = this.getBallIdForElement(element);
|
itemId = this.getBallIdForElement(element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate the particles.
|
// Generate the particles.
|
||||||
var pos = new Position(action.getPos());
|
var pos = new Position(action.getPos());
|
||||||
for (int i = 0; i < amount; i++) {
|
for (int i = 0; i < amount; i++) {
|
||||||
this.generateElemBall(itemId, pos, 1);
|
this.generateElemBall(itemId, pos, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Energy generation for NAs/CAs.
|
* Energy generation for NAs/CAs.
|
||||||
*
|
*
|
||||||
* @param avatar The avatar.
|
* @param avatar The avatar.
|
||||||
*/
|
*/
|
||||||
private void generateEnergyForNormalAndCharged(EntityAvatar avatar) {
|
private void generateEnergyForNormalAndCharged(EntityAvatar avatar) {
|
||||||
// This logic is based on the descriptions given in
|
// This logic is based on the descriptions given in
|
||||||
// https://genshin-impact.fandom.com/wiki/Energy#Energy_Generated_by_Normal_Attacks
|
// https://genshin-impact.fandom.com/wiki/Energy#Energy_Generated_by_Normal_Attacks
|
||||||
// https://library.keqingmains.com/combat-mechanics/energy#auto-attacking
|
// https://library.keqingmains.com/combat-mechanics/energy#auto-attacking
|
||||||
// Those descriptions are lacking in some information, so this implementation most likely
|
// Those descriptions are lacking in some information, so this implementation most likely
|
||||||
// does not fully replicate the behavior of the official server. Open questions:
|
// does not fully replicate the behavior of the official server. Open questions:
|
||||||
// - Does the probability for a character reset after some time?
|
// - Does the probability for a character reset after some time?
|
||||||
// - Does the probability for a character reset when switching them out?
|
// - Does the probability for a character reset when switching them out?
|
||||||
// - Does this really count every individual hit separately?
|
// - Does this really count every individual hit separately?
|
||||||
|
|
||||||
// Get the avatar's weapon type.
|
// Get the avatar's weapon type.
|
||||||
WeaponType weaponType = avatar.getAvatar().getAvatarData().getWeaponType();
|
WeaponType weaponType = avatar.getAvatar().getAvatarData().getWeaponType();
|
||||||
|
|
||||||
// Check if we already have probability data for this avatar. If not, insert it.
|
// Check if we already have probability data for this avatar. If not, insert it.
|
||||||
if (!this.avatarNormalProbabilities.containsKey(avatar)) {
|
if (!this.avatarNormalProbabilities.containsKey(avatar)) {
|
||||||
this.avatarNormalProbabilities.put(avatar, weaponType.getEnergyGainInitialProbability());
|
this.avatarNormalProbabilities.put(avatar, weaponType.getEnergyGainInitialProbability());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Roll for energy.
|
// Roll for energy.
|
||||||
int currentProbability = this.avatarNormalProbabilities.getInt(avatar);
|
int currentProbability = this.avatarNormalProbabilities.getInt(avatar);
|
||||||
int roll = ThreadLocalRandom.current().nextInt(0, 100);
|
int roll = ThreadLocalRandom.current().nextInt(0, 100);
|
||||||
|
|
||||||
// If the player wins the roll, we increase the avatar's energy and reset the probability.
|
// If the player wins the roll, we increase the avatar's energy and reset the probability.
|
||||||
if (roll < currentProbability) {
|
if (roll < currentProbability) {
|
||||||
avatar.addEnergy(1.0f, PropChangeReason.PROP_CHANGE_REASON_ABILITY, true);
|
avatar.addEnergy(1.0f, PropChangeReason.PROP_CHANGE_REASON_ABILITY, true);
|
||||||
this.avatarNormalProbabilities.put(avatar, weaponType.getEnergyGainInitialProbability());
|
this.avatarNormalProbabilities.put(avatar, weaponType.getEnergyGainInitialProbability());
|
||||||
}
|
}
|
||||||
// Otherwise, we increase the probability for the next hit.
|
// Otherwise, we increase the probability for the next hit.
|
||||||
else {
|
else {
|
||||||
this.avatarNormalProbabilities.put(
|
this.avatarNormalProbabilities.put(
|
||||||
avatar, currentProbability + weaponType.getEnergyGainIncreaseProbability());
|
avatar, currentProbability + weaponType.getEnergyGainIncreaseProbability());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleAttackHit(EvtBeingHitInfo hitInfo) {
|
public void handleAttackHit(EvtBeingHitInfo hitInfo) {
|
||||||
// Get the attack result.
|
// Get the attack result.
|
||||||
AttackResult attackRes = hitInfo.getAttackResult();
|
AttackResult attackRes = hitInfo.getAttackResult();
|
||||||
|
|
||||||
// Make sure the attack was performed by the currently active avatar. If not, we ignore the hit.
|
// Make sure the attack was performed by the currently active avatar. If not, we ignore the hit.
|
||||||
Optional<EntityAvatar> attackerEntity =
|
Optional<EntityAvatar> attackerEntity =
|
||||||
this.getCastingAvatarEntityForEnergy(attackRes.getAttackerId());
|
this.getCastingAvatarEntityForEnergy(attackRes.getAttackerId());
|
||||||
if (attackerEntity.isEmpty()
|
if (attackerEntity.isEmpty()
|
||||||
|| this.player.getTeamManager().getCurrentAvatarEntity().getId()
|
|| this.player.getTeamManager().getCurrentAvatarEntity().getId()
|
||||||
!= attackerEntity.get().getId()) {
|
!= attackerEntity.get().getId()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the target is an actual enemy.
|
// Make sure the target is an actual enemy.
|
||||||
GameEntity targetEntity = this.player.getScene().getEntityById(attackRes.getDefenseId());
|
GameEntity targetEntity = this.player.getScene().getEntityById(attackRes.getDefenseId());
|
||||||
if (!(targetEntity instanceof EntityMonster targetMonster)) {
|
if (!(targetEntity instanceof EntityMonster targetMonster)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MonsterType targetType = targetMonster.getMonsterData().getType();
|
MonsterType targetType = targetMonster.getMonsterData().getType();
|
||||||
if (targetType != MonsterType.MONSTER_ORDINARY && targetType != MonsterType.MONSTER_BOSS) {
|
if (targetType != MonsterType.MONSTER_ORDINARY && targetType != MonsterType.MONSTER_BOSS) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the ability that caused this hit.
|
// Get the ability that caused this hit.
|
||||||
AbilityIdentifier ability = attackRes.getAbilityIdentifier();
|
AbilityIdentifier ability = attackRes.getAbilityIdentifier();
|
||||||
|
|
||||||
// Make sure there is no actual "ability" associated with the hit. For now, this is how we
|
// Make sure there is no actual "ability" associated with the hit. For now, this is how we
|
||||||
// identify normal and charged attacks. Note that this is not completely accurate:
|
// identify normal and charged attacks. Note that this is not completely accurate:
|
||||||
// - Many character's charged attacks have an ability associated with them. This means that,
|
// - Many character's charged attacks have an ability associated with them. This means that,
|
||||||
// for now, we don't identify charged attacks reliably.
|
// for now, we don't identify charged attacks reliably.
|
||||||
// - There might also be some cases where we incorrectly identify something as a normal or
|
// - There might also be some cases where we incorrectly identify something as a normal or
|
||||||
// charged attack that is not (Diluc's E?).
|
// charged attack that is not (Diluc's E?).
|
||||||
// - Catalyst normal attacks have an ability, so we don't handle those for now.
|
// - Catalyst normal attacks have an ability, so we don't handle those for now.
|
||||||
// ToDo: Fix all of that.
|
// ToDo: Fix all of that.
|
||||||
if (ability != AbilityIdentifier.getDefaultInstance()) {
|
if (ability != AbilityIdentifier.getDefaultInstance()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle the energy generation.
|
// Handle the energy generation.
|
||||||
this.generateEnergyForNormalAndCharged(attackerEntity.get());
|
this.generateEnergyForNormalAndCharged(attackerEntity.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Energy logic related to using skills.
|
* Energy logic related to using skills.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private void handleBurstCast(Avatar avatar, int skillId) {
|
private void handleBurstCast(Avatar avatar, int skillId) {
|
||||||
// Don't do anything if energy usage is disabled.
|
// Don't do anything if energy usage is disabled.
|
||||||
if (!GAME_OPTIONS.energyUsage || !this.energyUsage) {
|
if (!GAME_OPTIONS.energyUsage || !this.energyUsage) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the cast skill was a burst, consume energy.
|
// If the cast skill was a burst, consume energy.
|
||||||
if (avatar.getSkillDepot() != null && skillId == avatar.getSkillDepot().getEnergySkill()) {
|
if (avatar.getSkillDepot() != null && skillId == avatar.getSkillDepot().getEnergySkill()) {
|
||||||
avatar.getAsEntity().clearEnergy(ChangeEnergyReason.CHANGE_ENERGY_REASON_SKILL_START);
|
avatar.getAsEntity().clearEnergy(ChangeEnergyReason.CHANGE_ENERGY_REASON_SKILL_START);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleEvtDoSkillSuccNotify(GameSession session, int skillId, int casterId) {
|
public void handleEvtDoSkillSuccNotify(GameSession session, int skillId, int casterId) {
|
||||||
// Determine the entity that has cast the skill. Cancel if we can't find that avatar.
|
// Determine the entity that has cast the skill. Cancel if we can't find that avatar.
|
||||||
Optional<EntityAvatar> caster =
|
Optional<EntityAvatar> caster =
|
||||||
this.player.getTeamManager().getActiveTeam().stream()
|
this.player.getTeamManager().getActiveTeam().stream()
|
||||||
.filter(character -> character.getId() == casterId)
|
.filter(character -> character.getId() == casterId)
|
||||||
.findFirst();
|
.findFirst();
|
||||||
|
|
||||||
if (caster.isEmpty()) {
|
if (caster.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Avatar avatar = caster.get().getAvatar();
|
Avatar avatar = caster.get().getAvatar();
|
||||||
|
|
||||||
// Handle elemental burst.
|
// Handle elemental burst.
|
||||||
this.handleBurstCast(avatar, skillId);
|
this.handleBurstCast(avatar, skillId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Monster energy drops.
|
* Monster energy drops.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private void generateElemBallDrops(EntityMonster monster, int dropId) {
|
private void generateElemBallDrops(EntityMonster monster, int dropId) {
|
||||||
// Generate all drops specified for the given drop id.
|
// Generate all drops specified for the given drop id.
|
||||||
if (!energyDropData.containsKey(dropId)) {
|
if (!energyDropData.containsKey(dropId)) {
|
||||||
Grasscutter.getLogger().warn("No drop data for dropId {} found.", dropId);
|
Grasscutter.getLogger().warn("No drop data for dropId {} found.", dropId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (EnergyDropInfo info : energyDropData.get(dropId)) {
|
for (EnergyDropInfo info : energyDropData.get(dropId)) {
|
||||||
this.generateElemBall(info.getBallId(), monster.getPosition(), info.getCount());
|
this.generateElemBall(info.getBallId(), monster.getPosition(), info.getCount());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleMonsterEnergyDrop(
|
public void handleMonsterEnergyDrop(
|
||||||
EntityMonster monster, float hpBeforeDamage, float hpAfterDamage) {
|
EntityMonster monster, float hpBeforeDamage, float hpAfterDamage) {
|
||||||
// Make sure this is actually a monster.
|
// Make sure this is actually a monster.
|
||||||
// Note that some wildlife also has that type, like boars or birds.
|
// Note that some wildlife also has that type, like boars or birds.
|
||||||
MonsterType type = monster.getMonsterData().getType();
|
MonsterType type = monster.getMonsterData().getType();
|
||||||
if (type != MonsterType.MONSTER_ORDINARY && type != MonsterType.MONSTER_BOSS) {
|
if (type != MonsterType.MONSTER_ORDINARY && type != MonsterType.MONSTER_BOSS) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the HP thresholds for before and after the damage was taken.
|
// Calculate the HP thresholds for before and after the damage was taken.
|
||||||
float maxHp = monster.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
|
float maxHp = monster.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
|
||||||
float thresholdBefore = hpBeforeDamage / maxHp;
|
float thresholdBefore = hpBeforeDamage / maxHp;
|
||||||
float thresholdAfter = hpAfterDamage / maxHp;
|
float thresholdAfter = hpAfterDamage / maxHp;
|
||||||
|
|
||||||
// Determine the thresholds the monster has passed, and generate drops based on that.
|
// Determine the thresholds the monster has passed, and generate drops based on that.
|
||||||
for (HpDrops drop : monster.getMonsterData().getHpDrops()) {
|
for (HpDrops drop : monster.getMonsterData().getHpDrops()) {
|
||||||
if (drop.getDropId() == 0) {
|
if (drop.getDropId() == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
float threshold = drop.getHpPercent() / 100.0f;
|
float threshold = drop.getHpPercent() / 100.0f;
|
||||||
if (threshold < thresholdBefore && threshold >= thresholdAfter) {
|
if (threshold < thresholdBefore && threshold >= thresholdAfter) {
|
||||||
this.generateElemBallDrops(monster, drop.getDropId());
|
this.generateElemBallDrops(monster, drop.getDropId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle kill drops.
|
// Handle kill drops.
|
||||||
if (hpAfterDamage <= 0 && monster.getMonsterData().getKillDropId() != 0) {
|
if (hpAfterDamage <= 0 && monster.getMonsterData().getKillDropId() != 0) {
|
||||||
this.generateElemBallDrops(monster, monster.getMonsterData().getKillDropId());
|
this.generateElemBallDrops(monster, monster.getMonsterData().getKillDropId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Utilities.
|
* Utilities.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private void generateElemBall(int ballId, Position position, int count) {
|
private void generateElemBall(int ballId, Position position, int count) {
|
||||||
// Generate a particle/orb with the specified parameters.
|
// Generate a particle/orb with the specified parameters.
|
||||||
ItemData itemData = GameData.getItemDataMap().get(ballId);
|
ItemData itemData = GameData.getItemDataMap().get(ballId);
|
||||||
if (itemData == null) {
|
if (itemData == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityItem energyBall =
|
EntityItem energyBall =
|
||||||
new EntityItem(this.getPlayer().getScene(), this.getPlayer(), itemData, position, count);
|
new EntityItem(this.getPlayer().getScene(), this.getPlayer(), itemData, position, count);
|
||||||
this.getPlayer().getScene().addEntity(energyBall);
|
this.getPlayer().getScene().addEntity(energyBall);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<EntityAvatar> getCastingAvatarEntityForEnergy(int invokeEntityId) {
|
private Optional<EntityAvatar> getCastingAvatarEntityForEnergy(int invokeEntityId) {
|
||||||
// To determine the avatar that has cast the skill that caused the energy particle to be
|
// To determine the avatar that has cast the skill that caused the energy particle to be
|
||||||
// generated,
|
// generated,
|
||||||
// we have to look at the entity that has invoked the ability. This can either be that avatar
|
// we have to look at the entity that has invoked the ability. This can either be that avatar
|
||||||
// directly,
|
// directly,
|
||||||
// or it can be an `EntityClientGadget`, owned (some way up the owner hierarchy) by the avatar
|
// or it can be an `EntityClientGadget`, owned (some way up the owner hierarchy) by the avatar
|
||||||
// that cast the skill.
|
// that cast the skill.
|
||||||
|
|
||||||
// Try to get the invoking entity from the scene.
|
// Try to get the invoking entity from the scene.
|
||||||
GameEntity entity = this.player.getScene().getEntityById(invokeEntityId);
|
GameEntity entity = this.player.getScene().getEntityById(invokeEntityId);
|
||||||
|
|
||||||
// Determine the ID of the entity that originally cast this skill. If the scene entity is null,
|
// Determine the ID of the entity that originally cast this skill. If the scene entity is null,
|
||||||
// or not an `EntityClientGadget`, we assume that we are directly looking at the casting avatar
|
// or not an `EntityClientGadget`, we assume that we are directly looking at the casting avatar
|
||||||
// (the null case will happen if the avatar was switched out between casting the skill and the
|
// (the null case will happen if the avatar was switched out between casting the skill and the
|
||||||
// particle being generated). If the scene entity is an `EntityClientGadget`, we need to find
|
// particle being generated). If the scene entity is an `EntityClientGadget`, we need to find
|
||||||
// the
|
// the
|
||||||
// ID of the original owner of that gadget.
|
// ID of the original owner of that gadget.
|
||||||
int avatarEntityId =
|
int avatarEntityId =
|
||||||
(!(entity instanceof EntityClientGadget))
|
(!(entity instanceof EntityClientGadget))
|
||||||
? invokeEntityId
|
? invokeEntityId
|
||||||
: ((EntityClientGadget) entity).getOriginalOwnerEntityId();
|
: ((EntityClientGadget) entity).getOriginalOwnerEntityId();
|
||||||
|
|
||||||
// Finally, find the avatar entity in the player's team.
|
// Finally, find the avatar entity in the player's team.
|
||||||
return this.player.getTeamManager().getActiveTeam().stream()
|
return this.player.getTeamManager().getActiveTeam().stream()
|
||||||
.filter(character -> character.getId() == avatarEntityId)
|
.filter(character -> character.getId() == avatarEntityId)
|
||||||
.findFirst();
|
.findFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refills the energy of the active avatar.
|
* Refills the energy of the active avatar.
|
||||||
*
|
*
|
||||||
* @return True if the energy was refilled, false otherwise.
|
* @return True if the energy was refilled, false otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean refillActiveEnergy() {
|
public boolean refillActiveEnergy() {
|
||||||
var activeEntity = this.player.getTeamManager().getCurrentAvatarEntity();
|
var activeEntity = this.player.getTeamManager().getCurrentAvatarEntity();
|
||||||
return activeEntity.addEnergy(
|
return activeEntity.addEnergy(
|
||||||
activeEntity.getAvatar().getSkillDepot().getEnergySkillData().getCostElemVal());
|
activeEntity.getAvatar().getSkillDepot().getEnergySkillData().getCostElemVal());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refills the energy of the entire team.
|
* Refills the energy of the entire team.
|
||||||
*
|
*
|
||||||
* @param changeReason The reason for the energy change.
|
* @param changeReason The reason for the energy change.
|
||||||
* @param isFlat Whether the energy should be added as a flat value.
|
* @param isFlat Whether the energy should be added as a flat value.
|
||||||
*/
|
*/
|
||||||
public void refillTeamEnergy(PropChangeReason changeReason, boolean isFlat) {
|
public void refillTeamEnergy(PropChangeReason changeReason, boolean isFlat) {
|
||||||
for (var entityAvatar : this.player.getTeamManager().getActiveTeam()) {
|
for (var entityAvatar : this.player.getTeamManager().getActiveTeam()) {
|
||||||
// giving the exact amount read off the AvatarSkillData.json
|
// giving the exact amount read off the AvatarSkillData.json
|
||||||
entityAvatar.addEnergy(
|
entityAvatar.addEnergy(
|
||||||
entityAvatar.getAvatar().getSkillDepot().getEnergySkillData().getCostElemVal(),
|
entityAvatar.getAvatar().getSkillDepot().getEnergySkillData().getCostElemVal(),
|
||||||
changeReason,
|
changeReason,
|
||||||
isFlat);
|
isFlat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
consumption.amount = -2500;
|
case 10421:
|
||||||
|
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,276 +1,303 @@
|
|||||||
package emu.grasscutter.game.player;
|
package emu.grasscutter.game.player;
|
||||||
|
|
||||||
import emu.grasscutter.data.GameData;
|
import emu.grasscutter.data.GameData;
|
||||||
import emu.grasscutter.data.binout.ScenePointEntry;
|
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.QuestCond;
|
import emu.grasscutter.game.quest.enums.ParentQuestState;
|
||||||
import emu.grasscutter.game.quest.enums.QuestContent;
|
import emu.grasscutter.game.quest.enums.QuestCond;
|
||||||
import emu.grasscutter.game.quest.enums.QuestState;
|
import emu.grasscutter.game.quest.enums.QuestContent;
|
||||||
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
|
import emu.grasscutter.game.quest.enums.QuestState;
|
||||||
import emu.grasscutter.server.packet.send.*;
|
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
|
||||||
import java.util.Set;
|
import emu.grasscutter.scripts.data.ScriptArgs;
|
||||||
import java.util.stream.Collectors;
|
import emu.grasscutter.server.packet.send.*;
|
||||||
|
import java.util.Set;
|
||||||
// @Entity
|
import java.util.stream.Collectors;
|
||||||
public final class PlayerProgressManager extends BasePlayerDataManager {
|
|
||||||
/******************************************************************************************************************
|
import static emu.grasscutter.scripts.constants.EventType.EVENT_UNLOCK_TRANS_POINT;
|
||||||
******************************************************************************************************************
|
|
||||||
* OPEN STATES
|
// @Entity
|
||||||
******************************************************************************************************************
|
public final class PlayerProgressManager extends BasePlayerDataManager {
|
||||||
*****************************************************************************************************************/
|
/******************************************************************************************************************
|
||||||
|
******************************************************************************************************************
|
||||||
// Set of open states that are never unlocked, whether they fulfill the conditions or not.
|
* OPEN STATES
|
||||||
public static final Set<Integer> BLACKLIST_OPEN_STATES =
|
******************************************************************************************************************
|
||||||
Set.of(
|
*****************************************************************************************************************/
|
||||||
48 // blacklist OPEN_STATE_LIMIT_REGION_GLOBAL to make Meledy happy. =D Remove this as
|
|
||||||
// soon as quest unlocks are fully implemented.
|
// Set of open states that are never unlocked, whether they fulfill the conditions or not.
|
||||||
);
|
public static final Set<Integer> BLACKLIST_OPEN_STATES =
|
||||||
// Set of open states that are set per default for all accounts. Can be overwritten by an entry in
|
Set.of(
|
||||||
// `map`.
|
48 // blacklist OPEN_STATE_LIMIT_REGION_GLOBAL to make Meledy happy. =D Remove this as
|
||||||
public static final Set<Integer> DEFAULT_OPEN_STATES =
|
// soon as quest unlocks are fully implemented.
|
||||||
GameData.getOpenStateList().stream()
|
);
|
||||||
.filter(
|
// Set of open states that are set per default for all accounts. Can be overwritten by an entry in
|
||||||
s ->
|
// `map`.
|
||||||
s.isDefaultState() // Actual default-opened states.
|
public static final Set<Integer> DEFAULT_OPEN_STATES =
|
||||||
// All states whose unlock we don't handle correctly yet.
|
GameData.getOpenStateList().stream()
|
||||||
|| (s.getCond().stream()
|
.filter(
|
||||||
.filter(
|
s ->
|
||||||
c ->
|
s.isDefaultState() && !s.isAllowClientOpen() // Actual default-opened states.
|
||||||
c.getCondType()
|
|| ((s.getCond().size() == 1)
|
||||||
== OpenStateCondType.OPEN_STATE_COND_PLAYER_LEVEL)
|
&& (s.getCond().get(0).getCondType()
|
||||||
.count()
|
== OpenStateCondType.OPEN_STATE_COND_PLAYER_LEVEL)
|
||||||
== 0)
|
&& (s.getCond().get(0).getParam() == 1))
|
||||||
// Always unlock OPEN_STATE_PAIMON, otherwise the player will not have a
|
// All states whose unlock we don't handle correctly yet.
|
||||||
// working chat.
|
|| (s.getCond().stream()
|
||||||
|| s.getId() == 1)
|
.anyMatch(
|
||||||
.filter(
|
c ->
|
||||||
s ->
|
c.getCondType() == OpenStateCondType.OPEN_STATE_OFFERING_LEVEL
|
||||||
!BLACKLIST_OPEN_STATES.contains(s.getId())) // Filter out states in the blacklist.
|
|| c.getCondType()
|
||||||
.map(s -> s.getId())
|
== OpenStateCondType.OPEN_STATE_CITY_REPUTATION_LEVEL))
|
||||||
.collect(Collectors.toSet());
|
// Always unlock OPEN_STATE_PAIMON, otherwise the player will not have a
|
||||||
|
// working chat.
|
||||||
public PlayerProgressManager(Player player) {
|
|| s.getId() == 1)
|
||||||
super(player);
|
.filter(
|
||||||
}
|
s ->
|
||||||
|
!BLACKLIST_OPEN_STATES.contains(s.getId())) // Filter out states in the blacklist.
|
||||||
/**********
|
.map(OpenStateData::getId)
|
||||||
* Handler for player login.
|
.collect(Collectors.toSet());
|
||||||
**********/
|
|
||||||
public void onPlayerLogin() {
|
public PlayerProgressManager(Player player) {
|
||||||
// Try unlocking open states on player login. This handles accounts where unlock conditions were
|
super(player);
|
||||||
// already met before certain open state unlocks were implemented.
|
}
|
||||||
this.tryUnlockOpenStates(false);
|
|
||||||
|
/**********
|
||||||
// Send notify to the client.
|
* Handler for player login.
|
||||||
player.getSession().send(new PacketOpenStateUpdateNotify(this.player));
|
**********/
|
||||||
|
public void onPlayerLogin() {
|
||||||
// Add statue quests if necessary.
|
// Try unlocking open states on player login. This handles accounts where unlock conditions were
|
||||||
this.addStatueQuestsOnLogin();
|
// already met before certain open state unlocks were implemented.
|
||||||
|
this.tryUnlockOpenStates(false);
|
||||||
// Auto-unlock the first statue and map area, until we figure out how to make
|
|
||||||
// that particular statue interactable.
|
// Send notify to the client.
|
||||||
this.player.getUnlockedScenePoints(3).add(7);
|
player.getSession().send(new PacketOpenStateUpdateNotify(this.player));
|
||||||
this.player.getUnlockedSceneAreas(3).add(1);
|
|
||||||
}
|
// Add statue quests if necessary.
|
||||||
|
this.addStatueQuestsOnLogin();
|
||||||
/**********
|
|
||||||
* Direct getters and setters for open states.
|
// Auto-unlock the first statue and map area, until we figure out how to make
|
||||||
**********/
|
// that particular statue interactable.
|
||||||
public int getOpenState(int openState) {
|
this.player.getUnlockedScenePoints(3).add(7);
|
||||||
return this.player.getOpenStates().getOrDefault(openState, 0);
|
this.player.getUnlockedSceneAreas(3).add(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setOpenState(int openState, int value, boolean sendNotify) {
|
/**********
|
||||||
int previousValue = this.player.getOpenStates().getOrDefault(openState, 0);
|
* Direct getters and setters for open states.
|
||||||
|
**********/
|
||||||
if (value != previousValue) {
|
public int getOpenState(int openState) {
|
||||||
this.player.getOpenStates().put(openState, value);
|
return this.player.getOpenStates().getOrDefault(openState, 0);
|
||||||
|
}
|
||||||
if (sendNotify) {
|
|
||||||
player.getSession().send(new PacketOpenStateChangeNotify(openState, value));
|
private void setOpenState(int openState, int value, boolean sendNotify) {
|
||||||
}
|
int previousValue = this.player.getOpenStates().getOrDefault(openState, 0);
|
||||||
}
|
|
||||||
}
|
if (value != previousValue) {
|
||||||
|
this.player.getOpenStates().put(openState, value);
|
||||||
private void setOpenState(int openState, int value) {
|
|
||||||
this.setOpenState(openState, value, true);
|
this.player
|
||||||
}
|
.getQuestManager()
|
||||||
|
.queueEvent(QuestCond.QUEST_COND_OPEN_STATE_EQUAL, openState, value);
|
||||||
/**********
|
|
||||||
* Condition checking for setting open states.
|
if (sendNotify) {
|
||||||
**********/
|
player.getSession().send(new PacketOpenStateChangeNotify(openState, value));
|
||||||
private boolean areConditionsMet(OpenStateData openState) {
|
}
|
||||||
// Check all conditions and test if at least one of them is violated.
|
}
|
||||||
for (var condition : openState.getCond()) {
|
}
|
||||||
// For level conditions, check if the player has reached the necessary level.
|
|
||||||
if (condition.getCondType() == OpenStateCondType.OPEN_STATE_COND_PLAYER_LEVEL) {
|
private void setOpenState(int openState, int value) {
|
||||||
if (this.player.getLevel() < condition.getParam()) {
|
this.setOpenState(openState, value, true);
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
} else if (condition.getCondType() == OpenStateCondType.OPEN_STATE_COND_QUEST) {
|
/**********
|
||||||
// ToDo: Implement.
|
* Condition checking for setting open states.
|
||||||
} else if (condition.getCondType() == OpenStateCondType.OPEN_STATE_COND_PARENT_QUEST) {
|
**********/
|
||||||
// ToDo: Implement.
|
private boolean areConditionsMet(OpenStateData openState) {
|
||||||
} else if (condition.getCondType() == OpenStateCondType.OPEN_STATE_OFFERING_LEVEL) {
|
// Check all conditions and test if at least one of them is violated.
|
||||||
// ToDo: Implement.
|
for (var condition : openState.getCond()) {
|
||||||
} else if (condition.getCondType() == OpenStateCondType.OPEN_STATE_CITY_REPUTATION_LEVEL) {
|
switch (condition.getCondType()) {
|
||||||
// ToDo: Implement.
|
// For level conditions, check if the player has reached the necessary level.
|
||||||
}
|
case OPEN_STATE_COND_PLAYER_LEVEL -> {
|
||||||
}
|
if (this.player.getLevel() < condition.getParam()) {
|
||||||
|
return false;
|
||||||
// Done. If we didn't find any violations, all conditions are met.
|
}
|
||||||
return true;
|
}
|
||||||
}
|
case OPEN_STATE_COND_QUEST -> {
|
||||||
|
// check sub quest id for quest finished met requirements
|
||||||
/**********
|
var quest = this.player.getQuestManager().getQuestById(condition.getParam());
|
||||||
* Setting open states from the client (via `SetOpenStateReq`).
|
if (quest == null || quest.getState() != QuestState.QUEST_STATE_FINISHED) {
|
||||||
**********/
|
return false;
|
||||||
public void setOpenStateFromClient(int openState, int value) {
|
}
|
||||||
// Get the data for this open state.
|
}
|
||||||
OpenStateData data = GameData.getOpenStateDataMap().get(openState);
|
case OPEN_STATE_COND_PARENT_QUEST -> {
|
||||||
if (data == null) {
|
// check main quest id for quest finished met requirements
|
||||||
this.player.sendPacket(new PacketSetOpenStateRsp(Retcode.RET_FAIL));
|
// TODO not sure if its having or finished quest
|
||||||
return;
|
var mainQuest = this.player.getQuestManager().getMainQuestById(condition.getParam());
|
||||||
}
|
if (mainQuest == null
|
||||||
|
|| mainQuest.getState() != ParentQuestState.PARENT_QUEST_STATE_FINISHED) {
|
||||||
// Make sure that this is an open state that the client is allowed to set,
|
return false;
|
||||||
// and that it doesn't have any further conditions attached.
|
}
|
||||||
if (!data.isAllowClientOpen() || !this.areConditionsMet(data)) {
|
}
|
||||||
this.player.sendPacket(new PacketSetOpenStateRsp(Retcode.RET_FAIL));
|
// ToDo: Implement.
|
||||||
return;
|
case OPEN_STATE_OFFERING_LEVEL, OPEN_STATE_CITY_REPUTATION_LEVEL -> {}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Set.
|
|
||||||
this.setOpenState(openState, value);
|
// Done. If we didn't find any violations, all conditions are met.
|
||||||
this.player.sendPacket(new PacketSetOpenStateRsp(openState, value));
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** This force sets an open state, ignoring all conditions and permissions */
|
/**********
|
||||||
public void forceSetOpenState(int openState, int value) {
|
* Setting open states from the client (via `SetOpenStateReq`).
|
||||||
this.setOpenState(openState, value);
|
**********/
|
||||||
}
|
public void setOpenStateFromClient(int openState, int value) {
|
||||||
|
// Get the data for this open state.
|
||||||
/**********
|
OpenStateData data = GameData.getOpenStateDataMap().get(openState);
|
||||||
* Triggered unlocking of open states (unlock states whose conditions have been met.)
|
if (data == null) {
|
||||||
**********/
|
this.player.sendPacket(new PacketSetOpenStateRsp(Retcode.RET_FAIL));
|
||||||
public void tryUnlockOpenStates(boolean sendNotify) {
|
return;
|
||||||
// Get list of open states that are not yet unlocked.
|
}
|
||||||
var lockedStates =
|
|
||||||
GameData.getOpenStateList().stream()
|
// Make sure that this is an open state that the client is allowed to set,
|
||||||
.filter(s -> this.player.getOpenStates().getOrDefault(s, 0) == 0)
|
// and that it doesn't have any further conditions attached.
|
||||||
.toList();
|
if (!data.isAllowClientOpen() || !this.areConditionsMet(data)) {
|
||||||
|
this.player.sendPacket(new PacketSetOpenStateRsp(Retcode.RET_FAIL));
|
||||||
// Try unlocking all of them.
|
return;
|
||||||
for (var state : lockedStates) {
|
}
|
||||||
// To auto-unlock a state, it has to meet three conditions:
|
|
||||||
// * it can not be a state that is unlocked by the client,
|
// Set.
|
||||||
// * it has to meet all its unlock conditions, and
|
this.setOpenState(openState, value);
|
||||||
// * it can not be in the blacklist.
|
this.player.sendPacket(new PacketSetOpenStateRsp(openState, value));
|
||||||
if (!state.isAllowClientOpen()
|
}
|
||||||
&& this.areConditionsMet(state)
|
|
||||||
&& !BLACKLIST_OPEN_STATES.contains(state.getId())) {
|
/** This force sets an open state, ignoring all conditions and permissions */
|
||||||
this.setOpenState(state.getId(), 1, sendNotify);
|
public void forceSetOpenState(int openState, int value) {
|
||||||
}
|
this.setOpenState(openState, value);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/**********
|
||||||
public void tryUnlockOpenStates() {
|
* Triggered unlocking of open states (unlock states whose conditions have been met.)
|
||||||
this.tryUnlockOpenStates(true);
|
**********/
|
||||||
}
|
public void tryUnlockOpenStates(boolean sendNotify) {
|
||||||
|
// Get list of open states that are not yet unlocked.
|
||||||
/******************************************************************************************************************
|
var lockedStates =
|
||||||
******************************************************************************************************************
|
GameData.getOpenStateList().stream()
|
||||||
* MAP AREAS AND POINTS
|
.filter(s -> this.player.getOpenStates().getOrDefault(s, 0) == 0)
|
||||||
******************************************************************************************************************
|
.toList();
|
||||||
*****************************************************************************************************************/
|
|
||||||
private void addStatueQuestsOnLogin() {
|
// Try unlocking all of them.
|
||||||
// Get all currently existing subquests for the "unlock all statues" main quest.
|
for (var state : lockedStates) {
|
||||||
var statueMainQuest = GameData.getMainQuestDataMap().get(303);
|
// To auto-unlock a state, it has to meet three conditions:
|
||||||
var statueSubQuests = statueMainQuest.getSubQuests();
|
// * it can not be a state that is unlocked by the client,
|
||||||
|
// * it has to meet all its unlock conditions, and
|
||||||
// Add the main statue quest if it isn't active yet.
|
// * it can not be in the blacklist.
|
||||||
var statueGameMainQuest = this.player.getQuestManager().getMainQuestById(303);
|
if (!state.isAllowClientOpen()
|
||||||
if (statueGameMainQuest == null) {
|
&& this.areConditionsMet(state)
|
||||||
this.player.getQuestManager().addQuest(30302);
|
&& !BLACKLIST_OPEN_STATES.contains(state.getId())) {
|
||||||
statueGameMainQuest = this.player.getQuestManager().getMainQuestById(303);
|
this.setOpenState(state.getId(), 1, sendNotify);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Set all subquests to active if they aren't already finished.
|
}
|
||||||
for (var subData : statueSubQuests) {
|
|
||||||
var subGameQuest = statueGameMainQuest.getChildQuestById(subData.getSubId());
|
public void tryUnlockOpenStates() {
|
||||||
if (subGameQuest != null && subGameQuest.getState() == QuestState.QUEST_STATE_UNSTARTED) {
|
this.tryUnlockOpenStates(true);
|
||||||
this.player.getQuestManager().addQuest(subData.getSubId());
|
}
|
||||||
}
|
|
||||||
}
|
/******************************************************************************************************************
|
||||||
}
|
******************************************************************************************************************
|
||||||
|
* MAP AREAS AND POINTS
|
||||||
public boolean unlockTransPoint(int sceneId, int pointId, boolean isStatue) {
|
******************************************************************************************************************
|
||||||
// Check whether the unlocked point exists and whether it is still locked.
|
*****************************************************************************************************************/
|
||||||
ScenePointEntry scenePointEntry = GameData.getScenePointEntryById(sceneId, pointId);
|
private void addStatueQuestsOnLogin() {
|
||||||
|
// Get all currently existing subquests for the "unlock all statues" main quest.
|
||||||
if (scenePointEntry == null || this.player.getUnlockedScenePoints(sceneId).contains(pointId)) {
|
var statueMainQuest = GameData.getMainQuestDataMap().get(303);
|
||||||
return false;
|
var statueSubQuests = statueMainQuest.getSubQuests();
|
||||||
}
|
|
||||||
|
// Add the main statue quest if it isn't active yet.
|
||||||
// Add the point to the list of unlocked points for its scene.
|
var statueGameMainQuest = this.player.getQuestManager().getMainQuestById(303);
|
||||||
this.player.getUnlockedScenePoints(sceneId).add(pointId);
|
if (statueGameMainQuest == null) {
|
||||||
|
this.player.getQuestManager().addQuest(30302);
|
||||||
// Give primogems and Adventure EXP for unlocking.
|
statueGameMainQuest = this.player.getQuestManager().getMainQuestById(303);
|
||||||
this.player.getInventory().addItem(201, 5, ActionReason.UnlockPointReward);
|
}
|
||||||
this.player.getInventory().addItem(102, isStatue ? 50 : 10, ActionReason.UnlockPointReward);
|
|
||||||
|
// Set all subquests to active if they aren't already finished.
|
||||||
// this.player.sendPacket(new
|
for (var subData : statueSubQuests) {
|
||||||
// PacketPlayerPropChangeReasonNotify(this.player.getProperty(PlayerProperty.PROP_PLAYER_EXP),
|
var subGameQuest = statueGameMainQuest.getChildQuestById(subData.getSubId());
|
||||||
// PlayerProperty.PROP_PLAYER_EXP, PropChangeReason.PROP_CHANGE_REASON_PLAYER_ADD_EXP));
|
if (subGameQuest != null && subGameQuest.getState() == QuestState.QUEST_STATE_UNSTARTED) {
|
||||||
|
this.player.getQuestManager().addQuest(subData.getSubId());
|
||||||
// Fire quest trigger for trans point unlock.
|
}
|
||||||
this.player
|
}
|
||||||
.getQuestManager()
|
}
|
||||||
.queueEvent(QuestContent.QUEST_CONTENT_UNLOCK_TRANS_POINT, sceneId, pointId);
|
|
||||||
|
public boolean unlockTransPoint(int sceneId, int pointId, boolean isStatue) {
|
||||||
// Send packet.
|
// Check whether the unlocked point exists and whether it is still locked.
|
||||||
this.player.sendPacket(new PacketScenePointUnlockNotify(sceneId, pointId));
|
ScenePointEntry scenePointEntry = GameData.getScenePointEntryById(sceneId, pointId);
|
||||||
return true;
|
|
||||||
}
|
if (scenePointEntry == null || this.player.getUnlockedScenePoints(sceneId).contains(pointId)) {
|
||||||
|
return false;
|
||||||
public void unlockSceneArea(int sceneId, int areaId) {
|
}
|
||||||
// Add the area to the list of unlocked areas in its scene.
|
|
||||||
this.player.getUnlockedSceneAreas(sceneId).add(areaId);
|
// Add the point to the list of unlocked points for its scene.
|
||||||
|
this.player.getUnlockedScenePoints(sceneId).add(pointId);
|
||||||
// Send packet.
|
|
||||||
this.player.sendPacket(new PacketSceneAreaUnlockNotify(sceneId, areaId));
|
// Give primogems and Adventure EXP for unlocking.
|
||||||
}
|
this.player.getInventory().addItem(201, 5, ActionReason.UnlockPointReward);
|
||||||
|
this.player.getInventory().addItem(102, isStatue ? 50 : 10, ActionReason.UnlockPointReward);
|
||||||
/** Give replace costume to player (Amber, Jean, Mona, Rosaria) */
|
|
||||||
public void addReplaceCostumes() {
|
// this.player.sendPacket(new
|
||||||
var currentPlayerCostumes = player.getCostumeList();
|
// PacketPlayerPropChangeReasonNotify(this.player.getProperty(PlayerProperty.PROP_PLAYER_EXP),
|
||||||
GameData.getAvatarReplaceCostumeDataMap()
|
// PlayerProperty.PROP_PLAYER_EXP, PropChangeReason.PROP_CHANGE_REASON_PLAYER_ADD_EXP));
|
||||||
.keySet()
|
|
||||||
.forEach(
|
// Fire quest trigger for trans point unlock.
|
||||||
costumeId -> {
|
this.player
|
||||||
if (GameData.getAvatarCostumeDataMap().get(costumeId) == null
|
.getQuestManager()
|
||||||
|| currentPlayerCostumes.contains(costumeId)) {
|
.queueEvent(QuestContent.QUEST_CONTENT_UNLOCK_TRANS_POINT, sceneId, pointId);
|
||||||
return;
|
this.player
|
||||||
}
|
.getScene()
|
||||||
this.player.addCostume(costumeId);
|
.getScriptManager()
|
||||||
});
|
.callEvent(new ScriptArgs(0, EVENT_UNLOCK_TRANS_POINT, sceneId, pointId));
|
||||||
}
|
|
||||||
|
// Send packet.
|
||||||
/** Quest progress */
|
this.player.sendPacket(new PacketScenePointUnlockNotify(sceneId, pointId));
|
||||||
public void addQuestProgress(int id, int count) {
|
return true;
|
||||||
var newCount = player.getPlayerProgress().addToCurrentProgress(id, count);
|
}
|
||||||
player.save();
|
|
||||||
player
|
public void unlockSceneArea(int sceneId, int areaId) {
|
||||||
.getQuestManager()
|
// Add the area to the list of unlocked areas in its scene.
|
||||||
.queueEvent(QuestContent.QUEST_CONTENT_ADD_QUEST_PROGRESS, id, newCount);
|
this.player.getUnlockedSceneAreas(sceneId).add(areaId);
|
||||||
}
|
|
||||||
|
// Send packet.
|
||||||
/** Item history */
|
this.player.sendPacket(new PacketSceneAreaUnlockNotify(sceneId, areaId));
|
||||||
public void addItemObtainedHistory(int id, int count) {
|
}
|
||||||
var newCount = player.getPlayerProgress().addToItemHistory(id, count);
|
|
||||||
player.save();
|
/** Give replace costume to player (Amber, Jean, Mona, Rosaria) */
|
||||||
player.getQuestManager().queueEvent(QuestCond.QUEST_COND_HISTORY_GOT_ANY_ITEM, id, newCount);
|
public void addReplaceCostumes() {
|
||||||
}
|
var currentPlayerCostumes = player.getCostumeList();
|
||||||
}
|
GameData.getAvatarReplaceCostumeDataMap()
|
||||||
|
.keySet()
|
||||||
|
.forEach(
|
||||||
|
costumeId -> {
|
||||||
|
if (GameData.getAvatarCostumeDataMap().get(costumeId) == null
|
||||||
|
|| currentPlayerCostumes.contains(costumeId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.player.addCostume(costumeId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Quest progress */
|
||||||
|
public void addQuestProgress(int id, int count) {
|
||||||
|
var newCount = player.getPlayerProgress().addToCurrentProgress(id, count);
|
||||||
|
player.save();
|
||||||
|
player
|
||||||
|
.getQuestManager()
|
||||||
|
.queueEvent(QuestContent.QUEST_CONTENT_ADD_QUEST_PROGRESS, id, newCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Item history */
|
||||||
|
public void addItemObtainedHistory(int id, int count) {
|
||||||
|
var newCount = player.getPlayerProgress().addToItemHistory(id, count);
|
||||||
|
player.save();
|
||||||
|
player.getQuestManager().queueEvent(QuestCond.QUEST_COND_HISTORY_GOT_ANY_ITEM, id, newCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,25 +1,19 @@
|
|||||||
package emu.grasscutter.game.props.ItemUseAction;
|
package emu.grasscutter.game.props.ItemUseAction;
|
||||||
|
|
||||||
import emu.grasscutter.game.props.ItemUseOp;
|
import emu.grasscutter.game.props.ItemUseOp;
|
||||||
|
|
||||||
public class ItemUseUnlockHomeModule extends ItemUseInt {
|
public class ItemUseUnlockHomeModule extends ItemUseInt {
|
||||||
public ItemUseUnlockHomeModule(String[] useParam) {
|
public ItemUseUnlockHomeModule(String[] useParam) {
|
||||||
super(useParam);
|
super(useParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ItemUseOp getItemUseOp() {
|
public ItemUseOp getItemUseOp() {
|
||||||
return ItemUseOp.ITEM_USE_UNLOCK_HOME_MODULE;
|
return ItemUseOp.ITEM_USE_UNLOCK_HOME_MODULE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,95 +1,95 @@
|
|||||||
package emu.grasscutter.game.shop;
|
package emu.grasscutter.game.shop;
|
||||||
|
|
||||||
import emu.grasscutter.data.common.ItemParamData;
|
import emu.grasscutter.data.common.ItemParamData;
|
||||||
import emu.grasscutter.data.excels.ShopGoodsData;
|
import emu.grasscutter.data.excels.ShopGoodsData;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
public class ShopInfo {
|
public class ShopInfo {
|
||||||
@Getter @Setter private int goodsId = 0;
|
@Getter @Setter private int goodsId = 0;
|
||||||
@Getter @Setter private ItemParamData goodsItem;
|
@Getter @Setter private ItemParamData goodsItem;
|
||||||
@Getter @Setter private int scoin = 0;
|
@Getter @Setter private int scoin = 0;
|
||||||
@Getter @Setter private List<ItemParamData> costItemList;
|
@Getter @Setter private List<ItemParamData> costItemList;
|
||||||
@Getter @Setter private int boughtNum = 0;
|
@Getter @Setter private int boughtNum = 0;
|
||||||
@Getter @Setter private int buyLimit = 0;
|
@Getter @Setter private int buyLimit = 0;
|
||||||
@Getter @Setter private int beginTime = 0;
|
@Getter @Setter private int beginTime = 0;
|
||||||
@Getter @Setter private int endTime = 1924992000;
|
@Getter @Setter private int endTime = 1924992000;
|
||||||
@Getter @Setter private int minLevel = 0;
|
@Getter @Setter private int minLevel = 0;
|
||||||
@Getter @Setter private int maxLevel = 61;
|
@Getter @Setter private int maxLevel = 61;
|
||||||
@Getter @Setter private List<Integer> preGoodsIdList = new ArrayList<>();
|
@Getter @Setter private List<Integer> preGoodsIdList = new ArrayList<>();
|
||||||
@Getter @Setter private int mcoin = 0;
|
@Getter @Setter private int mcoin = 0;
|
||||||
@Getter @Setter private int hcoin = 0;
|
@Getter @Setter private int hcoin = 0;
|
||||||
@Getter @Setter private int disableType = 0;
|
@Getter @Setter private int disableType = 0;
|
||||||
@Getter @Setter private int secondarySheetId = 0;
|
@Getter @Setter private int secondarySheetId = 0;
|
||||||
|
|
||||||
private String refreshType;
|
private String refreshType;
|
||||||
@Setter private transient ShopRefreshType shopRefreshType;
|
@Setter private transient ShopRefreshType shopRefreshType;
|
||||||
@Getter @Setter private int shopRefreshParam;
|
@Getter @Setter private int shopRefreshParam;
|
||||||
|
|
||||||
public ShopInfo(ShopGoodsData sgd) {
|
public ShopInfo(ShopGoodsData sgd) {
|
||||||
this.goodsId = sgd.getGoodsId();
|
this.goodsId = sgd.getGoodsId();
|
||||||
this.goodsItem = new ItemParamData(sgd.getItemId(), sgd.getItemCount());
|
this.goodsItem = new ItemParamData(sgd.getItemId(), sgd.getItemCount());
|
||||||
this.scoin = sgd.getCostScoin();
|
this.scoin = sgd.getCostScoin();
|
||||||
this.mcoin = sgd.getCostMcoin();
|
this.mcoin = sgd.getCostMcoin();
|
||||||
this.hcoin = sgd.getCostHcoin();
|
this.hcoin = sgd.getCostHcoin();
|
||||||
this.buyLimit = sgd.getBuyLimit();
|
this.buyLimit = sgd.getBuyLimit();
|
||||||
|
|
||||||
this.minLevel = sgd.getMinPlayerLevel();
|
this.minLevel = sgd.getMinPlayerLevel();
|
||||||
this.maxLevel = sgd.getMaxPlayerLevel();
|
this.maxLevel = sgd.getMaxPlayerLevel();
|
||||||
this.costItemList =
|
this.costItemList =
|
||||||
sgd.getCostItems().stream()
|
sgd.getCostItems().stream()
|
||||||
.filter(x -> x.getId() != 0)
|
.filter(x -> x.getId() != 0)
|
||||||
.map(x -> new ItemParamData(x.getId(), x.getCount()))
|
.map(x -> new ItemParamData(x.getId(), x.getCount()))
|
||||||
.toList();
|
.toList();
|
||||||
this.secondarySheetId = sgd.getSubTabId();
|
this.secondarySheetId = sgd.getSubTabId();
|
||||||
this.shopRefreshType = sgd.getRefreshType();
|
this.shopRefreshType = sgd.getRefreshType();
|
||||||
this.shopRefreshParam = sgd.getRefreshParam();
|
this.shopRefreshParam = sgd.getRefreshParam();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShopRefreshType getShopRefreshType() {
|
public ShopRefreshType getShopRefreshType() {
|
||||||
if (refreshType == null) return ShopRefreshType.NONE;
|
if (refreshType == null) return ShopRefreshType.NONE;
|
||||||
return switch (refreshType) {
|
return switch (refreshType) {
|
||||||
case "SHOP_REFRESH_DAILY" -> ShopInfo.ShopRefreshType.SHOP_REFRESH_DAILY;
|
case "SHOP_REFRESH_DAILY" -> ShopInfo.ShopRefreshType.SHOP_REFRESH_DAILY;
|
||||||
case "SHOP_REFRESH_WEEKLY" -> ShopInfo.ShopRefreshType.SHOP_REFRESH_WEEKLY;
|
case "SHOP_REFRESH_WEEKLY" -> ShopInfo.ShopRefreshType.SHOP_REFRESH_WEEKLY;
|
||||||
case "SHOP_REFRESH_MONTHLY" -> ShopInfo.ShopRefreshType.SHOP_REFRESH_MONTHLY;
|
case "SHOP_REFRESH_MONTHLY" -> ShopInfo.ShopRefreshType.SHOP_REFRESH_MONTHLY;
|
||||||
default -> ShopInfo.ShopRefreshType.NONE;
|
default -> ShopInfo.ShopRefreshType.NONE;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean evaluateVirtualCost(ItemParamData item) {
|
private boolean evaluateVirtualCost(ItemParamData item) {
|
||||||
return switch (item.getId()) {
|
return switch (item.getId()) {
|
||||||
case 201 -> {
|
case 201 -> {
|
||||||
this.hcoin += item.getCount();
|
this.hcoin += item.getCount();
|
||||||
yield true;
|
yield true;
|
||||||
}
|
}
|
||||||
case 203 -> {
|
case 203 -> {
|
||||||
this.mcoin += item.getCount();
|
this.mcoin += item.getCount();
|
||||||
yield true;
|
yield true;
|
||||||
}
|
}
|
||||||
default -> false;
|
default -> false;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeVirtualCosts() {
|
public void removeVirtualCosts() {
|
||||||
if (this.costItemList != null) this.costItemList.removeIf(item -> evaluateVirtualCost(item));
|
if (this.costItemList != null) this.costItemList.removeIf(item -> evaluateVirtualCost(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ShopRefreshType {
|
public enum ShopRefreshType {
|
||||||
NONE(0),
|
NONE(0),
|
||||||
SHOP_REFRESH_DAILY(1),
|
SHOP_REFRESH_DAILY(1),
|
||||||
SHOP_REFRESH_WEEKLY(2),
|
SHOP_REFRESH_WEEKLY(2),
|
||||||
SHOP_REFRESH_MONTHLY(3);
|
SHOP_REFRESH_MONTHLY(3);
|
||||||
|
|
||||||
private final int value;
|
private final int value;
|
||||||
|
|
||||||
ShopRefreshType(int value) {
|
ShopRefreshType(int value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int value() {
|
public int value() {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user