diff --git a/1.4/Assemblies/CombatAI.dll b/1.4/Assemblies/CombatAI.dll index cad93bd..0a82ce5 100644 Binary files a/1.4/Assemblies/CombatAI.dll and b/1.4/Assemblies/CombatAI.dll differ diff --git a/Source/Rule56/CombatAI.csproj b/Source/Rule56/CombatAI.csproj index c7da079..6f73657 100644 --- a/Source/Rule56/CombatAI.csproj +++ b/Source/Rule56/CombatAI.csproj @@ -32,35 +32,35 @@ true - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + @@ -94,11 +94,11 @@ - - - - - + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -107,17 +107,17 @@ - + - + $(PkgMono_TextTransform)\tools\TextTransform.exe - - + + @@ -131,12 +131,12 @@ $(PubliciseOutputPath)UnityEngine.CoreModule_publicised.dll - - - - + + + + - + false @@ -153,10 +153,10 @@ - + - - + + @@ -166,7 +166,7 @@ - + diff --git a/Source/Rule56/CombatAIMod.cs b/Source/Rule56/CombatAIMod.cs index ed2a715..545d109 100644 --- a/Source/Rule56/CombatAIMod.cs +++ b/Source/Rule56/CombatAIMod.cs @@ -28,9 +28,9 @@ public class CombatAIMod : Mod private bool collapsibleGroupInited; public CombatAIMod(ModContentPack contentPack) : base(contentPack) { + Finder.Mod = this; Finder.Harmony = new Harmony("Krkr.Rule56"); Finder.Harmony.PatchAll(); - Finder.Mod = this; Finder.Settings = GetSettings(); if (Finder.Settings == null) { @@ -100,7 +100,7 @@ private void FillCollapsible_Basic(Listing_Collapsible collapsible) Finder.Settings.Enable_Sprinting = false; Finder.Settings.Enable_Groups = false; - Finder.Settings.Pathfinding_SappingMul = 0.5f; + Finder.Settings.Pathfinding_SappingMul = 1.5f; Finder.Settings.SightSettings_FriendliesAndRaiders.interval = 3; if (Current.ProgramState != ProgramState.Playing) @@ -135,7 +135,7 @@ private void FillCollapsible_Basic(Listing_Collapsible collapsible) Finder.Settings.Flank_Enabled = true; Finder.Settings.Enable_Sprinting = false; Finder.Settings.Enable_Groups = true; - Finder.Settings.Pathfinding_SappingMul = 1.0f; + Finder.Settings.Pathfinding_SappingMul = 1.3f; Finder.Settings.SightSettings_FriendliesAndRaiders.interval = 3; if (Current.ProgramState != ProgramState.Playing) { @@ -170,7 +170,7 @@ private void FillCollapsible_Basic(Listing_Collapsible collapsible) Finder.Settings.Enable_Sprinting = false; Finder.Settings.Enable_Groups = true; - Finder.Settings.Pathfinding_SappingMul = 0.9f; + Finder.Settings.Pathfinding_SappingMul = 1.0f; Finder.Settings.SightSettings_FriendliesAndRaiders.interval = 2; if (Current.ProgramState != ProgramState.Playing) @@ -207,7 +207,7 @@ private void FillCollapsible_Basic(Listing_Collapsible collapsible) Finder.Settings.Enable_Sprinting = true; Finder.Settings.Enable_Groups = true; - Finder.Settings.Pathfinding_SappingMul = 0.85f; + Finder.Settings.Pathfinding_SappingMul = 1.0f; Finder.Settings.SightSettings_FriendliesAndRaiders.interval = 1; if (Current.ProgramState != ProgramState.Playing) diff --git a/Source/Rule56/CombatAI_Utility.cs b/Source/Rule56/CombatAI_Utility.cs index 38acd5e..3de64ed 100644 --- a/Source/Rule56/CombatAI_Utility.cs +++ b/Source/Rule56/CombatAI_Utility.cs @@ -1,4 +1,5 @@ -using RimWorld; +using System.Collections.Generic; +using RimWorld; using UnityEngine; using Verse; using Verse.AI; diff --git a/Source/Rule56/Comps/ThingComp_CombatAI.cs b/Source/Rule56/Comps/ThingComp_CombatAI.cs index dee152b..bc06a0c 100644 --- a/Source/Rule56/Comps/ThingComp_CombatAI.cs +++ b/Source/Rule56/Comps/ThingComp_CombatAI.cs @@ -510,6 +510,11 @@ public void OnScanFinished() /// Damage info public void Notify_TookDamage(DamageInfo dInfo) { + // notify the custom duty manager that this pawn took damage. + if (duties != null) + { + duties.Notify_TookDamage(); + } // if the pawn is tanky enough skip. if (Finder.Settings.Retreat_Enabled && parent.Spawned && GenTicks.TicksGame - lastScanned < 90 && parent is Pawn pawn && !pawn.Dead && !pawn.Downed && armor.TankInt < 0.4f) { @@ -582,6 +587,12 @@ public void StartSapper(List blocked, IntVec3 cellBefore, bool findEsco sapperNodes.AddRange(blocked); _sap = 0; TryStartSapperJob(); +// if ((pawn.needs?.food?.CurCategory != HungerCategory.Fed) && pawn.Position.DistanceToSquared(cellBefore) < 13f) +// { +// List pawns = escorts.ToList(); +// pawns.Add(pawn); +// SuppliesUtility.FulfillFoodSupplies(pawns, pawn.Map); +// } } /// @@ -694,10 +705,10 @@ private void TryStartSapperJob() job.maxNumMeleeAttacks = 300; pawn.jobs.StopAll(); pawn.jobs.StartJob(job, JobCondition.InterruptForced); - if (findEscorts) + if (findEscorts && Rand.Chance(1 - Maths.Max(1f / (escorts.Count + 1f), 0.85f))) { int count = escorts.Count; - int countTarget = Rand.Int % 6 + 4 + Maths.Min(sapperNodes.Count, 10); + int countTarget = Rand.Int % 4 + 3 + Maths.Min(sapperNodes.Count, 10); Faction faction = pawn.Faction; Predicate validator = t => { @@ -708,16 +719,21 @@ private void TryStartSapperJob() && ally.skills?.GetSkill(SkillDefOf.Mining).Level < 10) { ThingComp_CombatAI comp = ally.GetComp_Fast(); - if (comp?.duties != null && comp.duties?.Any(DutyDefOf.Escort) == false && !comp.IsSapping && GenTicks.TicksGame - comp.releasedTick < 600) + if (comp?.duties != null && comp.duties?.Any(DutyDefOf.Escort) == false && !comp.IsSapping && GenTicks.TicksGame - comp.releasedTick > 600) { - Pawn_CustomDutyTracker.CustomPawnDuty custom = CustomDutyUtility.Escort(ally, pawn, 20, 100, 500 * sapperNodes.Count + Rand.Int % 1000); - if (custom != null) + Pawn_CustomDutyTracker.CustomPawnDuty custom = CustomDutyUtility.Escort(pawn, 20, 100, (500 * sapperNodes.Count) / (escorts.Count + 1) + Rand.Int % 500); + if (ally.TryStartCustomDuty(custom)) { - custom.duty.locomotion = Finder.Settings.Enable_Sprinting ? LocomotionUrgency.Sprint : LocomotionUrgency.Jog; - comp.duties.StartDuty(custom); escorts.Add(ally); } - count++; + if (comp.duties.curCustomDuty?.duty != duties.curCustomDuty?.duty) + { + count += 3; + } + else + { + count++; + } } return count == countTarget; } diff --git a/Source/Rule56/CustomDuties/CustomDutyUtility.cs b/Source/Rule56/CustomDuties/CustomDutyUtility.cs new file mode 100644 index 0000000..92e7278 --- /dev/null +++ b/Source/Rule56/CustomDuties/CustomDutyUtility.cs @@ -0,0 +1,102 @@ +using System.Runtime.CompilerServices; +using CombatAI.Comps; +using RimWorld; +using Verse; +using Verse.AI; +namespace CombatAI +{ + public static class CustomDutyUtility + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Pawn_CustomDutyTracker GetPawnCustomDutyTracker(this Pawn pawn) + { + return pawn.GetComp_Fast()?.duties ?? null; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool AnyCustomDuties(this Pawn pawn, DutyDef def) + { + return pawn.GetPawnCustomDutyTracker()?.Any(def) ?? false; + } + + public static bool TryStartCustomDuty(this Pawn pawn, Pawn_CustomDutyTracker.CustomPawnDuty duty, bool returnCurDutyToQueue = true) + { + ThingComp_CombatAI comp = pawn.GetComp_Fast(); + if (comp == null) + { + return false; + } + comp.duties.StartDuty(duty, returnCurDutyToQueue: returnCurDutyToQueue); + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void EnqueueFirstCustomDuty(this Pawn pawn, Pawn_CustomDutyTracker.CustomPawnDuty duty) + { + ThingComp_CombatAI comp = pawn.GetComp_Fast(); + if (comp?.duties == null) + { + comp.duties.EnqueueFirst(duty); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void EnqueueCustomDuty(this Pawn pawn, Pawn_CustomDutyTracker.CustomPawnDuty duty) + { + ThingComp_CombatAI comp = pawn.GetComp_Fast(); + if (comp?.duties == null) + { + comp.duties.Enqueue(duty); + } + } + + public static Pawn_CustomDutyTracker.CustomPawnDuty Escort(Pawn escortee, int radius = -1, int failOnDist = 0, int expireAfter = 0, int startAfter = 0, bool failOnFocusDowned = true, DutyDef failOnFocusDutyNot = null) + { + Pawn_CustomDutyTracker.CustomPawnDuty custom = new Pawn_CustomDutyTracker.CustomPawnDuty + { + duty = new PawnDuty(DutyDefOf.Escort, escortee, radius) + { + locomotion = LocomotionUrgency.Sprint + }, + endOnDistToFocusLarger = failOnDist, + expireAfter = expireAfter, + startAfter = startAfter, + endOnFocusDowned = failOnFocusDowned, + endOnFocusDutyNot = failOnFocusDutyNot, + endOnFocusDestroyed = true, + endOnFocusDeath = true + }; + return custom; + } + + public static Pawn_CustomDutyTracker.CustomPawnDuty AssaultPoint(IntVec3 dest, int switchAssaultRadius = 15, int expireAfter = 0, int startAfter = 0) + { + Pawn_CustomDutyTracker.CustomPawnDuty custom = new Pawn_CustomDutyTracker.CustomPawnDuty + { + duty = new PawnDuty(DutyDefOf.Defend, dest, switchAssaultRadius) + { + locomotion = LocomotionUrgency.Sprint + }, + endOnDistToFocusLess = switchAssaultRadius, + expireAfter = expireAfter, + startAfter = startAfter, + }; + return custom; + } + + public static Pawn_CustomDutyTracker.CustomPawnDuty DefendPoint(IntVec3 dest, int radius, bool endOnTookDamage, int expireAfter, int startAfter = 0) + { + Pawn_CustomDutyTracker.CustomPawnDuty custom = new Pawn_CustomDutyTracker.CustomPawnDuty + { + duty = new PawnDuty(DutyDefOf.Defend, dest, radius) + { + locomotion = LocomotionUrgency.Sprint + }, + expireAfter = expireAfter, + startAfter = startAfter, + endOnTookDamage = endOnTookDamage + }; + return custom; + } + } +} diff --git a/Source/Rule56/CustomDuties/Pawn_CustomDutyTracker.CustomPawnDuty.cs b/Source/Rule56/CustomDuties/Pawn_CustomDutyTracker.CustomPawnDuty.cs new file mode 100644 index 0000000..c7ed44c --- /dev/null +++ b/Source/Rule56/CustomDuties/Pawn_CustomDutyTracker.CustomPawnDuty.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using RimWorld; +using Verse; +using Verse.AI; +namespace CombatAI +{ + public partial class Pawn_CustomDutyTracker + { + public class CustomPawnDuty : IExposable + { + public bool finished; + public int startAfter; + public int startsAt = -1; + public bool canExitMap = true; + public bool canFlee = true; + public int expireAfter; + public int expiresAt = -1; + public int endOnDistToFocusLess; + public int endOnDistToFocusLarger; + public bool endOnFocusDeath; + public bool endOnFocusDestroyed; + public bool endOnFocusDowned; + public bool endOnTookDamage; + public PawnDuty duty; + public DutyDef endOnFocusDutyNot; + + public void ExposeData() + { + Scribe_Deep.Look(ref duty, "duty"); + Scribe_Values.Look(ref finished, "finished"); + Scribe_Values.Look(ref expireAfter, "expireAfter"); + Scribe_Values.Look(ref startAfter, "startAfter"); + Scribe_Values.Look(ref startsAt, "startsAt", -1); + Scribe_Values.Look(ref expiresAt, "expiresAt", -1); + Scribe_Values.Look(ref endOnDistToFocusLarger, "failOnDistanceToFocus"); + Scribe_Values.Look(ref endOnDistToFocusLess, "endOnDistanceToFocus"); + Scribe_Values.Look(ref endOnFocusDeath, "failOnFocusDeath"); + Scribe_Values.Look(ref endOnFocusDowned, "failOnFocusDowned"); + Scribe_Values.Look(ref endOnFocusDestroyed, "failOnFocusDestroyed"); + Scribe_Values.Look(ref canFlee, "canFlee", true); + Scribe_Values.Look(ref endOnTookDamage, "endOnTookDamage", false); + Scribe_Values.Look(ref canExitMap, "canExitMap", true); + Scribe_Defs.Look(ref endOnFocusDutyNot, "failOnFocusDutyNot"); + } + } + } +} diff --git a/Source/Rule56/Pawn_CustomDutyTracker.cs b/Source/Rule56/CustomDuties/Pawn_CustomDutyTracker.cs similarity index 68% rename from Source/Rule56/Pawn_CustomDutyTracker.cs rename to Source/Rule56/CustomDuties/Pawn_CustomDutyTracker.cs index 20f2dd2..ba3e524 100644 --- a/Source/Rule56/Pawn_CustomDutyTracker.cs +++ b/Source/Rule56/CustomDuties/Pawn_CustomDutyTracker.cs @@ -4,10 +4,9 @@ using Verse.AI; namespace CombatAI { - public class Pawn_CustomDutyTracker : IExposable + public partial class Pawn_CustomDutyTracker : IExposable { - public CustomPawnDuty curCustomDuty; - + public CustomPawnDuty curCustomDuty; public Pawn pawn; public List queue = new List(); @@ -50,20 +49,20 @@ public void TickRare() curCustomDuty = null; pawn.mindState.duty = null; } - else if (curCustomDuty.failOnDistanceToFocus > 0 && curCustomDuty.duty.focus.Cell.IsValid && curCustomDuty.duty.focus.Cell.DistanceToSquared(pawn.Position) > Maths.Sqr(curCustomDuty.failOnDistanceToFocus)) + else if (curCustomDuty.endOnDistToFocusLarger > 0 && curCustomDuty.duty.focus.Cell.IsValid && curCustomDuty.duty.focus.Cell.DistanceToSquared(pawn.Position) > Maths.Sqr(curCustomDuty.endOnDistToFocusLarger)) { curCustomDuty = null; pawn.mindState.duty = null; } - else if (curCustomDuty.endOnDistanceToFocus > 0 && curCustomDuty.duty.focus.Cell.IsValid && curCustomDuty.duty.focus.Cell.DistanceToSquared(pawn.Position) < Maths.Sqr(curCustomDuty.endOnDistanceToFocus)) + else if (curCustomDuty.endOnDistToFocusLess > 0 && curCustomDuty.duty.focus.Cell.IsValid && curCustomDuty.duty.focus.Cell.DistanceToSquared(pawn.Position) < Maths.Sqr(curCustomDuty.endOnDistToFocusLess)) { curCustomDuty = null; pawn.mindState.duty = null; } - else if (curCustomDuty?.duty.focus.Thing != null) + else if (curCustomDuty.duty.focus.Thing != null) { Thing focus = curCustomDuty.duty.focus.Thing; - if (curCustomDuty.failOnFocusDestroyed && (focus.Destroyed || !focus.Spawned)) + if (curCustomDuty.endOnFocusDestroyed && (focus.Destroyed || !focus.Spawned)) { curCustomDuty = null; pawn.mindState.duty = null; @@ -74,7 +73,7 @@ public void TickRare() curCustomDuty = null; pawn.mindState.duty = null; } - else if (curCustomDuty.failOnFocusDeath || curCustomDuty.failOnFocusDowned) + else if (curCustomDuty.endOnFocusDeath || curCustomDuty.endOnFocusDowned) { Pawn fpawn = focus as Pawn; if (fpawn == null) @@ -82,17 +81,17 @@ public void TickRare() curCustomDuty = null; pawn.mindState.duty = null; } - else if ((curCustomDuty.failOnFocusDowned || curCustomDuty.failOnFocusDeath) && fpawn.Dead) + else if ((curCustomDuty.endOnFocusDowned || curCustomDuty.endOnFocusDeath) && fpawn.Dead) { curCustomDuty = null; pawn.mindState.duty = null; } - else if (curCustomDuty.failOnFocusDowned && fpawn.Downed) + else if (curCustomDuty.endOnFocusDowned && fpawn.Downed) { curCustomDuty = null; pawn.mindState.duty = null; } - else if (curCustomDuty.failOnFocusDutyNot != null && fpawn.mindState?.duty?.def != curCustomDuty.failOnFocusDutyNot) + else if (curCustomDuty.endOnFocusDutyNot != null && fpawn.mindState?.duty?.def != curCustomDuty.endOnFocusDutyNot) { curCustomDuty = null; pawn.mindState.duty = null; @@ -217,40 +216,11 @@ private bool IsForcedDuty(DutyDef def) return def != null && (IsExitDuty(def) || def == DutyDefOf.PrisonerEscape || def == DutyDefOf.PrisonerEscapeSapper || def == DutyDefOf.PrisonerAssaultColony || def == DutyDefOf.Kidnap || def == DutyDefOf.Steal); } - public class CustomPawnDuty : IExposable + public void Notify_TookDamage() { - public bool canExitMap = true; - public bool canFlee = true; - public IntVec3 dest = IntVec3.Invalid; - public PawnDuty duty; - public int endOnDistanceToFocus; - public int expireAfter; - public int expiresAt = -1; - public int failOnDistanceToFocus; - public bool failOnFocusDeath; - public bool failOnFocusDestroyed; - public bool failOnFocusDowned; - public DutyDef failOnFocusDutyNot; - public bool finished; - public int startAfter; - public int startsAt = -1; - - public void ExposeData() + if (curCustomDuty is { endOnTookDamage: true }) { - Scribe_Deep.Look(ref duty, "duty"); - Scribe_Values.Look(ref expireAfter, "expireAfter"); - Scribe_Values.Look(ref startAfter, "startAfter"); - Scribe_Values.Look(ref startsAt, "startsAt", -1); - Scribe_Values.Look(ref expiresAt, "expiresAt", -1); - Scribe_Values.Look(ref dest, "endNear", IntVec3.Invalid); - Scribe_Values.Look(ref failOnDistanceToFocus, "failOnDistanceToFocus"); - Scribe_Values.Look(ref endOnDistanceToFocus, "endOnDistanceToFocus"); - Scribe_Values.Look(ref failOnFocusDeath, "failOnFocusDeath"); - Scribe_Values.Look(ref failOnFocusDowned, "failOnFocusDowned"); - Scribe_Values.Look(ref failOnFocusDestroyed, "failOnFocusDestroyed"); - Scribe_Values.Look(ref canFlee, "canFlee", true); - Scribe_Values.Look(ref canExitMap, "canExitMap", true); - Scribe_Defs.Look(ref failOnFocusDutyNot, "failOnFocusDutyNot"); + curCustomDuty = null; } } } diff --git a/Source/Rule56/CustomDutyUtility.cs b/Source/Rule56/CustomDutyUtility.cs deleted file mode 100644 index 9bd8a58..0000000 --- a/Source/Rule56/CustomDutyUtility.cs +++ /dev/null @@ -1,66 +0,0 @@ -using CombatAI.Comps; -using RimWorld; -using Verse; -using Verse.AI; -namespace CombatAI -{ - public static class CustomDutyUtility - { - public static Pawn_CustomDutyTracker.CustomPawnDuty Escort(Pawn escorter, Pawn escortee, int radius = -1, int failOnDist = 0, int expireAfter = 0, int startAfter = 0, bool failOnFocusDowned = true, DutyDef failOnFocusDutyNot = null) - { - if (!escorter.CanReach(escortee, PathEndMode.InteractionCell, Danger.Unspecified)) - { - return null; - } - ThingComp_CombatAI compee = escortee.GetComp_Fast(); - if (compee == null) - { - return null; - } - Pawn_CustomDutyTracker tracker = compee.duties; - if (tracker == null) - { - return null; - } - Pawn_CustomDutyTracker.CustomPawnDuty custom = new Pawn_CustomDutyTracker.CustomPawnDuty(); - custom.duty = new PawnDuty(DutyDefOf.Escort, escortee, radius); - custom.duty.locomotion = LocomotionUrgency.Sprint; - custom.failOnDistanceToFocus = failOnDist; - custom.expireAfter = expireAfter; - custom.startAfter = startAfter; - custom.failOnFocusDowned = failOnFocusDowned; - custom.failOnFocusDutyNot = failOnFocusDutyNot; - custom.failOnFocusDestroyed = true; - custom.failOnFocusDeath = true; - return custom; - } - - public static Pawn_CustomDutyTracker.CustomPawnDuty AssaultPoint(Pawn pawn, IntVec3 dest, int switchAssaultRadius = 15, int expireAfter = 0, int startAfter = 0) - { - //if (!pawn.CanReach(dest, PathEndMode.InteractionCell, Danger.Unspecified, true, true, TraverseMode.PassAllDestroyableThingsNotWater)) - //{ - // return null; - //} - ThingComp_CombatAI comp = pawn.GetComp_Fast(); - if (comp == null) - { - return null; - } - Pawn_CustomDutyTracker tracker = comp.duties; - if (tracker == null) - { - return null; - } - Pawn_CustomDutyTracker.CustomPawnDuty custom = new Pawn_CustomDutyTracker.CustomPawnDuty(); - custom.duty = new PawnDuty(DutyDefOf.Defend, dest, switchAssaultRadius); - custom.duty.locomotion = LocomotionUrgency.Sprint; - custom.dest = dest; - custom.endOnDistanceToFocus = switchAssaultRadius; - custom.expireAfter = expireAfter; - custom.startAfter = startAfter; - custom.failOnFocusDestroyed = false; - custom.failOnFocusDeath = false; - return custom; - } - } -} diff --git a/Source/Rule56/IBuckets.cs b/Source/Rule56/IBuckets.cs index e81fa97..a221886 100644 --- a/Source/Rule56/IBuckets.cs +++ b/Source/Rule56/IBuckets.cs @@ -3,12 +3,9 @@ namespace CombatAI { public class IBuckets where T : IBucketable { - private readonly Dictionary bucketIndexByIds = new Dictionary(); - - private readonly List[] buckets; - - public readonly int numBuckets; + private readonly List[] buckets; + public readonly int numBuckets; public IBuckets(int numBuckets) { diff --git a/Source/Rule56/ITSignalGrid.cs b/Source/Rule56/ITSignalGrid.cs index 9f55659..321a4c0 100644 --- a/Source/Rule56/ITSignalGrid.cs +++ b/Source/Rule56/ITSignalGrid.cs @@ -22,6 +22,7 @@ public class ITSignalGrid private readonly IField[] cells_meta; private readonly IField[] cells_sharp; private readonly IField[] cells_strength; + private readonly IField[] cells_aiming; private readonly CellIndices indices; @@ -29,6 +30,7 @@ public class ITSignalGrid private float curBlunt; private MetaCombatAttribute curMeta; private float curSharp; + private byte curAimAvailablity; private short r_sig = 19; @@ -45,6 +47,7 @@ public ITSignalGrid(Map map) cells_blunt = new IField[NumGridCells]; cells_sharp = new IField[NumGridCells]; cells_meta = new IField[NumGridCells]; + cells_aiming = new IField[NumGridCells]; } public short CycleNum @@ -73,6 +76,8 @@ public void Set(int index, float signalStrength, Vector2 dir) cells_meta[index].value |= curMeta; cells_sharp[index].value = Maths.Max(curSharp, cells_sharp[index].value); cells_blunt[index].value = Maths.Max(curBlunt, cells_blunt[index].value); + cells_aiming[index].value += curAimAvailablity; +// cells_aiming[index].value } else { @@ -92,6 +97,7 @@ public void Set(int index, float signalStrength, Vector2 dir) cells_meta[index].ReSet(curMeta, expired); cells_sharp[index].ReSet(curSharp, expired); cells_blunt[index].ReSet(curBlunt, expired); + cells_aiming[index].ReSet(curAimAvailablity, expired); info.cycle = CycleNum; } info.sig = r_sig; @@ -120,6 +126,7 @@ public void Set(int index, float signalStrength, Vector2 dir, MetaCombatAttribut cells_meta[index].value |= metaAttributes; cells_sharp[index].value = Maths.Max(curSharp, cells_sharp[index].value); cells_blunt[index].value = Maths.Max(curBlunt, cells_blunt[index].value); + cells_aiming[index].value += curAimAvailablity; } else { @@ -139,6 +146,7 @@ public void Set(int index, float signalStrength, Vector2 dir, MetaCombatAttribut cells_meta[index].ReSet(metaAttributes, expired); cells_sharp[index].ReSet(curSharp, expired); cells_blunt[index].ReSet(curBlunt, expired); + cells_aiming[index].ReSet(curAimAvailablity, expired); info.cycle = CycleNum; } info.sig = r_sig; @@ -161,9 +169,10 @@ public void Set(int index, MetaCombatAttribute metaAttributes) int dc = CycleNum - info.cycle; if (dc == 0) { - cells_meta[index].value |= metaAttributes; - cells_sharp[index].value = Maths.Max(curSharp, cells_sharp[index].value); - cells_blunt[index].value = Maths.Max(curBlunt, cells_blunt[index].value); + cells_meta[index].value |= metaAttributes; + cells_sharp[index].value = Maths.Max(curSharp, cells_sharp[index].value); + cells_blunt[index].value = Maths.Max(curBlunt, cells_blunt[index].value); + cells_aiming[index].value += curAimAvailablity; } else { @@ -183,6 +192,7 @@ public void Set(int index, MetaCombatAttribute metaAttributes) cells_meta[index].ReSet(metaAttributes, expired); cells_sharp[index].ReSet(curSharp, expired); cells_blunt[index].ReSet(curBlunt, expired); + cells_aiming[index].ReSet(curAimAvailablity, expired); info.cycle = CycleNum; } info.sig = r_sig; @@ -205,9 +215,10 @@ public void Set(int index, ulong flags) int dc = CycleNum - info.cycle; if (dc == 0) { - cells_flags[index].value |= flags; - cells_sharp[index].value = Maths.Max(curSharp, cells_sharp[index].value); - cells_blunt[index].value = Maths.Max(curBlunt, cells_blunt[index].value); + cells_flags[index].value |= flags; + cells_sharp[index].value = Maths.Max(curSharp, cells_sharp[index].value); + cells_blunt[index].value = Maths.Max(curBlunt, cells_blunt[index].value); + cells_aiming[index].value += curAimAvailablity; } else { @@ -227,6 +238,7 @@ public void Set(int index, ulong flags) cells_meta[index].ReSet(curMeta, expired); cells_sharp[index].ReSet(curSharp, expired); cells_blunt[index].ReSet(curBlunt, expired); + cells_aiming[index].ReSet(curAimAvailablity, expired); info.cycle = CycleNum; } info.sig = r_sig; @@ -256,6 +268,7 @@ public void Set(int index, float signalStrength, Vector2 dir, ulong flags) cells_meta[index].value |= curMeta; cells_sharp[index].value = Maths.Max(curSharp, cells_sharp[index].value); cells_blunt[index].value = Maths.Max(curBlunt, cells_blunt[index].value); + cells_aiming[index].value += curAimAvailablity; } else { @@ -275,6 +288,7 @@ public void Set(int index, float signalStrength, Vector2 dir, ulong flags) cells_meta[index].ReSet(curMeta, expired); cells_sharp[index].ReSet(curSharp, expired); cells_blunt[index].ReSet(curBlunt, expired); + cells_aiming[index].ReSet(curAimAvailablity, expired); info.cycle = CycleNum; } info.sig = r_sig; @@ -466,6 +480,28 @@ public float GetBlunt(int index) return 0; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int GetAvailability(IntVec3 cell) + { + return GetAvailability(indices.CellToIndex(cell)); + } + public int GetAvailability(int index) + { + if (index >= 0 && index < NumGridCells) + { + IFieldInfo cell = cells[index]; + switch (CycleNum - cell.cycle) + { + case 0: + IField availability = cells_aiming[index]; + return Maths.Max(availability.value, availability.valuePrev); + case 1: + return cells_aiming[index].value; + } + } + return 0; + } + public Vector2 GetSignalDirectionAt(IntVec3 cell) { return GetSignalDirectionAt(indices.CellToIndex(cell)); @@ -505,6 +541,14 @@ public void Next(float sharp, float blunt, MetaCombatAttribute meta) curSharp = sharp; curBlunt = blunt; curMeta = meta; + if ((meta & MetaCombatAttribute.Free) != 0) + { + curAimAvailablity = 1; + } + else + { + curAimAvailablity = 0; + } } /// diff --git a/Source/Rule56/MetaCombatAttribute.cs b/Source/Rule56/MetaCombatAttribute.cs index 73c2cf4..f1ccd71 100644 --- a/Source/Rule56/MetaCombatAttribute.cs +++ b/Source/Rule56/MetaCombatAttribute.cs @@ -11,6 +11,8 @@ public enum MetaCombatAttribute Fire = 32, Gas = 64, Explosives = 128, - Mortar = 256 + Mortar = 256, + Free = 512, + Occupied = 1024, } } diff --git a/Source/Rule56/Patches/LordToil_AssaultColony_Patch.cs b/Source/Rule56/Patches/LordToil_AssaultColony_Patch.cs index 0364cb2..c68ba80 100644 --- a/Source/Rule56/Patches/LordToil_AssaultColony_Patch.cs +++ b/Source/Rule56/Patches/LordToil_AssaultColony_Patch.cs @@ -64,11 +64,16 @@ public static void Postfix(LordToil_AssaultColony __instance) things.AddRange(map.listerThings.ThingsInGroup(ThingRequestGroup.Bed).Where(b => b is Building_Bed bed && bed.CompAssignableToPawn.AssignedPawns.Any(p => p.Faction == map.ParentFaction))); things.AddRange(map.listerThings.ThingsInGroup(ThingRequestGroup.ResearchBench).Where(t => t.Faction == map.ParentFaction)); things.AddRange(map.listerThings.ThingsInGroup(ThingRequestGroup.FoodDispenser).Where(t => t.Faction == map.ParentFaction)); - things.AddRange(map.mapPawns.PrisonersOfColony); + things.AddRange(map.mapPawns.PrisonersOfColonySpawned); if (ModsConfig.BiotechActive) { + things.AddRange(map.listerThings.ThingsInGroup(ThingRequestGroup.MechCharger).Where(t => t.Faction == map.ParentFaction)); things.AddRange(map.listerThings.ThingsInGroup(ThingRequestGroup.GenepackHolder)); } + if (ModsConfig.IdeologyActive) + { + things.AddRange(map.mapPawns.SlavesOfColonySpawned); + } if (ModsConfig.RoyaltyActive) { things.AddRange(map.listerThings.ThingsInGroup(ThingRequestGroup.Throne)); @@ -102,11 +107,22 @@ public static void Postfix(LordToil_AssaultColony __instance) ThingComp_CombatAI comp = force[j].GetComp_Fast(); if (comp != null && !comp.duties.Any(DutyDefOf.Defend)) { - Pawn_CustomDutyTracker.CustomPawnDuty customDuty = CustomDutyUtility.AssaultPoint(force[j], zone.Position, Rand.Range(7, 15), 3600 * Rand.Range(3, 8)); - comp.duties.StartDuty(customDuty); - if (Finder.Settings.Debug) + Pawn_CustomDutyTracker.CustomPawnDuty customDuty = CustomDutyUtility.AssaultPoint(zone.Position, Rand.Range(7, 15), 3600 * Rand.Range(3, 8)); + if(force[j].TryStartCustomDuty(customDuty)) { - Log.Message($"{comp.parent} task force {i} attacking {zone}"); + if (Finder.Settings.Debug) + { + Log.Message($"{comp.parent} task force {i} attacking {zone}"); + } + Pawn_CustomDutyTracker.CustomPawnDuty customDuty2 = CustomDutyUtility.DefendPoint(zone.Position, (int)Rand.Range(30, 60), true, 3600 + Rand.Range(0, 3600)); + if (Rand.Chance(0.33f)) + { + force[j].EnqueueFirstCustomDuty(customDuty); + if (Finder.Settings.Debug) + { + Log.Message($"{comp.parent} task force {i} occupying area around {zone}"); + } + } } } } @@ -121,11 +137,22 @@ public static void Postfix(LordToil_AssaultColony __instance) ThingComp_CombatAI comp = force[j].GetComp_Fast(); if (comp != null && !comp.duties.Any(DutyDefOf.Defend)) { - Pawn_CustomDutyTracker.CustomPawnDuty customDuty = CustomDutyUtility.AssaultPoint(force[j], thing.Position, Rand.Range(7, 15), 3600 * Rand.Range(3, 8)); - comp.duties.StartDuty(customDuty); - if (Finder.Settings.Debug) + Pawn_CustomDutyTracker.CustomPawnDuty customDuty = CustomDutyUtility.AssaultPoint(thing.Position, Rand.Range(7, 15), 3600 * Rand.Range(3, 8)); + if (force[j].TryStartCustomDuty(customDuty)) { - Log.Message($"{comp.parent} task force {i} attacking {thing}"); + if (Finder.Settings.Debug) + { + Log.Message($"{comp.parent} task force {i} attacking {thing}"); + } + Pawn_CustomDutyTracker.CustomPawnDuty customDuty2 = CustomDutyUtility.DefendPoint(thing.Position, (int)Rand.Range(30, 60), true, 3600 + Rand.Range(0, 3600)); + if (Rand.Chance(0.33f)) + { + force[j].EnqueueFirstCustomDuty(customDuty); + if (Finder.Settings.Debug) + { + Log.Message($"{comp.parent} task force {i} occupying area around {thing}"); + } + } } } } diff --git a/Source/Rule56/Patches/Trigger_FractionColonyDamageTaken_Patch.cs b/Source/Rule56/Patches/Trigger_FractionColonyDamageTaken_Patch.cs index 2539c3c..12f9a3c 100644 --- a/Source/Rule56/Patches/Trigger_FractionColonyDamageTaken_Patch.cs +++ b/Source/Rule56/Patches/Trigger_FractionColonyDamageTaken_Patch.cs @@ -1,4 +1,5 @@ -using HarmonyLib; +using System.Data.SqlTypes; +using HarmonyLib; using RimWorld; using Verse; namespace CombatAI.Patches @@ -10,8 +11,20 @@ private static class Trigger_FractionColonyDamageTaken_Constructor { public static void Prefix(ref float desiredColonyDamageFraction, ref float minDamage) { - minDamage *= 10f; - desiredColonyDamageFraction = Maths.Max(Rand.Range(0.25f, 0.80f), desiredColonyDamageFraction); + minDamage *= 15f; + desiredColonyDamageFraction = Maths.Max(Rand.Range(0.25f, 5.0f), desiredColonyDamageFraction); + } + } + + [HarmonyPatch(typeof(Trigger_FractionColonyDamageTaken), nameof(Trigger_FractionColonyDamageTaken.ActivateOn))] + private static class Trigger_FractionColonyDamageTaken_ActivateOn + { + public static void Postfix(ref bool __result) + { + if (__result && !Rand.Chance(0.0001f)) + { + __result = false; + } } } } diff --git a/Source/Rule56/Settings.cs b/Source/Rule56/Settings.cs index 5499273..d451240 100644 --- a/Source/Rule56/Settings.cs +++ b/Source/Rule56/Settings.cs @@ -125,13 +125,13 @@ public override void ExposeData() Scribe_Values.Look(ref Debug, $"Debug.{version}"); Scribe_Values.Look(ref Debug_ValidateSight, $"Debug_ValidateSight.{version}"); Scribe_Values.Look(ref Debug_DrawShadowCasts, $"Debug_DrawShadowCasts.{version}"); - Scribe_Values.Look(ref Enable_Sprinting, "Enable_Sprinting", true); - Scribe_Values.Look(ref Enable_Groups, "Enable_Groups", true); - Scribe_Values.Look(ref FogOfWar_Turrets, "FogOfWar_Turrets", true); - Scribe_Values.Look(ref FogOfWar_Animals, "FogOfWar_Animals", true); - Scribe_Values.Look(ref FogOfWar_AnimalsSmartOnly, "FogOfWar_AnimalsSmartOnly", true); - Scribe_Values.Look(ref FogOfWar_Allies, "FogOfWar_Allies", true); - Scribe_Values.Look(ref Pathfinding_SappingMul, "Pathfinding_SappingMul", 1.0f); + Scribe_Values.Look(ref Enable_Sprinting, $"Enable_Sprinting.{version}", true); + Scribe_Values.Look(ref Enable_Groups, $"Enable_Groups.{version}", true); + Scribe_Values.Look(ref FogOfWar_Turrets, $"FogOfWar_Turrets.{version}", true); + Scribe_Values.Look(ref FogOfWar_Animals, $"FogOfWar_Animals.{version}", true); + Scribe_Values.Look(ref FogOfWar_AnimalsSmartOnly, $"FogOfWar_AnimalsSmartOnly.{version}", true); + Scribe_Values.Look(ref FogOfWar_Allies, $"FogOfWar_Allies.{version}", true); + Scribe_Values.Look(ref Pathfinding_SappingMul, $"Pathfinding_SappingMul2.{version}", 1.0f); //ScribeValues(); // Scribe values. (Will not scribe IExposables nor enums) } diff --git a/Source/Rule56/SightGrid.cs b/Source/Rule56/SightGrid.cs index 0ce302a..692aa22 100644 --- a/Source/Rule56/SightGrid.cs +++ b/Source/Rule56/SightGrid.cs @@ -365,7 +365,23 @@ private bool TryCastSight(IBucketableThing item) ops += 1; suCentroid += pos; } - ISightRadius sightRadius = item.cachedSightRadius; + MetaCombatAttribute availability = 0; + if (item.thing != null) + { + Verb verb = item.thing.TryGetAttackVerb(); + if (!verb.IsMeleeAttack) + { + if (verb.state != VerbState.Idle || verb.WarmingUp) + { + availability = MetaCombatAttribute.Occupied; + } + else + { + availability = MetaCombatAttribute.Free; + } + } + } + ISightRadius sightRadius = item.cachedSightRadius; Action action = () => { if (playerAlliance && sightRadius.fog > 0) @@ -377,8 +393,8 @@ private bool TryCastSight(IBucketableThing item) gridFog.Set(item.path[i], 1.0f); } } - grid.Next(item.cachedDamage.adjustedSharp, item.cachedDamage.adjustedBlunt, item.cachedDamage.attributes); - grid_regions.Next(GetFlags(item), item.cachedDamage.adjustedSharp, item.cachedDamage.adjustedBlunt, item.cachedDamage.attributes); + grid.Next(item.cachedDamage.adjustedSharp, item.cachedDamage.adjustedBlunt, item.cachedDamage.attributes | availability); + grid_regions.Next(GetFlags(item), item.cachedDamage.adjustedSharp, item.cachedDamage.adjustedBlunt, item.cachedDamage.attributes | availability); float r_fade = sightRadius.fog * Finder.Settings.FogOfWar_RangeFadeMultiplier; float d_fade = sightRadius.fog - r_fade; float rSqr_sight = Maths.Sqr(sightRadius.sight); diff --git a/Source/Rule56/SightTracker.cs b/Source/Rule56/SightTracker.cs index bb23238..f5c4300 100644 --- a/Source/Rule56/SightTracker.cs +++ b/Source/Rule56/SightTracker.cs @@ -107,7 +107,7 @@ public override void MapComponentTick() float value = reader.GetThreat(cell); if (value > 0) { - map.debugDrawer.FlashCell(cell, Mathf.Clamp01(value / 4f), $"{Math.Round(value, 2)}", 15); + map.debugDrawer.FlashCell(cell, Mathf.Clamp01(value / 20f), $"{Math.Round(value, 2)}", 15); } //var value = reader.hostiles[0].GetSharp(cell) + reader.hostiles[0].GetBlunt(cell); //if (value > 0) @@ -564,19 +564,25 @@ public float GetThreat(IntVec3 cell) } public float GetThreat(int index) { - if (armor.weaknessAttributes != MetaCombatAttribute.None) + MetaCombatAttribute attributes = GetMetaAttributes(index); + float val; + if ((attributes & armor.weaknessAttributes) != MetaCombatAttribute.None) { - MetaCombatAttribute attributes = GetMetaAttributes(index); - if ((attributes & armor.weaknessAttributes) != MetaCombatAttribute.None) - { - return 2.0f; - } + val = 2.0f; } - if (!Mod_CE.active) + else { - return armor.createdAt != 0 ? Mathf.Clamp01(2f * Maths.Max(GetBlunt(index) / (armor.Blunt + 0.001f), GetSharp(index) / (armor.Sharp + 0.001f), 0f)) : 0f; + if (!Mod_CE.active) + { + val = armor.createdAt != 0 ? Mathf.Clamp01(2f * Maths.Max(GetBlunt(index) / (armor.Blunt + 0.001f), GetSharp(index) / (armor.Sharp + 0.001f), 0f)) : 0f; + } + else + { + val = armor.createdAt != 0 ? Mathf.Clamp01(Maths.Max(GetBlunt(index) / (armor.Blunt + 0.001f), GetSharp(index) / (armor.Sharp + 0.001f), 0f)) : 0f; + } } - return armor.createdAt != 0 ? Mathf.Clamp01(Maths.Max(GetBlunt(index) / (armor.Blunt + 0.001f), GetSharp(index) / (armor.Sharp + 0.001f), 0f)) : 0f; + val = Maths.Max((float)GetEnemyAvailability(index) * val, val); + return val; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public float GetRegionThreat(Region region) @@ -617,6 +623,33 @@ public float GetBlunt(int index) } return value; } + + public int GetEnemyAvailability(IntVec3 cell) + { + return GetEnemyAvailability(indices.CellToIndex(cell)); + } + public int GetEnemyAvailability(int index) + { + int value = 0; + for (int i = 0; i < hostiles.Length; i++) + { + value += hostiles[i].GetAvailability(index); + } + return value; + } + public int GetFriendlyAvailability(IntVec3 cell) + { + return GetFriendlyAvailability(indices.CellToIndex(cell)); + } + public int GetFriendlyAvailability(int index) + { + int value = 0; + for (int i = 0; i < friendlies.Length; i++) + { + value += friendlies[i].GetAvailability(index); + } + return value; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public float GetSharp(IntVec3 cell) { diff --git a/Source/Rule56/SuppliesUtility.cs b/Source/Rule56/SuppliesUtility.cs new file mode 100644 index 0000000..a8dbd55 --- /dev/null +++ b/Source/Rule56/SuppliesUtility.cs @@ -0,0 +1,91 @@ +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using RimWorld; +using Verse; +namespace CombatAI +{ + public class SuppliesUtility + { + private readonly static HashSet temp = new HashSet(); + + public static void DropSupplies(Map map, IntVec3 position, ThingDef thingDef, int count) + { + List list = new List(); + Thing thing = ThingMaker.MakeThing(thingDef); + thing.stackCount = count; + list.Add(thing); + DropPodUtility.DropThingsNear(position, map, list); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IEnumerable PotentialFoodFor(Pawn pawn) + { + return PotentialFoodFor(pawn.RaceProps); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IEnumerable PotentialFoodFor(RaceProperties race) + { + if (!race.EatsFood) + { + yield break; + } + // TODO add more food types. + yield return ThingDefOf.MealSurvivalPack; + } + + public static void FulfillFoodSupplies(List pawns, Map map) + { + if (pawns.Count == 0) + { + return; + } + TechLevel tech = pawns.RandomElement().Faction?.def?.techLevel ?? TechLevel.Undefined; + if (tech == TechLevel.Animal || tech == TechLevel.Medieval || tech == TechLevel.Neolithic) + { + return; + } + temp.Clear(); + temp.AddRange(pawns.SelectMany(PotentialFoodFor)); + float mul = tech == TechLevel.Industrial ? Rand.Range(1.0f, 1.5f) : Rand.Range(1.5f, 2.5f); + IntVec3 center = IntVec3.Zero; + for (int i = 0; i < pawns.Count; i++) + { + center += pawns[i].Position; + } + center.x /= pawns.Count; + center.z /= pawns.Count; + IntVec3 dropPoint = center; + float minDistSqr = 1e6f; + for (int i = 0; i < pawns.Count; i++) + { + float distSqr = pawns[i].Position.DistanceToSquared(center); + if (distSqr < minDistSqr) + { + Region region = pawns[i].GetRegion(); + if (region != null) + { + int k = 0; + IntVec3 cell = IntVec3.Invalid; + while (k++ < 16 && !(cell = region.RandomCell).Walkable(map)) + { + } + if (cell.IsValid && cell.Walkable(map)) + { + minDistSqr = distSqr; + dropPoint = region.RandomCell; + } + } + } + } + if (dropPoint.IsValid && dropPoint.Walkable(map)) + { + foreach (ThingDef def in temp) + { + DropSupplies(map, dropPoint, def, (int) (pawns.Count() * mul)); + } + } + } + } +} diff --git a/Source/Rule56/T4/Outputs/Keyed.generated.cs b/Source/Rule56/T4/Outputs/Keyed.generated.cs index 4c3c742..715f22c 100644 --- a/Source/Rule56/T4/Outputs/Keyed.generated.cs +++ b/Source/Rule56/T4/Outputs/Keyed.generated.cs @@ -196,7 +196,7 @@ public static TaggedString CombatAI_Settings_Basic_Presets_Applied { private static TaggedString _CombatAI_Settings_Basic_Presets_Description = null; /// Keyed string. key=CombatAI.Settings.Basic.Presets.Description. inner text: /// - /// Performance presets will determine the complexity of AI calculations\ and their interval. More complex calculations means harder AI but lower performance. Default is normal. + /// Performance presets will determine the complexity of AI calculations and their interval. More complex calculations means harder AI but lower performance. Default is normal. /// public static TaggedString CombatAI_Settings_Basic_Presets_Description { get => _CombatAI_Settings_Basic_Presets_Description != null ? @@ -236,7 +236,7 @@ public static TaggedString CombatAI_Settings_Basic_Groups { private static TaggedString _CombatAI_Settings_Basic_Groups_Description = null; /// Keyed string. key=CombatAI.Settings.Basic.Groups.Description. inner text: /// - /// Raiders will be divided into tactical groups (2-10) each with their own\ objective. Not all pawns will be assigned to groups, some will remain on the default assault duty. + /// Raiders will be divided into tactical groups (2-10) each with their own objective. Not all pawns will be assigned to groups, some will remain on the default assault duty. /// public static TaggedString CombatAI_Settings_Basic_Groups_Description { get => _CombatAI_Settings_Basic_Groups_Description != null ? @@ -256,7 +256,7 @@ public static TaggedString CombatAI_Settings_Basic_SappingMul { private static TaggedString _CombatAI_Settings_Basic_SappingMul_Description = null; /// Keyed string. key=CombatAI.Settings.Basic.SappingMul.Description. inner text: /// - /// Cost multiplier for pathing through walls. Higher values means\ raiders are less likely to path through walls. + /// Cost multiplier for pathing through walls. Higher values means raiders are less likely to path through walls. /// public static TaggedString CombatAI_Settings_Basic_SappingMul_Description { get => _CombatAI_Settings_Basic_SappingMul_Description != null ? @@ -416,7 +416,7 @@ public static TaggedString CombatAI_Settings_Basic_PerformanceOpt { private static TaggedString _CombatAI_Settings_Basic_PerformanceOpt_Warning = null; /// Keyed string. key=CombatAI.Settings.Basic.PerformanceOpt.Warning. inner text: /// - /// WARNING: NOT having dynamic performance settings ON will result in\ lag spikes and lose of performance! + /// WARNING: NOT having dynamic performance settings ON will result in lag spikes and lose of performance! /// public static TaggedString CombatAI_Settings_Basic_PerformanceOpt_Warning { get => _CombatAI_Settings_Basic_PerformanceOpt_Warning != null ? @@ -426,7 +426,7 @@ public static TaggedString CombatAI_Settings_Basic_PerformanceOpt_Warning { private static TaggedString _CombatAI_Settings_Basic_PerformanceOpt_Description = null; /// Keyed string. key=CombatAI.Settings.Basic.PerformanceOpt.Description. inner text: /// - /// Automatically adjust settings to maintain both a good level of\ TPS and a good AI. + /// Automatically adjust settings to maintain both a good level of TPS and a good AI. /// public static TaggedString CombatAI_Settings_Basic_PerformanceOpt_Description { get => _CombatAI_Settings_Basic_PerformanceOpt_Description != null ? @@ -506,7 +506,7 @@ public static TaggedString CombatAI_Settings_Basic_DestWeight { private static TaggedString _CombatAI_Settings_Basic_DestWeight_Description = null; /// Keyed string. key=CombatAI.Settings.Basic.DestWeight.Description. inner text: /// - /// Lower numbers mean pathfinding will be more aggressive at avoiding\ enemies, flanking and minimizing risk to pawns. + /// Lower numbers mean pathfinding will be more aggressive at avoiding enemies, flanking and minimizing risk to pawns. /// public static TaggedString CombatAI_Settings_Basic_DestWeight_Description { get => _CombatAI_Settings_Basic_DestWeight_Description != null ? @@ -556,7 +556,7 @@ public static TaggedString CombatAI_Settings_Advance { private static TaggedString _CombatAI_Settings_Advance_Warning = null; /// Keyed string. key=CombatAI.Settings.Advance.Warning. inner text: /// - /// WARNING: This is only for advanced users! Don't enable this if you don't know\ what you're doing! + /// WARNING: This is only for advanced users! Don't enable this if you don't know what you're doing! /// public static TaggedString CombatAI_Settings_Advance_Warning { get => _CombatAI_Settings_Advance_Warning != null ? @@ -586,7 +586,7 @@ public static TaggedString CombatAI_Settings_Advance_Sight_Performance { private static TaggedString _CombatAI_Settings_Advance_Sight_Performance_Description = null; /// Keyed string. key=CombatAI.Settings.Advance.Sight.Performance.Description. inner text: /// - /// You can adjust how many buckets pawns/turrets are divided\ into and how often they updates. Warning: DON'T USE THIS IF YOU DON'T KNOW WHAT YOU'RE DOING + /// You can adjust how many buckets pawns/turrets are divided into and how often they updates. Warning: DON'T USE THIS IF YOU DON'T KNOW WHAT YOU'RE DOING /// public static TaggedString CombatAI_Settings_Advance_Sight_Performance_Description { get => _CombatAI_Settings_Advance_Sight_Performance_Description != null ? @@ -676,7 +676,7 @@ public static TaggedString CombatAI_Settings_Advance_Sight_Performance_Readouts_ private static TaggedString _CombatAI_Settings_Advance_Sight_Performance_Readouts_CarryLimit_Description = null; /// Keyed string. key=CombatAI.Settings.Advance.Sight.Performance.Readouts.CarryLimit.Description. inner text: /// - /// The maximum number of things along line\ of sight. This includes trees, buildings, etc. Higher values means more accurate sight model but higher\ performance impact. + /// The maximum number of things along line of sight. This includes trees, buildings, etc. Higher values means more accurate sight model but higher performance impact. /// public static TaggedString CombatAI_Settings_Advance_Sight_Performance_Readouts_CarryLimit_Description { get => _CombatAI_Settings_Advance_Sight_Performance_Readouts_CarryLimit_Description != null ?