Commit d05b3207 authored by Magix's avatar Magix Committed by GitHub
Browse files

Add more events

Merge pull request #1516 from Grasscutters/more-events
parents 9a104f6f a3e0f7f5
......@@ -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"));
......
......@@ -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 {
......
......@@ -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();
}
public Avatar getAvatar() {
return avatar;
}
public int getKilledBy() {
return killedBy;
}
public PlayerDieType getKilledType() {
return killedType;
}
@Override
public boolean isAlive() {
return this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) > 0f;
}
@Override
public Int2FloatOpenHashMap getFightProperties() {
return getAvatar().getFightProperties();
}
public int getWeaponEntityId() {
if (getAvatar().getWeapon() != null) {
return getAvatar().getWeapon().getWeaponEntityId();
}
return 0;
}
}
@Override
public Position getRotation() {
return getPlayer().getRotation();
}
public Avatar getAvatar() {
return avatar;
}
public int getKilledBy() {
return killedBy;
}
public PlayerDieType getKilledType() {
return killedType;
}
@Override
public boolean isAlive() {
return this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) > 0f;
}
@Override
public Int2FloatOpenHashMap getFightProperties() {
return getAvatar().getFightProperties();
}
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);
}
@Override
public float heal(float amount) {
float healed = super.heal(amount);
if (healed > 0f) {
getScene().broadcastPacket(
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);
// 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));
}
}
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());
}
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();
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());
}
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);
entityInfo.setAvatar(this.getSceneAvatarInfo());
return entityInfo.build();
}
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);
}
}
//
return abilityControlBlock.build();
// Determine the new energy value.
float newEnergy = Math.min(curEnergy + amount, maxEnergy);
// 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));
}
}
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());
}
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();
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());
}
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);
entityInfo.setAvatar(this.getSceneAvatarInfo());
return entityInfo.build();
}
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);
}
}
//
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);
}
}
......@@ -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.
}
}
......@@ -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();
}
}
......@@ -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());
}
......
......@@ -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());
}
......
......@@ -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;
private int blockId;
private int configId;
private int groupId;
private MotionState moveState;
private int lastMoveSceneTimeMs;
private int lastMoveReliableSeq;
// Abilities
private Map<String, Float> metaOverrideMap;
private Int2ObjectMap<String> metaModifiers;
public GameEntity(Scene scene) {
this.scene = scene;
this.moveState = MotionState.MOTION_STATE_NONE;
}
public int getId() {
return this.id;
}
public int getEntityType() {
return getId() >> 24;
}
public World getWorld() {
return this.getScene().getWorld();
}
public Scene getScene() {
return this.scene;
}
public boolean isAlive() {
return true;
}
public LifeState getLifeState() {
return isAlive() ? LifeState.LIFE_ALIVE : LifeState.LIFE_DEAD;
}
public Map<String, Float> getMetaOverrideMap() {
if (this.metaOverrideMap == null) {
this.metaOverrideMap = new HashMap<>();
}
return this.metaOverrideMap;
}
public Int2ObjectMap<String> getMetaModifiers() {
if (this.metaModifiers == null) {
this.metaModifiers = new Int2ObjectOpenHashMap<>();
}
return this.metaModifiers;
}
protected int id;
private final Scene scene;
private SpawnDataEntry spawnEntry;
private int blockId;
private int configId;
private int groupId;
private MotionState moveState;
private int lastMoveSceneTimeMs;
private int lastMoveReliableSeq;
// Abilities
private Map<String, Float> metaOverrideMap;
private Int2ObjectMap<String> metaModifiers;
public GameEntity(Scene scene) {
this.scene = scene;
this.moveState = MotionState.MOTION_STATE_NONE;
}
public int getId() {
return this.id;
}
public int getEntityType() {
return getId() >> 24;
}
public World getWorld() {
return this.getScene().getWorld();
}
public Scene getScene() {
return this.scene;
}
public boolean isAlive() {
return true;
}
public LifeState getLifeState() {
return isAlive() ? LifeState.LIFE_ALIVE : LifeState.LIFE_DEAD;
}
public Map<String, Float> getMetaOverrideMap() {
if (this.metaOverrideMap == null) {
this.metaOverrideMap = new HashMap<>();
}
return this.metaOverrideMap;
}
public Int2ObjectMap<String> 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();
......
......@@ -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<EntityAvatar, Integer> avatarNormalProbabilities;
// energyUsage for each player
private Boolean energyUsage;
private boolean energyUsage; // Should energy usage be enabled for this player?
private final static Int2ObjectMap<List<EnergyDropInfo>> energyDropData = new Int2ObjectOpenHashMap<>();
private final static Int2ObjectMap<List<SkillParticleGenerationInfo>> 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<EntityAvatar> avatarEntity = getCastingAvatarEntityForEnergy(invoke.getEntityId());
Optional<EntityAvatar> 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);
}
}
......
......@@ -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<String, MapMark> 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));
}
}
......@@ -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<Integer, TeamInfo> 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<Avatar> 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<EntityAvatar> 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<Long> 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<Avatar> 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<Long> 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<Avatar> 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<List<Long>> 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<Avatar> 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();
}
}
......
......@@ -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;
}
......
......@@ -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<Player> {
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<Player> {
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<Player> {
}
public int getPlayerCount() {
return getPlayers().size();
return this.getPlayers().size();
}
public boolean isMultiplayer() {
......@@ -131,7 +133,7 @@ public class World implements Iterable<Player> {
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<Player> {
// 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> {
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<Player> {
}
// Disband world if host leaves
if (getHost() == player) {
if (this.getHost() == player) {
List<Player> kicked = new ArrayList<>(this.getPlayers());
for (Player victim : kicked) {
World world = new World(victim);
......@@ -207,14 +209,28 @@ public class World implements Iterable<Player> {
}
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<Player> {
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<Player> {
// 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<Player> {
}
// 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<Player> {
}
// 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> {
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<Player> {
@Override
public Iterator<Player> iterator() {
return getPlayers().iterator();
return this.getPlayers().iterator();
}
}
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
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;
}
}
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
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
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
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
......@@ -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);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment