diff --git a/1.4/Assemblies/CombatAI.dll b/1.4/Assemblies/CombatAI.dll index 930bd1c..67b3294 100644 Binary files a/1.4/Assemblies/CombatAI.dll and b/1.4/Assemblies/CombatAI.dll differ diff --git a/1.4/Defs/ThingDef_Buildings/Buildings_Security.xml b/1.4/Defs/ThingDef_Buildings/Buildings_Security.xml index 16258c6..b9bda22 100644 --- a/1.4/Defs/ThingDef_Buildings/Buildings_Security.xml +++ b/1.4/Defs/ThingDef_Buildings/Buildings_Security.xml @@ -324,7 +324,7 @@ 23 true 65 - CombatAI.CCTVTopAnimator_Periodic + CombatAI.CCTVTopAnimator_Controlled Graphic_Single Isma/Buildings/CCTV/cctv_wall_turret @@ -413,7 +413,7 @@ 41 true 70 - CombatAI.CCTVTopAnimator_Periodic + CombatAI.CCTVTopAnimator_Controlled Graphic_Single Isma/Buildings/CCTV/cctv_wall_turret diff --git a/1.4/Languages/English/Keyed/Translations.xml b/1.4/Languages/English/Keyed/Translations.xml index e54606e..10ede07 100644 --- a/1.4/Languages/English/Keyed/Translations.xml +++ b/1.4/Languages/English/Keyed/Translations.xml @@ -6,69 +6,54 @@ Open Hide Hide - Needs to be mounted to a wall or a solid structure. - + Needs to be mounted to a wall or a solid structure. Preparing Combat AI + Hold + Hold at the current angle. Basic Settings + Performance/Difficulty presets + Easy/Perf + Normal + Hard/Complex + Deathwish + Applied preset: + Performance presets will determine the complexity of AI calculations and their interval. More complex calculations means harder AI but lower performance. Default is normal. [BETA] Fog of war Enable fog of war - Fog density (how dark the fog of war is) - - {0} Fog desnity - - Fog of war sight radius multiplie (default is 1.0) - + Fog density (how dark the fog of war is) + {0} Fog desnity + Fog of war sight radius multiplie (default is 1.0) {0}x - Fog of war sight fade radius multiplier (default is 0.5) - + Fog of war sight fade radius multiplier (default is 0.5) {0}x Enable The Killbox killer 5000 Enable Combat Extended lean profile - Enable dynamic performance settings - Automatically adjust settings to maintain both a good level of - TPS and a good AI. - + Enable Dynamic performance settings + WARNING: NOT having dynamic performance settings ON will result in lag spikes and lose of performance! + Automatically adjust settings to maintain both a good level of TPS and a good AI. Enable fire position tweaks Enable target selection tweaks + Enable aggressive flanking + Enable fast reactions + Enable tactical retreat Enable pathfinding tweaks Pathfinding aggressiveness level - Lower numbers mean pathfinding will be more aggressive at avoiding - enemies, flanking and minimizing risk to pawns. - - WARNING: Lower values will cause alot of performance issues. - - + Lower numbers mean pathfinding will be more aggressive at avoiding enemies, flanking and minimizing risk to pawns. + WARNING: Lower values will cause alot of performance issues. Debugging Enable Debugging - Pathfinding_DestWeight Advance Settings - 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! I'm an advanced user! - Performance - 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 - - Humanlike factions - - Mechs and insects - - Wildlife - - Map turrets - - Update frequency {0}Hz (buckets * interval) - - Bucket count {0} - - Ticks between bucket updates {0} Ticks - - {0} Things maximum can obstuct line of sight - - 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. - + 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 + Humanlike factions + Mechs and insects + Wildlife + Map turrets + Update frequency {0}Hz (buckets * interval) + Bucket count {0} + Ticks between bucket updates {0} Ticks + {0} Things maximum can obstuct line of sight + 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. \ No newline at end of file diff --git a/Source/Rule56/CCTV/CCTVTopAnimator.cs b/Source/Rule56/CCTV/CCTVTopAnimator.cs index 93b10ef..4c867fa 100644 --- a/Source/Rule56/CCTV/CCTVTopAnimator.cs +++ b/Source/Rule56/CCTV/CCTVTopAnimator.cs @@ -1,10 +1,16 @@ +using System.Collections.Generic; using CombatAI.Comps; +using Verse; namespace CombatAI { - public abstract class CCTVTopAnimator + public abstract class CCTVTopAnimator : IExposable { public ThingComp_CCTVTop comp; + public CCTVTopAnimator() + { + } + public CCTVTopAnimator(ThingComp_CCTVTop comp) { this.comp = comp; @@ -17,5 +23,11 @@ public abstract float CurRotation } public abstract void Tick(); + public abstract void ExposeData(); + + public virtual IEnumerable GetGizmos() + { + yield break; + } } } diff --git a/Source/Rule56/CCTV/CCTVTopAnimator_Controlled.cs b/Source/Rule56/CCTV/CCTVTopAnimator_Controlled.cs new file mode 100644 index 0000000..3510868 --- /dev/null +++ b/Source/Rule56/CCTV/CCTVTopAnimator_Controlled.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using CombatAI.Comps; +using CombatAI.R; +using RimWorld; +using Verse; +namespace CombatAI +{ + public class CCTVTopAnimator_Controlled : CCTVTopAnimator_Periodic + { + private bool paused; + + public CCTVTopAnimator_Controlled(ThingComp_CCTVTop comp) : base(comp) + { + } + + public override void Tick() + { + if (!paused) + { + base.Tick(); + } + } + + public override IEnumerable GetGizmos() + { + Command_Toggle toggle = new Command_Toggle(); + toggle.toggleAction = () => + { + paused = !paused; + }; + toggle.isActive = () => + { + return paused; + }; + toggle.icon = TexCommand.PauseCaravan; + toggle.defaultLabel = R.Keyed.CombatAI_Animator_Controller; + toggle.defaultDesc = R.Keyed.CombatAI_Animator_Controller_Description; + yield return toggle; + } + + public override void ExposeData() + { + base.ExposeData(); + Scribe_Values.Look(ref paused, "paused", false); + } + } +} diff --git a/Source/Rule56/CCTV/CCTVTopAnimator_Periodic.cs b/Source/Rule56/CCTV/CCTVTopAnimator_Periodic.cs index 854ef69..dd7ffe4 100644 --- a/Source/Rule56/CCTV/CCTVTopAnimator_Periodic.cs +++ b/Source/Rule56/CCTV/CCTVTopAnimator_Periodic.cs @@ -5,8 +5,9 @@ namespace CombatAI { public class CCTVTopAnimator_Periodic : CCTVTopAnimator { - private bool idleTurnClockwise; - private int ticksUntilIdleTurn; + private float rot; + private bool idleTurnClockwise; + private int ticksUntilIdleTurn; public CCTVTopAnimator_Periodic(ThingComp_CCTVTop comp) : base(comp) { @@ -17,8 +18,8 @@ public CCTVTopAnimator_Periodic(ThingComp_CCTVTop comp) : base(comp) /// public override float CurRotation { - get; - set; + get => rot; + set => rot = value; } public override void Tick() @@ -45,5 +46,9 @@ public override void Tick() ticksUntilIdleTurn = Rand.RangeInclusive(30, 100); } } + public override void ExposeData() + { + Scribe_Values.Look(ref rot, "rot", 0); + } } } diff --git a/Source/Rule56/Comps/CompProperties_CCTVTop.cs b/Source/Rule56/CCTV/CompProperties_CCTVTop.cs similarity index 100% rename from Source/Rule56/Comps/CompProperties_CCTVTop.cs rename to Source/Rule56/CCTV/CompProperties_CCTVTop.cs diff --git a/Source/Rule56/Comps/ThingComp_CCTVTop.cs b/Source/Rule56/CCTV/ThingComp_CCTVTop.cs similarity index 74% rename from Source/Rule56/Comps/ThingComp_CCTVTop.cs rename to Source/Rule56/CCTV/ThingComp_CCTVTop.cs index cb54cae..f8f9f1d 100644 --- a/Source/Rule56/Comps/ThingComp_CCTVTop.cs +++ b/Source/Rule56/CCTV/ThingComp_CCTVTop.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using UnityEngine; using Verse; namespace CombatAI.Comps @@ -53,16 +54,19 @@ public override void CompTick() } } - public override void PostSpawnSetup(bool respawningAfterLoad) + public override void PostPostMake() { - base.PostSpawnSetup(respawningAfterLoad); - parent.Map.GetComp_Fast().Register(this); + base.PostPostMake(); + if (animator == null) + { + animator = (CCTVTopAnimator)Activator.CreateInstance((props as CompProperties_CCTVTop).animator, this); + } } - public override void Initialize(CompProperties props) + public override void PostSpawnSetup(bool respawningAfterLoad) { - base.Initialize(props); - animator = (CCTVTopAnimator)Activator.CreateInstance((props as CompProperties_CCTVTop).animator, this); + base.PostSpawnSetup(respawningAfterLoad); + parent.Map.GetComp_Fast().Register(this); } /// @@ -78,5 +82,20 @@ public override void PostDraw() matrix.SetTRS(parent.DrawPos + Altitudes.AltIncVect + position, rot.ToQuat(), new Vector3(props.graphicData.drawSize.x, 1, props.graphicData.drawSize.y)); Graphics.DrawMesh(MeshPool.plane10, matrix, props.turretTopMat, 0); } + + public override void PostExposeData() + { + base.PostExposeData(); + Scribe_Deep.Look(ref animator, "animator", this); + if (animator == null) + { + animator = (CCTVTopAnimator)Activator.CreateInstance((props as CompProperties_CCTVTop).animator, this); + } + } + + public override IEnumerable CompGetGizmosExtra() + { + return animator?.GetGizmos(); + } } } diff --git a/Source/Rule56/CellFlooder.cs b/Source/Rule56/CellFlooder.cs index 4fd64b4..e0a5034 100644 --- a/Source/Rule56/CellFlooder.cs +++ b/Source/Rule56/CellFlooder.cs @@ -39,7 +39,8 @@ public void Flood(IntVec3 center, Action action, Func cost Func blocked = GetBlockedTestFunc(validator); walls = map.GetComponent(); Node node = GetIntialFloodedCell(center); - node.dist = costFunction != null ? costFunction(node.cell) : 0; + node.dist = costFunction != null ? costFunction(node.cell) : 0; + sigArray[map.cellIndices.CellToIndex(node.cell)] = sig; Node nextNode; int cellIndex; IntVec3 nextCell; @@ -69,7 +70,8 @@ public void Flood(IntVec3 center, Action action, Func cost nextCell = node.cell + offset; if (nextCell.InBounds(map)) { - if (sigArray[cellIndex = map.cellIndices.CellToIndex(nextCell)] != sig) + bool visited; + if (!(visited = sigArray[cellIndex = map.cellIndices.CellToIndex(nextCell)] == sig)) { sigArray[cellIndex] = sig; if (!blocked(nextCell)) @@ -95,12 +97,11 @@ public void Flood(IntVec3 center, Action action, Func cost nextNode.dist += costFunction(nextCell); } floodQueue.Enqueue(nextNode); - // - //floodedCells.Add(nextNode); } } } } +// } } } diff --git a/Source/Rule56/CombatAI.csproj b/Source/Rule56/CombatAI.csproj index 31dc644..078d102 100644 --- a/Source/Rule56/CombatAI.csproj +++ b/Source/Rule56/CombatAI.csproj @@ -29,37 +29,38 @@ ..\..\1.4\Assemblies DEBUG;TRACE;DEBUG_REACTION;NETFRAMEWORK;NET472; true + false - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + @@ -84,11 +85,11 @@ - - - - - + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -97,17 +98,17 @@ - + - + $(PkgMono_TextTransform)\tools\TextTransform.exe - - + + @@ -121,12 +122,12 @@ $(PubliciseOutputPath)UnityEngine.CoreModule_publicised.dll - - - - + + + + - + false @@ -143,10 +144,10 @@ - + - - + + @@ -156,7 +157,7 @@ - + diff --git a/Source/Rule56/CombatAIMod.cs b/Source/Rule56/CombatAIMod.cs index 9fb370b..2bad85f 100644 --- a/Source/Rule56/CombatAIMod.cs +++ b/Source/Rule56/CombatAIMod.cs @@ -1,9 +1,12 @@ using System; +using System.Collections.Generic; using CombatAI.Gui; using CombatAI.R; using HarmonyLib; +using RimWorld; using UnityEngine; using Verse; +using GUIUtility = CombatAI.Gui.GUIUtility; namespace CombatAI { public class CombatAIMod : Mod @@ -72,25 +75,156 @@ private void FillCollapsible_Basic(Listing_Collapsible collapsible) } collapsible.CheckboxLabeled(Keyed.CombatAI_Settings_Basic_CELean, ref Finder.Settings.LeanCE_Enabled); collapsible.Line(1); - collapsible.CheckboxLabeled(Keyed.CombatAI_Settings_Basic_PerformanceOpt, ref Finder.Settings.PerformanceOpt_Enabled, Keyed.CombatAI_Settings_Basic_PerformanceOpt_Description); + + collapsible.Label(Keyed.CombatAI_Settings_Basic_Presets); + collapsible.Gap(1); + collapsible.Label(Keyed.CombatAI_Settings_Basic_Presets_Description); + collapsible.Lambda(25, inRect => + { + GUIUtility.Row(inRect, new List> + { + rect => + { + GUI.color = Color.green; + if (Widgets.ButtonText(rect, Keyed.CombatAI_Settings_Basic_Presets_Easy)) + { + Finder.Settings.Pathfinding_DestWeight = 0.9f; + Finder.Settings.Caster_Enabled = false; + Finder.Settings.Targeter_Enabled = false; + Finder.Settings.Pather_Enabled = true; + Finder.Settings.Pather_KillboxKiller = false; + Finder.Settings.PerformanceOpt_Enabled = true; + Finder.Settings.React_Enabled = false; + Finder.Settings.Retreat_Enabled = false; + Finder.Settings.Flank_Enabled = false; + Finder.Settings.SightSettings_FriendliesAndRaiders.interval = 3; + if (Current.ProgramState != ProgramState.Playing) + { + Finder.Settings.SightSettings_FriendliesAndRaiders.buckets = 10; + } + Finder.Settings.SightSettings_Wildlife.interval = 6; + if (Current.ProgramState != ProgramState.Playing) + { + Finder.Settings.SightSettings_Wildlife.buckets = 10; + } + Finder.Settings.SightSettings_MechsAndInsects.interval = 3; + if (Current.ProgramState != ProgramState.Playing) + { + Finder.Settings.SightSettings_MechsAndInsects.buckets = 12; + } + Messages.Message(Keyed.CombatAI_Settings_Basic_Presets_Applied + " " + Keyed.CombatAI_Settings_Basic_Presets_Easy, MessageTypeDefOf.TaskCompletion); + } + }, + rect => + { + if (Widgets.ButtonText(rect, Keyed.CombatAI_Settings_Basic_Presets_Normal)) + { + Finder.Settings.Pathfinding_DestWeight = 0.8f; + Finder.Settings.Caster_Enabled = true; + Finder.Settings.Targeter_Enabled = true; + Finder.Settings.Pather_Enabled = true; + Finder.Settings.Pather_KillboxKiller = true; + Finder.Settings.PerformanceOpt_Enabled = true; + Finder.Settings.React_Enabled = true; + Finder.Settings.Retreat_Enabled = false; + Finder.Settings.Flank_Enabled = true; + Finder.Settings.SightSettings_FriendliesAndRaiders.interval = 3; + if (Current.ProgramState != ProgramState.Playing) + { + Finder.Settings.SightSettings_FriendliesAndRaiders.buckets = 5; + } + Finder.Settings.SightSettings_Wildlife.interval = 3; + if (Current.ProgramState != ProgramState.Playing) + { + Finder.Settings.SightSettings_Wildlife.buckets = 10; + } + Finder.Settings.SightSettings_MechsAndInsects.interval = 3; + if (Current.ProgramState != ProgramState.Playing) + { + Finder.Settings.SightSettings_MechsAndInsects.buckets = 5; + } + Messages.Message(Keyed.CombatAI_Settings_Basic_Presets_Applied + " " + Keyed.CombatAI_Settings_Basic_Presets_Normal, MessageTypeDefOf.TaskCompletion); + } + }, + rect => + { + if (Widgets.ButtonText(rect, Keyed.CombatAI_Settings_Basic_Presets_Hard)) + { + Finder.Settings.Pathfinding_DestWeight = 0.75f; + Finder.Settings.Caster_Enabled = true; + Finder.Settings.Targeter_Enabled = true; + Finder.Settings.Pather_Enabled = true; + Finder.Settings.Pather_KillboxKiller = true; + Finder.Settings.React_Enabled = true; + Finder.Settings.Retreat_Enabled = true; + Finder.Settings.Flank_Enabled = true; + Finder.Settings.PerformanceOpt_Enabled = true; + Finder.Settings.SightSettings_FriendliesAndRaiders.interval = 2; + if (Current.ProgramState != ProgramState.Playing) + { + Finder.Settings.SightSettings_FriendliesAndRaiders.buckets = 5; + } + Finder.Settings.SightSettings_Wildlife.interval = 2; + if (Current.ProgramState != ProgramState.Playing) + { + Finder.Settings.SightSettings_Wildlife.buckets = 5; + } + Finder.Settings.SightSettings_MechsAndInsects.interval = 2; + if (Current.ProgramState != ProgramState.Playing) + { + Finder.Settings.SightSettings_MechsAndInsects.buckets = 5; + } + Messages.Message(Keyed.CombatAI_Settings_Basic_Presets_Applied + " " + Keyed.CombatAI_Settings_Basic_Presets_Hard, MessageTypeDefOf.TaskCompletion); + } + }, + rect => + { + GUI.color = Color.red; + if (Widgets.ButtonText(rect, Keyed.CombatAI_Settings_Basic_Presets_Deathwish)) + { + Finder.Settings.Pathfinding_DestWeight = 0.6f; + Finder.Settings.Caster_Enabled = true; + Finder.Settings.Targeter_Enabled = true; + Finder.Settings.Pather_Enabled = true; + Finder.Settings.Pather_KillboxKiller = true; + Finder.Settings.React_Enabled = true; + Finder.Settings.Retreat_Enabled = true; + Finder.Settings.Flank_Enabled = true; + Finder.Settings.PerformanceOpt_Enabled = false; + Finder.Settings.SightSettings_FriendliesAndRaiders.interval = 1; + if (Current.ProgramState != ProgramState.Playing) + { + Finder.Settings.SightSettings_FriendliesAndRaiders.buckets = 5; + } + Finder.Settings.SightSettings_Wildlife.interval = 2; + if (Current.ProgramState != ProgramState.Playing) + { + Finder.Settings.SightSettings_Wildlife.buckets = 5; + } + Finder.Settings.SightSettings_MechsAndInsects.interval = 2; + if (Current.ProgramState != ProgramState.Playing) + { + Finder.Settings.SightSettings_MechsAndInsects.buckets = 5; + } + Messages.Message(Keyed.CombatAI_Settings_Basic_PerformanceOpt_Warning, MessageTypeDefOf.CautionInput); + Messages.Message(Keyed.CombatAI_Settings_Basic_Presets_Applied + " " + Keyed.CombatAI_Settings_Basic_Presets_Deathwish, MessageTypeDefOf.TaskCompletion); + } + } + }, false); + }, useMargins: true); + collapsible.Line(1); + if (collapsible.CheckboxLabeled(Keyed.CombatAI_Settings_Basic_PerformanceOpt, ref Finder.Settings.PerformanceOpt_Enabled, Keyed.CombatAI_Settings_Basic_PerformanceOpt_Description) && !Finder.Settings.PerformanceOpt_Enabled) + { + Messages.Message(Keyed.CombatAI_Settings_Basic_PerformanceOpt_Warning, MessageTypeDefOf.CautionInput); + } collapsible.Line(1); collapsible.CheckboxLabeled(Keyed.CombatAI_Settings_Basic_KillBoxKiller, ref Finder.Settings.Pather_KillboxKiller); collapsible.CheckboxLabeled(Keyed.CombatAI_Settings_Basic_Pather, ref Finder.Settings.Pather_Enabled); collapsible.CheckboxLabeled(Keyed.CombatAI_Settings_Basic_Caster, ref Finder.Settings.Caster_Enabled); collapsible.CheckboxLabeled(Keyed.CombatAI_Settings_Basic_Targeter, ref Finder.Settings.Targeter_Enabled); - collapsible.Line(1); - collapsible.Label(Keyed.CombatAI_Settings_Basic_DestWeight); - collapsible.Gap(1); - collapsible.Label(Keyed.CombatAI_Settings_Basic_DestWeight_Description); - collapsible.Label(Keyed.CombatAI_Settings_Basic_DestWeight_Warning, fontStyle: FontStyle.Bold); - collapsible.Gap(1); - collapsible.Lambda(25, rect => - { - string color = Finder.Settings.Pathfinding_DestWeight < 0.75f ? "red" : "while"; - string extra = Finder.Settings.Pathfinding_DestWeight < 0.75f ? " WILL IMPACT PERFORMANCE" : ""; - Text.CurFontStyle.fontStyle = FontStyle.Bold; - Finder.Settings.Pathfinding_DestWeight = Widgets.HorizontalSlider(rect, Finder.Settings.Pathfinding_DestWeight, 0.95f, Finder.Settings.AdvancedUser ? 0.3f : 0.65f, true, $"{Math.Round(Finder.Settings.Pathfinding_DestWeight * 100f, 1)}%{extra}"); - }, useMargins: true); + collapsible.CheckboxLabeled(Keyed.CombatAI_Settings_Basic_Reaction, ref Finder.Settings.React_Enabled); + collapsible.CheckboxLabeled(Keyed.CombatAI_Settings_Basic_Flanking, ref Finder.Settings.Flank_Enabled); + collapsible.CheckboxLabeled(Keyed.CombatAI_Settings_Basic_Retreat, ref Finder.Settings.Retreat_Enabled); } private void FillCollapsible_FogOfWar(Listing_Collapsible collapsible) @@ -103,23 +237,23 @@ private void FillCollapsible_FogOfWar(Listing_Collapsible collapsible) collapsible.Label(Keyed.CombatAI_Settings_Basic_FogOfWar_Density); collapsible.Lambda(25, rect => { - Finder.Settings.FogOfWar_FogColor = Widgets.HorizontalSlider(rect, Finder.Settings.FogOfWar_FogColor, 0.0f, 1.0f, false, Keyed.CombatAI_Settings_Basic_FogOfWar_Density_Readouts.Formatted(Finder.Settings.FogOfWar_FogColor.ToString())); + Finder.Settings.FogOfWar_FogColor = Widgets.HorizontalSlider(rect, Finder.Settings.FogOfWar_FogColor, 0.0f, 1.0f, false, Keyed.CombatAI_Settings_Basic_FogOfWar_Density_Readouts.Formatted(Finder.Settings.FogOfWar_FogColor.ToString()), roundTo: 0.05f); }, useMargins: true); - collapsible.Line(1); +// collapsible.Line(1); collapsible.Label(Keyed.CombatAI_Settings_Basic_FogOfWar_RangeMul); collapsible.Lambda(25, rect => { - Finder.Settings.FogOfWar_RangeMultiplier = Widgets.HorizontalSlider(rect, Finder.Settings.FogOfWar_RangeMultiplier, 0.75f, 2.0f, false, Keyed.CombatAI_Settings_Basic_FogOfWar_RangeMul_Readouts.Formatted(Finder.Settings.FogOfWar_RangeMultiplier.ToString())); + Finder.Settings.FogOfWar_RangeMultiplier = Widgets.HorizontalSlider(rect, Finder.Settings.FogOfWar_RangeMultiplier, 0.75f, 2.0f, false, Keyed.CombatAI_Settings_Basic_FogOfWar_RangeMul_Readouts.Formatted(Finder.Settings.FogOfWar_RangeMultiplier.ToString()), roundTo: 0.05f); }, useMargins: true); - collapsible.Line(1); +// collapsible.Line(1); collapsible.Label(Keyed.CombatAI_Settings_Basic_FogOfWar_FadeMul); collapsible.Lambda(25, rect => { - Finder.Settings.FogOfWar_RangeFadeMultiplier = Widgets.HorizontalSlider(rect, Finder.Settings.FogOfWar_RangeFadeMultiplier, 0.0f, 1.0f, false, Keyed.CombatAI_Settings_Basic_FogOfWar_FadeMul_Readouts.Formatted(Finder.Settings.FogOfWar_RangeFadeMultiplier.ToString())); + Finder.Settings.FogOfWar_RangeFadeMultiplier = Widgets.HorizontalSlider(rect, Finder.Settings.FogOfWar_RangeFadeMultiplier, 0.0f, 1.0f, false, Keyed.CombatAI_Settings_Basic_FogOfWar_FadeMul_Readouts.Formatted(Finder.Settings.FogOfWar_RangeFadeMultiplier.ToString()), roundTo: 0.05f); }, useMargins: true); } } @@ -127,6 +261,20 @@ private void FillCollapsible_FogOfWar(Listing_Collapsible collapsible) private void FillCollapsible_Performance(Listing_Collapsible collapsible) { + collapsible.Label(Keyed.CombatAI_Settings_Basic_DestWeight); + collapsible.Gap(1); + collapsible.Label(Keyed.CombatAI_Settings_Basic_DestWeight_Description); + collapsible.Label(Keyed.CombatAI_Settings_Basic_DestWeight_Warning, fontStyle: FontStyle.Bold); + collapsible.Gap(1); + collapsible.Lambda(25, rect => + { + string color = Finder.Settings.Pathfinding_DestWeight < 0.75f ? "red" : "while"; + string extra = Finder.Settings.Pathfinding_DestWeight < 0.75f ? " WILL IMPACT PERFORMANCE" : ""; + Text.CurFontStyle.fontStyle = FontStyle.Bold; + Finder.Settings.Pathfinding_DestWeight = Widgets.HorizontalSlider(rect, Finder.Settings.Pathfinding_DestWeight, 0.95f, Finder.Settings.AdvancedUser ? 0.3f : 0.65f, true, $"{Math.Round(Finder.Settings.Pathfinding_DestWeight * 100f, 1)}%{extra}", roundTo: 0.05f); + }, useMargins: true); + + collapsible.Line(2); collapsible.Label(Keyed.CombatAI_Settings_Advance_Sight_Performance_Description); /* * ---------------------- @@ -137,18 +285,18 @@ private void FillCollapsible_Performance(Listing_Collapsible collapsible) collapsible.Label(Keyed.CombatAI_Settings_Advance_Sight_Performance_Readouts_Frequency.Formatted(60f / (Finder.Settings.SightSettings_FriendliesAndRaiders.buckets * Finder.Settings.SightSettings_FriendliesAndRaiders.interval))); collapsible.Lambda(25, rect => { - Finder.Settings.SightSettings_FriendliesAndRaiders.interval = (int)Widgets.HorizontalSlider(rect, Finder.Settings.SightSettings_FriendliesAndRaiders.interval, 1, 20, false, Keyed.CombatAI_Settings_Advance_Sight_Performance_Readouts_Interval.Formatted(Finder.Settings.SightSettings_FriendliesAndRaiders.interval)); + Finder.Settings.SightSettings_FriendliesAndRaiders.interval = (int)Widgets.HorizontalSlider(rect, Finder.Settings.SightSettings_FriendliesAndRaiders.interval, 1, 20, false, Keyed.CombatAI_Settings_Advance_Sight_Performance_Readouts_Interval.Formatted(Finder.Settings.SightSettings_FriendliesAndRaiders.interval), roundTo: 0.05f); }, useMargins: true); if (Current.ProgramState != ProgramState.Playing) { collapsible.Lambda(25, rect => { - Finder.Settings.SightSettings_FriendliesAndRaiders.buckets = (int)Widgets.HorizontalSlider(rect, Finder.Settings.SightSettings_FriendliesAndRaiders.buckets, 1, 20, false, Keyed.CombatAI_Settings_Advance_Sight_Performance_Readouts_Buckets.Formatted(Finder.Settings.SightSettings_FriendliesAndRaiders.buckets)); + Finder.Settings.SightSettings_FriendliesAndRaiders.buckets = (int)Widgets.HorizontalSlider(rect, Finder.Settings.SightSettings_FriendliesAndRaiders.buckets, 1, 20, false, Keyed.CombatAI_Settings_Advance_Sight_Performance_Readouts_Buckets.Formatted(Finder.Settings.SightSettings_FriendliesAndRaiders.buckets), roundTo: 0.05f); }, useMargins: true); } collapsible.Lambda(25, rect => { - Finder.Settings.SightSettings_FriendliesAndRaiders.carryLimit = (int)Widgets.HorizontalSlider(rect, Finder.Settings.SightSettings_FriendliesAndRaiders.carryLimit, 4, 16, false, Keyed.CombatAI_Settings_Advance_Sight_Performance_Readouts_CarryLimit.Formatted(Finder.Settings.SightSettings_FriendliesAndRaiders.carryLimit)); + Finder.Settings.SightSettings_FriendliesAndRaiders.carryLimit = (int)Widgets.HorizontalSlider(rect, Finder.Settings.SightSettings_FriendliesAndRaiders.carryLimit, 4, 16, false, Keyed.CombatAI_Settings_Advance_Sight_Performance_Readouts_CarryLimit.Formatted(Finder.Settings.SightSettings_FriendliesAndRaiders.carryLimit), roundTo: 0.05f); if (Mouse.IsOver(rect)) { TooltipHandler.TipRegion(rect, Keyed.CombatAI_Settings_Advance_Sight_Performance_Readouts_CarryLimit_Description); @@ -163,18 +311,18 @@ private void FillCollapsible_Performance(Listing_Collapsible collapsible) collapsible.Label(Keyed.CombatAI_Settings_Advance_Sight_Performance_Readouts_Frequency.Formatted(60f / (Finder.Settings.SightSettings_MechsAndInsects.buckets * Finder.Settings.SightSettings_MechsAndInsects.interval))); collapsible.Lambda(25, rect => { - Finder.Settings.SightSettings_MechsAndInsects.interval = (int)Widgets.HorizontalSlider(rect, Finder.Settings.SightSettings_MechsAndInsects.interval, 1, 20, false, Keyed.CombatAI_Settings_Advance_Sight_Performance_Readouts_Interval.Formatted(Finder.Settings.SightSettings_MechsAndInsects.interval)); + Finder.Settings.SightSettings_MechsAndInsects.interval = (int)Widgets.HorizontalSlider(rect, Finder.Settings.SightSettings_MechsAndInsects.interval, 1, 20, false, Keyed.CombatAI_Settings_Advance_Sight_Performance_Readouts_Interval.Formatted(Finder.Settings.SightSettings_MechsAndInsects.interval), roundTo: 0.05f); }, useMargins: true); if (Current.ProgramState != ProgramState.Playing) { collapsible.Lambda(25, rect => { - Finder.Settings.SightSettings_MechsAndInsects.buckets = (int)Widgets.HorizontalSlider(rect, Finder.Settings.SightSettings_MechsAndInsects.buckets, 1, 20, false, Keyed.CombatAI_Settings_Advance_Sight_Performance_Readouts_Buckets.Formatted(Finder.Settings.SightSettings_MechsAndInsects.buckets)); + Finder.Settings.SightSettings_MechsAndInsects.buckets = (int)Widgets.HorizontalSlider(rect, Finder.Settings.SightSettings_MechsAndInsects.buckets, 1, 20, false, Keyed.CombatAI_Settings_Advance_Sight_Performance_Readouts_Buckets.Formatted(Finder.Settings.SightSettings_MechsAndInsects.buckets), roundTo: 0.05f); }, useMargins: true); } collapsible.Lambda(25, rect => { - Finder.Settings.SightSettings_MechsAndInsects.carryLimit = (int)Widgets.HorizontalSlider(rect, Finder.Settings.SightSettings_MechsAndInsects.carryLimit, 4, 16, false, Keyed.CombatAI_Settings_Advance_Sight_Performance_Readouts_CarryLimit.Formatted(Finder.Settings.SightSettings_MechsAndInsects.carryLimit)); + Finder.Settings.SightSettings_MechsAndInsects.carryLimit = (int)Widgets.HorizontalSlider(rect, Finder.Settings.SightSettings_MechsAndInsects.carryLimit, 4, 16, false, Keyed.CombatAI_Settings_Advance_Sight_Performance_Readouts_CarryLimit.Formatted(Finder.Settings.SightSettings_MechsAndInsects.carryLimit), roundTo: 0.05f); if (Mouse.IsOver(rect)) { TooltipHandler.TipRegion(rect, Keyed.CombatAI_Settings_Advance_Sight_Performance_Readouts_CarryLimit_Description); @@ -189,18 +337,18 @@ private void FillCollapsible_Performance(Listing_Collapsible collapsible) collapsible.Label(Keyed.CombatAI_Settings_Advance_Sight_Performance_Readouts_Frequency.Formatted(60f / (Finder.Settings.SightSettings_Wildlife.buckets * Finder.Settings.SightSettings_Wildlife.interval))); collapsible.Lambda(25, rect => { - Finder.Settings.SightSettings_Wildlife.interval = (int)Widgets.HorizontalSlider(rect, Finder.Settings.SightSettings_Wildlife.interval, 1, 20, false, Keyed.CombatAI_Settings_Advance_Sight_Performance_Readouts_Interval.Formatted(Finder.Settings.SightSettings_Wildlife.interval)); + Finder.Settings.SightSettings_Wildlife.interval = (int)Widgets.HorizontalSlider(rect, Finder.Settings.SightSettings_Wildlife.interval, 1, 20, false, Keyed.CombatAI_Settings_Advance_Sight_Performance_Readouts_Interval.Formatted(Finder.Settings.SightSettings_Wildlife.interval), roundTo: 0.05f); }, useMargins: true); if (Current.ProgramState != ProgramState.Playing) { collapsible.Lambda(25, rect => { - Finder.Settings.SightSettings_Wildlife.buckets = (int)Widgets.HorizontalSlider(rect, Finder.Settings.SightSettings_Wildlife.buckets, 1, 20, false, Keyed.CombatAI_Settings_Advance_Sight_Performance_Readouts_Buckets.Formatted(Finder.Settings.SightSettings_Wildlife.buckets)); + Finder.Settings.SightSettings_Wildlife.buckets = (int)Widgets.HorizontalSlider(rect, Finder.Settings.SightSettings_Wildlife.buckets, 1, 20, false, Keyed.CombatAI_Settings_Advance_Sight_Performance_Readouts_Buckets.Formatted(Finder.Settings.SightSettings_Wildlife.buckets), roundTo: 0.05f); }, useMargins: true); } collapsible.Lambda(25, rect => { - Finder.Settings.SightSettings_Wildlife.carryLimit = (int)Widgets.HorizontalSlider(rect, Finder.Settings.SightSettings_Wildlife.carryLimit, 4, 16, false, Keyed.CombatAI_Settings_Advance_Sight_Performance_Readouts_CarryLimit.Formatted(Finder.Settings.SightSettings_Wildlife.carryLimit)); + Finder.Settings.SightSettings_Wildlife.carryLimit = (int)Widgets.HorizontalSlider(rect, Finder.Settings.SightSettings_Wildlife.carryLimit, 4, 16, false, Keyed.CombatAI_Settings_Advance_Sight_Performance_Readouts_CarryLimit.Formatted(Finder.Settings.SightSettings_Wildlife.carryLimit), roundTo: 0.05f); if (Mouse.IsOver(rect)) { TooltipHandler.TipRegion(rect, Keyed.CombatAI_Settings_Advance_Sight_Performance_Readouts_CarryLimit_Description); @@ -215,18 +363,18 @@ private void FillCollapsible_Performance(Listing_Collapsible collapsible) collapsible.Label(Keyed.CombatAI_Settings_Advance_Sight_Performance_Readouts_Frequency.Formatted((60f / ((float)Finder.Settings.SightSettings_SettlementTurrets.buckets * Finder.Settings.SightSettings_SettlementTurrets.interval)).ToString())); collapsible.Lambda(25, rect => { - Finder.Settings.SightSettings_SettlementTurrets.interval = (int)Widgets.HorizontalSlider(rect, Finder.Settings.SightSettings_SettlementTurrets.interval, 1, 20, false, Keyed.CombatAI_Settings_Advance_Sight_Performance_Readouts_Interval.Formatted(Finder.Settings.SightSettings_SettlementTurrets.interval)); + Finder.Settings.SightSettings_SettlementTurrets.interval = (int)Widgets.HorizontalSlider(rect, Finder.Settings.SightSettings_SettlementTurrets.interval, 1, 20, false, Keyed.CombatAI_Settings_Advance_Sight_Performance_Readouts_Interval.Formatted(Finder.Settings.SightSettings_SettlementTurrets.interval), roundTo: 0.05f); }, useMargins: true); if (Current.ProgramState != ProgramState.Playing) { collapsible.Lambda(25, rect => { - Finder.Settings.SightSettings_SettlementTurrets.buckets = (int)Widgets.HorizontalSlider(rect, Finder.Settings.SightSettings_SettlementTurrets.buckets, 1, 20, false, Keyed.CombatAI_Settings_Advance_Sight_Performance_Readouts_Buckets.Formatted(Finder.Settings.SightSettings_SettlementTurrets.buckets)); + Finder.Settings.SightSettings_SettlementTurrets.buckets = (int)Widgets.HorizontalSlider(rect, Finder.Settings.SightSettings_SettlementTurrets.buckets, 1, 20, false, Keyed.CombatAI_Settings_Advance_Sight_Performance_Readouts_Buckets.Formatted(Finder.Settings.SightSettings_SettlementTurrets.buckets), roundTo: 0.05f); }, useMargins: true); } collapsible.Lambda(25, rect => { - Finder.Settings.SightSettings_SettlementTurrets.carryLimit = (int)Widgets.HorizontalSlider(rect, Finder.Settings.SightSettings_SettlementTurrets.carryLimit, 4, 16, false, Keyed.CombatAI_Settings_Advance_Sight_Performance_Readouts_CarryLimit.Formatted(Finder.Settings.SightSettings_SettlementTurrets.carryLimit)); + Finder.Settings.SightSettings_SettlementTurrets.carryLimit = (int)Widgets.HorizontalSlider(rect, Finder.Settings.SightSettings_SettlementTurrets.carryLimit, 4, 16, false, Keyed.CombatAI_Settings_Advance_Sight_Performance_Readouts_CarryLimit.Formatted(Finder.Settings.SightSettings_SettlementTurrets.carryLimit), roundTo: 0.05f); if (Mouse.IsOver(rect)) { TooltipHandler.TipRegion(rect, Keyed.CombatAI_Settings_Advance_Sight_Performance_Readouts_CarryLimit_Description); diff --git a/Source/Rule56/CombatReservationManager.cs b/Source/Rule56/CombatReservationManager.cs deleted file mode 100644 index af2de85..0000000 --- a/Source/Rule56/CombatReservationManager.cs +++ /dev/null @@ -1,233 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Verse; -namespace CombatAI -{ - public class CombatReservationManager : MapComponent - { - - private readonly Dictionary reservations = new Dictionary(); - - public CombatReservationManager(Map map) : base(map) - { - } - - public override void MapComponentOnGUI() - { - base.MapComponentOnGUI(); - //if (Controller.settings.DebugDrawTargetedBy && !Find.Selector.SelectedPawns.NullOrEmpty()) - //{ - //Pawn pawn = Find.Selector.SelectedPawns.First(); - //Vector2 center = UI.MapToUIPosition(pawn.Position.ToVector3()); - //if(Reserved(pawn, out List attackers)) - //{ - // foreach(Pawn attacker in attackers) - // { - // Vector2 pos = UI.MapToUIPosition(attacker.Position.ToVector3()); - // Widgets.DrawLine(center, pos, Color.red, 1); - // } - //} - //} - - } - - public override void MapComponentTick() - { - base.MapComponentTick(); - if ((GenTicks.TicksGame + 31) % 15000 == 0) - { - reservations.RemoveAll(r => !r.Value.IsValid); - foreach (KeyValuePair pair in reservations) - pair.Value.Dirty(); - } - } - - public void Reserve(Pawn attacker, Thing target) - { - if (attacker != null && TryGetReservedCombatTarget(target, out ReservedCombatTarget store)) - { - store.Add(attacker); - } - } - - public bool Reserved(Thing target) - { - return Reserved(target, out int _); - } - public bool Reserved(Thing target, out int num) - { - if (!TryGetReservedCombatTarget(target, out ReservedCombatTarget store)) - { - num = 0; - return false; - } - return (num = store.RecordCount) > 0; - } - public bool Reserved(Thing target, out List attackers) - { - if (!TryGetReservedCombatTarget(target, out ReservedCombatTarget store)) - { - attackers = null; - return false; - } - store.Dirty(); - attackers = new List(); - attackers.AddRange(store.Records.Select(r => r.attacker)); - return attackers.Count > 0; - } - - private bool TryGetReservedCombatTarget(Thing target, out ReservedCombatTarget store) - { - if (target == null || !target.Spawned) - { - store = null; - return false; - } - if (!reservations.TryGetValue(target, out store)) - { - store = new ReservedCombatTarget(target); - reservations[target] = store; - } - return true; - } - - private struct PawnReservationRecord : IExposable, IEquatable - { - public Pawn attacker; - public Thing target; - - public Pawn TargetPawn - { - get => target as Pawn; - } - - private bool _tragetIsPawn; - public bool TargetIsPawn - { - get => _tragetIsPawn; - } - - public bool IsValid - { - get => (attacker?.Spawned ?? false) - && (target?.Spawned ?? false) - && !attacker.Downed - && attacker.mindState?.enemyTarget != null - && attacker.mindState?.enemyTarget == target; - } - - public PawnReservationRecord(Pawn attacker, Thing target) - { - this.attacker = attacker; - this.target = target; - _tragetIsPawn = target is Pawn; - } - - public void ExposeData() - { - Scribe_References.Look(ref attacker, "attacker"); - Scribe_References.Look(ref target, "target"); - Scribe_Values.Look(ref _tragetIsPawn, "_tragetIsPawn"); - } - - public bool Equals(PawnReservationRecord other) - { - return other.attacker == attacker && other.target == target; - } - public override bool Equals(object obj) - { - return obj is PawnReservationRecord other ? other.Equals(this) : false; - } - public override int GetHashCode() - { - int hash = 17; - unchecked - { - hash = hash * 7919 ^ (target?.thingIDNumber ?? 0); - hash = hash * 7919 ^ (attacker?.thingIDNumber ?? 0); - hash = hash * 7919 ^ (_tragetIsPawn ? 13 : 0); - } - return hash; - } - } - - private class ReservedCombatTarget : IExposable - { - private int _reservationsDirtyAt = -1; - private int _reservationsNum; - private List reservations = new List(4); - - private Thing target; - - public ReservedCombatTarget() - { - } - - public ReservedCombatTarget(Thing thing) - { - target = thing; - } - - public Thing Target - { - get => target; - } - - public int RecordCount - { - get - { - if (_reservationsDirtyAt < GenTicks.TicksGame) - { - Dirty(); - } - return _reservationsNum; - } - } - - public List Records - { - get - { - if (_reservationsDirtyAt < GenTicks.TicksGame) - { - Dirty(); - } - return reservations; - } - } - - public bool IsValid - { - get => target != null && target.Spawned; - } - public void ExposeData() - { - if (Scribe.mode == LoadSaveMode.Saving) - { - reservations.RemoveAll(r => !r.IsValid); - } - Scribe_References.Look(ref target, "target"); - Scribe_Collections.Look(ref reservations, "reservations", LookMode.Deep); - reservations ??= new List(); - } - - public void Add(Pawn attacker) - { - if (reservations.All(r => r.attacker != attacker)) - { - reservations.Add(new PawnReservationRecord(attacker, target)); - Dirty(); - } - } - public void Dirty() - { - reservations.RemoveAll(r => !r.IsValid); - - _reservationsDirtyAt = GenTicks.TicksGame + 30; - _reservationsNum = reservations.Count; - } - } - } -} diff --git a/Source/Rule56/Comps/ThingComp_CombatAI.cs b/Source/Rule56/Comps/ThingComp_CombatAI.cs index c741284..dc87961 100644 --- a/Source/Rule56/Comps/ThingComp_CombatAI.cs +++ b/Source/Rule56/Comps/ThingComp_CombatAI.cs @@ -251,7 +251,7 @@ public void OnScanFinished() float bestEnemyScore = verb.currentTarget.IsValid && verb.currentTarget.Cell.IsValid ? verb.currentTarget.Cell.DistanceToSquared(pawnPosition) : 1e6f; bool bestEnemyVisibleNow = warmup != null; bool retreat = false; - bool canRetreat = pawn.RaceProps.baseHealthScale <= 2.0f && pawn.RaceProps.baseBodySize <= 2.2f; + bool canRetreat = Finder.Settings.Retreat_Enabled && pawn.RaceProps.baseHealthScale <= 2.0f && pawn.RaceProps.baseBodySize <= 2.2f; float retreatDistSqr = Maths.Max(verb.EffectiveRange * verb.EffectiveRange / 9, 36); foreach (Thing enemy in visibleEnemies) { @@ -272,7 +272,7 @@ public void OnScanFinished() float distSqr = pawnPosition.DistanceToSquared(shiftedPos); if (canRetreat && distSqr < retreatDistSqr) { - if (enemyPawn != null && distSqr < 49) + if (enemyPawn != null && distSqr < 81) { bestEnemy = enemy; retreat = true; @@ -329,14 +329,18 @@ public void OnScanFinished() request.caster = pawn; request.target = new LocalTargetInfo(bestEnemyPositon); request.verb = verb; - request.maxRangeFromCaster = Maths.Min(retreatDistSqr * 2 / (pawn.BodySize + 0.01f), 15); + request.maxRangeFromCaster = Maths.Min(Mathf.Max(retreatDistSqr * 2 / (pawn.BodySize + 0.01f), 5), 15); request.checkBlockChance = true; if (CoverPositionFinder.TryFindRetreatPosition(request, out IntVec3 cell) && cell != pawnPosition) { - Job job_goto = JobMaker.MakeJob(JobDefOf.Goto, cell); - job_goto.locomotionUrgency = LocomotionUrgency.Sprint; - pawn.jobs.ClearQueuedJobs(); - pawn.jobs.StartJob(moveJob = job_goto, JobCondition.InterruptForced); +// if (Rand.Chance((sightReader.GetThreat(pawn.Position) - sightReader.GetThreat(cell)) + 0.1f)) +// { + Job job_goto = JobMaker.MakeJob(JobDefOf.Goto, cell); + job_goto.locomotionUrgency = LocomotionUrgency.Sprint; + pawn.jobs.ClearQueuedJobs(); + pawn.jobs.StopAll(); + pawn.jobs.StartJob(moveJob = job_goto, JobCondition.InterruptForced); +// } } else if (warmup == null) { @@ -447,7 +451,7 @@ public void OnScanFinished() public void Notify_TookDamage(DamageInfo dInfo) { // if the pawn is tanky enough skip. - if (parent.Spawned && GenTicks.TicksGame - lastScanned < 90 && parent is Pawn pawn && !pawn.Dead && !pawn.Downed && armor.TankInt < 0.4f) + if (Finder.Settings.Retreat_Enabled && parent.Spawned && GenTicks.TicksGame - lastScanned < 90 && parent is Pawn pawn && !pawn.Dead && !pawn.Downed && armor.TankInt < 0.4f) { if (!pawn.RaceProps.IsMechanoid && dInfo.Def != null && dInfo.Instigator != null) { @@ -475,12 +479,15 @@ public void Notify_TookDamage(DamageInfo dInfo) request.checkBlockChance = true; if (CoverPositionFinder.TryFindRetreatPosition(request, out IntVec3 cell) && cell != pawnPosition) { - Job job_goto = JobMaker.MakeJob(JobDefOf.Goto, cell); - job_goto.locomotionUrgency = LocomotionUrgency.Sprint; - Job job_waitCombat = JobMaker.MakeJob(JobDefOf.Wait_Combat, Rand.Int % 100 + 100); - pawn.jobs.StartJob(moveJob = job_goto, JobCondition.InterruptForced); - pawn.jobs.ClearQueuedJobs(); - pawn.jobs.jobQueue.EnqueueFirst(job_waitCombat); + if (Rand.Chance(sightReader.GetVisibilityToEnemies(pawn.Position) - sightReader.GetVisibilityToEnemies(cell)) || Rand.Chance(sightReader.GetThreat(pawn.Position) - sightReader.GetThreat(cell))) + { + Job job_goto = JobMaker.MakeJob(JobDefOf.Goto, cell); + job_goto.locomotionUrgency = LocomotionUrgency.Sprint; + Job job_waitCombat = JobMaker.MakeJob(JobDefOf.Wait_Combat, Rand.Int % 100 + 100); + pawn.jobs.StartJob(moveJob = job_goto, JobCondition.InterruptForced); + pawn.jobs.ClearQueuedJobs(); + pawn.jobs.jobQueue.EnqueueFirst(job_waitCombat); + } } lastRetreated = GenTicks.TicksGame - Rand.Int % 50; } diff --git a/Source/Rule56/CoverPositionFinder.cs b/Source/Rule56/CoverPositionFinder.cs index 2ec0de6..51a0ecb 100644 --- a/Source/Rule56/CoverPositionFinder.cs +++ b/Source/Rule56/CoverPositionFinder.cs @@ -1,4 +1,7 @@ -using Verse; +using System; +using System.Collections.Generic; +using UnityEngine; +using Verse; using static CombatAI.AvoidanceTracker; using static CombatAI.SightTracker; @@ -6,6 +9,9 @@ namespace CombatAI { public static class CoverPositionFinder { + private static readonly Dictionary parentTree = new Dictionary(512); + private static readonly Dictionary scores = new Dictionary(512); + public static bool TryFindCoverPosition(CoverPositionRequest request, out IntVec3 coverCell) { request.caster.TryGetSightReader(out SightReader sightReader); @@ -95,24 +101,32 @@ public static bool TryFindRetreatPosition(CoverPositionRequest request, out IntV { request.maxRangeFromLocus = float.MaxValue; } - InterceptorTracker interceptors = map.GetComp_Fast().interceptors; - float maxDistSqr = request.maxRangeFromLocus * request.maxRangeFromLocus; - CellFlooder flooder = map.GetCellFlooder(); - IntVec3 enemyLoc = request.target.Cell; - IntVec3 bestCell = IntVec3.Invalid; - float rootVis = sightReader.GetVisibilityToEnemies(request.locus); - float rootVisFriendlies = sightReader.GetVisibilityToFriendlies(request.locus); - float rootThreat = sightReader.GetThreat(request.locus); - float bestCellDist = request.locus.DistanceToSquared(enemyLoc); - float bestCellScore = 1e8f; + parentTree.Clear(); + scores.Clear(); + InterceptorTracker interceptors = map.GetComp_Fast().interceptors; + CellIndices indices = map.cellIndices; + float adjustedMaxDist = request.maxRangeFromLocus * 2; + float adjustedMaxDistSqr = adjustedMaxDist * adjustedMaxDist; + CellFlooder flooder = map.GetCellFlooder(); + IntVec3 enemyLoc = request.target.Cell; + IntVec3 bestCell = IntVec3.Invalid; + float rootVis = sightReader.GetVisibilityToEnemies(request.locus); + float rootVisFriendlies = sightReader.GetVisibilityToFriendlies(request.locus); + float rootThreat = sightReader.GetThreat(request.locus); + float bestCellDist = request.locus.DistanceToSquared(enemyLoc); + float bestCellScore = 1e8f; flooder.Flood(request.locus, node => { - if (maxDistSqr < request.locus.DistanceToSquared(node.cell) || !map.reservationManager.CanReserve(caster, node.cell)) + parentTree[node.cell] = node.parent; + + if (adjustedMaxDistSqr < request.locus.DistanceToSquared(node.cell) || !map.reservationManager.CanReserve(caster, node.cell)) { return; } + // do math float c = (node.dist - node.distAbs) / (node.distAbs + 1f) + avoidanceReader.GetProximity(node.cell) * 0.5f - interceptors.grid.Get(node.cell) + (sightReader.GetThreat(node.cell) - rootThreat) * 0.75f; + if (c < bestCellScore) { float d = node.cell.DistanceToSquared(enemyLoc); @@ -123,7 +137,7 @@ public static bool TryFindRetreatPosition(CoverPositionRequest request, out IntV bestCell = node.cell; } } - //map.debugDrawer.FlashCell(node.cell, c / 5f, text: $"{Math.Round(c, 2)}"); + scores[node.cell] = c; }, cell => { @@ -133,8 +147,30 @@ public static bool TryFindRetreatPosition(CoverPositionRequest request, out IntV { return (request.validator == null || request.validator(cell)) && cell.WalkableBy(map, caster); }, - (int)Maths.Min(request.maxRangeFromLocus, 30) + (int)Maths.Min(adjustedMaxDist, 45f) ); +// if (Find.Selector.SelectedPawns.Contains(caster)) +// { +// foreach (var v in scores) +// { +// map.debugDrawer.FlashCell(v.Key, (v.Value + 15f) / 30f, $"{Math.Round(v.Value, 2)}"); +// } +// } + if (!bestCell.IsValid) + { + coverCell = IntVec3.Invalid; + return false; + } + int distSqr = Mathf.CeilToInt(request.maxRangeFromLocus * request.maxRangeFromLocus); + if (bestCellDist > distSqr) + { + IntVec3 cell = bestCell; + while (parentTree.TryGetValue(cell, out IntVec3 parent) && parent != cell && parent.DistanceToSquared(request.locus) > distSqr) + { + cell = parent; + } + bestCell = cell; + } coverCell = bestCell; return bestCell.IsValid; } diff --git a/Source/Rule56/Debugging/CombatAI_DebugTooltip.cs b/Source/Rule56/Debugging/CombatAI_DebugTooltip.cs new file mode 100644 index 0000000..210bdd9 --- /dev/null +++ b/Source/Rule56/Debugging/CombatAI_DebugTooltip.cs @@ -0,0 +1,46 @@ +using System; +using UnityEngine; +namespace CombatAI +{ + /* + * Taken from my original commit to CE where I introduced this debugging tool. + * https://github.com/CombatExtended-Continued/CombatExtended/tree/31446370a92855de32c709bcd8ff09039db85452 + */ + + /// + /// Important: the return type should be string. + /// Used to allow for access to debug information anywhere via a tooltip. + /// This will be called when the left shift button is down or incase alternative key is not none, it will one will be + /// required. + /// A call to this function should return a string to be added to the tooltip. + /// Usage: + /// Requires that the function has (Map map, IntVec3 pos) as input parameters for usage within maps. + /// Requires that the function has (World map, int tile) as input parameters for usage within the world map. + /// Requires that the flaged function has a return type of string. + /// + [AttributeUsage(AttributeTargets.Method)] + public class CombatAI_DebugTooltip : Attribute + { + public readonly KeyCode altKey; + public readonly CombatAI_DebugTooltipType tooltipType; + + /// + /// Important: the return type should be string. + /// Used to allow for access to debug information anywhere via a tooltip. + /// This will be called when the left shift button is down or incase alternative key is not none, it will one will be + /// required. + /// A call to this function should return a string to be added to the tooltip. + /// Usage: + /// Requires that the function has (Map map, IntVec3 pos) as input parameters for usage within maps. + /// Requires that the function has (World map, int tile) as input parameters for usage within the world map. + /// Requires that the flaged function has a return type of string. + /// + /// Where to call this function + /// alternative key instead of left shift + public CombatAI_DebugTooltip(CombatAI_DebugTooltipType tooltipType, KeyCode altKey = KeyCode.None) + { + this.tooltipType = tooltipType; + this.altKey = altKey; + } + } +} diff --git a/Source/Rule56/Debugging/CombatAI_DebugTooltipHelper.cs b/Source/Rule56/Debugging/CombatAI_DebugTooltipHelper.cs new file mode 100644 index 0000000..c092857 --- /dev/null +++ b/Source/Rule56/Debugging/CombatAI_DebugTooltipHelper.cs @@ -0,0 +1,149 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using HarmonyLib; +using RimWorld; +using RimWorld.Planet; +using UnityEngine; +using Verse; +namespace CombatAI +{ + /* + * Taken from my original commit to CE where I introduced this debugging tool. + * https://github.com/CombatExtended-Continued/CombatExtended/tree/31446370a92855de32c709bcd8ff09039db85452 + */ + public class CombatAI_DebugTooltipHelper : GameComponent + { + + private static readonly List, KeyCode>> mapCallbacks = new List, KeyCode>>(); + private static readonly List, KeyCode>> worldCallbacks = new List, KeyCode>>(); + + private static readonly Rect MouseRect = new Rect(0, 0, 50, 50); + private readonly StringBuilder builder = new StringBuilder(); + + static CombatAI_DebugTooltipHelper() + { + IEnumerable functions = typeof(CombatAI_DebugTooltipHelper).Assembly + .GetTypes() + .SelectMany(t => t.GetMethods(AccessTools.all)) + .Where(m => m.HasAttribute() && m.IsStatic); + foreach (MethodBase m in functions) + { + CombatAI_DebugTooltip attribute = m.TryGetAttribute(); + if (attribute.tooltipType == CombatAI_DebugTooltipType.World) + { + ParameterInfo[] param = m.GetParameters(); + if (param[0].ParameterType != typeof(World) || param[1].ParameterType != typeof(int)) + { + Log.Error($"ISMA: Error processing debug tooltip {m.GetType().Name}:{m.Name} {m.FullDescription()} need to have (World, int) as parameters, skipped"); + continue; + } + worldCallbacks.Add(new Pair, KeyCode>((world, tile) => (string)m.Invoke(null, new object[] + { + world, tile + }), attribute.altKey)); + } + else if (attribute.tooltipType == CombatAI_DebugTooltipType.Map) + { + ParameterInfo[] param = m.GetParameters(); + if (param[0].ParameterType != typeof(Map) || param[1].ParameterType != typeof(IntVec3)) + { + Log.Error($"ISMA: Error processing debug tooltip {m.GetType().Name}:{m.Name} {m.FullDescription()} need to have (Map, IntVec3) as parameters, skipped"); + continue; + } + mapCallbacks.Add(new Pair, KeyCode>((map, cell) => (string)m.Invoke(null, new object[] + { + map, cell + }), attribute.altKey)); + } + } + } + + public CombatAI_DebugTooltipHelper(Game game) + { + } + + public override void GameComponentOnGUI() + { + base.GameComponentOnGUI(); + if (!Finder.Settings.Debug || !Input.anyKey || Find.CurrentMap == null || Current.ProgramState != ProgramState.Playing) + { + return; + } + Rect mouseRect; + mouseRect = MouseRect; + mouseRect.center = Event.current.mousePosition; + Camera worldCamera = Find.WorldCamera; + + if (!worldCamera.gameObject.activeInHierarchy) + { + IntVec3 mouseCell = UI.MouseCell(); + if (mouseCell.InBounds(Find.CurrentMap)) + { + TryMapTooltips(mouseRect, mouseCell); + } + } + else + { + int tile = GenWorld.MouseTile(); + if (tile != -1) + { + TryWorldTooltips(mouseRect, tile); + } + } + } + + private void TryMapTooltips(Rect mouseRect, IntVec3 mouseCell) + { + bool bracketShown = false; + for (int i = 0; i < mapCallbacks.Count; i++) + { + Pair, KeyCode> callback = mapCallbacks[i]; + if (Input.GetKey(callback.Second == KeyCode.None ? KeyCode.LeftShift : callback.Second)) + { + string message = callback.First(Find.CurrentMap, mouseCell); + if (!message.NullOrEmpty()) + { + DoTipSignal(mouseRect, message, i); + } + if (!bracketShown) + { + GenUI.RenderMouseoverBracket(); + bracketShown = true; + } + } + } + } + + private void TryWorldTooltips(Rect mouseRect, int tile) + { + for (int i = 0; i < worldCallbacks.Count; i++) + { + Pair, KeyCode> callback = worldCallbacks[i]; + if (Input.GetKey(callback.Second == KeyCode.None ? KeyCode.LeftShift : callback.Second)) + { + string message = callback.First(Find.World, tile); + if (!message.NullOrEmpty()) + { + DoTipSignal(mouseRect, message, i); + } + } + } + } + + private TipSignal DoTipSignal(Rect rect, string message, int id) + { + builder.Clear(); + builder.Append("DEBUG_CAI:. "); + builder.AppendLine(message); + TipSignal tip = new TipSignal(); + tip.text = builder.ToString(); + tip.uniqueId = id + 1037; + tip.priority = (TooltipPriority)3; + TooltipHandler.TipRegion(rect, tip); + return tip; + } + } +} diff --git a/Source/Rule56/Debugging/CombatAI_DebugTooltipType.cs b/Source/Rule56/Debugging/CombatAI_DebugTooltipType.cs new file mode 100644 index 0000000..7d6667c --- /dev/null +++ b/Source/Rule56/Debugging/CombatAI_DebugTooltipType.cs @@ -0,0 +1,19 @@ +namespace CombatAI +{ + /* + * Taken from my original commit to CE where I introduced this debugging tool. + * https://github.com/CombatExtended-Continued/CombatExtended/tree/31446370a92855de32c709bcd8ff09039db85452 + */ + + public enum CombatAI_DebugTooltipType + { + /// + /// World map tooltip. + /// + World = 1, + /// + /// Map tooltip. + /// + Map = 2 + } +} diff --git a/Source/Rule56/Debugging/CombatAI_DebugTooltipUtility.cs b/Source/Rule56/Debugging/CombatAI_DebugTooltipUtility.cs new file mode 100644 index 0000000..cecc199 --- /dev/null +++ b/Source/Rule56/Debugging/CombatAI_DebugTooltipUtility.cs @@ -0,0 +1,25 @@ +using System; +using RimWorld.Planet; +using Verse; +namespace CombatAI +{ + /* + * Taken from my original commit to CE where I introduced this debugging tool. + * https://github.com/CombatExtended-Continued/CombatExtended/tree/31446370a92855de32c709bcd8ff09039db85452 + */ + + public class CombatAI_DebugTooltipUtility + { + [CombatAI_DebugTooltip(CombatAI_DebugTooltipType.Map)] + public static string CellPositionTip(Map map, IntVec3 cell) + { + return $"Cell: ({cell.x}, {cell.z})"; + } + + [CombatAI_DebugTooltip(CombatAI_DebugTooltipType.World)] + public static string TileIndexTip(World world, int tile) + { + return $"Tile index:\t\t{tile}"; + } + } +} diff --git a/Source/Rule56/ITRegionGrid.cs b/Source/Rule56/ITRegionGrid.cs new file mode 100644 index 0000000..3d64fc7 --- /dev/null +++ b/Source/Rule56/ITRegionGrid.cs @@ -0,0 +1,383 @@ +using System; +using Verse; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using RimWorld; +namespace CombatAI +{ + public class ITRegionGrid + { + private readonly Map map; + private readonly CellIndices cellIndices; + private readonly IFieldInfo[] regions; + private readonly IField[] regions_flags; + private readonly IField[] regions_meta; + private readonly IField[] regions_blunt; + private readonly IField[] regions_sharp; + private readonly int[] cells_ids; + + private readonly int NumGridCells; + private short r_sig = 19; + + private float curBlunt; + private float curSharp; + private UInt64 curFlag; + private MetaCombatAttribute curMeta; + + public ITRegionGrid(Map map) + { + this.map = map; + cellIndices = map.cellIndices; + NumGridCells = cellIndices.NumGridCells; + cells_ids = new int[NumGridCells]; + regions = new IFieldInfo[short.MaxValue]; + regions_flags = new IField[short.MaxValue]; + regions_meta = new IField[short.MaxValue]; + regions_blunt = new IField[short.MaxValue]; + regions_sharp = new IField[short.MaxValue]; + for (int i = 0; i < NumGridCells; i++) + { + cells_ids[i] = -1; + } + } + + public short CycleNum + { + get; + private set; + } = 19; + + /// + /// Set region by id. + /// + /// Cell + public void Set(IntVec3 cell) + { + Set(cellIndices.CellToIndex(cell)); + } + /// + /// Set region by id. + /// + /// Cell index. + public void Set(int index) + { + if (index >= 0 && index < NumGridCells) + { + int id = cells_ids[index]; + if (id >= 0 && id < short.MaxValue) + { + IFieldInfo info = regions[id]; + if (info.sig != r_sig) + { + int dc = CycleNum - info.cycle; + if (dc == 0) + { + info.num += 1; + regions_flags[id].value |= curFlag; + regions_sharp[id].value = Maths.Max(curSharp, regions_sharp[id].value); + regions_blunt[id].value = Maths.Max(curBlunt, regions_blunt[id].value); + regions_meta[id].value |= curMeta; + } + else + { + bool expired = dc > 1; + if (expired) + { + info.numPrev = 0; + } + else + { + info.numPrev = info.num; + } + info.num = 1; + regions_flags[id].ReSet(curFlag, expired); + regions_sharp[id].ReSet(curSharp, expired); + regions_blunt[id].ReSet(curBlunt, expired); + regions_meta[id].ReSet(curMeta, expired); + } + info.cycle = CycleNum; + info.sig = r_sig; + regions[id] = info; + } + } + } + } + /// + /// Update the region id grids. + /// + /// Cell + /// Region + public void SetRegionAt(IntVec3 cell, Region region) + { + SetRegionAt(cellIndices.CellToIndex(cell), region); + } + /// + /// Update the region id grids. + /// + /// Cell index + /// region + public void SetRegionAt(int index, Region region) + { + if (index >= 0 && index < NumGridCells) + { + cells_ids[index] = region?.id ?? -1; + } + } + + /// + /// Returns number of sources who can view a region. + /// + /// Region. + /// Number of sources. + public int GetSignalNumByRegion(Region region) + { + if (region != null) + { + return GetSignalNumById(region.id); + } + return 0; + } + /// + /// Returns number of sources who can view a region. + /// + /// Region id. + /// Number of sources. + public int GetSignalNumById(int id) + { + if (id != -1) + { + IFieldInfo cell = regions[id]; + switch (CycleNum - cell.cycle) + { + case 0: + return Maths.Max(cell.num, cell.numPrev); + case 1: + return cell.num; + default: + return 0; + } + } + return 0; + } + /// + /// Returns avg sharp at a region. + /// + /// Region. + /// Sharp + public float GetSharpAt(Region region) + { + if (region != null) + { + return GetSharpAt(region.id); + } + return 0; + } + /// + /// Returns avg sharp at a region. + /// + /// Region id + /// Sharp + public float GetSharpAt(int id) + { + if (id != -1) + { + IFieldInfo cell = regions[id]; + switch (CycleNum - cell.cycle) + { + case 0: + IField field = regions_sharp[id]; + return Maths.Max(field.value, field.valuePrev); + case 1: + return regions_sharp[id].value; + default: + return 0; + } + } + return 0; + } + /// + /// Returns avg blunt at a region. + /// + /// Region. + /// Blunt + public float GetBluntAt(Region region) + { + if (region != null) + { + return GetBluntAt(region.id); + } + return 0; + } + /// + /// Returns avg blunt at a region. + /// + /// Region id + /// Blunt + public float GetBluntAt(int id) + { + if (id != -1) + { + IFieldInfo cell = regions[id]; + switch (CycleNum - cell.cycle) + { + case 0: + IField field = regions_blunt[id]; + return Maths.Max(field.value, field.valuePrev); + case 1: + return regions_blunt[id].value; + default: + return 0; + } + } + return 0; + } + /// + /// Return region meta combat attributes. + /// + /// Region + /// Meta combat attribute + public MetaCombatAttribute GetCombatAttributesAt(Region region) + { + if (region != null) + { + return GetCombatAttributesAt(region.id); + } + return 0; + } + /// + /// Return region meta combat attributes. + /// + /// Region id + /// Meta combat attribute + public MetaCombatAttribute GetCombatAttributesAt(int id) + { + if (id != -1) + { + IFieldInfo cell = regions[id]; + switch (CycleNum - cell.cycle) + { + case 0: + IField field = regions_meta[id]; + return field.value | field.valuePrev; + case 1: + return regions_meta[id].value; + default: + return 0; + } + } + return 0; + } + /// + /// Return region flags. + /// + /// Region + /// Region flags. + public UInt64 GetFlagsAt(Region region) + { + if (region != null) + { + return GetFlagsAt(region.id); + } + return 0; + } + /// + /// Return region flags. + /// + /// Region id + /// Region flags. + public UInt64 GetFlagsAt(int id) + { + if (id != -1) + { + IFieldInfo cell = regions[id]; + switch (CycleNum - cell.cycle) + { + case 0: + IField field = regions_flags[id]; + return field.value | field.valuePrev; + case 1: + return regions_flags[id].value; + default: + return 0; + } + } + return 0; + } + /// + /// Returns region id for cell. + /// + /// Cell. + /// Region id + public int GetRegionId(IntVec3 cell) + { + return GetRegionId(cellIndices.CellToIndex(cell)); + } + /// + /// Returns region id for cell index. + /// + /// Cell index. + /// Region id + public int GetRegionId(int index) + { + if (index >= 0 && index < NumGridCells) + { + return cells_ids[index]; + } + return -1; + } + + /// + /// TODO + /// + public void Next(UInt64 flag, float sharp, float blunt, MetaCombatAttribute meta) + { + if (r_sig++ == short.MaxValue) + { + r_sig = 19; + } + curSharp = sharp; + curBlunt = blunt; + curMeta = meta; + curFlag = flag; + } + + /// + /// TODO + /// + public void NextCycle() + { + if (r_sig++ == short.MaxValue) + { + r_sig = 19; + } + if (CycleNum++ == short.MaxValue) + { + CycleNum = 13; + } + } + + private struct IField where T : struct + { + public T value; + public T valuePrev; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ReSet(T newVal, bool expired) + { + valuePrev = expired ? default(T) : value; + value = newVal; + } + } + + [StructLayout(LayoutKind.Sequential)] + private struct IFieldInfo + { + public short cycle; + public short sig; + public short num; + public short numPrev; + } + } +} diff --git a/Source/Rule56/ITSignalGrid.cs b/Source/Rule56/ITSignalGrid.cs index 1ce58f0..25b1f50 100644 --- a/Source/Rule56/ITSignalGrid.cs +++ b/Source/Rule56/ITSignalGrid.cs @@ -16,10 +16,10 @@ namespace CombatAI public class ITSignalGrid { private readonly IFieldInfo[] cells; - private readonly IField[] cells_blunt; private readonly IField[] cells_dir; private readonly IField[] cells_flags; private readonly IField[] cells_meta; + private readonly IField[] cells_blunt; private readonly IField[] cells_sharp; private readonly IField[] cells_strength; @@ -534,6 +534,7 @@ public void ReSet(T newVal, bool expired) value = newVal; } } + [StructLayout(LayoutKind.Sequential)] private struct IFieldInfo diff --git a/Source/Rule56/MapComponent_FogGrid.cs b/Source/Rule56/MapComponent_FogGrid.cs index 1e0faec..af1ff28 100644 --- a/Source/Rule56/MapComponent_FogGrid.cs +++ b/Source/Rule56/MapComponent_FogGrid.cs @@ -132,13 +132,6 @@ public override void MapComponentUpdate() } } - //public override void MapComponentOnGUI() - //{ - // base.MapComponentOnGUI(); - // // - // //Widgets.Label(new Rect(0, 0, 100, 25), $"{zoom} {Find.CameraDriver.rootPos}"); - //} - public override void MapRemoved() { alive = false; @@ -191,7 +184,7 @@ private void OffThreadLoop(int minU, int minV, int maxU, int maxV) } } stopwatch.Stop(); - float t = 0.016f - (float)stopwatch.ElapsedTicks / Stopwatch.Frequency; + float t = 0.064f - (float)stopwatch.ElapsedTicks / Stopwatch.Frequency; if (t > 0f) { Thread.Sleep(Mathf.CeilToInt(t * 1000)); @@ -291,11 +284,11 @@ public void Update() changed = true; if (val > old) { - cells[x * SECTION_SIZE + z] = Maths.Min(old + 0.05f,val); + cells[x * SECTION_SIZE + z] = Maths.Min(old + 0.1f,val); } else { - cells[x * SECTION_SIZE + z] = Maths.Max(old - 0.2f,val); + cells[x * SECTION_SIZE + z] = Maths.Max(old - 0.4f,val); } } } diff --git a/Source/Rule56/Patches/AttackTargetFinder_Patch.cs b/Source/Rule56/Patches/AttackTargetFinder_Patch.cs index 4a62e58..23ae3bf 100644 --- a/Source/Rule56/Patches/AttackTargetFinder_Patch.cs +++ b/Source/Rule56/Patches/AttackTargetFinder_Patch.cs @@ -1,7 +1,9 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Reflection.Emit; using HarmonyLib; +using Mono.Unix.Native; using RimWorld; using UnityEngine; using Verse; @@ -10,14 +12,15 @@ namespace CombatAI.Patches { internal static class AttackTargetFinder_Patch { - private static Map map; - private static Pawn searcherPawn; - private static Faction searcherFaction; - private static Verb searcherVerb; - private static float dmg05; - private static ProjectileProperties projectile; - private static SightTracker.SightReader sightReader; - private static DamageReport damageReport; + private static Map map; + private static Pawn searcherPawn; + private static Faction searcherFaction; + private static Verb searcherVerb; + private static float dmg05; + private static ProjectileProperties projectile; + private static SightTracker.SightReader sightReader; + private static DamageReport damageReport; + private static readonly Dictionary distDict = new Dictionary(256); [HarmonyPatch(typeof(AttackTargetFinder), nameof(AttackTargetFinder.BestAttackTarget))] internal static class AttackTargetFinder_BestAttackTarget_Patch @@ -28,14 +31,47 @@ internal static void Prefix(IAttackTargetSearcher searcher) if (searcher.Thing is Pawn pawn && !pawn.RaceProps.Animal) { + distDict.Clear(); damageReport = DamageUtility.GetDamageReport(searcher.Thing); searcherPawn = pawn; searcherVerb = pawn.CurrentEffectiveVerb; + pawn.TryGetSightReader(out sightReader); + if (sightReader != null) + { + int num = 0; +// if (Find.Selector.SelectedPawns.Contains(pawn)) +// { +// map.debugDrawer.FlashCell(pawn.Position, 0.1f, "s"); +// } + Func action = (region, depth) => + { + List things = region.ListerThings.ThingsInGroup(ThingRequestGroup.Pawn); + depth = Maths.Min(depth, 45); + for (int i = 0; i < things.Count; i++) + { + if (things[i] != null) + { + num++; + distDict[things[i].thingIDNumber] = depth; +// if (Find.Selector.SelectedPawns.Contains(pawn)) +// { +// map.debugDrawer.FlashCell(things[i].Position, 0.9f, $"{depth}"); +// } + } + } + return num >= 32; + }; + float costConst = !Mod_CE.active ? 7.5f : 2.5f; + Func cost = region => + { + return Maths.Min(sightReader.GetRegionAbsVisibilityToEnemies(region), 10) * Mathf.Clamp(sightReader.GetRegionThreat(region) + 0.5f, 1.0f, 2.0f) * costConst; + }; + RegionFlooder.Flood(pawn.Position, pawn.Position, pawn.Map, action, null, cost, maxRegions: !Finder.Performance.TpsCriticallyLow ? 200 : 75); + } if (searcherVerb != null && !searcherVerb.IsMeleeAttack && (projectile = searcherVerb.GetProjectile()?.projectile ?? null) != null) { dmg05 = projectile.damageAmountBase / 2f; } - pawn.TryGetSightReader(out sightReader); } searcherFaction = searcher.Thing?.Faction ?? null; } @@ -46,6 +82,7 @@ internal static void Postfix() damageReport = default(DamageReport); searcherPawn = null; sightReader = null; + distDict.Clear(); } } @@ -77,8 +114,16 @@ internal static IEnumerable Transpiler(IEnumerable sightReader.GetAbsVisibilityToFriendlies(target.Thing.Position) + 1) @@ -106,19 +151,6 @@ public static float GetTargetBaseScore(IAttackTarget target, IAttackTargetSearch diff = Mathf.Clamp01(1 - Maths.Max(armorReport.Blunt - damageReport.adjustedBlunt, armorReport.Sharp - damageReport.adjustedSharp, 0f)); } result += 8f * diff; - //var armor = armorReport.GetArmor(projectile.damageDef); - //var damage = damageReport.GetAdjustedDamage(projectile.damageDef.armorCategory); - //if (projectile.armorPenetrationBase > 0) - //{ - // result += Mathf.Lerp(0f, 12f, damageReport.ad); - //if (Find.Selector.SelectedPawns.Contains(searcher.Thing as Pawn)) - //{ - // map.debugDrawer.FlashCell(target.Thing.Position, diff, $"{diff}"); - //} - //else - //{ - // result -= Maths.Min(armor * 0.5f, 8f); - //} } Thing targeted; if (enemy.stances?.stagger != null && enemy.stances.stagger.Staggered) diff --git a/Source/Rule56/Patches/CastPositionFinder_Patch.cs b/Source/Rule56/Patches/CastPositionFinder_Patch.cs index d345e6e..2f68cc4 100644 --- a/Source/Rule56/Patches/CastPositionFinder_Patch.cs +++ b/Source/Rule56/Patches/CastPositionFinder_Patch.cs @@ -51,7 +51,7 @@ public static void Prefix(ref CastPositionRequest newReq) newReq.maxRangeFromTarget = 0; } skipped = true; - if (newReq.caster != null && newReq.target != null && (newReq.maxRangeFromTarget == 0 || newReq.maxRangeFromTarget * newReq.maxRangeFromTarget > newReq.caster.Position.DistanceToSquared(newReq.target.Position)) && newReq.maxRangeFromLocus == 0) + if (newReq.caster != null && Finder.Settings.Caster_Enabled && newReq.target != null && (newReq.maxRangeFromTarget == 0 || newReq.maxRangeFromTarget * newReq.maxRangeFromTarget > newReq.caster.Position.DistanceToSquared(newReq.target.Position)) && newReq.maxRangeFromLocus == 0) { if ((pawn = newReq.caster) != null && !(pawn.RaceProps?.Animal ?? true) && !((pawn.Faction?.IsPlayerSafe() ?? false) && !pawn.Drafted && pawn.mindState?.duty == null) && !(pawn.mindState != null && (pawn.mindState.duty?.def == DutyDefOf.Sapper || pawn.mindState.duty?.def == DutyDefOf.Breaching))) { diff --git a/Source/Rule56/Patches/JobGiver_AIGotoNearestHostile_Patch.cs b/Source/Rule56/Patches/JobGiver_AIGotoNearestHostile_Patch.cs index 1c327ec..231e4a5 100644 --- a/Source/Rule56/Patches/JobGiver_AIGotoNearestHostile_Patch.cs +++ b/Source/Rule56/Patches/JobGiver_AIGotoNearestHostile_Patch.cs @@ -1,107 +1,60 @@ -namespace CombatAI.Patches +using System.Collections.Generic; +using HarmonyLib; +using RimWorld; +using UnityEngine; +using Verse; +using Verse.AI; +namespace CombatAI.Patches { - //public static class JobGiver_AIGotoNearestHostile_Patch - //{ - // [HarmonyPatch(typeof(JobGiver_AIGotoNearestHostile), nameof(JobGiver_AIGotoNearestHostile.TryGiveJob))] - // private static class JobGiver_AIGotoNearestHostile_TryGiveJob_Patch - // { - // private static Pawn attacker; - // private static IntVec3 attackerLoc; - // private static DamageReport attackerDamage; - // private static ArmorReport attackerArmor; - // private static SightTracker.SightReader reader; - // private static MethodInfo mIntVec3Utility = AccessTools.Method(typeof(IntVec3Utility), nameof(IntVec3Utility.DistanceToSquared)); - // - // public static void Prefix(Pawn pawn) - // { - // if (pawn != null) - // { - // attacker = pawn; - // attackerArmor = ArmorUtility.GetArmorReport(pawn); - // attackerLoc = pawn.Position; - // if (!attacker.GetSightReader(out reader) || !(attackerDamage = DamageUtility.GetDamageReport(attacker)).IsValid) - // { - // attacker = null; - // reader = null; - // attackerLoc = IntVec3.Invalid; - // attackerArmor = default(ArmorReport); - // attackerDamage = default(DamageReport); - // } - // } - // } - // - // public static void Postfix() - // { - // reader = null; - // attacker = null; - // attackerLoc = IntVec3.Invalid; - // attackerArmor = default(ArmorReport); - // attackerDamage = default(DamageReport); - // } - // - // public static IEnumerable Transpiler(IEnumerable instructions) - // { - // List codes = instructions.ToList(); - // bool finished = false; - // for(int i = 0;i < codes.Count; i++) - // { - // yield return codes[i]; - // if (!finished) - // { - // if (codes[i].opcode == OpCodes.Call && codes[i].OperandIs(mIntVec3Utility)) - // { - // finished = true; - // yield return new CodeInstruction(OpCodes.Ldloc_S, 6); - // yield return new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(JobGiver_AIGotoNearestHostile_TryGiveJob_Patch), nameof(JobGiver_AIGotoNearestHostile_TryGiveJob_Patch.DistanceToSquaredOffset))); - // yield return new CodeInstruction(OpCodes.Add); - // } - // } - // } - // } - // - // public static int DistanceToSquaredOffset(int distSqr, Thing enemy) - // { - // if(attacker == null) - // { - // return distSqr; - // } - // IntVec3 targetLoc = enemy.Position; - // Vector2 targetDir = new Vector2(targetLoc.x - attackerLoc.x, targetLoc.z - attackerLoc.z); - // - // Vector2 enemiesDir = reader.GetEnemyDirection(enemy.Position); - // float distEnemiesSqr = enemiesDir.SqrMagnitude(); - // if() - // if (distSqr > distEnemiesSqr) - // { - // distSqr = Mathf.CeilToInt(Mathf.CeilToInt(distSqr - 2 * Maths.Max(enemiesDir.x, enemiesDir.y) * Maths.Max(targetDir.x, targetDir.y) + distEnemiesSqr)); - // } - // else if(distSqr < distEnemiesSqr) - // { - // distSqr = -1 * Mathf.CeilToInt(Mathf.CeilToInt(distSqr - 2 * Maths.Max(enemiesDir.x, enemiesDir.y) * Maths.Max(targetDir.x, targetDir.y) + distEnemiesSqr)); - // } - // else - // { - // distSqr = 0; - // } - // DamageReport targetDamage = DamageUtility.GetDamageReport(enemy); - // if (distSqr != 0 && targetDamage.IsValid) - // { - // if (attackerDamage.primaryIsRanged) - // { - // } - // else - // { - // if (enemy is Pawn enemyPawn) - // { - // ArmorReport targetArmor = ArmorUtility.GetArmorReport(enemyPawn); - // float threatToTarget = attackerDamage.ThreatTo(targetArmor); - // float threatToAttacker = targetDamage.ThreatTo(attackerArmor); - // return distSqr * Mathf.Max(threatToAttacker - threatToTarget, 0.25f, 0.1f); - // } - // } - // } - // return distSqr; - // } - // } - //} + public static class JobGiver_AIGotoNearestHostile_Patch + { + [HarmonyPatch(typeof(JobGiver_AIGotoNearestHostile), nameof(JobGiver_AIGotoNearestHostile.TryGiveJob))] + private static class JobGiver_AIGotoNearestHostile_TryGiveJob_Patch + { + public static bool Prefix(Pawn pawn, ref Job __result) + { + if (pawn.TryGetSightReader(out SightTracker.SightReader reader)) + { + Thing nearestEnemy = null; + RegionFlooder.Flood(pawn.Position, + pawn.mindState.enemyTarget == null ? pawn.Position : pawn.mindState.enemyTarget.Position, pawn.Map, + (region, depth) => + { + if (reader.GetRegionAbsVisibilityToEnemies(region) > 0) + { + List things = region.ListerThings.ThingsInGroup(ThingRequestGroup.Pawn); + if (things != null) + { + for (int i = 0; i < things.Count; i++) + { + Thing thing = things[i]; + Pawn other; + if (thing is IAttackTarget target && !target.ThreatDisabled(pawn) && AttackTargetFinder.IsAutoTargetable(target) && thing.HostileTo(pawn) && ((other = thing as Pawn) == null || other.IsCombatant())) + { + nearestEnemy = thing; + return true; + } + } + } + } + return false; + }, + cost: region => + { + return Maths.Min(reader.GetRegionAbsVisibilityToEnemies(region), 8 * Finder.P50) * 10 * Mathf.Clamp(reader.GetRegionThreat(region) + 0.5f, 1.0f, 2.0f); + }, maxRegions:512); + if (nearestEnemy != null) + { + Job job = JobMaker.MakeJob(JobDefOf.Goto, nearestEnemy); + job.checkOverrideOnExpire = true; + job.expiryInterval = 500; + job.collideWithPawns = true; + __result = job; + return false; + } + } + return true; + } + } + } } diff --git a/Source/Rule56/Patches/PathFinder_Patch.cs b/Source/Rule56/Patches/PathFinder_Patch.cs index bdb85b2..1654e39 100644 --- a/Source/Rule56/Patches/PathFinder_Patch.cs +++ b/Source/Rule56/Patches/PathFinder_Patch.cs @@ -318,7 +318,7 @@ private static int GetCostOffsetAt(int index, int parentIndex, int openNum) } } } - if (avoidanceReader != null && !isPlayer) + if (avoidanceReader != null && !isPlayer && Finder.Settings.Flank_Enabled) { if (value > 3) { diff --git a/Source/Rule56/Patches/RegionGrid_Patch.cs b/Source/Rule56/Patches/RegionGrid_Patch.cs new file mode 100644 index 0000000..2c22bc0 --- /dev/null +++ b/Source/Rule56/Patches/RegionGrid_Patch.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using HarmonyLib; +using RimWorld; +using UnityEngine; +using Verse; +namespace CombatAI.Patches +{ + public static class RegionGrid_Patch + { + [HarmonyPatch(typeof(RegionGrid), nameof(RegionGrid.SetRegionAt))] + private static class RegionGrid_SetRegionAt_Patch + { + public static void Prefix(RegionGrid __instance, IntVec3 c, Region reg) + { + __instance.map.GetComp_Fast().Notify_RegionChanged(c, reg); + } + } + } +} diff --git a/Source/Rule56/Pawn_CustomThreatTracker.cs b/Source/Rule56/Pawn_CustomThreatTracker.cs deleted file mode 100644 index 15c3ad6..0000000 --- a/Source/Rule56/Pawn_CustomThreatTracker.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System.Collections.Generic; -using Verse; -namespace CombatAI -{ - public class Pawn_CustomThreatTracker - { - - public Pawn pawn; - public CustomThreatVector[] threatVectors = new CustomThreatVector[4]; - - public Pawn_CustomThreatTracker(Pawn pawn) - { - this.pawn = pawn; - } - - public class CustomThreatVector - { - public readonly Thing parent; - - public readonly List things = new List(64); - private int oldCount; - - public CustomThreatVector(Thing parent) - { - this.parent = parent; - } - - public int NewNum - { - get => Maths.Max(things.Count - oldCount, 0); - } - - public int CurNum - { - get => things.Count; - } - - public float CurDistSqr - { - get; - private set; - } - - public float OldDistSqr - { - get; - private set; - } - - public void Push(Thing thing) - { - things.Add(thing); - CurDistSqr = Maths.Min(thing.Position.DistanceToSquared(thing.Position), CurDistSqr); - } - - public void Clear() - { - OldDistSqr = CurDistSqr; - CurDistSqr = 1e6f; - oldCount = things.Count; - things.Clear(); - } - } - } -} diff --git a/Source/Rule56/RegionFlooder.cs b/Source/Rule56/RegionFlooder.cs new file mode 100644 index 0000000..dfc49a5 --- /dev/null +++ b/Source/Rule56/RegionFlooder.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using Verse; +namespace CombatAI +{ + public class RegionFlooder + { + private static readonly FastHeap queue = new FastHeap(128); + private static readonly HashSet flooded = new HashSet(256); + + public static void Flood(IntVec3 root, IntVec3 target, Map map, Func action, Func validator = null, Func cost = null, TraverseParms? traverseParms = null, int minRegions = 0, int maxRegions = 9999, float maxDist = 9999f, bool depthCost = true) + { + Region rootRegion = root.GetRegion(map); + if (rootRegion == null || (validator != null && !validator(rootRegion))) + { + return; + } + Func traverseValidator = GetTraverseValidator(traverseParms); + int num = 1; + queue.Clear(); + queue.Enqueue(new Node(){ region = rootRegion, score = Maths.Sqrt_Fast(target.DistanceToSquared(rootRegion.extentsClose.TopRight), 3)}); + flooded.Clear(); + flooded.Add(rootRegion.id); + while (!queue.IsEmpty && (num < minRegions || num <= maxRegions)) + { + Node node = queue.Dequeue(); + Region current = node.region; + if (action(current, Mathf.CeilToInt(node.score / 12f))) + { + break; + } + num++; + List links = current.links; + for (int li = 0; li < links.Count; li++) + { + RegionLink link = links[li]; + for (int ri = 0; ri < 2; ri++) + { + Region next = link.regions[ri]; + if (next != null && next != current && !flooded.Contains(next.id) && next.valid) + { + flooded.Add(next.id); + if ((validator == null || validator(next)) && (traverseValidator == null || traverseValidator(next))) + { + float distToTarget = Maths.Sqrt_Fast(target.DistanceToSquared(next.extentsClose.TopRight), 3); + if (distToTarget < maxDist) + { + queue.Enqueue(new Node() + { + region = next, + score = distToTarget + (!depthCost ? 0 : ((node.depth + 1) * 12)) + (cost?.Invoke(next) ?? 0), + depth = node.depth + 1, + }); + } + } + } + } + } + } + queue.Clear(); + flooded.Clear(); + } + + private static Func GetTraverseValidator(TraverseParms? traverseParms) + { + if ( traverseParms != null) + { + TraverseParms traverse = traverseParms.Value; + Pawn pawn = traverse.pawn; + SightTracker.SightReader sight = null; + if (pawn != null && traverse.maxDanger != Danger.Unspecified && traverse.maxDanger != Danger.Deadly) + { + pawn.TryGetSightReader(out sight); + } + return (region) => + { + if (!traverse.canBashDoors && region.IsDoorway) + { + return false; + } + if (sight != null) + { + int visibility = sight.GetRegionAbsVisibilityToEnemies(region); + if (traverse.maxDanger == Danger.None && visibility > 0) + { + return false; + } + if (traverse.maxDanger == Danger.Some && visibility > 2) + { + return false; + } + } + return true; + }; + } + return null; + } + + private struct Node : IComparable + { + public Region region; + public float score; + public int depth; + + public int CompareTo(Node other) + { + return score.CompareTo(other.score) * -1; + } + } + } +} diff --git a/Source/Rule56/Settings.cs b/Source/Rule56/Settings.cs index 27ed3b1..3e4fa6c 100644 --- a/Source/Rule56/Settings.cs +++ b/Source/Rule56/Settings.cs @@ -51,9 +51,12 @@ public class Settings : ModSettings public bool Pather_Enabled = true; public bool Pather_KillboxKiller = true; public float Pathfinding_DestWeight = 0.85f; + public bool React_Enabled = true; + public bool Retreat_Enabled = true; + public bool Flank_Enabled = true; public bool PerformanceOpt_Enabled = true; - public SightPerformanceSettings SightSettings_FriendliesAndRaiders = new SightPerformanceSettings(3, 5, 12); + public SightPerformanceSettings SightSettings_FriendliesAndRaiders = new SightPerformanceSettings(3, 5, 16); public SightPerformanceSettings SightSettings_MechsAndInsects = new SightPerformanceSettings(3, 10, 6); public SightPerformanceSettings SightSettings_SettlementTurrets = new SightPerformanceSettings(8, 15, 12); public SightPerformanceSettings SightSettings_Wildlife = new SightPerformanceSettings(6, 5, 4); @@ -69,7 +72,7 @@ public override void ExposeData() Scribe_Deep.Look(ref SightSettings_FriendliesAndRaiders, $"CombatAI.SightSettings_FriendliesAndRaiders.{version}"); if (SightSettings_FriendliesAndRaiders == null) { - SightSettings_FriendliesAndRaiders = new SightPerformanceSettings(3, 5, 12); + SightSettings_FriendliesAndRaiders = new SightPerformanceSettings(3, 5, 16); } Scribe_Deep.Look(ref SightSettings_MechsAndInsects, $"CombatAI.SightSettings_MechsAndInsects.{version}"); if (SightSettings_MechsAndInsects == null) @@ -98,6 +101,9 @@ public override void ExposeData() Scribe_Values.Look(ref Pather_Enabled, $"Pather_Enabled.{version}", true); Scribe_Values.Look(ref Caster_Enabled, $"Caster_Enabled.{version}", true); Scribe_Values.Look(ref Targeter_Enabled, $"Targeter_Enabled.{version}", true); + Scribe_Values.Look(ref React_Enabled, $"React_Enabled.{version}", true); + Scribe_Values.Look(ref Retreat_Enabled, $"Retreat_Enabled.{version}", true); + Scribe_Values.Look(ref Flank_Enabled, $"Flank_Enabled.{version}", true); Scribe_Values.Look(ref Pathfinding_DestWeight, $"Pathfinding_DestWeight.{version}", 0.85f); Scribe_Values.Look(ref AdvancedUser, $"AdvancedUser.{version}"); Scribe_Values.Look(ref FogOfWar_FogColor, $"FogOfWar_FogColor.{version}", 0.35f); diff --git a/Source/Rule56/SightGrid.cs b/Source/Rule56/SightGrid.cs index a8b16bd..08a9975 100644 --- a/Source/Rule56/SightGrid.cs +++ b/Source/Rule56/SightGrid.cs @@ -18,6 +18,10 @@ public class SightGrid /// Sight grid contains all sight data. /// public readonly ITSignalGrid grid; + /// + /// Region grid contains sight data for regions. + /// + public readonly ITRegionGrid grid_regions; /// /// Parent map. @@ -38,6 +42,7 @@ public class SightGrid private readonly List tmpInconsistentRecords = new List(64); private readonly List tmpInvalidRecords = new List(64); + private int ops; private WallGrid _walls; /// /// Fog of war grid. Can be null. @@ -47,6 +52,20 @@ public class SightGrid /// Whether this is the player grid /// public bool playerAlliance = false; + /// + /// Super rect containing all none sighter things casting. + /// + private CellRect suRect_Combatant = CellRect.Empty; + /// + /// Super rect containing all none sighter things casting from the prev cycle. + /// + private CellRect suRectPrev_Combatant = CellRect.Empty; + + private IntVec3 suCentroid; + private IntVec3 suCentroidPrev; + /// + /// Ticks until update. + /// private int ticksUntilUpdate; /// /// Whether this is the player grid @@ -56,13 +75,20 @@ public class SightGrid public SightGrid(SightTracker sightTracker, Settings.SightPerformanceSettings settings) { - this.sightTracker = sightTracker; - map = sightTracker.map; - this.settings = settings; - grid = new ITSignalGrid(map); - asyncActions = new AsyncActions(1); - ticksUntilUpdate = Rand.Int % this.settings.interval; - buckets = new IBuckets(settings.buckets); + this.sightTracker = sightTracker; + map = sightTracker.map; + this.settings = settings; + grid = new ITSignalGrid(map); + grid_regions = new ITRegionGrid(map); + asyncActions = new AsyncActions(1); + ticksUntilUpdate = Rand.Int % this.settings.interval; + buckets = new IBuckets(settings.buckets); + suRect_Combatant = new CellRect(); + suRect_Combatant.minX = map.cellIndices.mapSizeX; + suRect_Combatant.maxX = 0; + suRect_Combatant.minZ = map.cellIndices.mapSizeZ; + suRect_Combatant.maxZ = 0; + suRectPrev_Combatant = CellRect.Empty; } /// /// Tracks the number of factions tracked. @@ -72,6 +98,20 @@ public int FactionNum get => numsByFaction.Count; } /// + /// CellRect containing all combatant pawns. + /// + public CellRect SuRect_Combatant + { + get => suRectPrev_Combatant; + } + /// + /// Avg position of combatant pawns. + /// + public IntVec3 SuCentroid + { + get => suCentroidPrev; + } + /// /// The map's wallgrid. /// public WallGrid Walls @@ -188,9 +228,25 @@ public virtual void Destroy() private void Continue() { + suRectPrev_Combatant = suRect_Combatant; + suRect_Combatant.minX = map.cellIndices.mapSizeX; + suRect_Combatant.maxX = 0; + suRect_Combatant.minZ = map.cellIndices.mapSizeZ; + suRect_Combatant.maxZ = 0; + suRectPrev_Combatant.minX -= 5; + suRectPrev_Combatant.minZ -= 5; + suRectPrev_Combatant.maxX += 5; + suRectPrev_Combatant.maxZ += 5; + suCentroidPrev = suCentroid; gridFog?.NextCycle(); grid.NextCycle(); - wait = false; + grid_regions.NextCycle(); + wait = false; + suCentroidPrev = suCentroid; + suCentroidPrev.x = Mathf.CeilToInt(suCentroidPrev.x / (ops + 1e-3f)); + suCentroidPrev.z = Mathf.CeilToInt(suCentroidPrev.z / (ops + 1e-3f)); + suCentroid = IntVec3.Zero; + ops = 0; } private bool Consistent(IBucketableThing item) @@ -283,7 +339,7 @@ private bool TryCastSight(IBucketableThing item) } SightTracker.SightReader reader = item.ai?.sightReader ?? null; bool scanForEnemies; - if (scanForEnemies = !item.isPlayer && item.sighter == null && reader != null && item.ai != null && !item.ai.ReactedRecently(45) && ticks - item.lastScannedForEnemies >= (!Finder.Performance.TpsCriticallyLow ? 10 : 15)) + if (scanForEnemies = Finder.Settings.React_Enabled && !item.isPlayer && item.sighter == null && reader != null && item.ai != null && !item.ai.ReactedRecently(45) && ticks - item.lastScannedForEnemies >= (!Finder.Performance.TpsCriticallyLow ? 10 : 15)) { if (item.dormant != null && !item.dormant.Awake) { @@ -300,13 +356,18 @@ private bool TryCastSight(IBucketableThing item) item.ai.OnScanStarted(); item.spottings.Clear(); } + if (scanForEnemies || (item.sighter == null && item.CctvTop == null)) + { + suRect_Combatant.minX = Maths.Min(suRect_Combatant.minX, pos.x); + suRect_Combatant.maxX = Maths.Max(suRect_Combatant.maxX, pos.x); + suRect_Combatant.minZ = Maths.Min(suRect_Combatant.minZ, pos.z); + suRect_Combatant.maxZ = Maths.Max(suRect_Combatant.maxZ, pos.z); + ops += 1; + suCentroid += pos; + } ISightRadius sightRadius = item.cachedSightRadius; Action action = () => { - grid.Next(item.cachedDamage.adjustedSharp, item.cachedDamage.adjustedBlunt, item.cachedDamage.attributes); - grid.Set(flagPos, item.pawn == null || !item.pawn.Downed ? GetFlags(item) : 0); - grid.Next(item.cachedDamage.adjustedSharp, item.cachedDamage.adjustedBlunt, item.cachedDamage.attributes); - grid.Set(origin, 1.0f, new Vector2(origin.x - pos.x, origin.z - pos.z)); if (playerAlliance) { gridFog.Next(); @@ -316,6 +377,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); float r_fade = sightRadius.fog * Finder.Settings.FogOfWar_RangeFadeMultiplier; float d_fade = sightRadius.fog - r_fade; float rSqr_sight = Maths.Sqr(sightRadius.sight); @@ -332,6 +395,7 @@ private bool TryCastSight(IBucketableThing item) if (visibility > 0f) { grid.Set(cell, visibility, new Vector2(cell.x - pos.x, cell.z - pos.z)); + grid_regions.Set(cell); } } if (playerAlliance && d2 < rSqr_fog) @@ -368,6 +432,10 @@ private bool TryCastSight(IBucketableThing item) { ShadowCastingUtility.CastWeighted(map, pos, item.CctvTop.LookDirection, setAction, Maths.Max(sightRadius.scan, sightRadius.fog, sightRadius.sight), item.CctvTop.BaseWidth, settings.carryLimit, buffer); } + grid.Set(origin, 1.0f, new Vector2(origin.x - pos.x, origin.z - pos.z)); + grid.Set(pos, 1.0f, new Vector2(origin.x - pos.x, origin.z - pos.z)); + grid.Next(0, 0, item.cachedDamage.attributes); + grid.Set(flagPos, item.pawn == null || !item.pawn.Downed ? GetFlags(item) : 0); if (scanForEnemies) { if (item.spottings.Count > 0 || Finder.Settings.Debug && Finder.Settings.Debug_ValidateSight) diff --git a/Source/Rule56/SightTracker.cs b/Source/Rule56/SightTracker.cs index 53b5132..bb23238 100644 --- a/Source/Rule56/SightTracker.cs +++ b/Source/Rule56/SightTracker.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; using CombatAI.Comps; using RimWorld; using UnityEngine; @@ -140,10 +141,15 @@ public override void MapComponentTick() if (cell.InBounds(map) && !_drawnCells.Contains(cell)) { _drawnCells.Add(cell); - float value = raidersAndHostiles.grid.GetSignalStrengthAt(cell, out int enemies1) + colonistsAndFriendlies.grid.GetSignalStrengthAt(cell, out int enemies2) + insectsAndMechs.grid.GetSignalStrengthAt(cell, out int enemies4); - if (value > 0) +// float value = raidersAndHostiles.grid.GetSignalStrengthAt(cell, out int enemies1) + colonistsAndFriendlies.grid.GetSignalStrengthAt(cell, out int enemies2) + insectsAndMechs.grid.GetSignalStrengthAt(cell, out int enemies4); + Region region = cell.GetRegion(map); + if (region != null) { - map.debugDrawer.FlashCell(cell, Mathf.Clamp(value / 7f, 0.1f, 0.99f), $"{Math.Round(value, 3)} {enemies1 + enemies2}", 15); + float value = raidersAndHostiles.grid_regions.GetSignalNumByRegion(region) + colonistsAndFriendlies.grid_regions.GetSignalNumByRegion(region); + if (value > 0) + { + map.debugDrawer.FlashCell(cell, Mathf.Clamp(value / 7f, 0.1f, 0.99f), $"{Math.Round(value, 3)}", 15); + } } } } @@ -215,6 +221,13 @@ public bool TryGetReader(Thing thing, out SightReader reader) new[] { wildlife.grid, colonistsAndFriendlies.grid, raidersAndHostiles.grid + }, + new ITRegionGrid[] + { + }, + new[] + { + insectsAndMechs.grid_regions }); return true; } @@ -232,6 +245,14 @@ public bool TryGetReader(Thing thing, out SightReader reader) new[] { wildlife.grid + }, + new[] + { + insectsAndMechs.grid_regions + }, + new[] + { + colonistsAndFriendlies.grid_regions, raidersAndHostiles.grid_regions }); return true; } @@ -250,6 +271,14 @@ public bool TryGetReader(Thing thing, out SightReader reader) new[] { wildlife.grid + }, + new[] + { + colonistsAndFriendlies.grid_regions + }, + new[] + { + raidersAndHostiles.grid_regions, insectsAndMechs.grid_regions }); } else @@ -266,6 +295,14 @@ public bool TryGetReader(Thing thing, out SightReader reader) new[] { wildlife.grid + }, + new[] + { + raidersAndHostiles.grid_regions + }, + new[] + { + colonistsAndFriendlies.grid_regions, insectsAndMechs.grid_regions }); } return true; @@ -286,6 +323,13 @@ public bool TryGetReader(Faction faction, out SightReader reader) new[] { wildlife.grid, colonistsAndFriendlies.grid, raidersAndHostiles.grid + }, + new ITRegionGrid[] + { + }, + new[] + { + insectsAndMechs.grid_regions }); return true; } @@ -303,6 +347,14 @@ public bool TryGetReader(Faction faction, out SightReader reader) new[] { wildlife.grid + }, + new[] + { + insectsAndMechs.grid_regions + }, + new[] + { + colonistsAndFriendlies.grid_regions, raidersAndHostiles.grid_regions }); return true; } @@ -321,6 +373,14 @@ public bool TryGetReader(Faction faction, out SightReader reader) new[] { wildlife.grid + }, + new[] + { + colonistsAndFriendlies.grid_regions + }, + new[] + { + raidersAndHostiles.grid_regions, insectsAndMechs.grid_regions }); } else @@ -337,6 +397,14 @@ public bool TryGetReader(Faction faction, out SightReader reader) new[] { wildlife.grid + }, + new[] + { + raidersAndHostiles.grid_regions + }, + new[] + { + colonistsAndFriendlies.grid_regions, insectsAndMechs.grid_regions }); } return true; @@ -429,6 +497,14 @@ public void Notify_PlayerRelationChanged(Faction faction) } } + public void Notify_RegionChanged(IntVec3 cell, Region region) + { + colonistsAndFriendlies.grid_regions.SetRegionAt(cell, region); + raidersAndHostiles.grid_regions.SetRegionAt(cell, region); + insectsAndMechs.grid_regions.SetRegionAt(cell, region); + wildlife.grid_regions.SetRegionAt(cell, region); + } + public override void MapRemoved() { // TODO redo this @@ -453,17 +529,22 @@ public class SightReader public ArmorReport armor; public ITSignalGrid[] friendlies; + + public ITRegionGrid[] friendlies_regions; public ITSignalGrid[] hostiles; + public ITRegionGrid[] hostiles_regions; public ITSignalGrid[] neutrals; - public SightReader(SightTracker tracker, ITSignalGrid[] friendlies, ITSignalGrid[] hostiles, ITSignalGrid[] neutrals) + public SightReader(SightTracker tracker, ITSignalGrid[] friendlies, ITSignalGrid[] hostiles, ITSignalGrid[] neutrals, ITRegionGrid[] friendlies_regions, ITRegionGrid[] hostiles_regions) { - Tacker = tracker; - Map = tracker.map; - indices = tracker.map.cellIndices; - this.friendlies = friendlies.ToArray(); - this.hostiles = hostiles.ToArray(); - this.neutrals = neutrals.ToArray(); + Tacker = tracker; + Map = tracker.map; + indices = tracker.map.cellIndices; + this.friendlies = friendlies.ToArray(); + this.hostiles = hostiles.ToArray(); + this.neutrals = neutrals.ToArray(); + this.friendlies_regions = friendlies_regions.ToArray(); + this.hostiles_regions = hostiles_regions.ToArray(); } public SightTracker Tacker @@ -472,9 +553,11 @@ public SightTracker Tacker } public Map Map { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public float GetThreat(IntVec3 cell) { return GetThreat(indices.CellToIndex(cell)); @@ -495,7 +578,32 @@ public float GetThreat(int index) } return armor.createdAt != 0 ? Mathf.Clamp01(Maths.Max(GetBlunt(index) / (armor.Blunt + 0.001f), GetSharp(index) / (armor.Sharp + 0.001f), 0f)) : 0f; } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float GetRegionThreat(Region region) + { + if (region != null) + { + return GetRegionThreat(region.id); + } + return 0; + } + public float GetRegionThreat(int id) + { + if (armor.weaknessAttributes != MetaCombatAttribute.None) + { + MetaCombatAttribute attributes = GetRegionMetaAttributes(id); + if ((attributes & armor.weaknessAttributes) != MetaCombatAttribute.None) + { + return 2.0f; + } + } + if (!Mod_CE.active) + { + return armor.createdAt != 0 ? Mathf.Clamp01(2f * Maths.Max(GetRegionBlunt(id) / (armor.Blunt + 0.001f), GetRegionSharp(id) / (armor.Sharp + 0.001f), 0f)) : 0f; + } + return armor.createdAt != 0 ? Mathf.Clamp01(Maths.Max(GetRegionBlunt(id) / (armor.Blunt + 0.001f), GetRegionSharp(id) / (armor.Sharp + 0.001f), 0f)) : 0f; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public float GetBlunt(IntVec3 cell) { return GetBlunt(indices.CellToIndex(cell)); @@ -509,7 +617,7 @@ public float GetBlunt(int index) } return value; } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] public float GetSharp(IntVec3 cell) { return GetSharp(indices.CellToIndex(cell)); @@ -523,7 +631,7 @@ public float GetSharp(int index) } return value; } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] public MetaCombatAttribute GetMetaAttributes(IntVec3 cell) { return GetMetaAttributes(indices.CellToIndex(cell)); @@ -537,7 +645,136 @@ public MetaCombatAttribute GetMetaAttributes(int index) } return value; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int GetRegionAbsVisibilityToEnemies(Region region) + { + if (region != null) + { + return GetRegionAbsVisibilityToEnemies(region.id); + } + return 0; + } + public int GetRegionAbsVisibilityToEnemies(int id) + { + int value = 0; + for (int i = 0; i < hostiles_regions.Length; i++) + { + value += hostiles_regions[i].GetSignalNumById(id); + } + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ulong GetRegionEnemyFlags(Region region) + { + if (region != null) + { + return GetRegionEnemyFlags(region.id); + } + return 0; + } + public ulong GetRegionEnemyFlags(int id) + { + ulong value = 0; + for (int i = 0; i < hostiles_regions.Length; i++) + { + value |= hostiles_regions[i].GetFlagsAt(id); + } + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ulong GetRegionFriendlyFlags(Region region) + { + if (region != null) + { + return GetRegionFriendlyFlags(region.id); + } + return 0; + } + public ulong GetRegionFriendlyFlags(int id) + { + ulong value = 0; + for (int i = 0; i < friendlies_regions.Length; i++) + { + value |= friendlies_regions[i].GetFlagsAt(id); + } + return value; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MetaCombatAttribute GetRegionMetaAttributes(Region region) + { + if (region != null) + { + return GetRegionMetaAttributes(region.id); + } + return 0; + } + public MetaCombatAttribute GetRegionMetaAttributes(int id) + { + MetaCombatAttribute value = 0; + for (int i = 0; i < hostiles_regions.Length; i++) + { + value |= hostiles_regions[i].GetCombatAttributesAt(id); + } + return value; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float GetRegionSharp(Region region) + { + if (region != null) + { + return GetRegionSharp(region.id); + } + return 0; + } + public float GetRegionSharp(int id) + { + float value = 0; + for (int i = 0; i < hostiles_regions.Length; i++) + { + value += hostiles_regions[i].GetSharpAt(id); + } + return value; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public float GetRegionBlunt(Region region) + { + if (region != null) + { + return GetRegionBlunt(region.id); + } + return 0; + } + public float GetRegionBlunt(int id) + { + float value = 0; + for (int i = 0; i < hostiles_regions.Length; i++) + { + value += hostiles_regions[i].GetBluntAt(id); + } + return value; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int GetRegionAbsVisibilityToFriendlies(Region region) + { + if (region != null) + { + return GetRegionAbsVisibilityToFriendlies(region.id); + } + return 0; + } + public int GetRegionAbsVisibilityToFriendlies(int id) + { + int value = 0; + for (int i = 0; i < friendlies_regions.Length; i++) + { + value += friendlies_regions[i].GetSignalNumById(id); + } + return value; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public float GetAbsVisibilityToNeutrals(IntVec3 cell) { return GetAbsVisibilityToNeutrals(indices.CellToIndex(cell)); @@ -552,6 +789,7 @@ public float GetAbsVisibilityToNeutrals(int index) return value; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public float GetAbsVisibilityToEnemies(IntVec3 cell) { return GetAbsVisibilityToEnemies(indices.CellToIndex(cell)); @@ -566,6 +804,7 @@ public float GetAbsVisibilityToEnemies(int index) return value; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public float GetAbsVisibilityToFriendlies(IntVec3 cell) { return GetAbsVisibilityToFriendlies(indices.CellToIndex(cell)); @@ -580,6 +819,7 @@ public float GetAbsVisibilityToFriendlies(int index) return value; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public float GetVisibilityToNeutrals(IntVec3 cell) { return GetVisibilityToNeutrals(indices.CellToIndex(cell)); @@ -594,6 +834,7 @@ public float GetVisibilityToNeutrals(int index) return value; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public float GetVisibilityToEnemies(IntVec3 cell) { return GetVisibilityToEnemies(indices.CellToIndex(cell)); @@ -608,6 +849,7 @@ public float GetVisibilityToEnemies(int index) return value; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public float GetVisibilityToFriendlies(IntVec3 cell) { return GetVisibilityToFriendlies(indices.CellToIndex(cell)); @@ -622,15 +864,18 @@ public float GetVisibilityToFriendlies(int index) return value; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool CheckFlags(IntVec3 cell, ulong flags) { return CheckFlags(indices.CellToIndex(cell), flags); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool CheckFlags(int index, ulong flags) { return (flags & GetEnemyFlags(index)) == flags; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public ulong GetEnemyFlags(IntVec3 cell) { return GetEnemyFlags(indices.CellToIndex(cell)); @@ -645,6 +890,7 @@ public ulong GetEnemyFlags(int index) return value; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public ulong GetFriendlyFlags(IntVec3 cell) { return GetFriendlyFlags(indices.CellToIndex(cell)); @@ -659,6 +905,7 @@ public ulong GetFriendlyFlags(int index) return value; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector2 GetEnemyDirection(IntVec3 cell) { return GetEnemyDirection(indices.CellToIndex(cell)); @@ -673,6 +920,7 @@ public Vector2 GetEnemyDirection(int index) return value; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector2 GetFriendlyDirection(IntVec3 cell) { return GetFriendlyDirection(indices.CellToIndex(cell)); diff --git a/Source/Rule56/SightUtility.cs b/Source/Rule56/SightUtility.cs index c57afeb..a20664d 100644 --- a/Source/Rule56/SightUtility.cs +++ b/Source/Rule56/SightUtility.cs @@ -50,11 +50,14 @@ private static SightGrid.ISightRadius GetSightRadius_Pawn(Pawn pawn) return result; } Verb verb = pawn.equipment?.PrimaryEq?.PrimaryVerb ?? null; +// if (verb == null || !verb.Available()) +// { +// if (pawn.verbTracker != null && !pawn.verbTracker.AllVerbs.NullOrEmpty()) +// { +// verb = pawn.verbTracker.AllVerbs.Where(v => v.Available()).MaxBy(v => v.IsMeleeAttack ? 0 : v.EffectiveRange) ?? null; +// } +// } if (verb == null || !verb.Available()) - { - verb = pawn.verbTracker?.AllVerbs.Where(v => v.Available()).MaxBy(v => v.IsMeleeAttack ? 0 : v.EffectiveRange) ?? null; - } - if (verb == null) { result.sight = 4; result.scan = 0; @@ -140,7 +143,7 @@ private static float GetFogRadius(Thing thing, float sightRadius) float hearing = pawn.health.capacities?.GetLevel(PawnCapacityDefOf.Hearing) ?? 1f; float rest = Mathf.Lerp(0.65f, 1f, pawn.needs?.rest?.curLevelInt ?? 1f); float mul = Mathf.Clamp(Maths.Min(vision, hearing, rest) * 0.6f + Maths.Max(vision, hearing, rest) * 0.4f, 0.5f, 1.5f); - return sightRadius * mul; + return Maths.Max(Maths.Max(sightRadius, 17) * mul, 10); } return sightRadius; } diff --git a/Source/Rule56/T4/Outputs/Keyed.generated.cs b/Source/Rule56/T4/Outputs/Keyed.generated.cs index 0548ab0..fcb4039 100644 --- a/Source/Rule56/T4/Outputs/Keyed.generated.cs +++ b/Source/Rule56/T4/Outputs/Keyed.generated.cs @@ -103,6 +103,26 @@ public static TaggedString CombatAI_Preparing { _CombatAI_Preparing : _CombatAI_Preparing = "CombatAI.Preparing".Translate(); } + private static TaggedString _CombatAI_Animator_Controller = null; + /// Keyed string. key=CombatAI.Animator.Controller. inner text: + /// + /// Hold + /// + public static TaggedString CombatAI_Animator_Controller { + get => _CombatAI_Animator_Controller != null ? + _CombatAI_Animator_Controller : _CombatAI_Animator_Controller = "CombatAI.Animator.Controller".Translate(); + } + + private static TaggedString _CombatAI_Animator_Controller_Description = null; + /// Keyed string. key=CombatAI.Animator.Controller.Description. inner text: + /// + /// Hold at the current angle. + /// + public static TaggedString CombatAI_Animator_Controller_Description { + get => _CombatAI_Animator_Controller_Description != null ? + _CombatAI_Animator_Controller_Description : _CombatAI_Animator_Controller_Description = "CombatAI.Animator.Controller.Description".Translate(); + } + private static TaggedString _CombatAI_Settings_Basic = null; /// Keyed string. key=CombatAI.Settings.Basic. inner text: /// @@ -113,6 +133,76 @@ public static TaggedString CombatAI_Settings_Basic { _CombatAI_Settings_Basic : _CombatAI_Settings_Basic = "CombatAI.Settings.Basic".Translate(); } + private static TaggedString _CombatAI_Settings_Basic_Presets = null; + /// Keyed string. key=CombatAI.Settings.Basic.Presets. inner text: + /// + /// Performance/Difficulty presets + /// + public static TaggedString CombatAI_Settings_Basic_Presets { + get => _CombatAI_Settings_Basic_Presets != null ? + _CombatAI_Settings_Basic_Presets : _CombatAI_Settings_Basic_Presets = "CombatAI.Settings.Basic.Presets".Translate(); + } + + private static TaggedString _CombatAI_Settings_Basic_Presets_Easy = null; + /// Keyed string. key=CombatAI.Settings.Basic.Presets.Easy. inner text: + /// + /// Easy/Perf + /// + public static TaggedString CombatAI_Settings_Basic_Presets_Easy { + get => _CombatAI_Settings_Basic_Presets_Easy != null ? + _CombatAI_Settings_Basic_Presets_Easy : _CombatAI_Settings_Basic_Presets_Easy = "CombatAI.Settings.Basic.Presets.Easy".Translate(); + } + + private static TaggedString _CombatAI_Settings_Basic_Presets_Normal = null; + /// Keyed string. key=CombatAI.Settings.Basic.Presets.Normal. inner text: + /// + /// Normal + /// + public static TaggedString CombatAI_Settings_Basic_Presets_Normal { + get => _CombatAI_Settings_Basic_Presets_Normal != null ? + _CombatAI_Settings_Basic_Presets_Normal : _CombatAI_Settings_Basic_Presets_Normal = "CombatAI.Settings.Basic.Presets.Normal".Translate(); + } + + private static TaggedString _CombatAI_Settings_Basic_Presets_Hard = null; + /// Keyed string. key=CombatAI.Settings.Basic.Presets.Hard. inner text: + /// + /// Hard/Complex + /// + public static TaggedString CombatAI_Settings_Basic_Presets_Hard { + get => _CombatAI_Settings_Basic_Presets_Hard != null ? + _CombatAI_Settings_Basic_Presets_Hard : _CombatAI_Settings_Basic_Presets_Hard = "CombatAI.Settings.Basic.Presets.Hard".Translate(); + } + + private static TaggedString _CombatAI_Settings_Basic_Presets_Deathwish = null; + /// Keyed string. key=CombatAI.Settings.Basic.Presets.Deathwish. inner text: + /// + /// Deathwish + /// + public static TaggedString CombatAI_Settings_Basic_Presets_Deathwish { + get => _CombatAI_Settings_Basic_Presets_Deathwish != null ? + _CombatAI_Settings_Basic_Presets_Deathwish : _CombatAI_Settings_Basic_Presets_Deathwish = "CombatAI.Settings.Basic.Presets.Deathwish".Translate(); + } + + private static TaggedString _CombatAI_Settings_Basic_Presets_Applied = null; + /// Keyed string. key=CombatAI.Settings.Basic.Presets.Applied. inner text: + /// + /// Applied preset: + /// + public static TaggedString CombatAI_Settings_Basic_Presets_Applied { + get => _CombatAI_Settings_Basic_Presets_Applied != null ? + _CombatAI_Settings_Basic_Presets_Applied : _CombatAI_Settings_Basic_Presets_Applied = "CombatAI.Settings.Basic.Presets.Applied".Translate(); + } + + 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. + /// + public static TaggedString CombatAI_Settings_Basic_Presets_Description { + get => _CombatAI_Settings_Basic_Presets_Description != null ? + _CombatAI_Settings_Basic_Presets_Description : _CombatAI_Settings_Basic_Presets_Description = "CombatAI.Settings.Basic.Presets.Description".Translate(); + } + private static TaggedString _CombatAI_Settings_Basic_FogOfWar = null; /// Keyed string. key=CombatAI.Settings.Basic.FogOfWar. inner text: /// @@ -216,17 +306,27 @@ public static TaggedString CombatAI_Settings_Basic_CELean { private static TaggedString _CombatAI_Settings_Basic_PerformanceOpt = null; /// Keyed string. key=CombatAI.Settings.Basic.PerformanceOpt. inner text: /// - /// Enable dynamic performance settings + /// Enable Dynamic performance settings /// public static TaggedString CombatAI_Settings_Basic_PerformanceOpt { get => _CombatAI_Settings_Basic_PerformanceOpt != null ? _CombatAI_Settings_Basic_PerformanceOpt : _CombatAI_Settings_Basic_PerformanceOpt = "CombatAI.Settings.Basic.PerformanceOpt".Translate(); } + 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! + /// + public static TaggedString CombatAI_Settings_Basic_PerformanceOpt_Warning { + get => _CombatAI_Settings_Basic_PerformanceOpt_Warning != null ? + _CombatAI_Settings_Basic_PerformanceOpt_Warning : _CombatAI_Settings_Basic_PerformanceOpt_Warning = "CombatAI.Settings.Basic.PerformanceOpt.Warning".Translate(); + } + 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 ? @@ -253,6 +353,36 @@ public static TaggedString CombatAI_Settings_Basic_Targeter { _CombatAI_Settings_Basic_Targeter : _CombatAI_Settings_Basic_Targeter = "CombatAI.Settings.Basic.Targeter".Translate(); } + private static TaggedString _CombatAI_Settings_Basic_Flanking = null; + /// Keyed string. key=CombatAI.Settings.Basic.Flanking. inner text: + /// + /// Enable aggressive flanking + /// + public static TaggedString CombatAI_Settings_Basic_Flanking { + get => _CombatAI_Settings_Basic_Flanking != null ? + _CombatAI_Settings_Basic_Flanking : _CombatAI_Settings_Basic_Flanking = "CombatAI.Settings.Basic.Flanking".Translate(); + } + + private static TaggedString _CombatAI_Settings_Basic_Reaction = null; + /// Keyed string. key=CombatAI.Settings.Basic.Reaction. inner text: + /// + /// Enable fast reactions + /// + public static TaggedString CombatAI_Settings_Basic_Reaction { + get => _CombatAI_Settings_Basic_Reaction != null ? + _CombatAI_Settings_Basic_Reaction : _CombatAI_Settings_Basic_Reaction = "CombatAI.Settings.Basic.Reaction".Translate(); + } + + private static TaggedString _CombatAI_Settings_Basic_Retreat = null; + /// Keyed string. key=CombatAI.Settings.Basic.Retreat. inner text: + /// + /// Enable tactical retreat + /// + public static TaggedString CombatAI_Settings_Basic_Retreat { + get => _CombatAI_Settings_Basic_Retreat != null ? + _CombatAI_Settings_Basic_Retreat : _CombatAI_Settings_Basic_Retreat = "CombatAI.Settings.Basic.Retreat".Translate(); + } + private static TaggedString _CombatAI_Settings_Basic_Pather = null; /// Keyed string. key=CombatAI.Settings.Basic.Pather. inner text: /// @@ -276,7 +406,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 ? @@ -326,7 +456,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 ? @@ -356,7 +486,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 ? @@ -446,7 +576,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 ?