mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-01-09 04:13:00 +08:00
Only deduct energy when elemental burst actually fires (#2424)
This commit is contained in:
parent
d461ee2eb3
commit
d224178a64
@ -327,6 +327,7 @@ public class AbilityModifier implements Serializable {
|
|||||||
public String srcKey, dstKey;
|
public String srcKey, dstKey;
|
||||||
|
|
||||||
public int skillID;
|
public int skillID;
|
||||||
|
public int resistanceListID;
|
||||||
|
|
||||||
public AbilityModifierAction[] actions;
|
public AbilityModifierAction[] actions;
|
||||||
public AbilityModifierAction[] successActions;
|
public AbilityModifierAction[] successActions;
|
||||||
|
@ -2,6 +2,7 @@ package emu.grasscutter.game.ability;
|
|||||||
|
|
||||||
import emu.grasscutter.data.GameData;
|
import emu.grasscutter.data.GameData;
|
||||||
import emu.grasscutter.data.binout.AbilityData;
|
import emu.grasscutter.data.binout.AbilityData;
|
||||||
|
import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction;
|
||||||
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.net.proto.AbilityStringOuterClass.AbilityString;
|
import emu.grasscutter.net.proto.AbilityStringOuterClass.AbilityString;
|
||||||
@ -24,6 +25,7 @@ public class Ability {
|
|||||||
private static Map<String, Object2FloatMap<String>> abilitySpecialsModified = new HashMap<>();
|
private static Map<String, Object2FloatMap<String>> abilitySpecialsModified = new HashMap<>();
|
||||||
|
|
||||||
@Getter private int hash;
|
@Getter private int hash;
|
||||||
|
@Getter private Set<Integer> avatarSkillStartIds;
|
||||||
|
|
||||||
public Ability(AbilityData data, GameEntity owner, Player playerOwner) {
|
public Ability(AbilityData data, GameEntity owner, Player playerOwner) {
|
||||||
this.data = data;
|
this.data = data;
|
||||||
@ -44,6 +46,30 @@ public class Ability {
|
|||||||
hash = Utils.abilityHash(data.abilityName);
|
hash = Utils.abilityHash(data.abilityName);
|
||||||
|
|
||||||
data.initialize();
|
data.initialize();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Collect skill IDs referenced by AvatarSkillStart modifier actions
|
||||||
|
// in onAbilityStart and in every modifier's onAdded action set.
|
||||||
|
// These skill IDs will be used by AbilityManager to determine whether
|
||||||
|
// an elemental burst has fired correctly.
|
||||||
|
//
|
||||||
|
avatarSkillStartIds = new HashSet<>();
|
||||||
|
if (data.onAbilityStart != null) {
|
||||||
|
avatarSkillStartIds.addAll(Arrays.stream(data.onAbilityStart)
|
||||||
|
.filter(action ->
|
||||||
|
action.type == AbilityModifierAction.Type.AvatarSkillStart)
|
||||||
|
.map(action -> action.skillID)
|
||||||
|
.toList());
|
||||||
|
}
|
||||||
|
avatarSkillStartIds.addAll(data.modifiers.values()
|
||||||
|
.stream()
|
||||||
|
.map(m -> (List<AbilityModifierAction>)(m.onAdded == null ?
|
||||||
|
Collections.emptyList() :
|
||||||
|
Arrays.asList(m.onAdded)))
|
||||||
|
.flatMap(List::stream)
|
||||||
|
.filter(action -> action.type == AbilityModifierAction.Type.AvatarSkillStart)
|
||||||
|
.map(action -> action.skillID)
|
||||||
|
.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getAbilityName(AbilityString abString) {
|
public static String getAbilityName(AbilityString abString) {
|
||||||
|
@ -21,8 +21,9 @@ import emu.grasscutter.net.proto.AbilityScalarValueEntryOuterClass.AbilityScalar
|
|||||||
import emu.grasscutter.net.proto.ModifierActionOuterClass.ModifierAction;
|
import emu.grasscutter.net.proto.ModifierActionOuterClass.ModifierAction;
|
||||||
import emu.grasscutter.server.event.player.PlayerUseSkillEvent;
|
import emu.grasscutter.server.event.player.PlayerUseSkillEvent;
|
||||||
import io.netty.util.concurrent.FastThreadLocalThread;
|
import io.netty.util.concurrent.FastThreadLocalThread;
|
||||||
import java.util.HashMap;
|
import java.util.*;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
public final class AbilityManager extends BasePlayerManager {
|
public final class AbilityManager extends BasePlayerManager {
|
||||||
@ -48,9 +49,56 @@ public final class AbilityManager extends BasePlayerManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Getter private boolean abilityInvulnerable = false;
|
@Getter private boolean abilityInvulnerable = false;
|
||||||
|
@Getter private Consumer<Integer> clearBurstEnergy;
|
||||||
|
private int burstCasterId;
|
||||||
|
private int burstSkillId;
|
||||||
|
|
||||||
public AbilityManager(Player player) {
|
public AbilityManager(Player player) {
|
||||||
super(player);
|
super(player);
|
||||||
|
removePendingEnergyClear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removePendingEnergyClear() {
|
||||||
|
this.clearBurstEnergy = null;
|
||||||
|
this.burstCasterId = 0;
|
||||||
|
this.burstSkillId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onPossibleElementalBurst(Ability ability, AbilityModifier modifier, int entityId) {
|
||||||
|
//
|
||||||
|
// Possibly clear avatar energy spent on elemental burst
|
||||||
|
// and set invulnerability.
|
||||||
|
//
|
||||||
|
// Problem: Burst can misfire occasionally, like hitting Q when
|
||||||
|
// dashing, doing E, or switching avatars. The client would
|
||||||
|
// still send EvtDoSkillSuccNotify, but the burst may not
|
||||||
|
// actually happen. We don't know when to clear avatar energy.
|
||||||
|
//
|
||||||
|
// When burst does happen, a number of AbilityInvokeEntry will
|
||||||
|
// come in. Use the Ability it references and search for any
|
||||||
|
// modifier with type=AvatarSkillStart, skillID=burst skill ID.
|
||||||
|
//
|
||||||
|
// If that is missing, search for modifier action that sets
|
||||||
|
// invulnerability as a fallback.
|
||||||
|
//
|
||||||
|
if (this.burstCasterId == 0) return;
|
||||||
|
|
||||||
|
boolean skillInvincibility = modifier.state == AbilityModifier.State.Invincible;
|
||||||
|
if (modifier.onAdded != null) {
|
||||||
|
skillInvincibility |= Arrays.stream(modifier.onAdded)
|
||||||
|
.filter(action ->
|
||||||
|
action.type == AbilityModifierAction.Type.AttachAbilityStateResistance &&
|
||||||
|
action.resistanceListID == 11002)
|
||||||
|
.toList().size() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.clearBurstEnergy != null && this.burstCasterId == entityId &&
|
||||||
|
(ability.getAvatarSkillStartIds().contains(this.burstSkillId) || skillInvincibility)) {
|
||||||
|
Grasscutter.getLogger().trace("Caster ID's {} burst successful, clearing energy and setting invulnerability", entityId);
|
||||||
|
this.abilityInvulnerable = true;
|
||||||
|
this.clearBurstEnergy.accept(entityId);
|
||||||
|
this.removePendingEnergyClear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void registerHandlers() {
|
public static void registerHandlers() {
|
||||||
@ -280,8 +328,12 @@ public final class AbilityManager extends BasePlayerManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the player as invulnerable.
|
// Track this elemental burst to possibly clear avatar energy later.
|
||||||
this.abilityInvulnerable = true;
|
this.clearBurstEnergy = (ignored) ->
|
||||||
|
player.getEnergyManager().handleEvtDoSkillSuccNotify(
|
||||||
|
player.getSession(), skillId, casterId);
|
||||||
|
this.burstCasterId = casterId;
|
||||||
|
this.burstSkillId = skillId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -454,6 +506,8 @@ public final class AbilityManager extends BasePlayerManager {
|
|||||||
modifierData);
|
modifierData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onPossibleElementalBurst(instancedAbility, modifierData, invoke.getEntityId());
|
||||||
|
|
||||||
AbilityModifierController modifier =
|
AbilityModifierController modifier =
|
||||||
new AbilityModifierController(instancedAbility, instancedAbilityData, modifierData);
|
new AbilityModifierController(instancedAbility, instancedAbilityData, modifierData);
|
||||||
|
|
||||||
|
@ -259,8 +259,14 @@ public class EnergyManager extends BasePlayerManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Also reference AvatarSkillData in case the burst gets a different skill ID
|
||||||
|
// when the avatar is in a different state. For example, Wanderer's burst is
|
||||||
|
// 10755 usually but when he floats, it becomes 10753.
|
||||||
|
var skillData = GameData.getAvatarSkillDataMap().get(skillId);
|
||||||
|
|
||||||
// If the cast skill was a burst, consume energy.
|
// If the cast skill was a burst, consume energy.
|
||||||
if (avatar.getSkillDepot() != null && skillId == avatar.getSkillDepot().getEnergySkill()) {
|
if ((avatar.getSkillDepot() != null && skillId == avatar.getSkillDepot().getEnergySkill()) ||
|
||||||
|
(skillData != null && skillData.getCostElemVal() > 0)) {
|
||||||
avatar.getAsEntity().clearEnergy(ChangeEnergyReason.CHANGE_ENERGY_REASON_SKILL_START);
|
avatar.getAsEntity().clearEnergy(ChangeEnergyReason.CHANGE_ENERGY_REASON_SKILL_START);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,6 @@ public class HandlerEvtDoSkillSuccNotify extends PacketHandler {
|
|||||||
|
|
||||||
// Handle skill notify in other managers.
|
// Handle skill notify in other managers.
|
||||||
player.getStaminaManager().handleEvtDoSkillSuccNotify(session, skillId, casterId);
|
player.getStaminaManager().handleEvtDoSkillSuccNotify(session, skillId, casterId);
|
||||||
player.getEnergyManager().handleEvtDoSkillSuccNotify(session, skillId, casterId);
|
|
||||||
player.getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_SKILL, skillId);
|
player.getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_SKILL, skillId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user