diff --git a/src/GameServer/Data/BinOut/Ability/Temp/AbilityModifier.cs b/src/GameServer/Data/BinOut/Ability/Temp/AbilityModifier.cs index d477c876..8ce1ca4d 100644 --- a/src/GameServer/Data/BinOut/Ability/Temp/AbilityModifier.cs +++ b/src/GameServer/Data/BinOut/Ability/Temp/AbilityModifier.cs @@ -16,8 +16,72 @@ namespace Weedwacker.GameServer.Data.BinOut.Ability.Temp [JsonProperty] public readonly BaseAbilityMixin[]? modifierMixins; [JsonProperty] public readonly BaseAction[]? onAdded; [JsonProperty] public readonly BaseAction[]? onRemoved; + [JsonProperty] public readonly BaseAction[]? onBeingHit; + [JsonProperty] public readonly BaseAction[]? onAttackLanded; + [JsonProperty] public readonly BaseAction[]? onHittingOther; [JsonProperty] public readonly BaseAction[]? onThinkInterval; [JsonProperty] public readonly BaseAction[]? onKill; + [JsonProperty] public readonly BaseAction[]? onCrash; + [JsonProperty] public readonly BaseAction[]? onAvatarIn; [JsonProperty] public readonly BaseAction[]? onAvatarOut; + [JsonProperty] public readonly BaseAction[]? onReconnect; + [JsonProperty] public readonly BaseAction[]? onChangeAuthority; + [JsonProperty] public readonly BaseAction[]? onVehicleIn; + [JsonProperty] public readonly BaseAction[]? onVehicleOut; + [JsonProperty] public readonly BaseAction[]? onZoneEnter; + [JsonProperty] public readonly BaseAction[]? onZoneExit; + [JsonProperty] public readonly BaseAction[]? onHeal; + [JsonProperty] public readonly BaseAction[]? onBeingHealed; + + internal async Task Initialize(Dictionary localIdToInvocationMap) + { + // DO NOT CHANGE THE ORDER + LocalIdGenerator idGenerator = new(ConfigAbilitySubContainerType.MODIFIER_ACTION); + idGenerator.InitializeActionLocalIds(onAdded, localIdToInvocationMap); + idGenerator.ConfigIndex++; + idGenerator.InitializeActionLocalIds(onRemoved, localIdToInvocationMap); + idGenerator.ConfigIndex++; + idGenerator.InitializeActionLocalIds(onBeingHit, localIdToInvocationMap); + idGenerator.ConfigIndex++; + idGenerator.InitializeActionLocalIds(onAttackLanded, localIdToInvocationMap); + idGenerator.ConfigIndex++; + idGenerator.InitializeActionLocalIds(onHittingOther, localIdToInvocationMap); + idGenerator.ConfigIndex++; + idGenerator.InitializeActionLocalIds(onThinkInterval, localIdToInvocationMap); + idGenerator.ConfigIndex++; + idGenerator.InitializeActionLocalIds(onKill, localIdToInvocationMap); + idGenerator.ConfigIndex++; + idGenerator.InitializeActionLocalIds(onCrash, localIdToInvocationMap); + idGenerator.ConfigIndex++; + idGenerator.InitializeActionLocalIds(onAvatarIn, localIdToInvocationMap); + idGenerator.ConfigIndex++; + idGenerator.InitializeActionLocalIds(onAvatarOut, localIdToInvocationMap); + idGenerator.ConfigIndex++; + idGenerator.InitializeActionLocalIds(onReconnect, localIdToInvocationMap); + idGenerator.ConfigIndex++; + idGenerator.InitializeActionLocalIds(onChangeAuthority, localIdToInvocationMap); + idGenerator.ConfigIndex++; + idGenerator.InitializeActionLocalIds(onVehicleIn, localIdToInvocationMap); + idGenerator.ConfigIndex++; + idGenerator.InitializeActionLocalIds(onVehicleOut, localIdToInvocationMap); + idGenerator.ConfigIndex++; + idGenerator.InitializeActionLocalIds(onZoneEnter, localIdToInvocationMap); + idGenerator.ConfigIndex++; + idGenerator.InitializeActionLocalIds(onZoneExit, localIdToInvocationMap); + idGenerator.ConfigIndex++; + idGenerator.InitializeActionLocalIds(onHeal, localIdToInvocationMap); + idGenerator.ConfigIndex++; + idGenerator.InitializeActionLocalIds(onBeingHealed, localIdToInvocationMap); + idGenerator.ConfigIndex++; + + if (modifierMixins == null) return; + idGenerator.Type = ConfigAbilitySubContainerType.MODIFIER_MIXIN; + for (uint i = 0; i < modifierMixins.Length; i++) + { + idGenerator.ConfigIndex = 0; + await modifierMixins[i].Initialize(idGenerator, localIdToInvocationMap); + idGenerator.MixinIndex++; + } + } } } diff --git a/src/GameServer/Data/BinOut/Ability/Temp/Actions/LoseHP.cs b/src/GameServer/Data/BinOut/Ability/Temp/Actions/LoseHP.cs index 9b4331bc..8c30843a 100644 --- a/src/GameServer/Data/BinOut/Ability/Temp/Actions/LoseHP.cs +++ b/src/GameServer/Data/BinOut/Ability/Temp/Actions/LoseHP.cs @@ -12,8 +12,9 @@ namespace Weedwacker.GameServer.Data.BinOut.Ability.Temp.Actions [JsonProperty] public readonly bool lethal; [JsonProperty] public readonly float? limboByTargetMaxHPRatio; - public override async Task Execute(string abilityName, AvatarEntity avatar, SceneEntity enemyTarget = null) + public override async Task Invoke(string abilityName, BaseEntity avatarr, SceneEntity? enemyTarget = null) { + if (!(avatarr is AvatarEntity avatar)) return; if (!doOffStage && avatar.Avatar.Owner.TeamManager.GetCurrentAvatarEntity() != avatar) return; float curHP; @@ -21,10 +22,11 @@ namespace Weedwacker.GameServer.Data.BinOut.Ability.Temp.Actions float newHP; switch(target) { + case TargetType.None: case TargetType.Self: curHP = avatar.FightProps[FightProperty.FIGHT_PROP_CUR_HP]; maxHP = avatar.FightProps[FightProperty.FIGHT_PROP_MAX_HP]; - newHP = curHP - DynamicFloatHelper.ResolveDynamicFloat(amountByTargetCurrentHPRatio, avatar.Avatar, abilityName) * curHP; + newHP = curHP - DynamicFloatHelper.ResolveDynamicFloat(amountByTargetCurrentHPRatio, avatar, abilityName) * curHP; if(limboByTargetMaxHPRatio != null) { newHP = Math.Max(newHP, (float)limboByTargetMaxHPRatio); diff --git a/src/GameServer/Data/BinOut/Ability/Temp/BaseAbilityMixin.cs b/src/GameServer/Data/BinOut/Ability/Temp/BaseAbilityMixin.cs index bdd812f8..dcb5f1d1 100644 --- a/src/GameServer/Data/BinOut/Ability/Temp/BaseAbilityMixin.cs +++ b/src/GameServer/Data/BinOut/Ability/Temp/BaseAbilityMixin.cs @@ -1,6 +1,30 @@ -namespace Weedwacker.GameServer.Data.BinOut.Ability.Temp +using Weedwacker.GameServer.Systems.World; + +namespace Weedwacker.GameServer.Data.BinOut.Ability.Temp { - internal abstract class BaseAbilityMixin + internal abstract class BaseAbilityMixin : IInvocation { + public virtual async Task Invoke(string abilityName, BaseEntity srcEntity, SceneEntity? targetEntity = null) + { + + } + + public virtual async Task Initialize(LocalIdGenerator idGenerator, Dictionary localIdToInvocationMap) + { + uint id = idGenerator.GetLocalId(); + localIdToInvocationMap[id] = this; + /* + idGenerator.ConfigIndex = 0; + for(BaseAction[] actions in BaseAction[][] containrer) + { + for(BaseAction action) + { + idGenerator.InitializeActionLocalIds + } + idGenerator.ConfigIndex++; + } + idGenerator.ConfigIndex = 0; + */ + } } } diff --git a/src/GameServer/Data/BinOut/Ability/Temp/BaseAction.cs b/src/GameServer/Data/BinOut/Ability/Temp/BaseAction.cs index d1849222..9479f141 100644 --- a/src/GameServer/Data/BinOut/Ability/Temp/BaseAction.cs +++ b/src/GameServer/Data/BinOut/Ability/Temp/BaseAction.cs @@ -2,9 +2,9 @@ namespace Weedwacker.GameServer.Data.BinOut.Ability.Temp { - internal abstract class BaseAction + internal abstract class BaseAction : IInvocation { - public virtual async Task Execute(string abilityName, AvatarEntity avatar, SceneEntity enemyTarget = null) + public virtual async Task Invoke(string abilityName, BaseEntity srcEntity, SceneEntity? targetEntity = null) { } diff --git a/src/GameServer/Data/BinOut/Ability/Temp/ConfigAbility.cs b/src/GameServer/Data/BinOut/Ability/Temp/ConfigAbility.cs index 4cc44d1d..4aa8c145 100644 --- a/src/GameServer/Data/BinOut/Ability/Temp/ConfigAbility.cs +++ b/src/GameServer/Data/BinOut/Ability/Temp/ConfigAbility.cs @@ -1,5 +1,6 @@ using System.Runtime.Serialization; using Newtonsoft.Json; +using Weedwacker.GameServer.Enums; namespace Weedwacker.GameServer.Data.BinOut.Ability.Temp { @@ -9,8 +10,78 @@ namespace Weedwacker.GameServer.Data.BinOut.Ability.Temp [JsonProperty] public readonly Dictionary? abilitySpecials; [JsonProperty] public readonly BaseAbilityMixin[]? abilityMixins; [JsonProperty] public readonly Dictionary? modifiers; - [JsonProperty] public readonly BaseAction[] onAdded; + [JsonProperty] public readonly BaseAction[]? onAdded; + [JsonProperty] public readonly BaseAction[]? onRemoved; [JsonProperty] public readonly BaseAction[]? onAbilityStart; + [JsonProperty] public readonly BaseAction[]? onKill; + [JsonProperty] public readonly BaseAction[]? onFieldEnter; + [JsonProperty] public readonly BaseAction[]? onExit; + [JsonProperty] public readonly BaseAction[]? onAttach; + [JsonProperty] public readonly BaseAction[]? onDetach; + [JsonProperty] public readonly BaseAction[]? onAvatarIn; + [JsonProperty] public readonly BaseAction[]? onAvatarOut; + [JsonProperty] public readonly BaseAction[]? onTriggerAvatarRay; + [JsonProperty] public readonly BaseAction[]? onVehicleIn; + [JsonProperty] public readonly BaseAction[]? onVehicleOut; [JsonProperty] public readonly bool isDynamicAbility; // if true, disable this ability by default. Enable via ConfigTalent AddAbility + + [JsonIgnore] public Dictionary LocalIdToInvocationMap; + [JsonIgnore] public SortedList ModifierList; + + [OnDeserialized] + internal async void OnDeserializedMethod(StreamingContext context) + { + // DO NOT CHANGE THE ORDER + LocalIdToInvocationMap = new(); + + LocalIdGenerator idGenerator = new(ConfigAbilitySubContainerType.ACTION); + idGenerator.InitializeActionLocalIds(onAdded, LocalIdToInvocationMap); + idGenerator.ConfigIndex++; + idGenerator.InitializeActionLocalIds(onRemoved, LocalIdToInvocationMap); + idGenerator.ConfigIndex++; + idGenerator.InitializeActionLocalIds(onAbilityStart, LocalIdToInvocationMap); + idGenerator.ConfigIndex++; + idGenerator.InitializeActionLocalIds(onKill, LocalIdToInvocationMap); + idGenerator.ConfigIndex++; + idGenerator.InitializeActionLocalIds(onFieldEnter, LocalIdToInvocationMap); + idGenerator.ConfigIndex++; + idGenerator.InitializeActionLocalIds(onExit, LocalIdToInvocationMap); + idGenerator.ConfigIndex++; + idGenerator.InitializeActionLocalIds(onAttach, LocalIdToInvocationMap); + idGenerator.ConfigIndex++; + idGenerator.InitializeActionLocalIds(onDetach, LocalIdToInvocationMap); + idGenerator.ConfigIndex++; + idGenerator.InitializeActionLocalIds(onAvatarIn, LocalIdToInvocationMap); + idGenerator.ConfigIndex++; + idGenerator.InitializeActionLocalIds(onAvatarOut, LocalIdToInvocationMap); + idGenerator.ConfigIndex++; + idGenerator.InitializeActionLocalIds(onTriggerAvatarRay, LocalIdToInvocationMap); + idGenerator.ConfigIndex++; + idGenerator.InitializeActionLocalIds(onVehicleIn, LocalIdToInvocationMap); + idGenerator.ConfigIndex++; + idGenerator.InitializeActionLocalIds(onVehicleOut, LocalIdToInvocationMap); + + if (modifiers != null) + { + ModifierList = new(); + var modifierArray = modifiers.ToArray(); + for (uint i = 0; i < modifierArray.Length; i++) + { + ModifierList[i] = modifierArray[i].Value; + await modifierArray[i].Value.Initialize(LocalIdToInvocationMap); + } + } + + if (abilityMixins != null) + { + LocalIdGenerator idGenerator2 = new(ConfigAbilitySubContainerType.MIXIN); + for (uint i = 0; i < abilityMixins.Length; i++) + { + idGenerator2.ConfigIndex = 0; + await abilityMixins[i].Initialize(idGenerator2, LocalIdToInvocationMap); + idGenerator2.MixinIndex++; + } + } + } } } diff --git a/src/GameServer/Data/BinOut/Ability/Temp/IInvocation.cs b/src/GameServer/Data/BinOut/Ability/Temp/IInvocation.cs new file mode 100644 index 00000000..f65e6c3a --- /dev/null +++ b/src/GameServer/Data/BinOut/Ability/Temp/IInvocation.cs @@ -0,0 +1,9 @@ +using Weedwacker.GameServer.Systems.World; + +namespace Weedwacker.GameServer.Data.BinOut.Ability.Temp +{ + public interface IInvocation + { + internal Task Invoke(string abilityName, BaseEntity srcEntity, SceneEntity? targetEntity = null); + } +} diff --git a/src/GameServer/Data/BinOut/Ability/Temp/LocalIdGenerator.cs b/src/GameServer/Data/BinOut/Ability/Temp/LocalIdGenerator.cs new file mode 100644 index 00000000..cc31cd1d --- /dev/null +++ b/src/GameServer/Data/BinOut/Ability/Temp/LocalIdGenerator.cs @@ -0,0 +1,50 @@ +using Weedwacker.GameServer.Enums; +using Weedwacker.Shared.Utils; + +namespace Weedwacker.GameServer.Data.BinOut.Ability.Temp +{ + internal class LocalIdGenerator + { + public ConfigAbilitySubContainerType Type = ConfigAbilitySubContainerType.NONE; + public uint ConfigIndex = 0; + public uint MixinIndex = 0; + private uint ActionIndex = 0; + + public LocalIdGenerator(ConfigAbilitySubContainerType type) + { + if (type == ConfigAbilitySubContainerType.NONE) throw new Exception(); + Type = type; + } + + public void InitializeActionLocalIds(BaseAction[]? actions, Dictionary localIdToInvocationMap) + { + if (actions == null) return; + ActionIndex = 0; + for (ushort i = 0; i < actions.Length; i++) + { + ActionIndex++; + uint id = GetLocalId(); + localIdToInvocationMap[id] = actions[i]; + } + ActionIndex = 0; + } + + public uint GetLocalId() + { + switch (Type) + { + case ConfigAbilitySubContainerType.ACTION: + return 8 * ConfigIndex + 0x200 * ActionIndex + 1; + case ConfigAbilitySubContainerType.MIXIN: + return 0; + case ConfigAbilitySubContainerType.MODIFIER_ACTION: + return 0; + case ConfigAbilitySubContainerType.MODIFIER_MIXIN: + return 0; + default: + Logger.WriteErrorLine("Invalid ConfigAbilitySubContainerType"); + return 0; + } + } + } +} diff --git a/src/GameServer/Data/DynamicFloatHelper.cs b/src/GameServer/Data/DynamicFloatHelper.cs index 7e875ffd..b6be58a2 100644 --- a/src/GameServer/Data/DynamicFloatHelper.cs +++ b/src/GameServer/Data/DynamicFloatHelper.cs @@ -3,6 +3,7 @@ using System.Text.RegularExpressions; using Newtonsoft.Json.Linq; using Weedwacker.GameServer.Enums; using Weedwacker.GameServer.Systems.Avatar; +using Weedwacker.GameServer.Systems.World; using Weedwacker.Shared.Utils; namespace Weedwacker.GameServer.Data @@ -10,20 +11,22 @@ namespace Weedwacker.GameServer.Data internal static class DynamicFloatHelper { - public static float ResolveDynamicFloat(object dynFloat, Avatar avatar, string configAbilityName) + public static float ResolveDynamicFloat(object dynFloat, BaseEntity entity, string configAbilityName) { if (dynFloat is double asFloat) return (float)asFloat; else if (dynFloat is string) { - if (avatar.GetCurSkillDepot().AbilitySpecials[configAbilityName].ContainsKey(dynFloat as string)) - return avatar.GetCurSkillDepot().AbilitySpecials[configAbilityName][dynFloat as string]; + uint ability = Utils.AbilityHash(configAbilityName); + uint special = Utils.AbilityHash(dynFloat as string); + if (entity.AbilityManager.AbilitySpecialOverrideMap[ability].ContainsKey(special)) + return entity.AbilityManager.AbilitySpecialOverrideMap[ability][special]; else // eg %X+%Y - return ResolvePercentageSymbolReferences(dynFloat as string, avatar, configAbilityName); + return ResolvePercentageSymbolReferences(dynFloat as string, entity, configAbilityName); } else if (dynFloat is JArray expression) { - return ResolvePolishNotation(expression.ToObject(), avatar, configAbilityName); + return ResolvePolishNotation(expression.ToObject(), entity, configAbilityName); } else { @@ -32,10 +35,12 @@ namespace Weedwacker.GameServer.Data } } - private static float ResolvePolishNotation(object[] expression, Avatar avatar, string configAbilityName) + private static float ResolvePolishNotation(object[] expression, BaseEntity entity, string configAbilityName) { Stack myStack = new Stack(); float value; + uint ability = Utils.AbilityHash(configAbilityName); + for (int i = 0; i < expression.Length; i++) { if (expression[i] is float asFloat) @@ -54,7 +59,8 @@ namespace Weedwacker.GameServer.Data myStack.Push(local); break; default: - if (avatar.GetCurSkillDepot().AbilitySpecials[configAbilityName].TryGetValue(expression[i] as string, out float fVal)) + uint special = Utils.AbilityHash(expression[i] as string); + if (entity.AbilityManager.AbilitySpecialOverrideMap[ability].TryGetValue(special, out float fVal)) { myStack.Push(fVal); break; @@ -64,7 +70,7 @@ namespace Weedwacker.GameServer.Data myStack.Push(floatyFloat); break; } - else if (avatar.FightProp != null && avatar.FightProp.TryGetValue((FightProperty)Enum.Parse(typeof(FightProperty), expression[i] as string), out float fightProp)) + else if (entity is SceneEntity sceneEntity && sceneEntity.FightProps != null && sceneEntity.FightProps.TryGetValue((FightProperty)Enum.Parse(typeof(FightProperty), expression[i] as string), out float fightProp)) { myStack.Push(fightProp); break; @@ -80,15 +86,17 @@ namespace Weedwacker.GameServer.Data return myStack.Pop(); } - private static float ResolvePercentageSymbolReferences(string dynFloat, Avatar avatar, string configAbilityName) + private static float ResolvePercentageSymbolReferences(string dynFloat, BaseEntity entity, string configAbilityName) { + uint ability = Utils.AbilityHash(configAbilityName); var specials = Regex.Matches(dynFloat, @"(? InstanceToAbilityHashMap = new(); // protected abstract Dictionary ConfigAbilityHashMap { get; } // - protected Dictionary> AbilitySpecialOverrideMap = new(); // > + public readonly Dictionary> AbilitySpecialOverrideMap = new(); // > public abstract Dictionary?>? AbilitySpecials { get; }// > public abstract HashSet ActiveDynamicAbilities { get; } public abstract Dictionary> UnlockedTalentParams { get; } @@ -41,13 +41,16 @@ namespace Weedwacker.GameServer.Systems.Ability } public virtual async Task HandleAbilityInvokeAsync(AbilityInvokeEntry invoke) { - IBufferMessage info; + IBufferMessage info = new AbilityMetaModifierChange(); ByteString data = invoke.AbilityData; //TODO add all cases switch (invoke.ArgumentType) { case AbilityInvokeArgument.None: - //TODO process head + //TODO + ConfigAbility ability = ConfigAbilityHashMap[InstanceToAbilityHashMap[invoke.Head.InstancedAbilityId]]; + IInvocation invocation = ability.LocalIdToInvocationMap[(uint)invoke.Head.LocalId]; + await invocation.Invoke(ability.abilityName, Owner); info = new AbilityMetaModifierChange(); // just to satisfy the compiler. abilityData is empty anyway. break; case AbilityInvokeArgument.MetaModifierChange: diff --git a/src/Shared/Utils/Utils.cs b/src/Shared/Utils/Utils.cs index ad4f402d..bc5c3470 100644 --- a/src/Shared/Utils/Utils.cs +++ b/src/Shared/Utils/Utils.cs @@ -2,9 +2,9 @@ { public static class Utils { - public static ulong AbilityHash(string str) + public static uint AbilityHash(string str) { - ulong hash = 0; + uint hash = 0; char[] asCharArray = str.ToCharArray(); for (int i = 0; i < str.Length; i++) {