Commit 6d59159b authored by Melledy's avatar Melledy
Browse files

Cleanup package names

parent ac49114c
package emu.grasscutter.game.managers.StaminaManager; package emu.grasscutter.game.managers.stamina;
import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.Logger;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.commands.NoStaminaCommand; import emu.grasscutter.command.commands.NoStaminaCommand;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.entity.EntityAvatar; import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.LifeState; import emu.grasscutter.game.props.LifeState;
import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.props.WeaponType; import emu.grasscutter.game.props.WeaponType;
import emu.grasscutter.net.proto.EntityMoveInfoOuterClass.EntityMoveInfo; import emu.grasscutter.net.proto.EntityMoveInfoOuterClass.EntityMoveInfo;
import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo; import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo;
import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState; import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState;
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType; import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType;
import emu.grasscutter.net.proto.VectorOuterClass.Vector; import emu.grasscutter.net.proto.VectorOuterClass.Vector;
import emu.grasscutter.net.proto.VehicleInteractTypeOuterClass.VehicleInteractType; import emu.grasscutter.net.proto.VehicleInteractTypeOuterClass.VehicleInteractType;
import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.*; import emu.grasscutter.server.packet.send.*;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.*; import java.util.*;
import static emu.grasscutter.Configuration.GAME_OPTIONS; import static emu.grasscutter.Configuration.GAME_OPTIONS;
public class StaminaManager { public class StaminaManager {
// TODO: Skiff state detection? // TODO: Skiff state detection?
private final Player player; private final Player player;
private static final HashMap<String, HashSet<MotionState>> MotionStatesCategorized = new HashMap<>() {{ private static final HashMap<String, HashSet<MotionState>> MotionStatesCategorized = new HashMap<>() {{
put("CLIMB", new HashSet<>(List.of( put("CLIMB", new HashSet<>(List.of(
MotionState.MOTION_STATE_CLIMB, // sustained, when not moving no cost no recover MotionState.MOTION_STATE_CLIMB, // sustained, when not moving no cost no recover
MotionState.MOTION_STATE_STANDBY_TO_CLIMB // NOT OBSERVED, see MOTION_JUMP_UP_WALL_FOR_STANDBY MotionState.MOTION_STATE_STANDBY_TO_CLIMB // NOT OBSERVED, see MOTION_JUMP_UP_WALL_FOR_STANDBY
))); )));
put("DASH", new HashSet<>(List.of( put("DASH", new HashSet<>(List.of(
MotionState.MOTION_STATE_DANGER_DASH, // sustained MotionState.MOTION_STATE_DANGER_DASH, // sustained
MotionState.MOTION_STATE_DASH // sustained MotionState.MOTION_STATE_DASH // sustained
))); )));
put("FLY", new HashSet<>(List.of( put("FLY", new HashSet<>(List.of(
MotionState.MOTION_STATE_FLY, // sustained MotionState.MOTION_STATE_FLY, // sustained
MotionState.MOTION_STATE_FLY_FAST, // sustained MotionState.MOTION_STATE_FLY_FAST, // sustained
MotionState.MOTION_STATE_FLY_SLOW, // sustained MotionState.MOTION_STATE_FLY_SLOW, // sustained
MotionState.MOTION_STATE_POWERED_FLY // sustained, recover MotionState.MOTION_STATE_POWERED_FLY // sustained, recover
))); )));
put("RUN", new HashSet<>(List.of( put("RUN", new HashSet<>(List.of(
MotionState.MOTION_STATE_DANGER_RUN, // sustained, recover MotionState.MOTION_STATE_DANGER_RUN, // sustained, recover
MotionState.MOTION_STATE_RUN // sustained, recover MotionState.MOTION_STATE_RUN // sustained, recover
))); )));
put("SKIFF", new HashSet<>(List.of( put("SKIFF", new HashSet<>(List.of(
MotionState.MOTION_STATE_SKIFF_BOARDING, // NOT OBSERVED even when boarding MotionState.MOTION_STATE_SKIFF_BOARDING, // NOT OBSERVED even when boarding
MotionState.MOTION_STATE_SKIFF_DASH, // sustained, observed with waverider entity ID. MotionState.MOTION_STATE_SKIFF_DASH, // sustained, observed with waverider entity ID.
MotionState.MOTION_STATE_SKIFF_NORMAL, // sustained, OBSERVED when both normal and dashing MotionState.MOTION_STATE_SKIFF_NORMAL, // sustained, OBSERVED when both normal and dashing
MotionState.MOTION_STATE_SKIFF_POWERED_DASH // sustained, recover MotionState.MOTION_STATE_SKIFF_POWERED_DASH // sustained, recover
))); )));
put("STANDBY", new HashSet<>(List.of( put("STANDBY", new HashSet<>(List.of(
MotionState.MOTION_STATE_DANGER_STANDBY_MOVE, // sustained, recover MotionState.MOTION_STATE_DANGER_STANDBY_MOVE, // sustained, recover
MotionState.MOTION_STATE_DANGER_STANDBY, // sustained, recover MotionState.MOTION_STATE_DANGER_STANDBY, // sustained, recover
MotionState.MOTION_STATE_LADDER_TO_STANDBY, // NOT OBSERVED MotionState.MOTION_STATE_LADDER_TO_STANDBY, // NOT OBSERVED
MotionState.MOTION_STATE_STANDBY_MOVE, // sustained, recover MotionState.MOTION_STATE_STANDBY_MOVE, // sustained, recover
MotionState.MOTION_STATE_STANDBY // sustained, recover MotionState.MOTION_STATE_STANDBY // sustained, recover
))); )));
put("SWIM", new HashSet<>(List.of( put("SWIM", new HashSet<>(List.of(
MotionState.MOTION_STATE_SWIM_IDLE, // sustained MotionState.MOTION_STATE_SWIM_IDLE, // sustained
MotionState.MOTION_STATE_SWIM_DASH, // immediate and sustained MotionState.MOTION_STATE_SWIM_DASH, // immediate and sustained
MotionState.MOTION_STATE_SWIM_JUMP, // NOT OBSERVED MotionState.MOTION_STATE_SWIM_JUMP, // NOT OBSERVED
MotionState.MOTION_STATE_SWIM_MOVE // sustained MotionState.MOTION_STATE_SWIM_MOVE // sustained
))); )));
put("WALK", new HashSet<>(List.of( put("WALK", new HashSet<>(List.of(
MotionState.MOTION_STATE_DANGER_WALK, // sustained, recover MotionState.MOTION_STATE_DANGER_WALK, // sustained, recover
MotionState.MOTION_STATE_WALK // sustained, recover MotionState.MOTION_STATE_WALK // sustained, recover
))); )));
put("OTHER", new HashSet<>(List.of( put("OTHER", new HashSet<>(List.of(
MotionState.MOTION_STATE_CLIMB_JUMP, // cost only once if repeated without switching state MotionState.MOTION_STATE_CLIMB_JUMP, // cost only once if repeated without switching state
MotionState.MOTION_STATE_DASH_BEFORE_SHAKE, // immediate one time sprint charge. MotionState.MOTION_STATE_DASH_BEFORE_SHAKE, // immediate one time sprint charge.
MotionState.MOTION_STATE_FIGHT, // immediate, if sustained then subsequent will be MOTION_NOTIFY MotionState.MOTION_STATE_FIGHT, // immediate, if sustained then subsequent will be MOTION_NOTIFY
MotionState.MOTION_STATE_JUMP_UP_WALL_FOR_STANDBY, // immediate, observed when RUN/WALK->CLIMB MotionState.MOTION_STATE_JUMP_UP_WALL_FOR_STANDBY, // immediate, observed when RUN/WALK->CLIMB
MotionState.MOTION_STATE_NOTIFY, // can be either cost or recover - check previous state and check skill casting MotionState.MOTION_STATE_NOTIFY, // can be either cost or recover - check previous state and check skill casting
MotionState.MOTION_STATE_SIT_IDLE, // sustained, recover MotionState.MOTION_STATE_SIT_IDLE, // sustained, recover
MotionState.MOTION_STATE_JUMP // recover MotionState.MOTION_STATE_JUMP // recover
))); )));
put("NOCOST_NORECOVER", new HashSet<>(List.of( put("NOCOST_NORECOVER", new HashSet<>(List.of(
MotionState.MOTION_STATE_LADDER_SLIP, // NOT OBSERVED MotionState.MOTION_STATE_LADDER_SLIP, // NOT OBSERVED
MotionState.MOTION_STATE_SLIP, // sustained, no cost no recover MotionState.MOTION_STATE_SLIP, // sustained, no cost no recover
MotionState.MOTION_STATE_FLY_IDLE // NOT OBSERVED MotionState.MOTION_STATE_FLY_IDLE // NOT OBSERVED
))); )));
put("IGNORE", new HashSet<>(List.of( put("IGNORE", new HashSet<>(List.of(
// these states have no impact on stamina // these states have no impact on stamina
MotionState.MOTION_STATE_CROUCH_IDLE, MotionState.MOTION_STATE_CROUCH_IDLE,
MotionState.MOTION_STATE_CROUCH_MOVE, MotionState.MOTION_STATE_CROUCH_MOVE,
MotionState.MOTION_STATE_CROUCH_ROLL, MotionState.MOTION_STATE_CROUCH_ROLL,
MotionState.MOTION_STATE_DESTROY_VEHICLE, MotionState.MOTION_STATE_DESTROY_VEHICLE,
MotionState.MOTION_STATE_FALL_ON_GROUND, MotionState.MOTION_STATE_FALL_ON_GROUND,
MotionState.MOTION_STATE_FOLLOW_ROUTE, MotionState.MOTION_STATE_FOLLOW_ROUTE,
MotionState.MOTION_STATE_FORCE_SET_POS, MotionState.MOTION_STATE_FORCE_SET_POS,
MotionState.MOTION_STATE_GO_UPSTAIRS, MotionState.MOTION_STATE_GO_UPSTAIRS,
MotionState.MOTION_STATE_JUMP_OFF_WALL, MotionState.MOTION_STATE_JUMP_OFF_WALL,
MotionState.MOTION_STATE_LADDER_IDLE, MotionState.MOTION_STATE_LADDER_IDLE,
MotionState.MOTION_STATE_LADDER_MOVE, MotionState.MOTION_STATE_LADDER_MOVE,
MotionState.MOTION_STATE_LAND_SPEED, MotionState.MOTION_STATE_LAND_SPEED,
MotionState.MOTION_STATE_MOVE_FAIL_ACK, MotionState.MOTION_STATE_MOVE_FAIL_ACK,
MotionState.MOTION_STATE_NONE, MotionState.MOTION_STATE_NONE,
MotionState.MOTION_STATE_NUM, MotionState.MOTION_STATE_NUM,
MotionState.MOTION_STATE_QUEST_FORCE_DRAG, MotionState.MOTION_STATE_QUEST_FORCE_DRAG,
MotionState.MOTION_STATE_RESET, MotionState.MOTION_STATE_RESET,
MotionState.MOTION_STATE_STANDBY_TO_LADDER, MotionState.MOTION_STATE_STANDBY_TO_LADDER,
MotionState.MOTION_STATE_WATERFALL MotionState.MOTION_STATE_WATERFALL
))); )));
}}; }};
private final Logger logger = Grasscutter.getLogger(); private final Logger logger = Grasscutter.getLogger();
public final static int GlobalCharacterMaximumStamina = 24000; public final static int GlobalCharacterMaximumStamina = 24000;
public final static int GlobalVehicleMaxStamina = 24000; public final static int GlobalVehicleMaxStamina = 24000;
private Position currentCoordinates = new Position(0, 0, 0); private Position currentCoordinates = new Position(0, 0, 0);
private Position previousCoordinates = new Position(0, 0, 0); private Position previousCoordinates = new Position(0, 0, 0);
private MotionState currentState = MotionState.MOTION_STATE_STANDBY; private MotionState currentState = MotionState.MOTION_STATE_STANDBY;
private MotionState previousState = MotionState.MOTION_STATE_STANDBY; private MotionState previousState = MotionState.MOTION_STATE_STANDBY;
private Timer sustainedStaminaHandlerTimer; private Timer sustainedStaminaHandlerTimer;
private GameSession cachedSession = null; private GameSession cachedSession = null;
private GameEntity cachedEntity = null; private GameEntity cachedEntity = null;
private int staminaRecoverDelay = 0; private int staminaRecoverDelay = 0;
private final HashMap<String, BeforeUpdateStaminaListener> beforeUpdateStaminaListeners = new HashMap<>(); private final HashMap<String, BeforeUpdateStaminaListener> beforeUpdateStaminaListeners = new HashMap<>();
private final HashMap<String, AfterUpdateStaminaListener> afterUpdateStaminaListeners = new HashMap<>(); private final HashMap<String, AfterUpdateStaminaListener> afterUpdateStaminaListeners = new HashMap<>();
private int lastSkillId = 0; private int lastSkillId = 0;
private int lastSkillCasterId = 0; private int lastSkillCasterId = 0;
private boolean lastSkillFirstTick = true; private boolean lastSkillFirstTick = true;
private int vehicleId = -1; private int vehicleId = -1;
private int vehicleStamina = GlobalVehicleMaxStamina; private int vehicleStamina = GlobalVehicleMaxStamina;
private static final HashSet<Integer> TalentMovements = new HashSet<>(List.of( private static final HashSet<Integer> TalentMovements = new HashSet<>(List.of(
10013, 10413 10013, 10413
)); ));
private static final HashMap<Integer, Float> ClimbFoodReductionMap = new HashMap<>() {{ private static final HashMap<Integer, Float> ClimbFoodReductionMap = new HashMap<>() {{
// TODO: get real food id // TODO: get real food id
put(0, 0.8f); // Sample food put(0, 0.8f); // Sample food
}}; }};
private static final HashMap<Integer, Float> DashFoodReductionMap = new HashMap<>() {{ private static final HashMap<Integer, Float> DashFoodReductionMap = new HashMap<>() {{
// TODO: get real food id // TODO: get real food id
put(0, 0.8f); // Sample food put(0, 0.8f); // Sample food
}}; }};
private static final HashMap<Integer, Float> FlyFoodReductionMap = new HashMap<>() {{ private static final HashMap<Integer, Float> FlyFoodReductionMap = new HashMap<>() {{
// TODO: get real food id // TODO: get real food id
put(0, 0.8f); // Sample food put(0, 0.8f); // Sample food
}}; }};
private static final HashMap<Integer, Float> SwimFoodReductionMap = new HashMap<>() {{ private static final HashMap<Integer, Float> SwimFoodReductionMap = new HashMap<>() {{
// TODO: get real food id // TODO: get real food id
put(0, 0.8f); // Sample food put(0, 0.8f); // Sample food
}}; }};
private static final HashMap<Integer, Float> ClimbTalentReductionMap = new HashMap<>() {{ private static final HashMap<Integer, Float> ClimbTalentReductionMap = new HashMap<>() {{
put(262301, 0.8f); put(262301, 0.8f);
}}; }};
private static final HashMap<Integer, Float> FlyTalentReductionMap = new HashMap<>() {{ private static final HashMap<Integer, Float> FlyTalentReductionMap = new HashMap<>() {{
put(212301, 0.8f); put(212301, 0.8f);
put(222301, 0.8f); put(222301, 0.8f);
}}; }};
private static final HashMap<Integer, Float> SwimTalentReductionMap = new HashMap<>() {{ private static final HashMap<Integer, Float> SwimTalentReductionMap = new HashMap<>() {{
put(242301, 0.8f); put(242301, 0.8f);
put(542301, 0.8f); put(542301, 0.8f);
}}; }};
public static void initialize() { public static void initialize() {
// TODO: Initialize foods etc. // TODO: Initialize foods etc.
} }
public StaminaManager(Player player) { public StaminaManager(Player player) {
this.player = player; this.player = player;
} }
// Accessors // Accessors
public void setSkillCast(int skillId, int skillCasterId) { public void setSkillCast(int skillId, int skillCasterId) {
lastSkillFirstTick = true; lastSkillFirstTick = true;
lastSkillId = skillId; lastSkillId = skillId;
lastSkillCasterId = skillCasterId; lastSkillCasterId = skillCasterId;
} }
public int getMaxCharacterStamina() { public int getMaxCharacterStamina() {
return player.getProperty(PlayerProperty.PROP_MAX_STAMINA); return player.getProperty(PlayerProperty.PROP_MAX_STAMINA);
} }
public int getCurrentCharacterStamina() { public int getCurrentCharacterStamina() {
return player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA); return player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA);
} }
public int getMaxVehicleStamina() { public int getMaxVehicleStamina() {
return GlobalVehicleMaxStamina; return GlobalVehicleMaxStamina;
} }
public int getCurrentVehicleStamina() { public int getCurrentVehicleStamina() {
return vehicleStamina; return vehicleStamina;
} }
public boolean registerBeforeUpdateStaminaListener(String listenerName, BeforeUpdateStaminaListener listener) { public boolean registerBeforeUpdateStaminaListener(String listenerName, BeforeUpdateStaminaListener listener) {
if (beforeUpdateStaminaListeners.containsKey(listenerName)) { if (beforeUpdateStaminaListeners.containsKey(listenerName)) {
return false; return false;
} }
beforeUpdateStaminaListeners.put(listenerName, listener); beforeUpdateStaminaListeners.put(listenerName, listener);
return true; return true;
} }
public boolean unregisterBeforeUpdateStaminaListener(String listenerName) { public boolean unregisterBeforeUpdateStaminaListener(String listenerName) {
if (!beforeUpdateStaminaListeners.containsKey(listenerName)) { if (!beforeUpdateStaminaListeners.containsKey(listenerName)) {
return false; return false;
} }
beforeUpdateStaminaListeners.remove(listenerName); beforeUpdateStaminaListeners.remove(listenerName);
return true; return true;
} }
public boolean registerAfterUpdateStaminaListener(String listenerName, AfterUpdateStaminaListener listener) { public boolean registerAfterUpdateStaminaListener(String listenerName, AfterUpdateStaminaListener listener) {
if (afterUpdateStaminaListeners.containsKey(listenerName)) { if (afterUpdateStaminaListeners.containsKey(listenerName)) {
return false; return false;
} }
afterUpdateStaminaListeners.put(listenerName, listener); afterUpdateStaminaListeners.put(listenerName, listener);
return true; return true;
} }
public boolean unregisterAfterUpdateStaminaListener(String listenerName) { public boolean unregisterAfterUpdateStaminaListener(String listenerName) {
if (!afterUpdateStaminaListeners.containsKey(listenerName)) { if (!afterUpdateStaminaListeners.containsKey(listenerName)) {
return false; return false;
} }
afterUpdateStaminaListeners.remove(listenerName); afterUpdateStaminaListeners.remove(listenerName);
return true; return true;
} }
private boolean isPlayerMoving() { private boolean isPlayerMoving() {
float diffX = currentCoordinates.getX() - previousCoordinates.getX(); float diffX = currentCoordinates.getX() - previousCoordinates.getX();
float diffY = currentCoordinates.getY() - previousCoordinates.getY(); float diffY = currentCoordinates.getY() - previousCoordinates.getY();
float diffZ = currentCoordinates.getZ() - previousCoordinates.getZ(); float diffZ = currentCoordinates.getZ() - previousCoordinates.getZ();
logger.trace("isPlayerMoving: " + previousCoordinates + ", " + currentCoordinates + logger.trace("isPlayerMoving: " + previousCoordinates + ", " + currentCoordinates +
", " + diffX + ", " + diffY + ", " + diffZ); ", " + diffX + ", " + diffY + ", " + diffZ);
return Math.abs(diffX) > 0.3 || Math.abs(diffY) > 0.2 || Math.abs(diffZ) > 0.3; return Math.abs(diffX) > 0.3 || Math.abs(diffY) > 0.2 || Math.abs(diffZ) > 0.3;
} }
public int updateStaminaRelative(GameSession session, Consumption consumption, boolean isCharacterStamina) { public int updateStaminaRelative(GameSession session, Consumption consumption, boolean isCharacterStamina) {
int currentStamina = isCharacterStamina ? getCurrentCharacterStamina() : getCurrentVehicleStamina(); int currentStamina = isCharacterStamina ? getCurrentCharacterStamina() : getCurrentVehicleStamina();
if (consumption.amount == 0) { if (consumption.amount == 0) {
return currentStamina; return currentStamina;
} }
// notify will update // notify will update
for (Map.Entry<String, BeforeUpdateStaminaListener> listener : beforeUpdateStaminaListeners.entrySet()) { for (Map.Entry<String, BeforeUpdateStaminaListener> listener : beforeUpdateStaminaListeners.entrySet()) {
Consumption overriddenConsumption = listener.getValue().onBeforeUpdateStamina(consumption.type.toString(), consumption, isCharacterStamina); Consumption overriddenConsumption = listener.getValue().onBeforeUpdateStamina(consumption.type.toString(), consumption, isCharacterStamina);
if ((overriddenConsumption.type != consumption.type) && (overriddenConsumption.amount != consumption.amount)) { if ((overriddenConsumption.type != consumption.type) && (overriddenConsumption.amount != consumption.amount)) {
logger.debug("Stamina update relative(" + logger.debug("Stamina update relative(" +
consumption.type.toString() + ", " + consumption.amount + ") overridden to relative(" + consumption.type.toString() + ", " + consumption.amount + ") overridden to relative(" +
consumption.type.toString() + ", " + consumption.amount + ") by: " + listener.getKey()); consumption.type.toString() + ", " + consumption.amount + ") by: " + listener.getKey());
return currentStamina; return currentStamina;
} }
} }
int maxStamina = isCharacterStamina ? getMaxCharacterStamina() : getMaxVehicleStamina(); int maxStamina = isCharacterStamina ? getMaxCharacterStamina() : getMaxVehicleStamina();
logger.trace((isCharacterStamina ? "C " : "V ") + currentStamina + "/" + maxStamina + "\t" + currentState + "\t" + logger.trace((isCharacterStamina ? "C " : "V ") + currentStamina + "/" + maxStamina + "\t" + currentState + "\t" +
(isPlayerMoving() ? "moving" : " ") + "\t(" + consumption.type + "," + (isPlayerMoving() ? "moving" : " ") + "\t(" + consumption.type + "," +
consumption.amount + ")"); consumption.amount + ")");
int newStamina = currentStamina + consumption.amount; int newStamina = currentStamina + consumption.amount;
if (newStamina < 0) { if (newStamina < 0) {
newStamina = 0; newStamina = 0;
} else if (newStamina > maxStamina) { } else if (newStamina > maxStamina) {
newStamina = maxStamina; newStamina = maxStamina;
} }
return setStamina(session, consumption.type.toString(), newStamina, isCharacterStamina); return setStamina(session, consumption.type.toString(), newStamina, isCharacterStamina);
} }
public int updateStaminaAbsolute(GameSession session, String reason, int newStamina, boolean isCharacterStamina) { public int updateStaminaAbsolute(GameSession session, String reason, int newStamina, boolean isCharacterStamina) {
int currentStamina = isCharacterStamina ? getCurrentCharacterStamina() : getCurrentVehicleStamina(); int currentStamina = isCharacterStamina ? getCurrentCharacterStamina() : getCurrentVehicleStamina();
// notify will update // notify will update
for (Map.Entry<String, BeforeUpdateStaminaListener> listener : beforeUpdateStaminaListeners.entrySet()) { for (Map.Entry<String, BeforeUpdateStaminaListener> listener : beforeUpdateStaminaListeners.entrySet()) {
int overriddenNewStamina = listener.getValue().onBeforeUpdateStamina(reason, newStamina, isCharacterStamina); int overriddenNewStamina = listener.getValue().onBeforeUpdateStamina(reason, newStamina, isCharacterStamina);
if (overriddenNewStamina != newStamina) { if (overriddenNewStamina != newStamina) {
logger.debug("Stamina update absolute(" + logger.debug("Stamina update absolute(" +
reason + ", " + newStamina + ") overridden to absolute(" + reason + ", " + newStamina + ") overridden to absolute(" +
reason + ", " + newStamina + ") by: " + listener.getKey()); reason + ", " + newStamina + ") by: " + listener.getKey());
return currentStamina; return currentStamina;
} }
} }
int maxStamina = isCharacterStamina ? getMaxCharacterStamina() : getMaxVehicleStamina(); int maxStamina = isCharacterStamina ? getMaxCharacterStamina() : getMaxVehicleStamina();
if (newStamina < 0) { if (newStamina < 0) {
newStamina = 0; newStamina = 0;
} else if (newStamina > maxStamina) { } else if (newStamina > maxStamina) {
newStamina = maxStamina; newStamina = maxStamina;
} }
return setStamina(session, reason, newStamina, isCharacterStamina); return setStamina(session, reason, newStamina, isCharacterStamina);
} }
// Returns new stamina and sends PlayerPropNotify or VehicleStaminaNotify // Returns new stamina and sends PlayerPropNotify or VehicleStaminaNotify
public int setStamina(GameSession session, String reason, int newStamina, boolean isCharacterStamina) { public int setStamina(GameSession session, String reason, int newStamina, boolean isCharacterStamina) {
// Target Player // Target Player
if (!GAME_OPTIONS.staminaUsage || session.getPlayer().getStamina()) { if (!GAME_OPTIONS.staminaUsage || session.getPlayer().getStamina()) {
newStamina = getMaxCharacterStamina(); newStamina = getMaxCharacterStamina();
} }
// set stamina if is character stamina // set stamina if is character stamina
if (isCharacterStamina) { if (isCharacterStamina) {
player.setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA, newStamina); player.setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA, newStamina);
session.send(new PacketPlayerPropNotify(player, PlayerProperty.PROP_CUR_PERSIST_STAMINA)); session.send(new PacketPlayerPropNotify(player, PlayerProperty.PROP_CUR_PERSIST_STAMINA));
} else { } else {
vehicleStamina = newStamina; vehicleStamina = newStamina;
session.send(new PacketVehicleStaminaNotify(vehicleId, ((float) newStamina) / 100)); session.send(new PacketVehicleStaminaNotify(vehicleId, ((float) newStamina) / 100));
} }
// notify updated // notify updated
for (Map.Entry<String, AfterUpdateStaminaListener> listener : afterUpdateStaminaListeners.entrySet()) { for (Map.Entry<String, AfterUpdateStaminaListener> listener : afterUpdateStaminaListeners.entrySet()) {
listener.getValue().onAfterUpdateStamina(reason, newStamina, isCharacterStamina); listener.getValue().onAfterUpdateStamina(reason, newStamina, isCharacterStamina);
} }
return newStamina; return newStamina;
} }
// Kills avatar, removes entity and sends notification. // Kills avatar, removes entity and sends notification.
// TODO: Probably move this to Avatar class? since other components may also need to kill avatar. // TODO: Probably move this to Avatar class? since other components may also need to kill avatar.
public void killAvatar(GameSession session, GameEntity entity, PlayerDieType dieType) { public void killAvatar(GameSession session, GameEntity entity, PlayerDieType dieType) {
session.send(new PacketAvatarLifeStateChangeNotify(player.getTeamManager().getCurrentAvatarEntity().getAvatar(), session.send(new PacketAvatarLifeStateChangeNotify(player.getTeamManager().getCurrentAvatarEntity().getAvatar(),
LifeState.LIFE_DEAD, dieType)); LifeState.LIFE_DEAD, dieType));
session.send(new PacketLifeStateChangeNotify(entity, LifeState.LIFE_DEAD, dieType)); session.send(new PacketLifeStateChangeNotify(entity, LifeState.LIFE_DEAD, dieType));
entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0); entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0);
entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP)); entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP));
entity.getWorld().broadcastPacket(new PacketLifeStateChangeNotify(0, entity, LifeState.LIFE_DEAD)); entity.getWorld().broadcastPacket(new PacketLifeStateChangeNotify(0, entity, LifeState.LIFE_DEAD));
player.getScene().removeEntity(entity); player.getScene().removeEntity(entity);
((EntityAvatar) entity).onDeath(dieType, 0); ((EntityAvatar) entity).onDeath(dieType, 0);
} }
public void startSustainedStaminaHandler() { public void startSustainedStaminaHandler() {
if (!player.isPaused() && sustainedStaminaHandlerTimer == null) { if (!player.isPaused() && sustainedStaminaHandlerTimer == null) {
sustainedStaminaHandlerTimer = new Timer(); sustainedStaminaHandlerTimer = new Timer();
sustainedStaminaHandlerTimer.scheduleAtFixedRate(new SustainedStaminaHandler(), 0, 200); sustainedStaminaHandlerTimer.scheduleAtFixedRate(new SustainedStaminaHandler(), 0, 200);
logger.debug("[MovementManager] SustainedStaminaHandlerTimer started"); logger.debug("[MovementManager] SustainedStaminaHandlerTimer started");
} }
} }
public void stopSustainedStaminaHandler() { public void stopSustainedStaminaHandler() {
if (sustainedStaminaHandlerTimer != null) { if (sustainedStaminaHandlerTimer != null) {
sustainedStaminaHandlerTimer.cancel(); sustainedStaminaHandlerTimer.cancel();
sustainedStaminaHandlerTimer = null; sustainedStaminaHandlerTimer = null;
logger.debug("[MovementManager] SustainedStaminaHandlerTimer stopped"); logger.debug("[MovementManager] SustainedStaminaHandlerTimer stopped");
} }
} }
// Handlers // Handlers
// External trigger handler // External trigger handler
public void handleEvtDoSkillSuccNotify(GameSession session, int skillId, int casterId) { public void handleEvtDoSkillSuccNotify(GameSession session, int skillId, int casterId) {
// Ignore if skill not cast by not current active avatar // Ignore if skill not cast by not current active avatar
if (casterId != player.getTeamManager().getCurrentAvatarEntity().getId()) { if (casterId != player.getTeamManager().getCurrentAvatarEntity().getId()) {
return; return;
} }
setSkillCast(skillId, casterId); setSkillCast(skillId, casterId);
// Handle immediate stamina cost // Handle immediate stamina cost
Avatar currentAvatar = player.getTeamManager().getCurrentAvatarEntity().getAvatar(); Avatar currentAvatar = player.getTeamManager().getCurrentAvatarEntity().getAvatar();
if (currentAvatar.getAvatarData().getWeaponType() == WeaponType.WEAPON_CLAYMORE) { if (currentAvatar.getAvatarData().getWeaponType() == WeaponType.WEAPON_CLAYMORE) {
// Exclude claymore as their stamina cost starts when MixinStaminaCost gets in // Exclude claymore as their stamina cost starts when MixinStaminaCost gets in
return; return;
} }
// TODO: Differentiate normal attacks from charged attacks and exclude // TODO: Differentiate normal attacks from charged attacks and exclude
// TODO: Temporary: Exclude non-claymore attacks for now // TODO: Temporary: Exclude non-claymore attacks for now
/* /*
if (BowAvatars.contains(currentAvatarId) if (BowAvatars.contains(currentAvatarId)
|| SwordAvatars.contains(currentAvatarId) || SwordAvatars.contains(currentAvatarId)
|| PolearmAvatars.contains(currentAvatarId) || PolearmAvatars.contains(currentAvatarId)
|| CatalystAvatars.contains(currentAvatarId) || CatalystAvatars.contains(currentAvatarId)
) { ) {
return; return;
} }
*/ */
//handleImmediateStamina(session, skillId); //handleImmediateStamina(session, skillId);
} }
public void handleMixinCostStamina(boolean isSwim) { public void handleMixinCostStamina(boolean isSwim) {
// Talent moving and claymore avatar charged attack duration // Talent moving and claymore avatar charged attack duration
// logger.trace("abilityMixinCostStamina: isSwim: " + isSwim + "\tlastSkill: " + lastSkillId); // logger.trace("abilityMixinCostStamina: isSwim: " + isSwim + "\tlastSkill: " + lastSkillId);
if (lastSkillCasterId == player.getTeamManager().getCurrentAvatarEntity().getId()) { if (lastSkillCasterId == player.getTeamManager().getCurrentAvatarEntity().getId()) {
handleImmediateStamina(cachedSession, lastSkillId); handleImmediateStamina(cachedSession, lastSkillId);
} }
} }
public void handleCombatInvocationsNotify(@NotNull GameSession session, @NotNull EntityMoveInfo moveInfo, @NotNull GameEntity entity) { public void handleCombatInvocationsNotify(@NotNull GameSession session, @NotNull EntityMoveInfo moveInfo, @NotNull GameEntity entity) {
// cache info for later use in SustainedStaminaHandler tick // cache info for later use in SustainedStaminaHandler tick
cachedSession = session; cachedSession = session;
cachedEntity = entity; cachedEntity = entity;
MotionInfo motionInfo = moveInfo.getMotionInfo(); MotionInfo motionInfo = moveInfo.getMotionInfo();
MotionState motionState = motionInfo.getState(); MotionState motionState = motionInfo.getState();
int notifyEntityId = entity.getId(); int notifyEntityId = entity.getId();
int currentAvatarEntityId = session.getPlayer().getTeamManager().getCurrentAvatarEntity().getId(); int currentAvatarEntityId = session.getPlayer().getTeamManager().getCurrentAvatarEntity().getId();
if (notifyEntityId != currentAvatarEntityId && notifyEntityId != vehicleId) { if (notifyEntityId != currentAvatarEntityId && notifyEntityId != vehicleId) {
return; return;
} }
currentState = motionState; currentState = motionState;
// logger.trace(currentState + "\t" + (notifyEntityId == currentAvatarEntityId ? "character" : "vehicle")); // logger.trace(currentState + "\t" + (notifyEntityId == currentAvatarEntityId ? "character" : "vehicle"));
Vector posVector = motionInfo.getPos(); Vector posVector = motionInfo.getPos();
Position newPos = new Position(posVector.getX(), posVector.getY(), posVector.getZ()); Position newPos = new Position(posVector.getX(), posVector.getY(), posVector.getZ());
if (newPos.getX() != 0 && newPos.getY() != 0 && newPos.getZ() != 0) { if (newPos.getX() != 0 && newPos.getY() != 0 && newPos.getZ() != 0) {
currentCoordinates = newPos; currentCoordinates = newPos;
} }
startSustainedStaminaHandler(); startSustainedStaminaHandler();
handleImmediateStamina(session, motionState); handleImmediateStamina(session, motionState);
} }
public void handleVehicleInteractReq(GameSession session, int vehicleId, VehicleInteractType vehicleInteractType) { public void handleVehicleInteractReq(GameSession session, int vehicleId, VehicleInteractType vehicleInteractType) {
if (vehicleInteractType == VehicleInteractType.VEHICLE_INTERACT_TYPE_IN) { if (vehicleInteractType == VehicleInteractType.VEHICLE_INTERACT_TYPE_IN) {
this.vehicleId = vehicleId; this.vehicleId = vehicleId;
// Reset character stamina here to prevent falling into water immediately on ejection if char stamina is // Reset character stamina here to prevent falling into water immediately on ejection if char stamina is
// close to empty when boarding. // close to empty when boarding.
updateStaminaAbsolute(session, "board vehicle", getMaxCharacterStamina(), true); updateStaminaAbsolute(session, "board vehicle", getMaxCharacterStamina(), true);
updateStaminaAbsolute(session, "board vehicle", getMaxVehicleStamina(), false); updateStaminaAbsolute(session, "board vehicle", getMaxVehicleStamina(), false);
} else { } else {
this.vehicleId = -1; this.vehicleId = -1;
} }
} }
// Internal handler // Internal handler
private void handleImmediateStamina(GameSession session, @NotNull MotionState motionState) { private void handleImmediateStamina(GameSession session, @NotNull MotionState motionState) {
switch (motionState) { switch (motionState) {
case MOTION_STATE_CLIMB: case MOTION_STATE_CLIMB:
if (currentState != MotionState.MOTION_STATE_CLIMB) { if (currentState != MotionState.MOTION_STATE_CLIMB) {
updateStaminaRelative(session, new Consumption(ConsumptionType.CLIMB_START), true); updateStaminaRelative(session, new Consumption(ConsumptionType.CLIMB_START), true);
} }
break; break;
case MOTION_STATE_DASH_BEFORE_SHAKE: case MOTION_STATE_DASH_BEFORE_SHAKE:
if (previousState != MotionState.MOTION_STATE_DASH_BEFORE_SHAKE) { if (previousState != MotionState.MOTION_STATE_DASH_BEFORE_SHAKE) {
updateStaminaRelative(session, new Consumption(ConsumptionType.SPRINT), true); updateStaminaRelative(session, new Consumption(ConsumptionType.SPRINT), true);
} }
break; break;
case MOTION_STATE_CLIMB_JUMP: case MOTION_STATE_CLIMB_JUMP:
if (previousState != MotionState.MOTION_STATE_CLIMB_JUMP) { if (previousState != MotionState.MOTION_STATE_CLIMB_JUMP) {
updateStaminaRelative(session, new Consumption(ConsumptionType.CLIMB_JUMP), true); updateStaminaRelative(session, new Consumption(ConsumptionType.CLIMB_JUMP), true);
} }
break; break;
case MOTION_STATE_SWIM_DASH: case MOTION_STATE_SWIM_DASH:
if (previousState != MotionState.MOTION_STATE_SWIM_DASH) { if (previousState != MotionState.MOTION_STATE_SWIM_DASH) {
updateStaminaRelative(session, new Consumption(ConsumptionType.SWIM_DASH_START), true); updateStaminaRelative(session, new Consumption(ConsumptionType.SWIM_DASH_START), true);
} }
break; break;
} }
} }
private void handleImmediateStamina(GameSession session, int skillId) { private void handleImmediateStamina(GameSession session, int skillId) {
Consumption consumption = getFightConsumption(skillId); Consumption consumption = getFightConsumption(skillId);
updateStaminaRelative(session, consumption, true); updateStaminaRelative(session, consumption, true);
} }
private class SustainedStaminaHandler extends TimerTask { private class SustainedStaminaHandler extends TimerTask {
public void run() { public void run() {
boolean moving = isPlayerMoving(); boolean moving = isPlayerMoving();
int currentCharacterStamina = getCurrentCharacterStamina(); int currentCharacterStamina = getCurrentCharacterStamina();
int maxCharacterStamina = getMaxCharacterStamina(); int maxCharacterStamina = getMaxCharacterStamina();
int currentVehicleStamina = getCurrentVehicleStamina(); int currentVehicleStamina = getCurrentVehicleStamina();
int maxVehicleStamina = getMaxVehicleStamina(); int maxVehicleStamina = getMaxVehicleStamina();
if (moving || (currentCharacterStamina < maxCharacterStamina) || (currentVehicleStamina < maxVehicleStamina)) { if (moving || (currentCharacterStamina < maxCharacterStamina) || (currentVehicleStamina < maxVehicleStamina)) {
logger.trace("Player moving: " + moving + ", stamina full: " + logger.trace("Player moving: " + moving + ", stamina full: " +
(currentCharacterStamina >= maxCharacterStamina) + ", recalculate stamina"); (currentCharacterStamina >= maxCharacterStamina) + ", recalculate stamina");
boolean isCharacterStamina = true; boolean isCharacterStamina = true;
Consumption consumption; Consumption consumption;
if (MotionStatesCategorized.get("CLIMB").contains(currentState)) { if (MotionStatesCategorized.get("CLIMB").contains(currentState)) {
consumption = getClimbConsumption(); consumption = getClimbConsumption();
} else if (MotionStatesCategorized.get("DASH").contains(currentState)) { } else if (MotionStatesCategorized.get("DASH").contains(currentState)) {
consumption = getDashConsumption(); consumption = getDashConsumption();
} else if (MotionStatesCategorized.get("FLY").contains(currentState)) { } else if (MotionStatesCategorized.get("FLY").contains(currentState)) {
consumption = getFlyConsumption(); consumption = getFlyConsumption();
} else if (MotionStatesCategorized.get("RUN").contains(currentState)) { } else if (MotionStatesCategorized.get("RUN").contains(currentState)) {
consumption = new Consumption(ConsumptionType.RUN); consumption = new Consumption(ConsumptionType.RUN);
} else if (MotionStatesCategorized.get("SKIFF").contains(currentState)) { } else if (MotionStatesCategorized.get("SKIFF").contains(currentState)) {
consumption = getSkiffConsumption(); consumption = getSkiffConsumption();
isCharacterStamina = false; isCharacterStamina = false;
} else if (MotionStatesCategorized.get("STANDBY").contains(currentState)) { } else if (MotionStatesCategorized.get("STANDBY").contains(currentState)) {
consumption = new Consumption(ConsumptionType.STANDBY); consumption = new Consumption(ConsumptionType.STANDBY);
} else if (MotionStatesCategorized.get("SWIM").contains(currentState)) { } else if (MotionStatesCategorized.get("SWIM").contains(currentState)) {
consumption = getSwimConsumptions(); consumption = getSwimConsumptions();
} else if (MotionStatesCategorized.get("WALK").contains(currentState)) { } else if (MotionStatesCategorized.get("WALK").contains(currentState)) {
consumption = new Consumption(ConsumptionType.WALK); consumption = new Consumption(ConsumptionType.WALK);
} else if (MotionStatesCategorized.get("NOCOST_NORECOVER").contains(currentState)) { } else if (MotionStatesCategorized.get("NOCOST_NORECOVER").contains(currentState)) {
consumption = new Consumption(); consumption = new Consumption();
} else if (MotionStatesCategorized.get("OTHER").contains(currentState)) { } else if (MotionStatesCategorized.get("OTHER").contains(currentState)) {
consumption = getOtherConsumptions(); consumption = getOtherConsumptions();
} else { // ignore } else { // ignore
return; return;
} }
if (consumption.amount < 0 && isCharacterStamina) { if (consumption.amount < 0 && isCharacterStamina) {
// Do not apply reduction factor when recovering stamina // Do not apply reduction factor when recovering stamina
if (player.getTeamManager().getTeamResonances().contains(10301)) { if (player.getTeamManager().getTeamResonances().contains(10301)) {
consumption.amount *= 0.85f; consumption.amount *= 0.85f;
} }
} }
// Delay 1 seconds before starts recovering stamina // Delay 1 seconds before starts recovering stamina
if (consumption.amount != 0 && cachedSession != null) { if (consumption.amount != 0 && cachedSession != null) {
if (consumption.amount < 0) { if (consumption.amount < 0) {
staminaRecoverDelay = 0; staminaRecoverDelay = 0;
} }
if (consumption.amount > 0 if (consumption.amount > 0
&& consumption.type != ConsumptionType.POWERED_FLY && consumption.type != ConsumptionType.POWERED_FLY
&& consumption.type != ConsumptionType.POWERED_SKIFF) { && consumption.type != ConsumptionType.POWERED_SKIFF) {
// For POWERED_* recover immediately - things like Amber's gliding exam and skiff challenges may require this. // For POWERED_* recover immediately - things like Amber's gliding exam and skiff challenges may require this.
if (staminaRecoverDelay < 5) { if (staminaRecoverDelay < 5) {
// For others recover after 1 seconds (5 ticks) - as official server does. // For others recover after 1 seconds (5 ticks) - as official server does.
staminaRecoverDelay++; staminaRecoverDelay++;
consumption.amount = 0; consumption.amount = 0;
logger.trace("Delaying recovery: " + staminaRecoverDelay); logger.trace("Delaying recovery: " + staminaRecoverDelay);
} }
} }
updateStaminaRelative(cachedSession, consumption, isCharacterStamina); updateStaminaRelative(cachedSession, consumption, isCharacterStamina);
} }
} }
previousState = currentState; previousState = currentState;
previousCoordinates = new Position( previousCoordinates = new Position(
currentCoordinates.getX(), currentCoordinates.getX(),
currentCoordinates.getY(), currentCoordinates.getY(),
currentCoordinates.getZ() currentCoordinates.getZ()
); );
} }
} }
private void handleDrowning() { private void handleDrowning() {
// TODO: fix drowning waverider entity // TODO: fix drowning waverider entity
int stamina = getCurrentCharacterStamina(); int stamina = getCurrentCharacterStamina();
if (stamina < 10) { if (stamina < 10) {
logger.trace(getCurrentCharacterStamina() + "/" + logger.trace(getCurrentCharacterStamina() + "/" +
getMaxCharacterStamina() + "\t" + currentState); getMaxCharacterStamina() + "\t" + currentState);
if (currentState != MotionState.MOTION_STATE_SWIM_IDLE) { if (currentState != MotionState.MOTION_STATE_SWIM_IDLE) {
killAvatar(cachedSession, cachedEntity, PlayerDieType.PLAYER_DIE_TYPE_DRAWN); killAvatar(cachedSession, cachedEntity, PlayerDieType.PLAYER_DIE_TYPE_DRAWN);
} }
} }
} }
// Consumption Calculators // Consumption Calculators
// Stamina Consumption Reduction: https://genshin-impact.fandom.com/wiki/Stamina // Stamina Consumption Reduction: https://genshin-impact.fandom.com/wiki/Stamina
private Consumption getFightConsumption(int skillCasting) { private Consumption getFightConsumption(int skillCasting) {
// Talent moving // Talent moving
if (TalentMovements.contains(skillCasting)) { if (TalentMovements.contains(skillCasting)) {
// TODO: recover 1000 if kamisato hits an enemy at the end of dashing // TODO: recover 1000 if kamisato hits an enemy at the end of dashing
return getTalentMovingSustainedCost(skillCasting); return getTalentMovingSustainedCost(skillCasting);
} }
// Bow avatar charged attack // Bow avatar charged attack
Avatar currentAvatar = player.getTeamManager().getCurrentAvatarEntity().getAvatar(); Avatar currentAvatar = player.getTeamManager().getCurrentAvatarEntity().getAvatar();
switch (currentAvatar.getAvatarData().getWeaponType()) { switch (currentAvatar.getAvatarData().getWeaponType()) {
case WEAPON_BOW: case WEAPON_BOW:
return getBowSustainedCost(skillCasting); return getBowSustainedCost(skillCasting);
case WEAPON_CLAYMORE: case WEAPON_CLAYMORE:
return getClaymoreSustainedCost(skillCasting); return getClaymoreSustainedCost(skillCasting);
case WEAPON_CATALYST: case WEAPON_CATALYST:
return getCatalystCost(skillCasting); return getCatalystCost(skillCasting);
case WEAPON_POLE: case WEAPON_POLE:
return getPolearmCost(skillCasting); return getPolearmCost(skillCasting);
case WEAPON_SWORD_ONE_HAND: case WEAPON_SWORD_ONE_HAND:
return getSwordCost(skillCasting); return getSwordCost(skillCasting);
} }
return new Consumption(); return new Consumption();
} }
private Consumption getClimbConsumption() { private Consumption getClimbConsumption() {
Consumption consumption = new Consumption(); Consumption consumption = new Consumption();
if (currentState == MotionState.MOTION_STATE_CLIMB && isPlayerMoving()) { if (currentState == MotionState.MOTION_STATE_CLIMB && isPlayerMoving()) {
consumption.type = ConsumptionType.CLIMBING; consumption.type = ConsumptionType.CLIMBING;
consumption.amount = ConsumptionType.CLIMBING.amount; consumption.amount = ConsumptionType.CLIMBING.amount;
} }
// Climbing specific reductions // Climbing specific reductions
consumption.amount *= getFoodCostReductionFactor(ClimbFoodReductionMap); consumption.amount *= getFoodCostReductionFactor(ClimbFoodReductionMap);
consumption.amount *= getTalentCostReductionFactor(ClimbTalentReductionMap); consumption.amount *= getTalentCostReductionFactor(ClimbTalentReductionMap);
return consumption; return consumption;
} }
private Consumption getSwimConsumptions() { private Consumption getSwimConsumptions() {
handleDrowning(); handleDrowning();
Consumption consumption = new Consumption(); Consumption consumption = new Consumption();
if (currentState == MotionState.MOTION_STATE_SWIM_MOVE) { if (currentState == MotionState.MOTION_STATE_SWIM_MOVE) {
consumption.type = ConsumptionType.SWIMMING; consumption.type = ConsumptionType.SWIMMING;
consumption.amount = ConsumptionType.SWIMMING.amount; consumption.amount = ConsumptionType.SWIMMING.amount;
} }
if (currentState == MotionState.MOTION_STATE_SWIM_DASH) { if (currentState == MotionState.MOTION_STATE_SWIM_DASH) {
consumption.type = ConsumptionType.SWIM_DASH; consumption.type = ConsumptionType.SWIM_DASH;
consumption.amount = ConsumptionType.SWIM_DASH.amount; consumption.amount = ConsumptionType.SWIM_DASH.amount;
} }
// Swimming specific reductions // Swimming specific reductions
consumption.amount *= getFoodCostReductionFactor(SwimFoodReductionMap); consumption.amount *= getFoodCostReductionFactor(SwimFoodReductionMap);
consumption.amount *= getTalentCostReductionFactor(SwimTalentReductionMap); consumption.amount *= getTalentCostReductionFactor(SwimTalentReductionMap);
return consumption; return consumption;
} }
private Consumption getDashConsumption() { private Consumption getDashConsumption() {
Consumption consumption = new Consumption(); Consumption consumption = new Consumption();
if (currentState == MotionState.MOTION_STATE_DASH) { if (currentState == MotionState.MOTION_STATE_DASH) {
consumption.type = ConsumptionType.DASH; consumption.type = ConsumptionType.DASH;
consumption.amount = ConsumptionType.DASH.amount; consumption.amount = ConsumptionType.DASH.amount;
// Dashing specific reductions // Dashing specific reductions
consumption.amount *= getFoodCostReductionFactor(DashFoodReductionMap); consumption.amount *= getFoodCostReductionFactor(DashFoodReductionMap);
} }
return consumption; return consumption;
} }
private Consumption getFlyConsumption() { private Consumption getFlyConsumption() {
// POWERED_FLY, e.g. wind tunnel // POWERED_FLY, e.g. wind tunnel
if (currentState == MotionState.MOTION_STATE_POWERED_FLY) { if (currentState == MotionState.MOTION_STATE_POWERED_FLY) {
return new Consumption(ConsumptionType.POWERED_FLY); return new Consumption(ConsumptionType.POWERED_FLY);
} }
Consumption consumption = new Consumption(ConsumptionType.FLY); Consumption consumption = new Consumption(ConsumptionType.FLY);
// Flying specific reductions // Flying specific reductions
consumption.amount *= getFoodCostReductionFactor(FlyFoodReductionMap); consumption.amount *= getFoodCostReductionFactor(FlyFoodReductionMap);
consumption.amount *= getTalentCostReductionFactor(FlyTalentReductionMap); consumption.amount *= getTalentCostReductionFactor(FlyTalentReductionMap);
return consumption; return consumption;
} }
private Consumption getSkiffConsumption() { private Consumption getSkiffConsumption() {
// No known reduction for skiffing. // No known reduction for skiffing.
return switch (currentState) { return switch (currentState) {
case MOTION_STATE_SKIFF_DASH -> new Consumption(ConsumptionType.SKIFF_DASH); case MOTION_STATE_SKIFF_DASH -> new Consumption(ConsumptionType.SKIFF_DASH);
case MOTION_STATE_SKIFF_POWERED_DASH -> new Consumption(ConsumptionType.POWERED_SKIFF); case MOTION_STATE_SKIFF_POWERED_DASH -> new Consumption(ConsumptionType.POWERED_SKIFF);
case MOTION_STATE_SKIFF_NORMAL -> new Consumption(ConsumptionType.SKIFF); case MOTION_STATE_SKIFF_NORMAL -> new Consumption(ConsumptionType.SKIFF);
default -> new Consumption(); default -> new Consumption();
}; };
} }
private Consumption getOtherConsumptions() { private Consumption getOtherConsumptions() {
switch (currentState) { switch (currentState) {
case MOTION_STATE_NOTIFY: case MOTION_STATE_NOTIFY:
// if (BowSkills.contains(lastSkillId)) { // if (BowSkills.contains(lastSkillId)) {
// return new Consumption(ConsumptionType.FIGHT, 500); // return new Consumption(ConsumptionType.FIGHT, 500);
// } // }
break; break;
case MOTION_STATE_FIGHT: case MOTION_STATE_FIGHT:
// TODO: what if charged attack // TODO: what if charged attack
return new Consumption(ConsumptionType.FIGHT, 500); return new Consumption(ConsumptionType.FIGHT, 500);
} }
return new Consumption(); return new Consumption();
} }
// Reduction getter // Reduction getter
private float getTalentCostReductionFactor(HashMap<Integer, Float> talentReductionMap) { private float getTalentCostReductionFactor(HashMap<Integer, Float> talentReductionMap) {
// All known talents reductions are not stackable // All known talents reductions are not stackable
float reduction = 1; float reduction = 1;
for (EntityAvatar entity : cachedSession.getPlayer().getTeamManager().getActiveTeam()) { for (EntityAvatar entity : cachedSession.getPlayer().getTeamManager().getActiveTeam()) {
for (int skillId : entity.getAvatar().getProudSkillList()) { for (int skillId : entity.getAvatar().getProudSkillList()) {
if (talentReductionMap.containsKey(skillId)) { if (talentReductionMap.containsKey(skillId)) {
float potentialLowerReduction = talentReductionMap.get(skillId); float potentialLowerReduction = talentReductionMap.get(skillId);
if (potentialLowerReduction < reduction) { if (potentialLowerReduction < reduction) {
reduction = potentialLowerReduction; reduction = potentialLowerReduction;
} }
} }
} }
} }
return reduction; return reduction;
} }
private float getFoodCostReductionFactor(HashMap<Integer, Float> foodReductionMap) { private float getFoodCostReductionFactor(HashMap<Integer, Float> foodReductionMap) {
// All known food reductions are not stackable // All known food reductions are not stackable
// TODO: Check consumed food (buff?) and return proper factor // TODO: Check consumed food (buff?) and return proper factor
float reduction = 1; float reduction = 1;
return reduction; return reduction;
} }
private Consumption getTalentMovingSustainedCost(int skillId) { private Consumption getTalentMovingSustainedCost(int skillId) {
if (lastSkillFirstTick) { if (lastSkillFirstTick) {
lastSkillFirstTick = false; lastSkillFirstTick = false;
return new Consumption(ConsumptionType.TALENT_DASH, -1000); return new Consumption(ConsumptionType.TALENT_DASH, -1000);
} else { } else {
return new Consumption(ConsumptionType.TALENT_DASH, -500); return new Consumption(ConsumptionType.TALENT_DASH, -500);
} }
} }
private Consumption getBowSustainedCost(int skillId) { private Consumption getBowSustainedCost(int skillId) {
// Note that bow skills actually recovers stamina // Note that bow skills actually recovers stamina
// Character specific handling // Character specific handling
// switch (skillId) { // switch (skillId) {
// // No known bow skills cost stamina // // No known bow skills cost stamina
// } // }
return new Consumption(ConsumptionType.FIGHT, +500); return new Consumption(ConsumptionType.FIGHT, +500);
} }
private Consumption getCatalystCost(int skillId) { private Consumption getCatalystCost(int skillId) {
Consumption consumption = new Consumption(ConsumptionType.FIGHT, -5000); Consumption consumption = new Consumption(ConsumptionType.FIGHT, -5000);
// Character specific handling // Character specific handling
switch (skillId) { switch (skillId) {
// TODO: // TODO:
} }
return consumption; return consumption;
} }
private Consumption getClaymoreSustainedCost(int skillId) { private Consumption getClaymoreSustainedCost(int skillId) {
Consumption consumption = new Consumption(ConsumptionType.FIGHT, -1333); // 4000 / 3 = 1333 Consumption consumption = new Consumption(ConsumptionType.FIGHT, -1333); // 4000 / 3 = 1333
// Character specific handling // Character specific handling
switch (skillId) { switch (skillId) {
case 10571: case 10571:
case 10532: case 10532:
consumption.amount = 0; consumption.amount = 0;
break; break;
case 10160: case 10160:
if (player.getTeamManager().getCurrentAvatarEntity().getAvatar().getProudSkillList().contains(162101)) { if (player.getTeamManager().getCurrentAvatarEntity().getAvatar().getProudSkillList().contains(162101)) {
consumption.amount /= 2; consumption.amount /= 2;
} }
break; break;
} }
return consumption; return consumption;
} }
private Consumption getPolearmCost(int skillId) { private Consumption getPolearmCost(int skillId) {
Consumption consumption = new Consumption(ConsumptionType.FIGHT, -2500); Consumption consumption = new Consumption(ConsumptionType.FIGHT, -2500);
// Character specific handling // Character specific handling
switch (skillId) { switch (skillId) {
// TODO: // TODO:
} }
return consumption; return consumption;
} }
private Consumption getSwordCost(int skillId) { private Consumption getSwordCost(int skillId) {
Consumption consumption = new Consumption(ConsumptionType.FIGHT, -2000); Consumption consumption = new Consumption(ConsumptionType.FIGHT, -2000);
// Character specific handling // Character specific handling
switch (skillId) { switch (skillId) {
case 10421: case 10421:
consumption.amount = -2500; consumption.amount = -2500;
break; break;
} }
return consumption; return consumption;
} }
} }
...@@ -14,7 +14,6 @@ import emu.grasscutter.game.avatar.AvatarProfileData; ...@@ -14,7 +14,6 @@ import emu.grasscutter.game.avatar.AvatarProfileData;
import emu.grasscutter.game.avatar.AvatarStorage; import emu.grasscutter.game.avatar.AvatarStorage;
import emu.grasscutter.game.entity.EntityMonster; import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.game.entity.EntityVehicle; import emu.grasscutter.game.entity.EntityVehicle;
import emu.grasscutter.game.managers.DeforestationManager.DeforestationManager;
import emu.grasscutter.game.entity.EntityGadget; import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.EntityItem; import emu.grasscutter.game.entity.EntityItem;
import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.game.entity.GameEntity;
...@@ -28,18 +27,19 @@ import emu.grasscutter.game.mail.Mail; ...@@ -28,18 +27,19 @@ import emu.grasscutter.game.mail.Mail;
import emu.grasscutter.game.mail.MailHandler; import emu.grasscutter.game.mail.MailHandler;
import emu.grasscutter.game.managers.InsectCaptureManager; import emu.grasscutter.game.managers.InsectCaptureManager;
import emu.grasscutter.game.managers.ResinManager; import emu.grasscutter.game.managers.ResinManager;
import emu.grasscutter.game.managers.StaminaManager.StaminaManager; import emu.grasscutter.game.managers.deforestation.DeforestationManager;
import emu.grasscutter.game.managers.energy.EnergyManager;
import emu.grasscutter.game.managers.forging.ActiveForgeData;
import emu.grasscutter.game.managers.forging.ForgingManager;
import emu.grasscutter.game.managers.mapmark.*;
import emu.grasscutter.game.managers.stamina.StaminaManager;
import emu.grasscutter.game.managers.SotSManager; import emu.grasscutter.game.managers.SotSManager;
import emu.grasscutter.game.managers.EnergyManager.EnergyManager;
import emu.grasscutter.game.managers.ForgingManager.ActiveForgeData;
import emu.grasscutter.game.managers.ForgingManager.ForgingManager;
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;
import emu.grasscutter.game.props.SceneType; import emu.grasscutter.game.props.SceneType;
import emu.grasscutter.game.quest.QuestManager; import emu.grasscutter.game.quest.QuestManager;
import emu.grasscutter.game.shop.ShopLimit; import emu.grasscutter.game.shop.ShopLimit;
import emu.grasscutter.game.managers.MapMarkManager.*;
import emu.grasscutter.game.tower.TowerData; import emu.grasscutter.game.tower.TowerData;
import emu.grasscutter.game.tower.TowerManager; import emu.grasscutter.game.tower.TowerManager;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
......
...@@ -10,10 +10,10 @@ import emu.grasscutter.game.drop.DropManager; ...@@ -10,10 +10,10 @@ import emu.grasscutter.game.drop.DropManager;
import emu.grasscutter.game.dungeons.DungeonManager; import emu.grasscutter.game.dungeons.DungeonManager;
import emu.grasscutter.game.expedition.ExpeditionManager; import emu.grasscutter.game.expedition.ExpeditionManager;
import emu.grasscutter.game.gacha.GachaManager; import emu.grasscutter.game.gacha.GachaManager;
import emu.grasscutter.game.managers.ChatManager.ChatManager;
import emu.grasscutter.game.managers.ChatManager.ChatManagerHandler;
import emu.grasscutter.game.managers.InventoryManager; import emu.grasscutter.game.managers.InventoryManager;
import emu.grasscutter.game.managers.MultiplayerManager; import emu.grasscutter.game.managers.MultiplayerManager;
import emu.grasscutter.game.managers.chat.ChatManager;
import emu.grasscutter.game.managers.chat.ChatManagerHandler;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.ServerQuestHandler; import emu.grasscutter.game.quest.ServerQuestHandler;
import emu.grasscutter.game.shop.ShopManager; import emu.grasscutter.game.shop.ShopManager;
......
package emu.grasscutter.server.packet.send; package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.managers.MapMarkManager.MapMark; import emu.grasscutter.game.managers.mapmark.MapMark;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketOpcodes;
......
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