diff --git a/src/main/java/emu/grasscutter/command/commands/TeleportAllCommand.java b/src/main/java/emu/grasscutter/command/commands/TeleportAllCommand.java index 4ddf86ded..ac817766b 100644 --- a/src/main/java/emu/grasscutter/command/commands/TeleportAllCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/TeleportAllCommand.java @@ -3,7 +3,7 @@ package emu.grasscutter.command.commands; import emu.grasscutter.command.Command; import emu.grasscutter.command.CommandHandler; import emu.grasscutter.game.player.Player; -import emu.grasscutter.utils.Position; +import emu.grasscutter.server.event.player.PlayerTeleportEvent.TeleportType; import java.util.List; @@ -22,9 +22,8 @@ public final class TeleportAllCommand implements CommandHandler { for (Player player : targetPlayer.getWorld().getPlayers()) { if (player.equals(targetPlayer)) continue; - Position pos = targetPlayer.getPosition(); - player.getWorld().transferPlayerToScene(player, targetPlayer.getSceneId(), pos); + player.getWorld().transferPlayerToScene(player, targetPlayer.getSceneId(), TeleportType.COMMAND, targetPlayer.getPosition()); } CommandHandler.sendMessage(sender, translate(sender, "commands.teleportAll.success")); diff --git a/src/main/java/emu/grasscutter/command/commands/TeleportCommand.java b/src/main/java/emu/grasscutter/command/commands/TeleportCommand.java index e9864c5a4..6e5f99cc4 100644 --- a/src/main/java/emu/grasscutter/command/commands/TeleportCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/TeleportCommand.java @@ -3,6 +3,7 @@ package emu.grasscutter.command.commands; import emu.grasscutter.command.Command; import emu.grasscutter.command.CommandHandler; import emu.grasscutter.game.player.Player; +import emu.grasscutter.server.event.player.PlayerTeleportEvent.TeleportType; import emu.grasscutter.utils.Position; import java.util.List; @@ -40,20 +41,21 @@ public final class TeleportCommand implements CommandHandler { } // Fallthrough case 3: try { - x = parseRelative(args.get(0), x); - y = parseRelative(args.get(1), y); - z = parseRelative(args.get(2), z); + x = this.parseRelative(args.get(0), x); + y = this.parseRelative(args.get(1), y); + z = this.parseRelative(args.get(2), z); } catch (NumberFormatException ignored) { CommandHandler.sendMessage(sender, translate(sender, "commands.teleport.invalid_position")); } break; default: - sendUsageMessage(sender); + this.sendUsageMessage(sender); return; } Position target_pos = new Position(x, y, z); - boolean result = targetPlayer.getWorld().transferPlayerToScene(targetPlayer, sceneId, target_pos); + boolean result = targetPlayer.getWorld().transferPlayerToScene(targetPlayer, sceneId, TeleportType.COMMAND, target_pos); + if (!result) { CommandHandler.sendMessage(sender, translate(sender, "commands.teleport.exists_error")); } else { diff --git a/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java b/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java index 431054d14..cb640c424 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java @@ -30,6 +30,7 @@ import emu.grasscutter.net.proto.SceneAvatarInfoOuterClass.SceneAvatarInfo; import emu.grasscutter.net.proto.SceneEntityAiInfoOuterClass.SceneEntityAiInfo; import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo; import emu.grasscutter.net.proto.VectorOuterClass.Vector; +import emu.grasscutter.server.event.player.PlayerMoveEvent; import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify; import emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotify; import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; @@ -40,28 +41,28 @@ import it.unimi.dsi.fastutil.ints.Int2FloatMap; import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap; public class EntityAvatar extends GameEntity { - private final Avatar avatar; + private final Avatar avatar; - private PlayerDieType killedType; - private int killedBy; + private PlayerDieType killedType; + private int killedBy; - public EntityAvatar(Scene scene, Avatar avatar) { - super(scene); - this.avatar = avatar; - this.avatar.setCurrentEnergy(); - this.id = getScene().getWorld().getNextEntityId(EntityIdType.AVATAR); + public EntityAvatar(Scene scene, Avatar avatar) { + super(scene); + this.avatar = avatar; + this.avatar.setCurrentEnergy(); + this.id = getScene().getWorld().getNextEntityId(EntityIdType.AVATAR); - GameItem weapon = this.getAvatar().getWeapon(); - if (weapon != null) { - weapon.setWeaponEntityId(getScene().getWorld().getNextEntityId(EntityIdType.WEAPON)); - } - } + GameItem weapon = this.getAvatar().getWeapon(); + if (weapon != null) { + weapon.setWeaponEntityId(getScene().getWorld().getNextEntityId(EntityIdType.WEAPON)); + } + } - public EntityAvatar(Avatar avatar) { - super(null); - this.avatar = avatar; - this.avatar.setCurrentEnergy(); - } + public EntityAvatar(Avatar avatar) { + super(null); + this.avatar = avatar; + this.avatar.setCurrentEnergy(); + } public Player getPlayer() { return avatar.getPlayer(); @@ -70,72 +71,76 @@ public class EntityAvatar extends GameEntity { @Override public Position getPosition() { return getPlayer().getPosition(); - } + } - @Override - public Position getRotation() { - return getPlayer().getRotation(); - } + @Override + public Position getRotation() { + return getPlayer().getRotation(); + } - public Avatar getAvatar() { - return avatar; - } + public Avatar getAvatar() { + return avatar; + } - public int getKilledBy() { - return killedBy; - } + public int getKilledBy() { + return killedBy; + } - public PlayerDieType getKilledType() { - return killedType; - } + public PlayerDieType getKilledType() { + return killedType; + } - @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 Int2FloatOpenHashMap getFightProperties() { - return getAvatar().getFightProperties(); - } + @Override + public Int2FloatOpenHashMap getFightProperties() { + return getAvatar().getFightProperties(); + } - public int getWeaponEntityId() { - if (getAvatar().getWeapon() != null) { - return getAvatar().getWeapon().getWeaponEntityId(); - } - return 0; - } + public int getWeaponEntityId() { + if (getAvatar().getWeapon() != null) { + return getAvatar().getWeapon().getWeaponEntityId(); + } + return 0; + } @Override public void onDeath(int killerId) { + super.onDeath(killerId); // Invoke super class's onDeath() method. + 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) { - this.killedType = dieType; - 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. - @Override - public float heal(float amount) { - float healed = super.heal(amount); + this.killedType = dieType; + this.killedBy = killerId; + clearEnergy(ChangeEnergyReason.CHANGE_ENERGY_REASON_NONE); + } - if (healed > 0f) { - getScene().broadcastPacket( + @Override + public float heal(float amount) { + float healed = super.heal(amount); + + if (healed > 0f) { + getScene().broadcastPacket( new PacketEntityFightPropChangeReasonNotify(this, FightProperty.FIGHT_PROP_CUR_HP, healed, PropChangeReason.PROP_CHANGE_REASON_ABILITY, ChangeHpReason.CHANGE_HP_REASON_ADD_ABILITY) - ); - } + ); + } - return healed; - } + return healed; + } - public void clearEnergy(ChangeEnergyReason reason) { - // Fight props. - FightProperty curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp(); - FightProperty maxEnergyProp = this.getAvatar().getSkillDepot().getElementType().getMaxEnergyProp(); + public void clearEnergy(ChangeEnergyReason reason) { + // Fight props. + FightProperty curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp(); + FightProperty maxEnergyProp = this.getAvatar().getSkillDepot().getElementType().getMaxEnergyProp(); // Get max energy. float maxEnergy = this.avatar.getFightProperty(maxEnergyProp); @@ -146,172 +151,189 @@ public class EntityAvatar extends GameEntity { // Send packets. this.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, curEnergyProp)); - if (reason == ChangeEnergyReason.CHANGE_ENERGY_REASON_SKILL_START) { - this.getScene().broadcastPacket(new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, -maxEnergy, reason)); - } - } + if (reason == ChangeEnergyReason.CHANGE_ENERGY_REASON_SKILL_START) { + this.getScene().broadcastPacket(new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, -maxEnergy, reason)); + } + } - 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. - FightProperty curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp(); - FightProperty maxEnergyProp = this.getAvatar().getSkillDepot().getElementType().getMaxEnergyProp(); + 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. + FightProperty curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp(); + FightProperty maxEnergyProp = this.getAvatar().getSkillDepot().getElementType().getMaxEnergyProp(); - float curEnergy = this.getFightProperty(curEnergyProp); - float maxEnergy = this.getFightProperty(maxEnergyProp); + float curEnergy = this.getFightProperty(curEnergyProp); + float maxEnergy = this.getFightProperty(maxEnergyProp); - // Get energy recharge. - float energyRecharge = this.getFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY); + // Get energy recharge. + float energyRecharge = this.getFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY); // Scale amount by energy recharge, if the amount is not flat. if (!isFlat) { amount *= energyRecharge; } - // Determine the new energy value. - float newEnergy = Math.min(curEnergy + amount, maxEnergy); + // Determine the new energy value. + float newEnergy = Math.min(curEnergy + amount, maxEnergy); - // Set energy and notify. - if (newEnergy != curEnergy) { - this.avatar.setCurrentEnergy(curEnergyProp, newEnergy); + // Set energy and notify. + if (newEnergy != curEnergy) { + this.avatar.setCurrentEnergy(curEnergyProp, newEnergy); - this.getScene().broadcastPacket(new PacketAvatarFightPropUpdateNotify(this.getAvatar(), curEnergyProp)); - this.getScene().broadcastPacket(new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, newEnergy, reason)); - } - } + this.getScene().broadcastPacket(new PacketAvatarFightPropUpdateNotify(this.getAvatar(), curEnergyProp)); + this.getScene().broadcastPacket(new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, newEnergy, reason)); + } + } - public SceneAvatarInfo getSceneAvatarInfo() { - SceneAvatarInfo.Builder avatarInfo = SceneAvatarInfo.newBuilder() - .setUid(this.getPlayer().getUid()) - .setAvatarId(this.getAvatar().getAvatarId()) - .setGuid(this.getAvatar().getGuid()) - .setPeerId(this.getPlayer().getPeerId()) - .addAllTalentIdList(this.getAvatar().getTalentIdList()) - .setCoreProudSkillLevel(this.getAvatar().getCoreProudSkillLevel()) - .putAllSkillLevelMap(this.getAvatar().getSkillLevelMap()) - .setSkillDepotId(this.getAvatar().getSkillDepotId()) - .addAllInherentProudSkillList(this.getAvatar().getProudSkillList()) - .putAllProudSkillExtraLevelMap(this.getAvatar().getProudSkillBonusMap()) - .addAllTeamResonanceList(this.getAvatar().getPlayer().getTeamManager().getTeamResonances()) - .setWearingFlycloakId(this.getAvatar().getFlyCloak()) - .setCostumeId(this.getAvatar().getCostume()) - .setBornTime(this.getAvatar().getBornTime()); + public SceneAvatarInfo getSceneAvatarInfo() { + SceneAvatarInfo.Builder avatarInfo = SceneAvatarInfo.newBuilder() + .setUid(this.getPlayer().getUid()) + .setAvatarId(this.getAvatar().getAvatarId()) + .setGuid(this.getAvatar().getGuid()) + .setPeerId(this.getPlayer().getPeerId()) + .addAllTalentIdList(this.getAvatar().getTalentIdList()) + .setCoreProudSkillLevel(this.getAvatar().getCoreProudSkillLevel()) + .putAllSkillLevelMap(this.getAvatar().getSkillLevelMap()) + .setSkillDepotId(this.getAvatar().getSkillDepotId()) + .addAllInherentProudSkillList(this.getAvatar().getProudSkillList()) + .putAllProudSkillExtraLevelMap(this.getAvatar().getProudSkillBonusMap()) + .addAllTeamResonanceList(this.getAvatar().getPlayer().getTeamManager().getTeamResonances()) + .setWearingFlycloakId(this.getAvatar().getFlyCloak()) + .setCostumeId(this.getAvatar().getCostume()) + .setBornTime(this.getAvatar().getBornTime()); - for (GameItem item : avatar.getEquips().values()) { - if (item.getItemData().getEquipType() == EquipType.EQUIP_WEAPON) { - avatarInfo.setWeapon(item.createSceneWeaponInfo()); - } else { - avatarInfo.addReliquaryList(item.createSceneReliquaryInfo()); - } - avatarInfo.addEquipIdList(item.getItemId()); - } + for (GameItem item : avatar.getEquips().values()) { + if (item.getItemData().getEquipType() == EquipType.EQUIP_WEAPON) { + avatarInfo.setWeapon(item.createSceneWeaponInfo()); + } else { + avatarInfo.addReliquaryList(item.createSceneReliquaryInfo()); + } + avatarInfo.addEquipIdList(item.getItemId()); + } - return avatarInfo.build(); - } + return avatarInfo.build(); + } - @Override - public SceneEntityInfo toProto() { - EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder() - .setAbilityInfo(AbilitySyncStateInfo.newBuilder()) - .setRendererChangedInfo(EntityRendererChangedInfo.newBuilder()) - .setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(Vector.newBuilder())) - .setBornPos(Vector.newBuilder()) - .build(); + @Override + public SceneEntityInfo toProto() { + EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder() + .setAbilityInfo(AbilitySyncStateInfo.newBuilder()) + .setRendererChangedInfo(EntityRendererChangedInfo.newBuilder()) + .setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(Vector.newBuilder())) + .setBornPos(Vector.newBuilder()) + .build(); - SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder() - .setEntityId(getId()) - .setEntityType(ProtEntityType.PROT_ENTITY_TYPE_AVATAR) - .addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder()) - .setEntityClientData(EntityClientData.newBuilder()) - .setEntityAuthorityInfo(authority) - .setLastMoveSceneTimeMs(this.getLastMoveSceneTimeMs()) - .setLastMoveReliableSeq(this.getLastMoveReliableSeq()) - .setLifeState(this.getLifeState().getValue()); + SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder() + .setEntityId(getId()) + .setEntityType(ProtEntityType.PROT_ENTITY_TYPE_AVATAR) + .addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder()) + .setEntityClientData(EntityClientData.newBuilder()) + .setEntityAuthorityInfo(authority) + .setLastMoveSceneTimeMs(this.getLastMoveSceneTimeMs()) + .setLastMoveReliableSeq(this.getLastMoveReliableSeq()) + .setLifeState(this.getLifeState().getValue()); - if (this.getScene() != null) { - entityInfo.setMotionInfo(this.getMotionInfo()); - } + if (this.getScene() != null) { + entityInfo.setMotionInfo(this.getMotionInfo()); + } - for (Int2FloatMap.Entry entry : getFightProperties().int2FloatEntrySet()) { - if (entry.getIntKey() == 0) { - continue; - } - FightPropPair fightProp = FightPropPair.newBuilder().setPropType(entry.getIntKey()).setPropValue(entry.getFloatValue()).build(); - entityInfo.addFightPropList(fightProp); - } + for (Int2FloatMap.Entry entry : getFightProperties().int2FloatEntrySet()) { + if (entry.getIntKey() == 0) { + continue; + } + FightPropPair fightProp = FightPropPair.newBuilder().setPropType(entry.getIntKey()).setPropValue(entry.getFloatValue()).build(); + entityInfo.addFightPropList(fightProp); + } - PropPair pair = PropPair.newBuilder() - .setType(PlayerProperty.PROP_LEVEL.getId()) - .setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, getAvatar().getLevel())) - .build(); - entityInfo.addPropList(pair); + PropPair pair = PropPair.newBuilder() + .setType(PlayerProperty.PROP_LEVEL.getId()) + .setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, getAvatar().getLevel())) + .build(); + entityInfo.addPropList(pair); - entityInfo.setAvatar(this.getSceneAvatarInfo()); + entityInfo.setAvatar(this.getSceneAvatarInfo()); - return entityInfo.build(); - } + return entityInfo.build(); + } - public AbilityControlBlock getAbilityControlBlock() { - AvatarData data = this.getAvatar().getAvatarData(); - AbilityControlBlock.Builder abilityControlBlock = AbilityControlBlock.newBuilder(); - int embryoId = 0; + public AbilityControlBlock getAbilityControlBlock() { + AvatarData data = this.getAvatar().getAvatarData(); + AbilityControlBlock.Builder abilityControlBlock = AbilityControlBlock.newBuilder(); + int embryoId = 0; - // Add avatar abilities - if (data.getAbilities() != null) { - for (int id : data.getAbilities()) { - AbilityEmbryo emb = AbilityEmbryo.newBuilder() - .setAbilityId(++embryoId) - .setAbilityNameHash(id) - .setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME) - .build(); - abilityControlBlock.addAbilityEmbryoList(emb); - } - } - // Add default abilities - for (int id : GameConstants.DEFAULT_ABILITY_HASHES) { - AbilityEmbryo emb = AbilityEmbryo.newBuilder() - .setAbilityId(++embryoId) - .setAbilityNameHash(id) - .setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME) - .build(); - abilityControlBlock.addAbilityEmbryoList(emb); - } - // Add team resonances - for (int id : this.getPlayer().getTeamManager().getTeamResonancesConfig()) { - AbilityEmbryo emb = AbilityEmbryo.newBuilder() - .setAbilityId(++embryoId) - .setAbilityNameHash(id) - .setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME) - .build(); - abilityControlBlock.addAbilityEmbryoList(emb); - } - // Add skill depot abilities - AvatarSkillDepotData skillDepot = GameData.getAvatarSkillDepotDataMap().get(this.getAvatar().getSkillDepotId()); - if (skillDepot != null && skillDepot.getAbilities() != null) { - for (int id : skillDepot.getAbilities()) { - AbilityEmbryo emb = AbilityEmbryo.newBuilder() - .setAbilityId(++embryoId) - .setAbilityNameHash(id) - .setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME) - .build(); - abilityControlBlock.addAbilityEmbryoList(emb); - } - } - // Add equip abilities - if (this.getAvatar().getExtraAbilityEmbryos().size() > 0) { - for (String skill : this.getAvatar().getExtraAbilityEmbryos()) { - AbilityEmbryo emb = AbilityEmbryo.newBuilder() - .setAbilityId(++embryoId) - .setAbilityNameHash(Utils.abilityHash(skill)) - .setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME) - .build(); - abilityControlBlock.addAbilityEmbryoList(emb); - } - } + // Add avatar abilities + if (data.getAbilities() != null) { + for (int id : data.getAbilities()) { + AbilityEmbryo emb = AbilityEmbryo.newBuilder() + .setAbilityId(++embryoId) + .setAbilityNameHash(id) + .setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME) + .build(); + abilityControlBlock.addAbilityEmbryoList(emb); + } + } + // Add default abilities + for (int id : GameConstants.DEFAULT_ABILITY_HASHES) { + AbilityEmbryo emb = AbilityEmbryo.newBuilder() + .setAbilityId(++embryoId) + .setAbilityNameHash(id) + .setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME) + .build(); + abilityControlBlock.addAbilityEmbryoList(emb); + } + // Add team resonances + for (int id : this.getPlayer().getTeamManager().getTeamResonancesConfig()) { + AbilityEmbryo emb = AbilityEmbryo.newBuilder() + .setAbilityId(++embryoId) + .setAbilityNameHash(id) + .setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME) + .build(); + abilityControlBlock.addAbilityEmbryoList(emb); + } + // Add skill depot abilities + AvatarSkillDepotData skillDepot = GameData.getAvatarSkillDepotDataMap().get(this.getAvatar().getSkillDepotId()); + if (skillDepot != null && skillDepot.getAbilities() != null) { + for (int id : skillDepot.getAbilities()) { + AbilityEmbryo emb = AbilityEmbryo.newBuilder() + .setAbilityId(++embryoId) + .setAbilityNameHash(id) + .setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME) + .build(); + abilityControlBlock.addAbilityEmbryoList(emb); + } + } + // Add equip abilities + if (this.getAvatar().getExtraAbilityEmbryos().size() > 0) { + for (String skill : this.getAvatar().getExtraAbilityEmbryos()) { + AbilityEmbryo emb = AbilityEmbryo.newBuilder() + .setAbilityId(++embryoId) + .setAbilityNameHash(Utils.abilityHash(skill)) + .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. + * @param rotation The new rotation. + */ + @Override public void move(Position newPosition, Position rotation) { + // Invoke player move event. + PlayerMoveEvent event = new PlayerMoveEvent( + this.getPlayer(), PlayerMoveEvent.MoveType.PLAYER, + this.getPosition(), newPosition + ); event.call(); + + // Set position and rotation. + super.move(event.getDestination(), rotation); } } diff --git a/src/main/java/emu/grasscutter/game/entity/EntityBaseGadget.java b/src/main/java/emu/grasscutter/game/entity/EntityBaseGadget.java index a5b2cb6c5..b063db33a 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityBaseGadget.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityBaseGadget.java @@ -8,11 +8,11 @@ public abstract class EntityBaseGadget extends GameEntity { public EntityBaseGadget(Scene scene) { super(scene); } - + public abstract int getGadgetId(); - + @Override public void onDeath(int killerId) { - + super.onDeath(killerId); // Invoke super class's onDeath() method. } } diff --git a/src/main/java/emu/grasscutter/game/entity/EntityClientGadget.java b/src/main/java/emu/grasscutter/game/entity/EntityClientGadget.java index 5e0c5eb00..769f571e7 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityClientGadget.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityClientGadget.java @@ -3,7 +3,6 @@ package emu.grasscutter.game.entity; import emu.grasscutter.game.player.Player; import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.game.world.Scene; -import emu.grasscutter.game.world.World; import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo; import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair; import emu.grasscutter.net.proto.ClientGadgetInfoOuterClass; @@ -11,7 +10,6 @@ import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityIn import emu.grasscutter.net.proto.EntityClientDataOuterClass.EntityClientData; import emu.grasscutter.net.proto.EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo; import emu.grasscutter.net.proto.EvtCreateGadgetNotifyOuterClass.EvtCreateGadgetNotify; -import emu.grasscutter.net.proto.GadgetClientParamOuterClass.GadgetClientParam; import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo; import emu.grasscutter.net.proto.PropPairOuterClass.PropPair; import emu.grasscutter.net.proto.ProtEntityTypeOuterClass.ProtEntityType; @@ -25,10 +23,10 @@ import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap; public class EntityClientGadget extends EntityBaseGadget { private final Player owner; - + private final Position pos; private final Position rot; - + private int configId; private int campId; private int campType; @@ -37,7 +35,7 @@ public class EntityClientGadget extends EntityBaseGadget { private boolean asyncLoad; private int originalOwnerEntityId; - + public EntityClientGadget(Scene scene, Player player, EvtCreateGadgetNotify notify) { super(scene); this.owner = player; @@ -59,20 +57,20 @@ public class EntityClientGadget extends EntityBaseGadget { this.originalOwnerEntityId = this.ownerEntityId; } } - + @Override public int getGadgetId() { return configId; } - + public Player getOwner() { return owner; } - + public int getCampId() { return campId; } - + public int getCampType() { return campType; } @@ -80,7 +78,7 @@ public class EntityClientGadget extends EntityBaseGadget { public int getOwnerEntityId() { return ownerEntityId; } - + public int getTargetEntityId() { return targetEntityId; } @@ -95,7 +93,7 @@ public class EntityClientGadget extends EntityBaseGadget { @Override public void onDeath(int killerId) { - + super.onDeath(killerId); // Invoke super class's onDeath() method. } @Override @@ -124,7 +122,7 @@ public class EntityClientGadget extends EntityBaseGadget { .setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(Vector.newBuilder())) .setBornPos(Vector.newBuilder()) .build(); - + SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder() .setEntityId(getId()) .setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET) @@ -133,13 +131,13 @@ public class EntityClientGadget extends EntityBaseGadget { .setEntityClientData(EntityClientData.newBuilder()) .setEntityAuthorityInfo(authority) .setLifeState(1); - + PropPair pair = PropPair.newBuilder() .setType(PlayerProperty.PROP_LEVEL.getId()) .setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 1)) .build(); entityInfo.addPropList(pair); - + ClientGadgetInfoOuterClass.ClientGadgetInfo clientGadget = ClientGadgetInfoOuterClass.ClientGadgetInfo.newBuilder() .setCampId(this.getCampId()) .setCampType(this.getCampType()) @@ -147,7 +145,7 @@ public class EntityClientGadget extends EntityBaseGadget { .setTargetEntityId(this.getTargetEntityId()) .setAsyncLoad(this.isAsyncLoad()) .build(); - + SceneGadgetInfo.Builder gadgetInfo = SceneGadgetInfo.newBuilder() .setGadgetId(this.getGadgetId()) .setOwnerEntityId(this.getOwnerEntityId()) @@ -157,7 +155,7 @@ public class EntityClientGadget extends EntityBaseGadget { .setAuthorityPeerId(this.getOwner().getPeerId()); entityInfo.setGadget(gadgetInfo); - + return entityInfo.build(); } } diff --git a/src/main/java/emu/grasscutter/game/entity/EntityGadget.java b/src/main/java/emu/grasscutter/game/entity/EntityGadget.java index 7e68ad2bf..10a747fb2 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityGadget.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityGadget.java @@ -175,6 +175,8 @@ public class EntityGadget extends EntityBaseGadget { @Override public void onDeath(int killerId) { + super.onDeath(killerId); // Invoke super class's onDeath() method. + if (this.getSpawnEntry() != null) { this.getScene().getDeadSpawnedEntities().add(getSpawnEntry()); } diff --git a/src/main/java/emu/grasscutter/game/entity/EntityMonster.java b/src/main/java/emu/grasscutter/game/entity/EntityMonster.java index 4c1426c02..d60c96df7 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityMonster.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityMonster.java @@ -158,6 +158,8 @@ public class EntityMonster extends GameEntity { @Override public void onDeath(int killerId) { + super.onDeath(killerId); // Invoke super class's onDeath() method. + if (this.getSpawnEntry() != null) { this.getScene().getDeadSpawnedEntities().add(getSpawnEntry()); } diff --git a/src/main/java/emu/grasscutter/game/entity/GameEntity.java b/src/main/java/emu/grasscutter/game/entity/GameEntity.java index e505969c0..c0c15aaa2 100644 --- a/src/main/java/emu/grasscutter/game/entity/GameEntity.java +++ b/src/main/java/emu/grasscutter/game/entity/GameEntity.java @@ -15,6 +15,7 @@ import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo; import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState; import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo; import emu.grasscutter.net.proto.VectorOuterClass.Vector; +import emu.grasscutter.server.event.entity.EntityDeathEvent; import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; import emu.grasscutter.utils.Position; import it.unimi.dsi.fastutil.ints.Int2FloatMap; @@ -23,64 +24,64 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; public abstract class GameEntity { - protected int id; - private final Scene scene; - private SpawnDataEntry spawnEntry; + protected int id; + private final Scene scene; + private SpawnDataEntry spawnEntry; - private int blockId; - private int configId; - private int groupId; + private int blockId; + private int configId; + private int groupId; - private MotionState moveState; - private int lastMoveSceneTimeMs; - private int lastMoveReliableSeq; + private MotionState moveState; + private int lastMoveSceneTimeMs; + private int lastMoveReliableSeq; - // Abilities - private Map metaOverrideMap; - private Int2ObjectMap metaModifiers; + // Abilities + private Map metaOverrideMap; + private Int2ObjectMap metaModifiers; - public GameEntity(Scene scene) { - this.scene = scene; - this.moveState = MotionState.MOTION_STATE_NONE; - } + public GameEntity(Scene scene) { + this.scene = scene; + this.moveState = MotionState.MOTION_STATE_NONE; + } - public int getId() { - return this.id; - } + public int getId() { + return this.id; + } - public int getEntityType() { - return getId() >> 24; - } + public int getEntityType() { + return getId() >> 24; + } - public World getWorld() { - return this.getScene().getWorld(); - } + public World getWorld() { + return this.getScene().getWorld(); + } - public Scene getScene() { - return this.scene; - } + public Scene getScene() { + return this.scene; + } - public boolean isAlive() { - return true; - } + public boolean isAlive() { + return true; + } - public LifeState getLifeState() { - return isAlive() ? LifeState.LIFE_ALIVE : LifeState.LIFE_DEAD; - } + public LifeState getLifeState() { + return isAlive() ? LifeState.LIFE_ALIVE : LifeState.LIFE_DEAD; + } - public Map getMetaOverrideMap() { - if (this.metaOverrideMap == null) { - this.metaOverrideMap = new HashMap<>(); - } - return this.metaOverrideMap; - } + public Map getMetaOverrideMap() { + if (this.metaOverrideMap == null) { + this.metaOverrideMap = new HashMap<>(); + } + return this.metaOverrideMap; + } - public Int2ObjectMap getMetaModifiers() { - if (this.metaModifiers == null) { - this.metaModifiers = new Int2ObjectOpenHashMap<>(); - } - return this.metaModifiers; - } + public Int2ObjectMap getMetaModifiers() { + if (this.metaModifiers == null) { + this.metaModifiers = new Int2ObjectOpenHashMap<>(); + } + return this.metaModifiers; + } public abstract Int2FloatOpenHashMap getFightProperties(); @@ -230,6 +231,17 @@ public abstract class GameEntity { } } + /** + * Move this entity to a new position. + * @param position The new position. + * @param rotation The new rotation. + */ + public void move(Position position, Position rotation) { + // Set the position and rotation. + this.getPosition().set(position); + this.getRotation().set(rotation); + } + /** * Called when a player interacts with this entity * @param player Player that is interacting with this entity @@ -251,7 +263,9 @@ public abstract class GameEntity { * @param killerId Entity id of the entity that killed this entity */ public void onDeath(int killerId) { - + // Invoke entity death event. + EntityDeathEvent event = new EntityDeathEvent(this, killerId); + event.call(); } public abstract SceneEntityInfo toProto(); diff --git a/src/main/java/emu/grasscutter/game/managers/energy/EnergyManager.java b/src/main/java/emu/grasscutter/game/managers/energy/EnergyManager.java index e94a219fc..cf449977a 100644 --- a/src/main/java/emu/grasscutter/game/managers/energy/EnergyManager.java +++ b/src/main/java/emu/grasscutter/game/managers/energy/EnergyManager.java @@ -28,7 +28,6 @@ import emu.grasscutter.net.proto.EvtBeingHitInfoOuterClass.EvtBeingHitInfo; import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason; import emu.grasscutter.server.game.GameSession; import emu.grasscutter.utils.Position; -import it.unimi.dsi.fastutil.ints.Int2IntMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; @@ -42,15 +41,14 @@ import java.util.Optional; import java.util.concurrent.ThreadLocalRandom; import static emu.grasscutter.config.Configuration.GAME_OPTIONS; -import static java.util.Map.entry; import com.google.gson.reflect.TypeToken; import com.google.protobuf.InvalidProtocolBufferException; public class EnergyManager extends BasePlayerManager { private final Map avatarNormalProbabilities; -// energyUsage for each player - private Boolean energyUsage; + + private boolean energyUsage; // Should energy usage be enabled for this player? private final static Int2ObjectMap> energyDropData = new Int2ObjectOpenHashMap<>(); private final static Int2ObjectMap> skillParticleGenerationData = new Int2ObjectOpenHashMap<>(); @@ -90,9 +88,9 @@ public class EnergyManager extends BasePlayerManager { } } - /********** - Particle creation for elemental skills. - **********/ + /** + * Particle creation for elemental skills. + */ private int getBallCountForAvatar(int avatarId) { // We default to two particles. int count = 2; @@ -120,12 +118,12 @@ public class EnergyManager extends BasePlayerManager { } private int getBallIdForElement(ElementType element) { - // If we have no element, we default to an elementless particle. + // If we have no element, we default to an element-less particle. if (element == null) { return 2024; } - // Otherwise, we determin the particle's ID based on the element. + // Otherwise, we determine the particle's ID based on the element. return switch (element) { case Fire -> 2017; case Water -> 2018; @@ -156,7 +154,7 @@ public class EnergyManager extends BasePlayerManager { int amount = 2; // Try to get the casting avatar from the player's party. - Optional avatarEntity = getCastingAvatarEntityForEnergy(invoke.getEntityId()); + Optional avatarEntity = this.getCastingAvatarEntityForEnergy(invoke.getEntityId()); // Bug: invokes twice sometimes, Ayato, Keqing // ToDo: deal with press, hold difference. deal with charge(Beidou, Yunjin) @@ -174,20 +172,21 @@ public class EnergyManager extends BasePlayerManager { // particles we have to generate. if (skillDepotData != null) { ElementType element = skillDepotData.getElementType(); - itemId = getBallIdForElement(element); + itemId = this.getBallIdForElement(element); } } } // Generate the particles. for (int i = 0; i < amount; i++) { - generateElemBall(itemId, new Position(action.getPos()), 1); + this.generateElemBall(itemId, new Position(action.getPos()), 1); } } - /********** - Pickup of elemental particles and orbs. - **********/ + /** + * Pickup of elemental particles and orbs. + * @param elemBall The elemental particle or orb. + */ public void handlePickupElemBall(GameItem elemBall) { // Check if the item is indeed an energy particle/orb. if (elemBall.getItemId() < 2001 ||elemBall.getItemId() > 2024) { @@ -242,9 +241,10 @@ public class EnergyManager extends BasePlayerManager { } } - /********** - Energy generation for NAs/CAs. - **********/ + /** + * Energy generation for NAs/CAs. + * @param avatar The avatar. + */ private void generateEnergyForNormalAndCharged(EntityAvatar avatar) { // This logic is based on the descriptions given in // https://genshin-impact.fandom.com/wiki/Energy#Energy_Generated_by_Normal_Attacks @@ -290,11 +290,10 @@ public class EnergyManager extends BasePlayerManager { // Make sure the target is an actual enemy. GameEntity targetEntity = this.player.getScene().getEntityById(attackRes.getDefenseId()); - if (!(targetEntity instanceof EntityMonster)) { + if (!(targetEntity instanceof EntityMonster targetMonster)) { return; } - EntityMonster targetMonster = (EntityMonster)targetEntity; MonsterType targetType = targetMonster.getMonsterData().getType(); if (targetType != MonsterType.MONSTER_ORDINARY && targetType != MonsterType.MONSTER_BOSS) { return; @@ -319,10 +318,10 @@ public class EnergyManager extends BasePlayerManager { this.generateEnergyForNormalAndCharged(attackerEntity.get()); } + /* + * Energy logic related to using skills. + */ - /********** - Energy logic related to using skills. - **********/ private void handleBurstCast(Avatar avatar, int skillId) { // Don't do anything if energy usage is disabled. if (!GAME_OPTIONS.energyUsage || !this.energyUsage) { @@ -351,9 +350,10 @@ public class EnergyManager extends BasePlayerManager { this.handleBurstCast(avatar, skillId); } - /********** - Monster energy drops. - **********/ + /* + * Monster energy drops. + */ + private void generateElemBallDrops(EntityMonster monster, int dropId) { // Generate all drops specified for the given drop id. if (!energyDropData.containsKey(dropId)) { @@ -365,6 +365,7 @@ public class EnergyManager extends BasePlayerManager { this.generateElemBall(info.getBallId(), monster.getPosition(), info.getCount()); } } + public void handleMonsterEnergyDrop(EntityMonster monster, float hpBeforeDamage, float hpAfterDamage) { // Make sure this is actually a monster. // Note that some wildlife also has that type, like boars or birds. @@ -373,7 +374,7 @@ public class EnergyManager extends BasePlayerManager { return; } - // Calculate the HP tresholds 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 thresholdBefore = hpBeforeDamage / maxHp; float thresholdAfter = hpAfterDamage / maxHp; @@ -386,19 +387,20 @@ public class EnergyManager extends BasePlayerManager { float threshold = drop.getHpPercent() / 100.0f; if (threshold < thresholdBefore && threshold >= thresholdAfter) { - generateElemBallDrops(monster, drop.getDropId()); + this.generateElemBallDrops(monster, drop.getDropId()); } } // Handle kill drops. if (hpAfterDamage <= 0 && monster.getMonsterData().getKillDropId() != 0) { - generateElemBallDrops(monster, monster.getMonsterData().getKillDropId()); + this.generateElemBallDrops(monster, monster.getMonsterData().getKillDropId()); } } - /********** - Utility. - **********/ + /* + * Utilities. + */ + private void generateElemBall(int ballId, Position position, int count) { // Generate a particle/orb with the specified parameters. ItemData itemData = GameData.getItemDataMap().get(ballId); @@ -417,7 +419,7 @@ public class EnergyManager extends BasePlayerManager { // that cast the skill. // Try to get the invoking entity from the scene. - GameEntity entity = 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, // or not an `EntityClientGadget`, we assume that we are directly looking at the casting avatar @@ -430,20 +432,20 @@ public class EnergyManager extends BasePlayerManager { : ((EntityClientGadget)entity).getOriginalOwnerEntityId(); // Finally, find the avatar entity in the player's team. - return player.getTeamManager().getActiveTeam() + return this.player.getTeamManager().getActiveTeam() .stream() .filter(character -> character.getId() == avatarEntityId) .findFirst(); } - public Boolean getEnergyUsage() { - return energyUsage; + public boolean getEnergyUsage() { + return this.energyUsage; } - public void setEnergyUsage(Boolean energyUsage) { + public void setEnergyUsage(boolean energyUsage) { this.energyUsage = energyUsage; if (!energyUsage) { // Refill team energy if usage is disabled - for (EntityAvatar entityAvatar : player.getTeamManager().getActiveTeam()) { + for (EntityAvatar entityAvatar : this.player.getTeamManager().getActiveTeam()) { entityAvatar.addEnergy(1000, PropChangeReason.PROP_CHANGE_REASON_GM,true); } } diff --git a/src/main/java/emu/grasscutter/game/managers/mapmark/MapMarksManager.java b/src/main/java/emu/grasscutter/game/managers/mapmark/MapMarksManager.java index 713cd69c0..a40fbaaa6 100644 --- a/src/main/java/emu/grasscutter/game/managers/mapmark/MapMarksManager.java +++ b/src/main/java/emu/grasscutter/game/managers/mapmark/MapMarksManager.java @@ -5,11 +5,11 @@ import emu.grasscutter.game.player.Player; import emu.grasscutter.net.proto.MapMarkPointTypeOuterClass.MapMarkPointType; import emu.grasscutter.net.proto.MarkMapReqOuterClass.MarkMapReq; import emu.grasscutter.net.proto.MarkMapReqOuterClass.MarkMapReq.Operation; +import emu.grasscutter.server.event.player.PlayerTeleportEvent.TeleportType; import emu.grasscutter.server.packet.send.PacketMarkMapRsp; import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify; import emu.grasscutter.utils.Position; -import java.util.HashMap; import java.util.Map; public class MapMarksManager extends BasePlayerManager { @@ -20,7 +20,7 @@ public class MapMarksManager extends BasePlayerManager { } public Map getMapMarks() { - return getPlayer().getMapMarks(); + return this.getPlayer().getMapMarks(); } public void handleMapMarkReq(MarkMapReq req) { @@ -30,26 +30,26 @@ public class MapMarksManager extends BasePlayerManager { MapMark createMark = new MapMark(req.getMark()); // keep teleporting functionality on fishhook mark. if (createMark.getMapMarkPointType() == MapMarkPointType.MAP_MARK_POINT_TYPE_FISH_POOL) { - teleport(player, createMark); + this.teleport(player, createMark); return; } - addMapMark(createMark); + this.addMapMark(createMark); } case OPERATION_MOD -> { MapMark oldMark = new MapMark(req.getOld()); - removeMapMark(oldMark.getPosition()); + this.removeMapMark(oldMark.getPosition()); MapMark newMark = new MapMark(req.getMark()); - addMapMark(newMark); + this.addMapMark(newMark); } case OPERATION_DEL -> { MapMark deleteMark = new MapMark(req.getMark()); - removeMapMark(deleteMark.getPosition()); + this.removeMapMark(deleteMark.getPosition()); } } if (op != Operation.OPERATION_GET) { - save(); + this.save(); } - player.getSession().send(new PacketMarkMapRsp(getMapMarks())); + player.getSession().send(new PacketMarkMapRsp(this.getMapMarks())); } public String getMapMarkKey(Position position) { @@ -57,27 +57,25 @@ public class MapMarksManager extends BasePlayerManager { } public void removeMapMark(Position position) { - getMapMarks().remove(getMapMarkKey(position)); + this.getMapMarks().remove(this.getMapMarkKey(position)); } public void addMapMark(MapMark mapMark) { - if (getMapMarks().size() < mapMarkMaxCount) { - getMapMarks().put(getMapMarkKey(mapMark.getPosition()), mapMark); + if (this.getMapMarks().size() < mapMarkMaxCount) { + this.getMapMarks().put(this.getMapMarkKey(mapMark.getPosition()), mapMark); } } private void teleport(Player player, MapMark mapMark) { float y; try { - y = (float)Integer.parseInt(mapMark.getName()); + y = Float.parseFloat(mapMark.getName()); } catch (Exception e) { y = 300; } + Position pos = mapMark.getPosition(); - player.getPosition().set(pos.getX(), y, pos.getZ()); - if (mapMark.getSceneId() != player.getSceneId()) { - player.getWorld().transferPlayerToScene(player, mapMark.getSceneId(), player.getPosition()); - } + player.getWorld().transferPlayerToScene(player, mapMark.getSceneId(), TeleportType.MAP, new Position(pos.getX(), y, pos.getZ())); player.getScene().broadcastPacket(new PacketSceneEntityAppearNotify(player)); } } diff --git a/src/main/java/emu/grasscutter/game/player/TeamManager.java b/src/main/java/emu/grasscutter/game/player/TeamManager.java index ce4aafb66..8421b4ecf 100644 --- a/src/main/java/emu/grasscutter/game/player/TeamManager.java +++ b/src/main/java/emu/grasscutter/game/player/TeamManager.java @@ -20,6 +20,7 @@ import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.EnterTypeOuterClass.EnterType; import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState; import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType; +import emu.grasscutter.server.event.player.PlayerTeamDeathEvent; import emu.grasscutter.server.packet.send.PacketAvatarDieAnimationEndRsp; import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify; import emu.grasscutter.server.packet.send.PacketAvatarLifeStateChangeNotify; @@ -76,7 +77,7 @@ public class TeamManager extends BasePlayerDataManager { } public World getWorld() { - return getPlayer().getWorld(); + return this.getPlayer().getWorld(); } public Map getTeams() { @@ -123,7 +124,7 @@ public class TeamManager extends BasePlayerDataManager { } public long getCurrentCharacterGuid() { - return getCurrentAvatarEntity().getAvatar().getGuid(); + return this.getCurrentAvatarEntity().getAvatar().getGuid(); } public TeamInfo getCurrentTeamInfo() { @@ -166,20 +167,20 @@ public class TeamManager extends BasePlayerDataManager { } public EntityAvatar getCurrentAvatarEntity() { - return getActiveTeam().get(currentCharacterIndex); + return this.getActiveTeam().get(currentCharacterIndex); } public boolean isSpawned() { - return getPlayer().getScene() != null && getPlayer().getScene().getEntities().containsKey(getCurrentAvatarEntity().getId()); + return this.getPlayer().getScene() != null && this.getPlayer().getScene().getEntities().containsKey(this.getCurrentAvatarEntity().getId()); } public int getMaxTeamSize() { - if (getPlayer().isInMultiplayer()) { + if (this.getPlayer().isInMultiplayer()) { int max = GAME_OPTIONS.avatarLimits.multiplayerTeam; - if (getPlayer().getWorld().getHost() == this.getPlayer()) { - return Math.max(1, (int) Math.ceil(max / (double) getWorld().getPlayerCount())); + if (this.getPlayer().getWorld().getHost() == this.getPlayer()) { + return Math.max(1, (int) Math.ceil(max / (double) this.getWorld().getPlayerCount())); } - return Math.max(1, (int) Math.floor(max / (double) getWorld().getPlayerCount())); + return Math.max(1, (int) Math.floor(max / (double) this.getWorld().getPlayerCount())); } return GAME_OPTIONS.avatarLimits.singlePlayerTeam; @@ -191,14 +192,14 @@ public class TeamManager extends BasePlayerDataManager { * Returns true if there is space to add the number of avatars to the team. */ public boolean canAddAvatarsToTeam(TeamInfo team, int avatars) { - return team.size() + avatars <= getMaxTeamSize(); + return team.size() + avatars <= this.getMaxTeamSize(); } /** * Returns true if there is space to add to the team. */ public boolean canAddAvatarToTeam(TeamInfo team) { - return canAddAvatarsToTeam(team, 1); + return this.canAddAvatarsToTeam(team, 1); } /** @@ -209,7 +210,7 @@ public class TeamManager extends BasePlayerDataManager { if (this.useTemporarilyTeamIndex != -1) { return false; } - return canAddAvatarsToTeam(this.getCurrentTeamInfo(), avatars); + return this.canAddAvatarsToTeam(this.getCurrentTeamInfo(), avatars); } /** @@ -217,7 +218,7 @@ public class TeamManager extends BasePlayerDataManager { * If the current team is temporary, returns false. */ public boolean canAddAvatarToCurrentTeam() { - return canAddAvatarsToCurrentTeam(1); + return this.canAddAvatarsToCurrentTeam(1); } /** @@ -226,7 +227,7 @@ public class TeamManager extends BasePlayerDataManager { * If some can not be added, returns false and does not add any. */ public boolean addAvatarsToTeam(TeamInfo team, Collection avatars) { - if (!canAddAvatarsToTeam(team, avatars.size())) { + if (!this.canAddAvatarsToTeam(team, avatars.size())) { return false; } @@ -237,20 +238,20 @@ public class TeamManager extends BasePlayerDataManager { if (this.getPlayer().isInMultiplayer()) { if (team.equals(this.getMpTeam())) { // MP team Packet - this.updateTeamEntities(new PacketChangeMpTeamAvatarRsp(getPlayer(), team)); + this.updateTeamEntities(new PacketChangeMpTeamAvatarRsp(this.getPlayer(), team)); } } else { // SP team update packet - getPlayer().sendPacket(new PacketAvatarTeamUpdateNotify(getPlayer())); + this.getPlayer().sendPacket(new PacketAvatarTeamUpdateNotify(this.getPlayer())); int teamId = this.getTeamId(team); if (teamId != -1) { // This is one of the player's teams // Update entites if (teamId == this.getCurrentTeamId()) { - this.updateTeamEntities(new PacketSetUpAvatarTeamRsp(getPlayer(), teamId, team)); + this.updateTeamEntities(new PacketSetUpAvatarTeamRsp(this.getPlayer(), teamId, team)); } else { - getPlayer().sendPacket(new PacketSetUpAvatarTeamRsp(getPlayer(), teamId, team)); + this.getPlayer().sendPacket(new PacketSetUpAvatarTeamRsp(this.getPlayer(), teamId, team)); } } } @@ -263,7 +264,7 @@ public class TeamManager extends BasePlayerDataManager { * Returns true if successful. */ public boolean addAvatarToTeam(TeamInfo team, Avatar avatar) { - return addAvatarsToTeam(team, Collections.singleton(avatar)); + return this.addAvatarsToTeam(team, Collections.singleton(avatar)); } /** @@ -276,7 +277,7 @@ public class TeamManager extends BasePlayerDataManager { if (this.useTemporarilyTeamIndex != -1) { return false; } - return addAvatarsToTeam(this.getCurrentTeamInfo(), avatars); + return this.addAvatarsToTeam(this.getCurrentTeamInfo(), avatars); } /** @@ -285,7 +286,7 @@ public class TeamManager extends BasePlayerDataManager { * Returns true if successful. */ public boolean addAvatarToCurrentTeam(Avatar avatar) { - return addAvatarsToCurrentTeam(Collections.singleton(avatar)); + return this.addAvatarsToCurrentTeam(Collections.singleton(avatar)); } private void updateTeamResonances() { @@ -294,7 +295,7 @@ public class TeamManager extends BasePlayerDataManager { this.getTeamResonances().clear(); this.getTeamResonancesConfig().clear(); - for (EntityAvatar entity : getActiveTeam()) { + for (EntityAvatar entity : this.getActiveTeam()) { AvatarSkillDepotData skillData = entity.getAvatar().getAvatarData().getSkillDepot(); if (skillData != null) { map.addTo(skillData.getElementType().getValue(), 1); @@ -329,7 +330,7 @@ public class TeamManager extends BasePlayerDataManager { Int2ObjectMap existingAvatars = new Int2ObjectOpenHashMap<>(); int prevSelectedAvatarIndex = -1; - for (EntityAvatar entity : getActiveTeam()) { + for (EntityAvatar entity : this.getActiveTeam()) { existingAvatars.put(entity.getAvatar().getAvatarId(), entity); } @@ -348,7 +349,7 @@ public class TeamManager extends BasePlayerDataManager { prevSelectedAvatarIndex = i; } } else { - entity = new EntityAvatar(getPlayer().getScene(), getPlayer().getAvatars().getAvatarById(avatarId)); + entity = new EntityAvatar(this.getPlayer().getScene(), this.getPlayer().getAvatars().getAvatarById(avatarId)); } this.getActiveTeam().add(entity); @@ -356,7 +357,7 @@ public class TeamManager extends BasePlayerDataManager { // Unload removed entities for (EntityAvatar entity : existingAvatars.values()) { - getPlayer().getScene().removeEntity(entity); + this.getPlayer().getScene().removeEntity(entity); entity.getAvatar().save(); } @@ -368,33 +369,33 @@ public class TeamManager extends BasePlayerDataManager { this.currentCharacterIndex = prevSelectedAvatarIndex; // Update team resonances - updateTeamResonances(); + this.updateTeamResonances(); // Packets - getPlayer().getWorld().broadcastPacket(new PacketSceneTeamUpdateNotify(getPlayer())); + this.getPlayer().getWorld().broadcastPacket(new PacketSceneTeamUpdateNotify(this.getPlayer())); // Skill charges packet - Yes, this is official server behavior as of 2.6.0 - for (EntityAvatar entity : getActiveTeam()) { + for (EntityAvatar entity : this.getActiveTeam()) { if (entity.getAvatar().getSkillExtraChargeMap().size() > 0) { - getPlayer().sendPacket(new PacketAvatarSkillInfoNotify(entity.getAvatar())); + this.getPlayer().sendPacket(new PacketAvatarSkillInfoNotify(entity.getAvatar())); } } // Run callback if (responsePacket != null) { - getPlayer().sendPacket(responsePacket); + this.getPlayer().sendPacket(responsePacket); } // Check if character changed - if (currentEntity != getCurrentAvatarEntity()) { + if (currentEntity != this.getCurrentAvatarEntity()) { // Remove and Add - getPlayer().getScene().replaceEntity(currentEntity, getCurrentAvatarEntity()); + this.getPlayer().getScene().replaceEntity(currentEntity, this.getCurrentAvatarEntity()); } } public synchronized void setupAvatarTeam(int teamId, List list) { // Sanity checks - if (list.size() == 0 || list.size() > getMaxTeamSize() || getPlayer().isInMultiplayer()) { + if (list.size() == 0 || list.size() > this.getMaxTeamSize() || this.getPlayer().isInMultiplayer()) { return; } @@ -407,7 +408,7 @@ public class TeamManager extends BasePlayerDataManager { // Set team data LinkedHashSet newTeam = new LinkedHashSet<>(); for (Long aLong : list) { - Avatar avatar = getPlayer().getAvatars().getAvatarByGuid(aLong); + Avatar avatar = this.getPlayer().getAvatars().getAvatarByGuid(aLong); if (avatar == null || newTeam.contains(avatar)) { // Should never happen return; @@ -422,7 +423,7 @@ public class TeamManager extends BasePlayerDataManager { public void setupMpTeam(List list) { // Sanity checks - if (list.size() == 0 || list.size() > getMaxTeamSize() || !getPlayer().isInMultiplayer()) { + if (list.size() == 0 || list.size() > this.getMaxTeamSize() || !this.getPlayer().isInMultiplayer()) { return; } @@ -431,7 +432,7 @@ public class TeamManager extends BasePlayerDataManager { // Set team data LinkedHashSet newTeam = new LinkedHashSet<>(); for (Long aLong : list) { - Avatar avatar = getPlayer().getAvatars().getAvatarByGuid(aLong); + Avatar avatar = this.getPlayer().getAvatars().getAvatarByGuid(aLong); if (avatar == null || newTeam.contains(avatar)) { // Should never happen return; @@ -447,14 +448,14 @@ public class TeamManager extends BasePlayerDataManager { public void setupTemporaryTeam(List> guidList) { this.temporaryTeam = guidList.stream().map(list -> { // Sanity checks - if (list.size() == 0 || list.size() > getMaxTeamSize()) { + if (list.size() == 0 || list.size() > this.getMaxTeamSize()) { return null; } // Set team data LinkedHashSet newTeam = new LinkedHashSet<>(); for (Long aLong : list) { - Avatar avatar = getPlayer().getAvatars().getAvatarByGuid(aLong); + Avatar avatar = this.getPlayer().getAvatars().getAvatarByGuid(aLong); if (avatar == null || newTeam.contains(avatar)) { // Should never happen return null; @@ -474,7 +475,7 @@ public class TeamManager extends BasePlayerDataManager { public void useTemporaryTeam(int index) { this.useTemporarilyTeamIndex = index; - updateTeamEntities(null); + this.updateTeamEntities(null); } public void cleanTemporaryTeam() { @@ -485,11 +486,11 @@ public class TeamManager extends BasePlayerDataManager { this.useTemporarilyTeamIndex = -1; this.temporaryTeam = null; - updateTeamEntities(null); + this.updateTeamEntities(null); } public synchronized void setCurrentTeam(int teamId) { // - if (getPlayer().isInMultiplayer()) { + if (this.getPlayer().isInMultiplayer()) { return; } @@ -514,7 +515,7 @@ public class TeamManager extends BasePlayerDataManager { teamInfo.setName(teamName); // Packet - getPlayer().sendPacket(new PacketChangeTeamNameRsp(teamId, teamName)); + this.getPlayer().sendPacket(new PacketChangeTeamNameRsp(teamId, teamName)); } public synchronized void changeAvatar(long guid) { @@ -526,10 +527,10 @@ public class TeamManager extends BasePlayerDataManager { EntityAvatar newEntity = null; int index = -1; - for (int i = 0; i < getActiveTeam().size(); i++) { - if (guid == getActiveTeam().get(i).getAvatar().getGuid()) { + for (int i = 0; i < this.getActiveTeam().size(); i++) { + if (guid == this.getActiveTeam().get(i).getAvatar().getGuid()) { index = i; - newEntity = getActiveTeam().get(i); + newEntity = this.getActiveTeam().get(i); } } @@ -544,8 +545,8 @@ public class TeamManager extends BasePlayerDataManager { oldEntity.setMotionState(MotionState.MOTION_STATE_STANDBY); // Remove and Add - getPlayer().getScene().replaceEntity(oldEntity, newEntity); - getPlayer().sendPacket(new PacketChangeAvatarRsp(guid)); + this.getPlayer().getScene().replaceEntity(oldEntity, newEntity); + this.getPlayer().sendPacket(new PacketChangeAvatarRsp(guid)); } public void onAvatarDie(long dieGuid) { @@ -562,7 +563,7 @@ public class TeamManager extends BasePlayerDataManager { // Died in water. Do not replace // The official server has skipped this notify and will just respawn the team immediately after the animation. // TODO: Perhaps find a way to get vanilla experience? - getPlayer().sendPacket(new PacketWorldPlayerDieNotify(dieType, killedBy)); + this.getPlayer().sendPacket(new PacketWorldPlayerDieNotify(dieType, killedBy)); } else { // Replacement avatar EntityAvatar replacement = null; @@ -579,20 +580,24 @@ public class TeamManager extends BasePlayerDataManager { if (replacement == null) { // No more living team members... - getPlayer().sendPacket(new PacketWorldPlayerDieNotify(dieType, killedBy)); + this.getPlayer().sendPacket(new PacketWorldPlayerDieNotify(dieType, killedBy)); + // Invoke player team death event. + PlayerTeamDeathEvent event = new PlayerTeamDeathEvent(this.getPlayer(), + this.getActiveTeam().get(this.getCurrentCharacterIndex())); + event.call(); } else { // Set index and spawn replacement member this.setCurrentCharacterIndex(replaceIndex); - getPlayer().getScene().addEntity(replacement); + this.getPlayer().getScene().addEntity(replacement); } } // Response packet - getPlayer().sendPacket(new PacketAvatarDieAnimationEndRsp(deadAvatar.getId(), 0)); + this.getPlayer().sendPacket(new PacketAvatarDieAnimationEndRsp(deadAvatar.getId(), 0)); } public boolean reviveAvatar(Avatar avatar) { - for (EntityAvatar entity : getActiveTeam()) { + for (EntityAvatar entity : this.getActiveTeam()) { if (entity.getAvatar() == avatar) { if (entity.isAlive()) { return false; @@ -602,8 +607,8 @@ public class TeamManager extends BasePlayerDataManager { FightProperty.FIGHT_PROP_CUR_HP, entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * .1f ); - getPlayer().sendPacket(new PacketAvatarFightPropUpdateNotify(entity.getAvatar(), FightProperty.FIGHT_PROP_CUR_HP)); - getPlayer().sendPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar())); + this.getPlayer().sendPacket(new PacketAvatarFightPropUpdateNotify(entity.getAvatar(), FightProperty.FIGHT_PROP_CUR_HP)); + this.getPlayer().sendPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar())); return true; } } @@ -612,7 +617,7 @@ public class TeamManager extends BasePlayerDataManager { } public boolean healAvatar(Avatar avatar, int healRate, int healAmount) { - for (EntityAvatar entity : getActiveTeam()) { + for (EntityAvatar entity : this.getActiveTeam()) { if (entity.getAvatar() == avatar) { if (!entity.isAlive()) { return false; @@ -627,8 +632,8 @@ public class TeamManager extends BasePlayerDataManager { entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) ) ); - getPlayer().sendPacket(new PacketAvatarFightPropUpdateNotify(entity.getAvatar(), FightProperty.FIGHT_PROP_CUR_HP)); - getPlayer().sendPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar())); + this.getPlayer().sendPacket(new PacketAvatarFightPropUpdateNotify(entity.getAvatar(), FightProperty.FIGHT_PROP_CUR_HP)); + this.getPlayer().sendPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar())); return true; } } @@ -646,29 +651,29 @@ public class TeamManager extends BasePlayerDataManager { player.getStaminaManager().stopSustainedStaminaHandler(); // prevent drowning immediately after respawn // Revive all team members - for (EntityAvatar entity : getActiveTeam()) { + for (EntityAvatar entity : this.getActiveTeam()) { entity.setFightProperty( FightProperty.FIGHT_PROP_CUR_HP, entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * .4f ); - getPlayer().sendPacket(new PacketAvatarFightPropUpdateNotify(entity.getAvatar(), FightProperty.FIGHT_PROP_CUR_HP)); - getPlayer().sendPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar())); + this.getPlayer().sendPacket(new PacketAvatarFightPropUpdateNotify(entity.getAvatar(), FightProperty.FIGHT_PROP_CUR_HP)); + this.getPlayer().sendPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar())); } // Teleport player - getPlayer().sendPacket(new PacketPlayerEnterSceneNotify(getPlayer(), EnterType.ENTER_TYPE_SELF, EnterReason.Revival, 3, GameConstants.START_POSITION)); + this.getPlayer().sendPacket(new PacketPlayerEnterSceneNotify(this.getPlayer(), EnterType.ENTER_TYPE_SELF, EnterReason.Revival, 3, GameConstants.START_POSITION)); // Set player position player.setSceneId(3); player.getPosition().set(GameConstants.START_POSITION); // Packets - getPlayer().sendPacket(new BasePacket(PacketOpcodes.WorldPlayerReviveRsp)); + this.getPlayer().sendPacket(new BasePacket(PacketOpcodes.WorldPlayerReviveRsp)); } public void saveAvatars() { // Save all avatars from active team - for (EntityAvatar entity : getActiveTeam()) { + for (EntityAvatar entity : this.getActiveTeam()) { entity.getAvatar().save(); } } diff --git a/src/main/java/emu/grasscutter/game/systems/InventorySystem.java b/src/main/java/emu/grasscutter/game/systems/InventorySystem.java index 537f1c0b7..fbea95715 100644 --- a/src/main/java/emu/grasscutter/game/systems/InventorySystem.java +++ b/src/main/java/emu/grasscutter/game/systems/InventorySystem.java @@ -30,6 +30,7 @@ import emu.grasscutter.game.shop.ShopChestBatchUseTable; import emu.grasscutter.game.shop.ShopChestTable; import emu.grasscutter.net.proto.ItemParamOuterClass.ItemParam; import emu.grasscutter.net.proto.MaterialInfoOuterClass.MaterialInfo; +import emu.grasscutter.server.event.player.PlayerUseFoodEvent; import emu.grasscutter.server.game.BaseGameSystem; import emu.grasscutter.server.game.GameServer; import emu.grasscutter.server.packet.send.*; @@ -812,38 +813,49 @@ public class InventorySystem extends BaseGameSystem { int used = 0; boolean useSuccess = false; + ItemData itemData = useItem.getItemData(); // Use - switch (useItem.getItemData().getMaterialType()) { + switch (itemData.getMaterialType()) { case MATERIAL_FOOD: - if (useItem.getItemData().getUseTarget() == ItemUseTarget.ITEM_USE_TARGET_SPECIFY_DEAD_AVATAR) { + if (itemData.getUseTarget() == ItemUseTarget.ITEM_USE_TARGET_SPECIFY_DEAD_AVATAR) { if (target == null) { break; } - used = player.getTeamManager().reviveAvatar(target) ? 1 : 0; + // Invoke player use food event. + PlayerUseFoodEvent event = new PlayerUseFoodEvent(player, itemData, target.getAsEntity()); + // Call the event. + event.call(); if(!event.isCanceled()) { + used = player.getTeamManager().reviveAvatar(target) ? 1 : 0; + } } else { used = 1; } break; case MATERIAL_NOTICE_ADD_HP: - if (useItem.getItemData().getUseTarget() == ItemUseTarget.ITEM_USE_TARGET_SPECIFY_ALIVE_AVATAR) { + if (itemData.getUseTarget() == ItemUseTarget.ITEM_USE_TARGET_SPECIFY_ALIVE_AVATAR) { if (target == null) { break; } - int[] SatiationParams = useItem.getItemData().getSatiationParams(); - used = player.getTeamManager().healAvatar(target, SatiationParams[0], SatiationParams[1]) ? 1 : 0; + // Invoke player use food event. + PlayerUseFoodEvent event = new PlayerUseFoodEvent(player, itemData, target.getAsEntity()); + // Call the event. + event.call(); if(!event.isCanceled()) { + int[] SatiationParams = itemData.getSatiationParams(); + used = player.getTeamManager().healAvatar(target, SatiationParams[0], SatiationParams[1]) ? 1 : 0; + } } break; case MATERIAL_CONSUME: // Make sure we have usage data for this material. - if (useItem.getItemData().getItemUse() == null) { + if (itemData.getItemUse() == null) { break; } - - ItemUseOp useOp = useItem.getItemData().getItemUse().get(0).getUseOp(); - + + ItemUseOp useOp = itemData.getItemUse().get(0).getUseOp(); + // Unlock item based on use operation useSuccess = switch (useOp) { case ITEM_USE_UNLOCK_FORGE -> player.getForgingManager().unlockForgingBlueprint(useItem); @@ -854,7 +866,7 @@ public class InventorySystem extends BaseGameSystem { break; case MATERIAL_FURNITURE_FORMULA: case MATERIAL_FURNITURE_SUITE_FORMULA: - if (useItem.getItemData().getItemUse() == null) { + if (itemData.getItemUse() == null) { break; } useSuccess = player.getFurnitureManager().unlockFurnitureOrSuite(useItem); @@ -862,7 +874,7 @@ public class InventorySystem extends BaseGameSystem { break; case MATERIAL_CONSUME_BATCH_USE: // Make sure we have usage data for this material. - if (useItem.getItemData().getItemUse() == null) { + if (itemData.getItemUse() == null) { break; } @@ -889,11 +901,11 @@ public class InventorySystem extends BaseGameSystem { } for (ItemParamData itemParamData : shopChestTable.getContainsItem()) { - ItemData itemData = GameData.getItemDataMap().get(itemParamData.getId()); - if (itemData == null) { + ItemData containedItem = GameData.getItemDataMap().get(itemParamData.getId()); + if (containedItem == null) { continue; } - rewardItemList.add(new GameItem(itemData, itemParamData.getCount())); + rewardItemList.add(new GameItem(containedItem, itemParamData.getCount())); } if (!rewardItemList.isEmpty()) { @@ -919,12 +931,12 @@ public class InventorySystem extends BaseGameSystem { } int optionItemId = shopChestBatchUseTable.getOptionItem().get(optionId - 1); - ItemData itemData = GameData.getItemDataMap().get(optionItemId); - if (itemData == null) { + ItemData optionItem = GameData.getItemDataMap().get(optionItemId); + if (optionItem == null) { break; } - player.getInventory().addItem(new GameItem(itemData, count), ActionReason.Shop); + player.getInventory().addItem(new GameItem(optionItem, count), ActionReason.Shop); used = count; break; @@ -944,23 +956,23 @@ public class InventorySystem extends BaseGameSystem { // we return the item to make UseItemRsp a success. if (used > 0) { // Handle use params, mainly server buffs - for (ItemUseData useData : useItem.getItemData().getItemUse()) { + for (ItemUseData useData : itemData.getItemUse()) { switch (useData.getUseOp()) { case ITEM_USE_ADD_SERVER_BUFF -> { int buffId = Integer.parseInt(useData.getUseParam()[0]); float time = Float.parseFloat(useData.getUseParam()[1]); - + player.getBuffManager().addBuff(buffId, time); } default -> {} } } - + // Remove item from inventory since we used it player.getInventory().removeItem(useItem, used); return useItem; } - + if (useSuccess) { return useItem; } diff --git a/src/main/java/emu/grasscutter/game/world/World.java b/src/main/java/emu/grasscutter/game/world/World.java index 9d6b972fb..66f669ca4 100644 --- a/src/main/java/emu/grasscutter/game/world/World.java +++ b/src/main/java/emu/grasscutter/game/world/World.java @@ -17,6 +17,8 @@ import emu.grasscutter.data.excels.SceneData; import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.proto.EnterTypeOuterClass.EnterType; import emu.grasscutter.scripts.data.SceneConfig; +import emu.grasscutter.server.event.player.PlayerTeleportEvent; +import emu.grasscutter.server.event.player.PlayerTeleportEvent.TeleportType; import emu.grasscutter.server.game.GameServer; import emu.grasscutter.server.packet.send.PacketDelTeamEntityNotify; import emu.grasscutter.server.packet.send.PacketPlayerEnterSceneNotify; @@ -53,7 +55,7 @@ public class World implements Iterable { this.players = Collections.synchronizedList(new ArrayList<>()); this.scenes = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>()); - this.levelEntityId = getNextEntityId(EntityIdType.MPLEVEL); + this.levelEntityId = this.getNextEntityId(EntityIdType.MPLEVEL); this.worldLevel = player.getWorldLevel(); this.isMultiplayer = isMultiplayer; @@ -101,7 +103,7 @@ public class World implements Iterable { public Scene getSceneById(int sceneId) { // Get scene normally - Scene scene = getScenes().get(sceneId); + Scene scene = this.getScenes().get(sceneId); if (scene != null) { return scene; } @@ -118,7 +120,7 @@ public class World implements Iterable { } public int getPlayerCount() { - return getPlayers().size(); + return this.getPlayers().size(); } public boolean isMultiplayer() { @@ -131,7 +133,7 @@ public class World implements Iterable { public synchronized void addPlayer(Player player) { // Check if player already in - if (getPlayers().contains(player)) { + if (this.getPlayers().contains(player)) { return; } @@ -142,11 +144,11 @@ public class World implements Iterable { // Register player.setWorld(this); - getPlayers().add(player); + this.getPlayers().add(player); // Set player variables player.setPeerId(this.getNextPeerId()); - player.getTeamManager().setEntityId(getNextEntityId(EntityIdType.TEAM)); + player.getTeamManager().setEntityId(this.getNextEntityId(EntityIdType.TEAM)); // Copy main team to multiplayer team if (this.isMultiplayer()) { @@ -169,12 +171,12 @@ public class World implements Iterable { player.sendPacket( new PacketDelTeamEntityNotify( player.getSceneId(), - getPlayers().stream().map(p -> p.getTeamManager().getEntityId()).collect(Collectors.toList()) + this.getPlayers().stream().map(p -> p.getTeamManager().getEntityId()).collect(Collectors.toList()) ) ); // Deregister - getPlayers().remove(player); + this.getPlayers().remove(player); player.setWorld(null); // Remove from scene @@ -187,7 +189,7 @@ public class World implements Iterable { } // Disband world if host leaves - if (getHost() == player) { + if (this.getHost() == player) { List kicked = new ArrayList<>(this.getPlayers()); for (Player victim : kicked) { World world = new World(victim); @@ -207,14 +209,28 @@ public class World implements Iterable { } public boolean transferPlayerToScene(Player player, int sceneId, Position pos) { - return transferPlayerToScene(player, sceneId, null, pos); + return this.transferPlayerToScene(player, sceneId, TeleportType.INTERNAL, null, pos); + } + + public boolean transferPlayerToScene(Player player, int sceneId, TeleportType teleportType, Position pos) { + return this.transferPlayerToScene(player, sceneId, teleportType, null, pos); } public boolean transferPlayerToScene(Player player, int sceneId, DungeonData data) { - return transferPlayerToScene(player, sceneId, data, null); + return this.transferPlayerToScene(player, sceneId, TeleportType.DUNGEON, data, null); } - public boolean transferPlayerToScene(Player player, int sceneId, DungeonData dungeonData, Position pos) { + public boolean transferPlayerToScene(Player player, int sceneId, TeleportType teleportType, DungeonData dungeonData, Position teleportTo) { + // Call player teleport event. + PlayerTeleportEvent event = new PlayerTeleportEvent(player, teleportType, player.getPosition(), teleportTo); + // Call event & check if it was canceled. + event.call(); if (event.isCanceled()) { + return false; // Teleport was canceled. + } + + // Set the destination. + teleportTo = event.getDestination(); + if (GameData.getSceneDataMap().get(sceneId) == null) { return false; } @@ -224,7 +240,7 @@ public class World implements Iterable { if (player.getScene() != null) { oldScene = player.getScene(); - // Dont deregister scenes if the player is going to tp back into them + // Don't deregister scenes if the player is going to tp back into them if (oldScene.getId() == sceneId) { oldScene.setDontDestroyWhenEmpty(true); } @@ -238,9 +254,9 @@ public class World implements Iterable { // Dungeon SceneConfig config = newScene.getScriptManager().getConfig(); - if (pos == null && config != null) { + if (teleportTo == null && config != null) { if (config.born_pos != null) { - pos = newScene.getScriptManager().getConfig().born_pos; + teleportTo = newScene.getScriptManager().getConfig().born_pos; } if (config.born_rot != null) { player.getRotation().set(config.born_rot); @@ -248,11 +264,11 @@ public class World implements Iterable { } // Set player position - if (pos == null) { - pos = player.getPosition(); + if (teleportTo == null) { + teleportTo = player.getPosition(); } - player.getPosition().set(pos); + player.getPosition().set(teleportTo); if (oldScene != null) { newScene.setPrevScene(oldScene.getId()); @@ -276,12 +292,12 @@ public class World implements Iterable { } // Teleport packet - player.sendPacket(new PacketPlayerEnterSceneNotify(player, enterType, enterReason, sceneId, pos)); + player.sendPacket(new PacketPlayerEnterSceneNotify(player, enterType, enterReason, sceneId, teleportTo)); return true; } private void updatePlayerInfos(Player paramPlayer) { - for (Player player : getPlayers()) { + for (Player player : this.getPlayers()) { // Dont send packets if player is logging in and filter out joining player if (!player.hasSentLoginPackets() || player == paramPlayer) { continue; @@ -292,7 +308,7 @@ public class World implements Iterable { player.getTeamManager().getMpTeam().copyFrom(player.getTeamManager().getMpTeam(), player.getTeamManager().getMaxTeamSize()); player.getTeamManager().updateTeamEntities(null); } - + // Dont send packets if player is loading into the scene if (player.getSceneLoadState().getValue() < SceneLoadState.INIT.getValue() ) { // World player info packets @@ -326,6 +342,6 @@ public class World implements Iterable { @Override public Iterator iterator() { - return getPlayers().iterator(); + return this.getPlayers().iterator(); } } diff --git a/src/main/java/emu/grasscutter/server/event/entity/EntityDeathEvent.java b/src/main/java/emu/grasscutter/server/event/entity/EntityDeathEvent.java new file mode 100644 index 000000000..fb9871318 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/event/entity/EntityDeathEvent.java @@ -0,0 +1,20 @@ +package emu.grasscutter.server.event.entity; + +import emu.grasscutter.game.entity.GameEntity; +import emu.grasscutter.server.event.types.EntityEvent; +import emu.grasscutter.utils.Location; +import lombok.Getter; + +import javax.annotation.Nullable; + +public final class EntityDeathEvent extends EntityEvent { + @Getter private final Location deathLocation; + @Getter @Nullable private final GameEntity killer; + + public EntityDeathEvent(GameEntity entity, int killerId) { + super(entity); + + this.deathLocation = new Location(entity.getScene(), entity.getPosition()); + this.killer = entity.getScene().getEntityById(killerId); + } +} \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/server/event/game/CommandResponseEvent.java b/src/main/java/emu/grasscutter/server/event/game/CommandResponseEvent.java deleted file mode 100644 index bacb3be5b..000000000 --- a/src/main/java/emu/grasscutter/server/event/game/CommandResponseEvent.java +++ /dev/null @@ -1,27 +0,0 @@ -package emu.grasscutter.server.event.game; - -import emu.grasscutter.game.player.Player; -import emu.grasscutter.server.event.types.ServerEvent; - -/** - * @deprecated Will be removed in 1.2.3-dev or 1.3.0. - */ -@Deprecated(since = "1.2.2-dev", forRemoval = true) -public final class CommandResponseEvent extends ServerEvent { - private String message; - private Player player; - - public CommandResponseEvent(Type type, Player player,String message) { - super(type); - this.message = message; - this.player = player; - } - - public String getMessage() { - return message; - } - - public Player getPlayer() { - return player; - } -} diff --git a/src/main/java/emu/grasscutter/server/event/player/PlayerMoveEvent.java b/src/main/java/emu/grasscutter/server/event/player/PlayerMoveEvent.java new file mode 100644 index 000000000..ded4acfdc --- /dev/null +++ b/src/main/java/emu/grasscutter/server/event/player/PlayerMoveEvent.java @@ -0,0 +1,46 @@ +package emu.grasscutter.server.event.player; + +import emu.grasscutter.game.player.Player; +import emu.grasscutter.server.event.types.PlayerEvent; +import emu.grasscutter.utils.Position; + +/** + * TODO: Allow plugins to change the position of the player. + */ +public final class PlayerMoveEvent extends PlayerEvent { + private final MoveType type; + private final Position from; + private final Position to; + + public PlayerMoveEvent(Player player, MoveType type, Position from, Position to) { + super(player); + + this.type = type; + this.from = from; + this.to = to; + } + + public MoveType getMoveType() { + return this.type; + } + + public Position getSource() { + return this.from; + } + + public Position getDestination() { + return this.to; + } + + public enum MoveType { + /** + * The player has sent a combat invocation to move. + */ + PLAYER, + + /** + * The server has requested that the player moves. + */ + SERVER + } +} \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/server/event/player/PlayerTeamDeathEvent.java b/src/main/java/emu/grasscutter/server/event/player/PlayerTeamDeathEvent.java new file mode 100644 index 000000000..0729dc53e --- /dev/null +++ b/src/main/java/emu/grasscutter/server/event/player/PlayerTeamDeathEvent.java @@ -0,0 +1,20 @@ +package emu.grasscutter.server.event.player; + +import emu.grasscutter.game.entity.EntityAvatar; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.server.event.types.PlayerEvent; +import lombok.Getter; + +/** + * This event is invoked when the ENTIRE TEAM dies. + * To listen for one player death, use {@link emu.grasscutter.server.event.entity.EntityDeathEvent}. + */ +public final class PlayerTeamDeathEvent extends PlayerEvent { + @Getter private final EntityAvatar selectedAvatar; + + public PlayerTeamDeathEvent(Player player, EntityAvatar selectedAvatar) { + super(player); + + this.selectedAvatar = selectedAvatar; + } +} \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/server/event/player/PlayerTeleportEvent.java b/src/main/java/emu/grasscutter/server/event/player/PlayerTeleportEvent.java new file mode 100644 index 000000000..f7b9d2aae --- /dev/null +++ b/src/main/java/emu/grasscutter/server/event/player/PlayerTeleportEvent.java @@ -0,0 +1,63 @@ +package emu.grasscutter.server.event.player; + +import emu.grasscutter.game.player.Player; +import emu.grasscutter.server.event.Cancellable; +import emu.grasscutter.server.event.types.PlayerEvent; +import emu.grasscutter.utils.Position; + +public final class PlayerTeleportEvent extends PlayerEvent implements Cancellable { + private final TeleportType type; + private final Position from; + private Position to; + + public PlayerTeleportEvent(Player player, TeleportType type, Position from, Position to) { + super(player); + + this.type = type; + this.from = from; + this.to = to; + } + + public TeleportType getTeleportType() { + return this.type; + } + + public Position getSource() { + return this.from; + } + + public Position getDestination() { + return this.to; + } + + public void setDestination(Position to) { + this.to = to; + } + + public enum TeleportType { + /** + * There is no specified reason to teleport. + */ + INTERNAL, + + /** + * The player has asked to teleport to a waypoint. + */ + WAYPOINT, + + /** + * The player has asked to teleport using the map. + */ + MAP, + + /** + * The player has asked to teleport into a dungeon. + */ + DUNGEON, + + /** + * The player has asked to teleport using the command. + */ + COMMAND + } +} \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/server/event/player/PlayerUseFoodEvent.java b/src/main/java/emu/grasscutter/server/event/player/PlayerUseFoodEvent.java new file mode 100644 index 000000000..63f665e6e --- /dev/null +++ b/src/main/java/emu/grasscutter/server/event/player/PlayerUseFoodEvent.java @@ -0,0 +1,24 @@ +package emu.grasscutter.server.event.player; + +import emu.grasscutter.data.excels.ItemData; +import emu.grasscutter.game.entity.EntityAvatar; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.server.event.Cancellable; +import emu.grasscutter.server.event.types.PlayerEvent; +import lombok.Getter; +import lombok.Setter; + +/** + * This event is invoked when the player uses food on an avatar. + */ +public final class PlayerUseFoodEvent extends PlayerEvent implements Cancellable { + @Getter @Setter private ItemData foodUsed; + @Getter private final EntityAvatar selectedAvatar; + + public PlayerUseFoodEvent(Player player, ItemData foodUsed, EntityAvatar selectedAvatar) { + super(player); + + this.foodUsed = foodUsed; + this.selectedAvatar = selectedAvatar; + } +} \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerCombatInvocationsNotify.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerCombatInvocationsNotify.java index 5df89de0c..825a3cc02 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerCombatInvocationsNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerCombatInvocationsNotify.java @@ -60,8 +60,7 @@ public class HandlerCombatInvocationsNotify extends PacketHandler { new Position(motionInfo.getRot()), motionState); event.call(); - entity.getPosition().set(motionInfo.getPos()); - entity.getRotation().set(motionInfo.getRot()); + entity.move(event.getPosition(), event.getRotation()); entity.setLastMoveSceneTimeMs(moveInfo.getSceneTime()); entity.setLastMoveReliableSeq(moveInfo.getReliableSeq()); entity.setMotionState(motionState); diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneTransToPointReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneTransToPointReq.java index 209dacdbb..70b2f78d3 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneTransToPointReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneTransToPointReq.java @@ -1,36 +1,37 @@ -package emu.grasscutter.server.packet.recv; - -import emu.grasscutter.data.GameData; -import emu.grasscutter.data.binout.ScenePointEntry; -import emu.grasscutter.net.packet.Opcodes; -import emu.grasscutter.net.packet.PacketOpcodes; -import emu.grasscutter.net.proto.SceneTransToPointReqOuterClass.SceneTransToPointReq; -import emu.grasscutter.net.proto.SceneTransToPointRspOuterClass.SceneTransToPointRsp; -import emu.grasscutter.net.packet.PacketHandler; -import emu.grasscutter.server.game.GameSession; -import emu.grasscutter.server.packet.send.PacketSceneTransToPointRsp; -import emu.grasscutter.utils.Position; - -@Opcodes(PacketOpcodes.SceneTransToPointReq) -public class HandlerSceneTransToPointReq extends PacketHandler { - - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - SceneTransToPointReq req = SceneTransToPointReq.parseFrom(payload); - - String code = req.getSceneId() + "_" + req.getPointId(); - ScenePointEntry scenePointEntry = GameData.getScenePointEntries().get(code); - - if (scenePointEntry != null) { - float x = scenePointEntry.getPointData().getTranPos().getX(); - float y = scenePointEntry.getPointData().getTranPos().getY(); - float z = scenePointEntry.getPointData().getTranPos().getZ(); - - session.getPlayer().getWorld().transferPlayerToScene(session.getPlayer(), req.getSceneId(), new Position(x, y, z)); - session.send(new PacketSceneTransToPointRsp(session.getPlayer(), req.getPointId(), req.getSceneId())); - } else { - session.send(new PacketSceneTransToPointRsp()); - } - } - -} +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.binout.ScenePointEntry; +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.SceneTransToPointReqOuterClass.SceneTransToPointReq; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.server.event.player.PlayerTeleportEvent.TeleportType; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketSceneTransToPointRsp; +import emu.grasscutter.utils.Position; + +@Opcodes(PacketOpcodes.SceneTransToPointReq) +public class HandlerSceneTransToPointReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + SceneTransToPointReq req = SceneTransToPointReq.parseFrom(payload); + + String code = req.getSceneId() + "_" + req.getPointId(); + ScenePointEntry scenePointEntry = GameData.getScenePointEntries().get(code); + + if (scenePointEntry != null) { + float x = scenePointEntry.getPointData().getTranPos().getX(); + float y = scenePointEntry.getPointData().getTranPos().getY(); + float z = scenePointEntry.getPointData().getTranPos().getZ(); + + if (session.getPlayer().getWorld().transferPlayerToScene(session.getPlayer(), req.getSceneId(), TeleportType.WAYPOINT, new Position(x, y, z))) { + session.send(new PacketSceneTransToPointRsp(session.getPlayer(), req.getPointId(), req.getSceneId())); + } + } else { + session.send(new PacketSceneTransToPointRsp()); + } + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerTryEnterHomeReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerTryEnterHomeReq.java index 69e66e7d9..a4fd488f2 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerTryEnterHomeReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerTryEnterHomeReq.java @@ -1,14 +1,11 @@ package emu.grasscutter.server.packet.recv; -import emu.grasscutter.data.GameData; -import emu.grasscutter.database.DatabaseHelper; -import emu.grasscutter.game.home.GameHome; import emu.grasscutter.game.world.Scene; import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.TryEnterHomeReqOuterClass; -import emu.grasscutter.scripts.data.SceneConfig; +import emu.grasscutter.server.event.player.PlayerTeleportEvent.TeleportType; import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.packet.send.PacketTryEnterHomeRsp; import emu.grasscutter.utils.Position; @@ -38,13 +35,10 @@ public class HandlerTryEnterHomeReq extends PacketHandler { Scene scene = session.getPlayer().getWorld().getSceneById(realmId); Position pos = scene.getScriptManager().getConfig().born_pos; - session.getPlayer().getWorld().transferPlayerToScene( - session.getPlayer(), - realmId, - pos + boolean result = session.getPlayer().getWorld().transferPlayerToScene( + session.getPlayer(), realmId, + TeleportType.WAYPOINT, pos ); - - - session.send(new PacketTryEnterHomeRsp(req.getTargetUid())); + if (result) session.send(new PacketTryEnterHomeRsp(req.getTargetUid())); } } diff --git a/src/main/java/emu/grasscutter/utils/Location.java b/src/main/java/emu/grasscutter/utils/Location.java new file mode 100644 index 000000000..c0954b7ba --- /dev/null +++ b/src/main/java/emu/grasscutter/utils/Location.java @@ -0,0 +1,41 @@ +package emu.grasscutter.utils; + +import dev.morphia.annotations.Entity; +import dev.morphia.annotations.Transient; +import emu.grasscutter.game.world.Scene; +import lombok.Getter; +import lombok.Setter; + +@Entity +public class Location extends Position { + @Transient @Getter @Setter + private Scene scene; + + public Location(Scene scene, Position position) { + this.set(position); + + this.scene = scene; + } + + public Location(Scene scene, float x, float y) { + this.set(x, y); + + this.scene = scene; + } + + public Location(Scene scene, float x, float y, float z) { + this.set(x, y, z); + + this.scene = scene; + } + + @Override + public Location clone() { + return new Location(this.scene, super.clone()); + } + + @Override + public String toString() { + return String.format("%s:%s,%s,%s", this.scene.getId(), this.getX(), this.getY(), this.getZ()); + } +} \ No newline at end of file