From 458aadc2ff94ae3b59bbbe68e0deefdb94c2a306 Mon Sep 17 00:00:00 2001 From: saucebing Date: Tue, 14 Jun 2022 19:36:53 +0800 Subject: [PATCH] fix healing ability of some characters to some extent (#1201) * fix healing ability of some characters to some extent * using SerialName to replace replaceAll in avatar ability files reading * add class HealAbilityManager * move codes in onAbilityInvoke of class AbilityManager to class HealAbilityManager --- .../data/binout/AbilityModifier.java | 10 +- .../game/ability/AbilityManager.java | 25 +-- .../game/ability/HealAbilityManager.java | 185 ++++++++++++++++++ 3 files changed, 204 insertions(+), 16 deletions(-) create mode 100644 src/main/java/emu/grasscutter/game/ability/HealAbilityManager.java diff --git a/src/main/java/emu/grasscutter/data/binout/AbilityModifier.java b/src/main/java/emu/grasscutter/data/binout/AbilityModifier.java index 38e82db01..147445b0a 100644 --- a/src/main/java/emu/grasscutter/data/binout/AbilityModifier.java +++ b/src/main/java/emu/grasscutter/data/binout/AbilityModifier.java @@ -1,9 +1,16 @@ package emu.grasscutter.data.binout; import java.util.Map; +import java.io.Serializable; -public class AbilityModifier { +import com.google.gson.annotations.SerializedName; + +public class AbilityModifier implements Serializable { + private static final long serialVersionUID = -2001232313615923575L; + + @SerializedName(value="onAdded", alternate={"KCICDEJLIJD"}) public AbilityModifierAction[] onAdded; + @SerializedName(value="onThinkInterval", alternate={"PBDDACFFPOE"}) public AbilityModifierAction[] onThinkInterval; public AbilityModifierAction[] onRemoved; @@ -13,6 +20,7 @@ public class AbilityModifier { public static class AbilityData { public String abilityName; + @SerializedName(value="modifiers", alternate={"HNEIEGHMLKH"}) public Map modifiers; } diff --git a/src/main/java/emu/grasscutter/game/ability/AbilityManager.java b/src/main/java/emu/grasscutter/game/ability/AbilityManager.java index 078ef1b71..0eae4338d 100644 --- a/src/main/java/emu/grasscutter/game/ability/AbilityManager.java +++ b/src/main/java/emu/grasscutter/game/ability/AbilityManager.java @@ -1,6 +1,8 @@ package emu.grasscutter.game.ability; +import java.util.*; import java.util.Optional; +import java.util.Map.Entry; import com.google.protobuf.InvalidProtocolBufferException; @@ -28,12 +30,15 @@ import emu.grasscutter.net.proto.AbilityScalarValueEntryOuterClass.AbilityScalar import emu.grasscutter.net.proto.ModifierActionOuterClass.ModifierAction; import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Utils; +import emu.grasscutter.game.props.FightProperty; public class AbilityManager { private Player player; + HealAbilityManager healAbilityManager; public AbilityManager(Player player) { this.player = player; + this.healAbilityManager = new HealAbilityManager(player); } public Player getPlayer() { @@ -41,7 +46,9 @@ public class AbilityManager { } public void onAbilityInvoke(AbilityInvokeEntry invoke) throws Exception { - // Grasscutter.getLogger().info(invoke.getArgumentType() + " (" + invoke.getArgumentTypeValue() + "): " + Utils.bytesToHex(invoke.toByteArray())); + healAbilityManager.healHandler(invoke); + + //Grasscutter.getLogger().info(invoke.getArgumentType() + " (" + invoke.getArgumentTypeValue() + "): " + Utils.bytesToHex(invoke.toByteArray())); switch (invoke.getArgumentType()) { case ABILITY_INVOKE_ARGUMENT_META_OVERRIDE_PARAM: handleOverrideParam(invoke); @@ -61,6 +68,7 @@ public class AbilityManager { default: break; } + } private void handleOverrideParam(AbilityInvokeEntry invoke) throws Exception { @@ -109,7 +117,7 @@ public class AbilityManager { if (sourceEntity == null) { return; } - + // This is not how it works but we will keep it for now since healing abilities dont work properly anyways if (data.getAction() == ModifierAction.ADDED && data.getParentAbilityName() != null) { // Handle add modifier here @@ -155,19 +163,6 @@ public class AbilityManager { private void invokeAction(AbilityModifierAction action, GameEntity target, GameEntity sourceEntity) { switch (action.type) { case HealHP -> { - if (action.amount == null) { - return; - } - - float healAmount = 0; - - if (action.amount.isDynamic && action.amount.dynamicKey != null) { - healAmount = sourceEntity.getMetaOverrideMap().getOrDefault(action.amount.dynamicKey, 0f); - } - - if (healAmount > 0) { - target.heal(healAmount); - } } case LoseHP -> { if (action.amountByTargetCurrentHPRatio == null) { diff --git a/src/main/java/emu/grasscutter/game/ability/HealAbilityManager.java b/src/main/java/emu/grasscutter/game/ability/HealAbilityManager.java new file mode 100644 index 000000000..24513531c --- /dev/null +++ b/src/main/java/emu/grasscutter/game/ability/HealAbilityManager.java @@ -0,0 +1,185 @@ +package emu.grasscutter.game.ability; + +import java.util.*; +import java.util.Optional; +import java.util.Map.Entry; + +import com.google.protobuf.InvalidProtocolBufferException; + +import emu.grasscutter.Grasscutter; + +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.binout.AbilityModifierEntry; +import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction; +import emu.grasscutter.data.excels.AvatarSkillDepotData; +import emu.grasscutter.data.excels.ItemData; +import emu.grasscutter.game.avatar.Avatar; +import emu.grasscutter.game.entity.EntityAvatar; +import emu.grasscutter.game.entity.EntityClientGadget; +import emu.grasscutter.game.entity.EntityItem; +import emu.grasscutter.game.entity.GameEntity; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.props.ElementType; +import emu.grasscutter.net.proto.AbilityActionGenerateElemBallOuterClass.AbilityActionGenerateElemBall; +import emu.grasscutter.net.proto.AbilityInvokeEntryHeadOuterClass.AbilityInvokeEntryHead; +import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry; +import emu.grasscutter.net.proto.AbilityMetaModifierChangeOuterClass.AbilityMetaModifierChange; +import emu.grasscutter.net.proto.AbilityMetaReInitOverrideMapOuterClass.AbilityMetaReInitOverrideMap; +import emu.grasscutter.net.proto.AbilityMixinCostStaminaOuterClass.AbilityMixinCostStamina; +import emu.grasscutter.net.proto.AbilityScalarValueEntryOuterClass.AbilityScalarValueEntry; +import emu.grasscutter.net.proto.ModifierActionOuterClass.ModifierAction; +import emu.grasscutter.utils.Position; +import emu.grasscutter.utils.Utils; +import emu.grasscutter.game.props.FightProperty; + +public class HealAbilityManager { + private class HealData { + public boolean isString = true; + public String abilityType = ""; //"E" or "Q" + public String sRatio = ""; + public String sBase = ""; + public float fRatio = 0; + public float fBase = 0; + public boolean healAll = false; + + public HealData(String _abilityType, String _sRatio, String _sBase, boolean _healAll) { + abilityType = _abilityType; + isString = true; + sRatio = _sRatio; + sBase = _sBase; + healAll = _healAll; + } + + public HealData(String _abilityType, String _sRatio, float _fRatio, float _fBase, boolean _healAll) { + abilityType = _abilityType; + isString = false; + sRatio = _sRatio; + fRatio = _fRatio; + fBase = _fBase; + healAll = _healAll; + } + } + + private class HealDataAvatar { + public static int MAX_NUM_HEAL_ABILITY = 4; + public String avatar = ""; + public int fightPropertyType= 0; //0: maxHP, 1: curAttack, 2: curDefense + public ArrayList healDataList; + + public HealDataAvatar(String _avatar, int _fightPropertyType) { + avatar = _avatar; + fightPropertyType = _fightPropertyType; + healDataList = new ArrayList(); + } + + public HealDataAvatar addHealData(String abilityType, String sRatio, String sBase, boolean healAll) { + HealData healData = new HealData(abilityType, sRatio, sBase, healAll); + healDataList.add(healData); + return this; + } + + public HealDataAvatar addHealData(String abilityType, String sRatio, float fRatio, float fBase, boolean healAll) { + HealData healData = new HealData(abilityType, sRatio, fRatio, fBase, healAll); + healDataList.add(healData); + return this; + } + } + + ArrayList healDataAvatarList; + private Player player; + + public HealAbilityManager (Player player) { + this.player = player; + healDataAvatarList = new ArrayList(); + healDataAvatarList.add(new HealDataAvatar("Kokomi", 0).addHealData("E", "ElementalArt_Heal_MaxHP_Base_Percentage", "ElementalArt_Heal_Base_Amount", false)); + healDataAvatarList.add(new HealDataAvatar("Qin", 1).addHealData("Q", "Heal", "BurstHealConst", true)); + healDataAvatarList.add(new HealDataAvatar("Noel", 2).addHealData("E", "OnAttack_HealthRate", 0.452f, 282f, true)); + healDataAvatarList.add(new HealDataAvatar("Bennett", 0).addHealData("Q", "HealMaxHpRatio", "HealConst", false)); + healDataAvatarList.add(new HealDataAvatar("Diona", 0).addHealData("Q", "HealHPRatio", "HealHP_Const", false)); + healDataAvatarList.add(new HealDataAvatar("Sayu", 1).addHealData("Q", "Constellation_6_Damage", "Heal_BaseAmount", true).addHealData("Q", "Heal_AttackRatio", "Constellation_6_Heal", true)); + healDataAvatarList.add(new HealDataAvatar("Barbara", 0).addHealData("E", "HealHPOnAdded", "HealHPOnAdded_Const", true).addHealData("E", "HealHP_OnHittingOthers", "HealHP_Const_OnHittingOthers", true)); + healDataAvatarList.add(new HealDataAvatar("Shinobu", 0).addHealData("E", "ElementalArt_Heal_MaxHP_Percentage", 0.064f, 795f, false)); + healDataAvatarList.add(new HealDataAvatar("Qiqi", 1).addHealData("E", "HealHP_OnHittingOthers", "HealHP_Const_OnHittingOthers", true).addHealData("E", "ElementalArt_HealHp_Ratio", "ElementalArt_HealHp_Const", true)); + } + + public Player getPlayer() { + return this.player; + } + + public void healHandler(AbilityInvokeEntry invoke) throws Exception { + AbilityMetaModifierChange data = AbilityMetaModifierChange.parseFrom(invoke.getAbilityData()); + + if (data == null) { + return; + } + + GameEntity sourceEntity = player.getScene().getEntityById(data.getApplyEntityId()); + + String modifierString = ""; + if(data.getParentAbilityName() != null) + modifierString = data.getParentAbilityName().getStr(); + + if(sourceEntity != null) + checkAndHeal(sourceEntity, modifierString); + } + + public void checkAndHeal(GameEntity sourceEntity, String modifierString) { + int fightPropertyType = 0; + float healAmount = 0; + float ratio = 0, base = 0; + float maxHP, curAttack, curDefense; + Map map = sourceEntity.getMetaOverrideMap(); + for(int i = 0 ; i < healDataAvatarList.size() ; i ++) { + HealDataAvatar healDataAvatar = healDataAvatarList.get(i); + if(modifierString.contains(healDataAvatar.avatar)) { + fightPropertyType = healDataAvatar.fightPropertyType; + ArrayList healDataList = healDataAvatar.healDataList; + + for(int j = 0 ; j < healDataList.size(); j++) { + HealData healData = healDataList.get(j); + if(map.containsKey(healData.sRatio)) { + if(healData.isString) { + ratio = map.get(healData.sRatio); + base = map.get(healData.sBase); + } + else { + ratio = healData.fRatio; + base = healData.fBase; + } + } + + List activeTeam = player.getTeamManager().getActiveTeam(); + List needHealAvatars = new ArrayList(); + int currentIndex = player.getTeamManager().getCurrentCharacterIndex(); + EntityAvatar currentAvatar = activeTeam.get(currentIndex); + if(healData.healAll) { + needHealAvatars = activeTeam; + } + else { + needHealAvatars.add(currentAvatar); + } + + maxHP = currentAvatar.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP); + curAttack = currentAvatar.getFightProperty(FightProperty.FIGHT_PROP_CUR_ATTACK); + curDefense = currentAvatar.getFightProperty(FightProperty.FIGHT_PROP_CUR_DEFENSE); + switch(fightPropertyType) { + case 0: + healAmount = ratio * maxHP + base; + break; + case 1: + healAmount = ratio * curAttack + base; + break; + case 2: + healAmount = ratio * curDefense + base; + break; + } + for(int k = 0 ; k < needHealAvatars.size() ; k ++) { + EntityAvatar avatar = needHealAvatars.get(k); + avatar.heal(healAmount); + } + } + break; + } + } + } +}