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 ?