diff --git a/Assemblies/CombatExtended.dll b/Assemblies/CombatExtended.dll
index 19317a8aa7..662c0fd261 100644
Binary files a/Assemblies/CombatExtended.dll and b/Assemblies/CombatExtended.dll differ
diff --git a/Languages/English/Keyed/FloatMenu.xml b/Languages/English/Keyed/FloatMenu.xml
new file mode 100644
index 0000000000..8060d161a6
--- /dev/null
+++ b/Languages/English/Keyed/FloatMenu.xml
@@ -0,0 +1,8 @@
+
+
+ no viable ammo found nearby
+ turret magazine is fully loaded
+ turret belongs to a hostile faction
+ already reloading turret
+ turret is on fire
+
\ No newline at end of file
diff --git a/Patches/Core/ThinkTreeDefs/Humanlike.xml b/Patches/Core/ThinkTreeDefs/Humanlike.xml
index 7ea9cff210..eadd047edd 100644
--- a/Patches/Core/ThinkTreeDefs/Humanlike.xml
+++ b/Patches/Core/ThinkTreeDefs/Humanlike.xml
@@ -42,6 +42,15 @@
+
+
+ Defs/DutyDef[defName="Defend"]/thinkNode/subNodes
+
+
+
+ Prepend
+
+
+ Things/Building/Mech/MechDropBeacon
Graphic_Single
(1,1)
@@ -36,9 +36,9 @@
DormantCompInactive
-
+
True
-
+
diff --git a/Source/CombatExtended/CombatExtended.csproj b/Source/CombatExtended/CombatExtended.csproj
index 9d7f258f53..773c37c7e1 100644
--- a/Source/CombatExtended/CombatExtended.csproj
+++ b/Source/CombatExtended/CombatExtended.csproj
@@ -409,7 +409,6 @@
-
@@ -419,16 +418,19 @@
+
+
+
diff --git a/Source/CombatExtended/CombatExtended/Comps/CompAmmoUser.cs b/Source/CombatExtended/CombatExtended/Comps/CompAmmoUser.cs
index bb7e855134..2a5d34c230 100644
--- a/Source/CombatExtended/CombatExtended/Comps/CompAmmoUser.cs
+++ b/Source/CombatExtended/CombatExtended/Comps/CompAmmoUser.cs
@@ -68,6 +68,7 @@ public Pawn Wielder
return CompEquippable.PrimaryVerb.CasterPawn;
}
}
+ public bool IsEquippedGun => Wielder != null;
public Pawn Holder
{
get
@@ -110,7 +111,7 @@ public bool HasAmmo
return CompInventory != null && CompInventory.ammoList.Any(x => Props.ammoSet.ammoTypes.Any(a => a.ammo == x.def));
}
}
- public bool HasMagazine { get { return Props.magazineSize > 0; } }
+ public bool HasMagazine => Props.magazineSize > 0;
public AmmoDef CurrentAmmo
{
get
@@ -118,6 +119,20 @@ public AmmoDef CurrentAmmo
return UseAmmo ? currentAmmoInt : null;
}
}
+
+ public bool EmptyMagazine => HasMagazine && CurMagCount == 0;
+ public int MissingToFullMagazine
+ {
+ get
+ {
+ if (!HasMagazine) { return 0; }
+ if (SelectedAmmo == CurrentAmmo) { return Props.magazineSize - CurMagCount; }
+ return Props.magazineSize;
+ }
+ }
+
+ public bool FullMagazine => HasMagazine && SelectedAmmo == CurrentAmmo && CurMagCount >= Props.magazineSize;
+
public ThingDef CurAmmoProjectile => Props.ammoSet?.ammoTypes?.FirstOrDefault(x => x.ammo == CurrentAmmo)?.projectile ?? parent.def.Verbs.FirstOrDefault().defaultProjectile;
public CompInventory CompInventory
{
@@ -130,7 +145,7 @@ private IntVec3 Position
{
get
{
- if (Wielder != null) return Wielder.Position;
+ if (IsEquippedGun) return Wielder.Position;
else if (turret != null) return turret.Position;
else if (Holder != null) return Holder.Position;
else return parent.Position;
@@ -171,7 +186,7 @@ public override void Initialize(CompProperties vprops)
{
base.Initialize(vprops);
- //spawnUnloaded checks have all been moved to methods calling ResetAmmoCount.
+ //spawnUnloaded checks have all been moved to methods calling ResetAmmoCount.
//curMagCountInt = Props.spawnUnloaded && UseAmmo ? 0 : Props.magazineSize;
// Initialize ammo with default if none is set
@@ -244,7 +259,7 @@ public bool TryReduceAmmoCount(int ammoConsumedPerShot = 1)
{
ammoConsumedPerShot = (ammoConsumedPerShot > 0) ? ammoConsumedPerShot : 1;
- if (Wielder == null && turret == null)
+ if (!IsEquippedGun && turret == null)
{
Log.Error(parent.ToString() + " tried reducing its ammo count without a wielder");
}
@@ -307,7 +322,7 @@ public void TryStartReload()
}
return;
}
- if (Wielder == null && turret == null)
+ if (!IsEquippedGun && turret == null)
return;
// secondary branch for if we ended up being called up by a turret somehow...
@@ -326,7 +341,7 @@ public void TryStartReload()
TryUnload();
// Check for ammo
- if (Wielder != null && !HasAmmo)
+ if (IsEquippedGun && !HasAmmo)
{
DoOutOfAmmoAction();
return;
@@ -340,7 +355,7 @@ public void TryStartReload()
}
// Issue reload job
- if (Wielder != null)
+ if (IsEquippedGun)
{
Job reloadJob = TryMakeReloadJob();
if (reloadJob == null)
@@ -427,7 +442,7 @@ private void DoOutOfAmmoAction()
{
MoteMaker.ThrowText(Position.ToVector3Shifted(), Find.CurrentMap, "CE_OutOfAmmo".Translate() + "!");
}
- if (Wielder != null && CompInventory != null && (Wielder.CurJob == null || Wielder.CurJob.def != JobDefOf.Hunt)) CompInventory.SwitchToNextViableWeapon();
+ if (IsEquippedGun && CompInventory != null && (Wielder.CurJob == null || Wielder.CurJob.def != JobDefOf.Hunt)) CompInventory.SwitchToNextViableWeapon();
}
public void LoadAmmo(Thing ammo = null)
@@ -552,10 +567,10 @@ public override IEnumerable CompGetGizmosExtra()
GizmoAmmoStatus ammoStatusGizmo = new GizmoAmmoStatus { compAmmo = this };
yield return ammoStatusGizmo;
- if ((Wielder != null && Wielder.Faction == Faction.OfPlayer) || (turret != null && turret.Faction == Faction.OfPlayer && (turret.MannableComp != null || UseAmmo)))
+ if ((IsEquippedGun && Wielder.Faction == Faction.OfPlayer) || (turret != null && turret.Faction == Faction.OfPlayer && (turret.MannableComp != null || UseAmmo)))
{
Action action = null;
- if (Wielder != null) action = TryStartReload;
+ if (IsEquippedGun) action = TryStartReload;
else if (turret?.MannableComp != null) action = turret.TryForceReload;
// Check for teaching opportunities
diff --git a/Source/CombatExtended/CombatExtended/GenClosestAmmo.cs b/Source/CombatExtended/CombatExtended/GenClosestAmmo.cs
deleted file mode 100644
index a2e5e440bd..0000000000
--- a/Source/CombatExtended/CombatExtended/GenClosestAmmo.cs
+++ /dev/null
@@ -1,163 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using RimWorld;
-using Verse;
-using Verse.AI;
-using UnityEngine;
-
-namespace CombatExtended
-{
- public static class GenClosestAmmo
- {
- public const int pawnsPerTurret = 10;
- public const float ammoSearchRadius = 40f;
- public static Dictionary latestAmmoUpdate = new Dictionary();
-
- //Check for any AmmoThing having SPAWNED (not for AmmoThing having been destroyed, as this cannot resolve issues)
- public static Dictionary> listeners = new Dictionary>();
-
- public static Thing ClosestAmmoReachable(IntVec3 root, Map map, CompAmmoUser user, TraverseParms traverseParams, PathEndMode peMode = PathEndMode.ClosestTouch, float maxDistance = 9999f, Predicate validator = null, IEnumerable customGlobalSearchSet = null, int searchRegionsMin = 0, int searchRegionsMax = -1, bool forceAllowGlobalSearch = false, RegionType traversableRegionTypes = RegionType.Set_Passable, bool ignoreEntirelyForbiddenRegions = false)
- {
- //selectedAmmo
- //currentAmmo
- //others
-
- if (user == null)
- {
- Log.ErrorOnce("ClosestAmmoReachable with null CompAmmoUser", 724492);
- return null;
- }
-
- if (user.Props.ammoSet?.ammoTypes.NullOrEmpty() ?? true)
- return null;
-
- bool flag = searchRegionsMax < 0 | forceAllowGlobalSearch;
- if (!flag && customGlobalSearchSet != null)
- {
- Log.ErrorOnce("searchRegionsMax >= 0 && customGlobalSearchSet != null && !forceAllowGlobalSearch. customGlobalSearchSet will never be used.", 6568764, false);
- }
-
- #region EarlyOutSearch-like
- var findAny = user.Props.ammoSet?.ammoTypes?.Any(x => map.listerThings.ThingsOfDef(x.ammo).Any()) ?? false;
-
- if (!findAny)
- return null;
-
- var mDsq = maxDistance * maxDistance;
- if (maxDistance != 9999f)
- {
- var mDsqMax = Math.Max(
- Math.Max(root.DistanceToSquared(IntVec3.Zero), root.DistanceToSquared(map.Size)),
- Math.Max(root.DistanceToSquared(new IntVec3(0, 0, map.Size.z)), root.DistanceToSquared(new IntVec3(map.Size.x, 0, 0))));
-
- //maxDistance smaller than the maximum distance possible within the map -- already exclude some of the ammo
- if (mDsq < mDsqMax)
- {
- if (!user.Props.ammoSet.ammoTypes.Any(x => map.listerThings.ThingsOfDef(x.ammo).Any(y => y.Position.DistanceToSquared(root) <= mDsq)))
- return null;
- }
- else
- if (!user.Props.ammoSet.ammoTypes.Any(x => map.listerThings.ThingsOfDef(x.ammo).Any()))
- return null;
-
- }
- #endregion
-
- #region Preparing list to search through
- List thingList = new List();
-
- foreach (var ammo in user.Props.ammoSet.ammoTypes.Select(x => x.ammo))
- {
- thingList.AddRange(map.listerThings.ThingsOfDef(ammo).Where(y => y.Position.DistanceToSquared(root) <= mDsq
- && (!(y as AmmoThing)?.IsCookingOff ?? true)
- && !y.IsBurning()));
- }
- #endregion
-
- Func priorityGetter = (x => { if (x.def == user.SelectedAmmo) return 3f; else if (x.def == user.CurrentAmmo) return 2f; else return 1f; });
-
- int num = (searchRegionsMax > 0) ? searchRegionsMax : 30;
- var thing = RegionwiseBFSWorker(root, map, thingList, peMode, traverseParams, validator, priorityGetter, searchRegionsMin, num, mDsq, out int num2, traversableRegionTypes, ignoreEntirelyForbiddenRegions);
- var flag2 = (thing == null && num2 < num);
-
- if ((thing == null & flag) && !flag2)
- {
- if (traversableRegionTypes != RegionType.Set_Passable)
- {
- Log.ErrorOnce("ClosestAmmoReachable had to do a global search, but traversableRegionTypes is not set to passable only. It's not supported, because Reachability is based on passable regions only.", 14345767, false);
- }
-
- Predicate validator2 = (Thing t) => map.reachability.CanReach(root, t, peMode, traverseParams) && (validator == null || validator(t));
- thing = GenClosest.ClosestThing_Global(root, thingList, maxDistance, validator2, null);
- }
- return thing;
- }
-
- public static Thing RegionwiseBFSWorker(IntVec3 root, Map map, List thingList, PathEndMode peMode, TraverseParms traverseParams, Predicate validator, Func priorityGetter, int minRegions, int maxRegions, float mDsq, out int regionsSeen, RegionType traversableRegionTypes = RegionType.Set_Passable, bool ignoreEntirelyForbiddenRegions = false)
- {
- regionsSeen = 0;
- if (traverseParams.mode == TraverseMode.PassAllDestroyableThings)
- {
- Log.Error("CombatExtended :: RegionwiseBFSWorker with traverseParams.mode PassAllDestroyableThings. Use ClosestThingGlobal.", false);
- return null;
- }
- if (traverseParams.mode == TraverseMode.PassAllDestroyableThingsNotWater)
- {
- Log.Error("CombatExtended :: RegionwiseBFSWorker with traverseParams.mode PassAllDestroyableThingsNotWater. Use ClosestThingGlobal.", false);
- return null;
- }
- Region region = root.GetRegion(map, traversableRegionTypes);
- if (region == null)
- {
- return null;
- }
-
- RegionEntryPredicate entryCondition = (Region from, Region to) => to.Allows(traverseParams, false) && (mDsq > 25000000f || to.extentsClose.ClosestDistSquaredTo(root) < mDsq);
- Thing closestThing = null;
- int regionsSeenScan2 = 0;
- float closestDistSquared = 9999999f;
- float bestPrio = -1;
- RegionProcessor regionProcessor = delegate (Region r)
- {
- int regionsSeenScan;
- if (RegionTraverser.ShouldCountRegion(r))
- {
- regionsSeenScan = regionsSeenScan2;
- regionsSeenScan2++;
- }
- if (!r.IsDoorway && !r.Allows(traverseParams, true))
- {
- return false;
- }
- if (!ignoreEntirelyForbiddenRegions || !r.IsForbiddenEntirely(traverseParams.pawn))
- {
- foreach (var item in thingList)
- {
- if (ReachabilityWithinRegion.ThingFromRegionListerReachable(item, r, peMode, traverseParams.pawn))
- {
- float num = (priorityGetter != null) ? priorityGetter(item) : 0f;
- if (num >= bestPrio)
- {
- float num2 = (float)(item.Position - root).LengthHorizontalSquared;
- if ((num > bestPrio || num2 < closestDistSquared) && num2 < mDsq && (validator == null || validator(item)))
- {
- closestThing = item;
- closestDistSquared = num2;
- bestPrio = num;
- }
- }
- }
- }
- }
- return regionsSeenScan2 >= minRegions && closestThing != null;
- };
-
- RegionTraverser.BreadthFirstTraverse(region, entryCondition, regionProcessor, maxRegions, traversableRegionTypes);
- regionsSeen = regionsSeenScan2;
- return closestThing;
- }
- }
-}
diff --git a/Source/CombatExtended/CombatExtended/Jobs/JobDriver_ReloadTurret.cs b/Source/CombatExtended/CombatExtended/Jobs/JobDriver_ReloadTurret.cs
index e237ca9d85..e8c87aa431 100644
--- a/Source/CombatExtended/CombatExtended/Jobs/JobDriver_ReloadTurret.cs
+++ b/Source/CombatExtended/CombatExtended/Jobs/JobDriver_ReloadTurret.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using CombatExtended.CombatExtended.LoggerUtils;
using RimWorld;
using UnityEngine;
using Verse;
@@ -33,7 +34,22 @@ private CompAmmoUser compReloader
public override bool TryMakePreToilReservations(bool errorOnFailed)
{
- return pawn.Reserve(TargetA, job) && (ammo == null || pawn.Reserve(TargetB, job, Mathf.Max(1, TargetThingB.stackCount - job.count), job.count));
+ if (!pawn.Reserve(TargetA, job))
+ {
+ CELogger.Message("Combat Extended: Could not reserve turret for reloading job.");
+ return false;
+ }
+ if (ammo == null)
+ {
+ CELogger.Message("Combat Extended: Ammo is null");
+ return false;
+ }
+ if (!pawn.Reserve(TargetB, job, Mathf.Max(1, TargetThingB.stackCount - job.count), job.count)) {
+ CELogger.Message("Combat Extended: Could not reserve " + Mathf.Max(1, TargetThingB.stackCount - job.count) + " of ammo.");
+ return false;
+ }
+ CELogger.Message("Combat Extended: Managed to reserve everything successfully.");
+ return true;
}
public override string GetReport()
diff --git a/Source/CombatExtended/CombatExtended/Jobs/JobGiver_DefenderReloadTurret.cs b/Source/CombatExtended/CombatExtended/Jobs/JobGiver_DefenderReloadTurret.cs
new file mode 100644
index 0000000000..8e5309d34b
--- /dev/null
+++ b/Source/CombatExtended/CombatExtended/Jobs/JobGiver_DefenderReloadTurret.cs
@@ -0,0 +1,56 @@
+using CombatExtended.CombatExtended.Jobs.Utils;
+using CombatExtended.CombatExtended.LoggerUtils;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Verse;
+using Verse.AI;
+
+namespace CombatExtended
+{
+ public class JobGiver_DefenderReloadTurret : ThinkNode_JobGiver
+ {
+ ///
+ /// How low can ammo get before we want to reload the turret?
+ /// Set arbitrarily, balance if needed.
+ ///
+ private const float ammoReloadThreshold = .5f;
+ protected override Job TryGiveJob(Pawn pawn)
+ {
+ var turret = TryFindTurretWhichNeedsReloading(pawn);
+ if (turret == null)
+ {
+ return null; // signals ThinkResult.NoJob.
+ }
+ return JobGiverUtils_Reload.MakeReloadJob(pawn, turret);
+ }
+
+ private Building_TurretGunCE TryFindTurretWhichNeedsReloading(Pawn pawn)
+ {
+ bool _isTurretThatNeedsReloadingNow(Thing t)
+ {
+ var turret = t as Building_TurretGunCE;
+ if (turret == null) { return false; }
+ if (!JobGiverUtils_Reload.CanReload(pawn, turret, forced: false, emergency: true)) { return false; }
+
+ return turret.CompAmmo.CurMagCount <= turret.CompAmmo.Props.magazineSize / ammoReloadThreshold;
+ };
+ Thing hopefullyTurret = GenClosest.ClosestThingReachable(pawn.Position,
+ pawn.Map,
+ ThingRequest.ForGroup(ThingRequestGroup.BuildingArtificial),
+ PathEndMode.Touch,
+ TraverseParms.For(pawn),
+ 100f,
+ _isTurretThatNeedsReloadingNow);
+
+ var actuallyTurret = hopefullyTurret as Building_TurretGunCE;
+ if (actuallyTurret == null) { return null; }
+ return actuallyTurret;
+
+ }
+
+
+ }
+}
diff --git a/Source/CombatExtended/CombatExtended/Jobs/Utils/JobGiverUtils_Reload.cs b/Source/CombatExtended/CombatExtended/Jobs/Utils/JobGiverUtils_Reload.cs
new file mode 100644
index 0000000000..34d796e88c
--- /dev/null
+++ b/Source/CombatExtended/CombatExtended/Jobs/Utils/JobGiverUtils_Reload.cs
@@ -0,0 +1,200 @@
+using CombatExtended.CombatExtended.LoggerUtils;
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+using Verse;
+using Verse.AI;
+using Verse.Noise;
+
+namespace CombatExtended.CombatExtended.Jobs.Utils
+{
+ class JobGiverUtils_Reload
+ {
+ ///
+ /// The maximum allowed pathing cost to reach potential ammo. 2 ingame hours.
+ /// This is arbitrarily set. If you think this is too high or too low, feel free to change.
+ ///
+ private const float MaxPathCost = 2f * 60f * GenDate.TicksPerHour;
+ ///
+ /// Magic number. I took it from the now deprecated GenClosestAmmo class. Not sure why we would want 10 reservations, but there it is.
+ ///
+ private const int MagicMaxPawns = 10;
+
+ public static Job MakeReloadJob(Pawn pawn, Building_TurretGunCE turret)
+ {
+ var compAmmo = turret.CompAmmo;
+ var amountNeeded = turret.CompAmmo.MissingToFullMagazine;
+ if (compAmmo == null)
+ {
+ CELogger.Error("Tried to create a reload job on a thing that's not reloadable.");
+ return null;
+ }
+
+ var ammo = FindBestAmmo(pawn, turret);
+ if (ammo == null)
+ {
+ CELogger.Error($"{pawn} tried to create a reload job without ammo. This should have been checked earlier.");
+ return null;
+ }
+ CELogger.Message($"Making a reload job for {pawn}, {turret} and {ammo}");
+
+ Job job = JobMaker.MakeJob(CE_JobDefOf.ReloadTurret, turret, ammo);
+ job.count = Mathf.Min(ammo.stackCount, turret.CompAmmo.MissingToFullMagazine);
+ return job;
+ }
+
+ public static Job MakeReloadJobNoAmmo(Building_TurretGunCE turret)
+ {
+ var compAmmo = turret.TryGetComp();
+ if (compAmmo == null)
+ {
+ CELogger.Error("Tried to create a reload job on a thing that's not reloadable.");
+ return null;
+ }
+
+ return JobMaker.MakeJob(CE_JobDefOf.ReloadTurret, turret, null);
+ }
+
+ public static bool CanReload(Pawn pawn, Thing hopefullyTurret, bool forced = false, bool emergency = false)
+ {
+ if (pawn == null || hopefullyTurret == null)
+ {
+ CELogger.Error($"{pawn?.ToString() ?? "null pawn"} could not reload {hopefullyTurret?.ToString() ?? "null thing"} one of the two was null.");
+ return false;
+ }
+ if (!(hopefullyTurret is Building_TurretGunCE))
+ {
+ CELogger.Error($"{pawn} could not reload {hopefullyTurret} because {hopefullyTurret} is not a Combat Extended Turret. If you are a modder, make sure to use {nameof(CombatExtended)}.{nameof(Building_TurretGunCE)} for your turret's compClass.");
+ return false;
+ }
+ var turret = hopefullyTurret as Building_TurretGunCE;
+ var compAmmo = turret.CompAmmo;
+
+ if (compAmmo == null)
+ {
+ CELogger.Error($"{pawn} could not reload {turret} because turret has no {nameof(CompAmmoUser)}.");
+ return false;
+ }
+ if (turret.isReloading)
+ {
+ CELogger.Message($"{pawn} could not reload {turret} because turret is already reloading.");
+ JobFailReason.Is("CE_TurretAlreadyReloading".Translate());
+ return false;
+ }
+ if (turret.IsBurning() && !emergency)
+ {
+ CELogger.Message($"{pawn} could not reload {turret} because turret is on fire.");
+ JobFailReason.Is("CE_TurretIsBurning".Translate());
+ }
+ if (compAmmo.FullMagazine)
+ {
+ CELogger.Message($"{pawn} could not reload {turret} because it is full of ammo.");
+ JobFailReason.Is("CE_TurretFull".Translate());
+ return false;
+ }
+ if (turret.IsForbidden(pawn) || !pawn.CanReserve(turret, 1, -1, null, forced))
+ {
+ CELogger.Message($"{pawn} could not reload {turret} because it is forbidden or otherwise busy.");
+ return false;
+ }
+ if (turret.Faction != pawn.Faction && (turret.Faction != null && pawn.Faction != null && turret.Faction.RelationKindWith(pawn.Faction) != FactionRelationKind.Ally))
+ {
+ CELogger.Message($"{pawn} could not reload {turret} because the turret is hostile to them.");
+ JobFailReason.Is("CE_TurretNonAllied".Translate());
+ return false;
+ }
+ if ((turret.MannableComp?.ManningPawn != pawn) && !pawn.CanReserveAndReach(turret, PathEndMode.ClosestTouch, forced ? Danger.Deadly : pawn.NormalMaxDanger(), MagicMaxPawns))
+ {
+ CELogger.Message($"{pawn} could not reload {turret} because turret is manned (or was recently manned) by someone else.");
+ return false;
+ }
+ if (FindBestAmmo(pawn, turret) == null)
+ {
+ JobFailReason.Is("CE_NoAmmoAvailable".Translate());
+ return false;
+ }
+ return true;
+ }
+
+ private static Thing FindBestAmmo(Pawn pawn, Building_TurretGunCE reloadable)
+ {
+ //ThingFilter filter = refuelable.TryGetComp().Props.fuelFilter;
+ var requestedAmmo = reloadable.CompAmmo.SelectedAmmo;
+
+ bool validator(Thing potentialAmmo)
+ {
+ if (potentialAmmo.IsForbidden(pawn) || !pawn.CanReserve(potentialAmmo))
+ {
+ return false;
+ }
+ return GetPathCost(pawn, potentialAmmo) <= MaxPathCost;
+ }
+
+ return GenClosest.ClosestThingReachable(pawn.Position, pawn.Map, ThingRequest.ForDef(requestedAmmo), PathEndMode.ClosestTouch, TraverseParms.For(pawn), 9999f, validator);
+ }
+
+ ///
+ /// This method is a direct copy/paste of the private FindAllFuel method.
+ ///
+ /// Finds all relevant ammo in order of distance.
+ ///
+ ///
+ ///
+ ///
+ private static List FindAllAmmo(Pawn pawn, Building_TurretGunCE reloadable)
+ {
+ int quantity = reloadable.CompAmmo.MissingToFullMagazine;
+ var ammoKind = reloadable.CompAmmo.SelectedAmmo;
+ bool validator(Thing potentialAmmo)
+ {
+ if (potentialAmmo.IsForbidden(pawn) || !pawn.CanReserve(potentialAmmo))
+ {
+ return false;
+ }
+ return GetPathCost(pawn, potentialAmmo) <= MaxPathCost;
+ }
+ Region region = reloadable.Position.GetRegion(pawn.Map);
+ TraverseParms traverseParams = TraverseParms.For(pawn);
+ bool entryCondition(Region from, Region r) => r.Allows(traverseParams, isDestination: false);
+ var chosenThings = new List();
+ int accumulatedQuantity = 0;
+ bool regionProcessor(Region r)
+ {
+ List list = r.ListerThings.ThingsMatching(ThingRequest.ForDef(ammoKind));
+ foreach (var thing in list)
+ {
+ if (validator(thing) && !chosenThings.Contains(thing) && ReachabilityWithinRegion.ThingFromRegionListerReachable(thing, r, PathEndMode.ClosestTouch, pawn))
+ {
+ chosenThings.Add(thing);
+ accumulatedQuantity += thing.stackCount;
+ if (accumulatedQuantity >= quantity)
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ RegionTraverser.BreadthFirstTraverse(region, entryCondition, regionProcessor, 99999);
+ if (accumulatedQuantity >= quantity)
+ {
+ return chosenThings;
+ }
+ return null;
+ }
+
+ private static float GetPathCost(Pawn pawn, Thing thing)
+ {
+ var cell = thing.Position;
+ var pos = pawn.Position;
+ var traverseParams = TraverseParms.For(pawn, Danger.Deadly, TraverseMode.PassDoors, false);
+
+ using (PawnPath path = pawn.Map.pathFinder.FindPath(pos, cell, traverseParams, PathEndMode.Touch)) {
+ return path.TotalCost;
+ }
+ }
+ }
+
+}
diff --git a/Source/CombatExtended/CombatExtended/Jobs/WorkGiver_ReloadTurret.cs b/Source/CombatExtended/CombatExtended/Jobs/WorkGiver_ReloadTurret.cs
index 481f742423..803a162d0f 100644
--- a/Source/CombatExtended/CombatExtended/Jobs/WorkGiver_ReloadTurret.cs
+++ b/Source/CombatExtended/CombatExtended/Jobs/WorkGiver_ReloadTurret.cs
@@ -7,67 +7,21 @@
using Verse;
using Verse.AI;
using UnityEngine;
+using CombatExtended.CombatExtended.LoggerUtils;
+using CombatExtended.CombatExtended.Jobs.Utils;
namespace CombatExtended
{
public class WorkGiver_ReloadTurret : WorkGiver_Scanner
{
- public override ThingRequest PotentialWorkThingRequest
- {
- get
- {
- return ThingRequest.ForGroup(ThingRequestGroup.BuildingArtificial);
- }
- }
-
- private bool Checks(Pawn pawn, Building_TurretGunCE turret, bool forced)
- {
- //WorkTagIsDisabled check inherent to WorkGiver_Scanner
- //MissingRequiredCapacity check inherent to WorkGiver_Scanner
- if (turret.isReloading)
- {
- //Log.Message(pawn.ThingID + " failed " + turret.ThingID + ": turret is already reloading");
- return false; //Turret is already reloading
- }
- if (turret.FullMagazine)
- {
- // Log.Message(pawn.ThingID + " failed " + turret.ThingID + ": turret doesn't need reload");
- return false; //Turret doesn't need reload
- }
- //if (!forced && !turret.AllowAutomaticReload) return false; //Turret doesn't need auto-reload and isn't forced to reload
- if (turret.IsBurning())
- {
- // Log.Message(pawn.ThingID + " failed " + turret.ThingID + ": turret on fire");
- return false; //Turret on fire
- }
- if (turret.Faction != pawn.Faction && (turret.Faction != null && pawn.Faction != null && turret.Faction.RelationKindWith(pawn.Faction) != FactionRelationKind.Ally))
- {
- // Log.Message(pawn.ThingID + " failed " + turret.ThingID + ": non-allied turret");
- return false; //Allies reload turrets
- }
- if ((turret.MannableComp?.ManningPawn != pawn) && !pawn.CanReserveAndReach(turret, PathEndMode.ClosestTouch, forced ? Danger.Deadly : pawn.NormalMaxDanger(), GenClosestAmmo.pawnsPerTurret))
- {
- //Log.Message(pawn.ThingID + " failed " + turret.ThingID + ": turret unreachable");
- return false; //Pawn cannot reach turret
- }
-
- return true;
- }
+ public override ThingRequest PotentialWorkThingRequest => ThingRequest.ForGroup(ThingRequestGroup.BuildingArtificial);
- public override float GetPriority(Pawn pawn, TargetInfo t)
- {
- return GetThingPriority(pawn, t.Thing);
- }
+ public override float GetPriority(Pawn pawn, TargetInfo t) => GetThingPriority(pawn, t.Thing);
private float GetThingPriority(Pawn pawn, Thing t, bool forced = false)
{
+ CELogger.Message($"pawn: {pawn}. t: {t}. forced: {forced}");
Building_TurretGunCE turret = t as Building_TurretGunCE;
- if (pawn == null)
- {
- //Log.Message("Checking " + turret.ThingID + ", found pawn == null");
- return 0f;
- }
- if (turret == null || turret.IsForbidden(pawn) || !Checks(pawn, turret, forced)) return 0f;
if (!turret.Active) return 1f;
if (turret.EmptyMagazine) return 9f;
@@ -77,7 +31,28 @@ private float GetThingPriority(Pawn pawn, Thing t, bool forced = false)
public override bool ShouldSkip(Pawn pawn, bool forced = false)
{
- return !forced && pawn.CurJob != null && pawn.CurJobDef != JobDefOf.ManTurret && (pawn.CurJob.playerForced || pawn.CurJobDef == CE_JobDefOf.ReloadTurret || pawn.CurJobDef == CE_JobDefOf.ReloadWeapon);
+ if (forced)
+ {
+ CELogger.Message("Job is forced. Not skipping.");
+ return false;
+ }
+ if (pawn.CurJob == null)
+ {
+ CELogger.Message($"Pawn {pawn.ThingID} has no job. Not skipping.");
+ return false;
+ }
+ if (pawn.CurJobDef == JobDefOf.ManTurret)
+ {
+ CELogger.Message($"Pawn {pawn.ThingID}'s current job is {nameof(JobDefOf.ManTurret)}. Not skipping.");
+ return false;
+ }
+ if (pawn.CurJob.playerForced)
+ {
+ CELogger.Message($"Pawn {pawn.ThingID}'s current job is forced by the player. Skipping.");
+ return true;
+ }
+ CELogger.Message($"Pawn {pawn.ThingID}'s current job is {pawn.CurJobDef.reportString}. Skipping");
+ return (pawn.CurJobDef == CE_JobDefOf.ReloadTurret || pawn.CurJobDef == CE_JobDefOf.ReloadWeapon);
}
///
@@ -87,53 +62,23 @@ public override bool ShouldSkip(Pawn pawn, bool forced = false)
/// Can be non-turret
/// Generally not true
///
- /*public override bool HasJobOnThing(Pawn pawn, Thing t, bool forced = false)
- {
- Building_TurretGunCE turret = t as Building_TurretGunCE;
- if (turret == null || !Checks(pawn, turret, forced)) return false;
-
- if (!turret.CompAmmo.UseAmmo)
- return true;
-
- turret.UpdateNearbyAmmo();
-
- Thing ammo = turret.nearestViableAmmo;
-
- int amountNeeded = turret.CompAmmo.Props.magazineSize;
- if (turret.CompAmmo.CurrentAmmo == turret.CompAmmo.SelectedAmmo) amountNeeded -= turret.CompAmmo.CurMagCount;
-
- if (ammo == null || ammo.IsForbidden(pawn) || !pawn.CanReserveAndReach(new LocalTargetInfo(ammo), PathEndMode.ClosestTouch, pawn.NormalMaxDanger(), Mathf.Max(1, ammo.stackCount - amountNeeded), amountNeeded, null, forced))
- {
- ammo = turret.InventoryAmmo(pawn.TryGetComp());
- }
-
- return ammo != null;
- }*/
-
+
public override bool HasJobOnThing(Pawn pawn, Thing t, bool forced = false)
{
+ if (!(t is Building_TurretGunCE)) {
+ return false;
+ }
var priority = GetThingPriority(pawn, t, forced);
- if (priority == 0f) return false;
+ CELogger.Message($"Priority check completed. Got {priority}");
Building_TurretGunCE turret = t as Building_TurretGunCE;
+ CELogger.Message($"Turret uses ammo? {turret.CompAmmo.UseAmmo}");
if (!turret.CompAmmo.UseAmmo)
return true;
- int amountNeeded = turret.CompAmmo.Props.magazineSize;
- if (turret.CompAmmo.CurrentAmmo == turret.CompAmmo.SelectedAmmo) amountNeeded -= turret.CompAmmo.CurMagCount;
-
- turret.UpdateNearbyAmmo();
- Thing ammo = PawnClosestAmmo(pawn, turret, out bool _, forced);
-
- if (ammo == null)
- return false;
-
- // Update selected ammo if necessary
- if (ammo.def != turret.CompAmmo.SelectedAmmo
- && priority < 9f && ammo.stackCount < amountNeeded) //New option can fill magazine completely (and is otherwise closer)
- return false;
+ CELogger.Message($"Total magazine size: {turret.CompAmmo.Props.magazineSize}. Needed: {turret.CompAmmo.MissingToFullMagazine}");
- return true;
+ return JobGiverUtils_Reload.CanReload(pawn, turret, forced);
}
//Checks before called (ONLY when in SCANNER):
@@ -153,123 +98,16 @@ public override bool HasJobOnThing(Pawn pawn, Thing t, bool forced = false)
///
public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false)
{
- var priority = GetThingPriority(pawn, t, forced);
- if (priority == 0f) return null;
-
//Do not check for NeedsReload etc. -- if forced, treat as if NeedsReload && AllowAutomaticReload
Building_TurretGunCE turret = t as Building_TurretGunCE;
if (!turret.CompAmmo.UseAmmo)
- return new Job(CE_JobDefOf.ReloadTurret, t, null);
-
- int amountNeeded = turret.CompAmmo.Props.magazineSize;
- if (turret.CompAmmo.CurrentAmmo == turret.CompAmmo.SelectedAmmo) amountNeeded -= turret.CompAmmo.CurMagCount;
-
- turret.UpdateNearbyAmmo();
-
- var ammo = PawnClosestAmmo(pawn, turret, out bool fromInventory, forced);
-
- if (ammo == null)
- return null;
-
- // Update selected ammo if necessary
- if (ammo.def != turret.CompAmmo.SelectedAmmo) //New option can fill magazine completely (and is otherwise closer)
- {
- if ((priority >= 9f || ammo.stackCount >= amountNeeded))
- turret.CompAmmo.SelectedAmmo = ammo.def as AmmoDef;
- else
- return null;
- }
-
- if (fromInventory && !pawn.TryGetComp().container.TryDrop(ammo, pawn.Position, pawn.Map, ThingPlaceMode.Direct, Mathf.Min(ammo.stackCount, amountNeeded), out ammo))
- {
- Log.ErrorOnce("Found optimal ammo (" + ammo.LabelCap + "), but could not drop from " + pawn.LabelCap, 8164528);
- return null;
- }
-
- return new Job(CE_JobDefOf.ReloadTurret, t, ammo) { count = Mathf.Min(amountNeeded, ammo.stackCount) };
- }
-
- private Thing PawnClosestAmmo(Pawn pawn, Building_TurretGunCE turret, out bool fromInventory, bool forced = false)
- {
- var testAmmo = turret.nearestViableAmmo;
-
- int amount = turret.CompAmmo.Props.magazineSize;
- if (turret.CompAmmo.CurrentAmmo == turret.CompAmmo.SelectedAmmo) amount -= turret.CompAmmo.CurMagCount;
-
- if (testAmmo == null
- || testAmmo.IsForbidden(pawn)
- || !pawn.CanReserveAndReach(new LocalTargetInfo(testAmmo), PathEndMode.ClosestTouch, forced ? Danger.Deadly : pawn.NormalMaxDanger(),
- Mathf.Max(1, testAmmo.stackCount - amount), Mathf.Min(testAmmo.stackCount, amount), null, forced))
- {
- testAmmo = turret.InventoryAmmo(pawn?.TryGetComp());
- fromInventory = true;
- }
- else
- fromInventory = false;
+ return JobGiverUtils_Reload.MakeReloadJobNoAmmo(turret);
- return testAmmo;
- }
-
- public Pawn BestNonJobUser(IEnumerable pawns, Building_TurretGunCE turret, out bool fromInventory, bool forced = false)
- {
- fromInventory = false;
-
- if (!pawns.Any()) return null;
-
- //Cut a significant portion of pawns
- pawns = pawns.Where(p => !ShouldSkip(p) && !p.Downed && !p.Dead && p.Spawned && (p.mindState?.Active ?? true)
- && (!p.mindState?.mentalStateHandler?.InMentalState ?? true) && !turret.IsForbidden(p)
- && (turret.Faction == p.Faction || turret.Faction?.RelationKindWith(p.Faction) == FactionRelationKind.Ally)
- && p.CanReserveAndReach(new LocalTargetInfo(turret), PathEndMode.InteractionCell, forced ? Danger.Deadly : p.NormalMaxDanger(), GenClosestAmmo.pawnsPerTurret, -1, null, forced));
-
- //No pawns remaining => quit
- if (!pawns.Any())
- return null;
-
- //If no ammo is used, minimize distance
- if (!turret.CompAmmo?.UseAmmo ?? true)
- return pawns.MinBy(x => x.Position.DistanceTo(turret.Position));
-
- //ELSE: ammo is used, pawns remain
- turret.UpdateNearbyAmmo(forced);
- var hasNearbyAmmo = turret.nearestViableAmmo != null;
-
- int amount = turret.CompAmmo.Props.magazineSize;
- if (turret.CompAmmo.CurrentAmmo == turret.CompAmmo.SelectedAmmo) amount -= turret.CompAmmo.CurMagCount;
-
- if (hasNearbyAmmo)
- pawns = pawns.OrderBy(x => x.Position.DistanceTo(turret.Position));
-
- var bestNonInventoryDistance = 9999f;
- Pawn bestPawn = null;
- var minimumDistance = hasNearbyAmmo ? turret.Position.DistanceTo(turret.nearestViableAmmo.Position) : 0f;
-
- //Sort through ASCENDING DISTANCE TO TURRET -- e.g, inventory ammo is preferred
- foreach (var p in pawns)
- {
- if (PawnClosestAmmo(p, turret, out fromInventory, forced) == null)
- continue;
-
- if (fromInventory)
- {
- if (p.Position.DistanceTo(turret.Position) < bestNonInventoryDistance + minimumDistance)
- return p;
- }
- else if (hasNearbyAmmo)
- {
- var dist = p.Position.DistanceTo(turret.nearestViableAmmo.Position);
-
- if (dist < bestNonInventoryDistance)
- {
- bestNonInventoryDistance = dist;
- bestPawn = p;
- }
- }
- }
+ // NOTE: The removal of the code that used to be here disables reloading turrets directly from one's inventory.
+ // The player would need to drop the ammo the pawn is holding first.
- fromInventory = false;
- return bestPawn;
+ return JobGiverUtils_Reload.MakeReloadJob(pawn, turret);
}
}
}
\ No newline at end of file
diff --git a/Source/CombatExtended/CombatExtended/LoggerUtils/CELogger.cs b/Source/CombatExtended/CombatExtended/LoggerUtils/CELogger.cs
new file mode 100644
index 0000000000..2990ea58ed
--- /dev/null
+++ b/Source/CombatExtended/CombatExtended/LoggerUtils/CELogger.cs
@@ -0,0 +1,35 @@
+using Verse;
+
+namespace CombatExtended.CombatExtended.LoggerUtils
+{
+ class CELogger
+ {
+ ///
+ /// Am I in debug mode? Set this to true and recompile for more log output.
+ /// TODO: Maybe change this to a toggleable setting if I ever have the time to figure out how <3.
+ ///
+ private static readonly bool isInDebugMode = false;
+
+ public static void Message(string message, bool showOutOfDebugMode = false, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "")
+ {
+ if (!isInDebugMode && !showOutOfDebugMode) { return; }
+ Log.Message($"CE - {memberName}() ({Find.TickManager.TicksGame}) - {message}");
+ }
+ public static void Warn(string message, bool showOutOfDebugMode = false, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "")
+ {
+ if (!isInDebugMode && !showOutOfDebugMode) { return; }
+ Log.Warning($"CE - {memberName}() ({Find.TickManager.TicksGame}) - {message}");
+ }
+ public static void Error(string message, bool showOutOfDebugMode = true, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "")
+ {
+ if (!isInDebugMode && !showOutOfDebugMode) { return; }
+ Log.Error($"CE - {memberName}() ({Find.TickManager.TicksGame}) - {message}");
+ }
+
+ public static void ErrorOnce(string message, bool showOutOfDebugMode = true, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "", [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
+ {
+ if (!isInDebugMode && !showOutOfDebugMode) { return; }
+ Log.ErrorOnce($"CE - {memberName}() ({Find.TickManager.TicksGame}) - {message}", sourceLineNumber);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/CombatExtended/CombatExtended/Things/AmmoThing.cs b/Source/CombatExtended/CombatExtended/Things/AmmoThing.cs
index be86884485..ed6517bebd 100644
--- a/Source/CombatExtended/CombatExtended/Things/AmmoThing.cs
+++ b/Source/CombatExtended/CombatExtended/Things/AmmoThing.cs
@@ -108,10 +108,6 @@ public override void Tick()
Destroy(DestroyMode.KillFinalize);
}
}
-
- //Resubscribe ammo
- if (numToCookOff <= 0)
- RegisterAmmo();
}
}
@@ -197,14 +193,6 @@ private bool TryLaunchCookOffProjectile()
return true;
}
- public override void SpawnSetup(Map map, bool respawningAfterLoad)
- {
- base.SpawnSetup(map, respawningAfterLoad);
-
- //Keep track of newly spawned, non-cookoff AmmoThing
- RegisterAmmo();
- }
-
public override void ExposeData()
{
base.ExposeData();
@@ -212,39 +200,6 @@ public override void ExposeData()
//Such that save-reloading doesn't stop ammo cookoff
Scribe_Values.Look(ref numToCookOff, "numToCookOff", 0);
}
-
- public override void DeSpawn(DestroyMode mode = DestroyMode.Vanish)
- {
- base.DeSpawn(mode);
-
- if (GenClosestAmmo.listeners.ContainsKey(def))
- {
- foreach (var listener in GenClosestAmmo.listeners[def])
- {
- if (listener.nearestViableAmmo == this)
- {
- listener.nearestViableAmmo = null;
- listener.isSlow = false;
- }
- }
-
- if (GenClosestAmmo.latestAmmoUpdate.ContainsKey(def))
- GenClosestAmmo.latestAmmoUpdate[def] = Find.TickManager.TicksGame;
- }
- else if (GenClosestAmmo.latestAmmoUpdate.ContainsKey(def))
- GenClosestAmmo.latestAmmoUpdate.Clear();
- }
-
- private void RegisterAmmo()
- {
- if (numToCookOff <= 0 && !this.IsBurning() && GenClosestAmmo.listeners.ContainsKey(def))
- {
- GenClosestAmmo.listeners[def].ForEach(x => x.isSlow = false);
-
- if (GenClosestAmmo.latestAmmoUpdate.ContainsKey(def))
- GenClosestAmmo.latestAmmoUpdate[def] = Find.TickManager.TicksGame;
- }
- }
#endregion
}
}
diff --git a/Source/CombatExtended/CombatExtended/Things/Building_TurretGunCE.cs b/Source/CombatExtended/CombatExtended/Things/Building_TurretGunCE.cs
index 112213464f..41aad73f5e 100644
--- a/Source/CombatExtended/CombatExtended/Things/Building_TurretGunCE.cs
+++ b/Source/CombatExtended/CombatExtended/Things/Building_TurretGunCE.cs
@@ -8,6 +8,8 @@
using Verse.AI.Group;
using Verse.Sound;
using UnityEngine;
+using CombatExtended.CombatExtended.LoggerUtils;
+using CombatExtended.CombatExtended.Jobs.Utils;
namespace CombatExtended
{
@@ -46,7 +48,7 @@ public class Building_TurretGunCE : Building_Turret
private CompChangeableProjectile compChangeable = null;
public bool isReloading = false;
private int ticksUntilAutoReload = 0;
- private int lastSurroundingAmmoCheck = 0;
+ private int lastSurroundingAmmoCheck = int.MinValue;
#endregion
@@ -78,9 +80,9 @@ public Thing Gun
{
this.gunInt = ThingMaker.MakeThing(this.def.building.turretGunDef, null);
this.compAmmo = gunInt.TryGetComp();
-
+
InitGun();
-
+
// FIXME: Hack to make player-crafted turrets spawn unloaded
if (//Map != null && !Map.IsPlayerHome
//!Faction.IsPlayer
@@ -98,22 +100,22 @@ public ThingDef Projectile
{
if (CompAmmo != null && CompAmmo.CurrentAmmo != null)
{
- return CompAmmo.CurAmmoProjectile;
+ return CompAmmo.CurAmmoProjectile;
}
if (CompChangeable != null && CompChangeable.Loaded)
{
- return CompChangeable.Projectile;
+ return CompChangeable.Projectile;
}
return this.GunCompEq.PrimaryVerb.verbProps.defaultProjectile;
}
}
public CompChangeableProjectile CompChangeable
{
- get
- {
- if (compChangeable == null && Gun != null) compChangeable = Gun.TryGetComp();
- return compChangeable;
- }
+ get
+ {
+ if (compChangeable == null && Gun != null) compChangeable = Gun.TryGetComp();
+ return compChangeable;
+ }
}
public CompAmmoUser CompAmmo
{
@@ -132,25 +134,21 @@ public CompFireModes CompFireModes
}
}
- public bool EmptyMagazine => Reloadable && CompAmmo.CurMagCount == 0;
- public bool FullMagazine => !Reloadable || (CompAmmo.SelectedAmmo == CompAmmo.CurrentAmmo && CompAmmo.CurMagCount == CompAmmo.Props.magazineSize);
+ public bool EmptyMagazine => CompAmmo?.EmptyMagazine ?? false;
+ public bool FullMagazine => CompAmmo?.FullMagazine ?? false;
public bool AutoReloadableMagazine => AutoReloadableNow && CompAmmo.CurMagCount <= Mathf.CeilToInt(CompAmmo.Props.magazineSize / 6);
public bool AutoReloadableNow => (mannableComp == null || (!mannableComp.MannedNow && ticksUntilAutoReload == 0)) && Reloadable; //suppress manned turret auto-reload for a short time after spawning
- public bool Reloadable => CompAmmo != null && CompAmmo.HasMagazine;
-
+ public bool Reloadable => CompAmmo?.HasMagazine ?? false;
public CompMannable MannableComp => mannableComp;
#endregion
- #region Constructors
- // Core constructor
public Building_TurretGunCE()
{
top = new TurretTop(this);
}
- #endregion
#region Methods
-
+
public override void SpawnSetup(Map map, bool respawningAfterLoad) //Add mannableComp, ticksUntilAutoReload, register to GenClosestAmmo
{
base.SpawnSetup(map, respawningAfterLoad);
@@ -159,6 +157,7 @@ public override void SpawnSetup(Map map, bool respawningAfterLoad) //Add ma
mannableComp = GetComp();
if (!respawningAfterLoad)
{
+ CELogger.Message($"top is {top?.ToString() ?? "null"}");
top.SetRotationFromOrientation();
burstCooldownTicksLeft = def.building.turretInitialCooldownTime.SecondsToTicks();
@@ -167,20 +166,20 @@ public override void SpawnSetup(Map map, bool respawningAfterLoad) //Add ma
ticksUntilAutoReload = minTicksBeforeAutoReload;
}
- if (CompAmmo == null || CompAmmo.Props == null || CompAmmo.Props.ammoSet == null || CompAmmo.Props.ammoSet.ammoTypes.NullOrEmpty())
- return;
+ // if (CompAmmo == null || CompAmmo.Props == null || CompAmmo.Props.ammoSet == null || CompAmmo.Props.ammoSet.ammoTypes.NullOrEmpty())
+ // return;
- //"Subscribe" turret to GenClosestAmmo
- foreach (var ammo in CompAmmo.Props.ammoSet.ammoTypes.Select(x => x.ammo))
- {
- if (!GenClosestAmmo.listeners.ContainsKey(ammo))
- GenClosestAmmo.listeners.Add(ammo, new List() { this });
- else
- GenClosestAmmo.listeners[ammo].Add(this);
+ // //"Subscribe" turret to GenClosestAmmo
+ // foreach (var ammo in CompAmmo.Props.ammoSet.ammoTypes.Select(x => x.ammo))
+ // {
+ // if (!GenClosestAmmo.listeners.ContainsKey(ammo))
+ // GenClosestAmmo.listeners.Add(ammo, new List() { this });
+ // else
+ // GenClosestAmmo.listeners[ammo].Add(this);
- if (!GenClosestAmmo.latestAmmoUpdate.ContainsKey(ammo))
- GenClosestAmmo.latestAmmoUpdate.Add(ammo, 0);
- }
+ // if (!GenClosestAmmo.latestAmmoUpdate.ContainsKey(ammo))
+ // GenClosestAmmo.latestAmmoUpdate.Add(ammo, 0);
+ // }
}
//PostMake not added -- MakeGun-like code is run whenever Gun is called
@@ -189,15 +188,9 @@ public override void DeSpawn(DestroyMode mode = DestroyMode.Vanish) // Added
{
base.DeSpawn(mode);
ResetCurrentTarget();
-
- foreach (var ammo in CompAmmo.Props.ammoSet.ammoTypes.Select(x => x.ammo))
- {
- if (GenClosestAmmo.listeners.ContainsKey(ammo) && GenClosestAmmo.listeners[ammo].Contains(this))
- GenClosestAmmo.listeners[ammo].Remove(this);
- }
}
-
+
public override void ExposeData() // Added new variables, removed bool loaded (not used in CE)
{
base.ExposeData();
@@ -260,14 +253,14 @@ public override void Tick() //Autoreload code and IsReloading check
base.Tick();
if (ticksUntilAutoReload > 0) ticksUntilAutoReload--; // Reduce time until we can auto-reload
- if (!isReloading && this.IsHashIntervalTick(TicksBetweenAmmoChecks))
+ if (!isReloading && this.IsHashIntervalTick(TicksBetweenAmmoChecks) && (MannableComp?.MannedNow ?? false))
{
TryOrderReload();
}
//This code runs TryOrderReload for manning pawns or for non-humanlike intelligence such as mechs
- /*if (this.IsHashIntervalTick(TicksBetweenAmmoChecks) && !isReloading && (MannableComp?.MannedNow ?? false))
- TryOrderReload(CompAmmo?.CurMagCount == 0);*/
+ /*if (this.IsHashIntervalTick(TicksBetweenAmmoChecks) && !isReloading && (MannableComp?.MannedNow ?? false))
+ TryOrderReload(CompAmmo?.CurMagCount == 0);*/
if (!CanSetForcedTarget && !isReloading && forcedTarget.IsValid && burstCooldownTicksLeft <= 0)
{
ResetForcedTarget();
@@ -313,14 +306,14 @@ public override void Tick() //Autoreload code and IsReloading check
this.ResetCurrentTarget();
}
}
-
+
protected void TryStartShootSomething(bool canBeginBurstImmediately) // Added ammo check and use verb warmup time instead of turret's
{
// Check for ammo first
if (!Spawned
|| (holdFire && CanToggleHoldFire)
|| (Projectile.projectile.flyOverhead && Map.roofGrid.Roofed(Position))
- //|| !AttackVerb.Available() -- Check replaced by the following:
+ //|| !AttackVerb.Available() -- Check replaced by the following:
|| (CompAmmo != null && (isReloading || (mannableComp == null && CompAmmo.CurMagCount <= 0))))
{
ResetCurrentTarget();
@@ -357,7 +350,7 @@ protected void TryStartShootSomething(bool canBeginBurstImmediately) // Added
}
burstWarmupTicksLeft = 1;
}
-
+
protected LocalTargetInfo TryFindNewTarget() // Core method
{
IAttackTargetSearcher attackTargetSearcher = this.TargSearcher();
@@ -420,14 +413,14 @@ private bool IsValidTarget(Thing t) // Projectile flyoverhead check
}
return true;
}
-
+
protected void BeginBurst() // Added handling for ticksUntilAutoReload
{
ticksUntilAutoReload = minTicksBeforeAutoReload;
AttackVerb.TryStartCastOn(CurrentTarget, false, true);
OnAttackedTarget(CurrentTarget);
}
-
+
protected void BurstComplete() // Added CompAmmo reload check
{
burstCooldownTicksLeft = BurstCooldownTime().SecondsToTicks();
@@ -445,7 +438,7 @@ protected float BurstCooldownTime() // Core method
}
return AttackVerb.verbProps.defaultCooldownTime;
}
-
+
public override string GetInspectString() // Replaced vanilla loaded text with CE reloading
{
StringBuilder stringBuilder = new StringBuilder();
@@ -454,7 +447,7 @@ public override string GetInspectString() // Replaced vanilla loaded text
{
stringBuilder.AppendLine(inspectString);
}
-
+
stringBuilder.AppendLine("GunInstalled".Translate() + ": " + this.Gun.LabelCap); // New code
if (this.GunCompEq.PrimaryVerb.verbProps.minRange > 0f)
@@ -658,37 +651,6 @@ private void InitGun()
verb.castCompleteCallback = new Action(this.BurstComplete);
}
}
-
- public Thing nearestViableAmmo;
- public void UpdateNearbyAmmo(bool forceCheck = false)
- {
- if (lastSurroundingAmmoCheck + TicksBetweenAmmoChecks >= Find.TickManager.TicksGame)
- return;
-
- if (!CompAmmo.Props.ammoSet.ammoTypes.Any(x => GenClosestAmmo.latestAmmoUpdate[x.ammo] > lastSurroundingAmmoCheck))
- return;
-
- //If ammo remained viable, don't bother unless forced
- if (nearestViableAmmo != null
- && !forceCheck
- && nearestViableAmmo.Spawned
- && !nearestViableAmmo.IsForbidden(Faction)
- && nearestViableAmmo.ParentHolder == null
- && !Map.physicalInteractionReservationManager.IsReserved(nearestViableAmmo))
- return;
-
- //var prevIsSlow = isSlow;
- var prevViableAmmo = nearestViableAmmo;
- nearestViableAmmo = GenClosestAmmo.ClosestAmmoReachable(Position, Map, CompAmmo,
- TraverseParms.For(TraverseMode.NoPassClosedDoors, Danger.Deadly), PathEndMode.ClosestTouch, GenClosestAmmo.ammoSearchRadius,
- x => !x.IsForbidden(Faction) && !Map.physicalInteractionReservationManager.IsReserved(x));
-
- isSlow = (nearestViableAmmo == null || nearestViableAmmo == prevViableAmmo);
-
- //Log.Message("Updated " + ThingID + " (slow = "+prevIsSlow+" -> "+isSlow+") to " + nearestViableAmmo?.ThingID);
-
- lastSurroundingAmmoCheck = Find.TickManager.TicksGame;
- }
public void TryForceReload()
{
@@ -720,84 +682,29 @@ public void TryOrderReload(bool forced = false)
//Non-mannableComp interaction
if (!mannableComp?.MannedNow ?? true)
{
- //If auto-turret test for "auto-reload"
- if (!forced && ticksUntilAutoReload > 0)
- return;
-
- //Unmanned or autoturret -- cancel job on reserved turret
- if (Map.physicalInteractionReservationManager.IsReserved(new LocalTargetInfo(this)))
- return;
-
- //If manningPawn is null, WorkGiver_ReloadTurret might find a Humanlike intelligence pawn to reload the turret -- however, this method won't pick one at random to do so
- //If manningPawn is null, try to assign jobs to a pawn of faction Mechanoids
- if (Faction.def != FactionDefOf.Mechanoid)
- return;
-
- List pawns;
- if (this.GetLord() != null && this.GetLord().AnyActivePawn) //Prevents mechs in ancient danger from joining in
- {
- pawns = this.GetLord().ownedPawns;
- }
- //else if (Map.mapPawns?.PawnsInFaction(Faction).Any() ?? false)
- //{
- // pawns = Map.mapPawns.PawnsInFaction(Faction);
- //}
- else return; //Nothing could reload this
-
- var giver = new WorkGiver_ReloadTurret();
- var pawn = giver.BestNonJobUser(pawns.Where(x => x.RaceProps.intelligence == Intelligence.ToolUser
- //|| (x.training?.HasLearned(CE_TrainableDefOf.Haul) ?? false) //would allow trained pawns to reload as well
- ), this, out bool fromInventory, forced);
- if (pawn == null) return;
-
- Job reloadJob = null;
- if (!CompAmmo.UseAmmo) reloadJob = new Job(CE_JobDefOf.ReloadTurret, this, null);
- else
- {
- Thing ammo = fromInventory ? InventoryAmmo(pawn.TryGetComp()) : nearestViableAmmo;
- if (ammo != null)
- {
- if (ammo.def != CompAmmo.SelectedAmmo)
- CompAmmo.SelectedAmmo = ammo.def as AmmoDef;
-
- int amount = CompAmmo.Props.magazineSize;
- if (CompAmmo.CurrentAmmo == CompAmmo.SelectedAmmo) amount -= CompAmmo.CurMagCount;
-
- if (fromInventory)
- {
- if (!pawn.TryGetComp().container.TryDrop(ammo, Position, Map, ThingPlaceMode.Direct, Mathf.Min(ammo.stackCount, amount), out ammo))
- {
- Log.ErrorOnce("Found optimal ammo (" + ammo.LabelCap + "), but could not drop from " + pawn.LabelCap, 81274728);
- return;
- }
- }
+ return;
+ }
- reloadJob = new Job(CE_JobDefOf.ReloadTurret, this, ammo) { count = Mathf.Min(ammo.stackCount, amount) };
- }
- }
+ //Only have manningPawn reload after a long time of no firing
+ if (!forced && Reloadable && (compAmmo.CurMagCount != 0 || ticksUntilAutoReload > 0))
+ return;
- if (reloadJob != null)
- pawn.jobs.StartJob(reloadJob, JobCondition.Ongoing, null, pawn.CurJob?.def != reloadJob.def && (!pawn.CurJob?.def.isIdle ?? true));
- }
- else //MannableComp interaction
+ //Already reserved for manning
+ Pawn manningPawn = mannableComp.ManningPawn;
+ if (manningPawn != null)
{
- //Only have manningPawn reload after a long time of no firing
- if (!forced && Reloadable && (compAmmo.CurMagCount != 0 || ticksUntilAutoReload > 0))
+ if (!JobGiverUtils_Reload.CanReload(manningPawn, this))
+ {
return;
+ }
+ var jobOnThing = JobGiverUtils_Reload.MakeReloadJob(manningPawn, this);
- //Already reserved for manning
- Pawn manningPawn = mannableComp.ManningPawn;
- if (manningPawn != null)
+ if (jobOnThing != null)
{
- UpdateNearbyAmmo(forced);
- var jobOnThing = new WorkGiver_ReloadTurret().JobOnThing(manningPawn, this, forced);
-
- if (jobOnThing != null)
- manningPawn.jobs.StartJob(jobOnThing, JobCondition.Ongoing, null, manningPawn.CurJob?.def != CE_JobDefOf.ReloadTurret);
-
- return;
+ manningPawn.jobs.StartJob(jobOnThing, JobCondition.Ongoing, null, manningPawn.CurJob?.def != CE_JobDefOf.ReloadTurret);
}
}
+
}
#endregion
}