diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f33fec247f..676750e721 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @CombatExtendedRWMod/playtesters @CombatExtendedRWMod/coders +* @CombatExtended-Continued/playtesters @CombatExtended-Continued/coders diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 0000000000..c777090029 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -0,0 +1,40 @@ +--- +name: Bug Report +about: Report gameplay issues and errors to help us improve. +title: "[Bug]:" +labels: bug +assignees: '' + +--- + +**Complete the template below. Reports that do not follow the template will not be deleted.** +**Title should be:** [Bug]: (Short description of bug) + +**Specifications** +Please provide the following. + +Rimworld version: +Combat Extended version: +Combat Extended source (Steam, GitHub, etc.): +Your operating system: +Your mod list: + +**Description** +Provide a concise description of the issue or concern. What happened? + +**Expected behavior** +What did you expect to happen? + +**To reproduce** +Provide clear steps to reproduce the behavior. How do we make this happen? + +**Screenshots & log dumps** +Whenever possible, add screenshots to help explain your problem, and share error or crash logs to make our jobs easier. For logs, please enter the logs into a pastebin site (e.g pastebin.com, justpasteit.com, etc.) +and provide a link. Do not paste the entire log here. + +**Complete the following checklist** +I hereby verify that I have done the following: +- [ ] Confirmed that my game version and load order are correct. +- [ ] Confirmed that I am running the appropriate and most updated version of Combat Extended and required compatibility patches. +- [ ] Confirmed I am not running any mods with known incompatibilities with Combat Extended. +- [ ] Disabled Combat Extended and attempted to reproduce the behavior without success. diff --git a/.github/ISSUE_TEMPLATE/discussion-template.md b/.github/ISSUE_TEMPLATE/discussion-template.md new file mode 100644 index 0000000000..2ee58a2b06 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/discussion-template.md @@ -0,0 +1,11 @@ +--- +name: Discussion Template +about: For general discussions among contributors on matters relating to the mod. + Not for off-topic chatter. Label as appropriate. +title: "[Discussion]:" +labels: discussion +assignees: '' + +--- + +**Note: These are to be used for discussions related to Combat Extended, primarily by team members and other contributors. Not for feedback, requests, etc. Off-topic chatter will be deleted.** diff --git a/.github/ISSUE_TEMPLATE/patch-request.md b/.github/ISSUE_TEMPLATE/patch-request.md new file mode 100644 index 0000000000..b06339d324 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/patch-request.md @@ -0,0 +1,40 @@ +--- +name: Patch Request +about: 'Request a new or updated Combat Extended compatibility patch for a mod. ' +title: "[Patch Request]:" +labels: patch request +assignees: '' + +--- + +**Read and complete the template below. Requests that fail to follow the template will be deleted without warning.** + +**If a Patch Request (open or closed) already exists for the same mod, you may add a reaction to indicate your support. Do not create another request. Duplicate requests will be deleted.** + +**Title should be:** [Patch Request]: (Mod Name) + +**Mod name:** + +**Link to mod download (preferably Steam workshop or Github):** + +**Description:** +Provide a brief overview of what content the mod adds or modifies. Specify what content in particular you believe require a patch to be compatibly with Combat Extended. + +**Content:** +What does the new mod contain? Check all that apply. +- [ ] New weapons. +- [ ] New animals, humanoids, or other creatures. +- [ ] New factions. +- [ ] New armor or apparel. + + +**Complete the following checklist before submitting a request.** +I hereby verify that I have done all of the following: +- [ ] Searched the Steam workshop, Discord, and Github for an existing patch and not found one. +- [ ] Confirmed there are no other open Patch Requests for this mod. +- [ ] Confirmed the requested mod is not known to be fundamentally incompatible with Combat Extended. +- [ ] **Have completed this template accurately and completely.** +- [ ] **Understand that putting in a Patch Request does not guarantee a patch will be made.** +- [ ] **Agree that I will not directly contact contributors to ask about patch requests.** + +**Putting in request does not guarantee a patch will be made. Do not directly contact team members about Patch Requests. For smaller mods, consider reviewing the guide and making a patch yourself. If you wish to create a patch for any requested mod, there is no need to ask for permission.** diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index b5f9b6cb1f..54591ca016 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,18 +1,18 @@ ## Additions -Describe the functionality added by your code, e.g. +Describe new functionality added by your code, e.g. - Tribal smoke bombs - New tribal smoke bomb sprite - Tribal smoke bomb recipes at smithing bench and crafting spot using prometheum ## Changes -Adjustments to other features made in this merge, e.g. +Describe adjustments to existing features made in this merge, e.g. - Increased regular smoke bomb radius ## References -Links to the associated issues, e.g. +Links to the associated issues or other related pull requests, e.g. - Closes #[ISSUE_NUMBER] - Contributes towards #[ISSUE_NUMBER] diff --git a/About/About.xml b/About/About.xml index 2669b71fe8..353cff5167 100644 --- a/About/About.xml +++ b/About/About.xml @@ -2,9 +2,8 @@ Combat Extended CE Team - https://ludeon.com/forums/index.php?topic=33461.0 + https://github.com/CombatExtended-Continued/CombatExtended -
  • 1.0
  • 1.1
  • CETeam.CombatExtended diff --git a/Assemblies/CombatExtended.dll b/Assemblies/CombatExtended.dll index 83ae3f6147..662c0fd261 100644 Binary files a/Assemblies/CombatExtended.dll and b/Assemblies/CombatExtended.dll differ diff --git a/Defs/Ammo/Rocket/M74.xml b/Defs/Ammo/Rocket/M74.xml index 2044aefd40..b044100aaa 100644 --- a/Defs/Ammo/Rocket/M74.xml +++ b/Defs/Ammo/Rocket/M74.xml @@ -88,7 +88,7 @@ Bomb 1 - 30 + 30 MortarBomb_Explode 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/PawnKindDefs_Humanlike/PawnKinds.xml b/Patches/Core/PawnKindDefs_Humanlike/PawnKinds.xml index 71cdca4bc8..0196937d17 100644 --- a/Patches/Core/PawnKindDefs_Humanlike/PawnKinds.xml +++ b/Patches/Core/PawnKindDefs_Humanlike/PawnKinds.xml @@ -142,7 +142,7 @@ - Defs/PawnKindDef[defName="Mercenary_Gunner"] + Defs/PawnKindDef[defName="Mercenary_Gunner" or defName="Mercenary_Gunner_Acidifier"]
  • @@ -174,7 +174,7 @@ - Defs/PawnKindDef[defName="Mercenary_Sniper"] + Defs/PawnKindDef[defName="Mercenary_Sniper" or defName="Mercenary_Sniper_Acidifier"] 415~550 @@ -252,7 +252,7 @@ - Defs/PawnKindDef[defName = "Mercenary_Slasher"] + Defs/PawnKindDef[defName = "Mercenary_Slasher" or defName="Mercenary_Slasher_Acidifier"]
  • @@ -286,7 +286,7 @@ - Defs/PawnKindDef[@Name = "MercenaryEliteTierBase"] + Defs/PawnKindDef[@Name = "MercenaryEliteTierBase" or defName="Mercenary_Elite_Acidifier"]
  • diff --git a/Patches/Core/ThingDefs_Misc/Weapons_Guns.xml b/Patches/Core/ThingDefs_Misc/Weapons_Guns.xml index 59fecac86c..37ef7b830c 100644 --- a/Patches/Core/ThingDefs_Misc/Weapons_Guns.xml +++ b/Patches/Core/ThingDefs_Misc/Weapons_Guns.xml @@ -842,8 +842,8 @@
  • Blunt
  • 10 - 1.9 - 0.118 + 2.44 + 3.5 Barrels @@ -943,7 +943,7 @@
  • 1 - 400 + 400
  • @@ -1047,7 +1047,7 @@
  • 1 - 150 + 150
  • @@ -1158,8 +1158,8 @@
  • Blunt
  • 10 - 1.9 - 0.118 + 2.44 + 3.5 Barrel @@ -1225,8 +1225,8 @@
  • Blunt
  • 10 - 1.9 - 0.118 + 2.44 + 3.5 Barrel @@ -1289,8 +1289,8 @@
  • Blunt
  • 10 - 1.9 - 0.118 + 2.44 + 3.5 Barrel 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 + + + +
  • + Defs/ResearchProjectDef[defName="RecoilOperation"] +
  • + + + +
    + \ No newline at end of file diff --git a/Patches/DefensiveMachineGunTurretPack/ThingDefs/DMGT_Defensive_Turrets.xml b/Patches/DefensiveMachineGunTurretPack/DMGT_CE_Patch_ThingDefs_Defensive_Turrets.xml similarity index 98% rename from Patches/DefensiveMachineGunTurretPack/ThingDefs/DMGT_Defensive_Turrets.xml rename to Patches/DefensiveMachineGunTurretPack/DMGT_CE_Patch_ThingDefs_Defensive_Turrets.xml index 818ceb7046..c3200ac46a 100644 --- a/Patches/DefensiveMachineGunTurretPack/ThingDefs/DMGT_Defensive_Turrets.xml +++ b/Patches/DefensiveMachineGunTurretPack/DMGT_CE_Patch_ThingDefs_Defensive_Turrets.xml @@ -1,11 +1,11 @@ - - Always - -
  • - Defensive Machine Gun Turret Pack -
  • + + +
  • Defensive Machine Gun Turret Pack
  • +
    + + @@ -360,7 +360,7 @@ - +
    +
    -
    \ No newline at end of file diff --git a/Patches/DefensiveMachineGunTurretPack/DMGT_CE_Patch_ThingDefs_Sentry_Turrets.xml b/Patches/DefensiveMachineGunTurretPack/DMGT_CE_Patch_ThingDefs_Sentry_Turrets.xml new file mode 100644 index 0000000000..6267469e81 --- /dev/null +++ b/Patches/DefensiveMachineGunTurretPack/DMGT_CE_Patch_ThingDefs_Sentry_Turrets.xml @@ -0,0 +1,237 @@ + + + + +
  • Defensive Machine Gun Turret Pack
  • +
    + + + + + +
  • + Gun_M2HBs + + 0.36 + 1.00 + 0.01 + 1.65 + 18.54 + + + 1.19 + CombatExtended.Verb_ShootCE + true + Bullet_50BMG_FMJ + 1.3 + 86 + 6 + 10 + ShotM2 + GunTail_Heavy + 13 + Mounted + + + 100 + 7.8 + AmmoSet_50BMG + + + FALSE + AimedShot + 5 + true + true + + +
  • TurretGun
  • + + + +
  • + Defs/ThingDef[defName="Turret_M2HBs"]/specialDisplayRadius + + 86 + +
  • + + + +
  • + Gun_M134MGs + + 0.35 + 1.00 + 0.06 + 1.31 + 8.02 + + + 0.59 + CombatExtended.Verb_ShootCE + true + Bullet_762x51mmNATO_FMJ + 2.3 + 86 + 1 + 100 + Shot_Minigun + GunTail_Heavy + 10 + Mounted + + + 500 + 9.2 + AmmoSet_762x51mmNATO + + + FALSE + AimedShot + 50 + true + true + + +
  • TurretGun
  • + + + +
  • + Defs/ThingDef[defName="Turret_M134MGs"]/specialDisplayRadius + + 86 + +
  • + + + +
  • + Gun_Mk19MGLs + + 0.36 + 1.00 + 0.09 + 1.55 + 12.90 + + + 1.58 + CombatExtended.Verb_ShootCE + true + Bullet_40x53mmGrenade_HE + 1.3 + 78 + 6 + 9 + 6 + InfernoCannon_Fire + GunTail_Heavy + 16 + Mounted + + + 48 + 7.8 + AmmoSet_40x53mmGrenade + + + FALSE + AimedShot + 3 + true + true + + +
  • TurretGun
  • + + + +
  • + Defs/ThingDef[defName="Turret_Mk19MGLs"]/specialDisplayRadius + + 78 + +
  • + + + +
  • + Defs/ThingDef[ + defName="Turret_M2HBs" + ]/statBases/WorkToBuild + + 69500 + +
  • + +
  • + Defs/ThingDef[ + defName="Turret_M134MGs" + ]/statBases/WorkToBuild + + 62500 + +
  • + +
  • + Defs/ThingDef[ + defName="Turret_Mk19MGLs" + ]/statBases/WorkToBuild + + 66500 + +
  • + + + +
  • + Defs/ThingDef[ + defName="Turret_M2HBs" or + defName="Turret_M134MGs" or + defName="Turret_Mk19MGLs" + ]/thingClass + + CombatExtended.Building_TurretGunCE + +
  • + +
  • + Defs/ThingDef[ + defName="Turret_M2HBs" or + defName="Turret_M134MGs" or + defName="Turret_Mk19MGLs" + ]/researchPrerequisites + + +
  • CE_TurretHeavyWeapons
  • +
  • CE_HeavyTurret
  • + + + + +
  • + Defs/ThingDef[ + defName="Turret_M2HBs" or + defName="Turret_M134MGs" or + defName="Turret_Mk19MGLs" + ]/comps/li[@Class = "CompProperties_Refuelable"] +
  • + +
  • + Defs/ThingDef[ + defName="Turret_M2HBs" or + defName="Turret_M134MGs" or + defName="Turret_Mk19MGLs" + ]/statBases + + 1.0 + 1.0 + +
  • + +
    +
    +
    +
    \ No newline at end of file diff --git a/Patches/DefensiveMachineGunTurretPack/ThingDefs/DMGT_Sentry_Turrets.xml b/Patches/DefensiveMachineGunTurretPack/ThingDefs/DMGT_Sentry_Turrets.xml deleted file mode 100644 index 65ba6195df..0000000000 --- a/Patches/DefensiveMachineGunTurretPack/ThingDefs/DMGT_Sentry_Turrets.xml +++ /dev/null @@ -1,237 +0,0 @@ - - - - Always - -
  • - Defensive Machine Gun Turret Pack -
  • - - - -
  • - Gun_M2HBs - - 0.36 - 1.00 - 0.01 - 1.65 - 18.54 - - - 1.19 - CombatExtended.Verb_ShootCE - true - Bullet_50BMG_FMJ - 1.3 - 86 - 6 - 10 - ShotM2 - GunTail_Heavy - 13 - Mounted - - - 100 - 7.8 - AmmoSet_50BMG - - - FALSE - AimedShot - 5 - true - true - - -
  • TurretGun
  • - - - -
  • - Defs/ThingDef[defName="Turret_M2HBs"]/specialDisplayRadius - - 86 - -
  • - - - -
  • - Gun_M134MGs - - 0.35 - 1.00 - 0.06 - 1.31 - 8.02 - - - 0.59 - CombatExtended.Verb_ShootCE - true - Bullet_762x51mmNATO_FMJ - 2.3 - 86 - 1 - 100 - Shot_Minigun - GunTail_Heavy - 10 - Mounted - - - 500 - 9.2 - AmmoSet_762x51mmNATO - - - FALSE - AimedShot - 50 - true - true - - -
  • TurretGun
  • - - - -
  • - Defs/ThingDef[defName="Turret_M134MGs"]/specialDisplayRadius - - 86 - -
  • - - - -
  • - Gun_Mk19MGLs - - 0.36 - 1.00 - 0.09 - 1.55 - 12.90 - - - 1.58 - CombatExtended.Verb_ShootCE - true - Bullet_40x53mmGrenade_HE - 1.3 - 78 - 6 - 9 - 6 - InfernoCannon_Fire - GunTail_Heavy - 16 - Mounted - - - 48 - 7.8 - AmmoSet_40x53mmGrenade - - - FALSE - AimedShot - 3 - true - true - - -
  • TurretGun
  • - - - -
  • - Defs/ThingDef[defName="Turret_Mk19MGLs"]/specialDisplayRadius - - 78 - -
  • - - - -
  • - Defs/ThingDef[ - defName="Turret_M2HBs" - ]/statBases/WorkToBuild - - 69500 - -
  • - -
  • - Defs/ThingDef[ - defName="Turret_M134MGs" - ]/statBases/WorkToBuild - - 62500 - -
  • - -
  • - Defs/ThingDef[ - defName="Turret_Mk19MGLs" - ]/statBases/WorkToBuild - - 66500 - -
  • - - - -
  • - Defs/ThingDef[ - defName="Turret_M2HBs" or - defName="Turret_M134MGs" or - defName="Turret_Mk19MGLs" - ]/thingClass - - CombatExtended.Building_TurretGunCE - -
  • - -
  • - Defs/ThingDef[ - defName="Turret_M2HBs" or - defName="Turret_M134MGs" or - defName="Turret_Mk19MGLs" - ]/researchPrerequisites - - -
  • CE_TurretHeavyWeapons
  • -
  • CE_HeavyTurret
  • - - - - -
  • - Defs/ThingDef[ - defName="Turret_M2HBs" or - defName="Turret_M134MGs" or - defName="Turret_Mk19MGLs" - ]/comps/li[@Class = "CompProperties_Refuelable"] -
  • - -
  • - Defs/ThingDef[ - defName="Turret_M2HBs" or - defName="Turret_M134MGs" or - defName="Turret_Mk19MGLs" - ]/statBases - - 1.0 - 1.0 - -
  • - -
    -
    - -
    \ No newline at end of file diff --git a/Patches/Red Horse Faction Elite Crew/RH_EliteCrew_CE_Patch_RangedIndustrial_Grenades.xml b/Patches/Red Horse Faction Elite Crew/RH_EliteCrew_CE_Patch_RangedIndustrial_Grenades.xml index 25bbe22ddf..dbebb0c229 100644 --- a/Patches/Red Horse Faction Elite Crew/RH_EliteCrew_CE_Patch_RangedIndustrial_Grenades.xml +++ b/Patches/Red Horse Faction Elite Crew/RH_EliteCrew_CE_Patch_RangedIndustrial_Grenades.xml @@ -216,7 +216,7 @@
  • - 345 + 345
  • diff --git a/Patches/Rimmu-Nation - Weapons/RNW_CE_Patch_RangedIndustrial_Grenades.xml b/Patches/Rimmu-Nation - Weapons/RNW_CE_Patch_RangedIndustrial_Grenades.xml index 1ca3771126..8c8df9d01c 100644 --- a/Patches/Rimmu-Nation - Weapons/RNW_CE_Patch_RangedIndustrial_Grenades.xml +++ b/Patches/Rimmu-Nation - Weapons/RNW_CE_Patch_RangedIndustrial_Grenades.xml @@ -96,7 +96,7 @@
  • - 345 + 345
  • diff --git a/Patches/Vanilla Weapons Expanded/Ammo/AntiGrainRocket.xml b/Patches/Vanilla Weapons Expanded/Ammo/AntiGrainRocket.xml index cffa599714..8a5fdd4312 100644 --- a/Patches/Vanilla Weapons Expanded/Ammo/AntiGrainRocket.xml +++ b/Patches/Vanilla Weapons Expanded/Ammo/AntiGrainRocket.xml @@ -29,7 +29,7 @@ MortarBomb_Explode true - 400 + 400 diff --git a/Royalty/Defs/ThingDefs_Buildings/Buildings_Mech_Spawners.xml b/Royalty/Defs/ThingDefs_Buildings/Buildings_Mech_Spawners.xml index 6610e67cf1..6d220dcee1 100644 --- a/Royalty/Defs/ThingDefs_Buildings/Buildings_Mech_Spawners.xml +++ b/Royalty/Defs/ThingDefs_Buildings/Buildings_Mech_Spawners.xml @@ -14,13 +14,13 @@ 100 0 - +
  • MechClusterMember
  • - Things/Building/Mech/MechDropBeacon + 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 } diff --git a/Source/CombatExtended/Harmony/Harmony-GlobalControls.cs b/Source/CombatExtended/Harmony/Harmony-GlobalControls.cs index 783dcfe3ad..0e25810606 100644 --- a/Source/CombatExtended/Harmony/Harmony-GlobalControls.cs +++ b/Source/CombatExtended/Harmony/Harmony-GlobalControls.cs @@ -8,37 +8,15 @@ namespace CombatExtended.HarmonyCE { - [HarmonyPatch(typeof(GlobalControls), "GlobalControlsOnGUI")] + [HarmonyPatch(typeof(GlobalControlsUtility), nameof(GlobalControlsUtility.DoDate))] internal static class Harmony_GlobalControls { - internal static IEnumerable Transpiler(IEnumerable instructions) - { - var track = false; - var write = false; - var getComponentMethodInfo = AccessTools.Method(typeof(Map), nameof(Map.GetComponent), new Type[] { }).MakeGenericMethod(typeof(WeatherTracker)); - foreach (var code in instructions) - { - if (write) - { - yield return new CodeInstruction(OpCodes.Callvirt, AccessTools.Property(typeof(Find), nameof(Find.CurrentMap)).GetGetMethod()); - yield return new CodeInstruction(OpCodes.Call, getComponentMethodInfo); - yield return new CodeInstruction(OpCodes.Ldloc_0); - yield return new CodeInstruction(OpCodes.Ldloca_S, 1); - yield return new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(WeatherTracker), nameof(WeatherTracker.DoWindGUI))); - write = false; - } - else if (track) - { - write = code.opcode == OpCodes.Stloc_1; - track = !write; - } - else - { - track = code.operand is MethodInfo info && info == AccessTools.Method(typeof(WeatherManager), nameof(WeatherManager.DoWeatherGUI)); - } + private const float magicExtraOffset = 8f; - yield return code; - } + private static void Postfix(ref float curBaseY) + { + float offsetXFromOriginalMethod = UI.screenWidth - 200f; + Find.CurrentMap.GetComponent().DoWindGUI(offsetXFromOriginalMethod + magicExtraOffset, ref curBaseY); } } } \ No newline at end of file