mirror of
https://github.com/Melledy/Grasscutter.git
synced 2024-11-23 17:17:43 +00:00
Update StaminaManager
1. Update function signatures to prepare for vehicle stamina. 3. Remove hard-coded skills. 2. Wind resonance -15% stamina cost. 4. Climb talent cost reduction. 5. Swim talent cost reduction. 6. Diluc will now consume stamina at full price if talent not activated. 7. Sayu's windwheel no longer consumes stamina.
This commit is contained in:
parent
5ff8a4514e
commit
2fa2746246
@ -4,6 +4,7 @@ import java.io.*;
|
|||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
|
|
||||||
import emu.grasscutter.command.CommandMap;
|
import emu.grasscutter.command.CommandMap;
|
||||||
|
import emu.grasscutter.game.managers.StaminaManager.StaminaManager;
|
||||||
import emu.grasscutter.plugin.PluginManager;
|
import emu.grasscutter.plugin.PluginManager;
|
||||||
import emu.grasscutter.plugin.api.ServerHook;
|
import emu.grasscutter.plugin.api.ServerHook;
|
||||||
import emu.grasscutter.scripts.ScriptLoader;
|
import emu.grasscutter.scripts.ScriptLoader;
|
||||||
@ -110,6 +111,9 @@ public final class Grasscutter {
|
|||||||
new ServerHook(gameServer, dispatchServer);
|
new ServerHook(gameServer, dispatchServer);
|
||||||
// Create plugin manager instance.
|
// Create plugin manager instance.
|
||||||
pluginManager = new PluginManager();
|
pluginManager = new PluginManager();
|
||||||
|
|
||||||
|
// TODO: find a better place?
|
||||||
|
StaminaManager.initialize();
|
||||||
|
|
||||||
// Start servers.
|
// Start servers.
|
||||||
var runMode = SERVER.runMode;
|
var runMode = SERVER.runMode;
|
||||||
|
@ -2,6 +2,7 @@ package emu.grasscutter.game.managers.StaminaManager;
|
|||||||
|
|
||||||
import ch.qos.logback.classic.Logger;
|
import ch.qos.logback.classic.Logger;
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.data.GameData;
|
||||||
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;
|
||||||
@ -18,16 +19,15 @@ 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.lang.Math;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import static emu.grasscutter.Configuration.*;
|
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 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_CLIMB, // sustained, when not moving no cost no recover
|
MotionState.MOTION_CLIMB, // sustained, when not moving no cost no recover
|
||||||
MotionState.MOTION_STANDBY_TO_CLIMB // NOT OBSERVED, see MOTION_JUMP_UP_WALL_FOR_STANDBY
|
MotionState.MOTION_STANDBY_TO_CLIMB // NOT OBSERVED, see MOTION_JUMP_UP_WALL_FOR_STANDBY
|
||||||
@ -122,74 +122,67 @@ public class StaminaManager {
|
|||||||
private int lastSkillId = 0;
|
private int lastSkillId = 0;
|
||||||
private int lastSkillCasterId = 0;
|
private int lastSkillCasterId = 0;
|
||||||
private boolean lastSkillFirstTick = true;
|
private boolean lastSkillFirstTick = true;
|
||||||
public static final HashSet<Integer> TalentMovements = new HashSet<>(List.of(
|
private static final HashSet<Integer> TalentMovements = new HashSet<>(List.of(
|
||||||
10013, // Kamisato Ayaka
|
10013, // Kamisato Ayaka
|
||||||
10413 // Mona
|
10413 // Mona
|
||||||
));
|
));
|
||||||
|
private static final HashMap<Integer, Float> ClimbFoodReductionMap = new HashMap<>() {{
|
||||||
|
// TODO: get real food id
|
||||||
|
put(0, 0.8f); // Sample food
|
||||||
|
}};
|
||||||
|
private static final HashMap<Integer, Float> DashFoodReductionMap = new HashMap<>() {{
|
||||||
|
// TODO: get real food id
|
||||||
|
put(0, 0.8f); // Sample food
|
||||||
|
}};
|
||||||
|
private static final HashMap<Integer, Float> FlyFoodReductionMap = new HashMap<>() {{
|
||||||
|
// TODO: get real food id
|
||||||
|
put(0, 0.8f); // Sample food
|
||||||
|
}};
|
||||||
|
private static final HashMap<Integer, Float> SwimFoodReductionMap = new HashMap<>() {{
|
||||||
|
// TODO: get real food id
|
||||||
|
put(0, 0.8f); // Sample food
|
||||||
|
}};
|
||||||
|
private static final HashMap<Integer, Float> ClimbTalentReductionMap = new HashMap<>() {{
|
||||||
|
put(262301, 0.8f); // Xiao
|
||||||
|
}};
|
||||||
|
private static final HashMap<Integer, Float> FlyTalentReductionMap = new HashMap<>() {{
|
||||||
|
put(212301, 0.8f); // Amber
|
||||||
|
put(222301, 0.8f); // Venti
|
||||||
|
}};
|
||||||
|
private static final HashMap<Integer, Float> SwimTalentReductionMap = new HashMap<>() {{
|
||||||
|
put(242301, 0.8f); // Beidou
|
||||||
|
put(542301, 0.8f); // Sangonomiya Kokomi
|
||||||
|
}};
|
||||||
|
|
||||||
// TODO: Get from somewhere else, instead of hard-coded here?
|
public static final HashSet<Integer> BowAvatars = new HashSet<>();
|
||||||
public static final HashSet<Integer> ClaymoreSkills = new HashSet<>(List.of(
|
public static final HashSet<Integer> CatalystAvatars = new HashSet<>();
|
||||||
10160, // Diluc, /=2
|
public static final HashSet<Integer> ClaymoreAvatars = new HashSet<>();
|
||||||
10201, // Razor
|
public static final HashSet<Integer> PolearmAvatars = new HashSet<>();
|
||||||
10241, // Beidou
|
public static final HashSet<Integer> SwordAvatars = new HashSet<>();
|
||||||
10341, // Noelle
|
|
||||||
10401, // Chongyun
|
|
||||||
10441, // Xinyan
|
|
||||||
10511, // Eula
|
|
||||||
10531, // Sayu
|
|
||||||
10571 // Arataki Itto, = 0
|
|
||||||
));
|
|
||||||
public static final HashSet<Integer> CatalystSkills = new HashSet<>(List.of(
|
|
||||||
10060, // Lisa
|
|
||||||
10070, // Barbara
|
|
||||||
10271, // Ningguang
|
|
||||||
10291, // Klee
|
|
||||||
10411, // Mona
|
|
||||||
10431, // Sucrose
|
|
||||||
10481, // Yanfei
|
|
||||||
10541, // Sangonomoiya Kokomi
|
|
||||||
10581 // Yae Miko
|
|
||||||
));
|
|
||||||
public static final HashSet<Integer> PolearmSkills = new HashSet<>(List.of(
|
|
||||||
10231, // Xiangling
|
|
||||||
10261, // Xiao
|
|
||||||
10301, // Zhongli
|
|
||||||
10451, // Rosaria
|
|
||||||
10461, // Hu Tao
|
|
||||||
10501, // Thoma
|
|
||||||
10521, // Raiden Shogun
|
|
||||||
10631, // Shenhe
|
|
||||||
10641 // Yunjin
|
|
||||||
));
|
|
||||||
public static final HashSet<Integer> SwordSkills = new HashSet<>(List.of(
|
|
||||||
10024, // Kamisato Ayaka
|
|
||||||
10031, // Jean
|
|
||||||
10073, // Kaeya
|
|
||||||
10321, // Bennett
|
|
||||||
10337, // Tartaglia, melee stance (10332 switch to melee, 10336 switch to ranged stance)
|
|
||||||
10351, // Qiqi
|
|
||||||
10381, // Xingqiu
|
|
||||||
10386, // Albedo
|
|
||||||
10421, // Keqing, =-2500
|
|
||||||
10471, // Kaedehara Kazuha
|
|
||||||
10661, // Kamisato Ayato
|
|
||||||
100553, // Lumine
|
|
||||||
100540 // Aether
|
|
||||||
));
|
|
||||||
public static final HashSet<Integer> BowSkills = new HashSet<>(List.of(
|
|
||||||
10041, 10043, // Amber
|
|
||||||
10221, 10223,// Venti
|
|
||||||
10311, 10315, // Fischl
|
|
||||||
10331, 10335, // Tartaglia, ranged stance
|
|
||||||
10371, // Ganyu
|
|
||||||
10391, 10394, // Diona
|
|
||||||
10491, // Yoimiya
|
|
||||||
10551, 10554, // Gorou
|
|
||||||
10561, 10564, // Kojou Sara
|
|
||||||
10621, // Aloy
|
|
||||||
99998, 99999 // Yelan // TODO: get real values
|
|
||||||
));
|
|
||||||
|
|
||||||
|
public static void initialize() {
|
||||||
|
// Initialize skill categories
|
||||||
|
GameData.getAvatarDataMap().forEach((avatarId, avatarData) -> {
|
||||||
|
switch(avatarData.getWeaponType()) {
|
||||||
|
case "WEAPON_BOW":
|
||||||
|
BowAvatars.add(avatarId);
|
||||||
|
break;
|
||||||
|
case "WEAPON_CLAYMORE":
|
||||||
|
ClaymoreAvatars.add(avatarId);
|
||||||
|
break;
|
||||||
|
case "WEAPON_CATALYST":
|
||||||
|
CatalystAvatars.add(avatarId);
|
||||||
|
break;
|
||||||
|
case "WEAPON_POLE":
|
||||||
|
PolearmAvatars.add(avatarId);
|
||||||
|
break;
|
||||||
|
case "WEAPON_SWORD_ONE_HAND":
|
||||||
|
SwordAvatars.add(avatarId);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// TODO: Initialize foods etc.
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public StaminaManager(Player player) {
|
public StaminaManager(Player player) {
|
||||||
this.player = player;
|
this.player = player;
|
||||||
@ -244,8 +237,8 @@ public class StaminaManager {
|
|||||||
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) {
|
public int updateStaminaRelative(GameSession session, Consumption consumption, PlayerProperty staminaType) {
|
||||||
int currentStamina = player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA);
|
int currentStamina = player.getProperty(staminaType);
|
||||||
if (consumption.amount == 0) {
|
if (consumption.amount == 0) {
|
||||||
return currentStamina;
|
return currentStamina;
|
||||||
}
|
}
|
||||||
@ -253,7 +246,7 @@ public class StaminaManager {
|
|||||||
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);
|
Consumption overriddenConsumption = listener.getValue().onBeforeUpdateStamina(consumption.type.toString(), consumption);
|
||||||
if ((overriddenConsumption.type != consumption.type) && (overriddenConsumption.amount != consumption.amount)) {
|
if ((overriddenConsumption.type != consumption.type) && (overriddenConsumption.amount != consumption.amount)) {
|
||||||
logger.debug("[StaminaManager] 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;
|
||||||
@ -269,16 +262,16 @@ public class StaminaManager {
|
|||||||
} else if (newStamina > playerMaxStamina) {
|
} else if (newStamina > playerMaxStamina) {
|
||||||
newStamina = playerMaxStamina;
|
newStamina = playerMaxStamina;
|
||||||
}
|
}
|
||||||
return setStamina(session, consumption.type.toString(), newStamina);
|
return setStamina(session, consumption.type.toString(), newStamina, staminaType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int updateStaminaAbsolute(GameSession session, String reason, int newStamina) {
|
public int updateStaminaAbsolute(GameSession session, String reason, int newStamina, PlayerProperty staminaType) {
|
||||||
int currentStamina = player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA);
|
int currentStamina = player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA);
|
||||||
// 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);
|
int overriddenNewStamina = listener.getValue().onBeforeUpdateStamina(reason, newStamina);
|
||||||
if (overriddenNewStamina != newStamina) {
|
if (overriddenNewStamina != newStamina) {
|
||||||
logger.debug("[StaminaManager] 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;
|
||||||
@ -290,18 +283,22 @@ public class StaminaManager {
|
|||||||
} else if (newStamina > playerMaxStamina) {
|
} else if (newStamina > playerMaxStamina) {
|
||||||
newStamina = playerMaxStamina;
|
newStamina = playerMaxStamina;
|
||||||
}
|
}
|
||||||
return setStamina(session, reason, newStamina);
|
return setStamina(session, reason, newStamina, staminaType);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns new stamina and sends PlayerPropNotify
|
// Returns new stamina and sends PlayerPropNotify
|
||||||
public int setStamina(GameSession session, String reason, int newStamina) {
|
public int setStamina(GameSession session, String reason, int newStamina, PlayerProperty staminaType) {
|
||||||
if (!GAME_OPTIONS.staminaUsage) {
|
if (!GAME_OPTIONS.staminaUsage) {
|
||||||
newStamina = player.getProperty(PlayerProperty.PROP_MAX_STAMINA);
|
newStamina = player.getProperty(PlayerProperty.PROP_MAX_STAMINA);
|
||||||
}
|
}
|
||||||
|
|
||||||
// set stamina
|
// set stamina
|
||||||
player.setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA, newStamina);
|
player.setProperty(staminaType, newStamina);
|
||||||
session.send(new PacketPlayerPropNotify(player, PlayerProperty.PROP_CUR_PERSIST_STAMINA));
|
if (staminaType == PlayerProperty.PROP_CUR_TEMPORARY_STAMINA) {
|
||||||
|
// TODO: Implement
|
||||||
|
// session.send(new PacketVehicleStaminaNotify(vehicleEntity, newStamina));
|
||||||
|
} else {
|
||||||
|
session.send(new PacketPlayerPropNotify(player, PlayerProperty.PROP_CUR_PERSIST_STAMINA));
|
||||||
|
}
|
||||||
// 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);
|
listener.getValue().onAfterUpdateStamina(reason, newStamina);
|
||||||
@ -343,22 +340,23 @@ public class StaminaManager {
|
|||||||
// 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
|
// 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
|
||||||
if (ClaymoreSkills.contains(skillId)) {
|
int currentAvatarId = player.getTeamManager().getCurrentAvatarEntity().getAvatar().getAvatarId();
|
||||||
|
if (ClaymoreAvatars.contains(currentAvatarId)) {
|
||||||
// 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 (BowSkills.contains(skillId)
|
if (BowAvatars.contains(currentAvatarId)
|
||||||
|| SwordSkills.contains(skillId)
|
|| SwordAvatars.contains(currentAvatarId)
|
||||||
|| PolearmSkills.contains(skillId)
|
|| PolearmAvatars.contains(currentAvatarId)
|
||||||
|| CatalystSkills.contains(skillId)
|
|| CatalystAvatars.contains(currentAvatarId)
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -367,7 +365,7 @@ public class StaminaManager {
|
|||||||
|
|
||||||
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);
|
// 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);
|
||||||
}
|
}
|
||||||
@ -401,22 +399,22 @@ public class StaminaManager {
|
|||||||
switch (motionState) {
|
switch (motionState) {
|
||||||
case MOTION_CLIMB:
|
case MOTION_CLIMB:
|
||||||
if (currentState != MotionState.MOTION_CLIMB) {
|
if (currentState != MotionState.MOTION_CLIMB) {
|
||||||
updateStaminaRelative(session, new Consumption(ConsumptionType.CLIMB_START));
|
updateStaminaRelative(session, new Consumption(ConsumptionType.CLIMB_START), PlayerProperty.PROP_CUR_PERSIST_STAMINA);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MOTION_DASH_BEFORE_SHAKE:
|
case MOTION_DASH_BEFORE_SHAKE:
|
||||||
if (previousState != MotionState.MOTION_DASH_BEFORE_SHAKE) {
|
if (previousState != MotionState.MOTION_DASH_BEFORE_SHAKE) {
|
||||||
updateStaminaRelative(session, new Consumption(ConsumptionType.SPRINT));
|
updateStaminaRelative(session, new Consumption(ConsumptionType.SPRINT), PlayerProperty.PROP_CUR_PERSIST_STAMINA);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MOTION_CLIMB_JUMP:
|
case MOTION_CLIMB_JUMP:
|
||||||
if (previousState != MotionState.MOTION_CLIMB_JUMP) {
|
if (previousState != MotionState.MOTION_CLIMB_JUMP) {
|
||||||
updateStaminaRelative(session, new Consumption(ConsumptionType.CLIMB_JUMP));
|
updateStaminaRelative(session, new Consumption(ConsumptionType.CLIMB_JUMP), PlayerProperty.PROP_CUR_PERSIST_STAMINA);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MOTION_SWIM_DASH:
|
case MOTION_SWIM_DASH:
|
||||||
if (previousState != MotionState.MOTION_SWIM_DASH) {
|
if (previousState != MotionState.MOTION_SWIM_DASH) {
|
||||||
updateStaminaRelative(session, new Consumption(ConsumptionType.SWIM_DASH_START));
|
updateStaminaRelative(session, new Consumption(ConsumptionType.SWIM_DASH_START), PlayerProperty.PROP_CUR_PERSIST_STAMINA);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -424,7 +422,7 @@ public class StaminaManager {
|
|||||||
|
|
||||||
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);
|
updateStaminaRelative(session, consumption, PlayerProperty.PROP_CUR_PERSIST_STAMINA);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SustainedStaminaHandler extends TimerTask {
|
private class SustainedStaminaHandler extends TimerTask {
|
||||||
@ -449,41 +447,45 @@ public class StaminaManager {
|
|||||||
consumption = getSkiffConsumption();
|
consumption = getSkiffConsumption();
|
||||||
} 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("OTHER").contains((currentState))) {
|
} else if (MotionStatesCategorized.get("NOCOST_NORECOVER").contains(currentState)) {
|
||||||
|
consumption = new Consumption();
|
||||||
|
} else if (MotionStatesCategorized.get("OTHER").contains(currentState)) {
|
||||||
consumption = getOtherConsumptions();
|
consumption = getOtherConsumptions();
|
||||||
} else {
|
} else { // ignore
|
||||||
// ignore
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (consumption.amount < 0) {
|
if (consumption.amount < 0) {
|
||||||
/* Do not apply reduction factor when recovering stamina
|
/* Do not apply reduction factor when recovering stamina
|
||||||
TODO: Reductions that apply to all motion types:
|
TODO: Reductions that apply to all motion types:
|
||||||
Elemental Resonance
|
|
||||||
Wind: -15%
|
|
||||||
Skills
|
Skills
|
||||||
Diona E: -10% while shield lasts - applies to SP+MP
|
Diona E: -10% while shield lasts - applies to SP+MP
|
||||||
Barbara E: -12% while lasts - applies to SP+MP
|
Barbara E: -12% while lasts - applies to SP+MP
|
||||||
*/
|
*/
|
||||||
|
// Elemental Resonance - Winds -15%
|
||||||
|
if (player.getTeamManager().getTeamResonances().contains(10301)) {
|
||||||
|
consumption.amount *= 0.85f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Delay 2 seconds before starts recovering stamina
|
// Delay 1 seconds before starts recovering stamina
|
||||||
if (cachedSession != null) {
|
if (consumption.amount != 0 && cachedSession != null) {
|
||||||
if (consumption.amount < 0) {
|
if (consumption.amount < 0) {
|
||||||
staminaRecoverDelay = 0;
|
staminaRecoverDelay = 0;
|
||||||
}
|
}
|
||||||
if (consumption.amount > 0 && consumption.type != ConsumptionType.POWERED_FLY) {
|
if (consumption.amount > 0 && consumption.type != ConsumptionType.POWERED_FLY) {
|
||||||
// For POWERED_FLY recover immediately - things like Amber's gliding exam may require this.
|
// For POWERED_FLY recover immediately - things like Amber's gliding exam may require this.
|
||||||
if (staminaRecoverDelay < 10) {
|
if (staminaRecoverDelay < 5) {
|
||||||
// For others recover after 2 seconds (10 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("[StaminaManager] Delaying recovery: " + staminaRecoverDelay);
|
logger.trace("Delaying recovery: " + staminaRecoverDelay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateStaminaRelative(cachedSession, consumption);
|
updateStaminaRelative(cachedSession, consumption, PlayerProperty.PROP_CUR_PERSIST_STAMINA);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
previousState = currentState;
|
previousState = currentState;
|
||||||
@ -496,6 +498,7 @@ public class StaminaManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void handleDrowning() {
|
private void handleDrowning() {
|
||||||
|
// TODO: fix drowning waverider entity
|
||||||
int stamina = player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA);
|
int stamina = player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA);
|
||||||
if (stamina < 10) {
|
if (stamina < 10) {
|
||||||
logger.trace(player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA) + "/" +
|
logger.trace(player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA) + "/" +
|
||||||
@ -517,23 +520,24 @@ public class StaminaManager {
|
|||||||
return getTalentMovingSustainedCost(skillCasting);
|
return getTalentMovingSustainedCost(skillCasting);
|
||||||
}
|
}
|
||||||
// Bow avatar charged attack
|
// Bow avatar charged attack
|
||||||
if (BowSkills.contains(skillCasting)) {
|
int currentAvatarId = player.getTeamManager().getCurrentAvatarEntity().getAvatar().getAvatarId();
|
||||||
|
if (BowAvatars.contains(currentAvatarId)) {
|
||||||
return getBowSustainedCost(skillCasting);
|
return getBowSustainedCost(skillCasting);
|
||||||
}
|
}
|
||||||
// Claymore avatar charged attack
|
// Claymore avatar charged attack
|
||||||
if (ClaymoreSkills.contains(skillCasting)) {
|
if (ClaymoreAvatars.contains(currentAvatarId)) {
|
||||||
return getClaymoreSustainedCost(skillCasting);
|
return getClaymoreSustainedCost(skillCasting);
|
||||||
}
|
}
|
||||||
// Catalyst avatar charged attack
|
// Catalyst avatar charged attack
|
||||||
if (CatalystSkills.contains(skillCasting)) {
|
if (CatalystAvatars.contains(currentAvatarId)) {
|
||||||
return getCatalystSustainedCost(skillCasting);
|
return getCatalystSustainedCost(skillCasting);
|
||||||
}
|
}
|
||||||
// Polearm avatar charged attack
|
// Polearm avatar charged attack
|
||||||
if (PolearmSkills.contains(skillCasting)) {
|
if (PolearmAvatars.contains(currentAvatarId)) {
|
||||||
return getPolearmSustainedCost(skillCasting);
|
return getPolearmSustainedCost(skillCasting);
|
||||||
}
|
}
|
||||||
// Sword avatar charged attack
|
// Sword avatar charged attack
|
||||||
if (SwordSkills.contains(skillCasting)) {
|
if (SwordAvatars.contains(skillCasting)) {
|
||||||
return getSwordSustainedCost(skillCasting);
|
return getSwordSustainedCost(skillCasting);
|
||||||
}
|
}
|
||||||
return new Consumption();
|
return new Consumption();
|
||||||
@ -546,18 +550,8 @@ public class StaminaManager {
|
|||||||
consumption.amount = ConsumptionType.CLIMBING.amount;
|
consumption.amount = ConsumptionType.CLIMBING.amount;
|
||||||
}
|
}
|
||||||
// Climbing specific reductions
|
// Climbing specific reductions
|
||||||
// TODO: create a food cost reduction map
|
consumption.amount *= getFoodCostReductionFactor(ClimbFoodReductionMap);
|
||||||
HashMap<Integer, Float> foodReductionMap = new HashMap<>() {{
|
consumption.amount *= getTalentCostReductionFactor(ClimbTalentReductionMap);
|
||||||
// TODO: get real talent id
|
|
||||||
put(0, 0.8f); // Sample food
|
|
||||||
}};
|
|
||||||
consumption.amount *= getFoodCostReductionFactor(foodReductionMap);
|
|
||||||
|
|
||||||
HashMap<Integer, Float> talentReductionMap = new HashMap<>() {{
|
|
||||||
// TODO: get real talent id
|
|
||||||
put(0, 0.8f); // Xiao
|
|
||||||
}};
|
|
||||||
consumption.amount *= getTalentCostReductionFactor(talentReductionMap);
|
|
||||||
return consumption;
|
return consumption;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -572,13 +566,9 @@ public class StaminaManager {
|
|||||||
consumption.type = ConsumptionType.SWIM_DASH;
|
consumption.type = ConsumptionType.SWIM_DASH;
|
||||||
consumption.amount = ConsumptionType.SWIM_DASH.amount;
|
consumption.amount = ConsumptionType.SWIM_DASH.amount;
|
||||||
}
|
}
|
||||||
// Reductions
|
// Swimming specific reductions
|
||||||
HashMap<Integer, Float> talentReductionMap = new HashMap<>() {{
|
consumption.amount *= getFoodCostReductionFactor(SwimFoodReductionMap);
|
||||||
// TODO: get real talent id
|
consumption.amount *= getTalentCostReductionFactor(SwimTalentReductionMap);
|
||||||
put(0, 0.8f); // Beidou
|
|
||||||
put(1, 0.8f); // Sangonomiya Kokomi
|
|
||||||
}};
|
|
||||||
consumption.amount *= getTalentCostReductionFactor(talentReductionMap);
|
|
||||||
return consumption;
|
return consumption;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -587,8 +577,8 @@ public class StaminaManager {
|
|||||||
if (currentState == MotionState.MOTION_DASH) {
|
if (currentState == MotionState.MOTION_DASH) {
|
||||||
consumption.type = ConsumptionType.DASH;
|
consumption.type = ConsumptionType.DASH;
|
||||||
consumption.amount = ConsumptionType.DASH.amount;
|
consumption.amount = ConsumptionType.DASH.amount;
|
||||||
// TODO: Dashing specific reductions
|
// Dashing specific reductions
|
||||||
// Foods:
|
consumption.amount *= getFoodCostReductionFactor(DashFoodReductionMap);
|
||||||
}
|
}
|
||||||
return consumption;
|
return consumption;
|
||||||
}
|
}
|
||||||
@ -599,13 +589,9 @@ public class StaminaManager {
|
|||||||
return new Consumption(ConsumptionType.POWERED_FLY);
|
return new Consumption(ConsumptionType.POWERED_FLY);
|
||||||
}
|
}
|
||||||
Consumption consumption = new Consumption(ConsumptionType.FLY);
|
Consumption consumption = new Consumption(ConsumptionType.FLY);
|
||||||
// Passive Talents
|
// Flying specific reductions
|
||||||
HashMap<Integer, Float> talentReductionMap = new HashMap<>() {{
|
consumption.amount *= getFoodCostReductionFactor(FlyFoodReductionMap);
|
||||||
put(212301, 0.8f); // Amber
|
consumption.amount *= getTalentCostReductionFactor(FlyTalentReductionMap);
|
||||||
put(222301, 0.8f); // Venti
|
|
||||||
}};
|
|
||||||
consumption.amount *= getTalentCostReductionFactor(talentReductionMap);
|
|
||||||
// TODO: Foods
|
|
||||||
return consumption;
|
return consumption;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -619,12 +605,17 @@ public class StaminaManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Consumption getOtherConsumptions() {
|
private Consumption getOtherConsumptions() {
|
||||||
if (currentState == MotionState.MOTION_NOTIFY) {
|
switch (currentState) {
|
||||||
if (BowSkills.contains(lastSkillId)) {
|
case MOTION_NOTIFY:
|
||||||
|
// if (BowSkills.contains(lastSkillId)) {
|
||||||
|
// return new Consumption(ConsumptionType.FIGHT, 500);
|
||||||
|
// }
|
||||||
|
break;
|
||||||
|
case MOTION_FIGHT:
|
||||||
|
// TODO: what if charged attack
|
||||||
return new Consumption(ConsumptionType.FIGHT, 500);
|
return new Consumption(ConsumptionType.FIGHT, 500);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// TODO: Add other logic
|
|
||||||
return new Consumption();
|
return new Consumption();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -685,11 +676,13 @@ public class StaminaManager {
|
|||||||
// Character specific handling
|
// Character specific handling
|
||||||
switch (skillId) {
|
switch (skillId) {
|
||||||
case 10571: // Arataki Itto, does not consume stamina at all.
|
case 10571: // Arataki Itto, does not consume stamina at all.
|
||||||
|
case 10532: // Sayu, windwheel does not consume stamina.
|
||||||
consumption.amount = 0;
|
consumption.amount = 0;
|
||||||
break;
|
break;
|
||||||
case 10160: // Diluc, with talent "Relentless" stamina cost is decreased by 50%
|
case 10160: // Diluc, with talent "Relentless" stamina cost is decreased by 50%
|
||||||
// TODO: How to get talent status?
|
if (player.getTeamManager().getCurrentAvatarEntity().getAvatar().getProudSkillList().contains(162101)) {
|
||||||
consumption.amount /= 2;
|
consumption.amount /= 2;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return consumption;
|
return consumption;
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.entity.GameEntity;
|
||||||
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
|
import emu.grasscutter.net.proto.VehicleStaminaNotifyOuterClass.VehicleStaminaNotify;
|
||||||
|
|
||||||
|
public class PacketVehicleStaminaNotify extends BasePacket {
|
||||||
|
|
||||||
|
public PacketVehicleStaminaNotify(GameEntity entity, int newStamina) {
|
||||||
|
super(PacketOpcodes.VehicleStaminaNotify);
|
||||||
|
VehicleStaminaNotify.Builder proto = VehicleStaminaNotify.newBuilder();
|
||||||
|
|
||||||
|
proto.setEntityId(entity.getId());
|
||||||
|
proto.setCurStamina(newStamina);
|
||||||
|
|
||||||
|
this.setData(proto.build());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user