Skip to content

Commit

Permalink
Criticals cleanup.
Browse files Browse the repository at this point in the history
Split Criticals into CriticalEffects and ComponentCriticals to remove Lazy<>.
Moved some criticals-related classes into own files.
Changed some logs from Trace to Debug.
  • Loading branch information
CptMoore committed Jul 1, 2023
1 parent 080937a commit 8d535ab
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 144 deletions.
56 changes: 56 additions & 0 deletions source/Features/CriticalEffects/ComponentCriticals.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using System;
using BattleTech;
using CustomComponents;

namespace MechEngineer.Features.CriticalEffects;

internal readonly struct ComponentCriticals
{
internal readonly MechComponent component;
private AbstractActor actor => component.parent;

internal ComponentCriticals(MechComponent component)
{
this.component = component;
if (actor == null)
{
throw new NullReferenceException($"{nameof(actor)} is null");
}
}

internal int ComponentHittableCount()
{
return ComponentHitMax() - ComponentHitCount();
}

internal int ComponentHitMax()
{
var inventorySize = component.componentDef.Is<CriticalChanceCustom>(out var chance) ? chance.Size : component.componentDef.InventorySize;
// TODO fix size correctly for location:
// introduce fake items for overflow location that is crit linked and overwrite component hit max for original + crit linked
var additionalSize = component.componentDef.Is<DynamicSlots.DynamicSlots>(out var slot) && slot.InnerAdjacentOnly ? slot.ReservedSlots : 0;
return inventorySize + additionalSize;
}

internal int ComponentHitCount(int? setHits = null)
{
var stat = component.StatCollection.MECriticalSlotsHit();
stat.CreateIfMissing(); // move to Mech.init and remove "CreateIfMissing" from StatAdapter
if (setHits.HasValue)
{
stat.SetValue(setHits.Value);
}
return stat.Get();
}

internal int ComponentHitArmoredCount(int? setHits = null)
{
var stat = component.StatCollection.MECriticalSlotsHitArmored();
stat.CreateIfMissing(); // move to Mech.init and remove "CreateIfMissing" from StatAdapter
if (setHits.HasValue)
{
stat.SetValue(setHits.Value);
}
return stat.Get();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,26 @@
using BattleTech;
using CustomComponents;
using FluffyUnderware.DevTools.Extensions;
using MechEngineer.Features.CriticalEffects.Patches;
using MechEngineer.Features.PlaceholderEffects;
using MechEngineer.Helper;
using UnityEngine;

namespace MechEngineer.Features.CriticalEffects;

internal class Criticals
internal readonly struct CriticalEffects
{
internal Criticals(MechComponent component)
{
this.component = component;
if (actor == null)
{
throw new NullReferenceException($"{nameof(actor)} is null");
}
private readonly ComponentCriticals _criticals;
private MechComponent component => _criticals.component;
private AbstractActor actor => component.parent;

ce = new(FetchCriticalEffects);
internal CriticalEffects(MechComponent component)
{
_criticals = new(component);
Effects = FetchCriticalEffects();
}

private readonly MechComponent component;
private AbstractActor actor => component.parent;

private bool IsLocationDestroyed => actor.StructureForLocation(component.Location) <= 0f;

internal CriticalEffectsCustom? Effects => ce.Value;
private readonly Lazy<CriticalEffectsCustom?> ce;
internal readonly CriticalEffectsCustom? Effects;
private bool HasLinked => Effects?.LinkedStatisticName != null;
private bool IsArmored => Effects?.IsArmored ?? false;
private CriticalEffectsCustom? FetchCriticalEffects()
Expand All @@ -43,10 +36,12 @@ internal Criticals(MechComponent component)
var unitTypes = UnitTypeDatabase.Instance.GetUnitTypes(mech.MechDef);
if (unitTypes is { Count: > 0 })
{
Log.Main.Trace?.Log($"Found mech.UnitTypes=[{string.Join(",", unitTypes)}] on ChassisID={mech.MechDef.ChassisID}");
// TODO move back to trace
Log.Main.Debug?.Log($"Found mech.UnitTypes=[{string.Join(",", unitTypes)}] on ChassisID={mech.MechDef.ChassisID}");
foreach (var custom in customs.OfType<MechCriticalEffectsCustom>())
{
Log.Main.Trace?.Log($"Found custom.UnitTypes=[{string.Join(",", custom.UnitTypes)}] on ComponentDefId={component.componentDef.Description.Id}");
// TODO move back to trace
Log.Main.Debug?.Log($"Found custom.UnitTypes=[{string.Join(",", custom.UnitTypes)}] on ComponentDefId={component.componentDef.Description.Id}");
if (custom.UnitTypes.All(t => unitTypes.Contains(t)))
{
return custom;
Expand Down Expand Up @@ -102,8 +97,8 @@ private void SetHits(WeaponHitInfo hitInfo, out ComponentDamageLevel damageLevel
{
int effectsMax, effectsPrev, effectsNext;
{
var compCritsMax = ComponentHitMax();
var compCritsPrev = ComponentHitCount();
var compCritsMax = _criticals.ComponentHitMax();
var compCritsPrev = _criticals.ComponentHitCount();

var possibleAddedHits = 1;
if (IsLocationDestroyed)
Expand All @@ -112,15 +107,15 @@ private void SetHits(WeaponHitInfo hitInfo, out ComponentDamageLevel damageLevel
}
else if (IsArmored)
{
var compCritsArmoredPrev = ComponentHitArmoredCount();
var compCritsArmoredPrev = _criticals.ComponentHitArmoredCount();
if (compCritsArmoredPrev < compCritsMax)
{
var randomCache = actor.Combat.AttackDirector.GetRandomFromCache(hitInfo, 1);
var isArmoredHit = compCritsArmoredPrev <= Mathf.RoundToInt(compCritsMax * randomCache[0]);
if (isArmoredHit)
{
var compCritsArmoredNext = compCritsArmoredPrev + 1;
ComponentHitArmoredCount(compCritsArmoredNext);
_criticals.ComponentHitArmoredCount(compCritsArmoredNext);
damageLevel = component.DamageLevel;
return;
}
Expand All @@ -130,7 +125,7 @@ private void SetHits(WeaponHitInfo hitInfo, out ComponentDamageLevel damageLevel
var compCritsNext = Mathf.Min(compCritsMax, compCritsPrev + possibleAddedHits);
var compCritsAdded = Mathf.Max(compCritsNext - compCritsPrev, 0);

ComponentHitCount(compCritsNext);
_criticals.ComponentHitCount(compCritsNext);

// if max is reached, component is destroyed and no new effects can be applied
// Destroyed components can still soak up crits, requires properly configured AIM from CAC
Expand Down Expand Up @@ -179,42 +174,6 @@ private int DefaultEffectsMax()
return 1;
}

public int ComponentHittableCount()
{
return ComponentHitMax() - ComponentHitCount();
}

private int ComponentHitMax()
{
var inventorySize = component.componentDef.Is<CriticalChanceCustom>(out var chance) ? chance.Size : component.componentDef.InventorySize;
// TODO fix size correctly for location:
// introduce fake items for overflow location that is crit linked and overwrite component hit max for original + crit linked
var additionalSize = component.componentDef.Is<DynamicSlots.DynamicSlots>(out var slot) && slot.InnerAdjacentOnly ? slot.ReservedSlots : 0;
return inventorySize + additionalSize;
}

private int ComponentHitCount(int? setHits = null)
{
var stat = component.StatCollection.MECriticalSlotsHit();
stat.CreateIfMissing(); // move to Mech.init and remove "CreateIfMissing" from StatAdapter
if (setHits.HasValue)
{
stat.SetValue(setHits.Value);
}
return stat.Get();
}

private int ComponentHitArmoredCount(int? setHits = null)
{
var stat = component.StatCollection.MECriticalSlotsHitArmored();
stat.CreateIfMissing(); // move to Mech.init and remove "CreateIfMissing" from StatAdapter
if (setHits.HasValue)
{
stat.SetValue(setHits.Value);
}
return stat.Get();
}

private int GroupHitCount(int? setHits = null)
{
if (actor == null)
Expand Down Expand Up @@ -280,7 +239,7 @@ private void SetDamageLevel(WeaponHitInfo hitInfo, ComponentDamageLevel damageLe
continue;
}

var otherCriticals = otherMechComponent.Criticals();
var otherCriticals = otherMechComponent.CriticalEffects();
if (!otherCriticals.HasLinked)
{
continue;
Expand Down Expand Up @@ -387,13 +346,13 @@ static HashSet<string> DisabledSimpleScopedEffectIdsOnActor(AbstractActor actor)
{
var iter = from mc in actor.allComponents
where !mc.IsFunctional
let ce = mc.Criticals().Effects
let ce = mc.CriticalEffects().Effects
where ce != null
from effectId in ce.OnDestroyedDisableEffectIds
select mc.
Criticals().ScopedId(effectId);
CriticalEffects().ScopedId(effectId);

return new HashSet<string>(iter);
return new(iter);
}
}

Expand Down Expand Up @@ -431,81 +390,4 @@ private void PostDestructionEvents(WeaponHitInfo hitInfo)
WwiseManager.PostEvent(Effects.OnDestroyedAudioEventName, actor.GameRep.audioObject);
}
}
}

internal class EffectIdUtil
{
private readonly string templateEffectId;
private readonly string resolvedEffectId;
private readonly MechComponent mechComponent;

internal EffectIdUtil(string templateEffectId, string resolvedEffectId, MechComponent mechComponent)
{
this.templateEffectId = templateEffectId;
this.resolvedEffectId = $"MECriticalHitEffect_{resolvedEffectId}_{mechComponent.parent.GUID}";
this.mechComponent = mechComponent;
}

internal static void CreateEffect(MechComponent component, EffectData effectData, string effectId)
{
var actor = component.parent;

Log.Main.Debug?.Log($"Creating id={effectId} statName={effectData.statisticData.statName}");

EffectManager_GetTargetStatCollections_Patch.SetContext(component, effectData);
try
{
actor.Combat.EffectManager.CreateEffect(effectData, effectId, -1, actor, actor, default, 0);
}
finally
{
EffectManager_GetTargetStatCollections_Patch.ClearContext();
}
}

internal void CreateCriticalEffect()
{
var effectData = CriticalEffectsFeature.GetEffectData(templateEffectId);
if (effectData == null)
{
return;
}
if (effectData.targetingData.effectTriggerType != EffectTriggerType.Passive) // we only support passive for now
{
Log.Main.Warning?.Log($"Effect templateEffectId={templateEffectId} is not passive");
return;
}

PlaceholderEffectsFeature.ProcessLocationalEffectData(ref effectData, mechComponent);

CreateEffect(mechComponent, effectData, resolvedEffectId);
}

internal void CancelCriticalEffect()
{
var actor = mechComponent.parent;
var statusEffects = actor.Combat.EffectManager
.GetAllEffectsWithID(resolvedEffectId)
.Where(e => e.Target == actor);

Log.Main.Debug?.Log($"Canceling id={resolvedEffectId}");
foreach (var statusEffect in statusEffects)
{
Log.Main.Debug?.Log($"Canceling statName={statusEffect.EffectData.statisticData.statName}");
actor.CancelEffect(statusEffect);
}
}
}

internal static class StatCollectionExtension
{
internal static StatisticAdapter<int> MECriticalSlotsHit(this StatCollection statCollection)
{
return new("MECriticalSlotsHit", statCollection, 0);
}

internal static StatisticAdapter<int> MECriticalSlotsHitArmored(this StatCollection statCollection)
{
return new("MECriticalSlotsHitArmored", statCollection, 0);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ namespace MechEngineer.Features.CriticalEffects;

public static class CriticalEffectsMechComponentExtensions
{
internal static Criticals Criticals(this MechComponent mechComponent)
internal static ComponentCriticals Criticals(this MechComponent mechComponent)
{
return new(mechComponent);
}

internal static CriticalEffects CriticalEffects(this MechComponent mechComponent)
{
return new(mechComponent);
}
Expand Down
70 changes: 70 additions & 0 deletions source/Features/CriticalEffects/EffectIdUtil.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System.Linq;
using BattleTech;
using MechEngineer.Features.CriticalEffects.Patches;
using MechEngineer.Features.PlaceholderEffects;

namespace MechEngineer.Features.CriticalEffects;

internal class EffectIdUtil
{
private readonly string templateEffectId;
private readonly string resolvedEffectId;
private readonly MechComponent mechComponent;

internal EffectIdUtil(string templateEffectId, string resolvedEffectId, MechComponent mechComponent)
{
this.templateEffectId = templateEffectId;
this.resolvedEffectId = $"MECriticalHitEffect_{resolvedEffectId}_{mechComponent.parent.GUID}";
this.mechComponent = mechComponent;
}

internal static void CreateEffect(MechComponent component, EffectData effectData, string effectId)
{
var actor = component.parent;

Log.Main.Debug?.Log($"Creating id={effectId} statName={effectData.statisticData.statName}");

EffectManager_GetTargetStatCollections_Patch.SetContext(component, effectData);
try
{
actor.Combat.EffectManager.CreateEffect(effectData, effectId, -1, actor, actor, default, 0);
}
finally
{
EffectManager_GetTargetStatCollections_Patch.ClearContext();
}
}

internal void CreateCriticalEffect()
{
var effectData = CriticalEffectsFeature.GetEffectData(templateEffectId);
if (effectData == null)
{
return;
}
if (effectData.targetingData.effectTriggerType != EffectTriggerType.Passive) // we only support passive for now
{
Log.Main.Warning?.Log($"Effect templateEffectId={templateEffectId} is not passive");
return;
}

PlaceholderEffectsFeature.ProcessLocationalEffectData(ref effectData, mechComponent);

CreateEffect(mechComponent, effectData, resolvedEffectId);
}

internal void CancelCriticalEffect()
{
var actor = mechComponent.parent;
var statusEffects = actor.Combat.EffectManager
.GetAllEffectsWithID(resolvedEffectId)
.Where(e => e.Target == actor);

Log.Main.Debug?.Log($"Canceling id={resolvedEffectId}");
foreach (var statusEffect in statusEffects)
{
Log.Main.Debug?.Log($"Canceling statName={statusEffect.EffectData.statisticData.statName}");
actor.CancelEffect(statusEffect);
}
}
}
Loading

0 comments on commit 8d535ab

Please sign in to comment.