implement masochism

This commit is contained in:
akatatsu27 2022-11-09 02:29:33 +02:00
parent 36e6281bb4
commit 6c21989ffe
11 changed files with 265 additions and 23 deletions

View File

@ -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<uint, IInvocation> 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++;
}
}
}
}

View File

@ -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);

View File

@ -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<uint, IInvocation> 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;
*/
}
}
}

View File

@ -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)
{
}

View File

@ -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<string, float>? abilitySpecials;
[JsonProperty] public readonly BaseAbilityMixin[]? abilityMixins;
[JsonProperty] public readonly Dictionary<string, AbilityModifier>? 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<uint, IInvocation> LocalIdToInvocationMap;
[JsonIgnore] public SortedList<uint, AbilityModifier> 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++;
}
}
}
}
}

View File

@ -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);
}
}

View File

@ -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<uint, IInvocation> 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;
}
}
}
}

View File

@ -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<string[]>(), avatar, configAbilityName);
return ResolvePolishNotation(expression.ToObject<string[]>(), 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<float> myStack = new Stack<float>();
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, @"(?<!\w)#\w+"); // match "%Word"
foreach (Match match in specials)
{
string withoutPerc = Regex.Replace(match.Value, "%", "");
if (avatar.GetCurSkillDepot().AbilitySpecials[configAbilityName].TryGetValue(withoutPerc, out float fVal))
uint special = Utils.AbilityHash(withoutPerc);
if (entity.AbilityManager.AbilitySpecialOverrideMap[ability].TryGetValue(special, out float fVal))
dynFloat = Regex.Replace(dynFloat, match.Value, fVal.ToString());
else if (avatar.FightProp.TryGetValue((FightProperty)Enum.Parse(typeof(FightProperty), match.Value), out float fightProp))
else if (entity is SceneEntity sceneEntity && sceneEntity.FightProps.TryGetValue((FightProperty)Enum.Parse(typeof(FightProperty), match.Value), out float fightProp))
dynFloat = Regex.Replace(dynFloat, match.Value, fightProp.ToString());
else
{

View File

@ -0,0 +1,11 @@
namespace Weedwacker.GameServer.Enums
{
internal enum ConfigAbilitySubContainerType : uint
{
NONE,
ACTION,
MIXIN,
MODIFIER_ACTION,
MODIFIER_MIXIN
}
}

View File

@ -11,7 +11,7 @@ namespace Weedwacker.GameServer.Systems.Ability
protected readonly BaseEntity Owner;
protected Dictionary<uint, uint> InstanceToAbilityHashMap = new(); // <instancedAbilityId, abilityNameHash>
protected abstract Dictionary<uint, ConfigAbility> ConfigAbilityHashMap { get; } // <abilityNameHash, configAbility>
protected Dictionary<uint, Dictionary<uint, float>> AbilitySpecialOverrideMap = new(); // <abilityNameHash, <abilitySpecialNameHash, value>>
public readonly Dictionary<uint, Dictionary<uint, float>> AbilitySpecialOverrideMap = new(); // <abilityNameHash, <abilitySpecialNameHash, value>>
public abstract Dictionary<string, Dictionary<string, float>?>? AbilitySpecials { get; }// <abilityName, <abilitySpecial, value>>
public abstract HashSet<string> ActiveDynamicAbilities { get; }
public abstract Dictionary<string, HashSet<string>> 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:

View File

@ -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++)
{