Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented simple tactical goals. #17

Merged
merged 1 commit into from
Jan 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified 1.4/Assemblies/CombatAI.dll
Binary file not shown.
27 changes: 18 additions & 9 deletions Source/Rule56/CombatAI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,15 @@
<None Update="..\..\1.4\Defs\ThingDef_Buildings\Buildings_Security.xml">
<Link>Xml\Defs\ThingDef_Buildings\\Buildings_Security.xml</Link>
</None>
<None Update="..\..\1.4\Defs\Misc\Designation_categories.xml">
<Link>Xml\Defs\Misc\\Designation_categories.xml</Link>
</None>
<None Update="..\..\1.4\Defs\ThingDef_Research\Cai5000_Researchtab.xml">
<Link>Xml\Defs\ThingDef_Research\\Cai5000_Researchtab.xml</Link>
</None>
<None Update="..\..\1.4\Defs\ThingDef_Research\Cai5000_ResearchDefs.xml">
<Link>Xml\Defs\ThingDef_Research\\Cai5000_ResearchDefs.xml</Link>
</None>
</ItemGroup>
<!-- ................ -->
<!-- Packages -->
Expand All @@ -107,8 +116,8 @@
<PropertyGroup>
<TextTransform>$(PkgMono_TextTransform)\tools\TextTransform.exe</TextTransform>
</PropertyGroup>
<Message Text="Processing T4 files: @(TTFiles->'%(Identity)')" />
<Exec WorkingDirectory="$(ProjectDir)" Command="@(TTFiles->'pwd; mono $(TextTransform) --out=T4/Outputs/%(FileName).generated.cs T4/Sources/%(FileName).tt')" />
<Message Text="Processing T4 files: @(TTFiles-&gt;'%(Identity)')" />
<Exec WorkingDirectory="$(ProjectDir)" Command="@(TTFiles-&gt;'pwd; mono $(TextTransform) --out=T4/Outputs/%(FileName).generated.cs T4/Sources/%(FileName).tt')" />
</Target>
<!-- ................ -->
<!-- Publisize -->
Expand Down Expand Up @@ -154,12 +163,12 @@
<TransformOnBuild>false</TransformOnBuild>
</PropertyGroup>
<ProjectExtensions>
<MonoDevelop>
<Properties>
<Policies>
<StandardHeader Text="&#xA;${FileName}&#xA; &#xA;Author:&#xA; ${AuthorName} &lt;${AuthorEmail}&gt;&#xA;&#xA;Copyright (c) ${Year} ${CopyrightHolder}&#xA;&#xA;Permission is hereby granted, free of charge, to any person obtaining a copy&#xA;of this software and associated documentation files (the &quot;Software&quot;), to deal&#xA;in the Software without restriction, including without limitation the rights&#xA;to use, copy, modify, merge, publish, distribute, sublicense, and/or sell&#xA;copies of the Software, and to permit persons to whom the Software is&#xA;furnished to do so, subject to the following conditions:&#xA;&#xA;The above copyright notice and this permission notice shall be included in&#xA;all copies or substantial portions of the Software.&#xA;&#xA;THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR&#xA;IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,&#xA;FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE&#xA;AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER&#xA;LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,&#xA;OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN&#xA;THE SOFTWARE." IncludeInNewFiles="False" />
</Policies>
</Properties>
</MonoDevelop>
<MonoDevelop>
<Properties>
<Policies>
<StandardHeader Text="&#xA;${FileName}&#xA; &#xA;Author:&#xA; ${AuthorName} &lt;${AuthorEmail}&gt;&#xA;&#xA;Copyright (c) ${Year} ${CopyrightHolder}&#xA;&#xA;Permission is hereby granted, free of charge, to any person obtaining a copy&#xA;of this software and associated documentation files (the &quot;Software&quot;), to deal&#xA;in the Software without restriction, including without limitation the rights&#xA;to use, copy, modify, merge, publish, distribute, sublicense, and/or sell&#xA;copies of the Software, and to permit persons to whom the Software is&#xA;furnished to do so, subject to the following conditions:&#xA;&#xA;The above copyright notice and this permission notice shall be included in&#xA;all copies or substantial portions of the Software.&#xA;&#xA;THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR&#xA;IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,&#xA;FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE&#xA;AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER&#xA;LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,&#xA;OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN&#xA;THE SOFTWARE." IncludeInNewFiles="False" />
</Policies>
</Properties>
</MonoDevelop>
</ProjectExtensions>
</Project>
72 changes: 40 additions & 32 deletions Source/Rule56/Comps/ThingComp_CombatAI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -676,7 +676,7 @@ public override void PostExposeData()

private void TryStartSapperJob()
{
if (sightReader.GetVisibilityToEnemies(cellBefore) > 0)
if (sightReader.GetVisibilityToEnemies(cellBefore) > 0 || sapperNodes.Count == 0)
{
ReleaseEscorts();
cellBefore = IntVec3.Invalid;
Expand All @@ -686,46 +686,54 @@ private void TryStartSapperJob()
return;
}
Pawn pawn = parent as Pawn;
if(pawn.Destroyed || pawn.Downed || pawn.Dead || pawn.mindState?.duty == null || !(pawn.mindState.duty.def.Is(DutyDefOf.AssaultColony) || pawn.mindState.duty.def.Is(DutyDefOf.Defend) || pawn.mindState.duty.def.Is(DutyDefOf.AssaultThing) || pawn.mindState.duty.def.Is(DutyDefOf.Breaching)))
{
ReleaseEscorts();
return;
}
Map map = pawn.Map;
Thing blocker = sapperNodes[0].GetEdifice(map);
Job job = DigUtility.PassBlockerJob(pawn, blocker, cellBefore, true, true);
if (job != null)
if (blocker != null)
{
job.playerForced = true;
job.expiryInterval = 3600;
job.maxNumMeleeAttacks = 300;
pawn.jobs.StopAll();
pawn.jobs.StartJob(job, JobCondition.InterruptForced);
if (findEscorts)
Job job = DigUtility.PassBlockerJob(pawn, blocker, cellBefore, true, true);
if (job != null)
{
int count = escorts.Count;
int countTarget = Rand.Int % 6 + 4 + Maths.Min(sapperNodes.Count, 10);
Faction faction = pawn.Faction;
Predicate<Thing> validator = t =>
job.playerForced = true;
job.expiryInterval = 3600;
job.maxNumMeleeAttacks = 300;
pawn.jobs.StopAll();
pawn.jobs.StartJob(job, JobCondition.InterruptForced);
if (findEscorts)
{
if (count < countTarget && t.Faction == faction && t is Pawn ally && !ally.Destroyed
&& !ally.CurJobDef.Is(JobDefOf.Mine)
&& ally.mindState?.duty?.def != DutyDefOf.Escort
&& (sightReader == null || sightReader.GetAbsVisibilityToEnemies(ally.Position) == 0)
&& ally.skills?.GetSkill(SkillDefOf.Mining).Level < 10)
int count = escorts.Count;
int countTarget = Rand.Int % 6 + 4 + Maths.Min(sapperNodes.Count, 10);
Faction faction = pawn.Faction;
Predicate<Thing> validator = t =>
{
ThingComp_CombatAI comp = ally.GetComp_Fast<ThingComp_CombatAI>();
if (comp?.duties != null && comp.duties?.Any(DutyDefOf.Escort) == false && !comp.IsSapping && GenTicks.TicksGame - comp.releasedTick < 600)
if (count < countTarget && t.Faction == faction && t is Pawn ally && !ally.Destroyed
&& !ally.CurJobDef.Is(JobDefOf.Mine)
&& ally.mindState?.duty?.def != DutyDefOf.Escort
&& (sightReader == null || sightReader.GetAbsVisibilityToEnemies(ally.Position) == 0)
&& ally.skills?.GetSkill(SkillDefOf.Mining).Level < 10)
{
Pawn_CustomDutyTracker.CustomPawnDuty custom = CustomDutyUtility.Escort(ally, pawn, 20, 100, 500 * sapperNodes.Count + Rand.Int % 1000);
if (custom != null)
ThingComp_CombatAI comp = ally.GetComp_Fast<ThingComp_CombatAI>();
if (comp?.duties != null && comp.duties?.Any(DutyDefOf.Escort) == false && !comp.IsSapping && GenTicks.TicksGame - comp.releasedTick < 600)
{
custom.duty.locomotion = LocomotionUrgency.Sprint;
comp.duties.StartDuty(custom);
escorts.Add(ally);
Pawn_CustomDutyTracker.CustomPawnDuty custom = CustomDutyUtility.Escort(ally, pawn, 20, 100, 500 * sapperNodes.Count + Rand.Int % 1000);
if (custom != null)
{
custom.duty.locomotion = LocomotionUrgency.Sprint;
comp.duties.StartDuty(custom);
escorts.Add(ally);
}
count++;
}
count++;
}
return count == countTarget;
}
return false;
};
Verse.GenClosest.RegionwiseBFSWorker(pawn.Position, map, ThingRequest.ForGroup(ThingRequestGroup.Pawn), PathEndMode.InteractionCell, TraverseParms.For(pawn), validator, null, 1, 10, 40, out int _);
return count == countTarget;
}
return false;
};
Verse.GenClosest.RegionwiseBFSWorker(pawn.Position, map, ThingRequest.ForGroup(ThingRequestGroup.Pawn), PathEndMode.InteractionCell, TraverseParms.For(pawn), validator, null, 1, 10, 40, out int _);
}
}
}
}
Expand Down
28 changes: 28 additions & 0 deletions Source/Rule56/CustomDutyUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,33 @@ public static Pawn_CustomDutyTracker.CustomPawnDuty Escort(Pawn escorter, Pawn e
custom.failOnFocusDeath = true;
return custom;
}

public static Pawn_CustomDutyTracker.CustomPawnDuty AssaultPoint(Pawn pawn, IntVec3 dest, int switchAssaultRadius = 15, int expireAfter = 0, int startAfter = 0)
{
//if (!pawn.CanReach(dest, PathEndMode.InteractionCell, Danger.Unspecified, true, true, TraverseMode.PassAllDestroyableThingsNotWater))
//{
// return null;
//}
ThingComp_CombatAI comp = pawn.GetComp_Fast<ThingComp_CombatAI>();
if (comp == null)
{
return null;
}
Pawn_CustomDutyTracker tracker = comp.duties;
if (tracker == null)
{
return null;
}
Pawn_CustomDutyTracker.CustomPawnDuty custom = new Pawn_CustomDutyTracker.CustomPawnDuty();
custom.duty = new PawnDuty(DutyDefOf.Defend, dest, switchAssaultRadius);
custom.duty.locomotion = LocomotionUrgency.Sprint;
custom.dest = dest;
custom.failOnDistanceToFocus = switchAssaultRadius;
custom.expireAfter = expireAfter;
custom.startAfter = startAfter;
custom.failOnFocusDestroyed = true;
custom.failOnFocusDeath = true;
return custom;
}
}
}
100 changes: 73 additions & 27 deletions Source/Rule56/Patches/JobGiver_AIGotoNearestHostile_Patch.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Drawing;
using HarmonyLib;
using RimWorld;
using UnityEngine;
Expand All @@ -15,34 +16,79 @@ public static bool Prefix(Pawn pawn, ref Job __result)
{
if (pawn.TryGetSightReader(out SightTracker.SightReader reader))
{
//if (pawn.Faction.HostileTo(pawn.Map.ParentFaction))
//{
// Thing nearestBuildingEnemy = 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<Thing> things = region.ListerThings.ThingsInGroup(ThingRequestGroup.BuildingArtificial);
// if (things != null)
// {
// for (int i = 0; i < things.Count; i++)
// {
// Thing thing = things[i];
// if (TrashUtility.CanTrash(pawn, thing))
// {

// }
// //if(thing.Faction == F)
// //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;
// //}
// }
// }
// things = region.ListerThings.ThingsInGroup(ThingRequestGroup.BuildingArtificial);
// }
// 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);
// },
// validator: region =>
// {
// return reader.GetRegionAbsVisibilityToEnemies(region) == 0;
// }, maxRegions: 512);
// if (nearestBuildingEnemy != null)
// {
// __result = TrashUtility.TrashJob(pawn, nearestBuildingEnemy, true);
// return false;
// }
//}
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<Thing> 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);
RegionFlooder.Flood(pawn.Position, pawn.mindState.enemyTarget == null ? pawn.Position : pawn.mindState.enemyTarget.Position, pawn.Map,
(region, depth) =>
{
if (reader.GetRegionAbsVisibilityToEnemies(region) > 0)
{
List<Thing> 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;
}
}
}
things = region.ListerThings.ThingsInGroup(ThingRequestGroup.BuildingArtificial);
}
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);
Expand Down
91 changes: 91 additions & 0 deletions Source/Rule56/Patches/LordToil_AssaultColony_Patch.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using System.Linq;
using CombatAI.Comps;
using HarmonyLib;
using RimWorld;
using Verse;
using Verse.AI;
using Verse.AI.Group;

namespace CombatAI.Patches
{
public static class LordToil_AssaultColony_Patch
{
private static List<Pawn>[] forces = new List<Pawn>[5];
private static List<Zone_Stockpile> stockpiles = new List<Zone_Stockpile>();

static LordToil_AssaultColony_Patch()
{
forces[0] = new List<Pawn>();
forces[1] = new List<Pawn>();
forces[2] = new List<Pawn>();
forces[3] = new List<Pawn>();
forces[4] = new List<Pawn>();
}

public static void ClearCache()
{
stockpiles.Clear();
forces[0].Clear();
forces[1].Clear();
forces[2].Clear();
forces[3].Clear();
forces[4].Clear();
}

[HarmonyPatch(typeof(LordToil_AssaultColony), nameof(LordToil_AssaultColony.UpdateAllDuties))]
private static class LordToil_AssaultColony_UpdateAllDuties_Patch
{
public static void Postfix(LordToil_AssaultColony __instance)
{
if (__instance.lord.ownedPawns.Count > 10)
{
ClearCache();
stockpiles.AddRange(__instance.Map.zoneManager.AllZones.Where(z => z is Zone_Stockpile).Select(z => z as Zone_Stockpile));
if(stockpiles.Count == 0)
{
return;
}
int m = Rand.Int % 5 + 1;
int taskForceNum = Maths.Min(__instance.lord.ownedPawns.Count / 5, 5);
for (int i = 0; i < __instance.lord.ownedPawns.Count; i++)
{
int k = Rand.Range(0, taskForceNum + m);
if (k <= m)
{
continue;
}
forces[k - m].Add(__instance.lord.ownedPawns[i]);
}
for(int i = 0; i < 5; i++)
{
List<Pawn> force = forces[i];
IntVec3 cell = stockpiles.RandomElementByWeight(s => (int)s.settings.Priority / 5f + GetStockpileTotalMarketValue(s) / 100f).cells.RandomElement();
for (int j = 0; j < force.Count; j++)
{
ThingComp_CombatAI comp = force[j].GetComp_Fast<ThingComp_CombatAI>();
if(comp != null && !comp.duties.Any(DutyDefOf.Defend))
{
var customDuty = CustomDutyUtility.AssaultPoint(force[j], cell, Rand.Range(7, 15), 3600);
comp.duties.StartDuty(customDuty, true);
}
}
}
ClearCache();
}
}
}

private static float GetStockpileTotalMarketValue(Zone_Stockpile stockpile)
{
if (!TKVCache<int, Zone_Stockpile, float>.TryGet(stockpile.ID, out float val, 6000))
{
val = stockpile.AllContainedThings.Sum(t => t.GetStatValue_Fast(StatDefOf.MarketValue, 1200));
TKVCache<int, Zone_Stockpile, float>.Put(stockpile.ID, val);
}
return val;
}
}
}

2 changes: 1 addition & 1 deletion Source/Rule56/Patches/PathFinder_Patch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ internal static bool Prefix(PathFinder __instance, ref PawnPath __result, IntVec
}

float miningSkill = pawn.skills?.GetSkill(SkillDefOf.Mining)?.Level ?? 0f;
if (dig = Finder.Settings.Pather_KillboxKiller && !dump && comp != null && comp.CanSappOrEscort && pawn.mindState?.duty?.def != DutyDefOf.Sapper && pawn.CurJob?.def != JobDefOf.Mine && !comp.IsSapping && !comp.TookDamageRecently(360) && sightReader != null && sightReader.GetAbsVisibilityToEnemies(pawn.Position) == 0 && pawn.RaceProps.Humanlike && pawn.HostileTo(map.ParentFaction) && (pawn.mindState?.duty?.def == DutyDefOf.AssaultColony || pawn.mindState?.duty?.def == DutyDefOf.AssaultThing || pawn.mindState?.duty?.def == DutyDefOf.HuntEnemiesIndividual))
if (dig = Finder.Settings.Pather_KillboxKiller && !dump && comp != null && comp.CanSappOrEscort && pawn.mindState?.duty?.def != DutyDefOf.Sapper && pawn.CurJob?.def != JobDefOf.Mine && !comp.IsSapping && !comp.TookDamageRecently(360) && sightReader != null && sightReader.GetAbsVisibilityToEnemies(pawn.Position) == 0 && pawn.RaceProps.Humanlike && pawn.HostileTo(map.ParentFaction) && (pawn.mindState?.duty?.def == DutyDefOf.AssaultColony || pawn.mindState?.duty?.def == DutyDefOf.AssaultThing || pawn.mindState?.duty?.def == DutyDefOf.HuntEnemiesIndividual || pawn.mindState?.duty?.def == DutyDefOf.Defend))
{
raiders = true;
TraverseParms parms = traverseParms;
Expand Down
Loading