Skip to content
Snippets Groups Projects
Commit 93b3265d authored by gentlespoon's avatar gentlespoon Committed by Melledy
Browse files

Add drowning. Better movement ticking.

parent 81998b9c
Branches
Tags
No related merge requests found
Showing
with 485 additions and 296 deletions
...@@ -104,6 +104,11 @@ public class EntityAvatar extends GameEntity { ...@@ -104,6 +104,11 @@ public class EntityAvatar extends GameEntity {
this.killedType = PlayerDieType.PLAYER_DIE_KILL_BY_MONSTER; this.killedType = PlayerDieType.PLAYER_DIE_KILL_BY_MONSTER;
this.killedBy = killerId; this.killedBy = killerId;
} }
public void onDeath(PlayerDieType dieType, int killerId) {
this.killedType = dieType;
this.killedBy = killerId;
}
public SceneAvatarInfo getSceneAvatarInfo() { public SceneAvatarInfo getSceneAvatarInfo() {
SceneAvatarInfo.Builder avatarInfo = SceneAvatarInfo.newBuilder() SceneAvatarInfo.Builder avatarInfo = SceneAvatarInfo.newBuilder()
......
package emu.grasscutter.game.managers.MotionManager;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.LifeState;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.net.proto.EntityMoveInfoOuterClass;
import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState;
import emu.grasscutter.net.proto.VectorOuterClass;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify;
import emu.grasscutter.server.packet.send.PacketPlayerPropNotify;
import emu.grasscutter.utils.Position;
import java.util.ArrayList;
import java.lang.Math;
public class MotionManager {
private enum Consumption {
None(0),
// consumers
CLIMB_START(-500),
CLIMBING(-150),
CLIMB_JUMP(-2500),
DASH(-1800),
SPRINT(-360),
FLY(-60),
SWIM_DASH_START(-200),
SWIM_DASH(-200),
SWIMMING(-80),
// restorers
STANDBY(500),
RUN(500),
WALK(500),
STANDBY_MOVE(500);
public final int amount;
Consumption(int amount) {
this.amount = amount;
}
}
private EntityMoveInfoOuterClass.EntityMoveInfo moveInfo;
private MotionState previousState = MotionState.MOTION_STANDBY;
private ArrayList<Position> previousCoordinates = new ArrayList<>();
private final Player player;
private float landSpeed = 0;
public MotionManager(Player player) {
previousCoordinates.add(new Position(0,0,0));
this.player = player;
}
public void handle(GameSession session, GameEntity entity, EntityMoveInfoOuterClass.EntityMoveInfo moveInfo) {
MotionState state = moveInfo.getMotionInfo().getState();
setMoveInfo(moveInfo);
if (state == MotionState.MOTION_LAND_SPEED) {
setLandSpeed(moveInfo.getMotionInfo().getSpeed().getY());
}
if (state == MotionState.MOTION_FALL_ON_GROUND) {
handleFallOnGround(session, entity);
}
}
public void tick() {
if(Grasscutter.getConfig().OpenStamina){
EntityMoveInfoOuterClass.EntityMoveInfo mInfo = moveInfo;
if (mInfo == null) {
return;
}
MotionState state = moveInfo.getMotionInfo().getState();
Consumption consumption = Consumption.None;
boolean isMoving = false;
VectorOuterClass.Vector posVector = moveInfo.getMotionInfo().getPos();
Position currentCoordinates = new Position(posVector.getX(), posVector.getY(), posVector.getZ());
float diffX = currentCoordinates.getX() - previousCoordinates.get(0).getX();
float diffY = currentCoordinates.getY() - previousCoordinates.get(0).getY();
float diffZ = currentCoordinates.getZ() - previousCoordinates.get(0).getZ();
if (Math.abs(diffX) > 0.3 || Math.abs(diffY) > 0.3 || Math.abs(diffZ) > 0.3) {
isMoving = true;
}
if (isMoving) {
// TODO: refactor these conditions.
// CLIMB
if (state == MotionState.MOTION_CLIMB) {
if (previousState != MotionState.MOTION_CLIMB && previousState != MotionState.MOTION_CLIMB_JUMP) {
consumption = Consumption.CLIMB_START;
} else {
consumption = Consumption.CLIMBING;
}
}
// JUMP
if (state == MotionState.MOTION_CLIMB_JUMP) {
if (previousState != MotionState.MOTION_CLIMB_JUMP) {
consumption = Consumption.CLIMB_JUMP;
}
}
if (state == MotionState.MOTION_JUMP) {
if (previousState == MotionState.MOTION_CLIMB) {
consumption = Consumption.CLIMB_JUMP;
}
}
// SWIM
if (state == MotionState.MOTION_SWIM_MOVE) {
consumption = Consumption.SWIMMING;
}
if (state == MotionState.MOTION_SWIM_DASH) {
if (previousState != MotionState.MOTION_SWIM_DASH) {
consumption = Consumption.SWIM_DASH_START;
} else {
consumption = Consumption.SWIM_DASH;
}
}
// DASH
if (state == MotionState.MOTION_DASH) {
if (previousState == MotionState.MOTION_DASH) {
consumption = Consumption.SPRINT;
} else {
consumption = Consumption.DASH;
}
}
// RUN and WALK
if (state == MotionState.MOTION_RUN) {
consumption = Consumption.RUN;
}
if (state == MotionState.MOTION_WALK) {
consumption = Consumption.WALK;
}
// FLY
if (state == MotionState.MOTION_FLY) {
consumption = Consumption.FLY;
}
}
// STAND
if (state == MotionState.MOTION_STANDBY) {
consumption = Consumption.STANDBY;
}
if (state == MotionState.MOTION_STANDBY_MOVE) {
consumption = Consumption.STANDBY_MOVE;
}
GameSession session = player.getSession();
updateStamina(session, consumption.amount);
session.send(new PacketPlayerPropNotify(session.getPlayer(), PlayerProperty.PROP_CUR_PERSIST_STAMINA));
Grasscutter.getLogger().debug(session.getPlayer().getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA) + " " + state + " " + isMoving + " " + consumption + " " + consumption.amount);
previousState = state;
previousCoordinates.add(currentCoordinates);
if (previousCoordinates.size() > 3) {
previousCoordinates.remove(0);
}
}
}
public void updateStamina(GameSession session, int amount) {
if (amount == 0) {
return;
}
int currentStamina = session.getPlayer().getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA);
int playerMaxStamina = session.getPlayer().getProperty(PlayerProperty.PROP_MAX_STAMINA);
int newStamina = currentStamina + amount;
if (newStamina < 0) {
newStamina = 0;
}
if (newStamina > playerMaxStamina) {
newStamina = playerMaxStamina;
}
session.getPlayer().setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA, newStamina);
}
public void setMoveInfo(EntityMoveInfoOuterClass.EntityMoveInfo moveInfo) {
this.moveInfo = moveInfo;
}
public EntityMoveInfoOuterClass.EntityMoveInfo getMoveInfo() {
return moveInfo;
}
public void handleFallOnGround(GameSession session, GameEntity entity) {
float currentHP = entity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
float maxHP = entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
float damage = 0;
Grasscutter.getLogger().debug("LandSpeed: " + landSpeed);
if (landSpeed < -23.5) {
damage = (float)(maxHP * 0.33);
}
if (landSpeed < -25) {
damage = (float)(maxHP * 0.5);
}
if (landSpeed < -26.5) {
damage = (float)(maxHP * 0.66);
}
if (landSpeed < -28) {
damage = (maxHP * 1);
}
float newHP = currentHP - damage;
if (newHP < 0) {
newHP = 0;
}
Grasscutter.getLogger().debug("Max: " + maxHP + "\tCurr: " + currentHP + "\tDamage: " + damage + "\tnewHP: " + newHP);
entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, newHP);
entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP));
if (newHP == 0) {
entity.getWorld().broadcastPacket(new PacketLifeStateChangeNotify(0, entity, LifeState.LIFE_DEAD));
session.getPlayer().getScene().removeEntity(entity);
entity.onDeath(0);
}
}
public void setLandSpeed(float landSpeed) {
this.landSpeed = landSpeed;
}
}
package emu.grasscutter.game.managers.MovementManager;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.LifeState;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.net.proto.EntityMoveInfoOuterClass;
import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo;
import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState;
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType;
import emu.grasscutter.net.proto.VectorOuterClass;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.*;
import emu.grasscutter.utils.Position;
import org.jetbrains.annotations.NotNull;
import java.lang.Math;
import java.util.*;
public class MovementManager {
public HashMap<String, HashSet<MotionState>> MotionStatesCategorized = new HashMap<>();
private enum Consumption {
None(0),
// consume
CLIMB_START(-500),
CLIMBING(-150),
CLIMB_JUMP(-2500),
DASH(-1800),
SPRINT(-360),
FLY(-60),
SWIM_DASH_START(-200),
SWIM_DASH(-200),
SWIMMING(-80),
// restore
STANDBY(500),
RUN(500),
WALK(500),
STANDBY_MOVE(500),
POWERED_FLY(500);
public final int amount;
Consumption(int amount) {
this.amount = amount;
}
}
private MotionState previousState = MotionState.MOTION_STANDBY;
private MotionState currentState = MotionState.MOTION_STANDBY;
private Position previousCoordinates = new Position(0, 0, 0);
private Position currentCoordinates = new Position(0, 0, 0);
private final Player player;
private float landSpeed = 0;
private Timer movementManagerTickTimer;
private GameSession cachedSession = null;
private GameEntity cachedEntity = null;
private int staminaRecoverDelay = 0;
public MovementManager(Player player) {
previousCoordinates.add(new Position(0,0,0));
this.player = player;
MotionStatesCategorized.put("SWIM", new HashSet<>(Arrays.asList(
MotionState.MOTION_SWIM_MOVE,
MotionState.MOTION_SWIM_IDLE,
MotionState.MOTION_SWIM_DASH,
MotionState.MOTION_SWIM_JUMP
)));
MotionStatesCategorized.put("STANDBY", new HashSet<>(Arrays.asList(
MotionState.MOTION_STANDBY,
MotionState.MOTION_STANDBY_MOVE,
MotionState.MOTION_DANGER_STANDBY,
MotionState.MOTION_DANGER_STANDBY_MOVE,
MotionState.MOTION_LADDER_TO_STANDBY,
MotionState.MOTION_JUMP_UP_WALL_FOR_STANDBY
)));
MotionStatesCategorized.put("CLIMB", new HashSet<>(Arrays.asList(
MotionState.MOTION_CLIMB,
MotionState.MOTION_CLIMB_JUMP,
MotionState.MOTION_STANDBY_TO_CLIMB,
MotionState.MOTION_LADDER_IDLE,
MotionState.MOTION_LADDER_MOVE,
MotionState.MOTION_LADDER_SLIP,
MotionState.MOTION_STANDBY_TO_LADDER
)));
MotionStatesCategorized.put("FLY", new HashSet<>(Arrays.asList(
MotionState.MOTION_FLY,
MotionState.MOTION_FLY_IDLE,
MotionState.MOTION_FLY_SLOW,
MotionState.MOTION_FLY_FAST,
MotionState.MOTION_POWERED_FLY
)));
MotionStatesCategorized.put("RUN", new HashSet<>(Arrays.asList(
MotionState.MOTION_DASH,
MotionState.MOTION_DANGER_DASH,
MotionState.MOTION_DASH_BEFORE_SHAKE,
MotionState.MOTION_RUN,
MotionState.MOTION_DANGER_RUN,
MotionState.MOTION_WALK,
MotionState.MOTION_DANGER_WALK
)));
}
public void handle(GameSession session, EntityMoveInfoOuterClass.EntityMoveInfo moveInfo, GameEntity entity) {
if (movementManagerTickTimer == null) {
movementManagerTickTimer = new Timer();
movementManagerTickTimer.scheduleAtFixedRate(new MotionManagerTick(), 0, 200);
}
// cache info for later use in tick
cachedSession = session;
cachedEntity = entity;
MotionInfo motionInfo = moveInfo.getMotionInfo();
moveEntity(entity, moveInfo);
VectorOuterClass.Vector posVector = motionInfo.getPos();
Position newPos = new Position(posVector.getX(),
posVector.getY(), posVector.getZ());;
if (newPos.getX() != 0 && newPos.getY() != 0 && newPos.getZ() != 0) {
currentCoordinates = newPos;
}
currentState = motionInfo.getState();
Grasscutter.getLogger().debug("" + currentState);
handleFallOnGround(motionInfo);
}
public void resetTimer() {
movementManagerTickTimer.cancel();
movementManagerTickTimer = null;
}
private void moveEntity(GameEntity entity, EntityMoveInfoOuterClass.EntityMoveInfo moveInfo) {
entity.getPosition().set(moveInfo.getMotionInfo().getPos());
entity.getRotation().set(moveInfo.getMotionInfo().getRot());
entity.setLastMoveSceneTimeMs(moveInfo.getSceneTime());
entity.setLastMoveReliableSeq(moveInfo.getReliableSeq());
entity.setMotionState(moveInfo.getMotionInfo().getState());
}
private boolean isPlayerMoving() {
float diffX = currentCoordinates.getX() - previousCoordinates.getX();
float diffY = currentCoordinates.getY() - previousCoordinates.getY();
float diffZ = currentCoordinates.getZ() - previousCoordinates.getZ();
// Grasscutter.getLogger().debug("isPlayerMoving: " + previousCoordinates + ", " + currentCoordinates + ", " + diffX + ", " + diffY + ", " + diffZ);
return Math.abs(diffX) > 0.2 || Math.abs(diffY) > 0.1 || Math.abs(diffZ) > 0.2;
}
private int getCurrentStamina() {
return player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA);
}
private int getMaximumStamina() {
return player.getProperty(PlayerProperty.PROP_MAX_STAMINA);
}
// Returns new stamina
public int updateStamina(GameSession session, int amount) {
int currentStamina = session.getPlayer().getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA);
if (amount == 0) {
return currentStamina;
}
int playerMaxStamina = session.getPlayer().getProperty(PlayerProperty.PROP_MAX_STAMINA);
int newStamina = currentStamina + amount;
if (newStamina < 0) {
newStamina = 0;
}
if (newStamina > playerMaxStamina) {
newStamina = playerMaxStamina;
}
session.getPlayer().setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA, newStamina);
return newStamina;
}
private void handleFallOnGround(@NotNull MotionInfo motionInfo) {
MotionState state = motionInfo.getState();
// land speed and fall on ground event arrive in different packets
// cache land speed
if (state == MotionState.MOTION_LAND_SPEED) {
landSpeed = motionInfo.getSpeed().getY();
}
if (state == MotionState.MOTION_FALL_ON_GROUND) {
float currentHP = cachedEntity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
float maxHP = cachedEntity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
float damage = 0;
// Grasscutter.getLogger().debug("LandSpeed: " + landSpeed);
if (landSpeed < -23.5) {
damage = (float)(maxHP * 0.33);
}
if (landSpeed < -25) {
damage = (float)(maxHP * 0.5);
}
if (landSpeed < -26.5) {
damage = (float)(maxHP * 0.66);
}
if (landSpeed < -28) {
damage = (maxHP * 1);
}
float newHP = currentHP - damage;
if (newHP < 0) {
newHP = 0;
}
// Grasscutter.getLogger().debug("Max: " + maxHP + "\tCurr: " + currentHP + "\tDamage: " + damage + "\tnewHP: " + newHP);
cachedEntity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, newHP);
cachedEntity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(cachedEntity, FightProperty.FIGHT_PROP_CUR_HP));
if (newHP == 0) {
killAvatar(cachedSession, cachedEntity, PlayerDieType.PLAYER_DIE_FALL);
}
landSpeed = 0;
}
}
private void handleDrowning() {
int stamina = getCurrentStamina();
if (stamina < 10) {
boolean isSwimming = MotionStatesCategorized.get("SWIM").contains(currentState);
Grasscutter.getLogger().debug(player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA) + "/" + player.getProperty(PlayerProperty.PROP_MAX_STAMINA) + "\t" + currentState + "\t" + isSwimming);
if (isSwimming && currentState != MotionState.MOTION_SWIM_IDLE) {
killAvatar(cachedSession, cachedEntity, PlayerDieType.PLAYER_DIE_DRAWN);
}
}
}
public void killAvatar(GameSession session, GameEntity entity, PlayerDieType dieType) {
cachedSession.send(new PacketAvatarLifeStateChangeNotify(
cachedSession.getPlayer().getTeamManager().getCurrentAvatarEntity().getAvatar(),
LifeState.LIFE_DEAD,
dieType
));
cachedSession.send(new PacketLifeStateChangeNotify(
cachedEntity,
LifeState.LIFE_DEAD,
dieType
));
cachedEntity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0);
cachedEntity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(cachedEntity, FightProperty.FIGHT_PROP_CUR_HP));
entity.getWorld().broadcastPacket(new PacketLifeStateChangeNotify(0, entity, LifeState.LIFE_DEAD));
session.getPlayer().getScene().removeEntity(entity);
((EntityAvatar)entity).onDeath(dieType, 0);
}
private class MotionManagerTick extends TimerTask
{
public void run() {
if (Grasscutter.getConfig().OpenStamina) {
boolean moving = isPlayerMoving();
if (moving || (getCurrentStamina() < getMaximumStamina())) {
Grasscutter.getLogger().debug("Player moving: " + moving + ", stamina full: " + (getCurrentStamina() >= getMaximumStamina()) + ", recalculate stamina");
Consumption consumption = Consumption.None;
// TODO: refactor these conditions.
if (MotionStatesCategorized.get("CLIMB").contains(currentState)) {
if (currentState == MotionState.MOTION_CLIMB) {
// CLIMB
if (previousState != MotionState.MOTION_CLIMB && previousState != MotionState.MOTION_CLIMB_JUMP) {
consumption = Consumption.CLIMB_START;
} else {
consumption = Consumption.CLIMBING;
}
}
if (currentState == MotionState.MOTION_CLIMB_JUMP) {
if (previousState != MotionState.MOTION_CLIMB_JUMP) {
consumption = Consumption.CLIMB_JUMP;
}
}
if (currentState == MotionState.MOTION_JUMP) {
if (previousState == MotionState.MOTION_CLIMB) {
consumption = Consumption.CLIMB_JUMP;
}
}
} else if (MotionStatesCategorized.get("SWIM").contains((currentState))) {
// SWIM
if (currentState == MotionState.MOTION_SWIM_MOVE) {
consumption = Consumption.SWIMMING;
}
if (currentState == MotionState.MOTION_SWIM_DASH) {
if (previousState != MotionState.MOTION_SWIM_DASH) {
consumption = Consumption.SWIM_DASH_START;
} else {
consumption = Consumption.SWIM_DASH;
}
}
} else if (MotionStatesCategorized.get("RUN").contains(currentState)) {
// RUN, DASH and WALK
// DASH
if (currentState == MotionState.MOTION_DASH) {
if (previousState == MotionState.MOTION_DASH) {
consumption = Consumption.SPRINT;
} else {
// cost more to start dashing
consumption = Consumption.DASH;
}
}
// RUN
if (currentState == MotionState.MOTION_RUN) {
consumption = Consumption.RUN;
}
// WALK
if (currentState == MotionState.MOTION_WALK) {
consumption = Consumption.WALK;
}
} else if (MotionStatesCategorized.get("FLY").contains(currentState)) {
// FLY
consumption = Consumption.FLY;
// POWERED_FLY, e.g. wind tunnel
if (currentState == MotionState.MOTION_POWERED_FLY) {
consumption = Consumption.POWERED_FLY;
}
} else if (MotionStatesCategorized.get("STANDBY").contains(currentState)) {
// STAND
if (currentState == MotionState.MOTION_STANDBY) {
consumption = Consumption.STANDBY;
}
if (currentState == MotionState.MOTION_STANDBY_MOVE) {
consumption = Consumption.STANDBY_MOVE;
}
}
// tick triggered
handleDrowning();
if (cachedSession != null) {
if (consumption.amount < 0) {
staminaRecoverDelay = 0;
}
if (consumption.amount > 0) {
if (staminaRecoverDelay < 5) {
staminaRecoverDelay++;
consumption = Consumption.None;
}
}
int newStamina = updateStamina(cachedSession, consumption.amount);
cachedSession.send(new PacketPlayerPropNotify(player, PlayerProperty.PROP_CUR_PERSIST_STAMINA));
Grasscutter.getLogger().debug(player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA) + "/" + player.getProperty(PlayerProperty.PROP_MAX_STAMINA) + "\t" + currentState + "\t" + "isMoving: " + isPlayerMoving() + "\t" + consumption + "(" + consumption.amount + ")");
}
}
}
previousState = currentState;
previousCoordinates = new Position(currentCoordinates.getX(),
currentCoordinates.getY(), currentCoordinates.getZ());;
}
}
}
...@@ -21,7 +21,7 @@ import emu.grasscutter.game.inventory.GameItem; ...@@ -21,7 +21,7 @@ import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.inventory.Inventory; import emu.grasscutter.game.inventory.Inventory;
import emu.grasscutter.game.mail.Mail; import emu.grasscutter.game.mail.Mail;
import emu.grasscutter.game.mail.MailHandler; import emu.grasscutter.game.mail.MailHandler;
import emu.grasscutter.game.managers.MotionManager.MotionManager; import emu.grasscutter.game.managers.MovementManager.MovementManager;
import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.props.EntityType; import emu.grasscutter.game.props.EntityType;
import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.game.props.PlayerProperty;
...@@ -122,7 +122,7 @@ public class Player { ...@@ -122,7 +122,7 @@ public class Player {
@Transient private final InvokeHandler<AbilityInvokeEntry> clientAbilityInitFinishHandler; @Transient private final InvokeHandler<AbilityInvokeEntry> clientAbilityInitFinishHandler;
private MapMarksManager mapMarksManager; private MapMarksManager mapMarksManager;
@Transient private MotionManager motionManager; @Transient private MovementManager movementManager;
@Deprecated @Deprecated
...@@ -164,7 +164,7 @@ public class Player { ...@@ -164,7 +164,7 @@ public class Player {
this.shopLimit = new ArrayList<>(); this.shopLimit = new ArrayList<>();
this.messageHandler = null; this.messageHandler = null;
this.mapMarksManager = new MapMarksManager(); this.mapMarksManager = new MapMarksManager();
this.motionManager = new MotionManager(this); this.movementManager = new MovementManager(this);
} }
// On player creation // On player creation
...@@ -191,7 +191,7 @@ public class Player { ...@@ -191,7 +191,7 @@ public class Player {
this.getRotation().set(0, 307, 0); this.getRotation().set(0, 307, 0);
this.messageHandler = null; this.messageHandler = null;
this.mapMarksManager = new MapMarksManager(); this.mapMarksManager = new MapMarksManager();
this.motionManager = new MotionManager(this); this.movementManager = new MovementManager(this);
} }
public int getUid() { public int getUid() {
...@@ -974,7 +974,7 @@ public class Player { ...@@ -974,7 +974,7 @@ public class Player {
return mapMarksManager; return mapMarksManager;
} }
public MotionManager getMotionManager() { return motionManager; } public MovementManager getMovementManager() { return movementManager; }
public synchronized void onTick() { public synchronized void onTick() {
// Check ping // Check ping
...@@ -1004,33 +1004,9 @@ public class Player { ...@@ -1004,33 +1004,9 @@ public class Player {
this.resetSendPlayerLocTime(); this.resetSendPlayerLocTime();
} }
} }
scheduleStaminaNotify();
} }
private void scheduleStaminaNotify() {
// stamina tick
EntityMoveInfoOuterClass.EntityMoveInfo moveInfo = getMotionManager().getMoveInfo();
if (moveInfo == null) {
return;
}
if (getMotionManager().getMoveInfo().getMotionInfo().getState() == MotionStateOuterClass.MotionState.MOTION_STANDBY) {
if (getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA) == getProperty(PlayerProperty.PROP_MAX_STAMINA) ) {
return;
}
}
for (int i = 0; i <= 1000; i+=200) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
getMotionManager().tick();
}
}, i);
}
}
public void resetSendPlayerLocTime() { public void resetSendPlayerLocTime() {
......
...@@ -24,6 +24,7 @@ import emu.grasscutter.net.packet.BasePacket; ...@@ -24,6 +24,7 @@ import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.EnterTypeOuterClass.EnterType; import emu.grasscutter.net.proto.EnterTypeOuterClass.EnterType;
import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState; import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState;
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType;
import emu.grasscutter.server.packet.send.PacketAvatarDieAnimationEndRsp; import emu.grasscutter.server.packet.send.PacketAvatarDieAnimationEndRsp;
import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify; import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify;
import emu.grasscutter.server.packet.send.PacketAvatarLifeStateChangeNotify; import emu.grasscutter.server.packet.send.PacketAvatarLifeStateChangeNotify;
...@@ -419,27 +420,37 @@ public class TeamManager { ...@@ -419,27 +420,37 @@ public class TeamManager {
if (deadAvatar.isAlive() || deadAvatar.getId() != dieGuid) { if (deadAvatar.isAlive() || deadAvatar.getId() != dieGuid) {
return; return;
} }
// Replacement avatar PlayerDieType dieType = deadAvatar.getKilledType();
EntityAvatar replacement = null; int killedBy = deadAvatar.getKilledBy();
int replaceIndex = -1;
if (dieType == PlayerDieType.PLAYER_DIE_DRAWN) {
for (int i = 0; i < this.getActiveTeam().size(); i++) { // Died in water. Do not replace
EntityAvatar entity = this.getActiveTeam().get(i); // The official server has skipped this notify and will just respawn the team immediately after the animation.
if (entity.isAlive()) { // TODO: Perhaps find a way to get vanilla experience?
replaceIndex = i; getPlayer().sendPacket(new PacketWorldPlayerDieNotify(dieType, killedBy));
replacement = entity;
break;
}
}
if (replacement == null) {
// No more living team members...
getPlayer().sendPacket(new PacketWorldPlayerDieNotify(deadAvatar.getKilledType(), deadAvatar.getKilledBy()));
} else { } else {
// Set index and spawn replacement member // Replacement avatar
this.setCurrentCharacterIndex(replaceIndex); EntityAvatar replacement = null;
getPlayer().getScene().addEntity(replacement); int replaceIndex = -1;
for (int i = 0; i < this.getActiveTeam().size(); i++) {
EntityAvatar entity = this.getActiveTeam().get(i);
if (entity.isAlive()) {
replaceIndex = i;
replacement = entity;
break;
}
}
if (replacement == null) {
// No more living team members...
getPlayer().sendPacket(new PacketWorldPlayerDieNotify(dieType, killedBy));
} else {
// Set index and spawn replacement member
this.setCurrentCharacterIndex(replaceIndex);
getPlayer().getScene().addEntity(replacement);
}
} }
// Response packet // Response packet
...@@ -492,11 +503,13 @@ public class TeamManager { ...@@ -492,11 +503,13 @@ public class TeamManager {
public void respawnTeam() { public void respawnTeam() {
// Make sure all team members are dead // Make sure all team members are dead
for (EntityAvatar entity : getActiveTeam()) { // Drowning needs revive when there may be other team members still alive.
if (entity.isAlive()) { // for (EntityAvatar entity : getActiveTeam()) {
return; // if (entity.isAlive()) {
} // return;
} // }
// }
player.getMovementManager().resetTimer(); // prevent drowning immediately after respawn
// Revive all team members // Revive all team members
for (EntityAvatar entity : getActiveTeam()) { for (EntityAvatar entity : getActiveTeam()) {
......
package emu.grasscutter.server.packet.recv; package emu.grasscutter.server.packet.recv;
import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.managers.MotionManager.MotionManager;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.LifeState;
import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.CombatInvocationsNotifyOuterClass.CombatInvocationsNotify; import emu.grasscutter.net.proto.CombatInvocationsNotifyOuterClass.CombatInvocationsNotify;
...@@ -11,9 +8,7 @@ import emu.grasscutter.net.proto.CombatInvokeEntryOuterClass.CombatInvokeEntry; ...@@ -11,9 +8,7 @@ import emu.grasscutter.net.proto.CombatInvokeEntryOuterClass.CombatInvokeEntry;
import emu.grasscutter.net.proto.EntityMoveInfoOuterClass.EntityMoveInfo; import emu.grasscutter.net.proto.EntityMoveInfoOuterClass.EntityMoveInfo;
import emu.grasscutter.net.proto.EvtBeingHitInfoOuterClass.EvtBeingHitInfo; import emu.grasscutter.net.proto.EvtBeingHitInfoOuterClass.EvtBeingHitInfo;
import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState;
import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.*;
@Opcodes(PacketOpcodes.CombatInvocationsNotify) @Opcodes(PacketOpcodes.CombatInvocationsNotify)
public class HandlerCombatInvocationsNotify extends PacketHandler { public class HandlerCombatInvocationsNotify extends PacketHandler {
...@@ -33,13 +28,7 @@ public class HandlerCombatInvocationsNotify extends PacketHandler { ...@@ -33,13 +28,7 @@ public class HandlerCombatInvocationsNotify extends PacketHandler {
EntityMoveInfo moveInfo = EntityMoveInfo.parseFrom(entry.getCombatData()); EntityMoveInfo moveInfo = EntityMoveInfo.parseFrom(entry.getCombatData());
GameEntity entity = session.getPlayer().getScene().getEntityById(moveInfo.getEntityId()); GameEntity entity = session.getPlayer().getScene().getEntityById(moveInfo.getEntityId());
if (entity != null) { if (entity != null) {
//move session.getPlayer().getMovementManager().handle(session, moveInfo, entity);
entity.getPosition().set(moveInfo.getMotionInfo().getPos());
entity.getRotation().set(moveInfo.getMotionInfo().getRot());
entity.setLastMoveSceneTimeMs(moveInfo.getSceneTime());
entity.setLastMoveReliableSeq(moveInfo.getReliableSeq());
entity.setMotionState(moveInfo.getMotionInfo().getState());
session.getPlayer().getMotionManager().handle(session, entity, moveInfo);
} }
break; break;
default: default:
...@@ -52,7 +41,7 @@ public class HandlerCombatInvocationsNotify extends PacketHandler { ...@@ -52,7 +41,7 @@ public class HandlerCombatInvocationsNotify extends PacketHandler {
if (notif.getInvokeListList().size() > 0) { if (notif.getInvokeListList().size() > 0) {
session.getPlayer().getCombatInvokeHandler().update(session.getPlayer()); session.getPlayer().getCombatInvokeHandler().update(session.getPlayer());
} }
// Handle attack results last // Handle attack results last
while (!session.getPlayer().getAttackResults().isEmpty()) { while (!session.getPlayer().getAttackResults().isEmpty()) {
session.getPlayer().getScene().handleAttack(session.getPlayer().getAttackResults().poll()); session.getPlayer().getScene().handleAttack(session.getPlayer().getAttackResults().poll());
} }
......
...@@ -3,7 +3,9 @@ package emu.grasscutter.server.packet.recv; ...@@ -3,7 +3,9 @@ package emu.grasscutter.server.packet.recv;
import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.net.proto.WorldPlayerDieNotifyOuterClass;
import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketWorldPlayerReviveRsp;
@Opcodes(PacketOpcodes.WorldPlayerReviveReq) @Opcodes(PacketOpcodes.WorldPlayerReviveReq)
public class HandlerWorldPlayerReviveReq extends PacketHandler { public class HandlerWorldPlayerReviveReq extends PacketHandler {
...@@ -11,6 +13,7 @@ public class HandlerWorldPlayerReviveReq extends PacketHandler { ...@@ -11,6 +13,7 @@ public class HandlerWorldPlayerReviveReq extends PacketHandler {
@Override @Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
session.getPlayer().getTeamManager().respawnTeam(); session.getPlayer().getTeamManager().respawnTeam();
session.send(new PacketWorldPlayerReviveRsp());
} }
} }
...@@ -9,6 +9,10 @@ import emu.grasscutter.net.packet.BasePacket; ...@@ -9,6 +9,10 @@ import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.AvatarLifeStateChangeNotifyOuterClass.AvatarLifeStateChangeNotify; import emu.grasscutter.net.proto.AvatarLifeStateChangeNotifyOuterClass.AvatarLifeStateChangeNotify;
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType; import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType;
import emu.grasscutter.net.proto.ServerBuffOuterClass;
import emu.grasscutter.net.proto.ServerBuffOuterClass.ServerBuff;
import java.util.ArrayList;
public class PacketAvatarLifeStateChangeNotify extends BasePacket { public class PacketAvatarLifeStateChangeNotify extends BasePacket {
...@@ -22,7 +26,7 @@ public class PacketAvatarLifeStateChangeNotify extends BasePacket { ...@@ -22,7 +26,7 @@ public class PacketAvatarLifeStateChangeNotify extends BasePacket {
this.setData(proto); this.setData(proto);
} }
public PacketAvatarLifeStateChangeNotify(Avatar avatar,int attackerId,LifeState lifeState) { public PacketAvatarLifeStateChangeNotify(Avatar avatar, int attackerId, LifeState lifeState) {
super(PacketOpcodes.AvatarLifeStateChangeNotify); super(PacketOpcodes.AvatarLifeStateChangeNotify);
AvatarLifeStateChangeNotify proto = AvatarLifeStateChangeNotify.newBuilder() AvatarLifeStateChangeNotify proto = AvatarLifeStateChangeNotify.newBuilder()
...@@ -33,4 +37,26 @@ public class PacketAvatarLifeStateChangeNotify extends BasePacket { ...@@ -33,4 +37,26 @@ public class PacketAvatarLifeStateChangeNotify extends BasePacket {
this.setData(proto); this.setData(proto);
} }
public PacketAvatarLifeStateChangeNotify(Avatar avatar, LifeState lifeState, PlayerDieType dieType) {
this(avatar, lifeState, null, "", dieType);
}
public PacketAvatarLifeStateChangeNotify(Avatar avatar, LifeState lifeState, GameEntity sourceEntity,
String attackTag, PlayerDieType dieType) {
super(PacketOpcodes.AvatarLifeStateChangeNotify);
AvatarLifeStateChangeNotify.Builder proto = AvatarLifeStateChangeNotify.newBuilder();
proto.setAvatarGuid(avatar.getGuid());
proto.setLifeState(lifeState.getValue());
if (sourceEntity != null) {
proto.setSourceEntityId(sourceEntity.getId());
}
proto.setDieType(dieType);
proto.setAttackTag((attackTag));
this.setData(proto.build());
}
} }
package emu.grasscutter.server.packet.send; package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.props.LifeState; import emu.grasscutter.game.props.LifeState;
import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.LifeStateChangeNotifyOuterClass.LifeStateChangeNotify; import emu.grasscutter.net.proto.LifeStateChangeNotifyOuterClass.LifeStateChangeNotify;
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType;
import emu.grasscutter.net.proto.ServerBuffOuterClass.ServerBuff;
import java.util.ArrayList;
public class PacketLifeStateChangeNotify extends BasePacket { public class PacketLifeStateChangeNotify extends BasePacket {
public PacketLifeStateChangeNotify(GameEntity attacker, GameEntity target, LifeState lifeState) { public PacketLifeStateChangeNotify(GameEntity attacker, GameEntity target, LifeState lifeState) {
...@@ -26,7 +31,29 @@ public class PacketLifeStateChangeNotify extends BasePacket { ...@@ -26,7 +31,29 @@ public class PacketLifeStateChangeNotify extends BasePacket {
.setLifeState(lifeState.getValue()) .setLifeState(lifeState.getValue())
.setSourceEntityId(attackerId) .setSourceEntityId(attackerId)
.build(); .build();
this.setData(proto); this.setData(proto);
} }
public PacketLifeStateChangeNotify(GameEntity entity, LifeState lifeState, PlayerDieType dieType) {
this(entity, lifeState, null, "", dieType);
}
public PacketLifeStateChangeNotify(GameEntity entity, LifeState lifeState, GameEntity sourceEntity,
String attackTag, PlayerDieType dieType) {
super(PacketOpcodes.LifeStateChangeNotify);
LifeStateChangeNotify.Builder proto = LifeStateChangeNotify.newBuilder();
proto.setEntityId(entity.getId());
proto.setLifeState(lifeState.getValue());
if (sourceEntity != null) {
proto.setSourceEntityId(sourceEntity.getId());
}
proto.setAttackTag(attackTag);
proto.setDieType(dieType);
this.setData(proto.build());
}
} }
package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.world.World;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.WorldPlayerReviveRspOuterClass.WorldPlayerReviveRsp;
public class PacketWorldPlayerReviveRsp extends BasePacket {
public PacketWorldPlayerReviveRsp() {
super(PacketOpcodes.WorldPlayerReviveRsp);
WorldPlayerReviveRsp.Builder proto = WorldPlayerReviveRsp.newBuilder();
this.setData(proto.build());
}
}
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