diff --git a/Assets/Scripts/ECS/Data/BattleComp.cs b/Assets/Scripts/ECS/Data/BattleComp.cs index d923392..5c232aa 100644 --- a/Assets/Scripts/ECS/Data/BattleComp.cs +++ b/Assets/Scripts/ECS/Data/BattleComp.cs @@ -1,5 +1,6 @@ using Assets.Scripts.Data; using Leopotam.EcsLite; +using System.Collections.Generic; namespace Assets.Scripts.ECS.Data { @@ -30,7 +31,19 @@ public struct HeroInstanceRefComp : IPackedWithWorldRef { public EcsPackedEntityWithWorld HeroInstancePackedEntity { get; internal set; } public EcsPackedEntityWithWorld Packed => HeroInstancePackedEntity; + } + public struct HeroInstanceMapping + { + /// + /// Origin world entity packed used as a key, so when needed we can get a battle world entity by this key + /// + public Dictionary OriginToBattleMapping { get; internal set; } + + /// + /// Battle world entity packed used as a key, so when needed we can get an origin world entity by this key + /// + public Dictionary BattleToOriginMapping { get; internal set; } } /// diff --git a/Assets/Scripts/ECS/Data/Relations.cs b/Assets/Scripts/ECS/Data/Relations.cs index 29da7b7..ad6117a 100644 --- a/Assets/Scripts/ECS/Data/Relations.cs +++ b/Assets/Scripts/ECS/Data/Relations.cs @@ -13,15 +13,29 @@ public struct P2PRelationTag { } /// Marks a value of current relation score between some parties /// public struct RelationScoreTag { } - + + /// + /// For casting probability needs, we don't need any info about current effects, only their count + /// + public struct RelationEffectsCountTag { } + /// /// Marks a turn to process additional round queue manipulation to insert a hero after current turn /// - public struct PrepareRevengeComp{ + public struct PrepareRevengeComp { public EcsPackedEntityWithWorld RevengeFor { get; set; } public EcsPackedEntityWithWorld RevengeBy { get; set; } } + /// + /// Marks a turn to affect targeting (aiming) system so all teammates will attack an effect's focus + /// + public struct PrepareTargetComp + { + public EcsPackedEntityWithWorld TargetFor { get; set; } + public EcsPackedEntityWithWorld TargetBy { get; set; } + } + /// /// Contains references to each score entity by hero instance entity, /// so when event is spawned or we just need to check a score with some other guy @@ -52,6 +66,11 @@ public struct EffectInstanceInfo /// public EcsPackedEntityWithWorld EffectSource { get; set; } + /// + /// Score and effects count in the world of the relation origin + /// + public EcsPackedEntityWithWorld EffectP2PEntity { get; set; } + /// /// Applicable For: /// - AlgoRevenge @@ -76,6 +95,21 @@ public override string ToString() public RelationEffectInfo EffectInfo { get; set; } } + public struct RelEffectProbeComp + { + public EcsPackedEntityWithWorld TargetConfigRefPacked { get; internal set; } + public EcsPackedEntityWithWorld SourceOrigPacked { get; internal set; } + public EcsPackedEntityWithWorld TargetOrigPacked { get; internal set; } + + /// + /// Score, current effects count (in the origin world), current effects info (in the battle wolrd + /// + public EcsPackedEntityWithWorld P2PEntityPacked { get; internal set; } + + public EcsPackedEntity TurnEntity { get; internal set; } + public RelationSubjectState SubjectState { get; internal set; } + } + public struct RelationEffectsComp { private Dictionary currentEffects; diff --git a/Assets/Scripts/ECS/Systems/Battle/BattleAssignRelationEffectsSystem.cs b/Assets/Scripts/ECS/Systems/Battle/BattleAssignRelationEffectsSystem.cs index 8b67d76..4b376f6 100644 --- a/Assets/Scripts/ECS/Systems/Battle/BattleAssignRelationEffectsSystem.cs +++ b/Assets/Scripts/ECS/Systems/Battle/BattleAssignRelationEffectsSystem.cs @@ -1,13 +1,11 @@ using Assets.Scripts.Data; using Assets.Scripts.ECS.Data; -using Assets.Scripts.Services; using Leopotam.EcsLite; using Leopotam.EcsLite.Di; -using System; -using UnityEngine; namespace Assets.Scripts.ECS.Systems { + public class BattleAssignRelationEffectsSystem : IEcsRunSystem where T : struct, IPackedWithWorldRef { @@ -17,20 +15,13 @@ public class BattleAssignRelationEffectsSystem : IEcsRunSystem protected readonly EcsWorldInject ecsWorld; protected readonly EcsPoolInject pool = default; + protected readonly EcsPoolInject relEffectProbePool = default; protected readonly EcsPoolInject playerTeamTagPool = default; protected readonly EcsPoolInject heroInstanceOriginRefPool = default; protected readonly EcsPoolInject heroConfigRefPool = default; - protected readonly EcsPoolInject relEffectsPool = default; - protected readonly EcsPoolInject> updatePool = default; - protected readonly EcsPoolInject attackerRefPool = default; - protected readonly EcsPoolInject revengePool = default; protected readonly EcsFilterInject> filter = default; - protected readonly EcsCustomInject battleService = default; - - protected readonly EcsCustomInject heroLibraryService = default; - public void Run(IEcsSystems systems) { foreach (var entity in filter.Value) @@ -42,8 +33,8 @@ public void Run(IEcsSystems systems) if (!playerTeamTagPool.Value.Has(effectTargetEntity)) continue; - ref var originRef = ref heroInstanceOriginRefPool.Value.Get(effectTargetEntity); - if (!originRef.Packed.Unpack(out var origWorld, out var effectTargetOrig)) + ref var effTargetOriginRef = ref heroInstanceOriginRefPool.Value.Get(effectTargetEntity); + if (!effTargetOriginRef.Packed.Unpack(out var origWorld, out var effectTargetOrig)) continue; ref var heroConfigRef = ref heroConfigRefPool.Value.Get(effectTargetEntity); @@ -57,142 +48,23 @@ public void Run(IEcsSystems systems) foreach (var item in matrixComp.Matrix) { // matching only one side of the key to avoid duplicates - if (!item.Key.Item1.EqualsTo(originRef.Packed)) + if (!item.Key.Item1.EqualsTo(effTargetOriginRef.Packed)) continue; - if (TryCastRelationEffect(heroConfigRef.Packed, item.Key.Item2, originRef.Packed, item.Value, - out var effect)) - { - // registering effect for the hero affected (in the battle world, to make it handy when needed) - RelationEffectKey ruleKey = effect.Rule.Key; - item.Key.Item2.Unpack(out _, out var effectSourceEntity); - - var affectedParty = effectTargetEntity; - - if (ruleKey.RelationsEffectType switch { - RelationsEffectType.AlgoRevenge => true, - RelationsEffectType.AlgoTarget => true, - _ => false - }) - { - affectedParty = effectSourceEntity; - - ref var attackerRef = ref attackerRefPool.Value.Get(entity); - effect.EffectFocus = attackerRef.Packed; - - var revengeEntity = ecsWorld.Value.NewEntity(); - ref var revengeComp = ref revengePool.Value.Add(revengeEntity); - revengeComp.RevengeBy = ecsWorld.Value.PackEntityWithWorld(effectSourceEntity); - revengeComp.RevengeFor = ecsWorld.Value.PackEntityWithWorld(effectTargetEntity); - } - - // add info for UI - var heroIconPool = origWorld.GetPool>(); - ref var heroIcon = ref heroIconPool.Get(effectSourceEntity); - var info = effect.Rule.DraftEffectInfo(effect.Rule.GetHashCode(), heroIcon.Name); - effect.EffectInfo = info; - - ref var relEffects = ref relEffectsPool.Value.Get(affectedParty); - relEffects.SetEffect(ruleKey, effect); - - if (!updatePool.Value.Has(affectedParty)) - updatePool.Value.Add(affectedParty); - } - + var relEffectProbeEntity = ecsWorld.Value.NewEntity(); + ref var probeComp = ref relEffectProbePool.Value.Add(relEffectProbeEntity); + probeComp.SourceOrigPacked = item.Key.Item1; + probeComp.TargetOrigPacked = item.Key.Item2; + probeComp.TargetConfigRefPacked = heroConfigRef.Packed; + probeComp.P2PEntityPacked = item.Value; + probeComp.SubjectState = SubjectState; + probeComp.TurnEntity = systems.GetWorld().PackEntity(entity); } } } } - /// - /// Will add relation effect if rules applied will allow to - /// - /// Entity of a hero to wich the effect will be casted (if any) - /// EcsWorld to take relations from - /// Hero Config Entity of the given hero - /// Packed other guy entity in the origin world - /// Packed this guy entity in the origin world - /// Packed score entity for the given hero and the other guy - /// true if new effect was casted - protected bool TryCastRelationEffect( - EcsPackedEntityWithWorld heroConfigPackedEntity, - EcsPackedEntityWithWorld effectSource, - EcsPackedEntityWithWorld effectTarget, - EcsPackedEntityWithWorld scoreEntityPacked, - out EffectInstanceInfo effect) - { - effect = default; - if (!effectSource.Unpack(out var origWorld, out var otherGuyEntity)) - throw new Exception("Stale Other Guy Entity (probably dead already)"); - - if (!scoreEntityPacked.Unpack(out _, out var scoreEntity)) - throw new Exception("Stale Score Entity"); - - var relationsConfig = heroLibraryService.Value.HeroRelationsConfigProcessor(); - - var score = origWorld.ReadIntValue(scoreEntity); - var relationsState = relationsConfig.GetRelationState(score); - - - if (!heroConfigPackedEntity.Unpack(out var libWorld, out var heroConfigEntity)) - throw new Exception("No Hero Config for current guy"); - - var libHeroPool = libWorld.GetPool(); - ref var heroConfig = ref libHeroPool.Get(heroConfigEntity); - - var rulesCaseKey = new RelationEffectLibraryKey( - heroConfig.Id, SubjectState, relationsState); - - var effectRules = heroLibraryService.Value.HeroRelationEffectsLibProcessor(); - - if (!effectRules.SubjectStateEffectsIndex.TryGetValue(rulesCaseKey, out var scope)) - return false; // no effect for relation state, it's ok - - var origWorldConfigRefPool = origWorld.GetPool(); - ref var otherGuyConfigRef = ref origWorldConfigRefPool.Get(otherGuyEntity); - if (!otherGuyConfigRef.Packed.Unpack(out _, out var otherGuyHeroConfigEntity)) - throw new Exception("No Hero Config for the other guy"); - - ref var otherGuyHeroConfig = ref libHeroPool.Get(otherGuyHeroConfigEntity); - - var ruleType = scope.EffectRule.EffectType; - - if (ruleType.EffectClass() != RelationsEffectClass.Battle) - return false; // this system process only battle effects - - var rule = (IBattleEffectRule)scope.EffectRule; - ref var currentRound = ref battleService.Value.CurrentRound; - - Debug.Log($"Relations Effect of type {ruleType} was just spawned " + - $"for {heroConfig.Name} in {scope.SelfState} due to {scope.RelationState} " + - $"with {otherGuyHeroConfig.Name}"); - - // 1. add component to the entity of the current score data; - // 2. keep all spawned effects (EffectRules) in that component; - // 3. count of already spawned effects used as a wheight for spawn rate (key for - // AdditioinalEffectSpawnRate queries); - - var currentEffectsPool = origWorld.GetPool(); - // respect spawn rate from AdditionalEffectSpawnRate: - ref var currentEffects = ref currentEffectsPool.Get(scoreEntity); - if (!effectRules.TrySpawnAdditionalEffect(currentEffects.CurrentEffects.Count)) - return false; - - Debug.Log($"Spawned with respect of exisiting {currentEffects.CurrentEffects.Count} effects"); - - effect = new EffectInstanceInfo() - { - StartRound = currentRound.Round, - EndRound = currentRound.Round + rule.TurnsCount, - UsageLeft = rule.TurnsCount, - Rule = rule, - EffectSource = effectSource, - }; - - currentEffects.SetEffect(rule.Key, effect); // effect focus (if any) is omitted here, but added for battle context. - - return true; - } + } } diff --git a/Assets/Scripts/ECS/Systems/Battle/BattleDequeueExpiredRelationEffectsSystem.cs b/Assets/Scripts/ECS/Systems/Battle/BattleDequeueExpiredRelationEffectsSystem.cs index f714955..78a2ce9 100644 --- a/Assets/Scripts/ECS/Systems/Battle/BattleDequeueExpiredRelationEffectsSystem.cs +++ b/Assets/Scripts/ECS/Systems/Battle/BattleDequeueExpiredRelationEffectsSystem.cs @@ -1,6 +1,5 @@ using Assets.Scripts.Data; using Assets.Scripts.ECS.Data; -using Assets.Scripts.Services; using Leopotam.EcsLite; using Leopotam.EcsLite.Di; @@ -14,8 +13,6 @@ public class BattleDequeueExpiredRelationEffectsSystem : IEcsRunSystem private readonly EcsFilterInject> currentEffectFilter = default; private readonly EcsFilterInject> filter = default; - private readonly EcsCustomInject raidService = default; - public void Run(IEcsSystems systems) { foreach (var entity in filter.Value) @@ -25,14 +22,10 @@ public void Run(IEcsSystems systems) // for both battle and raid worlds and remove expired effects: DequeueRelationEffects(systems.GetWorld(), roundInfo.Round); - if (raidService.Value.EcsWorld != null) - DequeueRelationEffects(raidService.Value.EcsWorld, roundInfo.Round); - // enqueue view update to reflect changes (if any) foreach (var effectsEntity in currentEffectFilter.Value) if (!updatePool.Value.Has(effectsEntity)) - updatePool.Value.Add(effectsEntity); - + updatePool.Value.Add(effectsEntity); } } @@ -50,13 +43,18 @@ private void DequeueRelationEffects(EcsWorld world, int round) buff.Add(item.Key); foreach (var item in buff) + { + var effect = relEffect.CurrentEffects[item]; + if (effect.EffectP2PEntity.Unpack(out var origWorld, out var p2pEntity)) + origWorld.IncrementIntValue(-1, p2pEntity); + relEffect.CurrentEffects.Remove(item); + } buff.Clear(); } ListPool.Add(buff); } - } } diff --git a/Assets/Scripts/ECS/Systems/Battle/BattleHeroesInitSystem.cs b/Assets/Scripts/ECS/Systems/Battle/BattleHeroesInitSystem.cs index 1a64315..0c7e872 100644 --- a/Assets/Scripts/ECS/Systems/Battle/BattleHeroesInitSystem.cs +++ b/Assets/Scripts/ECS/Systems/Battle/BattleHeroesInitSystem.cs @@ -16,6 +16,7 @@ public class BattleHeroesInitSystem : IEcsRunSystem private readonly EcsPoolInject heroInstanceOriginRefPool = default; private readonly EcsPoolInject positionPool = default; private readonly EcsPoolInject battleFieldPool = default; + private readonly EcsPoolInject heroInstanceMappingPool = default; private readonly EcsFilterInject>> filter = default; @@ -34,6 +35,7 @@ public void Run(IEcsSystems systems) battleService.Value.NotifyBattleEventListeners(battleInfo); ref var battleField = ref battleFieldPool.Value.Get(battleEntity); + ref var heroInstanceMappings = ref heroInstanceMappingPool.Value.Get(battleEntity); #region Player team: @@ -48,7 +50,7 @@ public void Run(IEcsSystems systems) { var sourcePosition = playerPosBuffer[0]; - AddHeroBattleInstance(battleWorld, packed, sourcePosition); + AddHeroBattleInstance(battleWorld, heroInstanceMappings, packed, sourcePosition); playerPosBuffer.RemoveAt(0); } @@ -69,7 +71,7 @@ public void Run(IEcsSystems systems) { var sourcePosition = enemyPosBuffer[0]; - AddHeroBattleInstance(battleWorld, packed, sourcePosition); + AddHeroBattleInstance(battleWorld, heroInstanceMappings, packed, sourcePosition); enemyPosBuffer.RemoveAt(0); } @@ -79,8 +81,11 @@ public void Run(IEcsSystems systems) #endregion } - private void AddHeroBattleInstance(EcsWorld battleWorld, - EcsPackedEntityWithWorld packed, HeroPosition sourcePosition) + private void AddHeroBattleInstance( + EcsWorld battleWorld, + HeroInstanceMapping heroInstanceMappings, + EcsPackedEntityWithWorld originHeroInstancePacked, + HeroPosition sourcePosition) where T: struct { var entity = battleWorld.NewEntity(); @@ -94,16 +99,20 @@ private void AddHeroBattleInstance(EcsWorld battleWorld, // remember hero instance origin (raid or library) // to update HP and use bonuses from raid - ref var origin = ref heroInstanceOriginRefPool.Value.Add(entity); - origin.HeroInstancePackedEntity = packed; + ref var originRef = ref heroInstanceOriginRefPool.Value.Add(entity); + originRef.HeroInstancePackedEntity = originHeroInstancePacked; // getting hero config ref from the origin - if (!packed.Unpack(out var originWorld, out var originEntity)) + if (!originHeroInstancePacked.Unpack(out var originWorld, out var originEntity)) throw new Exception("No Source Hero"); ref var originConfigRefComp = ref originWorld.GetPool().Get(originEntity); ref var configRefComp = ref heroConfigRefPool.Value.Add(entity); configRefComp.HeroConfigPackedEntity = originConfigRefComp.Packed; + + var battleHeroInstancePacked = battleWorld.PackEntityWithWorld(entity); + heroInstanceMappings.OriginToBattleMapping.Add(originHeroInstancePacked, battleHeroInstancePacked); + heroInstanceMappings.BattleToOriginMapping.Add(battleHeroInstancePacked, originHeroInstancePacked); } } } diff --git a/Assets/Scripts/ECS/Systems/Battle/BattleInitSystem.cs b/Assets/Scripts/ECS/Systems/Battle/BattleInitSystem.cs index 877b5d7..d8ad1cf 100644 --- a/Assets/Scripts/ECS/Systems/Battle/BattleInitSystem.cs +++ b/Assets/Scripts/ECS/Systems/Battle/BattleInitSystem.cs @@ -12,6 +12,7 @@ public class BattleInitSystem : IEcsInitSystem private readonly EcsPoolInject battleInfoPool = default; private readonly EcsPoolInject> draftTagPool = default; + private readonly EcsPoolInject heroInstanceMappingPool = default; private readonly EcsCustomInject battleService = default; private readonly EcsCustomInject libraryService = default; @@ -39,6 +40,10 @@ public void Init(IEcsSystems systems) battle.WinnerTeamId = -1; + ref var heroInstanceMapping = ref heroInstanceMappingPool.Value.Add(entity); + heroInstanceMapping.OriginToBattleMapping = new(); + heroInstanceMapping.BattleToOriginMapping = new(); + battleService.Value.BattleEntity = ecsWorld.Value.PackEntityWithWorld(entity); } diff --git a/Assets/Scripts/ECS/Systems/Battle/BattlePrepareRelEffectVisualSystem.cs b/Assets/Scripts/ECS/Systems/Battle/BattlePrepareRelEffectVisualSystem.cs new file mode 100644 index 0000000..80b4190 --- /dev/null +++ b/Assets/Scripts/ECS/Systems/Battle/BattlePrepareRelEffectVisualSystem.cs @@ -0,0 +1,88 @@ +using Assets.Scripts.Data; +using Assets.Scripts.ECS.Data; +using Assets.Scripts.Services; +using Leopotam.EcsLite; +using Leopotam.EcsLite.Di; +using System; + +namespace Assets.Scripts.ECS.Systems +{ + public partial class BattlePrepareRelEffectVisualSystem : IEcsRunSystem + { + private readonly EcsPoolInject mappingsPool = default; + private readonly EcsPoolInject pool = default; + private readonly EcsPoolInject probePool = default; + private readonly EcsPoolInject relEffectsPool = default; + private readonly EcsPoolInject> updatePool = default; + + private readonly EcsFilterInject< + Inc< + DraftTag, + RelEffectProbeComp, + EffectInstanceInfo + >> filter = default; + + private readonly EcsFilterInject< + Inc, + Exc + > playerTeamFilter = default; + + private readonly EcsCustomInject battleManagementService = default; + + public void Run(IEcsSystems systems) + { + if (!battleManagementService.Value.BattleEntity.Unpack(out var world, out var battleEntity)) + throw new Exception("No battle!"); + + foreach (var entity in filter.Value) + { + ref var probe = ref probePool.Value.Get(entity); + ref var effect = ref pool.Value.Get(entity); + ref var mappings = ref mappingsPool.Value.Get(battleEntity); + + var sourcePacked = mappings.OriginToBattleMapping[probe.SourceOrigPacked]; + if (!sourcePacked.Unpack(out var battleWorld, out var sourceParty)) + throw new Exception("Stale effect source entity"); + + var targetPacked = mappings.OriginToBattleMapping[probe.TargetOrigPacked]; + if (!targetPacked.Unpack(out _, out var targetParty)) + throw new Exception("Stale effect target entity"); + + switch (effect.Rule.Key.RelationsEffectType) + { + case RelationsEffectType.AlgoRevenge: + PrepareVisualForEffect(probe.TargetOrigPacked, sourceParty, ref effect); + break; + case RelationsEffectType.AlgoTarget: + { + // here we should add visual to all team mates as they are affected by the effect caster + foreach (var teammateEntity in playerTeamFilter.Value) + PrepareVisualForEffect(probe.SourceOrigPacked, teammateEntity, ref effect); + } + break; + default: + PrepareVisualForEffect(probe.SourceOrigPacked, targetParty, ref effect); + break; + } + } + } + + private void PrepareVisualForEffect(EcsPackedEntityWithWorld origIconParty, int affectedParty, ref EffectInstanceInfo effect) + { + if (!origIconParty.Unpack(out var origWorld, out var iconEntity)) + throw new Exception("Stale origin effect source!"); + + var heroIconPool = origWorld.GetPool>(); + ref var heroIcon = ref heroIconPool.Get(iconEntity); + var info = effect.Rule.DraftEffectInfo(effect.Rule.GetHashCode(), heroIcon.Name); + effect.EffectInfo = info; + + ref var relEffects = ref relEffectsPool.Value.Get(affectedParty); + relEffects.SetEffect(effect.Rule.Key, effect); + + if (!updatePool.Value.Has(affectedParty)) + updatePool.Value.Add(affectedParty); + + } + } +} diff --git a/Assets/Scripts/ECS/Systems/Battle/BattlePrepareRelEffectVisualSystem.cs.meta b/Assets/Scripts/ECS/Systems/Battle/BattlePrepareRelEffectVisualSystem.cs.meta new file mode 100644 index 0000000..1aa2ca7 --- /dev/null +++ b/Assets/Scripts/ECS/Systems/Battle/BattlePrepareRelEffectVisualSystem.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: acb0d1c8b52d3ba41988b08d5e1374e5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/ECS/Systems/Battle/BattlePrepareRevengeEffectSystem.cs b/Assets/Scripts/ECS/Systems/Battle/BattlePrepareRevengeEffectSystem.cs new file mode 100644 index 0000000..d51e1fe --- /dev/null +++ b/Assets/Scripts/ECS/Systems/Battle/BattlePrepareRevengeEffectSystem.cs @@ -0,0 +1,59 @@ +using Assets.Scripts.Data; +using Assets.Scripts.ECS.Data; +using Assets.Scripts.Services; +using Leopotam.EcsLite; +using Leopotam.EcsLite.Di; +using System; + +namespace Assets.Scripts.ECS.Systems +{ + + public class BattlePrepareRevengeEffectSystem : IEcsRunSystem + { + private readonly EcsWorldInject ecsWorld; + + private readonly EcsPoolInject pool = default; + private readonly EcsPoolInject probePool = default; + private readonly EcsPoolInject attackerRefPool = default; + private readonly EcsPoolInject revengePool = default; + private readonly EcsPoolInject mappingsPool = default; + + private readonly EcsFilterInject< + Inc< + DraftTag, + RelEffectProbeComp, + EffectInstanceInfo + >> filter = default; + + private readonly EcsCustomInject battleManagementService = default; + + public void Run(IEcsSystems systems) + { + if (!battleManagementService.Value.BattleEntity.Unpack(out _, out var battleEntity)) + throw new Exception("No battle!"); + + foreach (var entity in filter.Value) + { + ref var effect = ref pool.Value.Get(entity); + ref var mappings = ref mappingsPool.Value.Get(battleEntity); + + if (effect.Rule.Key.RelationsEffectType == RelationsEffectType.AlgoRevenge) + { + ref var probe = ref probePool.Value.Get(entity); + + if (!probe.TurnEntity.Unpack(systems.GetWorld(), out var turnEntity)) + throw new Exception("Stale Turn Entity"); + + // registering effect for the hero affected (in the battle world, to make it handy when needed) + ref var attackerRef = ref attackerRefPool.Value.Get(turnEntity); + effect.EffectFocus = attackerRef.Packed; + + var revengeEntity = ecsWorld.Value.NewEntity(); + ref var revengeComp = ref revengePool.Value.Add(revengeEntity); + revengeComp.RevengeBy = mappings.OriginToBattleMapping[probe.SourceOrigPacked]; + revengeComp.RevengeFor = mappings.OriginToBattleMapping[probe.TargetOrigPacked]; + } + } + } + } +} diff --git a/Assets/Scripts/ECS/Systems/Battle/BattlePrepareRevengeEffectSystem.cs.meta b/Assets/Scripts/ECS/Systems/Battle/BattlePrepareRevengeEffectSystem.cs.meta new file mode 100644 index 0000000..5f30b63 --- /dev/null +++ b/Assets/Scripts/ECS/Systems/Battle/BattlePrepareRevengeEffectSystem.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 08fc95a6e7b08d14aadd1bc06dc3e4d1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/ECS/Systems/Battle/BattlePrepareTargetEffectSystem.cs b/Assets/Scripts/ECS/Systems/Battle/BattlePrepareTargetEffectSystem.cs new file mode 100644 index 0000000..5f15376 --- /dev/null +++ b/Assets/Scripts/ECS/Systems/Battle/BattlePrepareTargetEffectSystem.cs @@ -0,0 +1,61 @@ +using Assets.Scripts.Data; +using Assets.Scripts.ECS.Data; +using Assets.Scripts.Services; +using Leopotam.EcsLite; +using Leopotam.EcsLite.Di; +using System; + +namespace Assets.Scripts.ECS.Systems +{ + public class BattlePrepareTargetEffectSystem : IEcsRunSystem + { + private readonly EcsWorldInject ecsWorld; + + private readonly EcsPoolInject pool = default; + private readonly EcsPoolInject probePool = default; + private readonly EcsPoolInject attackerRefPool = default; + private readonly EcsPoolInject targetPool = default; + private readonly EcsPoolInject mappingsPool = default; + + private readonly EcsFilterInject< + Inc< + DraftTag, + RelEffectProbeComp, + EffectInstanceInfo + >> filter = default; + + private readonly EcsCustomInject battleManagementService = default; + + public void Run(IEcsSystems systems) + { + if (!battleManagementService.Value.BattleEntity.Unpack(out _, out var battleEntity)) + throw new Exception("No battle!"); + + foreach (var entity in filter.Value) + { + ref var effect = ref pool.Value.Get(entity); + ref var mappings = ref mappingsPool.Value.Get(battleEntity); + + if (effect.Rule.Key.RelationsEffectType == RelationsEffectType.AlgoTarget) + { + ref var probe = ref probePool.Value.Get(entity); + + if (!probe.TurnEntity.Unpack(systems.GetWorld(), out var turnEntity)) + throw new Exception("Stale Turn Entity"); + + // registering effect for the hero affected (in the battle world, to make it handy when needed) + probe.SourceOrigPacked.Unpack(out _, out var effectSourceEntity); + probe.TargetOrigPacked.Unpack(out _, out var effectTargetEntity); + + ref var attackerRef = ref attackerRefPool.Value.Get(turnEntity); + effect.EffectFocus = attackerRef.Packed; + + var targetEntity = ecsWorld.Value.NewEntity(); + ref var targetComp = ref targetPool.Value.Add(targetEntity); + targetComp.TargetBy = mappings.OriginToBattleMapping[probe.SourceOrigPacked]; + targetComp.TargetFor = mappings.OriginToBattleMapping[probe.TargetOrigPacked]; + } + } + } + } +} diff --git a/Assets/Scripts/ECS/Systems/Battle/BattlePrepareTargetEffectSystem.cs.meta b/Assets/Scripts/ECS/Systems/Battle/BattlePrepareTargetEffectSystem.cs.meta new file mode 100644 index 0000000..3c3f0b7 --- /dev/null +++ b/Assets/Scripts/ECS/Systems/Battle/BattlePrepareTargetEffectSystem.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f47ee94867ff0be478c77c9bd72088dd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/ECS/Systems/Battle/BattleRelEffectProbeSystem.cs b/Assets/Scripts/ECS/Systems/Battle/BattleRelEffectProbeSystem.cs new file mode 100644 index 0000000..b9d1bf9 --- /dev/null +++ b/Assets/Scripts/ECS/Systems/Battle/BattleRelEffectProbeSystem.cs @@ -0,0 +1,131 @@ +using Assets.Scripts.Data; +using Assets.Scripts.ECS.Data; +using Assets.Scripts.Services; +using Leopotam.EcsLite; +using Leopotam.EcsLite.Di; +using System; +using UnityEngine; + +namespace Assets.Scripts.ECS.Systems +{ + public class BattleRelEffectProbeSystem : IEcsRunSystem + { + private readonly EcsWorldInject ecsWorld; + + private readonly EcsPoolInject pool = default; + private readonly EcsPoolInject effectsPool = default; + private readonly EcsPoolInject> draftTagPool = default; + + private readonly EcsFilterInject> filter = default; + + private readonly EcsCustomInject heroLibraryService = default; + private readonly EcsCustomInject battleService = default; + + public void Run(IEcsSystems systems) + { + foreach (var entity in filter.Value) + { + ref var comp = ref pool.Value.Get(entity); + TryCastRelationEffect( + entity, + comp.TargetConfigRefPacked, + comp.SourceOrigPacked, + comp.TargetOrigPacked, + comp.P2PEntityPacked, + comp.SubjectState); + } + + } + + /// + /// Will add relation effect if rules applied will allow to + /// + /// Entity of a hero to wich the effect will be casted (if any) + /// EcsWorld to take relations from + /// Hero Config Entity of the given hero + /// Packed other guy entity in the origin world + /// Packed this guy entity in the origin world + /// Packed score entity for the given hero and the other guy + /// true if new effect was casted + private bool TryCastRelationEffect( + int effectProbeEntity, + EcsPackedEntityWithWorld targetConfigRefPacked, + EcsPackedEntityWithWorld effectSource, + EcsPackedEntityWithWorld effectTarget, + EcsPackedEntityWithWorld p2pEntityPacked, + RelationSubjectState SubjectState) + { + if (!effectSource.Unpack(out var origWorld, out var otherGuyEntity)) + throw new Exception("Stale Other Guy Entity (probably dead already)"); + + if (!p2pEntityPacked.Unpack(out _, out var p2pEntity)) + throw new Exception("Stale Score Entity"); + + var relationsConfig = heroLibraryService.Value.HeroRelationsConfigProcessor(); + var effectRules = heroLibraryService.Value.HeroRelationEffectsLibProcessor(); + + var currentEffectsCount = origWorld.ReadIntValue(p2pEntity); + // respect spawn rate from AdditionalEffectSpawnRate: + if (!effectRules.TrySpawnAdditionalEffect(currentEffectsCount)) + { + Debug.Log($"Spawn failed due to existing {currentEffectsCount} effects"); + return false; + } + + var score = origWorld.ReadIntValue(p2pEntity); + var relationsState = relationsConfig.GetRelationState(score); + + if (!targetConfigRefPacked.Unpack(out var libWorld, out var heroConfigEntity)) + throw new Exception("No Hero Config for current guy"); + + var libHeroPool = libWorld.GetPool(); + ref var heroConfig = ref libHeroPool.Get(heroConfigEntity); + + var rulesCaseKey = new RelationEffectLibraryKey( + heroConfig.Id, SubjectState, relationsState); + + if (!effectRules.SubjectStateEffectsIndex.TryGetValue(rulesCaseKey, out var scope)) + return false; // no effect for relation state, it's ok + + var origWorldConfigRefPool = origWorld.GetPool(); + ref var otherGuyConfigRef = ref origWorldConfigRefPool.Get(otherGuyEntity); + if (!otherGuyConfigRef.Packed.Unpack(out _, out var otherGuyHeroConfigEntity)) + throw new Exception("No Hero Config for the other guy"); + + ref var otherGuyHeroConfig = ref libHeroPool.Get(otherGuyHeroConfigEntity); + + var ruleType = scope.EffectRule.EffectType; + + if (ruleType.EffectClass() != RelationsEffectClass.Battle) + return false; // this system process only battle effects + + var rule = (IBattleEffectRule)scope.EffectRule; + ref var currentRound = ref battleService.Value.CurrentRound; + + Debug.Log($"Relations Effect of type {ruleType} was just spawned " + + $"for {heroConfig.Name} in {scope.SelfState} due to {scope.RelationState} " + + $"with {otherGuyHeroConfig.Name}"); + + // 1. add component to the entity of the current score data; + // 2. keep all spawned effects (EffectRules) in that component; + // 3. count of already spawned effects used as a wheight for spawn rate (key for + // AdditioinalEffectSpawnRate queries); + + draftTagPool.Value.Add(effectProbeEntity); + + ref var effect = ref effectsPool.Value.Add(effectProbeEntity); + effect.StartRound = currentRound.Round; + effect.EndRound = currentRound.Round + rule.TurnsCount; + effect.UsageLeft = rule.TurnsCount; + effect.Rule = rule; + effect.EffectSource = effectSource; + effect.EffectP2PEntity = p2pEntityPacked; + + Debug.Log($"Spawned with respect of exisiting {currentEffectsCount} effects"); + + origWorld.IncrementIntValue(1, p2pEntity); + + return true; + } + } +} diff --git a/Assets/Scripts/ECS/Systems/Battle/BattleRelEffectProbeSystem.cs.meta b/Assets/Scripts/ECS/Systems/Battle/BattleRelEffectProbeSystem.cs.meta new file mode 100644 index 0000000..035f566 --- /dev/null +++ b/Assets/Scripts/ECS/Systems/Battle/BattleRelEffectProbeSystem.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d899b8ef6777c054dbc5d734cc12b4aa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/ECS/Systems/HeroLibrary/LibraryHandleHeroMoveSystem.cs b/Assets/Scripts/ECS/Systems/HeroLibrary/LibraryHandleHeroMoveSystem.cs new file mode 100644 index 0000000..0cf81cd --- /dev/null +++ b/Assets/Scripts/ECS/Systems/HeroLibrary/LibraryHandleHeroMoveSystem.cs @@ -0,0 +1,44 @@ +using Assets.Scripts.ECS.Data; +using Assets.Scripts.Services; +using Leopotam.EcsLite; +using Leopotam.EcsLite.Di; + +namespace Assets.Scripts.ECS.Systems +{ + public class LibraryHandleHeroMoveSystem : IEcsRunSystem + { + private readonly EcsPoolInject positionPool = default; + private readonly EcsPoolInject playerTeamTagPool = default; + private readonly EcsPoolInject> updateTagPool = default; + + private readonly EcsFilterInject< + Inc, PositionComp>> moveFilter = default; + + private readonly EcsCustomInject libraryService = default; + + public void Run(IEcsSystems systems) + { + foreach (var moveEntity in moveFilter.Value) + { + ref var position = ref positionPool.Value.Get(moveEntity); + var playerTeamId = libraryService.Value.PlayerTeam.Id; + + if (position.Position.Team != position.PrevPosition.Team && + (playerTeamId == position.Position.Team || + playerTeamId == position.PrevPosition.Team)) + { + if (playerTeamTagPool.Value.Has(moveEntity)) + playerTeamTagPool.Value.Del(moveEntity); + + if (position.Position.Team == playerTeamId) + playerTeamTagPool.Value.Add(moveEntity); + + if (libraryService.Value.PlayerTeamEntity.Unpack(out _, out var playerTeamEntity) && + !updateTagPool.Value.Has(playerTeamEntity)) + updateTagPool.Value.Add(playerTeamEntity); + } + } + } + } +} + diff --git a/Assets/Scripts/ECS/Systems/HeroLibrary/LibraryHandleHeroMoveSystem.cs.meta b/Assets/Scripts/ECS/Systems/HeroLibrary/LibraryHandleHeroMoveSystem.cs.meta new file mode 100644 index 0000000..63ff280 --- /dev/null +++ b/Assets/Scripts/ECS/Systems/HeroLibrary/LibraryHandleHeroMoveSystem.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 344ece6d59f382b489d8102291017c94 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/ECS/Systems/HeroLibrary/LibraryUpdatePlayerTeamRelationContextSystem.cs b/Assets/Scripts/ECS/Systems/HeroLibrary/LibraryUpdatePlayerTeamRelationContextSystem.cs index 5421951..ba252b3 100644 --- a/Assets/Scripts/ECS/Systems/HeroLibrary/LibraryUpdatePlayerTeamRelationContextSystem.cs +++ b/Assets/Scripts/ECS/Systems/HeroLibrary/LibraryUpdatePlayerTeamRelationContextSystem.cs @@ -1,47 +1,11 @@ using Assets.Scripts.Data; using Assets.Scripts.ECS.Data; -using Assets.Scripts.Services; using Leopotam.EcsLite; using Leopotam.EcsLite.Di; using System.Collections.Generic; namespace Assets.Scripts.ECS.Systems { - public class LibraryHandleHeroMoveSystem : IEcsRunSystem - { - private readonly EcsPoolInject positionPool = default; - private readonly EcsPoolInject playerTeamTagPool = default; - private readonly EcsPoolInject> updateTagPool = default; - - private readonly EcsFilterInject< - Inc, PositionComp>> moveFilter = default; - - private readonly EcsCustomInject libraryService = default; - - public void Run(IEcsSystems systems) - { - foreach (var moveEntity in moveFilter.Value) - { - ref var position = ref positionPool.Value.Get(moveEntity); - var playerTeamId = libraryService.Value.PlayerTeam.Id; - - if (position.Position.Team != position.PrevPosition.Team && - (playerTeamId == position.Position.Team || - playerTeamId == position.PrevPosition.Team)) - { - if (playerTeamTagPool.Value.Has(moveEntity)) - playerTeamTagPool.Value.Del(moveEntity); - - if (position.Position.Team == playerTeamId) - playerTeamTagPool.Value.Add(moveEntity); - - if (libraryService.Value.PlayerTeamEntity.Unpack(out _, out var playerTeamEntity) && - !updateTagPool.Value.Has(playerTeamEntity)) - updateTagPool.Value.Add(playerTeamEntity); - } - } - } - } public class LibraryUpdatePlayerTeamRelationContextSystem : IEcsRunSystem { @@ -49,18 +13,13 @@ public class LibraryUpdatePlayerTeamRelationContextSystem : IEcsRunSystem private readonly EcsPoolInject pool = default; private readonly EcsPoolInject> scorePool = default; + private readonly EcsPoolInject> countPool = default; private readonly EcsPoolInject effectsPool = default; private readonly EcsPoolInject matrixPool = default; private readonly EcsFilterInject> matrixFilter = default; private readonly EcsFilterInject> filter = default; - private readonly EcsFilterInject< - Inc> effectsFilter = default; - - private readonly EcsFilterInject< - Inc>> scoreFilter = default; - private readonly EcsFilterInject< Inc> teamMemberFilter = default; @@ -77,13 +36,7 @@ public void Run(IEcsSystems systems) ecsWorld.Value.DelEntity(matrixEntity); foreach (var p2pEntity in filter.Value) - pool.Value.Del(p2pEntity); - - foreach (var scoreEntity in scoreFilter.Value) - scorePool.Value.Del(scoreEntity); - - foreach (var effectsEntity in effectsFilter.Value) - effectsPool.Value.Del(effectsEntity); + ecsWorld.Value.DelEntity(p2pEntity); // fill matrix var matrix = new Dictionary(); @@ -124,6 +77,9 @@ public void Run(IEcsSystems systems) ref var scoreComp = ref scorePool.Value.Add(p2pRelEntity); scoreComp.Value = 0; + + ref var countComp = ref countPool.Value.Add(p2pRelEntity); + countComp.Value = 0; } } } diff --git a/Assets/Scripts/ECS/Systems/Raid/PlayerTeamRelationScoresInitSystem.cs b/Assets/Scripts/ECS/Systems/Raid/PlayerTeamRelationScoresInitSystem.cs index dfe2b19..13df7f0 100644 --- a/Assets/Scripts/ECS/Systems/Raid/PlayerTeamRelationScoresInitSystem.cs +++ b/Assets/Scripts/ECS/Systems/Raid/PlayerTeamRelationScoresInitSystem.cs @@ -6,14 +6,18 @@ namespace Assets.Scripts.ECS.Systems { public class PlayerTeamRelationScoresInitSystem : IEcsInitSystem { - private readonly EcsPoolInject> pool = default; + private readonly EcsPoolInject> pool = default; + private readonly EcsPoolInject> countPool = default; private readonly EcsFilterInject> filter = default; public void Init(IEcsSystems systems) { foreach (var entity in filter.Value) + { pool.Value.Add(entity); + countPool.Value.Add(entity); + } } } } \ No newline at end of file diff --git a/Assets/Scripts/Services/Battle/BattleManagementService+ECS.cs b/Assets/Scripts/Services/Battle/BattleManagementService+ECS.cs index ed3cbdd..67044d6 100644 --- a/Assets/Scripts/Services/Battle/BattleManagementService+ECS.cs +++ b/Assets/Scripts/Services/Battle/BattleManagementService+ECS.cs @@ -63,6 +63,13 @@ public void StartEcsContext() .Add(new BattleAssignTargetRelationEffectsSystem()) // if target is hero (not enemy) // scans for relations and casts effects // on active target + .Add(new BattleRelEffectProbeSystem()) //for all probes try cast a real effect + .Add(new BattlePrepareRevengeEffectSystem()) + .Add(new BattlePrepareTargetEffectSystem()) //TODO: don't forget to reset this effect at some point + .Add(new BattlePrepareRelEffectVisualSystem()) + .DelHere() + .DelHere>() //effects (if any were spawned) will survive + .Add(new BattleMarkTurnReadySystem()) // marks ready turns for autoplay .DelHere() .Add(new BattleAutoMakeTurnSystem())