diff --git a/About/About.xml b/About/About.xml
index a89aa7b6..f0c68399 100644
--- a/About/About.xml
+++ b/About/About.xml
@@ -5,7 +5,7 @@
1.0
- Version 0.9.11
+ Version 0.10.1
This mod force prisoners to work. To enable this feature prisoners must have "Force to work" option checked ("Prisoner" tab). Prison labor needs management that consist:
- Motivation - prisoners need to be motivated by presence of colonists. Wardens have new job - supervising prisoners. Low motivation can lead to revolts.
@@ -16,3 +16,4 @@ This mod force prisoners to work. To enable this feature prisoners must have "Fo
This is beta version. Some ascpects of mod still need to be balanced (like motivation). There can be still some bugs.
+
diff --git a/Assemblies/PrisonLabor.dll b/Assemblies/PrisonLabor.dll
index b5380c85..ec9ef347 100644
Binary files a/Assemblies/PrisonLabor.dll and b/Assemblies/PrisonLabor.dll differ
diff --git a/Defs/ConceptDef.xml b/Defs/ConceptDef.xml
index ed6bcf75..91060a87 100644
--- a/Defs/ConceptDef.xml
+++ b/Defs/ConceptDef.xml
@@ -38,4 +38,11 @@
True
You can make time restrictions for prisoners.\n\n"Work" time will force them to work even when they're hungry or tired.\n\n"Joy" time will let them rest from job and get motivation bonus.\n\n"Sleep" time will force them to stay in prison cell.\n\n"Anything" is default setting.
+
+ PrisonLabor_Treatment
+ Treatment system
+ 50
+ True
+ In prison labor you need to take care for prisoners. Good treatment can prevent revolts, and can provoke recruit proposition (without recruting process!).\n\nTo maintain good treatment status you need to feed prisoners regularly and set some free time so they can regenerate their strength\n\nTreatment will go lower if you beat prisoners, starve them, or exploit in terms of labor.\nGood treatment will result in:\n - Random offers with request to join colony\n - Preventing revolts\n - Preventing suicide
+
diff --git a/Defs/Hediffs.xml b/Defs/Hediffs.xml
index ca0032cb..791281fa 100644
--- a/Defs/Hediffs.xml
+++ b/Defs/Hediffs.xml
@@ -25,4 +25,4 @@
-
+
\ No newline at end of file
diff --git a/Defs/Incidents.xml b/Defs/Incidents.xml
index c2281ab4..30ee950f 100644
--- a/Defs/Incidents.xml
+++ b/Defs/Incidents.xml
@@ -5,14 +5,43 @@
Map_PlayerHome
- PrisonLabor.IncidentWorker_Revolt
+ PrisonLabor.Core.Incidents.IncidentWorker_Revolt
Revolt
Revolt has been started by {0}. The prisoners united under faction {1}, and began uprising with self-made weapons
ThreatBig
- 2.7
+ 5.4
20
ThreatBig
true
200
+
+
+ PrisonLabor_ResocializationOffer
+ resocialization offer
+ Misc
+
+ Map_PlayerHome
+
+ PrisonLabor.Core.Incidents.IncidentWorker_ResocializationOffer
+ Resocialization offer
+ Offer by {0}.
+ PositiveEvent
+ 10
+ IncreaseEasy
+
+
+
+ PrisonLabor_Suicide
+ prisoner suicide
+ Misc
+
+ Map_PlayerHome
+
+ PrisonLabor.Core.Incidents.IncidentWorker_Suicide
+ Prisoner suicide
+ {0} has commited suicide, because of bad treatment.
+ NegativeEvent
+ 3
+
diff --git a/Defs/JobDef.xml b/Defs/JobDef.xml
index b84ad042..3627197a 100644
--- a/Defs/JobDef.xml
+++ b/Defs/JobDef.xml
@@ -2,28 +2,31 @@
PrisonLabor_PrisonerSupervise
- PrisonLabor.JobDriver_Supervise
+ PrisonLabor.Core.AI.JobDrivers.JobDriver_Supervise
watching prisoner TargetA.
true
- PrisonLabor_DeliverFood_Tweak
- PrisonLabor.JobDriver_FoodDeliver_Tweak
- feeding TargetA to TargetB.
+ PrisonLabor_Arrest
+ JobDriver_TakeToBed
+ arresting TargetA.
+ true
+ true
+ false
PrisonLabor_Mine_Tweak
- PrisonLabor.JobDriver_Mine_Tweak
+ PrisonLabor.Tweaks.JobDriver_Mine_Tweak
digging at TargetA.
PrisonLabor_Harvest_Tweak
- PrisonLabor.JobDriver_PlantHarvest_Tweak
+ PrisonLabor.Tweaks.JobDriver_PlantHarvest_Tweak
harvesting TargetA.
PrisonLabor_CutPlant_Tweak
- PrisonLabor.JobDriver_PlantCut_Tweak
+ PrisonLabor.Tweaks.JobDriver_PlantCut_Tweak
cutting TargetA.
diff --git a/Defs/Needs.xml b/Defs/Needs.xml
index 5e0db2db..e8ab7b5c 100644
--- a/Defs/Needs.xml
+++ b/Defs/Needs.xml
@@ -2,11 +2,21 @@
PrisonLabor_Motivation
- PrisonLabor.Need_Motivation
+ PrisonLabor.Core.Needs.Need_Motivation
Motivation
Motivation represents how motivated to work is prisoner. Motivation can be improved by colonists standing nearby.
90
false
false
+
+ PrisonLabor_Treatment
+ PrisonLabor.Core.Needs.Need_Treatment
+ Treatment happiness
+ Treatment happiness represents how prisoners are content of treatment in colony.
+ 89
+ false
+ false
+ false
+
diff --git a/Defs/ThinkTreeDef.xml b/Defs/ThinkTreeDef.xml
index 37e0fa6d..18571ade 100644
--- a/Defs/ThinkTreeDef.xml
+++ b/Defs/ThinkTreeDef.xml
@@ -4,14 +4,20 @@
PrisonLabor_WorkThinkTree
Humanlike_PostDuty
80
-
+
false
+
+
+
+
+
-
-
-
+
+
+
+
diff --git a/Defs/ThoughtsDef.xml b/Defs/ThoughtsDef.xml
new file mode 100644
index 00000000..4fb5052d
--- /dev/null
+++ b/Defs/ThoughtsDef.xml
@@ -0,0 +1,37 @@
+
+
+
+
+ PrisonLabor_VeryGoodTreatment
+ PrisonLabor.Core.AI.ThoughtWorkers.ThoughtWorker_VeryGoodTreatment
+
+
+ treated very good
+ In this prison I've been treated very well.
+ 15
+
+
+
+
+ PrisonLabor_LowMotivation
+ PrisonLabor.Core.AI.ThoughtWorkers.ThoughtWorker_LowMotivation
+
+
+ not supervised
+ Nobody cares if I'm working or not.
+ 5
+
+
+
+
+ PrisonLabor_FreeTime
+ PrisonLabor.Core.AI.ThoughtWorkers.ThoughtWorker_FreeTime
+
+
+ free time
+ Even tough I'm prisoner, I can still have some free time.
+ 5
+
+
+
+
diff --git a/Defs/WorkGiverDef.xml b/Defs/WorkGiverDef.xml
index 33d08d24..aa7f87aa 100644
--- a/Defs/WorkGiverDef.xml
+++ b/Defs/WorkGiverDef.xml
@@ -3,7 +3,7 @@
PrisonLabor_SupervisePrisonLabor
watch prisoner
- PrisonLabor.WorkGiver_Supervise
+ PrisonLabor.Core.AI.WorkGivers.WorkGiver_Supervise
PrisonLabor_Jailor
5
watch prisoner
diff --git a/Images/Discord.png b/Images/Discord.png
new file mode 100644
index 00000000..314babf0
Binary files /dev/null and b/Images/Discord.png differ
diff --git a/Images/Discord.xcf b/Images/Discord.xcf
new file mode 100644
index 00000000..0f88c6d6
Binary files /dev/null and b/Images/Discord.xcf differ
diff --git a/Images/FreezingIcon.xcf b/Images/FreezingIcon.xcf
new file mode 100644
index 00000000..28177293
Binary files /dev/null and b/Images/FreezingIcon.xcf differ
diff --git a/Images/InspireIcon.xcf b/Images/InspireIcon.xcf
index bde0d8d1..42de3e7c 100644
Binary files a/Images/InspireIcon.xcf and b/Images/InspireIcon.xcf differ
diff --git a/Images/LazyIcon.xcf b/Images/LazyIcon.xcf
new file mode 100644
index 00000000..0b91a961
Binary files /dev/null and b/Images/LazyIcon.xcf differ
diff --git a/Images/deleteLabor.png b/Images/deleteLabor.png
deleted file mode 100644
index 2ddf9e3b..00000000
Binary files a/Images/deleteLabor.png and /dev/null differ
diff --git a/Images/extendLabor.png b/Images/extendLabor.png
deleted file mode 100644
index e3cd185e..00000000
Binary files a/Images/extendLabor.png and /dev/null differ
diff --git a/Languages/ChineseSimplified/Keyed/Keys.xml b/Languages/ChineseSimplified/Keyed/Keys.xml
index 27a7500b..d85aef5d 100644
--- a/Languages/ChineseSimplified/Keyed/Keys.xml
+++ b/Languages/ChineseSimplified/Keyed/Keys.xml
@@ -49,4 +49,6 @@
When enabled revolts will sometimes occur instead of vanilla incidents
Labor area isn't required to make prisoners work.\n\nThis area forbids colonists from working.\n\nIt's for situations where you don't want colonists to work in certain area. Prisoners will work anywhere they can enter.
Don't show again
+
+Upgrading [PrisonLabor] mod
diff --git a/Languages/ChineseTraditional/Keyed/Keys.xml b/Languages/ChineseTraditional/Keyed/Keys.xml
index 644a5571..b4fa319a 100644
--- a/Languages/ChineseTraditional/Keyed/Keys.xml
+++ b/Languages/ChineseTraditional/Keyed/Keys.xml
@@ -52,5 +52,5 @@
所有人
殖民者
囚犯
-
-
+Upgrading [PrisonLabor] mod
+
\ No newline at end of file
diff --git a/Languages/Dutch/Keyed/Keys.xml b/Languages/Dutch/Keyed/Keys.xml
index 83938b3a..efa35396 100644
--- a/Languages/Dutch/Keyed/Keys.xml
+++ b/Languages/Dutch/Keyed/Keys.xml
@@ -49,4 +49,6 @@
When enabled revolts will sometimes occur instead of vanilla incidents
Labor area isn't required to make prisoners work.\n\nThis area forbids colonists from working.\n\nIt's for situations where you don't want colonists to work in certain area. Prisoners will work anywhere they can enter.
Don't show again
+
+Upgrading [PrisonLabor] mod
diff --git a/Languages/English/Keyed/Keys.xml b/Languages/English/Keyed/Keys.xml
index 5814442b..21849222 100644
--- a/Languages/English/Keyed/Keys.xml
+++ b/Languages/English/Keyed/Keys.xml
@@ -13,17 +13,13 @@
allow all
allow all work types
allowed work types:
- browse
+ Browse
Motivation mechanics (!)
When checked prisoners need to be motivated.\n\nWARINING: Needs reloading save.
Inspiration/Motivation Icons
When enabled icons will be displayed above prisoners heads. Blue icon for inspiration, and green icon for gaining motivation by other factors.
Prisoners can grow advanced plants
When disabled prisoners can only grow plants that not require any skills.
- Restart then re-save your game.
- After this steps you can safely disable this mod.
- Disable mod
- When enabled, worlds that are saved are transferred to 'safe Mode', and can be played without mod.
Version:
Difficulty:
Defaults
@@ -42,15 +38,40 @@
Labor area is area where only prisoners can work. No colonist's work allowed here except warden type jobs.
Clear Labor Area
Expand Labor Area
- Colonists only
- Prisoners only
- Colony only
+
Enable revolts
When enabled revolts will sometimes occur instead of vanilla incidents
Labor area isn't required to make prisoners work.\n\nThis area forbids colonists from working.\n\nIt's for situations where you don't want colonists to work in certain area. Prisoners will work anywhere they can enter.
Don't show again
+ Show treatment happiness
+ When enabled treatment happiness will be shown in Needs tab.
Everyone
Colonists
Prisoners
+
+ For everyone
+ Limit to colonists
+ Limit to prisoners
+
+
+ Recruit
+ Ready to join colony
+
+ Prisoners can escape
+ Prisoners can reach end of map and escape. They will run unless colonist will keep an eye on them.
+ Those prisoners can escape:\n\n{0}\nThey will run unless colonist will keep an eye on them.
+
+ Select Save
+ Remove Prison Labor mod from save
+
+ Backup
+ Proceed
+
+ Upgrading [PrisonLabor] mod
+ You are going to remove Prison Labor mod from save. After this operation you will be able to play without the mod.
+ Please backup your file with button below.
+
+ Tutorials
+ Show
diff --git a/Languages/French/Keyed/Keys.xml b/Languages/French/Keyed/Keys.xml
index bff7b1cc..c426fd2e 100644
--- a/Languages/French/Keyed/Keys.xml
+++ b/Languages/French/Keyed/Keys.xml
@@ -49,4 +49,6 @@
When enabled revolts will sometimes occur instead of vanilla incidents
Labor area isn't required to make prisoners work.\n\nThis area forbids colonists from working.\n\nIt's for situations where you don't want colonists to work in certain area. Prisoners will work anywhere they can enter.
Don't show again
+
+Upgrading [PrisonLabor] mod
\ No newline at end of file
diff --git a/Languages/Polish/Keyed/Keys.xml b/Languages/Polish/Keyed/Keys.xml
index 464b5b87..85267cb9 100644
--- a/Languages/Polish/Keyed/Keys.xml
+++ b/Languages/Polish/Keyed/Keys.xml
@@ -1,4 +1,4 @@
-
+
Zmuś do pracy
Praca i próba rekrutacji
@@ -49,4 +49,6 @@
When enabled revolts will sometimes occur instead of vanilla incidents
Labor area isn't required to make prisoners work.\n\nThis area forbids colonists from working.\n\nIt's for situations where you don't want colonists to work in certain area. Prisoners will work anywhere they can enter.
Don't show again
+
+ Aktualizacja modyfikacji [PrisonLabor]
\ No newline at end of file
diff --git a/Languages/Russian/Keyed/Keys.xml b/Languages/Russian/Keyed/Keys.xml
index fb4926f4..d42025da 100644
--- a/Languages/Russian/Keyed/Keys.xml
+++ b/Languages/Russian/Keyed/Keys.xml
@@ -49,4 +49,5 @@
Если включить то бунт может случатся в качестве случайного события.
Зона работ не обязательна для того чтоб заключенные работали. \n\n Зона работ запрещает поселенцам работу в указанных местах.\n\n Она создана для тех случаев, когда вы не хотите чтобы поселенцы работали в указанном месте. Заключенные же будут работать там, куда у них есть доступ.
Больше не показывать
+Upgrading [PrisonLabor] mod
\ No newline at end of file
diff --git a/Languages/Spanish/Keyed/Keys.xml b/Languages/Spanish/Keyed/Keys.xml
index 41e340cb..abc5ec09 100644
--- a/Languages/Spanish/Keyed/Keys.xml
+++ b/Languages/Spanish/Keyed/Keys.xml
@@ -49,4 +49,6 @@
When enabled revolts will sometimes occur instead of vanilla incidents
Labor area isn't required to make prisoners work.\n\nThis area forbids colonists from working.\n\nIt's for situations where you don't want colonists to work in certain area. Prisoners will work anywhere they can enter.
Don't show again
+
+Upgrading [PrisonLabor] mod
diff --git a/Languages/Swedish/Keyed/Keys.xml b/Languages/Swedish/Keyed/Keys.xml
index 2710d648..1e5bfca0 100644
--- a/Languages/Swedish/Keyed/Keys.xml
+++ b/Languages/Swedish/Keyed/Keys.xml
@@ -49,4 +49,6 @@
When enabled revolts will sometimes occur instead of vanilla incidents
Labor area isn't required to make prisoners work.\n\nThis area forbids colonists from working.\n\nIt's for situations where you don't want colonists to work in certain area. Prisoners will work anywhere they can enter.
Don't show again
+
+Upgrading [PrisonLabor] mod
\ No newline at end of file
diff --git a/README.md b/README.md
index b72947f8..30414ba6 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
-
+
diff --git a/Source/Behaviour_MotivationIcon.cs b/Source/Behaviour_MotivationIcon.cs
deleted file mode 100644
index 820c533c..00000000
--- a/Source/Behaviour_MotivationIcon.cs
+++ /dev/null
@@ -1,106 +0,0 @@
-using RimWorld.Planet;
-using System;
-using UnityEngine;
-using Verse;
-
-namespace PrisonLabor
-{
- [StaticConstructorOnStartup]
- internal class Behaviour_MotivationIcon : MonoBehaviour
- {
- // TODO delete later
- private static bool displayedError = false;
-
- private static readonly Texture2D inspiredTexture;
- private static readonly Texture2D motivatedTexture;
- private static readonly Vector3 iconPos;
-
- private float worldScale;
-
- static Behaviour_MotivationIcon()
- {
- inspiredTexture = ContentFinder.Get("InspireIcon", false);
- motivatedTexture = ContentFinder.Get("MotivateIcon", false);
- iconPos = new Vector3(0f, 0f, 1.3f);
- }
-
- private void DrawIcon(Texture2D texture, Vector3 pawnPos)
- {
- //TODO add iconSizeMult to prefs ?
- var iconSizeMult = 1.0f;
- //TODO add iconSize to prefs ?
- var iconSize = 2.0f;
-
- if (texture == null)
- {
- Log.Message("texture cant be found");
- return;
- }
-
- var scrPosVec = (pawnPos + iconPos).MapToUIPosition();
- var scrSize = worldScale * iconSizeMult * iconSize * 0.5f;
- var scrPos = new Rect(scrPosVec.x - scrSize * 0.5f, scrPosVec.y - scrSize * 0.5f, scrSize, scrSize);
- GUI.DrawTexture(scrPos, texture, ScaleMode.ScaleToFit, true);
- }
-
- public virtual void OnGUI()
- {
- try
- {
- var iconsEnabled = PrisonLaborPrefs.EnableMotivationIcons && !PrisonLaborPrefs.DisableMod;
- var inGame = Find.CurrentMap != null && Find.CurrentMap.mapPawns != null && !WorldRendererUtility.WorldRenderedNow;
-
- if (iconsEnabled && inGame)
- foreach (var pawn in Find.CurrentMap.mapPawns.AllPawns)
- {
- if (pawn == null) continue;
- if (pawn.RaceProps == null) continue;
-
- if (pawn.IsPrisonerOfColony)
- {
- var need = pawn.needs.TryGetNeed();
- if (need != null && need.Motivated)
- if (need.Insipred)
- DrawIcon(inspiredTexture, pawn.DrawPos);
- else
- DrawIcon(motivatedTexture, pawn.DrawPos);
- }
- }
- }
- catch (NullReferenceException e)
- {
- if (!displayedError)
- {
- Log.ErrorOnce("PrisonLaborError: null reference in OnGui() : " + e.Message + " trace: " + e.StackTrace, typeof(Behaviour_MotivationIcon).GetHashCode());
- displayedError = true;
- }
- }
- }
-
-
- public virtual void Update()
- {
- worldScale = Screen.height / (2 * Camera.current.orthographicSize);
- }
-
- public static void Initialization()
- {
- var iconModule = new GameObject("PrisonLabor_Initializer");
- iconModule.AddComponent();
- DontDestroyOnLoad(iconModule);
- }
- }
-
- internal class IconModuleInitializer : MonoBehaviour
- {
- public void FixedUpdate()
- {
- var iconModule = GameObject.Find("PrisonLabor_IconModule");
- if (iconModule == null)
- {
- iconModule = new GameObject("PrisonLabor_IconModule");
- iconModule.AddComponent();
- }
- }
- }
-}
\ No newline at end of file
diff --git a/Source/BugTracker.cs b/Source/BugTracker.cs
deleted file mode 100644
index 104cc31d..00000000
--- a/Source/BugTracker.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-namespace PrisonLabor
-{
- class BugTracker
- {
-
- }
-}
diff --git a/Source/CompatibilityPatches/OlderVersions.cs b/Source/CompatibilityPatches/OlderVersions.cs
index 258e2560..a917ef46 100644
--- a/Source/CompatibilityPatches/OlderVersions.cs
+++ b/Source/CompatibilityPatches/OlderVersions.cs
@@ -1,4 +1,7 @@
-using RimWorld;
+using PrisonLabor.Constants;
+using PrisonLabor.Core.LaborWorkSettings;
+using PrisonLabor.Core.Meta;
+using RimWorld;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -13,8 +16,8 @@ internal static void Pre_v0_9_4()
{
if (WorkSettings.AllowedWorkTypes.Contains(WorkTypeDefOf.Warden))
WorkSettings.AllowedWorkTypes.Remove(WorkTypeDefOf.Warden);
- if (WorkSettings.AllowedWorkTypes.Contains(PrisonLaborDefOf.PrisonLabor_Jailor))
- WorkSettings.AllowedWorkTypes.Remove(PrisonLaborDefOf.PrisonLabor_Jailor);
+ if (WorkSettings.AllowedWorkTypes.Contains(PL_DefOf.PrisonLabor_Jailor))
+ WorkSettings.AllowedWorkTypes.Remove(PL_DefOf.PrisonLabor_Jailor);
WorkSettings.Apply();
PrisonLaborPrefs.Save();
diff --git a/Source/CompatibilityPatches/SeedsPlease_WorkDriver_Patch.cs b/Source/CompatibilityPatches/SeedsPlease_WorkDriver_Patch.cs
index 86c8f95e..5f56f931 100644
--- a/Source/CompatibilityPatches/SeedsPlease_WorkDriver_Patch.cs
+++ b/Source/CompatibilityPatches/SeedsPlease_WorkDriver_Patch.cs
@@ -82,7 +82,7 @@ public static IEnumerable DelegateTranspiler(ILGenerator gen, M
{
if (ci.opcode == OpCodes.Beq)
{
- yield return new CodeInstruction(OpCodes.Call, typeof(SeedsPlease_WorkDriver_Patch).GetMethod("CorrectCondition"));
+ yield return new CodeInstruction(OpCodes.Call, typeof(SeedsPlease_WorkDriver_Patch).GetMethod(nameof(CorrectCondition)));
yield return new CodeInstruction(OpCodes.Brfalse, ci.operand);
step++;
}
diff --git a/Source/CompatibilityPatches/SeedsPlease_WorkGiver.cs b/Source/CompatibilityPatches/SeedsPlease_WorkGiver.cs
index 26b4ba7a..a8d276bc 100644
--- a/Source/CompatibilityPatches/SeedsPlease_WorkGiver.cs
+++ b/Source/CompatibilityPatches/SeedsPlease_WorkGiver.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using PrisonLabor.Tweaks;
using RimWorld;
using Verse;
using Verse.AI;
diff --git a/Source/CompatibilityPatches/WorkTab.cs b/Source/CompatibilityPatches/WorkTab.cs
index ff0c30d6..7b42da04 100644
--- a/Source/CompatibilityPatches/WorkTab.cs
+++ b/Source/CompatibilityPatches/WorkTab.cs
@@ -7,6 +7,8 @@
using PrisonLabor.Tweaks;
using UnityEngine;
using RimWorld.Planet;
+using PrisonLabor.Core;
+using PrisonLabor.Core.LaborWorkSettings;
namespace PrisonLabor.CompatibilityPatches
{
diff --git a/Source/Constants/BGP.cs b/Source/Constants/BGP.cs
new file mode 100644
index 00000000..08a78b36
--- /dev/null
+++ b/Source/Constants/BGP.cs
@@ -0,0 +1,51 @@
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace PrisonLabor.Constants
+{
+ ///
+ /// ||Balanced Gameplay Parameters||
+ /// Defined constants for balananced gameplay,
+ /// stored in one place for optimized re-balancing
+ ///
+ public static class BGP
+ {
+ #region Insipiration
+ public const float InspireRate = 0.015f;
+ public const int WardenCapacity = (int)(InspireRate / Laziness_LazyRate);
+ public const float InpirationRange = 10.0f;
+ #endregion
+
+ #region Laziness
+ public const float Laziness_LazyRate = 0.002f;
+ public const float Laziness_HungryRate = 0.006f;
+ public const float Laziness_TiredRate = 0.006f;
+ public const float Laziness_HealthRate = 0.006f;
+ public const float Laziness_JoyRate = 0.001f;
+ #endregion
+
+ #region Escape
+ // Escape time = ax + b (x -- treatment level)
+ public const int Escape_MinLevel = 100;
+ public const int Escape_MaxLevel = 5000;
+ public const float Escape_LevelTreatmentMultiplier = 7000;
+ public const int Escape_LevelBase = -950;
+ #endregion
+
+ #region Treatment
+ public const float ResocializationLevel = 0.1f;
+
+ // 10% every 12 days
+ public const float LaborRate = 1f / (120f * GenDate.TicksPerDay / 150f);
+ // 1% every 12 days for every point of status
+ public const float StatusMultiplier = 1f / (1200f * GenDate.TicksPerDay / 150f);
+ // 10% every 6 days
+ public const float JoyRate = 1f / (60f * GenDate.TicksPerDay / 150f);
+
+ public const float BeatenHit = -0.1f;
+ #endregion
+ }
+}
diff --git a/Source/PrisonLaborDefOf.cs b/Source/Constants/PL_DefOf.cs
similarity index 64%
rename from Source/PrisonLaborDefOf.cs
rename to Source/Constants/PL_DefOf.cs
index ffeb5866..f6a9fed4 100644
--- a/Source/PrisonLaborDefOf.cs
+++ b/Source/Constants/PL_DefOf.cs
@@ -1,18 +1,21 @@
-using RimWorld;
+using RimWorld;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Verse;
-namespace PrisonLabor
+namespace PrisonLabor.Constants
{
[DefOf]
- public static class PrisonLaborDefOf
+ public static class PL_DefOf
{
public static PrisonerInteractionModeDef PrisonLabor_workOption;
public static PrisonerInteractionModeDef PrisonLabor_workAndRecruitOption;
public static WorkTypeDef PrisonLabor_Jailor;
+
+ public static NeedDef PrisonLabor_Motivation;
+ public static NeedDef PrisonLabor_Treatment;
}
}
diff --git a/Source/JobDriver_Supervise.cs b/Source/Core/AI/JobDrivers/JobDriver_Supervise.cs
similarity index 80%
rename from Source/JobDriver_Supervise.cs
rename to Source/Core/AI/JobDrivers/JobDriver_Supervise.cs
index c46003bd..877117c1 100644
--- a/Source/JobDriver_Supervise.cs
+++ b/Source/Core/AI/JobDrivers/JobDriver_Supervise.cs
@@ -1,16 +1,19 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Linq;
+using PrisonLabor.Constants;
+using PrisonLabor.Core.Needs;
+using PrisonLabor.Core.Trackers;
using RimWorld;
using Verse;
using Verse.AI;
-namespace PrisonLabor
+namespace PrisonLabor.Core.AI.JobDrivers
{
internal class JobDriver_Supervise : JobDriver
{
private static readonly float GapLengh = 3.0f;
- protected Pawn Prisoner => (Pawn) job.targetA.Thing;
+ protected Pawn Prisoner => (Pawn)job.targetA.Thing;
protected override IEnumerable MakeNewToils()
{
@@ -42,19 +45,19 @@ protected Toil MakeWatchToil(Pawn prisoner)
int score = 0;
int curScore = 0;
bool found = false;
- foreach(var cell in prisoner.GetRoom().Cells)
+ foreach (var cell in prisoner.GetRoom().Cells)
{
float distance = cell.DistanceTo(prisoner.InteractionCell);
- if (distance < Need_Motivation.InpirationRange)
+ if (distance < BGP.InpirationRange)
{
if (distance < GapLengh)
curScore = (int)distance;
else
- curScore = (int)(Need_Motivation.InpirationRange - distance);
+ curScore = (int)(BGP.InpirationRange - distance);
foreach (var pawn in prisonersInRoom)
- if (cell.DistanceTo(pawn.Position) < Need_Motivation.InpirationRange)
- curScore += 100;
+ if (cell.DistanceTo(pawn.Position) < BGP.InpirationRange)
+ curScore += pawn.IsWatched() ? 50 : 100;
if (curScore > score)
{
@@ -75,12 +78,12 @@ protected Toil MakeWatchToil(Pawn prisoner)
private bool RangeCondition(Toil toil)
{
- return toil.actor.Position.DistanceTo(Prisoner.Position) > Need_Motivation.InpirationRange;
+ return toil.actor.Position.DistanceTo(Prisoner.Position) > BGP.InpirationRange;
}
private IEnumerable PrisonersInRoom(Room room)
{
- foreach(var pawn in room.Map.mapPawns.PrisonersOfColony)
+ foreach (var pawn in room.Map.mapPawns.PrisonersOfColony.Where(p => p.LaborEnabled() && p.needs?.TryGetNeed() != null))
{
if (pawn.GetRoom() == room)
yield return pawn;
diff --git a/Source/JobGiver_BedTime.cs b/Source/Core/AI/JobGivers/JobGiver_BedTime.cs
similarity index 90%
rename from Source/JobGiver_BedTime.cs
rename to Source/Core/AI/JobGivers/JobGiver_BedTime.cs
index 39b1224f..30977965 100644
--- a/Source/JobGiver_BedTime.cs
+++ b/Source/Core/AI/JobGivers/JobGiver_BedTime.cs
@@ -1,9 +1,10 @@
-using RimWorld;
+using PrisonLabor.Core.Needs;
+using RimWorld;
using Verse;
using Verse.AI;
using Verse.AI.Group;
-namespace PrisonLabor
+namespace PrisonLabor.Core.AI.JobGivers
{
internal class JobGiver_BedTime : ThinkNode_JobGiver
{
@@ -18,6 +19,8 @@ public override ThinkNode DeepCopy(bool resolve = true)
public override float GetPriority(Pawn pawn)
{
+ if (HealthAIUtility.ShouldHaveSurgeryDoneNow(pawn))
+ return 15f;
if (pawn.timetable != null && pawn.timetable.CurrentAssignment == TimeAssignmentDefOf.Sleep)
return 10f;
return 0f;
@@ -31,7 +34,7 @@ protected override Job TryGiveJob(Pawn pawn)
return null;
var need = pawn.needs.TryGetNeed();
if (need != null)
- need.Enabled = false;
+ need.IsPrisonerWorking = false;
var lord = pawn.GetLord();
Building_Bed building_Bed;
if (lord != null && lord.CurLordToil != null && !lord.CurLordToil.AllowRestingInBed)
diff --git a/Source/Core/AI/JobGivers/JobGiver_Diet.cs b/Source/Core/AI/JobGivers/JobGiver_Diet.cs
new file mode 100644
index 00000000..74411c86
--- /dev/null
+++ b/Source/Core/AI/JobGivers/JobGiver_Diet.cs
@@ -0,0 +1,49 @@
+using PrisonLabor.Core.Needs;
+using RimWorld;
+using Verse;
+using Verse.AI;
+
+namespace PrisonLabor.Core.AI.JobGivers
+{
+ internal class JobGiver_Diet : JobGiver_GetFood
+ {
+ private HungerCategory minCategory = HungerCategory.Hungry;
+ private readonly HungerCategory stopWorkingCat = HungerCategory.UrgentlyHungry;
+
+ public override ThinkNode DeepCopy(bool resolve = true)
+ {
+ var jobGiver_Diet = (JobGiver_Diet)base.DeepCopy(resolve);
+ jobGiver_Diet.minCategory = minCategory;
+ return jobGiver_Diet;
+ }
+
+ public override float GetPriority(Pawn pawn)
+ {
+ var food = pawn.needs.food;
+ if (food == null)
+ return 0f;
+ if (pawn.needs.food.CurCategory < HungerCategory.Starving && FoodUtility.ShouldBeFedBySomeone(pawn))
+ return 0f;
+ if (food.CurCategory < minCategory)
+ return 0f;
+ if (food.CurCategory <= stopWorkingCat)
+ return 11f;
+ if (food.CurLevelPercentage < pawn.RaceProps.FoodLevelPercentageWantEat)
+ return 7f;
+ return 0f;
+ }
+
+ protected override Job TryGiveJob(Pawn pawn)
+ {
+ var food = pawn.needs.food;
+ if (food == null || food.CurCategory < minCategory)
+ return null;
+
+ var need = pawn.needs.TryGetNeed();
+ if (need != null)
+ need.IsPrisonerWorking = false;
+
+ return base.TryGiveJob(pawn);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/JobGiver_Labor.cs b/Source/Core/AI/JobGivers/JobGiver_Labor.cs
similarity index 95%
rename from Source/JobGiver_Labor.cs
rename to Source/Core/AI/JobGivers/JobGiver_Labor.cs
index fa0038de..1a2f9c38 100644
--- a/Source/JobGiver_Labor.cs
+++ b/Source/Core/AI/JobGivers/JobGiver_Labor.cs
@@ -1,14 +1,20 @@
-using System;
+using System;
+using PrisonLabor.Core.LaborWorkSettings;
+using PrisonLabor.Core.Meta;
+using PrisonLabor.Core.Needs;
+using PrisonLabor.Core.Other;
using RimWorld;
using Verse;
using Verse.AI;
-namespace PrisonLabor
+namespace PrisonLabor.Core.AI.JobGivers
{
public class JobGiver_Labor : ThinkNode
{
public bool emergency;
+ public object Tutorials { get; private set; }
+
public override ThinkNode DeepCopy(bool resolve = true)
{
var jobGiver_Work = (JobGiver_Labor)base.DeepCopy(resolve);
@@ -34,9 +40,9 @@ public override ThinkResult TryIssueJobPackage(Pawn pawn, JobIssueParams jobPara
//Check medical assistance, fed, and rest if not override
if (!PrisonLaborUtility.WorkTime(pawn))
{
- Tutorials.Timetable();
+ Other.Tutorials.Timetable();
if (need != null)
- need.Enabled = false;
+ need.IsPrisonerWorking = false;
return ThinkResult.NoJob;
}
//Check motivation
@@ -48,7 +54,7 @@ public override ThinkResult TryIssueJobPackage(Pawn pawn, JobIssueParams jobPara
//TODO check this
//workList.RemoveAll(workGiver => workGiver.def.defName == "GrowerSow");
if (need != null)
- need.Enabled = false;
+ need.IsPrisonerWorking = false;
var num = -999;
var targetInfo = TargetInfo.Invalid;
@@ -66,7 +72,7 @@ public override ThinkResult TryIssueJobPackage(Pawn pawn, JobIssueParams jobPara
if (job2 != null)
{
if (need != null)
- need.Enabled = true;
+ need.IsPrisonerWorking = true;
return new ThinkResult(job2, this, workList[j].def.tagToGive);
}
var scanner = workGiver as WorkGiver_Scanner;
@@ -162,7 +168,7 @@ public override ThinkResult TryIssueJobPackage(Pawn pawn, JobIssueParams jobPara
if (job3 != null)
{
if (need != null)
- need.Enabled = true;
+ need.IsPrisonerWorking = true;
return new ThinkResult(job3, this, workList[j].def.tagToGive);
}
Log.ErrorOnce(
diff --git a/Source/Core/AI/JobGivers/JobGiver_PickupWeapon.cs b/Source/Core/AI/JobGivers/JobGiver_PickupWeapon.cs
new file mode 100644
index 00000000..6f5e4be1
--- /dev/null
+++ b/Source/Core/AI/JobGivers/JobGiver_PickupWeapon.cs
@@ -0,0 +1,127 @@
+using PrisonLabor.Core.Needs;
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Verse;
+using Verse.AI;
+
+namespace PrisonLabor.Core.AI.JobGivers
+{
+ public class JobGiver_PickupWeapon : ThinkNode_JobGiver
+ {
+ private static TreatmentCategory maxCategory = TreatmentCategory.Bad;
+
+ private bool preferBuildingDestroyers;
+
+ public override float GetPriority(Pawn pawn) => 12f;
+
+ protected override Job TryGiveJob(Pawn pawn)
+ {
+ if (pawn.equipment == null)
+ {
+ return null;
+ }
+ if (AlreadySatisfiedWithCurrentWeapon(pawn))
+ {
+ return null;
+ }
+ var treatmentNeed = pawn.needs?.TryGetNeed();
+ if(treatmentNeed == null || treatmentNeed.CurCategory > maxCategory)
+ {
+ return null;
+ }
+ if (pawn.RaceProps.Humanlike && pawn.story.WorkTagIsDisabled(WorkTags.Violent))
+ {
+ return null;
+ }
+ if (!pawn.health.capacities.CapableOf(PawnCapacityDefOf.Manipulation))
+ {
+ return null;
+ }
+ if (pawn.GetRegion(RegionType.Set_Passable) == null)
+ {
+ return null;
+ }
+ Thing thing = GenClosest.ClosestThingReachable(pawn.Position, pawn.Map, ThingRequest.ForGroup(ThingRequestGroup.Weapon), PathEndMode.OnCell, TraverseParms.For(pawn, Danger.Deadly, TraverseMode.ByPawn, false), 8f, (Thing x) => pawn.CanReserve(x, 1, -1, null, false) && ShouldEquip(x, pawn), null, 0, 15, false, RegionType.Set_Passable, false);
+ if (thing != null)
+ {
+ return new Job(JobDefOf.Equip, thing);
+ }
+ return null;
+ }
+
+ public override ThinkNode DeepCopy(bool resolve = true)
+ {
+ JobGiver_PickupWeapon jobGiver = (JobGiver_PickupWeapon)base.DeepCopy(resolve);
+ jobGiver.preferBuildingDestroyers = preferBuildingDestroyers;
+ return jobGiver;
+ }
+
+ private bool ShouldEquip(Thing newWep, Pawn pawn)
+ {
+ return GetWeaponScore(newWep) > GetWeaponScore(pawn.equipment.Primary);
+ }
+
+ private int GetWeaponScore(Thing wep)
+ {
+ if (wep == null)
+ {
+ return 0;
+ }
+ if (wep.def.IsMeleeWeapon && wep.GetStatValue(StatDefOf.MeleeWeapon_AverageDPS, true) < MinMeleeWeaponDPSThreshold)
+ {
+ return 0;
+ }
+ if (preferBuildingDestroyers && wep.TryGetComp().PrimaryVerb.verbProps.ai_IsBuildingDestroyer)
+ {
+ return 3;
+ }
+ if (wep.def.IsRangedWeapon)
+ {
+ return 2;
+ }
+ return 1;
+ }
+
+ private bool AlreadySatisfiedWithCurrentWeapon(Pawn pawn)
+ {
+ ThingWithComps primary = pawn.equipment.Primary;
+ if (primary == null)
+ {
+ return false;
+ }
+ if (preferBuildingDestroyers)
+ {
+ if (!pawn.equipment.PrimaryEq.PrimaryVerb.verbProps.ai_IsBuildingDestroyer)
+ {
+ return false;
+ }
+ }
+ else if (!primary.def.IsRangedWeapon)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ private float MinMeleeWeaponDPSThreshold
+ {
+ get
+ {
+ List tools = ThingDefOf.Human.tools;
+ float num = 0f;
+ for (int i = 0; i < tools.Count; i++)
+ {
+ if (tools[i].linkedBodyPartsGroup == BodyPartGroupDefOf.LeftHand || tools[i].linkedBodyPartsGroup == BodyPartGroupDefOf.RightHand)
+ {
+ num = tools[i].power / tools[i].cooldownTime;
+ break;
+ }
+ }
+ return num + 2f;
+ }
+ }
+ }
+}
diff --git a/Source/ThinkNode_Labor.cs b/Source/Core/AI/ThinkNodes/ThinkNode_Labor.cs
similarity index 69%
rename from Source/ThinkNode_Labor.cs
rename to Source/Core/AI/ThinkNodes/ThinkNode_Labor.cs
index 336a1062..cada9731 100644
--- a/Source/ThinkNode_Labor.cs
+++ b/Source/Core/AI/ThinkNodes/ThinkNode_Labor.cs
@@ -1,8 +1,11 @@
-using RimWorld;
+using PrisonLabor.Core.Needs;
+using PrisonLabor.Core.Other;
+using PrisonLabor.Core.Trackers;
+using RimWorld;
using Verse;
using Verse.AI;
-namespace PrisonLabor
+namespace PrisonLabor.Core.AI.ThinkNodes
{
internal class ThinkNode_Labor : ThinkNode_Conditional
{
@@ -29,16 +32,17 @@ protected override bool Satisfied(Pawn pawn)
// Prisoner will escape if get ready to run.
// If he can run he will start ticking impatient, once complete he will get ready.
- if (!pawn.guest.PrisonerIsSecure ||
- RCellFinder.TryFindBestExitSpot(pawn, out c, TraverseMode.ByPawn))
+ var escapeTracker = EscapeTracker.Of(pawn, true);
+ if (pawn.guest.PrisonerIsSecure && RCellFinder.TryFindBestExitSpot(pawn, out c, TraverseMode.ByPawn))
{
- need.CanEscape = true;
- if (need.ReadyToRun)
+ if (escapeTracker.ReadyToEscape)
return false;
+ else
+ escapeTracker.CanEscape = true;
}
else
{
- need.CanEscape = false;
+ escapeTracker.CanEscape = false;
}
@@ -47,7 +51,7 @@ protected override bool Satisfied(Pawn pawn)
return true;
}
- need.Enabled = false;
+ need.IsPrisonerWorking = false;
}
return false;
}
diff --git a/Source/Core/AI/ThinkNodes/ThinkNode_SeekSafeTemperature.cs b/Source/Core/AI/ThinkNodes/ThinkNode_SeekSafeTemperature.cs
new file mode 100644
index 00000000..97c76a3c
--- /dev/null
+++ b/Source/Core/AI/ThinkNodes/ThinkNode_SeekSafeTemperature.cs
@@ -0,0 +1,21 @@
+using PrisonLabor.Core.Trackers;
+using RimWorld;
+using Verse;
+using Verse.AI;
+
+namespace PrisonLabor.Core.AI.ThinkNodes
+{
+ internal class ThinkNode_SeekSafeTemperature : ThinkNode_Conditional
+ {
+ protected override bool Satisfied(Pawn pawn)
+ {
+ if (pawn.IsPrisoner)
+ {
+ if (pawn.IsWatched() && PrisonLaborUtility.WorkTime(pawn))
+ return false;
+ return true;
+ }
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/Core/AI/ThoughtWorkers/ThoughtWorker_FreeTime.cs b/Source/Core/AI/ThoughtWorkers/ThoughtWorker_FreeTime.cs
new file mode 100644
index 00000000..a867d701
--- /dev/null
+++ b/Source/Core/AI/ThoughtWorkers/ThoughtWorker_FreeTime.cs
@@ -0,0 +1,19 @@
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Verse;
+
+namespace PrisonLabor.Core.AI.ThoughtWorkers
+{
+ public class ThoughtWorker_FreeTime : ThoughtWorker
+ {
+ protected override ThoughtState CurrentStateInternal(Pawn p)
+ {
+ if (!p.IsPrisoner)
+ return false;
+ return p.timetable != null && p.timetable.CurrentAssignment == TimeAssignmentDefOf.Joy;
+ }
+ }
+}
diff --git a/Source/Core/AI/ThoughtWorkers/ThoughtWorker_LowMotivation.cs b/Source/Core/AI/ThoughtWorkers/ThoughtWorker_LowMotivation.cs
new file mode 100644
index 00000000..5486083e
--- /dev/null
+++ b/Source/Core/AI/ThoughtWorkers/ThoughtWorker_LowMotivation.cs
@@ -0,0 +1,21 @@
+using PrisonLabor.Core.Needs;
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Verse;
+
+namespace PrisonLabor.Core.AI.ThoughtWorkers
+{
+ public class ThoughtWorker_LowMotivation : ThoughtWorker
+ {
+ protected override ThoughtState CurrentStateInternal(Pawn p)
+ {
+ if (!p.IsPrisoner)
+ return false;
+ var need = p.needs.TryGetNeed();
+ return need != null && need.IsLazy;
+ }
+ }
+}
diff --git a/Source/Core/AI/ThoughtWorkers/ThoughtWorker_VeryGoodTreatment.cs b/Source/Core/AI/ThoughtWorkers/ThoughtWorker_VeryGoodTreatment.cs
new file mode 100644
index 00000000..0bc43a26
--- /dev/null
+++ b/Source/Core/AI/ThoughtWorkers/ThoughtWorker_VeryGoodTreatment.cs
@@ -0,0 +1,21 @@
+using PrisonLabor.Core.Needs;
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Verse;
+
+namespace PrisonLabor.Core.AI.ThoughtWorkers
+{
+ public class ThoughtWorker_VeryGoodTreatment : ThoughtWorker
+ {
+ protected override ThoughtState CurrentStateInternal(Pawn p)
+ {
+ if (!p.IsPrisoner)
+ return false;
+ var need = p.needs.TryGetNeed();
+ return need != null && need.CurCategory == TreatmentCategory.VeryGood;
+ }
+ }
+}
diff --git a/Source/WorkGiver_Supervise.cs b/Source/Core/AI/WorkGivers/WorkGiver_Supervise.cs
similarity index 70%
rename from Source/WorkGiver_Supervise.cs
rename to Source/Core/AI/WorkGivers/WorkGiver_Supervise.cs
index 9c145838..a8520c1f 100644
--- a/Source/WorkGiver_Supervise.cs
+++ b/Source/Core/AI/WorkGivers/WorkGiver_Supervise.cs
@@ -1,16 +1,17 @@
-using RimWorld;
+using PrisonLabor.Core.Needs;
+using PrisonLabor.Core.Trackers;
+using RimWorld;
using Verse;
using Verse.AI;
-namespace PrisonLabor
+namespace PrisonLabor.Core.AI.WorkGivers
{
internal class WorkGiver_Supervise : WorkGiver_Warden
{
public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false)
{
var prisoner = t as Pawn;
- var need = prisoner.needs.TryGetNeed();
-
+ var need = prisoner?.needs.TryGetNeed();
if (need == null || prisoner == null)
return null;
if (!ShouldTakeCareOfPrisoner(pawn, prisoner))
@@ -19,11 +20,12 @@ public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false)
return null;
if (pawn.IsPrisoner)
return null;
- if (!PrisonLaborUtility.LaborEnabled(prisoner) && !need.CanEscape)
+ var escapeTracker = EscapeTracker.Of(prisoner, true);
+ if (!PrisonLaborUtility.LaborEnabled(prisoner) && !escapeTracker.CanEscape)
return null;
if (PrisonLaborUtility.RecruitInLaborEnabled(prisoner))
return new Job(JobDefOf.PrisonerAttemptRecruit, t);
- if ((!PrisonLaborUtility.WorkTime(prisoner) || !need.NeedToBeInspired) && !need.CanEscape)
+ if ((!PrisonLaborUtility.WorkTime(prisoner) || !need.ShouldToBeMotivated) && !escapeTracker.CanEscape)
return null;
return new Job(DefDatabase.GetNamed("PrisonLabor_PrisonerSupervise"), prisoner);
diff --git a/Source/Core/Alerts/Alert_EscapingPrisoners.cs b/Source/Core/Alerts/Alert_EscapingPrisoners.cs
new file mode 100644
index 00000000..84394d61
--- /dev/null
+++ b/Source/Core/Alerts/Alert_EscapingPrisoners.cs
@@ -0,0 +1,50 @@
+using PrisonLabor.Core.Meta;
+using PrisonLabor.Core.Other;
+using PrisonLabor.Core.Trackers;
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using UnityEngine;
+using Verse;
+
+namespace PrisonLabor.Core.Alerts
+{
+ public class Alert_EscapingPrisoners : Alert_Critical
+ {
+ public Alert_EscapingPrisoners()
+ {
+ defaultLabel = "PrisonLabor_Alert_EscapingPrisoners_Title".Translate();
+ defaultExplanation = "PrisonLabor_Alert_EscapingPrisoners_DefaultExplanation".Translate();
+ }
+
+ private IEnumerable PotentialEscapingPrisoners
+ {
+ get
+ {
+ var maps = Find.Maps;
+ for (var i = 0; i < maps.Count; i++)
+ foreach (var pawn in maps[i].mapPawns.AllPawns.Where(p=>p.IsPrisoner && EscapeTracker.Of(p, true).CanEscape))
+ yield return pawn;
+ }
+ }
+
+ public override string GetExplanation()
+ {
+ Tutorials.Motivation();
+
+ var stringBuilder = new StringBuilder();
+ foreach (var current in PotentialEscapingPrisoners)
+ stringBuilder.AppendLine(" " + current.Name.ToStringShort);
+ return string.Format("PrisonLabor_Alert_EscapingPrisoners_ExplanationFormat".Translate(), stringBuilder.ToString());
+ }
+
+ public override AlertReport GetReport()
+ {
+ if (PrisonLaborPrefs.EnableMotivationMechanics)
+ return AlertReport.CulpritIs(PotentialEscapingPrisoners.FirstOrDefault());
+ return false;
+ }
+ }
+}
diff --git a/Source/Alert_LazyPrisoners.cs b/Source/Core/Alerts/Alert_LazyPrisoners.cs
similarity index 88%
rename from Source/Alert_LazyPrisoners.cs
rename to Source/Core/Alerts/Alert_LazyPrisoners.cs
index b7762fb1..67634481 100644
--- a/Source/Alert_LazyPrisoners.cs
+++ b/Source/Core/Alerts/Alert_LazyPrisoners.cs
@@ -1,10 +1,13 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Linq;
using System.Text;
+using PrisonLabor.Core.Meta;
+using PrisonLabor.Core.Needs;
+using PrisonLabor.Core.Other;
using RimWorld;
using Verse;
-namespace PrisonLabor
+namespace PrisonLabor.Core.Alerts
{
internal class Alert_LazyPrisoners : Alert
{
@@ -29,6 +32,8 @@ private IEnumerable LazyPrisoners
public override string GetExplanation()
{
+ Tutorials.Motivation();
+
var stringBuilder = new StringBuilder();
foreach (var current in LazyPrisoners)
stringBuilder.AppendLine(" " + current.Name.ToStringShort);
diff --git a/Source/Alert_StarvingPrisoners.cs b/Source/Core/Alerts/Alert_StarvingPrisoners.cs
similarity index 87%
rename from Source/Alert_StarvingPrisoners.cs
rename to Source/Core/Alerts/Alert_StarvingPrisoners.cs
index 24b108b4..4c408527 100644
--- a/Source/Alert_StarvingPrisoners.cs
+++ b/Source/Core/Alerts/Alert_StarvingPrisoners.cs
@@ -1,10 +1,12 @@
-using System.Collections.Generic;
+using PrisonLabor.Core.Meta;
+using PrisonLabor.Core.Needs;
+using RimWorld;
+using System.Collections.Generic;
using System.Linq;
using System.Text;
-using RimWorld;
using Verse;
-namespace PrisonLabor
+namespace PrisonLabor.Core.Alerts
{
internal class Alert_StarvingPrisoners : Alert
{
@@ -40,9 +42,7 @@ public override string GetExplanation()
public override AlertReport GetReport()
{
- if (!PrisonLaborPrefs.DisableMod)
- return AlertReport.CulpritIs(StarvingPrisoners.FirstOrDefault());
- return false;
+ return AlertReport.CulpritIs(StarvingPrisoners.FirstOrDefault());
}
}
}
\ No newline at end of file
diff --git a/Source/Core/BaseClasses/SimpleTimer.cs b/Source/Core/BaseClasses/SimpleTimer.cs
new file mode 100644
index 00000000..dadb8fb2
--- /dev/null
+++ b/Source/Core/BaseClasses/SimpleTimer.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Verse;
+
+namespace PrisonLabor.Core.BaseClasses
+{
+ public class SimpleTimer : IExposable
+ {
+ private bool _IsActive;
+ public bool IsActive
+ {
+ get => _IsActive;
+ set => _IsActive = value;
+ }
+
+ private int _Ticks;
+ public int Ticks
+ {
+ get => _Ticks;
+ set => _Ticks = value;
+ }
+
+ public void Start() => IsActive = true;
+
+ public void Stop() => IsActive = false;
+
+ public void Reset() => Ticks = 0;
+
+ public void Tick()
+ {
+ if (_IsActive)
+ {
+ Ticks++;
+ }
+ }
+
+ public void ResetAndStop()
+ {
+ IsActive = false;
+ Ticks = 0;
+ }
+
+ public void ExposeData()
+ {
+ Scribe_Values.Look(ref _IsActive, nameof(IsActive));
+ Scribe_Values.Look(ref _Ticks, nameof(Ticks));
+ }
+ }
+}
diff --git a/Source/BillUtility.cs b/Source/Core/BillAssignation/BillAssignationUtility.cs
similarity index 87%
rename from Source/BillUtility.cs
rename to Source/Core/BillAssignation/BillAssignationUtility.cs
index 06129088..36dcf153 100644
--- a/Source/BillUtility.cs
+++ b/Source/Core/BillAssignation/BillAssignationUtility.cs
@@ -1,9 +1,9 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using RimWorld;
-namespace PrisonLabor
+namespace PrisonLabor.Core.BillAssignation
{
- internal class BillUtility
+ internal class BillAssignationUtility
{
private static readonly Dictionary Map = new Dictionary();
diff --git a/Source/BillGroupData.cs b/Source/Core/BillAssignation/BillGroupData.cs
similarity index 88%
rename from Source/BillGroupData.cs
rename to Source/Core/BillAssignation/BillGroupData.cs
index 8b99daa2..0dce3ae3 100644
--- a/Source/BillGroupData.cs
+++ b/Source/Core/BillAssignation/BillGroupData.cs
@@ -1,6 +1,6 @@
-using Verse;
+using Verse;
-namespace PrisonLabor
+namespace PrisonLabor.Core.BillAssignation
{
public enum GroupMode
{
diff --git a/Source/Core/GUI_Components/PawnIcons.cs b/Source/Core/GUI_Components/PawnIcons.cs
new file mode 100644
index 00000000..57a37a2f
--- /dev/null
+++ b/Source/Core/GUI_Components/PawnIcons.cs
@@ -0,0 +1,93 @@
+using PrisonLabor.Core.Meta;
+using PrisonLabor.Core.Needs;
+using PrisonLabor.Core.Trackers;
+using System;
+using UnityEngine;
+using Verse;
+
+namespace PrisonLabor.Core.GUI_Components
+{
+ [StaticConstructorOnStartup]
+ public class PawnIcons : MapComponent
+ {
+ private static readonly Texture2D watchedTexture;
+ private static readonly Texture2D lazyTexture;
+ private static readonly Texture2D freezingTexture;
+ private static readonly Vector3 iconPos;
+
+ private static float worldScale;
+
+ static PawnIcons()
+ {
+ watchedTexture = ContentFinder.Get("InspireIcon", false);
+ lazyTexture = ContentFinder.Get("LazyIcon", false);
+ freezingTexture = ContentFinder.Get("FreezingIcon", false);
+ iconPos = new Vector3(0.3f, 0f, 0.9f);
+ }
+
+ public PawnIcons(Map map) : base(map) { }
+
+ private static void DrawIcon(Texture2D texture, Vector3 pawnPos)
+ {
+ //TODO add iconSizeMult to prefs ?
+ var iconSizeMult = 1.0f;
+ //TODO add iconSize to prefs ?
+ var iconSize = 2.0f;
+
+ if (texture == null)
+ {
+ Log.Message("texture cant be found");
+ return;
+ }
+
+ var scrPosVec = (pawnPos + iconPos).MapToUIPosition();
+ var scrSize = worldScale * iconSizeMult * iconSize * 0.5f;
+ var scrPos = new Rect(scrPosVec.x - scrSize * 0.5f, scrPosVec.y - scrSize * 0.5f, scrSize, scrSize);
+ GUI.DrawTexture(scrPos, texture, ScaleMode.ScaleToFit, true);
+ }
+
+ public override void MapComponentOnGUI()
+ {
+ try
+ {
+ if (!PrisonLaborPrefs.EnableMotivationIcons)
+ return;
+
+ if (map.mapPawns == null)
+ return;
+
+ foreach (var pawn in map.mapPawns.AllPawns)
+ {
+ if (pawn == null) continue;
+ if (pawn.RaceProps == null) continue;
+
+ if (pawn.IsPrisonerOfColony && pawn.CarriedBy == null)
+ {
+ var need = pawn.needs.TryGetNeed();
+ if (pawn.health.hediffSet.HasTemperatureInjury(TemperatureInjuryStage.Serious) && PrisonLaborUtility.WorkTime(pawn))
+ {
+ DrawIcon(freezingTexture, pawn.DrawPos);
+ }
+ else if (pawn.IsWatched())
+ {
+ DrawIcon(watchedTexture, pawn.DrawPos);
+ }
+ else if (need != null && need.IsLazy && PrisonLaborUtility.LaborEnabled(pawn) && PrisonLaborUtility.WorkTime(pawn))
+ {
+ DrawIcon(lazyTexture, pawn.DrawPos);
+ }
+ }
+ }
+ }
+ catch (NullReferenceException e)
+ {
+ Log.ErrorOnce("PrisonLaborError: null reference in OnGui() : " + e.Message + " trace: " + e.StackTrace, typeof(PawnIcons).GetHashCode());
+ }
+ }
+
+ public override void MapComponentTick()
+ {
+ worldScale = Screen.height / (2 * Camera.current.orthographicSize);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/PawnTable_Prisoners.cs b/Source/Core/GUI_Components/PawnTable_Prisoners.cs
similarity index 89%
rename from Source/PawnTable_Prisoners.cs
rename to Source/Core/GUI_Components/PawnTable_Prisoners.cs
index 870f6756..311042da 100644
--- a/Source/PawnTable_Prisoners.cs
+++ b/Source/Core/GUI_Components/PawnTable_Prisoners.cs
@@ -1,11 +1,11 @@
-using RimWorld;
+using RimWorld;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Verse;
-namespace PrisonLabor
+namespace PrisonLabor.Core.GUI_Components
{
public class PawnTable_Prisoners : PawnTable
{
diff --git a/Source/Core/GUI_Components/RichListing.cs b/Source/Core/GUI_Components/RichListing.cs
new file mode 100644
index 00000000..564f293d
--- /dev/null
+++ b/Source/Core/GUI_Components/RichListing.cs
@@ -0,0 +1,232 @@
+using System.Collections.Generic;
+using UnityEngine;
+using Verse;
+
+namespace PrisonLabor.Core.GUI_Components
+{
+ public class RichListing
+ {
+ private Vector2 scrollPosition;
+ private Rect windowRect;
+ private Rect viewRect;
+ private IEnumerable entries;
+
+
+ public float Spacing { get; set; }
+ public float GapHeight { get; set; }
+ public string MarginText { get; set; }
+ public float MarginWidth { get; set; }
+ public GameFont TitleFont { get; set; }
+ public GameFont ItemFont { get; set; }
+
+ public RichListing() // Defaults
+ {
+ TitleFont = GameFont.Medium;
+ ItemFont = GameFont.Small;
+
+ MarginText = " - ";
+ MarginWidth = Text.fontStyles[1].CalcSize(new GUIContent(MarginText)).x;
+
+ GapHeight = 12f;
+ Spacing = 2f;
+ }
+
+ public void PreRender(Rect bounds, IEnumerable entries)
+ {
+ var calculatedRect = new Rect(0, 0, bounds.width - 16f, CalculateHeight(bounds.width - 16f, entries));
+ viewRect = calculatedRect;
+ windowRect = bounds;
+ this.entries = entries;
+ }
+
+ public void OnGui()
+ {
+ Start(windowRect, viewRect);
+ foreach (var entry in entries)
+ Append(entry);
+ End();
+ }
+
+ public void OnGui(ref Vector2 scroller)
+ {
+ Start(windowRect, viewRect, ref scroller);
+ foreach (var entry in entries)
+ Append(entry);
+ End();
+ }
+
+ public void Start(Rect windowRect, Rect viewRect)
+ {
+ this.viewRect = viewRect;
+ Widgets.BeginScrollView(windowRect, ref scrollPosition, viewRect, true);
+ }
+
+ public void Start(Rect windowRect, Rect viewRect, ref Vector2 scroller)
+ {
+ this.viewRect = viewRect;
+ Widgets.BeginScrollView(windowRect, ref scroller, viewRect, true);
+ }
+
+ public void Append(string item)
+ {
+ //Insert html formatting
+ item = item
+ .Replace("[b]", "")
+ .Replace("[/b]", " ");
+
+ Text.Font = ItemFont;
+
+ // Draw title
+ if (item.StartsWith("[title]"))
+ {
+ Text.Font = TitleFont;
+ Widgets.Label(viewRect, item.Substring(7));
+ viewRect.y += Text.CalcHeight(item, viewRect.width) + Spacing;
+
+ // Draw line gap
+ Color color = GUI.color;
+ GUI.color = GUI.color * new Color(1f, 1f, 1f, 0.4f);
+ Widgets.DrawLineHorizontal(viewRect.x, viewRect.y + +GapHeight * 0.5f, viewRect.width);
+ GUI.color = color;
+ viewRect.y += GapHeight;
+ }
+ // Draw Image with Text
+ else if (item.StartsWith("[img]"))
+ {
+ int imgLength = item.IndexOf("[/img]");
+ var imageString = item.Substring(5, imgLength - 5);
+ var textToDraw = item.Substring(imgLength + 6);
+
+ var content = new GUIContent();
+ content.image = ContentFinder.Get(imageString, false);
+ content.text = textToDraw;
+ Widgets.Label(viewRect, content);
+
+ viewRect.y += GuiStyle(Text.Font).CalcHeight(content, viewRect.width);
+ }
+ // Draw Gap
+ else if (item.StartsWith("[gap]"))
+ {
+ Color color = GUI.color;
+ GUI.color = GUI.color * new Color(1f, 1f, 1f, 0.4f);
+ Widgets.DrawLineHorizontal(viewRect.x, viewRect.y + +GapHeight * 0.5f, viewRect.width);
+ GUI.color = color;
+ viewRect.y += GapHeight;
+ }
+ // Draw Subtitle (without margin, old)
+ else if (item.StartsWith("[subtitle]"))
+ {
+ Widgets.Label(viewRect, item.Substring(10));
+ viewRect.y += Text.CalcHeight(item.Substring(10), viewRect.width) + Spacing;
+ }
+ // Draw Video
+ else if (item.StartsWith("[video]"))
+ {
+ int imgLength = item.IndexOf("[/video]");
+ var framesSrc = item.Substring(7, imgLength - 7);
+ var dimensions = item.Substring(imgLength + 8).Split('x');
+
+ Vector2 videoSize = dimensions.Length >= 2 ? new Vector2(int.Parse(dimensions[0]), int.Parse(dimensions[1])) : new Vector2(100, 100);
+ int framesPerSecond = dimensions.Length >= 3 ? int.Parse(dimensions[2]) : 10;
+
+ new SimpleVideo(framesSrc, framesPerSecond).OnGui(new Rect((viewRect.x - videoSize.x) / 2, viewRect.y, videoSize.x, videoSize.y));
+
+ viewRect.y += videoSize.y;
+ }
+ // List point (with margin)
+ else if (item.StartsWith("[-]"))
+ {
+ viewRect.width -= MarginWidth;
+ Widgets.Label(viewRect, MarginText);
+ viewRect.x += MarginWidth;
+ Widgets.Label(viewRect, item.Substring(3));
+ viewRect.x -= MarginWidth;
+ viewRect.y += Text.CalcHeight(item.Substring(3), viewRect.width) + Spacing;
+ viewRect.width += MarginWidth;
+ }
+ // Draw Text
+ else
+ {
+ Widgets.Label(viewRect, item);
+ viewRect.y += Text.CalcHeight(item, viewRect.width) + Spacing;
+ }
+ }
+
+ public void End()
+ {
+ Widgets.EndScrollView();
+ }
+
+ private float CalculateHeight(float width, IEnumerable items)
+ {
+ float height = 0;
+ foreach (var item in items)
+ {
+ if (item.StartsWith("[title]"))
+ {
+ Text.Font = TitleFont;
+ height += Text.CalcHeight(item.Substring(7), width) + Spacing + GapHeight;
+ }
+ // Image with Text
+ else if (item.StartsWith("[img]"))
+ {
+ Text.Font = ItemFont;
+ int imgLength = item.IndexOf("[/img]");
+ var imageString = item.Substring(5, imgLength - 5);
+ var textToDraw = item.Substring(imgLength + 6);
+
+ var content = new GUIContent();
+ content.image = ContentFinder.Get(imageString, false);
+ content.text = textToDraw;
+
+
+ height += GuiStyle(Text.Font).CalcHeight(content, width);
+ }
+ // Gap
+ else if (item.StartsWith("[gap]"))
+ {
+ height += GapHeight;
+ }
+ else if (item.StartsWith("[subtitle]"))
+ {
+ Text.Font = ItemFont;
+ height += Text.CalcHeight(item.Substring(10), width) + Spacing;
+ }
+ else if (item.StartsWith("[-]"))
+ {
+ Text.Font = ItemFont;
+ height += Text.CalcHeight(item.Substring(3), width - MarginWidth) + Spacing;
+ }
+ // Only Text
+ else
+ {
+ Text.Font = ItemFont;
+ height += Text.CalcHeight(item, width) + Spacing;
+ }
+ }
+ return height;
+ }
+
+ private static GUIStyle GuiStyle(GameFont font)
+ {
+ GUIStyle gUIStyle;
+ switch (font)
+ {
+ case GameFont.Tiny:
+ gUIStyle = Text.fontStyles[0];
+ break;
+ case GameFont.Small:
+ gUIStyle = Text.fontStyles[1];
+ break;
+ case GameFont.Medium:
+ gUIStyle = Text.fontStyles[2];
+ break;
+ default:
+ return null;
+ }
+ gUIStyle.alignment = Text.Anchor;
+ gUIStyle.wordWrap = Text.WordWrap;
+ return gUIStyle;
+ }
+ }
+}
diff --git a/Source/Core/GUI_Components/SImpleVideo.cs b/Source/Core/GUI_Components/SImpleVideo.cs
new file mode 100644
index 00000000..ddca4668
--- /dev/null
+++ b/Source/Core/GUI_Components/SImpleVideo.cs
@@ -0,0 +1,38 @@
+using System.Collections.Generic;
+using UnityEngine;
+using Verse;
+
+namespace PrisonLabor.Core.GUI_Components
+{
+ public class SimpleVideo
+ {
+ Texture2D[] frames;
+ double framesPerSecond;
+
+ public SimpleVideo(string texturesPath, double framesPerSecond)
+ {
+ var framesList = new List();
+ int iterator = 0;
+
+ Texture2D texture;
+ do
+ {
+ texture = ContentFinder.Get($"{texturesPath}/{iterator++}", false);
+ if (texture != null)
+ framesList.Add(texture);
+ }
+ while (texture != null);
+
+ frames = framesList.ToArray();
+ this.framesPerSecond = framesPerSecond;
+ }
+
+ public void OnGui(Rect rect)
+ {
+ if (frames.Length > 0)
+ GUI.DrawTexture(rect, frames[(int)(Time.time * framesPerSecond) % frames.Length]);
+ else
+ Log.Warning("PrisonLabor Warning: no frames found in SimpleVideo");
+ }
+ }
+}
diff --git a/Source/Core/GameSaves/SaveCleaner.cs b/Source/Core/GameSaves/SaveCleaner.cs
new file mode 100644
index 00000000..92815495
--- /dev/null
+++ b/Source/Core/GameSaves/SaveCleaner.cs
@@ -0,0 +1,266 @@
+using PrisonLabor.Constants;
+using PrisonLabor.Core.GUI_Components;
+using PrisonLabor.Core.LaborArea;
+using PrisonLabor.Core.Needs;
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Xml;
+using Verse;
+
+namespace PrisonLabor.Core.GameSaves
+{
+ public static class SaveCleaner
+ {
+ public static void BackupSavegame(string fileName)
+ {
+ string savegamePath = GenFilePaths.FilePathForSavedGame(fileName);
+ string backupPath = GetFilePathForBackup(savegamePath);
+
+ File.Copy(savegamePath, backupPath, false);
+ Log.Message($"Save copied to \"{backupPath}\"");
+ }
+
+ public static void RemoveFromSave(string fileName)
+ {
+ LongEventHandler.QueueLongEvent(
+ () => UpdateFile(fileName),
+ "Removing",
+ false,
+ (e) => OnError(e)
+ );
+ }
+
+ private static void UpdateFile(string fileName)
+ {
+ string filePath = GenFilePaths.FilePathForSavedGame(fileName);
+
+ XmlElement xmlNode;
+ using (StreamReader streamReader = new StreamReader(filePath))
+ {
+ using (XmlTextReader xmlTextReader = new XmlTextReader(streamReader))
+ {
+ var XmlDocument = new XmlDocument();
+ XmlDocument.Load(xmlTextReader);
+ xmlNode = XmlDocument.DocumentElement;
+ }
+ }
+
+ UpdateData(xmlNode);
+
+ using (FileStream saveStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
+ {
+ var xmlWriterSettings = new XmlWriterSettings();
+ xmlWriterSettings.Indent = true;
+ xmlWriterSettings.IndentChars = "\t";
+ using (XmlWriter writer = XmlWriter.Create(saveStream, xmlWriterSettings))
+ {
+ writer.WriteStartDocument();
+ writer.WriteNode(xmlNode.CreateNavigator(), false);
+ }
+ }
+
+ Log.Message($"Save'{fileName}' converted successfuly");
+ }
+
+ private static void UpdateData(XmlElement xmlNode)
+ {
+ List removalBuffer = new List();
+ XmlNode curNode = xmlNode;
+
+ #region Meta
+ var metaNode = xmlNode["meta"];
+
+ // Meta.ModIds & Meta.ModNames
+ XmlNode modIdsNode = metaNode["modIds"], modNamesNode = metaNode["modNames"];
+ for (int i = 0; i < modIdsNode.ChildNodes.Count; i++)
+ {
+ var modNode = modIdsNode.ChildNodes[i];
+
+ if (modNode.InnerText == "972057888" || modNode.InnerText == "PrisonLabor")
+ {
+ modIdsNode.RemoveChild(modIdsNode.ChildNodes[i]);
+ modNamesNode.RemoveChild(modNamesNode.ChildNodes[i]);
+ break;
+ }
+ }
+
+ // Remove version of PL
+ var plVersionInfo = metaNode["PrisonLaborVersion"];
+ if (plVersionInfo != null)
+ metaNode.RemoveChild(plVersionInfo);
+ #endregion
+
+ #region Game
+ var gameNode = xmlNode["game"];
+
+ // Game.Tutor
+ string[] conceptDefs = { "PrisonLabor_Indroduction", "PrisonLabor_Motivation", "PrisonLabor_Growing", "PrisonLabor_Management", "PrisonLabor_Timetable" };
+
+ var tutorNode = gameNode["tutor"];
+ //var activeLessonsNode = tutorNode["activeLesson"];
+ var learningReadoutNode = tutorNode["learningReadout"];
+ if (learningReadoutNode["activeConcepts"].HasChildNodes)
+ {
+ removalBuffer.Clear();
+ foreach (XmlNode concept in learningReadoutNode["activeConcepts"].ChildNodes)
+ {
+ foreach (string conceptDef in conceptDefs)
+ {
+ if (concept.InnerText == conceptDef)
+ removalBuffer.Add(concept);
+ }
+ }
+ foreach (var concept in removalBuffer)
+ learningReadoutNode["activeConcepts"].RemoveChild(concept);
+ }
+ if (learningReadoutNode["selectedConcept"] != null)
+ {
+ removalBuffer.Clear();
+ foreach (string conceptDef in conceptDefs)
+ {
+ if (learningReadoutNode["selectedConcept"].InnerText == conceptDef)
+ learningReadoutNode.RemoveChild(learningReadoutNode["selectedConcept"]);
+ }
+ }
+ //var tutorialStateNode = tutorNode["tutorialState"];
+
+ // Game.Maps
+ foreach (XmlNode mapNode in gameNode["maps"].ChildNodes)
+ {
+ // Game.Maps.AreaManager
+ var areaManagerNode = mapNode["areaManager"];
+
+ if (areaManagerNode["areas"] != null && areaManagerNode["areas"].HasChildNodes)
+ {
+ removalBuffer.Clear();
+ foreach (XmlNode areaNode in areaManagerNode["areas"].ChildNodes)
+ {
+ if (areaNode.Attributes["Class"].Value == typeof(Area_Labor).FullName)
+ {
+ removalBuffer.Add(areaNode);
+ }
+ }
+ foreach (var node in removalBuffer)
+ areaManagerNode["areas"].RemoveChild(node);
+ }
+
+ //Game.Maps.Components
+ var components = mapNode["components"];
+ removalBuffer.Clear();
+ foreach (XmlNode component in components)
+ {
+ if (component.Attributes["Class"].Value == typeof(PawnIcons).FullName)
+ removalBuffer.Add(component);
+ }
+ foreach (var item in removalBuffer)
+ components.RemoveChild(item);
+ }
+
+ // TODO bills
+
+ // Interaction Mode
+ string[] interactions = { PL_DefOf.PrisonLabor_workOption.defName, PL_DefOf.PrisonLabor_workAndRecruitOption.defName };
+
+ foreach (var guestTracker in gameNode.GetEveryNode("guest"))
+ {
+ var interactionMode = guestTracker["interactionMode"];
+ if (interactionMode != null)
+ {
+ foreach (string interaction in interactions)
+ {
+ if (interactionMode.InnerText == interaction)
+ interactionMode.InnerText = PrisonerInteractionModeDefOf.NoInteraction.defName;
+ }
+ }
+ }
+
+
+ // Remove Heddifs
+ foreach (var needTracker in gameNode.GetEveryNode("needs"))
+ {
+ var needs = needTracker["needs"];
+ if (needs != null)
+ {
+
+ if (needs != null && needs.HasChildNodes)
+ {
+ removalBuffer.Clear();
+ foreach (XmlNode need in needs.ChildNodes)
+ {
+ if (need.Attributes["Class"].Value == typeof(Need_Motivation).FullName)
+ removalBuffer.Add(need);
+ else if (need.Attributes["Class"].Value == typeof(Need_Treatment).FullName)
+ removalBuffer.Add(need);
+ }
+ foreach (var node in removalBuffer)
+ needs.RemoveChild(node);
+ }
+ }
+ }
+
+ // Remove Heddifs
+ foreach (var hediffSet in gameNode.GetEveryNode("hediffSet"))
+ {
+ var hediffs = hediffSet["hediffs"];
+
+ if (hediffs != null && hediffs.HasChildNodes)
+ {
+ removalBuffer.Clear();
+ foreach (XmlNode hediff in hediffs.ChildNodes)
+ {
+ if (hediff["def"].InnerText == "PrisonLabor_PrisonerChains")
+ {
+ removalBuffer.Add(hediff);
+ }
+ }
+ foreach (var node in removalBuffer)
+ hediffs.RemoveChild(node);
+ }
+ }
+ #endregion
+ }
+
+ private static IEnumerable GetEveryNode(this XmlNode rootElement, string nodeName)
+ {
+ foreach (XmlNode node in rootElement.ChildNodes)
+ {
+ if (node.Name.Equals(nodeName))
+ yield return node;
+ if (node.HasChildNodes)
+ {
+ foreach (XmlNode childNode in node.GetEveryNode(nodeName))
+ yield return childNode;
+ }
+ }
+ }
+
+ private static void OnError(Exception e)
+ {
+ Log.Error(e.ToString());
+ }
+
+ private static string GetFilePathForBackup(string filePath)
+ {
+ string originFilePathWithoutExtension = Path.GetDirectoryName(filePath) + @"\" + Path.GetFileNameWithoutExtension(filePath);
+
+ string backupFileCoreString = originFilePathWithoutExtension + "_Backup";
+
+ string backupFilePathFinal = backupFileCoreString + ".rws";
+
+ if (!File.Exists(backupFilePathFinal))
+ return backupFilePathFinal;
+
+ for (int i = 1; i < int.MaxValue; i++)
+ {
+ backupFilePathFinal = backupFileCoreString + i.ToString() + ".rws";
+
+ if (!File.Exists(backupFilePathFinal))
+ return backupFilePathFinal;
+ }
+
+ throw new IndexOutOfRangeException();
+ }
+ }
+}
diff --git a/Source/Core/GameSaves/SaveUpgrader.cs b/Source/Core/GameSaves/SaveUpgrader.cs
new file mode 100644
index 00000000..55e506a1
--- /dev/null
+++ b/Source/Core/GameSaves/SaveUpgrader.cs
@@ -0,0 +1,47 @@
+using PrisonLabor.Core.GUI_Components;
+using PrisonLabor.Core.Meta;
+using PrisonLabor.Core.Needs;
+using System;
+using System.Xml;
+using Verse;
+using Version = PrisonLabor.Core.Meta.Version;
+
+namespace PrisonLabor.Core.GameSaves
+{
+ static class SaveUpgrader
+ {
+ public static void Upgrade()
+ {
+ if (Scribe.mode == LoadSaveMode.LoadingVars || Scribe.mode == LoadSaveMode.ResolvingCrossRefs || Scribe.mode == LoadSaveMode.PostLoadInit)
+ {
+ if (VersionUtility.VersionOfSaveFile == VersionUtility.versionNumber)
+ return;
+
+ LongEventHandler.SetCurrentEventText("PrisonLabor_UpgradeSaveProcessMessage".Translate());
+
+ var xmlNode = Scribe.loader.curXmlParent;
+
+ if (VersionUtility.VersionOfSaveFile < Version.v0_10_0)
+ UpTo_0_10(xmlNode);
+ }
+ }
+
+ private static void UpTo_0_10(XmlNode xml)
+ {
+ // Replace job trackers
+ xml.InnerXml = xml.InnerXml.Replace("", "");
+
+ // Replace job defs
+ xml.InnerXml = xml.InnerXml.Replace("PrisonLabor_DeliverFood_Tweak ", "DeliverFood ");
+
+ // Replace need classes
+ xml.InnerXml = xml.InnerXml.Replace("PrisonLabor.Need_Motivation", typeof(Need_Motivation).FullName);
+
+ // Replace need classes
+ xml.InnerXml = xml.InnerXml.Replace("PrisonLabor.Need_Treatment", typeof(Need_Treatment).FullName);
+
+ // Replace need classes
+ xml.InnerXml = xml.InnerXml.Replace("PrisonLabor.MapComponent_Icons", typeof(PawnIcons).FullName);
+ }
+ }
+}
diff --git a/Source/HediffGiver_PrisonersChains.cs b/Source/Core/Hediffs/HediffGiver_PrisonersChains.cs
similarity index 95%
rename from Source/HediffGiver_PrisonersChains.cs
rename to Source/Core/Hediffs/HediffGiver_PrisonersChains.cs
index 02f06706..3c3ace79 100644
--- a/Source/HediffGiver_PrisonersChains.cs
+++ b/Source/Core/Hediffs/HediffGiver_PrisonersChains.cs
@@ -1,11 +1,11 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Verse;
using RimWorld;
-namespace PrisonLabor
+namespace PrisonLabor.Core.Hediffs
{
class HediffGiver_PrisonersChains : HediffGiver
{
diff --git a/Source/HediffManager.cs b/Source/Core/Hediffs/HediffManager.cs
similarity index 89%
rename from Source/HediffManager.cs
rename to Source/Core/Hediffs/HediffManager.cs
index 6ccf0870..20198441 100644
--- a/Source/HediffManager.cs
+++ b/Source/Core/Hediffs/HediffManager.cs
@@ -1,11 +1,11 @@
-using RimWorld;
+using RimWorld;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Verse;
-namespace PrisonLabor
+namespace PrisonLabor.Core.Hediffs
{
public static class HediffManager
{
diff --git a/Source/Core/Incidents/IncidentWorker_ResocializationOffer.cs b/Source/Core/Incidents/IncidentWorker_ResocializationOffer.cs
new file mode 100644
index 00000000..8624ebfe
--- /dev/null
+++ b/Source/Core/Incidents/IncidentWorker_ResocializationOffer.cs
@@ -0,0 +1,66 @@
+using System;
+using UnityEngine;
+using Verse;
+using RimWorld;
+using Verse.AI.Group;
+using System.Collections.Generic;
+using PrisonLabor.Constants;
+using PrisonLabor.Core.Needs;
+using PrisonLabor.Core.Other;
+
+namespace PrisonLabor.Core.Incidents
+{
+ public class IncidentWorker_ResocializationOffer : IncidentWorker
+ {
+ protected override bool CanFireNowSub(IncidentParms parms)
+ {
+ Map map = (Map)parms.target;
+
+ foreach (var pawn in map.mapPawns.PrisonersOfColony)
+ {
+ if (pawn.IsColonist)
+ continue;
+
+ var treatment = pawn.needs.TryGetNeed();
+ if (treatment == null)
+ continue;
+
+ if (treatment.CurLevel >= BGP.ResocializationLevel)
+ return true;
+ }
+
+ return false;
+ }
+
+ protected override bool TryExecuteWorker(IncidentParms parms)
+ {
+ Map map = (Map)parms.target;
+ Pawn prisoner = null;
+ var affectedPawns = new List(map.mapPawns.PrisonersOfColony);
+ foreach (var pawn in map.mapPawns.PrisonersOfColony)
+ {
+ if (pawn.IsColonist)
+ continue;
+
+ var treatment = pawn.needs.TryGetNeed();
+ if (treatment == null)
+ continue;
+
+ if (treatment.CurLevel >= BGP.ResocializationLevel)
+ {
+ treatment.ResocializationReady = true;
+ parms.faction = pawn.Faction;
+ prisoner = pawn;
+ break;
+ }
+ }
+ if (prisoner == null)
+ return false;
+
+ Tutorials.Treatment();
+
+ SendStandardLetter(prisoner, null, new string[] { prisoner.Name.ToStringShort, prisoner.Faction.Name });
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/Core/Incidents/IncidentWorker_Revolt.cs b/Source/Core/Incidents/IncidentWorker_Revolt.cs
new file mode 100644
index 00000000..16e405d2
--- /dev/null
+++ b/Source/Core/Incidents/IncidentWorker_Revolt.cs
@@ -0,0 +1,179 @@
+using System;
+using UnityEngine;
+using Verse;
+using RimWorld;
+using Verse.AI.Group;
+using System.Collections.Generic;
+using PrisonLabor.Core.Needs;
+using PrisonLabor.Core.Meta;
+using PrisonLabor.Core.Other;
+
+namespace PrisonLabor.Core.Incidents
+{
+ public class IncidentWorker_Revolt : IncidentWorker
+ {
+ private const float MaxMotivationToStart = 0.4f;
+ private const float MaxTreatmentToStart = 3.5f;
+
+ protected override bool CanFireNowSub(IncidentParms parms)
+ {
+ Map map = parms.target as Map;
+
+ bool enemyFaction = false;
+ float accumulatedMotivation = 0.0f;
+ float accumulatedTreatment = 0.0f;
+ int prisonersCount = 0;
+
+ // Calculate values
+ foreach (var pawn in map.mapPawns.PrisonersOfColony)
+ {
+ if (pawn.Faction.HostileTo(Faction.OfPlayer))
+ enemyFaction = true;
+
+ var need = pawn.needs.TryGetNeed();
+ if (need == null)
+ continue;
+ accumulatedMotivation += need.CurLevel;
+ prisonersCount++;
+ }
+
+ // If motivation is too high
+ if (accumulatedMotivation / prisonersCount >= MaxMotivationToStart)
+ return false;
+ // If treatment is too good
+ if (accumulatedTreatment / prisonersCount >= MaxTreatmentToStart)
+ return false;
+
+ return enemyFaction && PrisonLaborPrefs.EnableRevolts;
+ }
+
+ protected override bool TryExecuteWorker(IncidentParms parms)
+ {
+ try
+ {
+ Map map = (Map)parms.target;
+ Pawn t = null;
+ var affectedPawns = new List(map.mapPawns.PrisonersOfColony);
+
+ // Calculate chance for blocking incident if prisoners are treated good
+ float treatment = 0f;
+ float chance = 0f;
+ foreach (Pawn pawn in affectedPawns)
+ if (pawn.needs.TryGetNeed() != null)
+ treatment += (float)pawn.needs.TryGetNeed().CurCategory;
+ treatment = treatment / affectedPawns.Count;
+ if (treatment < 0.5)
+ chance = 1f;
+ else if (treatment < 1.5)
+ chance = 0.95f;
+ else if (treatment < 2.5)
+ chance = 0.5f;
+ else if (treatment < 3.5)
+ chance = 0.1f;
+
+ // When incident is forced, log instead of blocking
+ if (!parms.forced)
+ {
+ if (Prefs.DevMode)
+ {
+ string msg = $"Prison Labor: Revolt blocking chance is currently equal to {chance * 100}% (overall treatment = {treatment}). Rolling ...";
+ Log.Message(msg);
+ }
+ if (UnityEngine.Random.value > chance)
+ return false;
+ }
+
+
+ foreach (Pawn pawn in affectedPawns)
+ {
+ if (pawn.Faction.HostileTo(Faction.OfPlayer))
+ {
+ parms.faction = pawn.Faction;
+ t = pawn;
+ break;
+ }
+ }
+ float points = parms.points;
+ int prisonersLeft = affectedPawns.Count;
+ foreach (Pawn pawn in affectedPawns)
+ {
+ pawn.ClearMind();
+ pawn.guest.SetGuestStatus(null, false);
+ pawn.SetFaction(parms.faction);
+
+ ThingWithComps weapon = ThingMaker.MakeThing(DefDatabase.GetNamed("MeleeWeapon_Knife"), ThingDefOf.WoodLog) as ThingWithComps;
+ ThingWithComps ammo = null;
+ int pointsToRemove = 0;
+
+ if (parms.points >= 1000)
+ weapon = ThingMaker.MakeThing(DefDatabase.GetNamed("MeleeWeapon_Knife"), ThingDefOf.Steel) as ThingWithComps;
+
+ if (points >= 1000)
+ {
+ // If combat extended is enabled
+ if (DefDatabase.GetNamed("Weapon_GrenadeStickBomb", false) != null)
+ {
+ if (UnityEngine.Random.value > 0.5f)
+ {
+ weapon = ThingMaker.MakeThing(DefDatabase.GetNamed("Weapon_GrenadeStickBomb")) as ThingWithComps;
+ ammo = ThingMaker.MakeThing(DefDatabase.GetNamed("Weapon_GrenadeStickBomb")) as ThingWithComps;
+ ammo.stackCount = 6;
+ }
+ else
+ {
+ weapon = ThingMaker.MakeThing(DefDatabase.GetNamed("Weapon_GrenadeMolotov")) as ThingWithComps;
+ ammo = ThingMaker.MakeThing(DefDatabase.GetNamed("Weapon_GrenadeMolotov")) as ThingWithComps;
+ ammo.stackCount = 6;
+ }
+ }
+ else
+ {
+ weapon = ThingMaker.MakeThing(DefDatabase.GetNamed("Weapon_GrenadeMolotov")) as ThingWithComps;
+ }
+
+ pointsToRemove = 500;
+ }
+ else if (points >= 500)
+ {
+ weapon = ThingMaker.MakeThing(DefDatabase.GetNamed("Bow_Short")) as ThingWithComps;
+
+ if (DefDatabase.GetNamed("Ammo_Arrow_Stone", false) != null)
+ {
+ ammo = ThingMaker.MakeThing(DefDatabase.GetNamed("Ammo_Arrow_Stone")) as ThingWithComps;
+ ammo.stackCount = 30;
+ }
+
+ pointsToRemove = 100;
+ }
+ else if (points >= 300)
+ {
+ weapon = ThingMaker.MakeThing(DefDatabase.GetNamed("MeleeWeapon_Club"), ThingDefOf.Granite) as ThingWithComps;
+ pointsToRemove = 100;
+ }
+
+ if (pawn.equipment.Primary == null)
+ {
+ pawn.equipment.AddEquipment(weapon);
+ if (ammo != null)
+ pawn.inventory.innerContainer.TryAdd(ammo);
+ points -= pointsToRemove;
+ }
+ }
+ var lordJob = new LordJob_AssaultColony(parms.faction, true, true, false, true, true);
+ //TODO old code:
+ LordMaker.MakeNewLord(parms.faction, lordJob/*(new RaidStrategyWorker_ImmediateAttackSmart()).MakeLordJob(parms, map)*/, map, affectedPawns);
+ SendStandardLetter(t, null, new string[] { t.Name.ToStringShort, t.Faction.Name });
+ Find.TickManager.slower.SignalForceNormalSpeedShort();
+
+ Tutorials.Treatment();
+
+ return true;
+ }
+ catch(Exception e)
+ {
+ Log.Error($"PrisonLabor: Erron on executing Revolt Incident: {e.ToString()}");
+ return false;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/Core/Incidents/IncidentWorker_Suicide.cs b/Source/Core/Incidents/IncidentWorker_Suicide.cs
new file mode 100644
index 00000000..e39b3771
--- /dev/null
+++ b/Source/Core/Incidents/IncidentWorker_Suicide.cs
@@ -0,0 +1,71 @@
+using System;
+using UnityEngine;
+using Verse;
+using RimWorld;
+using Verse.AI.Group;
+using System.Collections.Generic;
+using PrisonLabor.Core.Needs;
+using PrisonLabor.Core.Other;
+
+namespace PrisonLabor.Core.Incidents
+{
+ public class IncidentWorker_Suicide : IncidentWorker
+ {
+ protected override bool CanFireNowSub(IncidentParms parms)
+ {
+ Map map = parms.target as Map;
+
+ foreach (var pawn in map.mapPawns.PrisonersOfColony)
+ {
+ if (pawn.IsColonist)
+ continue;
+
+ var need = pawn.needs.TryGetNeed();
+ if (need == null)
+ continue;
+
+ if (need.CurCategory >= TreatmentCategory.Bad)
+ return true;
+ }
+
+ return false;
+ }
+
+ protected override bool TryExecuteWorker(IncidentParms parms)
+ {
+ Map map = (Map)parms.target;
+ var affectedPawns = new List(map.mapPawns.PrisonersOfColony);
+ foreach (var pawn in map.mapPawns.PrisonersOfColony)
+ {
+ if (pawn.IsColonist)
+ continue;
+
+ var need = pawn.needs.TryGetNeed();
+ if (need == null)
+ continue;
+
+ if (need.CurCategory >= TreatmentCategory.Bad)
+ {
+ // If treatment is only bad reduce chance by 50%
+ if (need.CurCategory == TreatmentCategory.Bad && !parms.forced)
+ {
+ if (UnityEngine.Random.value < 0.5f)
+ continue;
+ }
+
+ SendStandardLetter(new TargetInfo(pawn.Position, pawn.Map), null, new string[] { pawn.Name.ToStringShort });
+ parms.faction = pawn.Faction;
+
+ DamageInfo dinfo = new DamageInfo(DamageDefOf.Cut, 29, 0, 0, pawn, pawn.RaceProps.body.AllParts.Find(p => p.def == BodyPartDefOf.Neck));
+ while (!pawn.Dead)
+ pawn.TakeDamage(dinfo);
+
+ Tutorials.Treatment();
+
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/Area_Labor.cs b/Source/Core/LaborArea/Area_Labor.cs
similarity index 89%
rename from Source/Area_Labor.cs
rename to Source/Core/LaborArea/Area_Labor.cs
index 44599812..87744b49 100644
--- a/Source/Area_Labor.cs
+++ b/Source/Core/LaborArea/Area_Labor.cs
@@ -1,7 +1,7 @@
-using UnityEngine;
+using UnityEngine;
using Verse;
-namespace PrisonLabor
+namespace PrisonLabor.Core.LaborArea
{
internal class Area_Labor : Area
{
diff --git a/Source/Designator_AreaLabor.cs b/Source/Core/LaborArea/Designator_AreaLabor.cs
similarity index 96%
rename from Source/Designator_AreaLabor.cs
rename to Source/Core/LaborArea/Designator_AreaLabor.cs
index c30bcd99..608c46b8 100644
--- a/Source/Designator_AreaLabor.cs
+++ b/Source/Core/LaborArea/Designator_AreaLabor.cs
@@ -1,9 +1,10 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
+using PrisonLabor.Core.Other;
using RimWorld;
using UnityEngine;
using Verse;
-namespace PrisonLabor
+namespace PrisonLabor.Core.LaborArea
{
[StaticConstructorOnStartup]
public abstract class Designator_AreaLabor : Designator_Area
diff --git a/Source/Designator_AreaLaborClear.cs b/Source/Core/LaborArea/Designator_AreaLaborClear.cs
similarity index 92%
rename from Source/Designator_AreaLaborClear.cs
rename to Source/Core/LaborArea/Designator_AreaLaborClear.cs
index b068b003..fd1b1d45 100644
--- a/Source/Designator_AreaLaborClear.cs
+++ b/Source/Core/LaborArea/Designator_AreaLaborClear.cs
@@ -1,8 +1,8 @@
-using RimWorld;
+using RimWorld;
using UnityEngine;
using Verse;
-namespace PrisonLabor
+namespace PrisonLabor.Core.LaborArea
{
internal class Designator_AreaLaborClear : Designator_AreaLabor
{
diff --git a/Source/Designator_AreaLaborExpand.cs b/Source/Core/LaborArea/Designator_AreaLaborExpand.cs
similarity index 92%
rename from Source/Designator_AreaLaborExpand.cs
rename to Source/Core/LaborArea/Designator_AreaLaborExpand.cs
index bdd0029a..cc434fb8 100644
--- a/Source/Designator_AreaLaborExpand.cs
+++ b/Source/Core/LaborArea/Designator_AreaLaborExpand.cs
@@ -1,8 +1,8 @@
-using RimWorld;
+using RimWorld;
using UnityEngine;
using Verse;
-namespace PrisonLabor
+namespace PrisonLabor.Core.LaborArea
{
internal class Designator_AreaLaborExpand : Designator_AreaLabor
{
diff --git a/Source/WorkSettings.cs b/Source/Core/LaborWorkSettings/WorkSettings.cs
similarity index 95%
rename from Source/WorkSettings.cs
rename to Source/Core/LaborWorkSettings/WorkSettings.cs
index 6a3f65c6..711a8c36 100644
--- a/Source/WorkSettings.cs
+++ b/Source/Core/LaborWorkSettings/WorkSettings.cs
@@ -1,11 +1,13 @@
-using RimWorld;
+using PrisonLabor.Constants;
+using PrisonLabor.Core.Meta;
+using RimWorld;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Verse;
-namespace PrisonLabor
+namespace PrisonLabor.Core.LaborWorkSettings
{
public static class WorkSettings
{
@@ -24,7 +26,7 @@ public static List AvailableWorkTypes
_availableWorkTypes.Add(worktype);
_availableWorkTypes.Remove(DefDatabase.GetNamed("Warden"));
- _availableWorkTypes.Remove(PrisonLaborDefOf.PrisonLabor_Jailor);
+ _availableWorkTypes.Remove(PL_DefOf.PrisonLabor_Jailor);
}
return _availableWorkTypes;
}
diff --git a/Source/Prefs.cs b/Source/Core/Meta/Prefs.cs
similarity index 91%
rename from Source/Prefs.cs
rename to Source/Core/Meta/Prefs.cs
index 0e6a08b6..5eddf52f 100644
--- a/Source/Prefs.cs
+++ b/Source/Core/Meta/Prefs.cs
@@ -1,9 +1,12 @@
-using System;
+using PrisonLabor.Core.LaborWorkSettings;
+using PrisonLabor.Core.Needs;
+using PrisonLabor.Core.Other;
+using System;
using System.IO;
using System.Xml.Linq;
using Verse;
-namespace PrisonLabor
+namespace PrisonLabor.Core.Meta
{
public static class PrisonLaborPrefs
{
@@ -105,12 +108,15 @@ public static bool EnableRevolts
}
}
- public static bool DisableMod
+ public static bool ShowTreatmentHappiness
{
- get { return data.disable_mod; }
+ get
+ {
+ return data.show_treatment_happiness;
+ }
set
{
- data.disable_mod = value;
+ data.show_treatment_happiness = value;
Apply();
}
}
@@ -145,7 +151,7 @@ public static void Init()
public static void Save()
{
- Tutorials.UpdateTutorialFlags();
+ Other.Tutorials.UpdateTutorialFlags();
try
{
var xDocument = new XDocument();
@@ -165,6 +171,7 @@ public static void Apply()
data.Apply();
WorkSettings.DataString = AllowedWorkTypes;
Tutorials.Apply();
+ Need_Treatment.ShowOnList = ShowTreatmentHappiness;
}
public static void RestoreToDefault()
diff --git a/Source/PrefsData.cs b/Source/Core/Meta/PrefsData.cs
similarity index 56%
rename from Source/PrefsData.cs
rename to Source/Core/Meta/PrefsData.cs
index 9507fcf0..1dbe41f8 100644
--- a/Source/PrefsData.cs
+++ b/Source/Core/Meta/PrefsData.cs
@@ -1,6 +1,6 @@
-using System;
+using System;
-namespace PrisonLabor
+namespace PrisonLabor.Core.Meta
{
public class PrisonLaborPrefsData
{
@@ -12,8 +12,7 @@ public class PrisonLaborPrefsData
public bool enable_motivation_mechanics = true;
public bool enable_motivation_icons = true;
public bool enable_revolts = true;
-
- public bool disable_mod = false;
+ public bool show_treatment_happiness = false;
public Version last_version = Version.v0_0;
public bool show_news = true;
@@ -27,48 +26,6 @@ public void Apply()
}
}
- public enum Version
- {
- v0_0,
- v0_1,
- v0_2,
- v0_3,
- v0_4,
- v0_5,
- v0_6,
- v0_7,
- v0_7_dev1,
- v0_7_dev2,
- v0_7_dev3,
- v0_7_dev4,
- v0_7_dev5,
- v0_8_0,
- v0_8_1,
- v0_8_2,
- v0_8_3,
- v0_8_4,
- v0_8_5,
- v0_8_6,
- v0_8_7,
- v0_8_8,
- v0_8_9,
- v0_8_9_1,
- v0_8_9_2,
- v0_8_9_4,
- v0_8_9_5,
- v0_9_0,
- v0_9_1,
- v0_9_2,
- v0_9_3,
- v0_9_4,
- v0_9_5,
- v0_9_6,
- v0_9_8,
- v0_9_9,
- v0_9_10,
- v0_9_11
- }
-
[Flags]
public enum TutorialFlag
{
@@ -79,5 +36,6 @@ public enum TutorialFlag
Managment = 0x8,
Timetable = 0x10,
LaborAreaWarning = 0x20,
+ Treatment = 0x40,
}
}
\ No newline at end of file
diff --git a/Source/Core/Meta/Version.cs b/Source/Core/Meta/Version.cs
new file mode 100644
index 00000000..5856c083
--- /dev/null
+++ b/Source/Core/Meta/Version.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace PrisonLabor.Core.Meta
+{
+ public enum Version
+ {
+ v0_0,
+ v0_1,
+ v0_2,
+ v0_3,
+ v0_4,
+ v0_5,
+ v0_6,
+ v0_7,
+ v0_7_dev1,
+ v0_7_dev2,
+ v0_7_dev3,
+ v0_7_dev4,
+ v0_7_dev5,
+ v0_8_0,
+ v0_8_1,
+ v0_8_2,
+ v0_8_3,
+ v0_8_4,
+ v0_8_5,
+ v0_8_6,
+ v0_8_7,
+ v0_8_8,
+ v0_8_9,
+ v0_8_9_1,
+ v0_8_9_2,
+ v0_8_9_4,
+ v0_8_9_5,
+ v0_9_0,
+ v0_9_1,
+ v0_9_2,
+ v0_9_3,
+ v0_9_4,
+ v0_9_5,
+ v0_9_6,
+ v0_9_8,
+ v0_9_9,
+ v0_9_10,
+ v0_9_11,
+ v0_10_0,
+ v0_10_1,
+ }
+}
diff --git a/Source/Core/Meta/VersionUtility.cs b/Source/Core/Meta/VersionUtility.cs
new file mode 100644
index 00000000..374fa1c3
--- /dev/null
+++ b/Source/Core/Meta/VersionUtility.cs
@@ -0,0 +1,55 @@
+using PrisonLabor.Core.Windows;
+
+namespace PrisonLabor.Core.Meta
+{
+ class VersionUtility
+ {
+ public const Version versionNumber = Version.v0_10_1;
+ public const string versionString = "0.10.1";
+
+ public static Version VersionOfSaveFile { get; set; }
+
+ public static void CheckVersion()
+ {
+ // Update actual version
+ if (PrisonLaborPrefs.Version == Version.v0_0)
+ {
+ PrisonLaborPrefs.Version = versionNumber;
+ PrisonLaborPrefs.LastVersion = versionNumber;
+ }
+ else if (PrisonLaborPrefs.Version != versionNumber)
+ {
+ PrisonLaborPrefs.Version = versionNumber;
+ }
+
+ // Client has new version
+ if (PrisonLaborPrefs.LastVersion < versionNumber)
+ {
+ // Show version news
+ NewsWindow.LastVersionString = GetVersionString(PrisonLaborPrefs.LastVersion);
+ NewsWindow.AutoShow = true;
+
+ // Dev version fix, it can be removed in future
+ // There is no changelog for 0.10 so it will skip it, and display all changes
+ if (PrisonLaborPrefs.LastVersion == Version.v0_10_0)
+ {
+ NewsWindow.LastVersionString = GetVersionString(Version.v0_9_11);
+ }
+
+ // Pre 0.9.4
+ if (PrisonLaborPrefs.LastVersion < Version.v0_9_4)
+ {
+ CompatibilityPatches.OlderVersions.Pre_v0_9_4();
+ }
+ }
+
+ PrisonLaborPrefs.Version = versionNumber;
+ PrisonLaborPrefs.Save();
+ }
+
+ public static string GetVersionString(Version version)
+ {
+ return version.ToString().Replace("_", ".").Substring(1);
+ }
+ }
+}
diff --git a/Source/Core/Needs/Need_Motivation.cs b/Source/Core/Needs/Need_Motivation.cs
new file mode 100644
index 00000000..4eb2da41
--- /dev/null
+++ b/Source/Core/Needs/Need_Motivation.cs
@@ -0,0 +1,140 @@
+using System.Collections.Generic;
+using System.Text;
+using PrisonLabor.Constants;
+using PrisonLabor.Core.Trackers;
+using PrisonLabor.HarmonyPatches;
+using RimWorld;
+using UnityEngine;
+using Verse;
+
+namespace PrisonLabor.Core.Needs
+{
+ public class Need_Motivation : Need
+ {
+ private const float LazyLevel = 0.2f;
+ private const float NeedInspirationLevel = 0.5f;
+
+ public float PercentageThreshNeedInsipration => NeedInspirationLevel;
+
+ public float PercentageThreshLazy => LazyLevel;
+ //TODO change to lazy category?
+ public HungerCategory CurCategory => 0;
+
+ public static NeedDef Def => PL_DefOf.PrisonLabor_Motivation;
+
+ public bool Motivated => _GUIChangeArrow > 0;
+
+ private int _GUIChangeArrow;
+ public override int GUIChangeArrow => _GUIChangeArrow;
+
+ ///
+ /// Indicates whenever pawn should be motivated.
+ /// This property purpose is that pawn should be only motivated in semi-auto mode,
+ /// which means after getting to full, it should wait to drop a bit before recharging again.
+ ///
+ public bool ShouldToBeMotivated { get; private set; }
+
+ ///
+ /// Indicates whenever pawn is currently working, and his motivation is decreasing by laziness rate.
+ ///
+ public bool IsPrisonerWorking { get; set; }
+
+ ///
+ /// Indicates whenever pawn is lazy and stopped working by lack of motivation.
+ ///
+ public bool IsLazy { get; private set; }
+
+ public Need_Motivation(Pawn pawn) : base(pawn) { }
+
+ public override void SetInitialLevel()
+ {
+ CurLevelPercentage = 1.0f;
+ IsPrisonerWorking = false;
+ }
+
+ public override void NeedInterval()
+ {
+ // Update value of need
+ CurLevel += GetChangePoints();
+
+ // Set NeedToBeMotivated
+ if (CurLevel == MaxLevel)
+ ShouldToBeMotivated = false;
+ else if (CurLevel <= NeedInspirationLevel)
+ ShouldToBeMotivated = true;
+
+ // Set IsLazy
+ if (CurLevel <= LazyLevel && _GUIChangeArrow <= 0)
+ IsLazy = true;
+ else
+ IsLazy = false;
+ }
+
+ private float GetChangePoints()
+ {
+ if (pawn.IsPrisoner && pawn.IsPrisonerOfColony)
+ {
+ if (pawn.GetRoomGroup() != null)
+ {
+ var value = InspirationTracker.GetInsiprationValue(pawn, true);
+
+ if (PrisonLaborUtility.LaborEnabled(pawn))
+ {
+ if (IsPrisonerWorking)
+ {
+ value -= BGP.Laziness_LazyRate;
+ if (HealthAIUtility.ShouldSeekMedicalRest(pawn))
+ value -= BGP.Laziness_HealthRate;
+ value -= (int)pawn.needs.food.CurCategory * BGP.Laziness_HungryRate;
+ value -= (int)pawn.needs.rest.CurCategory * BGP.Laziness_TiredRate;
+ }
+ else if (pawn.timetable != null && pawn.timetable.CurrentAssignment == TimeAssignmentDefOf.Joy)
+ {
+ value += BGP.Laziness_JoyRate;
+ }
+ }
+
+ _GUIChangeArrow = value.CompareTo(0.0f);
+ return value;
+ }
+ else
+ {
+ _GUIChangeArrow = 0;
+ return 0.0f;
+ }
+ }
+ else
+ {
+ _GUIChangeArrow = 1;
+ return +0.01f;
+ }
+ }
+
+ public override string GetTipString()
+ {
+ var stringBuilder = new StringBuilder();
+ stringBuilder.AppendLine(base.GetTipString());
+ stringBuilder.AppendLine();
+ stringBuilder.Append("PrisonLabor_WardenResponseThreshold".Translate());
+ stringBuilder.AppendLine($": {PercentageThreshNeedInsipration.ToStringPercent()}");
+ stringBuilder.Append("PrisonLabor_StoppingWorkThreshold".Translate());
+ stringBuilder.AppendLine($": {PercentageThreshLazy.ToStringPercent()}");
+ return stringBuilder.ToString();
+ }
+
+ public override void DrawOnGUI(Rect rect, int maxThresholdMarkers = 2147483647, float customMargin = -1f, bool drawArrows = true, bool doTooltip = true)
+ {
+ if (threshPercents == null)
+ threshPercents = new List();
+ threshPercents.Clear();
+ threshPercents.Add(PercentageThreshLazy);
+ threshPercents.Add(PercentageThreshNeedInsipration);
+ base.DrawOnGUI(rect, maxThresholdMarkers, customMargin, drawArrows, doTooltip);
+ }
+
+ public override void ExposeData()
+ {
+ base.ExposeData();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/Core/Needs/Need_Treatment.cs b/Source/Core/Needs/Need_Treatment.cs
new file mode 100644
index 00000000..b036eb8d
--- /dev/null
+++ b/Source/Core/Needs/Need_Treatment.cs
@@ -0,0 +1,140 @@
+using System.Collections.Generic;
+using System.Text;
+using PrisonLabor.Constants;
+using RimWorld;
+using UnityEngine;
+using Verse;
+
+namespace PrisonLabor.Core.Needs
+{
+ ///
+ /// VeryGood: 4
+ /// Good: 3
+ /// Normal: 2
+ /// Bad: 1
+ /// VeryBad: 0
+ ///
+ public enum TreatmentCategory : byte
+ {
+ VeryGood = 4,
+ Good = 3,
+ Normal = 2,
+ Bad = 1,
+ VeryBad = 0,
+ }
+
+ public class Need_Treatment : Need
+ {
+ private bool _resocializationReady = false;
+
+ public float PercentageThreshVeryGood => 0.85f;
+ public float PercentageThreshGood => 0.65f;
+ public float PercentageThreshNormal => 0.35f;
+ public float PercentageThreshBad => 0.15f;
+
+ public override int GUIChangeArrow => 0;
+
+ public static NeedDef Def => PL_DefOf.PrisonLabor_Treatment;
+
+ public bool ResocializationReady
+ {
+ get => _resocializationReady;
+ set { _resocializationReady = value; }
+ }
+
+ public TreatmentCategory CurCategory
+ {
+ get
+ {
+ if (CurLevelPercentage < PercentageThreshBad)
+ return TreatmentCategory.VeryBad;
+ else if (CurLevelPercentage < PercentageThreshNormal)
+ return TreatmentCategory.Bad;
+ else if (CurLevelPercentage < PercentageThreshGood)
+ return TreatmentCategory.Normal;
+ else if (CurLevelPercentage < PercentageThreshVeryGood)
+ return TreatmentCategory.Good;
+ else
+ return TreatmentCategory.VeryGood;
+ }
+ }
+
+ public static bool ShowOnList
+ {
+ get
+ {
+ return PL_DefOf.PrisonLabor_Treatment.showOnNeedList;
+ }
+ set
+ {
+ PL_DefOf.PrisonLabor_Treatment.showOnNeedList = value;
+ }
+ }
+
+ public Need_Treatment(Pawn pawn) : base(pawn) { }
+
+ public override void SetInitialLevel()
+ {
+ CurLevelPercentage = 0.5f;
+ }
+
+ public override void NeedInterval()
+ {
+ // Joy
+ if (pawn.timetable != null && pawn.timetable.CurrentAssignment == TimeAssignmentDefOf.Joy)
+ CurLevel += BGP.JoyRate;
+
+ // Status
+ var hunger = pawn.needs.TryGetNeed();
+
+ int statusScore = 0;
+ if (hunger.CurCategory < HungerCategory.UrgentlyHungry)
+ statusScore += 5;
+ if (hunger.CurCategory < HungerCategory.Hungry)
+ statusScore += 5;
+ statusScore -= (int)pawn.health.hediffSet.PainTotal / 10;
+ if (pawn.health.HasHediffsNeedingTend())
+ statusScore -= 7;
+
+ CurLevel += statusScore * BGP.StatusMultiplier;
+
+
+ // Labor
+ var motivation = pawn.needs.TryGetNeed();
+ if (motivation != null && motivation.IsPrisonerWorking)
+ CurLevel += BGP.LaborRate;
+ }
+
+ public override string GetTipString()
+ {
+ var stringBuilder = new StringBuilder();
+ stringBuilder.AppendLine(base.GetTipString());
+ return stringBuilder.ToString();
+ }
+
+ public void NotifyPrisonerBeaten(DamageInfo dinfo, bool absorbed)
+ {
+ CurLevel += BGP.BeatenHit;
+ }
+
+ public override void DrawOnGUI(Rect rect, int maxThresholdMarkers = 2147483647, float customMargin = -1f,
+ bool drawArrows = true, bool doTooltip = true)
+ {
+ if (threshPercents == null)
+ {
+ threshPercents = new List();
+ threshPercents.Add(PercentageThreshBad);
+ threshPercents.Add(PercentageThreshNormal);
+ threshPercents.Add(PercentageThreshGood);
+ threshPercents.Add(PercentageThreshVeryGood);
+ }
+ base.DrawOnGUI(rect, maxThresholdMarkers, customMargin, drawArrows, doTooltip);
+ }
+
+ public override void ExposeData()
+ {
+ base.ExposeData();
+ Scribe_Values.Look(ref _resocializationReady, "PrisonLabor_resocialization_ready", false, false);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/Core/Other/ArrestUtility.cs b/Source/Core/Other/ArrestUtility.cs
new file mode 100644
index 00000000..eabd184d
--- /dev/null
+++ b/Source/Core/Other/ArrestUtility.cs
@@ -0,0 +1,88 @@
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using UnityEngine;
+using Verse;
+using Verse.AI;
+
+namespace PrisonLabor.Core.Other
+{
+ public static class ArrestUtility
+ {
+ public static void AddArrestOrder(Vector3 clickPos, Pawn pawn, List opts)
+ {
+ //TODO this is some copy-pasted example, refactor it so it should add arrest option
+ IntVec3 c = IntVec3.FromVector3(clickPos);
+ if (pawn.health.capacities.CapableOf(PawnCapacityDefOf.Manipulation))
+ {
+ foreach (LocalTargetInfo current in GenUI.TargetsAt(clickPos, ForArrest(pawn), true))
+ {
+ LocalTargetInfo dest = current;
+ bool flag = dest.HasThing && dest.Thing is Pawn && ((Pawn)dest.Thing).IsWildMan();
+ if (!pawn.Drafted || flag)
+ {
+ if (!pawn.CanReach(dest, PathEndMode.OnCell, Danger.Deadly, false, TraverseMode.ByPawn))
+ {
+ opts.Add(new FloatMenuOption("CannotArrest".Translate() + " (" + "NoPath".Translate() + ")", null, MenuOptionPriority.Default, null, null, 0f, null, null));
+ }
+ else
+ {
+ Pawn pTarg = (Pawn)dest.Thing;
+ Action action = delegate
+ {
+ Building_Bed building_Bed = RestUtility.FindBedFor(pTarg, pawn, true, false, false);
+ if (building_Bed == null)
+ {
+ building_Bed = RestUtility.FindBedFor(pTarg, pawn, true, false, true);
+ }
+ if (building_Bed == null)
+ {
+ Messages.Message("CannotArrest".Translate() + ": " + "NoPrisonerBed".Translate(), pTarg, MessageTypeDefOf.RejectInput, false);
+ return;
+ }
+ Job job = new Job(JobDefOf.Arrest, pTarg, building_Bed);
+ job.count = 1;
+ pawn.jobs.TryTakeOrderedJob(job, JobTag.Misc);
+ if (pTarg.Faction != null && pTarg.Faction != Faction.OfPlayer && !pTarg.Faction.def.hidden)
+ {
+ TutorUtility.DoModalDialogIfNotKnown(ConceptDefOf.ArrestingCreatesEnemies);
+ }
+ };
+ string label = "TryToArrest".Translate(dest.Thing.LabelCap, dest.Thing);
+ Action action2 = action;
+ MenuOptionPriority priority = MenuOptionPriority.High;
+ Thing thing = dest.Thing;
+ opts.Add(FloatMenuUtility.DecoratePrioritizedTask(new FloatMenuOption(label, action2, priority, null, thing, 0f, null, null), pawn, pTarg, "ReservedBy"));
+ }
+ }
+ }
+ }
+ }
+
+ public static TargetingParameters ForArrest(Pawn arrester)
+ {
+ return new TargetingParameters
+ {
+ canTargetPawns = true,
+ canTargetBuildings = false,
+ mapObjectTargetsMustBeAutoAttackable = false,
+ validator = delegate (TargetInfo targ)
+ {
+ if (!targ.HasThing)
+ {
+ return false;
+ }
+ Pawn pawn = targ.Thing as Pawn;
+ return pawn != null && pawn != arrester && CanBeArrestedBy(pawn, arrester) && !pawn.Downed;
+ }
+ };
+ }
+
+ public static bool CanBeArrestedBy(Pawn pawn, Pawn arrester)
+ {
+ return pawn.RaceProps.Humanlike && pawn.HostileTo(arrester.Faction) && pawn.CurJob.def == JobDefOf.Flee && (!pawn.IsPrisonerOfColony || !pawn.Position.IsInPrisonCell(pawn.Map));
+ }
+ }
+}
diff --git a/Source/Core/Other/NewsProvider.cs b/Source/Core/Other/NewsProvider.cs
new file mode 100644
index 00000000..babe8298
--- /dev/null
+++ b/Source/Core/Other/NewsProvider.cs
@@ -0,0 +1,122 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System.Xml;
+using Verse;
+
+namespace PrisonLabor.Core.Other
+{
+ public static class NewsProvider
+ {
+ public struct VersionNotes
+ {
+ public string version;
+ public string[] entries;
+ }
+
+ public static VersionNotes[] allVersionNotes;
+
+ static NewsProvider()
+ {
+ try
+ {
+ allVersionNotes = GetVersionNotesFromChangelog(Properties.Resources.changelog).ToArray();
+
+ int iterator = 0;
+ foreach (var notes in GetVersionNotesFromNewsFeed(Properties.Resources.NewsFeed))
+ {
+ while (allVersionNotes[iterator].version != notes.version && iterator < allVersionNotes.Length)
+ iterator++;
+
+ if (iterator < allVersionNotes.Length)
+ {
+ allVersionNotes[iterator] = notes;
+ }
+ else
+ {
+ Log.Warning($"PrisonLabor: couldn't find version {notes.version} to alter");
+ iterator = 0;
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ Log.Error("PrisonLabor: Failed to initialize news" + e.ToString());
+ }
+ }
+
+ public static IEnumerable NewsAfterVersion(string versionString)
+ {
+ bool stop = false;
+ for (int i = 0; i < allVersionNotes.Length && !stop; i++)
+ {
+ if (allVersionNotes[i].version == versionString)
+ stop = true;
+ else
+ yield return allVersionNotes[i];
+ }
+ }
+
+ private static IEnumerable GetVersionNotesFromChangelog(string text)
+ {
+ var lines = Regex.Split(text, "\r\n|\r|\n");
+
+ if (lines.Length < 2)
+ yield break;
+
+ string currentPatch = lines[0];
+ List currentPatchNotes = new List();
+
+ for (int i = 1; i < lines.Length; i++)
+ {
+ string line = lines[i];
+
+ var match = Regex.Match(line, "^\\*|-|\t");
+
+ if (match.Success)
+ {
+ currentPatchNotes.Add("[-]" + line.Replace(match.Value, "").Trim());
+ }
+ else
+ {
+ if (currentPatchNotes.Count > 0)
+ {
+ currentPatchNotes.Insert(0, $"[title]Prison Labor v{currentPatch}");
+ yield return new VersionNotes() { version = currentPatch, entries = currentPatchNotes.ToArray() };
+ }
+ currentPatch = line;
+ currentPatchNotes = new List();
+ }
+ }
+
+ // Last iteration
+ if (currentPatchNotes.Count > 0)
+ {
+ currentPatchNotes.Insert(0, $"[title]Prison Labor v{currentPatch}");
+ yield return new VersionNotes() { version = currentPatch, entries = currentPatchNotes.ToArray() };
+ }
+ }
+
+ private static IEnumerable GetVersionNotesFromNewsFeed(string xml)
+ {
+ XmlDocument xmlDocument = new XmlDocument();
+ xmlDocument.LoadXml(xml);
+ foreach (XmlNode patch in xmlDocument.DocumentElement["patches"].ChildNodes)
+ {
+ if (patch.Name == "patch")
+ {
+ var entries = new List();
+ entries.Add($"[title]{patch["title"].InnerXml}");
+ foreach (XmlNode item in patch["items"].ChildNodes)
+ entries.Add(item.InnerXml);
+ yield return new VersionNotes()
+ {
+ version = patch.Attributes["version"].Value,
+ entries = entries.ToArray(),
+ };
+ }
+ }
+ }
+ }
+}
diff --git a/Source/PrisonerFoodReservation.cs b/Source/Core/Other/PrisonerFoodReservation.cs
similarity index 72%
rename from Source/PrisonerFoodReservation.cs
rename to Source/Core/Other/PrisonerFoodReservation.cs
index 1eecf2b4..3af288c1 100644
--- a/Source/PrisonerFoodReservation.cs
+++ b/Source/Core/Other/PrisonerFoodReservation.cs
@@ -1,18 +1,18 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using RimWorld;
using Verse;
-namespace PrisonLabor
+namespace PrisonLabor.Core.Other
{
internal class PrisonerFoodReservation
{
private static readonly Dictionary reservation = new Dictionary();
- public static bool isReserved(Thing t)
+ public static bool IsReserved(Thing t)
{
Pawn p;
reservation.TryGetValue(t, out p);
- if (p != null && p.GetRoom() == t.GetRoom() && p.needs.food.CurCategory != HungerCategory.Fed)
+ if (p != null && t!= null && p.GetRoom() == t.GetRoom() && p.needs != null && p.needs.food != null && p.needs.food.CurCategory != HungerCategory.Fed)
return true;
return false;
}
@@ -32,7 +32,7 @@ private static void clearEatenFood()
{
var removeList = new List();
foreach (var t in reservation.Keys)
- if (t == null || t.GetRoom() == null || !isReserved(t))
+ if (t == null || t.GetRoom() == null || !IsReserved(t))
removeList.Add(t);
foreach (var t in removeList)
reservation.Remove(t);
diff --git a/Source/Core/Other/TreatmentUtility.cs b/Source/Core/Other/TreatmentUtility.cs
new file mode 100644
index 00000000..18e491d8
--- /dev/null
+++ b/Source/Core/Other/TreatmentUtility.cs
@@ -0,0 +1,32 @@
+using PrisonLabor.Core.Needs;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using Verse;
+
+namespace PrisonLabor.Core.Other
+{
+ public static class TreatmentUtility
+ {
+ public static void OnApplyDamage(Pawn_HealthTracker instance, DamageInfo dinfo, bool absorbed)
+ {
+ if (dinfo.Instigator != null && !(dinfo.Instigator is Pawn))
+ return;
+
+ Pawn attacker = dinfo.Instigator as Pawn;
+ Pawn victim = (Pawn)(typeof(Pawn_HealthTracker).GetField("pawn", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(instance));
+
+ if (attacker == null || victim == null)
+ return;
+
+ if (victim.IsPrisonerOfColony && attacker.IsColonist)
+ {
+ var need = victim.needs.TryGetNeed();
+ if (need != null)
+ need.NotifyPrisonerBeaten(dinfo, absorbed);
+ }
+ }
+ }
+}
diff --git a/Source/Core/Other/TutorialProvider.cs b/Source/Core/Other/TutorialProvider.cs
new file mode 100644
index 00000000..0dc4de74
--- /dev/null
+++ b/Source/Core/Other/TutorialProvider.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.Xml;
+using Verse;
+
+namespace PrisonLabor.Core.Other
+{
+ public static class TutorialProvider
+ {
+ public struct TutorialNode
+ {
+ public string[] entries;
+ }
+
+ public static Dictionary TutorialNodes = new Dictionary();
+
+ static TutorialProvider()
+ {
+ try
+ {
+ Log.Message("PrisonLabor: tutorials initialized");
+
+ GetVersionNotesFromTutorialFeed(Properties.Resources.TutorialFeed);
+ }
+ catch (Exception e)
+ {
+ Log.Error("PrisonLabor: Failed to initialize tutorials" + e.ToString());
+ }
+ }
+
+ private static void GetVersionNotesFromTutorialFeed(string xml)
+ {
+ XmlDocument xmlDocument = new XmlDocument();
+ xmlDocument.LoadXml(xml);
+ foreach (XmlNode tutorial in xmlDocument.DocumentElement["tutorials"].ChildNodes)
+ {
+ if (tutorial.Name == "tutorial")
+ {
+ var entries = new List();
+ foreach (XmlNode item in tutorial["items"].ChildNodes)
+ entries.Add(item.InnerXml);
+
+ TutorialNodes.Add(
+ key: tutorial.Attributes["key"].Value,
+ value: new TutorialNode() { entries = entries.ToArray(), }
+ );
+ }
+ }
+ }
+ }
+}
diff --git a/Source/Tutorials.cs b/Source/Core/Other/Tutorials.cs
similarity index 71%
rename from Source/Tutorials.cs
rename to Source/Core/Other/Tutorials.cs
index ad35d50c..f64197df 100644
--- a/Source/Tutorials.cs
+++ b/Source/Core/Other/Tutorials.cs
@@ -1,25 +1,25 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
+using PrisonLabor.Core.Meta;
+using PrisonLabor.Core.Windows;
using RimWorld;
using UnityEngine;
using Verse;
-namespace PrisonLabor
+namespace PrisonLabor.Core.Other
{
- public class Tutorials
+ public static class Tutorials
{
- private static readonly ConceptDef introductionDef =
- DefDatabase.GetNamed("PrisonLabor_Indroduction", true);
+ private static readonly ConceptDef introductionDef = DefDatabase.GetNamed("PrisonLabor_Indroduction", true);
- private static readonly ConceptDef motivationDef =
- DefDatabase.GetNamed("PrisonLabor_Motivation", true);
+ private static readonly ConceptDef motivationDef = DefDatabase.GetNamed("PrisonLabor_Motivation", true);
private static readonly ConceptDef growingDef = DefDatabase.GetNamed("PrisonLabor_Growing", true);
- private static readonly ConceptDef managementDef =
- DefDatabase.GetNamed("PrisonLabor_Management", true);
+ private static readonly ConceptDef managementDef = DefDatabase.GetNamed("PrisonLabor_Management", true);
- private static readonly ConceptDef timetableDef =
- DefDatabase.GetNamed("PrisonLabor_Timetable", true);
+ private static readonly ConceptDef timetableDef = DefDatabase.GetNamed("PrisonLabor_Timetable", true);
+
+ private static readonly ConceptDef treatmentDef = DefDatabase.GetNamed("PrisonLabor_Treatment", true);
private static readonly List triggeredTutorials = new List();
private static float lastTutorTime;
@@ -36,11 +36,18 @@ public static void Apply()
PlayerKnowledgeDatabase.SetKnowledge(managementDef, 1.0f);
if (PrisonLaborPrefs.HasTutorialFlag(TutorialFlag.Timetable))
PlayerKnowledgeDatabase.SetKnowledge(timetableDef, 1.0f);
+ if (PrisonLaborPrefs.HasTutorialFlag(TutorialFlag.Treatment))
+ PlayerKnowledgeDatabase.SetKnowledge(treatmentDef, 1.0f);
}
public static void Introduction()
{
- TryActivateTutorial(introductionDef, OpportunityType.Important);
+ //TryActivateTutorial(introductionDef, OpportunityType.Important);
+ if (!PrisonLaborPrefs.HasTutorialFlag(TutorialFlag.Introduction))
+ {
+ TutorialWindow.Show("Introduction");
+ PrisonLaborPrefs.AddTutorialFlag(TutorialFlag.Introduction);
+ }
}
public static void Motivation()
@@ -63,6 +70,11 @@ public static void Growing()
TryActivateTutorial(growingDef, OpportunityType.Important);
}
+ public static void Treatment()
+ {
+ TryActivateTutorial(treatmentDef, OpportunityType.Important);
+ }
+
public static void LaborAreaWarning()
{
if (!PrisonLaborPrefs.HasTutorialFlag(TutorialFlag.LaborAreaWarning))
@@ -105,6 +117,8 @@ public static void UpdateTutorialFlags()
PrisonLaborPrefs.AddTutorialFlag(TutorialFlag.Timetable);
if (!PlayerKnowledgeDatabase.IsComplete(growingDef))
PrisonLaborPrefs.AddTutorialFlag(TutorialFlag.Growing);
+ if (!PlayerKnowledgeDatabase.IsComplete(treatmentDef))
+ PrisonLaborPrefs.AddTutorialFlag(TutorialFlag.Treatment);
}
public static void Reset()
@@ -114,6 +128,7 @@ public static void Reset()
PlayerKnowledgeDatabase.SetKnowledge(managementDef, 0.0f);
PlayerKnowledgeDatabase.SetKnowledge(timetableDef, 0.0f);
PlayerKnowledgeDatabase.SetKnowledge(growingDef, 0.0f);
+ PlayerKnowledgeDatabase.SetKnowledge(treatmentDef, 0.0f);
}
}
}
\ No newline at end of file
diff --git a/Source/Core/PrisonLaborUtility.cs b/Source/Core/PrisonLaborUtility.cs
new file mode 100644
index 00000000..c31e0d30
--- /dev/null
+++ b/Source/Core/PrisonLaborUtility.cs
@@ -0,0 +1,59 @@
+using System.Collections.Generic;
+using PrisonLabor.Constants;
+using PrisonLabor.Core.LaborArea;
+using PrisonLabor.Core.LaborWorkSettings;
+using PrisonLabor.Core.Meta;
+using RimWorld;
+using Verse;
+
+namespace PrisonLabor.Core
+{
+ internal static class PrisonLaborUtility
+ {
+ public static bool LaborEnabled(this Pawn pawn)
+ {
+ if (pawn.IsPrisoner)
+ if (pawn.guest.interactionMode == PL_DefOf.PrisonLabor_workOption || pawn.guest.interactionMode == PL_DefOf.PrisonLabor_workAndRecruitOption)
+ return true;
+
+ return false;
+ }
+
+ public static bool RecruitInLaborEnabled(Pawn pawn)
+ {
+ if (pawn.guest.interactionMode == PL_DefOf.PrisonLabor_workAndRecruitOption && pawn.guest.ScheduledForInteraction && pawn.health.capacities.CapableOf(PawnCapacityDefOf.Talking))
+ return true;
+
+ return false;
+ }
+
+ public static bool WorkTime(Pawn pawn)
+ {
+ if (pawn.needs == null || pawn.needs.food == null || pawn.needs.rest == null)
+ return false;
+ if (pawn.timetable == null)
+ return true;
+ if (pawn.timetable.CurrentAssignment == TimeAssignmentDefOf.Work)
+ return true;
+ if (pawn.timetable.CurrentAssignment == TimeAssignmentDefOf.Anything)
+ {
+ if (HealthAIUtility.ShouldSeekMedicalRest(pawn) ||
+ pawn.health.hediffSet.HasTemperatureInjury(TemperatureInjuryStage.Serious) ||
+ pawn.needs.food.CurCategory > HungerCategory.Hungry ||
+ pawn.needs.rest.CurCategory != RestCategory.Rested)
+ return false;
+ else
+ return true;
+ }
+ return false;
+ }
+
+ public static bool IsDisabledByLabor(IntVec3 pos, Pawn pawn, WorkTypeDef workType)
+ {
+ if (pos != null && pawn.Map.areaManager.Get() != null &&
+ !WorkSettings.WorkDisabled(workType))
+ return pawn.Map.areaManager.Get()[pos];
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/PrisonLaborWidgets.cs b/Source/Core/PrisonLaborWidgets.cs
similarity index 97%
rename from Source/PrisonLaborWidgets.cs
rename to Source/Core/PrisonLaborWidgets.cs
index f992ca5c..d84949b6 100644
--- a/Source/PrisonLaborWidgets.cs
+++ b/Source/Core/PrisonLaborWidgets.cs
@@ -1,11 +1,11 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Verse;
-namespace PrisonLabor
+namespace PrisonLabor.Core
{
public static class PrisonLaborWidgets
{
diff --git a/Source/Core/Settings/CleanSaveDialog.cs b/Source/Core/Settings/CleanSaveDialog.cs
new file mode 100644
index 00000000..97fc3e6c
--- /dev/null
+++ b/Source/Core/Settings/CleanSaveDialog.cs
@@ -0,0 +1,60 @@
+using PrisonLabor.Core.GameSaves;
+using PrisonLabor.Tweaks;
+using UnityEngine;
+using Verse;
+
+namespace PrisonLabor.Core.Settings
+{
+ public class CleanSaveDialog : Window
+ {
+ private string fileName;
+
+ private bool saveBackuped = false;
+
+ public CleanSaveDialog(string fileName)
+ {
+ absorbInputAroundWindow = true;
+ doCloseX = true;
+
+ windowRect = new Rect(0f, 0f, 464f, 232f);
+
+ this.fileName = fileName;
+ }
+
+ public override void DoWindowContents(Rect inRect)
+ {
+ var innerRect = new Rect(inRect.x, inRect.y, inRect.width, inRect.height).ContractedBy(10f);
+ GUI.BeginGroup(innerRect);
+
+ var messageRect = new Rect(0f, 0f, innerRect.width, innerRect.height - 90f);
+ var cancelButtonRect = new Rect((innerRect.width - 100f) / 2, innerRect.height - 40f, 100f, 40f);
+ var backupButtonRect = new Rect(innerRect.width - 110f, innerRect.height - 90f, 100f, 40f);
+ var proceedButtonRect = new Rect(innerRect.width - 110f, innerRect.height - 40f, 100f, 40f);
+
+ string dialogMessage = $"{"PrisonLabor_UpgradeSaveDialogMessage".Translate()}\n{"PrisonLabor_BackupSaveDialogMessage".Translate()} ";
+ var listing = new Listing_Standard(GameFont.Medium);
+ listing.Begin(messageRect);
+ listing.Label(dialogMessage);
+ listing.End();
+ //GUI.Label(messageRect, dialogMessage);
+
+ Text.Font = GameFont.Small;
+ if (Widgets.ButtonText(cancelButtonRect, "CancelButton".Translate()))
+ {
+ Close();
+ }
+ if (!saveBackuped && Widgets.ButtonText(backupButtonRect, "PrisonLabor_ButtonBackup".Translate(), true, false, Color.green, !saveBackuped))
+ {
+ saveBackuped = true;
+ SaveCleaner.BackupSavegame(fileName);
+ }
+ if (Widgets.ButtonText(proceedButtonRect, "PrisonLabor_ButtonProceed".Translate(), true, false, Color.red))
+ {
+ SaveCleaner.RemoveFromSave(fileName);
+ Close();
+ }
+
+ GUI.EndGroup();
+ }
+ }
+}
diff --git a/Source/Core/Settings/SelectSaveForCleaningDialog.cs b/Source/Core/Settings/SelectSaveForCleaningDialog.cs
new file mode 100644
index 00000000..3a7d59de
--- /dev/null
+++ b/Source/Core/Settings/SelectSaveForCleaningDialog.cs
@@ -0,0 +1,61 @@
+using PrisonLabor.Tweaks;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using UnityEngine;
+using Verse;
+
+namespace PrisonLabor.Core.Settings
+{
+ public class SelectSaveForCleaningDialog : Window
+ {
+ private float maxH;
+ private Vector2 position;
+ private List saves;
+
+ public SelectSaveForCleaningDialog()
+ {
+ absorbInputAroundWindow = true;
+ doCloseX = true;
+ doCloseButton = true;
+
+ saves = new List();
+ foreach (var file in GenFilePaths.AllSavedGameFiles)
+ saves.Add(Path.GetFileNameWithoutExtension(file.Name));
+ }
+
+ public override void DoWindowContents(Rect inRect)
+ {
+ var listRect = new Rect(inRect.x, inRect.y + 10f, inRect.width, inRect.height - 50f);
+ var contentRect = new Rect(0f, 0f, inRect.width - 20f, 24f * saves.Count());
+ Widgets.BeginScrollView(listRect, ref this.position, contentRect, true);
+ var listing_Standard = new Listing_Standard();
+ listing_Standard.Begin(contentRect);
+
+ foreach (var save in saves)
+ {
+ var lineHeight = Text.LineHeight;
+
+ var rect = listing_Standard.GetRect(30f);
+
+ //if (!tooltip.NullOrEmpty())
+ //{
+ // if (Mouse.IsOver(rect))
+ // Widgets.DrawHighlight(rect);
+ // TooltipHandler.TipRegion(rect, tooltip);
+ //}
+
+ if (Widgets.ButtonText(rect, save))
+ {
+ Find.WindowStack.Add(new CleanSaveDialog(save));
+ }
+ listing_Standard.Gap(listing_Standard.verticalSpacing);
+ }
+
+ maxH = listing_Standard.CurHeight;
+
+ listing_Standard.End();
+ Widgets.EndScrollView();
+ }
+ }
+}
diff --git a/Source/SelectWorkTypesDialog.cs b/Source/Core/Settings/SelectWorkTypesDialog.cs
similarity index 95%
rename from Source/SelectWorkTypesDialog.cs
rename to Source/Core/Settings/SelectWorkTypesDialog.cs
index fc6134b9..f06d1e85 100644
--- a/Source/SelectWorkTypesDialog.cs
+++ b/Source/Core/Settings/SelectWorkTypesDialog.cs
@@ -1,13 +1,14 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Linq;
+using PrisonLabor.Core.LaborWorkSettings;
using RimWorld;
using UnityEngine;
using Verse;
using Verse.Sound;
-namespace PrisonLabor
+namespace PrisonLabor.Core.Settings
{
- internal class SelectWorkTypesDialog : Window
+ public class SelectWorkTypesDialog : Window
{
private float maxH;
private Vector2 position;
diff --git a/Source/SettingsMenu.cs b/Source/Core/Settings/SettingsMenu.cs
similarity index 68%
rename from Source/SettingsMenu.cs
rename to Source/Core/Settings/SettingsMenu.cs
index 7905932c..638477cf 100644
--- a/Source/SettingsMenu.cs
+++ b/Source/Core/Settings/SettingsMenu.cs
@@ -1,9 +1,12 @@
-using RimWorld;
+using PrisonLabor.Core.LaborWorkSettings;
+using PrisonLabor.Core.Meta;
+using PrisonLabor.Core.Windows;
+using RimWorld;
using System.Collections.Generic;
using UnityEngine;
using Verse;
-namespace PrisonLabor
+namespace PrisonLabor.Core.Settings
{
[StaticConstructorOnStartup]
internal class SettingsMenu : Mod
@@ -14,8 +17,8 @@ internal class SettingsMenu : Mod
private static bool enableMotivationMechanics;
private static bool enableMotivationIcons;
private static bool enableRevolts;
+ private static bool showTreatmentHappiness;
private static bool advancedGrowing;
- private static bool disableMod;
private static int defaultInteractionMode;
private static List interactionModeList;
@@ -32,7 +35,7 @@ public static void Init()
enableMotivationMechanics = PrisonLaborPrefs.EnableMotivationMechanics;
enableMotivationIcons = PrisonLaborPrefs.EnableMotivationIcons;
enableRevolts = PrisonLaborPrefs.EnableRevolts;
- disableMod = PrisonLaborPrefs.DisableMod;
+ showTreatmentHappiness = PrisonLaborPrefs.ShowTreatmentHappiness;
interactionModeList = new List(DefDatabase.AllDefs);
defaultInteractionMode = interactionModeList.IndexOf(DefDatabase.GetNamed(PrisonLaborPrefs.DefaultInteractionMode));
@@ -43,7 +46,7 @@ public static void Init()
public override void DoSettingsWindowContents(Rect inRect)
{
var leftRect = new Rect(inRect.x, inRect.y, inRect.width * 0.65f, inRect.height);
- var rightRect = new Rect(inRect.x + inRect.width * 0.65f + 30f, inRect.y, inRect.width * 0.35f - 30f,
+ var rightRect = new Rect((int)(inRect.x + inRect.width * 0.65f + 30f), inRect.y, inRect.width * 0.35f - 30f,
inRect.height);
var listing_options = new Listing_Standard();
@@ -60,57 +63,49 @@ public override void DoSettingsWindowContents(Rect inRect)
listing_options.GapLine();
- if (!disableMod)
+ listing_options.Label("PrisonLabor_AllowedWorkTypes".Translate(), -1f);
+ listing_options.CheckboxLabeled(" " + "PrisonLabor_AllowAll".Translate(), ref allowAllWorktypes, "PrisonLabor_AllowAllWorkTypes".Translate());
+ if (!allowAllWorktypes)
+ {
+ if (listing_options.ButtonTextLabeled(" " + "PrisonLabor_AllowedWorkTypesL".Translate(), "PrisonLabor_Browse".Translate()))
+ Find.WindowStack.Add(new SelectWorkTypesDialog());
+ }
+ else
{
- listing_options.Label("PrisonLabor_AllowedWorkTypes".Translate(), -1f);
- listing_options.CheckboxLabeled(" " + "PrisonLabor_AllowAll".Translate(), ref allowAllWorktypes, "PrisonLabor_AllowAllWorkTypes".Translate());
- if (!allowAllWorktypes)
- {
- if (listing_options.ButtonTextLabeled(" " + "PrisonLabor_AllowedWorkTypesL".Translate(), "PrisonLabor_Browse".Translate()))
- Find.WindowStack.Add(new SelectWorkTypesDialog());
- }
- else
- {
- listing_options.Gap();
- }
+ listing_options.Gap();
+ }
- listing_options.GapLine();
+ listing_options.GapLine();
- listing_options.CheckboxLabeled("PrisonLabor_MotivationMechanics".Translate(), ref enableMotivationMechanics,
- "PrisonLabor_MotivationWarning".Translate());
+ listing_options.CheckboxLabeled("PrisonLabor_MotivationMechanics".Translate(), ref enableMotivationMechanics,
+ "PrisonLabor_MotivationWarning".Translate());
- listing_options.GapLine();
+ listing_options.GapLine();
- listing_options.CheckboxLabeled("PrisonLabor_MotivationIcons".Translate(), ref enableMotivationIcons,
- "PrisonLabor_MotivationIconsDesc".Translate());
+ listing_options.CheckboxLabeled("PrisonLabor_MotivationIcons".Translate(), ref enableMotivationIcons,
+ "PrisonLabor_MotivationIconsDesc".Translate());
- listing_options.GapLine();
+ listing_options.GapLine();
- listing_options.CheckboxLabeled("PrisonLabor_CanGrowAdvanced".Translate(), ref advancedGrowing,
- "PrisonLabor_CanGrowAdvancedDesc".Translate());
+ listing_options.CheckboxLabeled("PrisonLabor_CanGrowAdvanced".Translate(), ref advancedGrowing,
+ "PrisonLabor_CanGrowAdvancedDesc".Translate());
- listing_options.GapLine();
+ listing_options.GapLine();
- listing_options.CheckboxLabeled("PrisonLabor_EnableRevolts".Translate(), ref enableRevolts,
- "PrisonLabor_EnableRevoltsDesc".Translate());
- }
- else
- {
- listing_options.Gap();
- listing_options.Gap();
- listing_options.Label("PrisonLabor_RestartInfo".Translate(), -1f);
- listing_options.Label("PrisonLabor_RestartInfo2".Translate(), -1f);
- listing_options.Gap();
- listing_options.Gap();
- listing_options.Gap();
- }
+ listing_options.CheckboxLabeled("PrisonLabor_EnableRevolts".Translate(), ref enableRevolts,
+ "PrisonLabor_EnableRevoltsDesc".Translate());
+
+ listing_options.GapLine();
+
+ listing_options.CheckboxLabeled("PrisonLabor_ShowTreatmentHappiness".Translate(), ref showTreatmentHappiness,
+ "PrisonLabor_ShowTreatmentHappinessDesc".Translate());
listing_options.Gap();
listing_options.Gap();
listing_options.Gap();
- listing_options.CheckboxLabeled("PrisonLabor_DisableMod".Translate(), ref disableMod,
- "PrisonLabor_DisableModDesc".Translate());
+ if (listing_options.ButtonTextLabeled("PrisonLabor_ButtonRemoveModFromSaveDesc".Translate(), "PrisonLabor_ButtonRemoveModFromSave".Translate()))
+ Find.WindowStack.Add(new SelectSaveForCleaningDialog());
listing_options.End();
@@ -138,8 +133,13 @@ public override void DoSettingsWindowContents(Rect inRect)
if (listing_panel.ButtonText("PrisonLabor_ShowNews".Translate()))
{
- NewsDialog.showAll = true;
- NewsDialog.ForceShow();
+ NewsWindow.ShowAll = true;
+ NewsWindow.ForceShow();
+ }
+
+ if (listing_panel.ButtonText("PrisonLabor_ReplayTurorialsButton".Translate()))
+ {
+ ReplayTutorialsWindow.Show();
}
listing_panel.End();
@@ -156,12 +156,11 @@ public override void WriteSettings()
{
PrisonLaborPrefs.ShowNews = showNews;
PrisonLaborPrefs.AllowAllWorkTypes = allowAllWorktypes;
- if (!disableMod)
- PrisonLaborPrefs.EnableMotivationMechanics = enableMotivationMechanics;
+ PrisonLaborPrefs.EnableMotivationMechanics = enableMotivationMechanics;
PrisonLaborPrefs.EnableMotivationIcons = enableMotivationIcons;
PrisonLaborPrefs.EnableRevolts = enableRevolts;
+ PrisonLaborPrefs.ShowTreatmentHappiness = showTreatmentHappiness;
PrisonLaborPrefs.AdvancedGrowing = advancedGrowing;
- PrisonLaborPrefs.DisableMod = disableMod;
PrisonLaborPrefs.DefaultInteractionMode = interactionModeList[defaultInteractionMode].defName;
PrisonLaborPrefs.Save();
Log.Message("Prison Labor settings saved");
diff --git a/Source/Core/Trackers/EscapeTracker.cs b/Source/Core/Trackers/EscapeTracker.cs
new file mode 100644
index 00000000..3fbb460e
--- /dev/null
+++ b/Source/Core/Trackers/EscapeTracker.cs
@@ -0,0 +1,140 @@
+using PrisonLabor.Constants;
+using PrisonLabor.Core.BaseClasses;
+using PrisonLabor.Core.Needs;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Verse;
+
+namespace PrisonLabor.Core.Trackers
+{
+ public class EscapeTracker : IExposable
+ {
+ #region Object Access
+ /**
+ * Object Access region:
+ * This region is for ensuring that for every pawn there will be only one escape tracker.
+ * It is constructed in this way to prevent heavy modification of Pawn class (on external library).
+ */
+ private static Dictionary escapeTimers = new Dictionary();
+
+ ///
+ /// Access EscapeTracker of Pawn
+ ///
+ public static EscapeTracker Of(Pawn pawn, bool forced = false)
+ {
+ if(!pawn.IsPrisoner && !forced)
+ {
+ return null;
+ }
+ if (!escapeTimers.ContainsKey(pawn))
+ {
+ escapeTimers.Add(pawn, new EscapeTracker(pawn));
+ }
+ return escapeTimers[pawn];
+ }
+
+ public static void Save(Pawn pawn, EscapeTracker tracker)
+ {
+ if(pawn != null && tracker != null)
+ {
+ escapeTimers[pawn] = tracker;
+ }
+ }
+ #endregion
+
+ private SimpleTimer timer = new SimpleTimer();
+
+ ///
+ /// Debug information
+ ///
+ public int ReadyToRunPercentage => timer.Ticks * 100 / EscapeLevel;
+
+ ///
+ /// Attached pawn
+ ///
+ public Pawn Pawn { get; private set; }
+
+ ///
+ /// Flag that indicates whenever pawn should escape when JobGiver is trying to assign him a job
+ ///
+ public bool ReadyToEscape { get; private set; }
+
+ ///
+ /// This value is tracking whenever pawn is ready to run,
+ /// in order to count time of this state
+ ///
+ public bool CanEscape
+ {
+ get => timer.IsActive;
+
+ set
+ {
+ if (value == true)
+ timer.Start();
+ else
+ timer.Stop();
+ }
+ }
+
+ ///
+ /// This value represent how long pawn will wait until he decides to escape.
+ /// Measured in game ticks.
+ ///
+ public int EscapeLevel
+ {
+ get
+ {
+ var treatment = Pawn.needs?.TryGetNeed();
+ if (treatment == null || treatment.CurCategory <= TreatmentCategory.Bad)
+ return BGP.Escape_MinLevel;
+
+ int level = (int)(treatment.CurLevel * BGP.Escape_LevelTreatmentMultiplier) + BGP.Escape_LevelBase;
+
+ if (level < BGP.Escape_MinLevel)
+ return BGP.Escape_MinLevel;
+ else if (level > BGP.Escape_MaxLevel)
+ return BGP.Escape_MaxLevel;
+ else
+ return level;
+ }
+ }
+
+ ///
+ /// Creates new escape tracker attached to pawn
+ ///
+ ///
+ public EscapeTracker(Pawn pawn)
+ {
+ Pawn = pawn;
+ }
+
+ public void Tick()
+ {
+ // Reset all
+ if (Pawn.IsWatched())
+ {
+ // Check if state isn't reseted already
+ if (timer.Ticks != 0)
+ {
+ timer.ResetAndStop();
+ ReadyToEscape = false;
+ }
+ }
+ // Check if timer should trigger escape
+ else if (timer.Ticks >= EscapeLevel)
+ {
+ ReadyToEscape = true;
+ }
+
+ // Tick timer
+ timer.Tick();
+ }
+
+ public void ExposeData()
+ {
+ Scribe_Deep.Look(ref timer, "Timer");
+ }
+ }
+}
diff --git a/Source/Core/Trackers/InspirationTracker.cs b/Source/Core/Trackers/InspirationTracker.cs
new file mode 100644
index 00000000..263fee22
--- /dev/null
+++ b/Source/Core/Trackers/InspirationTracker.cs
@@ -0,0 +1,99 @@
+using PrisonLabor.Constants;
+using System;
+using System.Collections.Generic;
+using Verse;
+
+namespace PrisonLabor.Core.Trackers
+{
+ internal static class InspirationTracker
+ {
+ public static Dictionary> inspirationValues = new Dictionary>();
+
+ ///
+ /// Check if pawn is watched(supervised) by a Jailor
+ ///
+ public static bool IsWatched(this Pawn pawn)
+ {
+ Dictionary iValues;
+ float value;
+
+ var map = pawn?.Map;
+ lock (inspirationValues)
+ {
+ if (map != null && inspirationValues.TryGetValue(map, out iValues))
+ {
+ if (iValues.TryGetValue(pawn, out value))
+ {
+ return value != 0;
+ }
+ }
+ }
+ return false;
+ }
+
+ public static float GetInsiprationValue(Pawn pawn, bool refresh = false)
+ {
+ var map = pawn.Map;
+ if (!inspirationValues.ContainsKey(map) || !inspirationValues[map].ContainsKey(pawn))
+ {
+ Log.Message("PrisonLabor Warning: InspirationTracker.GetInsipirationValue() didn't find map or pawn. Maybe it was called before calculating values.");
+ return 0;
+ }
+ lock (inspirationValues)
+ {
+ return inspirationValues[map][pawn];
+ }
+ }
+
+ public static void Calculate(Map map)
+ {
+ lock (inspirationValues)
+ {
+ var wardens = new List();
+ wardens.AddRange(map.mapPawns.FreeColonists);
+ var prisoners = new List();
+ prisoners.AddRange(map.mapPawns.PrisonersOfColony);
+
+ Dictionary mapCalculations;
+ if (inspirationValues.TryGetValue(map, out mapCalculations))
+ mapCalculations.Clear();
+ else
+ inspirationValues[map] = mapCalculations = new Dictionary();
+
+ foreach (var prisoner in prisoners)
+ {
+ mapCalculations[prisoner] = 0f;
+ }
+
+ var inRange = new Dictionary();
+ foreach (var warden in wardens)
+ {
+ inRange.Clear();
+ foreach (var prisoner in prisoners)
+ {
+ float distance = warden.Position.DistanceTo(prisoner.Position);
+ if (distance < BGP.InpirationRange && prisoner.GetRoom() == warden.GetRoom())
+ inRange.Add(prisoner, distance);
+ }
+
+ var watchedPawns = new List(inRange.Keys);
+ float points;
+ if (inRange.Count > BGP.WardenCapacity)
+ {
+ watchedPawns.Sort(new Comparison((x, y) => inRange[x].CompareTo(inRange[y])));
+ points = BGP.InspireRate / BGP.WardenCapacity;
+ }
+ else
+ {
+ points = BGP.InspireRate / inRange.Count;
+ }
+
+ for (int i = 0; i < watchedPawns.Count && i < BGP.WardenCapacity; i++)
+ {
+ mapCalculations[watchedPawns[i]] += points;
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/Core/Windows/NewsWindow.cs b/Source/Core/Windows/NewsWindow.cs
new file mode 100644
index 00000000..e863e2a9
--- /dev/null
+++ b/Source/Core/Windows/NewsWindow.cs
@@ -0,0 +1,79 @@
+using PrisonLabor.Core.Meta;
+using PrisonLabor.Core.Other;
+using System.Collections.Generic;
+using UnityEngine;
+using Verse;
+
+namespace PrisonLabor.Core.Windows
+{
+ public class NewsWindow : Window
+ {
+ // Static Properties
+ public static bool AutoShow { get; set; }
+
+ public static bool ShowAll { get; set; }
+
+ public static string LastVersionString { get; set; }
+
+ // Fields
+ private string[] entries;
+ private Vector2 position;
+
+ public NewsWindow()
+ {
+ doCloseButton = true;
+ doCloseX = true;
+ Init();
+ }
+
+ public void Init()
+ {
+ var entriesList = new List();
+
+ if (ShowAll)
+ {
+ foreach (var patch in NewsProvider.allVersionNotes)
+ {
+ entriesList.AddRange(patch.entries);
+ }
+ }
+ else
+ {
+ foreach (var patch in NewsProvider.NewsAfterVersion(LastVersionString))
+ {
+ entriesList.AddRange(patch.entries);
+ }
+ }
+
+ entries = entriesList.ToArray();
+ }
+
+ public static void TryShow()
+ {
+ if (AutoShow && PrisonLaborPrefs.ShowNews)
+ {
+ Find.WindowStack.Add(new NewsWindow());
+ PrisonLaborPrefs.LastVersion = PrisonLaborPrefs.Version;
+ PrisonLaborPrefs.Save();
+ AutoShow = false;
+ }
+ }
+
+ public static void ForceShow()
+ {
+ Find.WindowStack.Add(new NewsWindow());
+ PrisonLaborPrefs.LastVersion = PrisonLaborPrefs.Version;
+ PrisonLaborPrefs.Save();
+ AutoShow = false;
+ }
+
+ public override void DoWindowContents(Rect inRect)
+ {
+ var displayRect = new Rect(inRect.x, inRect.y, inRect.width, inRect.height - 50f);
+
+ var richListing = new GUI_Components.RichListing();
+ richListing.PreRender(displayRect, entries);
+ richListing.OnGui(ref position);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/Core/Windows/ReplayTutorialsWindow.cs b/Source/Core/Windows/ReplayTutorialsWindow.cs
new file mode 100644
index 00000000..f9b39fab
--- /dev/null
+++ b/Source/Core/Windows/ReplayTutorialsWindow.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using UnityEngine;
+using Verse;
+
+namespace PrisonLabor.Core.Windows
+{
+ public class ReplayTutorialsWindow : Window
+ {
+ public ReplayTutorialsWindow()
+ {
+ doCloseButton = true;
+ }
+
+ public override void DoWindowContents(Rect inRect)
+ {
+ var listing_panel = new Listing_Standard();
+
+ listing_panel.Begin(inRect);
+
+ if (listing_panel.ButtonTextLabeled("Introduction", "PrisonLabor_ShowTutorialButton".Translate()))
+ {
+ TutorialWindow.Show("Introduction");
+ }
+
+ if (listing_panel.ButtonTextLabeled("Labor Area", "PrisonLabor_ShowTutorialButton".Translate()))
+ {
+ TutorialWindow.Show("LaborArea");
+ }
+
+ if (listing_panel.ButtonTextLabeled("Treatment system", "PrisonLabor_ShowTutorialButton".Translate()))
+ {
+ TutorialWindow.Show("Treatment");
+ }
+
+ listing_panel.End();
+ }
+
+ public static void Show()
+ {
+ Find.WindowStack.Add(new ReplayTutorialsWindow());
+ }
+ }
+}
diff --git a/Source/Core/Windows/TutorialWindow.cs b/Source/Core/Windows/TutorialWindow.cs
new file mode 100644
index 00000000..42ec6d11
--- /dev/null
+++ b/Source/Core/Windows/TutorialWindow.cs
@@ -0,0 +1,36 @@
+using PrisonLabor.Core.Other;
+using UnityEngine;
+using Verse;
+
+namespace PrisonLabor.Core.Windows
+{
+ public class TutorialWindow : Window
+ {
+ // Fields
+ private string[] entries;
+ private Vector2 position;
+
+ public override Vector2 InitialSize => new Vector2(800, 700);
+
+ public TutorialWindow(string tutorialKey)
+ {
+ entries = TutorialProvider.TutorialNodes[tutorialKey].entries;
+
+ doCloseButton = true;
+ }
+
+ public override void DoWindowContents(Rect inRect)
+ {
+ var displayRect = new Rect(inRect.x, inRect.y, inRect.width, inRect.height - 50f);
+
+ var richListing = new GUI_Components.RichListing();
+ richListing.PreRender(displayRect, entries);
+ richListing.OnGui(ref position);
+ }
+
+ public static void Show(string key)
+ {
+ Find.WindowStack.Add(new TutorialWindow(key));
+ }
+ }
+}
diff --git a/Source/HarmonyPatches/DevTools.cs b/Source/HarmonyPatches/DevTools.cs
new file mode 100644
index 00000000..a69276f5
--- /dev/null
+++ b/Source/HarmonyPatches/DevTools.cs
@@ -0,0 +1,108 @@
+using Harmony;
+using PrisonLabor.Core.Needs;
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using Verse;
+
+namespace PrisonLabor.HarmonyPatches
+{
+ ///
+ /// This patch is adding Prison Labor dev tools
+ ///
+ [HarmonyPatch(typeof(Dialog_DebugActionsMenu), "DoListingItems_MapTools")]
+ public static class DevTools
+ {
+ public static bool LogEscapeUtilityEnabled { get; set; }
+
+ static void Postfix(Dialog_DebugActionsMenu __instance)
+ {
+ var menu = __instance;
+ menu.DoLabel("Prison Labor Tools:");
+
+ // Increase motivation
+ menu.DebugToolMapForPawns("Tool: Motivation +10%", delegate (Pawn p)
+ {
+ if (p.needs.TryGetNeed() != null)
+ {
+ OffsetNeed(p, Need_Motivation.Def, 0.1f);
+ }
+ });
+ // Decrease motivation
+ menu.DebugToolMapForPawns("Tool: Motivation -10%", delegate (Pawn p)
+ {
+ if (p.needs.TryGetNeed() != null)
+ {
+ OffsetNeed(p, Need_Motivation.Def, -0.1f);
+ }
+ });
+ // Increase treatment
+ menu.DebugToolMapForPawns("Tool: Treatment +10%", delegate (Pawn p)
+ {
+ if (p.needs.TryGetNeed() != null)
+ {
+ OffsetNeed(p, Need_Treatment.Def, 0.1f);
+ }
+ });
+ // Decrease treatment
+ menu.DebugToolMapForPawns("Tool: Treatment -10%", delegate (Pawn p)
+ {
+ if (p.needs.TryGetNeed() != null)
+ {
+ OffsetNeed(p, Need_Treatment.Def, -0.1f);
+ }
+ });
+ // Turn into prisoner
+ menu.DebugToolMapForPawns("Tool: Turn into prisoner", delegate (Pawn p)
+ {
+ p.guest.SetGuestStatus(Faction.OfPlayer, true);
+ });
+ // Set free
+ menu.DebugToolMapForPawns("Tool: Set free", delegate (Pawn p)
+ {
+ p.guest.SetGuestStatus(null, false);
+ });
+ // Set free
+ menu.DebugAction("Toggle logging escape utility", ()=>
+ {
+ LogEscapeUtilityEnabled = !LogEscapeUtilityEnabled;
+ });
+ }
+
+ #region Utilities
+ static void DoLabel(this Dialog_DebugActionsMenu instance, string label)
+ {
+ var method = typeof(Dialog_DebugActionsMenu).GetMethod("DoLabel", BindingFlags.Instance | BindingFlags.NonPublic);
+ method.Invoke(instance, new object[] { label });
+ }
+
+ static void DebugAction(this Dialog_DebugActionsMenu instance, string label, Action action)
+ {
+ var method = typeof(Dialog_DebugActionsMenu).GetMethod("DebugAction", BindingFlags.Instance | BindingFlags.NonPublic);
+ method.Invoke(instance, new object[] { label, action });
+ }
+
+ static void DebugToolMapForPawns(this Dialog_DebugActionsMenu instance, string label, Action pawnAction)
+ {
+ var method = typeof(Dialog_DebugActionsMenu).GetMethod("DebugToolMapForPawns", BindingFlags.Instance | BindingFlags.NonPublic);
+ method.Invoke(instance, new object[] { label, pawnAction });
+ }
+
+ static void OffsetNeed(Pawn pawn, NeedDef nd, float offsetPct)
+ {
+ if (pawn != null)
+ {
+ Need need = pawn.needs.TryGetNeed(nd);
+ if (need != null)
+ {
+ need.CurLevel += offsetPct * need.MaxLevel;
+ pawn.Drawer.Notify_DebugAffected();
+ }
+ }
+ }
+ #endregion
+ }
+}
diff --git a/Source/HarmonyPatches/HPatcher.cs b/Source/HarmonyPatches/HPatcher.cs
index ced36df2..dbf07106 100644
--- a/Source/HarmonyPatches/HPatcher.cs
+++ b/Source/HarmonyPatches/HPatcher.cs
@@ -1,7 +1,6 @@
using System;
using System.Reflection;
using Harmony;
-using PrisonLabor.Harmony;
using Verse;
using System.Collections.Generic;
using System.IO;
@@ -28,15 +27,15 @@ public static void Init()
harmony.PatchAll(Assembly.GetExecutingAssembly());
// Print out not completed methods
- foreach(var f in fragments.Keys)
+ foreach (var f in fragments.Keys)
{
- if(!fragments[f])
+ if (!fragments[f])
Log.Error($"PrisonLaborWarning: Harmony patch failed to find \"{f}\" fragment.");
}
}
catch (Exception e)
{
- Log.Error($"PrisonLaborException: failed to proceed harmony patches: {e.Message}");
+ Log.Error($"PrisonLaborException: failed to proceed harmony patches: {e.InnerException.Message}");
}
// SECTION - Patches with references in method
@@ -49,7 +48,7 @@ public static void Init()
typeof(IntVec3), typeof(ThingPlaceMode), typeof(Thing).MakeByRefType(),
typeof(Action)
}),
- new HarmonyMethod(null), new HarmonyMethod(typeof(ForibiddenDropPatch).GetMethod("Postfix")));
+ new HarmonyMethod(null), new HarmonyMethod(typeof(Patches_PermissionFix.ForibiddenDropPatch).GetMethod("Postfix")));
harmony.Patch(
typeof(Pawn_CarryTracker).GetMethod("TryDropCarriedThing",
new[]
@@ -57,11 +56,11 @@ public static void Init()
typeof(IntVec3), typeof(int), typeof(ThingPlaceMode), typeof(Thing).MakeByRefType(),
typeof(Action)
}),
- new HarmonyMethod(null), new HarmonyMethod(typeof(ForibiddenDropPatch).GetMethod("Postfix2")));
+ new HarmonyMethod(null), new HarmonyMethod(typeof(Patches_PermissionFix.ForibiddenDropPatch).GetMethod("Postfix2")));
}
catch (Exception e)
{
- Log.Error($"PrisonLaborException: failed to proceed harmony patches (reference section): {e.Message}");
+ Log.Error($"PrisonLaborException: failed to proceed harmony patches (reference section): {e.InnerException.Message}");
}
}
@@ -106,7 +105,7 @@ public static void CreateDebugFileOnDesktop(string fileName, IEnumerable
///
///
- public static bool IsFragment(OpCode[] opCodes, String[] operands, CodeInstruction instr, ref int step, string fragmentName)
+ public static bool IsFragment(OpCode[] opCodes, String[] operands, CodeInstruction instr, ref int step, string fragmentName, bool perfectMatch = true)
{
if (opCodes.Length != operands.Length)
{
@@ -121,7 +120,8 @@ public static bool IsFragment(OpCode[] opCodes, String[] operands, CodeInstructi
var finalStep = opCodes.Length;
- if (instr.opcode == opCodes[step] && (instr.operand == null || instr.operand.ToString() == operands[step]))
+
+ if (InstructionMatching(instr, opCodes[step], operands[step], perfectMatch))
step++;
else
step = 0;
@@ -143,7 +143,7 @@ public static bool IsFragment(OpCode[] opCodes, String[] operands, CodeInstructi
///
///
///
- public static object FindOperandAfter(OpCode[] opCodes, String[] operands, IEnumerable instr)
+ public static object FindOperandAfter(OpCode[] opCodes, String[] operands, IEnumerable instr, bool perfectMatch = true)
{
if (opCodes.Length != operands.Length)
{
@@ -156,7 +156,7 @@ public static object FindOperandAfter(OpCode[] opCodes, String[] operands, IEnum
int step = 0;
foreach (var ci in instr)
{
- if (ci.opcode == opCodes[step] && (ci.operand == null || ci.operand.ToString() == operands[step]))
+ if (InstructionMatching(ci, opCodes[step], operands[step], perfectMatch))
step++;
else
step = 0;
@@ -168,5 +168,16 @@ public static object FindOperandAfter(OpCode[] opCodes, String[] operands, IEnum
Log.Error("PrisonLaborException: FindOperandAfter() didn't find any lines. Trace:" + new StackTrace());
return null;
}
+
+ private static bool InstructionMatching(CodeInstruction instr, OpCode opCode, string operand, bool perfectMatch)
+ {
+ bool matchingOpCodes = instr.opcode == opCode;
+ bool noOperands = instr.operand == null || string.IsNullOrEmpty(operand);
+ bool matchingOperands;
+ if (perfectMatch) matchingOperands = instr.operand != null && instr.operand.ToString() == operand;
+ else matchingOperands = instr.operand != null && instr.operand.ToString().Contains(operand);
+
+ return matchingOpCodes && (noOperands || matchingOperands);
+ }
}
}
diff --git a/Source/HarmonyPatches/Patch_RemoveHediffIfDisabled.cs b/Source/HarmonyPatches/Patch_RemoveHediffIfDisabled.cs
deleted file mode 100644
index 404aa036..00000000
--- a/Source/HarmonyPatches/Patch_RemoveHediffIfDisabled.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using Harmony;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Verse;
-
-namespace PrisonLabor.HarmonyPatches
-{
- [HarmonyPatch(typeof(Pawn))]
- [HarmonyPatch("ExposeData")]
- class Patch_RemoveHediffIfDisabled
- {
- private static void Prefix(Pawn __instance)
- {
- if (PrisonLaborPrefs.DisableMod)
- {
- if (__instance.health != null && __instance.health.hediffSet != null)
- {
- var hediff = __instance.health.hediffSet.GetFirstHediffOfDef(DefDatabase.GetNamed("PrisonLabor_PrisonerChains"), false);
- if (hediff != null)
- __instance.health.RemoveHediff(hediff);
- }
- }
- }
- }
-}
diff --git a/Source/HarmonyPatches/Patch_RemoveLaborAreaIfDisabled.cs b/Source/HarmonyPatches/Patch_RemoveLaborAreaIfDisabled.cs
deleted file mode 100644
index bbb72cf0..00000000
--- a/Source/HarmonyPatches/Patch_RemoveLaborAreaIfDisabled.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using Harmony;
-using Verse;
-
-namespace PrisonLabor.HarmonyPatches
-{
- [HarmonyPatch(typeof(AreaManager))]
- [HarmonyPatch("ExposeData")]
- internal class Patch_RemoveLaborAreaIfDisabled
- {
- private static void Prefix(AreaManager __instance)
- {
- if (PrisonLaborPrefs.DisableMod)
- {
- __instance.AllAreas.RemoveAll(area => area is Area_Labor);
- }
- }
- }
-}
diff --git a/Source/HarmonyPatches/Patch_ShowNews.cs b/Source/HarmonyPatches/Patch_ShowNews.cs
deleted file mode 100644
index 685a4ac5..00000000
--- a/Source/HarmonyPatches/Patch_ShowNews.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System;
-using Harmony;
-using Verse;
-
-namespace PrisonLabor.HarmonyPatches
-{
- [HarmonyPatch(typeof(Map))]
- [HarmonyPatch("FinalizeInit")]
- [HarmonyPatch(new Type[] { })]
- internal class Patch_ShowNews
- {
- private static void Postfix()
- {
- NewsDialog.TryShow();
- }
- }
-}
\ No newline at end of file
diff --git a/Source/HarmonyPatches/Patches_AssignBed/Patch_AssignPrisonersToBed.cs b/Source/HarmonyPatches/Patches_AssignBed/Patch_AssignPrisonersToBed.cs
new file mode 100644
index 00000000..5e4810af
--- /dev/null
+++ b/Source/HarmonyPatches/Patches_AssignBed/Patch_AssignPrisonersToBed.cs
@@ -0,0 +1,160 @@
+using Harmony;
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Reflection.Emit;
+using UnityEngine;
+using Verse;
+using Verse.AI;
+
+namespace PrisonLabor.HarmonyPatches.Patches_AssignBed
+{
+ [HarmonyPatch(typeof(Building_Bed))]
+ [HarmonyPatch(nameof(Building_Bed.GetGizmos))]
+ static class Patch_AssignPrisonersToBed
+ {
+ static IEnumerable Transpiler(ILGenerator gen, MethodBase mBase, IEnumerable instructions)
+ {
+ foreach (var instr in instructions)
+ {
+ if (instr.opcode == OpCodes.Ret)
+ {
+ yield return new CodeInstruction(OpCodes.Ldarg_0);
+ yield return new CodeInstruction(OpCodes.Call, typeof(Patch_AssignPrisonersToBed).GetMethod(nameof(NewGizmos)));
+ }
+ yield return instr;
+ }
+ }
+
+ public static IEnumerable NewGizmos(IEnumerable gizmos, Building_Bed bed)
+ {
+ foreach (var gizmo in gizmos)
+ yield return gizmo;
+
+ if (bed.ForPrisoners)
+ {
+ yield return new Command_Action()
+ {
+ defaultLabel = "CommandBedSetOwnerLabel".Translate(),
+ defaultDesc = "CommandBedSetOwnerDesc".Translate(),
+ icon = ContentFinder.Get("ui/commands/AssignOwner", true),
+ action = new Action(() => Find.WindowStack.Add(new Dialog_AssignBuildingOwner(bed))),
+ };
+ }
+ }
+ }
+
+
+ [HarmonyPatch(typeof(Building_Bed))]
+ [HarmonyPatch("get_" + nameof(Building_Bed.AssigningCandidates))]
+ static class Patch_MakePrisonersCandidates
+ {
+ static IEnumerable Transpiler(ILGenerator gen, MethodBase mBase, IEnumerable instructions)
+ {
+ foreach (var instr in instructions)
+ {
+ if (instr.opcode == OpCodes.Ret)
+ {
+ yield return new CodeInstruction(OpCodes.Ldarg_0);
+ yield return new CodeInstruction(OpCodes.Call, typeof(Patch_MakePrisonersCandidates).GetMethod(nameof(NewCandidates)));
+ }
+ yield return instr;
+ }
+ }
+
+ public static IEnumerable NewCandidates(IEnumerable pawns, Building_Bed bed)
+ {
+ if (!bed.ForPrisoners)
+ {
+ foreach (var pawn in pawns)
+ yield return pawn;
+ }
+ else
+ {
+ foreach (var pawn in bed.Map.mapPawns.PrisonersOfColony)
+ yield return pawn;
+ }
+ }
+ }
+
+
+ [HarmonyPatch(typeof(WorkGiver_Warden_TakeToBed))]
+ [HarmonyPatch("TakeToPreferredBedJob")]
+ static class Patch_TakePrisonersToOwnedBed
+ {
+ /* === Orignal code Look-up===
+ *
+ * if (RestUtility.FindBedFor(prisoner, prisoner, true, true, false) != null)
+ * {
+ * return null;
+ * }
+ *
+ * === CIL Instructions ===
+ *
+ * ldarg.1 | | Label 2
+ * ldarg.1 | | no labels
+ * ldc.i4.1 | | no labels
+ * ldc.i4.1 | | no labels
+ * ldc.i4.0 | | no labels
+ * call | RimWorld.Building_Bed FindBedFor(Verse.Pawn, Verse.Pawn, Boolean, Boolean, Boolean) | no labels
+ * brfalse | Label 3 | no labels
+ */
+
+ static IEnumerable Transpiler(ILGenerator gen, MethodBase mBase, IEnumerable instructions)
+ {
+ OpCode[] opCodes1 =
+{
+ OpCodes.Ldarg_1,
+ OpCodes.Ldarg_1,
+ OpCodes.Ldc_I4_1,
+ OpCodes.Ldc_I4_1,
+ OpCodes.Ldc_I4_0,
+ OpCodes.Call,
+ OpCodes.Brfalse,
+ };
+ string[] operands1 =
+ {
+ "",
+ "",
+ "",
+ "",
+ "",
+ "RimWorld.Building_Bed FindBedFor(Verse.Pawn, Verse.Pawn, Boolean, Boolean, Boolean)",
+ "System.Reflection.Emit.Label",
+ };
+ int step1 = 0;
+
+ var label_OriginalBranch = gen.DefineLabel();
+
+ foreach (var instr in instructions)
+ {
+ if (HPatcher.IsFragment(opCodes1, operands1, instr, ref step1, nameof(Patch_TakePrisonersToOwnedBed), true))
+ {
+ yield return new CodeInstruction(OpCodes.Ldarg_1);
+ yield return new CodeInstruction(OpCodes.Call, typeof(Patch_TakePrisonersToOwnedBed).GetMethod(nameof(HaveOwnedBed)));
+ yield return new CodeInstruction(OpCodes.Brfalse, label_OriginalBranch);
+ yield return new CodeInstruction(OpCodes.Pop);
+ yield return new CodeInstruction(OpCodes.Ldarg_1);
+ yield return new CodeInstruction(OpCodes.Call, typeof(Patch_TakePrisonersToOwnedBed).GetMethod(nameof(CanReachBed)));
+ yield return new CodeInstruction(OpCodes.Brfalse, instr.operand);
+ yield return new CodeInstruction(OpCodes.Ldnull);
+ yield return new CodeInstruction(OpCodes.Ret);
+
+ instr.labels.Add(label_OriginalBranch);
+ }
+ yield return instr;
+ }
+ }
+
+ public static bool HaveOwnedBed(Pawn pawn)
+ {
+ return pawn.ownership != null && pawn.ownership.OwnedBed != null;
+ }
+
+ public static bool CanReachBed(Pawn pawn)
+ {
+ return pawn.CanReach(pawn.ownership.OwnedBed, PathEndMode.OnCell, Danger.Some);
+ }
+ }
+}
diff --git a/Source/HarmonyPatches/Patch_BillPrevention.cs b/Source/HarmonyPatches/Patches_BillAssignation/Patch_BillPrevention.cs
similarity index 90%
rename from Source/HarmonyPatches/Patch_BillPrevention.cs
rename to Source/HarmonyPatches/Patches_BillAssignation/Patch_BillPrevention.cs
index 5caf8781..1bb58de0 100644
--- a/Source/HarmonyPatches/Patch_BillPrevention.cs
+++ b/Source/HarmonyPatches/Patches_BillAssignation/Patch_BillPrevention.cs
@@ -1,4 +1,4 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using Harmony;
@@ -6,8 +6,9 @@
using Verse;
using System;
using System.IO;
+using PrisonLabor.Core.BillAssignation;
-namespace PrisonLabor.HarmonyPatches
+namespace PrisonLabor.HarmonyPatches.Patches_BillAssignation
{
[HarmonyPatch(typeof(WorkGiver_DoBill))]
[HarmonyPatch("StartOrResumeBillJob")]
@@ -63,7 +64,7 @@ private static IEnumerable Transpiler(ILGenerator gen, MethodBa
{
yield return new CodeInstruction(OpCodes.Ldarg_1);
yield return new CodeInstruction(OpCodes.Ldloc_1);
- yield return new CodeInstruction(OpCodes.Call, typeof(Patch_BillPrevention).GetMethod("IsForCertainGroup"));
+ yield return new CodeInstruction(OpCodes.Call, typeof(Patch_BillPrevention).GetMethod(nameof(IsForCertainGroup)));
yield return new CodeInstruction(OpCodes.Brfalse, label);
}
}
@@ -71,7 +72,7 @@ private static IEnumerable Transpiler(ILGenerator gen, MethodBa
public static bool IsForCertainGroup(Pawn pawn, Bill bill)
{
- var group = BillUtility.IsFor(bill);
+ var group = BillAssignationUtility.IsFor(bill);
if (group == GroupMode.ColonyOnly)
return true;
if (group == GroupMode.ColonistsOnly && !pawn.IsPrisoner)
diff --git a/Source/HarmonyPatches/Patch_ExposeBillGroup.cs b/Source/HarmonyPatches/Patches_BillAssignation/Patch_ExposeBillGroup.cs
similarity index 55%
rename from Source/HarmonyPatches/Patch_ExposeBillGroup.cs
rename to Source/HarmonyPatches/Patches_BillAssignation/Patch_ExposeBillGroup.cs
index be05b353..84b65e43 100644
--- a/Source/HarmonyPatches/Patch_ExposeBillGroup.cs
+++ b/Source/HarmonyPatches/Patches_BillAssignation/Patch_ExposeBillGroup.cs
@@ -1,8 +1,10 @@
-using System;
+using System;
using Harmony;
+using PrisonLabor.Core.BillAssignation;
+using PrisonLabor.Core.Meta;
using RimWorld;
-namespace PrisonLabor.HarmonyPatches
+namespace PrisonLabor.HarmonyPatches.Patches_BillAssignation
{
[HarmonyPatch(typeof(Bill))]
[HarmonyPatch("ExposeData")]
@@ -11,10 +13,7 @@ internal class Patch_ExposeBillGroup
{
private static void Postfix(Bill __instance)
{
- if (PrisonLaborPrefs.DisableMod)
- return;
-
- BillUtility.GetData(__instance).ExposeData();
+ BillAssignationUtility.GetData(__instance).ExposeData();
}
}
}
\ No newline at end of file
diff --git a/Source/HarmonyPatches/Patch_RemoveBillFromUtility.cs b/Source/HarmonyPatches/Patches_BillAssignation/Patch_RemoveBillFromUtility.cs
similarity index 60%
rename from Source/HarmonyPatches/Patch_RemoveBillFromUtility.cs
rename to Source/HarmonyPatches/Patches_BillAssignation/Patch_RemoveBillFromUtility.cs
index b4e54c80..c9d91e0a 100644
--- a/Source/HarmonyPatches/Patch_RemoveBillFromUtility.cs
+++ b/Source/HarmonyPatches/Patches_BillAssignation/Patch_RemoveBillFromUtility.cs
@@ -1,7 +1,8 @@
-using Harmony;
+using Harmony;
+using PrisonLabor.Core.BillAssignation;
using RimWorld;
-namespace PrisonLabor.HarmonyPatches
+namespace PrisonLabor.HarmonyPatches.Patches_BillAssignation
{
[HarmonyPatch(typeof(BillStack))]
[HarmonyPatch("Delete")]
@@ -10,7 +11,7 @@ internal class Patch_RemoveBillFromUtility
{
public static void Postfix(Bill bill)
{
- BillUtility.Remove(bill);
+ BillAssignationUtility.Remove(bill);
}
}
}
\ No newline at end of file
diff --git a/Source/HarmonyPatches/Patches_Construction/EnableConstructionFinishFrames.cs b/Source/HarmonyPatches/Patches_Construction/EnableConstructionFinishFrames.cs
new file mode 100644
index 00000000..fa5a37d6
--- /dev/null
+++ b/Source/HarmonyPatches/Patches_Construction/EnableConstructionFinishFrames.cs
@@ -0,0 +1,65 @@
+using Harmony;
+using RimWorld;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Reflection.Emit;
+using Verse;
+
+namespace PrisonLabor.HarmonyPatches.Patches_Construction
+{
+ [HarmonyPatch(typeof(WorkGiver_ConstructFinishFrames))]
+ [HarmonyPatch(nameof(WorkGiver_ConstructFinishFrames.JobOnThing))]
+ static class EnableConstructionFinishFrames
+ {
+ /* === Orignal code Look-up===
+ *
+ * if (t.Faction != pawn.Faction)
+ * {
+ * return false;
+ * }
+ *
+ * === CIL Instructions ===
+ *
+ * ldarg.2 | | no labels
+ * callvirt | RimWorld.Faction get_Faction() | no labels
+ * ldarg.1 | | no labels
+ * callvirt | RimWorld.Faction get_Faction() | no labels
+ * beq | Label 1 | no labels
+ * ldc.i4.0 | | no labels
+ * ret | | no labels
+ * ldarg.2 | | Label 1
+ */
+
+ static IEnumerable Transpiler(ILGenerator gen, MethodBase mBase, IEnumerable instructions)
+ {
+ //find label to jump
+ OpCode[] opCodes1 =
+ {
+ OpCodes.Ldarg_2,
+ OpCodes.Callvirt,
+ OpCodes.Ldarg_1,
+ OpCodes.Callvirt,
+ OpCodes.Beq,
+ };
+ string[] operands1 =
+ {
+ "",
+ "RimWorld.Faction get_Faction()",
+ "",
+ "RimWorld.Faction get_Faction()",
+ "System.Reflection.Emit.Label",
+ };
+ var label = HPatcher.FindOperandAfter(opCodes1, operands1, instructions, true);
+
+ //Add If(pawn.IsPrisonerOfColony) {jump next condition}
+ yield return new CodeInstruction(OpCodes.Ldarg_1);
+ yield return new CodeInstruction(OpCodes.Callvirt, typeof(Pawn).GetProperty(nameof(Pawn.IsPrisoner)).GetGetMethod());
+ yield return new CodeInstruction(OpCodes.Brtrue, label);
+
+ foreach (var instr in instructions)
+ {
+ yield return instr;
+ }
+ }
+ }
+}
diff --git a/Source/HarmonyPatches/Patches_DeepDrill/EnableDeepDrillsToPrisoners.cs b/Source/HarmonyPatches/Patches_DeepDrill/EnableDeepDrillsToPrisoners.cs
new file mode 100644
index 00000000..6584a152
--- /dev/null
+++ b/Source/HarmonyPatches/Patches_DeepDrill/EnableDeepDrillsToPrisoners.cs
@@ -0,0 +1,68 @@
+using Harmony;
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Text;
+using Verse;
+
+namespace PrisonLabor.HarmonyPatches.Patches_DeepDrill
+{
+ [HarmonyPatch(typeof(WorkGiver_DeepDrill))]
+ [HarmonyPatch(nameof(WorkGiver_DeepDrill.HasJobOnThing))]
+ static class EnableDeepDrillsToPrisoners
+ {
+ /* === Orignal code Look-up===
+ *
+ * if (t.Faction != pawn.Faction)
+ * {
+ * return false;
+ * }
+ *
+ * === CIL Instructions ===
+ *
+ * ldarg.2 | | no labels
+ * callvirt | RimWorld.Faction get_Faction() | no labels
+ * ldarg.1 | | no labels
+ * callvirt | RimWorld.Faction get_Faction() | no labels
+ * beq | Label 1 | no labels
+ * ldc.i4.0 | | no labels
+ * ret | | no labels
+ * ldarg.2 | | Label 1
+ */
+
+ static IEnumerable Transpiler(ILGenerator gen, MethodBase mBase, IEnumerable instructions)
+ {
+ //find label to jump
+ OpCode[] opCodes1 =
+ {
+ OpCodes.Ldarg_2,
+ OpCodes.Callvirt,
+ OpCodes.Ldarg_1,
+ OpCodes.Callvirt,
+ OpCodes.Beq,
+ };
+ string[] operands1 =
+ {
+ "",
+ "RimWorld.Faction get_Faction()",
+ "",
+ "RimWorld.Faction get_Faction()",
+ "System.Reflection.Emit.Label",
+ };
+ var label = HPatcher.FindOperandAfter(opCodes1, operands1, instructions, true);
+
+ //Add If(pawn.IsPrisonerOfColony) {jump next condition}
+ yield return new CodeInstruction(OpCodes.Ldarg_1);
+ yield return new CodeInstruction(OpCodes.Callvirt, typeof(Pawn).GetProperty(nameof(Pawn.IsPrisoner)).GetGetMethod());
+ yield return new CodeInstruction(OpCodes.Brtrue, label);
+
+ foreach (var instr in instructions)
+ {
+ yield return instr;
+ }
+ }
+ }
+}
diff --git a/Source/HarmonyPatches/Patches_Escaping/Patch_EscapeTracker.cs b/Source/HarmonyPatches/Patches_Escaping/Patch_EscapeTracker.cs
new file mode 100644
index 00000000..4e500692
--- /dev/null
+++ b/Source/HarmonyPatches/Patches_Escaping/Patch_EscapeTracker.cs
@@ -0,0 +1,38 @@
+using Harmony;
+using PrisonLabor.Core.Trackers;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Verse;
+
+namespace PrisonLabor.HarmonyPatches.Patches_Escaping
+{
+ static class Patch_EscapeTracker
+ {
+ [HarmonyPatch(typeof(Pawn))]
+ [HarmonyPatch(nameof(Pawn.ExposeData))]
+ static class Patch_ExposeData
+ {
+ static void Postfix(Pawn __instance)
+ {
+ var escapeTracker = EscapeTracker.Of(__instance);
+ Scribe_Deep.Look(ref escapeTracker, "EscapeTracker", new object[] { __instance});
+ EscapeTracker.Save(__instance, escapeTracker);
+ }
+ }
+
+ [HarmonyPatch(typeof(Pawn))]
+ [HarmonyPatch(nameof(Pawn.Tick))]
+ static class Patch_Tick
+ {
+ static void Postfix(Pawn __instance)
+ {
+ if (!__instance.Dead)
+ {
+ EscapeTracker.Of(__instance)?.Tick();
+ }
+ }
+ }
+ }
+}
diff --git a/Source/HarmonyPatches/Patch_EscapingPrisoner.cs b/Source/HarmonyPatches/Patches_Escaping/Patch_EscapingPrisoner.cs
similarity index 81%
rename from Source/HarmonyPatches/Patch_EscapingPrisoner.cs
rename to Source/HarmonyPatches/Patches_Escaping/Patch_EscapingPrisoner.cs
index c9a38303..bef1c698 100644
--- a/Source/HarmonyPatches/Patch_EscapingPrisoner.cs
+++ b/Source/HarmonyPatches/Patches_Escaping/Patch_EscapingPrisoner.cs
@@ -1,4 +1,4 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using Harmony;
@@ -6,8 +6,9 @@
using Verse;
using System;
using System.IO;
+using PrisonLabor.Core.Trackers;
-namespace PrisonLabor.HarmonyPatches
+namespace PrisonLabor.HarmonyPatches.Patches_Escaping
{
[HarmonyPatch(typeof(JobGiver_PrisonerEscape))]
[HarmonyPatch("TryGiveJob")]
@@ -19,7 +20,7 @@ private static IEnumerable Transpiler(ILGenerator gen, MethodBa
{
Label restOfMethod = gen.DefineLabel();
yield return new CodeInstruction(OpCodes.Ldarg_1);
- yield return new CodeInstruction(OpCodes.Call, typeof(Patch_EscapingPrisoner).GetMethod("IsReadyToEscape"));
+ yield return new CodeInstruction(OpCodes.Call, typeof(Patch_EscapingPrisoner).GetMethod(nameof(IsReadyToEscape)));
yield return new CodeInstruction(OpCodes.Brtrue, restOfMethod);
yield return new CodeInstruction(OpCodes.Ldnull);
yield return new CodeInstruction(OpCodes.Ret);
@@ -38,11 +39,11 @@ private static IEnumerable Transpiler(ILGenerator gen, MethodBa
public static bool IsReadyToEscape(Pawn pawn)
{
- Need_Motivation need = pawn.needs.TryGetNeed();
- if (need != null && !need.ReadyToRun)
- return false;
- else
+ var escapeTracker = EscapeTracker.Of(pawn, true);
+ if (escapeTracker.ReadyToEscape)
return true;
+ else
+ return false;
}
}
}
diff --git a/Source/HarmonyPatches/Patches_Food/AddCustomFoodReservation.cs b/Source/HarmonyPatches/Patches_Food/AddCustomFoodReservation.cs
new file mode 100644
index 00000000..6b6dc99a
--- /dev/null
+++ b/Source/HarmonyPatches/Patches_Food/AddCustomFoodReservation.cs
@@ -0,0 +1,73 @@
+using Harmony;
+using PrisonLabor.Core.Other;
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using System.Reflection.Emit;
+using Verse;
+
+namespace PrisonLabor.HarmonyPatches.Patches_Food
+{
+ ///
+ /// Adds check if food is already reserved before trying to bring it
+ ///
+ [HarmonyPatch(typeof(FoodUtility))]
+ [HarmonyPatch(nameof(FoodUtility.BestFoodSourceOnMap))]
+ static class AddCustomFoodReservation
+ {
+ /* === Orignal code Look-up===
+ *
+ * c__AnonStorey.foodValidator = delegate(Thing t)
+ *
+ * === CIL Instructions ===
+ *
+ * ldloc.0 | | Label 6Label 8
+ * ldloc.0 | | no labels
+ * ldftn | Boolean <>m__0(Verse.Thing) | no labels
+ * newobj | Void .ctor(Object, IntPtr) | no labels
+ * stfld | System.Predicate`1[Verse.Thing] foodValidator | no labels
+ *
+ */
+
+ static IEnumerable Transpiler(ILGenerator gen, IEnumerable instructions)
+ {
+ OpCode[] opCodes =
+ {
+ OpCodes.Ldftn,
+ OpCodes.Newobj,
+ OpCodes.Stfld,
+ };
+ string[] operands =
+ {
+ "Boolean <>m__0(Verse.Thing)",
+ "Void .ctor(Object, IntPtr)",
+ "foodValidator",
+ };
+ int step = 0;
+
+ foreach (var instr in instructions)
+ {
+ if (HPatcher.IsFragment(opCodes, operands, instr, ref step, nameof(AddCustomFoodReservation), false))
+ {
+ yield return new CodeInstruction(OpCodes.Ldarg_0);
+ yield return new CodeInstruction(OpCodes.Ldarg_1);
+ yield return new CodeInstruction(OpCodes.Ldarg_2);
+ yield return new CodeInstruction(OpCodes.Call, typeof(AddCustomFoodReservation).GetMethod(nameof(AddContitionToPredicate)));
+ }
+ yield return instr;
+ }
+ }
+
+ public static Predicate AddContitionToPredicate(Predicate predicate, Pawn getter, Pawn eater, bool desperate)
+ {
+ return new Predicate(target =>
+ {
+ if (PrisonerFoodReservation.IsReserved(target) && (eater != getter || !eater.IsPrisoner) && !desperate)
+ return false;
+ else
+ return predicate.Invoke(target);
+ }
+ );
+ }
+ }
+}
diff --git a/Source/HarmonyPatches/Patches_Food/DeliverEvenOutsidePrisonCell.cs b/Source/HarmonyPatches/Patches_Food/DeliverEvenOutsidePrisonCell.cs
new file mode 100644
index 00000000..10dc82dd
--- /dev/null
+++ b/Source/HarmonyPatches/Patches_Food/DeliverEvenOutsidePrisonCell.cs
@@ -0,0 +1,62 @@
+using Harmony;
+using RimWorld;
+using System.Collections.Generic;
+using System.Reflection.Emit;
+
+namespace PrisonLabor.HarmonyPatches.Patches_Food
+{
+ ///
+ /// This patch is ensuring prisoner will be brought food despite beign outside of prison cell.
+ /// It skips the condition "IsInPrisonCell"
+ ///
+ [HarmonyPatch(typeof(WorkGiver_Warden_DeliverFood))]
+ [HarmonyPatch(nameof(WorkGiver_Warden_DeliverFood.JobOnThing))]
+ static class DeliverEvenOutsidePrisonCell
+ {
+ /* === Orignal code Look-up===
+ *
+ * if (!pawn2.Position.IsInPrisonCell(pawn2.Map))
+ * {
+ * return null;
+ * }
+ *
+ * === CIL Instructions ===
+ *
+ * ldloc.0 | | Label 2
+ * callvirt | IntVec3 get_Position() | no labels
+ * ldloc.0 | | no labels
+ * callvirt | Verse.Map get_Map() | no labels
+ * call | Boolean IsInPrisonCell(IntVec3, Verse.Map) | no labels
+ * brtrue | Label 3 | no labels
+ * ldnull | | no labels
+ * ret | | no labels
+ *
+ * ldloc.0 | | Label 3
+ */
+
+ static IEnumerable Transpiler(ILGenerator gen, IEnumerable instructions)
+ {
+ OpCode[] opCodes =
+ {
+ OpCodes.Call,
+ OpCodes.Brtrue,
+ };
+ string[] operands =
+ {
+ "Boolean IsInPrisonCell(IntVec3, Verse.Map)",
+ "System.Reflection.Emit.Label",
+ };
+ int step = 0;
+
+ foreach (var instr in instructions)
+ {
+ if (HPatcher.IsFragment(opCodes, operands, instr, ref step, nameof(DeliverEvenOutsidePrisonCell), true))
+ {
+ yield return new CodeInstruction(OpCodes.Pop);
+ instr.opcode = OpCodes.Br;
+ }
+ yield return instr;
+ }
+ }
+ }
+}
diff --git a/Source/HarmonyPatches/Patches_Food/FoodUtility_IsFoodSourceOnMapSociallyProper.cs b/Source/HarmonyPatches/Patches_Food/FoodUtility_IsFoodSourceOnMapSociallyProper.cs
new file mode 100644
index 00000000..3fe17888
--- /dev/null
+++ b/Source/HarmonyPatches/Patches_Food/FoodUtility_IsFoodSourceOnMapSociallyProper.cs
@@ -0,0 +1,79 @@
+using Harmony;
+using RimWorld;
+using System.Collections.Generic;
+using System.Reflection.Emit;
+
+namespace PrisonLabor.HarmonyPatches.Patches_Food
+{
+ ///
+ /// This patch is ignoring socially improper
+ ///
+ [HarmonyPatch(typeof(FoodUtility))]
+ [HarmonyPatch("IsFoodSourceOnMapSociallyProper")]
+ static class FoodUtility_IsFoodSourceOnMapSociallyProper
+ {
+ /* === Original code Look-up ===
+ *
+ * if (!allowSociallyImproper)
+ * {
+ * bool animalsCare = !getter.RaceProps.Animal;
+ * if (!t.IsSociallyProper(getter) && !t.IsSociallyProper(eater, eater.IsPrisonerOfColony, animalsCare))
+ * {
+ * return false;
+ * }
+ * }
+ * return true;
+ *
+ * === CIL Instructions ===
+ *
+ * ldarg.3 | | no labels
+ * brtrue | Label 1 | no labels
+ * ldarg.1 | | no labels
+ * callvirt | Verse.RaceProperties get_RaceProps() | no labels
+ * callvirt | Boolean get_Animal() | no labels
+ * ldc.i4.0 | | no labels
+ * ceq | | no labels
+ * stloc.0 | | no labels
+ * ldarg.0 | | no labels
+ * ldarg.1 | | no labels
+ * call | Boolean IsSociallyProper(Verse.Thing, Verse.Pawn) | no labels
+ * brtrue | Label 2 | no labels
+ * ldarg.0 | | no labels
+ * ldarg.2 | | no labels
+ * ldarg.2 | | no labels
+ * callvirt | Boolean get_IsPrisonerOfColony() | no labels
+ * ldloc.0 | | no labels
+ * call | Boolean IsSociallyProper(Verse.Thing, Verse.Pawn, Boolean, Boolean) | no labels
+ * brtrue | Label 3 | no labels
+ * ldc.i4.0 | | no labels
+ * ret | | no labels
+ * ldc.i4.1 | | Label 1Label 2Label 3
+ * ret | | no labels
+ */
+
+ static IEnumerable Transpiler(ILGenerator gen, IEnumerable instructions)
+ {
+ OpCode[] opCodes =
+ {
+ OpCodes.Ldarg_3,
+ OpCodes.Brtrue,
+ };
+ string[] operands =
+ {
+ "",
+ "",
+ };
+ int step = 0;
+
+ foreach (var instr in instructions)
+ {
+ if (HPatcher.IsFragment(opCodes, operands, instr, ref step, nameof(FoodUtility_IsFoodSourceOnMapSociallyProper), false))
+ {
+ yield return new CodeInstruction(OpCodes.Pop);
+ instr.opcode = OpCodes.Br;
+ }
+ yield return instr;
+ }
+ }
+ }
+}
diff --git a/Source/HarmonyPatches/Patches_Food/ReserveFoodForPrisonerAfterDropping.cs b/Source/HarmonyPatches/Patches_Food/ReserveFoodForPrisonerAfterDropping.cs
new file mode 100644
index 00000000..49c78b8d
--- /dev/null
+++ b/Source/HarmonyPatches/Patches_Food/ReserveFoodForPrisonerAfterDropping.cs
@@ -0,0 +1,32 @@
+using Harmony;
+using PrisonLabor.Core.Other;
+using RimWorld;
+using System.Collections.Generic;
+using System.Linq;
+using Verse;
+using Verse.AI;
+
+namespace PrisonLabor.HarmonyPatches.Patches_Food
+{
+ ///
+ /// Adds food reservation after dropping food for prisoner
+ ///
+ [HarmonyPatch(typeof(JobDriver_FoodDeliver))]
+ [HarmonyPatch("MakeNewToils")]
+ static class ReserveFoodForPrisonerAfterDropping
+ {
+ static void Postfix(ref IEnumerable __result, JobDriver_FoodDeliver __instance)
+ {
+ __result = new List(__result);
+ var lastToil = ((List)__result).Last();
+
+ lastToil.initAction = delegate
+ {
+ Thing thing;
+ __instance.pawn.carryTracker.TryDropCarriedThing(lastToil.actor.jobs.curJob.targetC.Cell, ThingPlaceMode.Direct,
+ out thing, null);
+ PrisonerFoodReservation.reserve(thing, (Pawn)lastToil.actor.jobs.curJob.targetB.Thing);
+ };
+ }
+ }
+}
diff --git a/Source/HarmonyPatches/Patch_ReservedByPrisoner.cs b/Source/HarmonyPatches/Patches_Food/ReservedByPrisonerPatch.cs
similarity index 70%
rename from Source/HarmonyPatches/Patch_ReservedByPrisoner.cs
rename to Source/HarmonyPatches/Patches_Food/ReservedByPrisonerPatch.cs
index 9ee20eb8..4da67d3e 100644
--- a/Source/HarmonyPatches/Patch_ReservedByPrisoner.cs
+++ b/Source/HarmonyPatches/Patches_Food/ReservedByPrisonerPatch.cs
@@ -1,20 +1,23 @@
-using System.Collections.Generic;
+using Harmony;
+using PrisonLabor.Core.Other;
+using RimWorld;
+using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
-using Harmony;
-using RimWorld;
using Verse;
using Verse.AI;
-namespace PrisonLabor.Harmony
+namespace PrisonLabor.HarmonyPatches.Patches_Food
{
+ ///
+ /// Complete overhaul PawnCanAutomaticallyHaulFast check, to include FoodReservation
+ ///
[HarmonyPatch(typeof(HaulAIUtility))]
[HarmonyPatch("PawnCanAutomaticallyHaulFast")]
- [HarmonyPatch(new[] {typeof(Pawn), typeof(Thing), typeof(bool)})]
- internal class ReservedByPrisonerPatch
+ [HarmonyPatch(new[] { typeof(Pawn), typeof(Thing), typeof(bool) })]
+ static class ReservedByPrisonerPatch
{
- private static IEnumerable Transpiler(ILGenerator gen, MethodBase mBase,
- IEnumerable instr)
+ static IEnumerable Transpiler(ILGenerator gen, MethodBase mBase, IEnumerable instr)
{
//Load arguments onto stack
yield return new CodeInstruction(OpCodes.Ldarg_0);
@@ -22,7 +25,7 @@ private static IEnumerable Transpiler(ILGenerator gen, MethodBa
yield return new CodeInstruction(OpCodes.Ldarg_2);
//Call function
yield return new CodeInstruction(OpCodes.Call,
- typeof(ReservedByPrisonerPatch).GetMethod("CanHaulAndInPrisonCell"));
+ typeof(ReservedByPrisonerPatch).GetMethod(nameof(CanHaulAndInPrisonCell)));
//Return
yield return new CodeInstruction(OpCodes.Ret);
}
@@ -39,7 +42,7 @@ public static bool CanHaulAndInPrisonCell(Pawn p, Thing t, bool forced)
return false;
if (t.def.IsNutritionGivingIngestible && t.def.ingestible.HumanEdible &&
!t.IsSociallyProper(p, false, true))
- if (PrisonerFoodReservation.isReserved(t))
+ if (PrisonerFoodReservation.IsReserved(t))
{
JobFailReason.Is("ReservedForPrisoners".Translate());
return false;
diff --git a/Source/HarmonyPatches/Patches_Food/StopIfPrisonerCanGetFoodByHimself.cs b/Source/HarmonyPatches/Patches_Food/StopIfPrisonerCanGetFoodByHimself.cs
new file mode 100644
index 00000000..951bd65e
--- /dev/null
+++ b/Source/HarmonyPatches/Patches_Food/StopIfPrisonerCanGetFoodByHimself.cs
@@ -0,0 +1,70 @@
+using Harmony;
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection.Emit;
+using System.Text;
+using Verse;
+
+namespace PrisonLabor.HarmonyPatches.Patches_Food
+{
+ ///
+ /// This patch is preventing wardens to deliver food when prisoner can get it by himself.
+ /// There is already some kind of this mechanic in Vanilla RimWorld, but it only affect room that prisoner is inside.
+ ///
+ [HarmonyPatch(typeof(WorkGiver_Warden_DeliverFood))]
+ [HarmonyPatch(nameof(WorkGiver_Warden_DeliverFood.JobOnThing))]
+ static class StopIfPrisonerCanGetFoodByHimself
+ {
+ /* === Orignal code Look-up===
+ *
+ * if (WorkGiver_Warden_DeliverFood.FoodAvailableInRoomTo(pawn2))
+ * {
+ * return null;
+ * }
+ *
+ * === CIL Instructions ===
+ *
+ * ldloc.0 | | Label 7
+ * call | Boolean FoodAvailableInRoomTo(Verse.Pawn) | no labels
+ * brfalse | Label 8 | no labels
+ * ldnull | | no labels
+ * ret | | no labels
+ * ldloc.2 | | Label 8
+ */
+
+ static IEnumerable Transpiler(ILGenerator gen, IEnumerable instructions)
+ {
+ OpCode[] opCodes =
+ {
+ OpCodes.Call,
+ };
+ string[] operands =
+ {
+ "Boolean FoodAvailableInRoomTo(Verse.Pawn)",
+ };
+ int step = 0;
+
+ foreach (var instr in instructions)
+ {
+ if (HPatcher.IsFragment(opCodes, operands, instr, ref step, nameof(StopIfPrisonerCanGetFoodByHimself) + 1, true))
+ {
+ yield return new CodeInstruction(OpCodes.Call, typeof(StopIfPrisonerCanGetFoodByHimself).GetMethod(nameof(FoodAvailableForPrisoner)));
+ }
+ else
+ {
+ yield return instr;
+ }
+ }
+ }
+
+ public static bool FoodAvailableForPrisoner(Pawn pawn)
+ {
+ Thing thing;
+ ThingDef thingDef;
+
+ return FoodUtility.TryFindBestFoodSourceFor(pawn, pawn, false, out thing, out thingDef, true, true, false, false, true, pawn.IsWildMan());
+ }
+ }
+}
diff --git a/Source/HarmonyPatches/Patch_BillCheckbox.cs b/Source/HarmonyPatches/Patches_GUI/GUI_Bill/Patch_BillCheckbox.cs
similarity index 61%
rename from Source/HarmonyPatches/Patch_BillCheckbox.cs
rename to Source/HarmonyPatches/Patches_GUI/GUI_Bill/Patch_BillCheckbox.cs
index 81e47f8a..c317c0e4 100644
--- a/Source/HarmonyPatches/Patch_BillCheckbox.cs
+++ b/Source/HarmonyPatches/Patches_GUI/GUI_Bill/Patch_BillCheckbox.cs
@@ -1,4 +1,4 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using Harmony;
@@ -8,11 +8,15 @@
using System;
using System.IO;
using Verse.Sound;
+using PrisonLabor.Core.BillAssignation;
#pragma warning disable CS0252
-namespace PrisonLabor.HarmonyPatches
+namespace PrisonLabor.HarmonyPatches.Patches_GUI.GUI_Bill
{
+ ///
+ /// This patch is adding checkbox to toggle between allowed workers
+ ///
[HarmonyPatch(typeof(Dialog_BillConfig))]
[HarmonyPatch("DoWindowContents")]
[HarmonyPatch(new[] { typeof(Rect) })]
@@ -71,13 +75,12 @@ private static IEnumerable Transpiler(ILGenerator gen, MethodBa
if (ci.labels.Contains((Label)label))
{
var injectedInstruction = new CodeInstruction(OpCodes.Ldloc_S, listing);
- foreach(var item in ci.labels)
+ foreach (var item in ci.labels)
injectedInstruction.labels.Add(item);
yield return injectedInstruction;
yield return new CodeInstruction(OpCodes.Ldarg_0);
yield return new CodeInstruction(OpCodes.Ldfld, billField);
- yield return new CodeInstruction(OpCodes.Call,
- typeof(Patch_BillCheckbox).GetMethod("GroupExclusionButton"));
+ yield return new CodeInstruction(OpCodes.Call, typeof(Patch_BillCheckbox).GetMethod(nameof(GroupExclusionButton)));
ci.labels.Clear();
}
@@ -87,51 +90,47 @@ private static IEnumerable Transpiler(ILGenerator gen, MethodBa
public static void GroupExclusionButton(Listing_Standard listing, Bill bill)
{
- if (BillUtility.IsFor(bill) == GroupMode.ColonistsOnly)
+ string label;
+ switch (BillAssignationUtility.IsFor(bill))
{
- if (listing.ButtonText("PrisonLabor_ColonistsOnly".Translate()))
- {
- BillUtility.SetFor(bill, GroupMode.PrisonersOnly);
- SoundDefOf.Click.PlayOneShotOnCamera();
- }
- }
- else if (BillUtility.IsFor(bill) == GroupMode.PrisonersOnly)
- {
- if (listing.ButtonText("PrisonLabor_PrisonersOnly".Translate()))
- {
- BillUtility.SetFor(bill, GroupMode.ColonyOnly);
- SoundDefOf.Click.PlayOneShotOnCamera();
- }
+ case GroupMode.ColonistsOnly:
+ label = "PrisonLabor_ColonistsOnlyShort".Translate();
+ break;
+ case GroupMode.PrisonersOnly:
+ label = "PrisonLabor_PrisonersOnlyShort".Translate();
+ break;
+ case GroupMode.ColonyOnly:
+ label = "PrisonLabor_ColonyOnlyShort".Translate();
+ break;
+ default:
+ label = "no label";
+ break;
}
- else
+
+ if (listing.ButtonText(label))
{
- if (listing.ButtonText("PrisonLabor_ColonyOnly".Translate()))
- {
- BillUtility.SetFor(bill, GroupMode.ColonistsOnly);
- SoundDefOf.Click.PlayOneShotOnCamera();
- }
+ MakeModeFloatMenu(bill);
}
- listing.Gap(12f);
- }
- public static Rect SetRect(Rect rect)
- {
- rect.height += 32;
- rect.width -= 16;
- return rect;
- }
-
- public static Vector2 position;
- public static void StartScrolling(Rect rect)
- {
- Rect viewRect = new Rect(0, 0, rect.width - 16, rect.height + 32);
- Rect outRect = new Rect(0, 0, rect.width, rect.height);
- Widgets.BeginScrollView(outRect, ref position, viewRect, true);
+ listing.Gap(12f);
}
- public static void StopScrolling()
+ private static void MakeModeFloatMenu(Bill bill)
{
- Widgets.EndScrollView();
+ List list = new List();
+ list.Add(new FloatMenuOption("PrisonLabor_ColonyOnly".Translate(), delegate
+ {
+ BillAssignationUtility.SetFor(bill, GroupMode.ColonyOnly);
+ }));
+ list.Add(new FloatMenuOption("PrisonLabor_ColonistsOnly".Translate(), delegate
+ {
+ BillAssignationUtility.SetFor(bill, GroupMode.ColonistsOnly);
+ }));
+ list.Add(new FloatMenuOption("PrisonLabor_PrisonersOnly".Translate(), delegate
+ {
+ BillAssignationUtility.SetFor(bill, GroupMode.PrisonersOnly);
+ }));
+ Find.WindowStack.Add(new FloatMenu(list));
}
}
}
\ No newline at end of file
diff --git a/Source/HarmonyPatches/Patch_AddScrollToPrisonerTab.cs b/Source/HarmonyPatches/Patches_GUI/GUI_PrisonerTab/Patch_AddScrollToPrisonerTab.cs
similarity index 94%
rename from Source/HarmonyPatches/Patch_AddScrollToPrisonerTab.cs
rename to Source/HarmonyPatches/Patches_GUI/GUI_PrisonerTab/Patch_AddScrollToPrisonerTab.cs
index 48f8636c..785db417 100644
--- a/Source/HarmonyPatches/Patch_AddScrollToPrisonerTab.cs
+++ b/Source/HarmonyPatches/Patches_GUI/GUI_PrisonerTab/Patch_AddScrollToPrisonerTab.cs
@@ -1,4 +1,4 @@
-using Harmony;
+using Harmony;
using RimWorld;
using System;
using System.Collections.Generic;
@@ -8,7 +8,7 @@
using UnityEngine;
using Verse;
-namespace PrisonLabor.HarmonyPatches
+namespace PrisonLabor.HarmonyPatches.Patches_GUI.GUI_PrisonerTab
{
///
/// This patch is adding scroll bar to prisoner tab to ensure all interaction modes are visible
@@ -80,7 +80,7 @@ private static IEnumerable Transpiler(ILGenerator gen, IEnumera
// end scroll
if (HPatcher.IsFragment(opCodes2, operands2, ci, ref step2, "AddScrollToPrisonerTab2"))
{
- var instruction = new CodeInstruction(OpCodes.Call, typeof(Patch_AddScrollToPrisonerTab).GetMethod("StopScrolling"));
+ var instruction = new CodeInstruction(OpCodes.Call, typeof(Patch_AddScrollToPrisonerTab).GetMethod(nameof(StopScrolling)));
instruction.labels.AddRange(ci.labels);
ci.labels.Clear();
yield return instruction;
@@ -89,7 +89,7 @@ private static IEnumerable Transpiler(ILGenerator gen, IEnumera
// resize
if (HPatcher.IsFragment(opCodes3, operands3, ci, ref step3, "AddScrollToPrisonerTab3"))
{
-
+
}
yield return ci;
@@ -98,7 +98,7 @@ private static IEnumerable Transpiler(ILGenerator gen, IEnumera
if (HPatcher.IsFragment(opCodes1, operands1, ci, ref step1, "AddScrollToPrisonerTab1"))
{
yield return new CodeInstruction(OpCodes.Ldloc_S, rect);
- yield return new CodeInstruction(OpCodes.Call, typeof(Patch_AddScrollToPrisonerTab).GetMethod("StartScrolling"));
+ yield return new CodeInstruction(OpCodes.Call, typeof(Patch_AddScrollToPrisonerTab).GetMethod(nameof(StartScrolling)));
yield return new CodeInstruction(OpCodes.Stloc_S, rect);
}
}
diff --git a/Source/HarmonyPatches/Patch_ExtendVistorRect.cs b/Source/HarmonyPatches/Patches_GUI/GUI_PrisonerTab/Patch_ExtendVistorRect.cs
similarity index 89%
rename from Source/HarmonyPatches/Patch_ExtendVistorRect.cs
rename to Source/HarmonyPatches/Patches_GUI/GUI_PrisonerTab/Patch_ExtendVistorRect.cs
index ad229df2..1caaddd8 100644
--- a/Source/HarmonyPatches/Patch_ExtendVistorRect.cs
+++ b/Source/HarmonyPatches/Patches_GUI/GUI_PrisonerTab/Patch_ExtendVistorRect.cs
@@ -1,4 +1,4 @@
-using Harmony;
+using Harmony;
using RimWorld;
using System;
using System.Collections.Generic;
@@ -8,7 +8,7 @@
using System.Text;
using Verse;
-namespace PrisonLabor.HarmonyPatches
+namespace PrisonLabor.HarmonyPatches.Patches_GUI.GUI_PrisonerTab
{
[HarmonyPatch(typeof(ITab_Pawn_Visitor))]
[HarmonyPatch("FillTab")]
diff --git a/Source/HarmonyPatches/Patches_GUI/GUI_PrisonerTab/Patch_PrisonerTab.cs b/Source/HarmonyPatches/Patches_GUI/GUI_PrisonerTab/Patch_PrisonerTab.cs
new file mode 100644
index 00000000..32c0212d
--- /dev/null
+++ b/Source/HarmonyPatches/Patches_GUI/GUI_PrisonerTab/Patch_PrisonerTab.cs
@@ -0,0 +1,92 @@
+using Harmony;
+using PrisonLabor.Core.Needs;
+using PrisonLabor.Core.Trackers;
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Text;
+using Verse;
+
+namespace PrisonLabor.HarmonyPatches.Patches_GUI.GUI_PrisonerTab
+{
+ ///
+ /// This patch is adding:
+ /// 1. string in dev mode indicating percentage of being unwatched before escaping
+ /// 2. Recruit option
+ ///
+ [HarmonyPatch(typeof(ITab_Pawn_Visitor), "FillTab")]
+ public static class Patch_PrisonerTab
+ {
+ static IEnumerable Transpiler(ILGenerator gen, IEnumerable instr)
+ {
+ // "if (Prefs.DevMode)" fragment
+ OpCode[] opCodes1 =
+ {
+ OpCodes.Call,
+ OpCodes.Brfalse,
+ };
+ string[] operands1 =
+ {
+ "Boolean get_DevMode()",
+ "System.Reflection.Emit.Label",
+ };
+ int step1 = 0;
+
+ // "listing_Standard.End()" fragment
+ OpCode[] opCodes2 =
+ {
+ OpCodes.Ldloc_3,
+ OpCodes.Callvirt,
+ };
+ string[] operands2 =
+ {
+ "",
+ "Void End()",
+ };
+ int step2 = 0;
+
+ foreach (var ci in instr)
+ {
+ if (HPatcher.IsFragment(opCodes2, operands2, ci, ref step2, "Patch_PrisonerTab listing_standard.End()"))
+ {
+ yield return new CodeInstruction(OpCodes.Ldloc_3);
+ yield return new CodeInstruction(OpCodes.Call, typeof(Patch_PrisonerTab).GetMethod(nameof(AddRecruitButton)));
+ }
+
+ yield return ci;
+
+ if (HPatcher.IsFragment(opCodes1, operands1, ci, ref step1, "Patch_PrisonerTab IfDevMode"))
+ {
+ yield return new CodeInstruction(OpCodes.Ldloc_3);
+ yield return new CodeInstruction(OpCodes.Call, typeof(Patch_PrisonerTab).GetMethod(nameof(AppendDevLines)));
+ }
+
+ }
+ }
+
+ public static void AppendDevLines(Listing_Standard listingStandard)
+ {
+ var pawn = Find.Selector.SingleSelectedThing as Pawn;
+ var escapeTracker = EscapeTracker.Of(pawn);
+ if (escapeTracker != null)
+ listingStandard.Label("Dev: Ready to escape: " + (escapeTracker.ReadyToEscape ? "ready" : escapeTracker.ReadyToRunPercentage + "%") + $" (Cap:{escapeTracker.EscapeLevel})", -1f);
+ }
+
+ public static void AddRecruitButton(Listing_Standard listingStandard)
+ {
+ var pawn = Find.Selector.SingleSelectedThing as Pawn;
+ var need = pawn.needs.TryGetNeed();
+ if (need != null && need.ResocializationReady)
+ {
+ if (listingStandard.ButtonTextLabeled("PrisonLabor_RecruitButtonDesc".Translate(), "PrisonLabor_RecruitButtonLabel".Translate()))
+ {
+ pawn.guest.SetGuestStatus(null);
+ pawn.SetFaction(Faction.OfPlayer);
+ }
+ }
+ }
+ }
+}
diff --git a/Source/HarmonyPatches/Patch_ChangeWorkTabPrisonerLabelColor.cs b/Source/HarmonyPatches/Patches_GUI/GUI_WorkTab/Patch_ChangeWorkTabPrisonerLabelColor.cs
similarity index 88%
rename from Source/HarmonyPatches/Patch_ChangeWorkTabPrisonerLabelColor.cs
rename to Source/HarmonyPatches/Patches_GUI/GUI_WorkTab/Patch_ChangeWorkTabPrisonerLabelColor.cs
index 2e50bc6b..04a92c12 100644
--- a/Source/HarmonyPatches/Patch_ChangeWorkTabPrisonerLabelColor.cs
+++ b/Source/HarmonyPatches/Patches_GUI/GUI_WorkTab/Patch_ChangeWorkTabPrisonerLabelColor.cs
@@ -1,9 +1,9 @@
-using Harmony;
+using Harmony;
using RimWorld;
using UnityEngine;
using Verse;
-namespace PrisonLabor.Harmony
+namespace PrisonLabor.HarmonyPatches.Patches_GUI.GUI_WorkTab
{
[HarmonyPatch(typeof(PawnColumnWorker_Label))]
[HarmonyPatch("DoCell")]
diff --git a/Source/HarmonyPatches/Patches_GUI/GUI_WorkTab/Patch_PawnTableSetDirtyFix.cs b/Source/HarmonyPatches/Patches_GUI/GUI_WorkTab/Patch_PawnTableSetDirtyFix.cs
new file mode 100644
index 00000000..b207b298
--- /dev/null
+++ b/Source/HarmonyPatches/Patches_GUI/GUI_WorkTab/Patch_PawnTableSetDirtyFix.cs
@@ -0,0 +1,28 @@
+using System.Collections.Generic;
+using System.Reflection;
+using System.Reflection.Emit;
+using Harmony;
+using RimWorld;
+using Verse;
+
+namespace PrisonLabor.HarmonyPatches.Patches_GUI.GUI_WorkTab
+{
+ ///
+ /// This partch is ensuring prisonersTable is set dirty, when parent component is set to dirty too.
+ ///
+ [HarmonyPatch(typeof(MainTabWindow_PawnTable))]
+ [HarmonyPatch("SetDirty")]
+ internal class Patch_PawnTableSetDirtyFix
+ {
+ private static void Prefix(MainTabWindow_PawnTable __instance)
+ {
+ var prisonersTable = __instance.GetType().GetField("prisonersTable", BindingFlags.NonPublic | BindingFlags.Instance);
+ if (prisonersTable != null)
+ {
+ var SetDirty = prisonersTable.FieldType.GetMethod("SetDirty");
+ if (SetDirty != null)
+ SetDirty.Invoke(prisonersTable.GetValue(__instance), new object[] { });
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/HarmonyPatches/Patch_WorkDisable.cs b/Source/HarmonyPatches/Patches_GUI/GUI_WorkTab/Patch_WorkDisabled.cs
similarity index 82%
rename from Source/HarmonyPatches/Patch_WorkDisable.cs
rename to Source/HarmonyPatches/Patches_GUI/GUI_WorkTab/Patch_WorkDisabled.cs
index e91dcc9f..f9056a33 100644
--- a/Source/HarmonyPatches/Patch_WorkDisable.cs
+++ b/Source/HarmonyPatches/Patches_GUI/GUI_WorkTab/Patch_WorkDisabled.cs
@@ -1,11 +1,12 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using Harmony;
+using PrisonLabor.Core.LaborWorkSettings;
using RimWorld;
using Verse;
-namespace PrisonLabor.HarmonyPatches
+namespace PrisonLabor.HarmonyPatches.Patches_GUI.GUI_WorkTab
{
[HarmonyPatch(typeof(WidgetsWork))]
[HarmonyPatch("DrawWorkBoxFor")]
@@ -20,7 +21,7 @@ private static IEnumerable Transpiler(ILGenerator gen, MethodBa
yield return new CodeInstruction(OpCodes.Ldarg_2);
yield return new CodeInstruction(OpCodes.Ldarg_3);
yield return new CodeInstruction(OpCodes.Call,
- typeof(WorkSettings).GetMethod("WorkDisabled", new[] {typeof(Pawn), typeof(WorkTypeDef)}));
+ typeof(WorkSettings).GetMethod(nameof(WorkSettings.WorkDisabled), new[] {typeof(Pawn), typeof(WorkTypeDef)}));
//If false continue
yield return new CodeInstruction(OpCodes.Brfalse, jumpTo);
//Return
diff --git a/Source/HarmonyPatches/Patch_WorkDisable2.cs b/Source/HarmonyPatches/Patches_GUI/GUI_WorkTab/Patch_WorkDisabled2.cs
similarity index 83%
rename from Source/HarmonyPatches/Patch_WorkDisable2.cs
rename to Source/HarmonyPatches/Patches_GUI/GUI_WorkTab/Patch_WorkDisabled2.cs
index 25093990..894db34f 100644
--- a/Source/HarmonyPatches/Patch_WorkDisable2.cs
+++ b/Source/HarmonyPatches/Patches_GUI/GUI_WorkTab/Patch_WorkDisabled2.cs
@@ -1,11 +1,12 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using Harmony;
+using PrisonLabor.Core.LaborWorkSettings;
using RimWorld;
using Verse;
-namespace PrisonLabor.Harmony
+namespace PrisonLabor.HarmonyPatches.Patches_GUI.GUI_WorkTab
{
[HarmonyPatch(typeof(WidgetsWork))]
[HarmonyPatch("TipForPawnWorker")]
@@ -20,7 +21,7 @@ private static IEnumerable Transpiler(ILGenerator gen, MethodBa
yield return new CodeInstruction(OpCodes.Ldarg_0);
yield return new CodeInstruction(OpCodes.Ldarg_1);
yield return new CodeInstruction(OpCodes.Call,
- typeof(WorkSettings).GetMethod("WorkDisabled", new[] {typeof(Pawn), typeof(WorkTypeDef)}));
+ typeof(WorkSettings).GetMethod(nameof(WorkSettings.WorkDisabled), new[] {typeof(Pawn), typeof(WorkTypeDef)}));
//If false continue
yield return new CodeInstruction(OpCodes.Brfalse, jumpTo);
//Load string TODO translate
diff --git a/Source/HarmonyPatches/Patch_DefaultInteractionMode.cs b/Source/HarmonyPatches/Patches_InteractionMode/Patch_DefaultInteractionMode.cs
similarity index 84%
rename from Source/HarmonyPatches/Patch_DefaultInteractionMode.cs
rename to Source/HarmonyPatches/Patches_InteractionMode/Patch_DefaultInteractionMode.cs
index 4a1cb96a..910c5b6b 100644
--- a/Source/HarmonyPatches/Patch_DefaultInteractionMode.cs
+++ b/Source/HarmonyPatches/Patches_InteractionMode/Patch_DefaultInteractionMode.cs
@@ -1,12 +1,13 @@
-using Harmony;
+using Harmony;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RimWorld;
using Verse;
+using PrisonLabor.Core.Meta;
-namespace PrisonLabor.HarmonyPatches
+namespace PrisonLabor.HarmonyPatches.Patches_InteractionMode
{
[HarmonyPatch(typeof(Pawn_GuestTracker))]
[HarmonyPatch("SetGuestStatus")]
diff --git a/Source/HarmonyPatches/Patch_PrisonInteractionLabel.cs b/Source/HarmonyPatches/Patches_InteractionMode/Patch_PrisonInteractionLabel.cs
similarity index 88%
rename from Source/HarmonyPatches/Patch_PrisonInteractionLabel.cs
rename to Source/HarmonyPatches/Patches_InteractionMode/Patch_PrisonInteractionLabel.cs
index 62e3d7d7..dc16836f 100644
--- a/Source/HarmonyPatches/Patch_PrisonInteractionLabel.cs
+++ b/Source/HarmonyPatches/Patches_InteractionMode/Patch_PrisonInteractionLabel.cs
@@ -1,11 +1,11 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using Harmony;
using RimWorld;
using Verse;
-namespace PrisonLabor.HarmonyPatches
+namespace PrisonLabor.HarmonyPatches.Patches_InteractionMode
{
//[HarmonyPatch(typeof(PrisonerInteractionModeUtility))]
//[HarmonyPatch("GetLabel")]
@@ -18,10 +18,10 @@ private static IEnumerable Transpiler(ILGenerator gen, MethodBa
// create our WORK label
var jumpTo = gen.DefineLabel();
yield return new CodeInstruction(OpCodes.Ldarg_0);
- yield return new CodeInstruction(OpCodes.Call, typeof(Patch_PrisonInteractionLabel).GetMethod("getLabelWork"));
+ yield return new CodeInstruction(OpCodes.Call, typeof(Patch_PrisonInteractionLabel).GetMethod(nameof(getLabelWork)));
yield return new CodeInstruction(OpCodes.Brfalse, jumpTo);
yield return new CodeInstruction(OpCodes.Ldarg_0);
- yield return new CodeInstruction(OpCodes.Call, typeof(Patch_PrisonInteractionLabel).GetMethod("getLabelWork"));
+ yield return new CodeInstruction(OpCodes.Call, typeof(Patch_PrisonInteractionLabel).GetMethod(nameof(getLabelWork)));
yield return new CodeInstruction(OpCodes.Ret);
var first = true;
diff --git a/Source/HarmonyPatches/Patch_AddLaborArea.cs b/Source/HarmonyPatches/Patches_LaborArea/Patch_AddLaborArea.cs
similarity index 74%
rename from Source/HarmonyPatches/Patch_AddLaborArea.cs
rename to Source/HarmonyPatches/Patches_LaborArea/Patch_AddLaborArea.cs
index 4e923537..8b11b6a1 100644
--- a/Source/HarmonyPatches/Patch_AddLaborArea.cs
+++ b/Source/HarmonyPatches/Patches_LaborArea/Patch_AddLaborArea.cs
@@ -1,7 +1,8 @@
-using Harmony;
+using Harmony;
+using PrisonLabor.Core.LaborArea;
using Verse;
-namespace PrisonLabor.Harmony
+namespace PrisonLabor.HarmonyPatches.Patches_LaborArea
{
[HarmonyPatch(typeof(AreaManager))]
[HarmonyPatch("AddStartingAreas")]
diff --git a/Source/HarmonyPatches/Patch_LaborForbid.cs b/Source/HarmonyPatches/Patches_LaborArea/Patch_LaborForbid.cs
similarity index 83%
rename from Source/HarmonyPatches/Patch_LaborForbid.cs
rename to Source/HarmonyPatches/Patches_LaborArea/Patch_LaborForbid.cs
index 31c753d5..4b8b1cca 100644
--- a/Source/HarmonyPatches/Patch_LaborForbid.cs
+++ b/Source/HarmonyPatches/Patches_LaborArea/Patch_LaborForbid.cs
@@ -1,13 +1,14 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using Harmony;
+using PrisonLabor.Core;
using RimWorld;
using Verse;
using Verse.AI;
-namespace PrisonLabor.HarmonyPatches
+namespace PrisonLabor.HarmonyPatches.Patches_LaborArea
{
[HarmonyPatch(typeof(JobGiver_Work))]
[HarmonyPatch("TryIssueJobPackage")]
@@ -18,9 +19,9 @@ private static IEnumerable Transpiler(ILGenerator gen, MethodBa
IEnumerable instr)
{
//var pawn = HPatcher.FindOperandAfter(new[] { OpCodes.Ldfld }, new[] { "Verse.Pawn pawn" }, instr);
- var jobgiver = HPatcher.FindOperandAfter(new[] {OpCodes.Ldloc_S }, new[] { "RimWorld.JobGiver_Work+c__AnonStorey1 (11)" }, instr );
+ var jobgiver = HPatcher.FindOperandAfter(new[] { OpCodes.Ldloc_S }, new[] { "RimWorld.JobGiver_Work+c__AnonStorey1 (11)" }, instr);
var scanner = HPatcher.FindOperandAfter(new[] { OpCodes.Ldfld }, new[] { "RimWorld.WorkGiver_Scanner scanner" }, instr);
- var cell = HPatcher.FindOperandAfter(new[] {OpCodes.Ldloc_S }, new[] { "Verse.IntVec3 (33)" }, instr );
+ var cell = HPatcher.FindOperandAfter(new[] { OpCodes.Ldloc_S }, new[] { "Verse.IntVec3 (33)" }, instr);
OpCode[] opcodes1 =
{
@@ -83,17 +84,17 @@ private static IEnumerable Transpiler(ILGenerator gen, MethodBa
yield return new CodeInstruction(OpCodes.Ldarg_1);
yield return new CodeInstruction(OpCodes.Ldloc_S, jobgiver);
yield return new CodeInstruction(OpCodes.Ldfld, scanner);
- yield return new CodeInstruction(OpCodes.Call, typeof(Patch_LaborForbid).GetMethod("CreatePredicate"));
+ yield return new CodeInstruction(OpCodes.Call, typeof(Patch_LaborForbid).GetMethod(nameof(CreatePredicate)));
}
- if(HPatcher.IsFragment(opCodes2, operands2, ci, ref step2, "Patch_LaborForbid2"))
+ if (HPatcher.IsFragment(opCodes2, operands2, ci, ref step2, "Patch_LaborForbid2"))
{
yield return new CodeInstruction(OpCodes.Ldloc_S, cell);
yield return new CodeInstruction(OpCodes.Ldarg_1);
yield return new CodeInstruction(OpCodes.Ldloc_S, jobgiver);
yield return new CodeInstruction(OpCodes.Ldfld, scanner);
- yield return new CodeInstruction(OpCodes.Call, typeof(Patch_LaborForbid).GetMethod("GetWorkType"));
- yield return new CodeInstruction(OpCodes.Call, typeof(LaborExclusionUtility).GetMethod("IsDisabledByLabor"));
+ yield return new CodeInstruction(OpCodes.Call, typeof(Patch_LaborForbid).GetMethod((nameof(GetWorkType))));
+ yield return new CodeInstruction(OpCodes.Call, typeof(PrisonLaborUtility).GetMethod((nameof(PrisonLaborUtility.IsDisabledByLabor))));
yield return new CodeInstruction(OpCodes.Brtrue, ci.operand);
}
}
@@ -103,7 +104,7 @@ public static Predicate CreatePredicate(Pawn pawn, WorkGiver_Scanner scan
{
return t => !t.IsForbidden(pawn)
&& scanner.HasJobOnThing(pawn, t, false)
- && !LaborExclusionUtility.IsDisabledByLabor(t.Position, pawn, scanner.def.workType);
+ && !PrisonLaborUtility.IsDisabledByLabor(t.Position, pawn, scanner.def.workType);
}
public static WorkTypeDef GetWorkType(WorkGiver_Scanner scanner)
@@ -112,4 +113,3 @@ public static WorkTypeDef GetWorkType(WorkGiver_Scanner scanner)
}
}
}
-
\ No newline at end of file
diff --git a/Source/HarmonyPatches/Patch_NeedOnlyByPrisoners.cs b/Source/HarmonyPatches/Patches_Needs/Patch_NeedOnlyByPrisoners.cs
similarity index 74%
rename from Source/HarmonyPatches/Patch_NeedOnlyByPrisoners.cs
rename to Source/HarmonyPatches/Patches_Needs/Patch_NeedOnlyByPrisoners.cs
index 9a1e4eb0..f495383d 100644
--- a/Source/HarmonyPatches/Patch_NeedOnlyByPrisoners.cs
+++ b/Source/HarmonyPatches/Patches_Needs/Patch_NeedOnlyByPrisoners.cs
@@ -1,19 +1,19 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using Harmony;
+using PrisonLabor.Core.Meta;
using RimWorld;
using Verse;
-namespace PrisonLabor.HarmonyPatches
+namespace PrisonLabor.HarmonyPatches.Patches_Needs
{
[HarmonyPatch(typeof(Pawn_NeedsTracker))]
[HarmonyPatch("ShouldHaveNeed")]
[HarmonyPatch(new[] { typeof(NeedDef) })]
- internal class Patch_NeedOnlyByPrisoners
+ public class Patch_NeedOnlyByPrisoners
{
- private static IEnumerable Transpiler(ILGenerator gen, MethodBase mBase,
- IEnumerable instr)
+ private static IEnumerable Transpiler(ILGenerator gen, MethodBase mBase, IEnumerable instr)
{
//Searches for pawn
var pawn = HPatcher.FindOperandAfter(new[] { OpCodes.Ldfld }, new[] { "Verse.Pawn pawn" }, instr);
@@ -25,8 +25,7 @@ private static IEnumerable Transpiler(ILGenerator gen, MethodBa
yield return new CodeInstruction(OpCodes.Ldarg_0);
yield return new CodeInstruction(OpCodes.Ldfld, pawn);
//Call function
- yield return new CodeInstruction(OpCodes.Call,
- typeof(Patch_NeedOnlyByPrisoners).GetMethod("ShouldHaveNeedPrisoner"));
+ yield return new CodeInstruction(OpCodes.Call, typeof(Patch_NeedOnlyByPrisoners).GetMethod(nameof(ShouldHaveNeedPrisoner)));
//If true continue
yield return new CodeInstruction(OpCodes.Brtrue, jumpTo);
//Load false to stack
@@ -46,11 +45,9 @@ private static IEnumerable Transpiler(ILGenerator gen, MethodBa
}
}
-
public static bool ShouldHaveNeedPrisoner(NeedDef nd, Pawn pawn)
{
- if (nd.defName == "PrisonLabor_Motivation" &&
- !(pawn.IsPrisoner && PrisonLaborPrefs.EnableMotivationMechanics))
+ if ((nd.defName == "PrisonLabor_Motivation" || nd.defName == "PrisonLabor_Treatment") && !(pawn.IsPrisoner && PrisonLaborPrefs.EnableMotivationMechanics))
return false;
return true;
}
diff --git a/Source/HarmonyPatches/Patch_ForibiddenDrop.cs b/Source/HarmonyPatches/Patches_PermissionFix/Patch_ForibiddenDrop.cs
similarity index 90%
rename from Source/HarmonyPatches/Patch_ForibiddenDrop.cs
rename to Source/HarmonyPatches/Patches_PermissionFix/Patch_ForibiddenDrop.cs
index e76c8050..dddc951e 100644
--- a/Source/HarmonyPatches/Patch_ForibiddenDrop.cs
+++ b/Source/HarmonyPatches/Patches_PermissionFix/Patch_ForibiddenDrop.cs
@@ -1,8 +1,8 @@
-using System;
+using System;
using RimWorld;
using Verse;
-namespace PrisonLabor.Harmony
+namespace PrisonLabor.HarmonyPatches.Patches_PermissionFix
{
internal class ForibiddenDropPatch
{
diff --git a/Source/HarmonyPatches/Patch_ItemIsForbidden.cs b/Source/HarmonyPatches/Patches_PermissionFix/Patch_ItemIsForbidden.cs
similarity index 68%
rename from Source/HarmonyPatches/Patch_ItemIsForbidden.cs
rename to Source/HarmonyPatches/Patches_PermissionFix/Patch_ItemIsForbidden.cs
index b14a9f81..8f024ff5 100644
--- a/Source/HarmonyPatches/Patch_ItemIsForbidden.cs
+++ b/Source/HarmonyPatches/Patches_PermissionFix/Patch_ItemIsForbidden.cs
@@ -1,11 +1,13 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using Harmony;
+using PrisonLabor.Core.Other;
+using PrisonLabor.Core.Trackers;
using RimWorld;
using Verse;
-namespace PrisonLabor.HarmonyPatches
+namespace PrisonLabor.HarmonyPatches.Patches_PermissionFix
{
///
/// Add checking if food is reserved by prisoner
@@ -20,11 +22,9 @@ private static IEnumerable Transpiler(ILGenerator gen, MethodBa
{
var endOfPatch = gen.DefineLabel();
yield return new CodeInstruction(OpCodes.Ldarg_0);
- yield return new CodeInstruction(OpCodes.Call, typeof(PrisonerFoodReservation).GetMethod("isReserved"));
- yield return new CodeInstruction(OpCodes.Brfalse, endOfPatch);
yield return new CodeInstruction(OpCodes.Ldarg_1);
- yield return new CodeInstruction(OpCodes.Call, typeof(Pawn).GetMethod("get_IsPrisoner"));
- yield return new CodeInstruction(OpCodes.Brtrue, endOfPatch);
+ yield return new CodeInstruction(OpCodes.Call, typeof(Patch_ItemIsForbidden).GetMethod(nameof(CustomForbidConditions)));
+ yield return new CodeInstruction(OpCodes.Brfalse, endOfPatch);
yield return new CodeInstruction(OpCodes.Ldc_I4_1);
yield return new CodeInstruction(OpCodes.Ret);
@@ -39,5 +39,14 @@ private static IEnumerable Transpiler(ILGenerator gen, MethodBa
yield return ci;
}
}
+
+ public static bool CustomForbidConditions(Thing thing, Pawn pawn)
+ {
+ if (PrisonerFoodReservation.IsReserved(thing) && !pawn.IsPrisoner)
+ return true;
+ if (pawn.IsWatched() && ForbidUtility.IsForbidden(thing, Faction.OfPlayer))
+ return true;
+ return false;
+ }
}
}
\ No newline at end of file
diff --git a/Source/HarmonyPatches/Patch_RespectReservation.cs b/Source/HarmonyPatches/Patches_PermissionFix/Patch_RespectReservation.cs
similarity index 91%
rename from Source/HarmonyPatches/Patch_RespectReservation.cs
rename to Source/HarmonyPatches/Patches_PermissionFix/Patch_RespectReservation.cs
index f7778bae..95ae9568 100644
--- a/Source/HarmonyPatches/Patch_RespectReservation.cs
+++ b/Source/HarmonyPatches/Patches_PermissionFix/Patch_RespectReservation.cs
@@ -1,4 +1,4 @@
-using Harmony;
+using Harmony;
using RimWorld;
using System;
using System.Collections.Generic;
@@ -8,7 +8,7 @@
using Verse;
using Verse.AI;
-namespace PrisonLabor.HarmonyPatches
+namespace PrisonLabor.HarmonyPatches.Patches_PermissionFix
{
[HarmonyPatch(typeof(ReservationManager))]
[HarmonyPatch("RespectsReservationsOf")]
@@ -19,7 +19,7 @@ private static IEnumerable Transpiler(ILGenerator gen, IEnumera
Label label = gen.DefineLabel();
yield return new CodeInstruction(OpCodes.Ldarg_0);
yield return new CodeInstruction(OpCodes.Ldarg_1);
- yield return new CodeInstruction(OpCodes.Call, typeof(Patch_RespectReservation).GetMethod("RespectPrisoners"));
+ yield return new CodeInstruction(OpCodes.Call, typeof(Patch_RespectReservation).GetMethod(nameof(RespectPrisoners)));
yield return new CodeInstruction(OpCodes.Brfalse, label);
yield return new CodeInstruction(OpCodes.Ldc_I4_1);
yield return new CodeInstruction(OpCodes.Ret);
diff --git a/Source/HarmonyPatches/Patch_SocialPropernessFix.cs b/Source/HarmonyPatches/Patches_PermissionFix/Patch_SocialPropernessFix.cs
similarity index 96%
rename from Source/HarmonyPatches/Patch_SocialPropernessFix.cs
rename to Source/HarmonyPatches/Patches_PermissionFix/Patch_SocialPropernessFix.cs
index d7eb5ff5..69e13e32 100644
--- a/Source/HarmonyPatches/Patch_SocialPropernessFix.cs
+++ b/Source/HarmonyPatches/Patches_PermissionFix/Patch_SocialPropernessFix.cs
@@ -1,4 +1,4 @@
-using Harmony;
+using Harmony;
using RimWorld;
using System;
using System.Collections.Generic;
@@ -7,7 +7,7 @@
using System.Text;
using Verse;
-namespace PrisonLabor.HarmonyPatches
+namespace PrisonLabor.HarmonyPatches.Patches_PermissionFix
{
///
/// This patch will fix conditions that allows prisoners only to use some activities inside room where he is.
diff --git a/Source/HarmonyPatches/Patch_RenamePrisoners.cs b/Source/HarmonyPatches/Patches_RenamingPrisoners/Patch_RenamePrisoners.cs
similarity index 97%
rename from Source/HarmonyPatches/Patch_RenamePrisoners.cs
rename to Source/HarmonyPatches/Patches_RenamingPrisoners/Patch_RenamePrisoners.cs
index f9eed890..0f1d0d54 100644
--- a/Source/HarmonyPatches/Patch_RenamePrisoners.cs
+++ b/Source/HarmonyPatches/Patches_RenamingPrisoners/Patch_RenamePrisoners.cs
@@ -1,4 +1,4 @@
-using Harmony;
+using Harmony;
using RimWorld;
using System;
using System.Collections.Generic;
@@ -9,7 +9,7 @@
using UnityEngine;
using Verse;
-namespace PrisonLabor.HarmonyPatches
+namespace PrisonLabor.HarmonyPatches.Patches_RenamingPrisoners
{
///
/// This patch is enabling to temporary rename prisoners for a duration of improsiment
@@ -56,7 +56,7 @@ static IEnumerable Transpiler(ILGenerator gen, MethodBase mBase
foreach (var instr in instructions)
{
- if (HPatcher.IsFragment(opCodes, operands, instr, ref step, nameof(Patch_RenamePrisoners) + nameof(EnableRenamingPrisoners)))
+ if (HPatcher.IsFragment(opCodes, operands, instr, ref step, nameof(Patch_RenamePrisoners) + nameof(EnableRenamingPrisoners), true))
{
yield return new CodeInstruction(OpCodes.Call, typeof(EnableRenamingPrisoners).GetMethod(nameof(IsColonistOrPrisonerOfColony)));
}
diff --git a/Source/HarmonyPatches/Patch_RestrainsPatch.cs b/Source/HarmonyPatches/Patches_Restraints/Patch_RestrainsPatch.cs
similarity index 93%
rename from Source/HarmonyPatches/Patch_RestrainsPatch.cs
rename to Source/HarmonyPatches/Patches_Restraints/Patch_RestrainsPatch.cs
index 24370bf0..3e4c7fe8 100644
--- a/Source/HarmonyPatches/Patch_RestrainsPatch.cs
+++ b/Source/HarmonyPatches/Patches_Restraints/Patch_RestrainsPatch.cs
@@ -1,4 +1,4 @@
-using Harmony;
+using Harmony;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -6,7 +6,7 @@
using System.Text;
using Verse;
-namespace PrisonLabor.HarmonyPatches
+namespace PrisonLabor.HarmonyPatches.Patches_Restraints
{
[HarmonyPatch(typeof(Pawn))]
[HarmonyPatch("TicksPerMove")]
diff --git a/Source/HarmonyPatches/Patch_JailorTypeSaveCompatibility.cs b/Source/HarmonyPatches/Patches_SaveCompatibility/Patch_JailorTypeSaveCompatibility.cs
similarity index 87%
rename from Source/HarmonyPatches/Patch_JailorTypeSaveCompatibility.cs
rename to Source/HarmonyPatches/Patches_SaveCompatibility/Patch_JailorTypeSaveCompatibility.cs
index e443a7f7..6588f99e 100644
--- a/Source/HarmonyPatches/Patch_JailorTypeSaveCompatibility.cs
+++ b/Source/HarmonyPatches/Patches_SaveCompatibility/Patch_JailorTypeSaveCompatibility.cs
@@ -1,4 +1,5 @@
-using Harmony;
+using Harmony;
+using PrisonLabor.Constants;
using RimWorld;
using System;
using System.Collections.Generic;
@@ -7,7 +8,7 @@
using System.Text;
using Verse;
-namespace PrisonLabor.HarmonyPatches
+namespace PrisonLabor.HarmonyPatches.Patches_SaveCompatibility
{
[HarmonyPatch(typeof(Pawn_WorkSettings))]
[HarmonyPatch("ExposeData")]
@@ -21,7 +22,7 @@ private static IEnumerable Transpiler(ILGenerator gen, IEnumera
yield return new CodeInstruction(OpCodes.Ldarg_0);
yield return new CodeInstruction(OpCodes.Dup);
yield return new CodeInstruction(OpCodes.Ldfld, priorities);
- yield return new CodeInstruction(OpCodes.Call, typeof(Patch_JailorTypeSaveCompatibility).GetMethod("AddJailor"));
+ yield return new CodeInstruction(OpCodes.Call, typeof(Patch_JailorTypeSaveCompatibility).GetMethod(nameof(AddJailor)));
//yield return new CodeInstruction(OpCodes.Pop);
//yield return new CodeInstruction(OpCodes.Ldarg_0);
yield return new CodeInstruction(OpCodes.Stfld, priorities);
@@ -46,8 +47,8 @@ public static DefMap AddJailor(DefMap priori
{
var newPriorities = new DefMap();
- int jailorIndex = PrisonLaborDefOf.PrisonLabor_Jailor.index;
- newPriorities[PrisonLaborDefOf.PrisonLabor_Jailor] = 0;
+ int jailorIndex = PL_DefOf.PrisonLabor_Jailor.index;
+ newPriorities[PL_DefOf.PrisonLabor_Jailor] = 0;
foreach (var def in DefDatabase.AllDefs.Where(d => d.index < priorities.Count))
{
if (def.index < jailorIndex)
diff --git a/Source/HarmonyPatches/Patches_TreatmentTinkering/Patch_ReduceChanceForMentalBreak.cs b/Source/HarmonyPatches/Patches_TreatmentTinkering/Patch_ReduceChanceForMentalBreak.cs
new file mode 100644
index 00000000..3ea59cf3
--- /dev/null
+++ b/Source/HarmonyPatches/Patches_TreatmentTinkering/Patch_ReduceChanceForMentalBreak.cs
@@ -0,0 +1,58 @@
+using Harmony;
+using PrisonLabor.Core.Needs;
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using Verse;
+using Verse.AI;
+
+namespace PrisonLabor.HarmonyPatches.Patches_TreatmentTinkering
+{
+ ///
+ /// This patch is reducing chance of mental break for prisoners with low treatment
+ ///
+ [HarmonyPatch(typeof(MentalStateHandler), "TryStartMentalState")]
+ static class Patch_ReduceChanceForMentalBreak
+ {
+ static bool Prefix(MentalStateHandler __instance, bool __result, MentalStateDef stateDef, string reason, bool forceWake, bool causedByMood, Pawn otherPawn)
+ {
+ if (!causedByMood)
+ return true;
+
+ Pawn pawn = (Pawn)(typeof(MentalStateHandler).GetField("pawn", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance));
+
+ if (pawn.IsPrisonerOfColony)
+ {
+ var need = pawn.needs.TryGetNeed();
+ TreatmentCategory treatmentCat = need.CurCategory;
+ bool suspended = false;
+ float chance = 0f;
+
+ switch (treatmentCat)
+ {
+ case TreatmentCategory.Normal:
+ chance = 0.1f;
+ break;
+ case TreatmentCategory.Bad:
+ chance = 0.5f;
+ break;
+ case TreatmentCategory.VeryBad:
+ chance = 1f;
+ break;
+ }
+
+ suspended = UnityEngine.Random.value < chance;
+
+ if (suspended)
+ {
+ __result = false;
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+}
diff --git a/Source/HarmonyPatches/Patches_Version/Patch_AddModVersionToFile.cs b/Source/HarmonyPatches/Patches_Version/Patch_AddModVersionToFile.cs
new file mode 100644
index 00000000..e0200df8
--- /dev/null
+++ b/Source/HarmonyPatches/Patches_Version/Patch_AddModVersionToFile.cs
@@ -0,0 +1,117 @@
+using Harmony;
+using PrisonLabor.Core.Meta;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Reflection.Emit;
+using Verse;
+using Version = PrisonLabor.Core.Meta.Version;
+
+namespace PrisonLabor.HarmonyPatches.Patches_Version
+{
+ static class Patch_AddModVersionToFile
+ {
+ private const string ParameterName = "PrisonLaborVersion";
+
+ [HarmonyPatch(typeof(ScribeMetaHeaderUtility))]
+ [HarmonyPatch(nameof(ScribeMetaHeaderUtility.WriteMetaHeader))]
+ static class Patch_WriteVersion
+ {
+ /* === Orignal code Look-up===
+ *
+ * catch
+ * {
+ * (...)
+ * <---
+ * }
+ * finally
+ * {
+ * (...)
+ * }
+ *
+ * === CIL Instructions ===
+ *
+ * leave | label 4 | no labels
+ *
+ */
+
+ static IEnumerable Transpiler(ILGenerator gen, MethodBase mBase, IEnumerable instructions)
+ {
+ OpCode[] opCodes1 =
+ {
+ OpCodes.Leave,
+ };
+ string[] operands1 =
+ {
+ "System.Reflection.Emit.Label",
+ };
+ int step1 = 0;
+
+ foreach (var instr in instructions)
+ {
+ if (HPatcher.IsFragment(opCodes1, operands1, instr, ref step1, nameof(Patch_WriteVersion), true))
+ yield return new CodeInstruction(OpCodes.Call, typeof(Patch_WriteVersion).GetMethod(nameof(AddMetaData)));
+ yield return instr;
+ }
+ }
+
+ public static void AddMetaData()
+ {
+ var currentVersionString = VersionUtility.versionNumber;
+ Scribe_Values.Look(ref currentVersionString, ParameterName, default(Version), true);
+ }
+ }
+
+
+ [HarmonyPatch(typeof(ScribeMetaHeaderUtility))]
+ [HarmonyPatch(nameof(ScribeMetaHeaderUtility.LoadGameDataHeader))]
+ static class Patch_LoadVersion
+ {
+ /* === Orignal code Look-up===
+ *
+ * catch
+ * {
+ * (...)
+ * <---
+ * }
+ * finally
+ * {
+ * (...)
+ * }
+ *
+ * === CIL Instructions ===
+ *
+ * leave | label x | no labels
+ *
+ */
+
+ static IEnumerable Transpiler(ILGenerator gen, MethodBase mBase, IEnumerable instructions)
+ {
+ OpCode[] opCodes1 =
+ {
+ OpCodes.Leave,
+ };
+ string[] operands1 =
+ {
+ "System.Reflection.Emit.Label",
+ };
+ int step1 = 0;
+
+ foreach (var instr in instructions)
+ {
+ if (HPatcher.IsFragment(opCodes1, operands1, instr, ref step1, nameof(Patch_LoadVersion), true))
+ yield return new CodeInstruction(OpCodes.Call, typeof(Patch_LoadVersion).GetMethod(nameof(ReadMetaData)));
+ yield return instr;
+ }
+ }
+
+ public static void ReadMetaData()
+ {
+ var currentVersionString = VersionUtility.versionNumber;
+ // TODO change version to 0.9.6 later, on next major update from RimWorld 1.0
+ Scribe_Values.Look(ref currentVersionString, ParameterName, Version.v0_9_6, true);
+ VersionUtility.VersionOfSaveFile = currentVersionString;
+ }
+ }
+
+ }
+}
diff --git a/Source/HarmonyPatches/Patch_DisableAreaRestrictionsForPrisoners.cs b/Source/HarmonyPatches/Patches_WorkSettings/Patch_DisableAreaRestrictionsForPrisoners.cs
similarity index 92%
rename from Source/HarmonyPatches/Patch_DisableAreaRestrictionsForPrisoners.cs
rename to Source/HarmonyPatches/Patches_WorkSettings/Patch_DisableAreaRestrictionsForPrisoners.cs
index 47422715..6d4cc489 100644
--- a/Source/HarmonyPatches/Patch_DisableAreaRestrictionsForPrisoners.cs
+++ b/Source/HarmonyPatches/Patches_WorkSettings/Patch_DisableAreaRestrictionsForPrisoners.cs
@@ -1,4 +1,4 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using Harmony;
@@ -6,7 +6,7 @@
using UnityEngine;
using Verse;
-namespace PrisonLabor.Harmony
+namespace PrisonLabor.HarmonyPatches.Patches_WorkSettings
{
///
/// This patch will remove prisoners in "Restrict" tab.
@@ -23,7 +23,7 @@ private static IEnumerable Transpiler(ILGenerator gen, MethodBa
var jumpTo = gen.DefineLabel();
yield return new CodeInstruction(OpCodes.Ldarg_2);
yield return new CodeInstruction(OpCodes.Call,
- typeof(DisableAreaRestrictionsForPrisoners).GetMethod("isPrisoner"));
+ typeof(DisableAreaRestrictionsForPrisoners).GetMethod(nameof(isPrisoner)));
yield return new CodeInstruction(OpCodes.Brfalse, jumpTo);
yield return new CodeInstruction(OpCodes.Ret);
diff --git a/Source/HarmonyPatches/Patches_WorkSettings/Patch_ResetWorktableWhenRecruited.cs b/Source/HarmonyPatches/Patches_WorkSettings/Patch_ResetWorktableWhenRecruited.cs
new file mode 100644
index 00000000..e0ab96fe
--- /dev/null
+++ b/Source/HarmonyPatches/Patches_WorkSettings/Patch_ResetWorktableWhenRecruited.cs
@@ -0,0 +1,26 @@
+using Harmony;
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Verse;
+
+//Faction newFaction, Pawn recruiter = null
+
+namespace PrisonLabor.HarmonyPatches.Patches_WorkSettings
+{
+ [HarmonyPatch(typeof(Pawn))]
+ [HarmonyPatch("SetFaction")]
+ [HarmonyPatch(new[] { typeof(Faction), typeof(Pawn) })]
+ class Patch_ResetWorktableWhenRecruited
+ {
+ private static void Prefix(Pawn __instance, Faction newFaction, Pawn recruiter)
+ {
+ if(__instance.IsPrisonerOfColony && newFaction == Faction.OfPlayer)
+ {
+ __instance.workSettings = null;
+ }
+ }
+ }
+}
diff --git a/Source/HarmonyPatches/Patch_TimetableFix.cs b/Source/HarmonyPatches/Patches_WorkSettings/Patch_TimetableFix.cs
similarity index 95%
rename from Source/HarmonyPatches/Patch_TimetableFix.cs
rename to Source/HarmonyPatches/Patches_WorkSettings/Patch_TimetableFix.cs
index f68e3b15..a8dafa2d 100644
--- a/Source/HarmonyPatches/Patch_TimetableFix.cs
+++ b/Source/HarmonyPatches/Patches_WorkSettings/Patch_TimetableFix.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -7,7 +7,7 @@
using System.Reflection.Emit;
using Verse;
-namespace PrisonLabor.HarmonyPatches
+namespace PrisonLabor.HarmonyPatches.Patches_WorkSettings
{
[HarmonyPatch(typeof(Pawn_TimetableTracker))]
[HarmonyPatch("get_CurrentAssignment")]
diff --git a/Source/HarmonyPatches/Triggers.cs b/Source/HarmonyPatches/Triggers.cs
new file mode 100644
index 00000000..3956dd29
--- /dev/null
+++ b/Source/HarmonyPatches/Triggers.cs
@@ -0,0 +1,73 @@
+using Harmony;
+using PrisonLabor.Core.GameSaves;
+using PrisonLabor.Core.Other;
+using PrisonLabor.Core.Trackers;
+using PrisonLabor.Core.Windows;
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using System.Reflection.Emit;
+using UnityEngine;
+using Verse;
+
+namespace PrisonLabor.HarmonyPatches
+{
+ ///
+ /// This is group of patch dedicated to attach execution of other methods similar to event handling.
+ /// The difference between patches inside this class, and others is that
+ /// this patches are executing single method of Prison Labor classes.
+ ///
+ static class Triggers
+ {
+ [HarmonyPatch(typeof(FloatMenuMakerMap))]
+ [HarmonyPatch("AddHumanlikeOrders")]
+ [HarmonyPatch(new[] { typeof(Vector3), typeof(Pawn), typeof(List) })]
+ static class AddHumanlikeOrders
+ {
+ public static void Prefix(Vector3 clickPos, Pawn pawn, List opts) { ArrestUtility.AddArrestOrder(clickPos, pawn, opts); }
+ }
+
+ [HarmonyPatch(typeof(Pawn_HealthTracker), "PreApplyDamage")]
+ static class Trigger_PreApplyDamage
+ {
+ static IEnumerable Transpiler(IEnumerable instr)
+ {
+ foreach (var ci in instr)
+ {
+ if (ci.opcode == OpCodes.Ret)
+ {
+ yield return new CodeInstruction(OpCodes.Ldarg_0);
+ yield return new CodeInstruction(OpCodes.Ldarg_1);
+ yield return new CodeInstruction(OpCodes.Ldarg_2);
+ yield return new CodeInstruction(OpCodes.Ldind_I1);
+ yield return new CodeInstruction(OpCodes.Call, typeof(TreatmentUtility).GetMethod(nameof(TreatmentUtility.OnApplyDamage)));
+ }
+
+ yield return ci;
+ }
+ }
+ }
+
+ [HarmonyPatch(typeof(Game))]
+ [HarmonyPatch(nameof(Game.LoadGame))]
+ static class UpgradeSave
+ {
+ static bool Prefix() { SaveUpgrader.Upgrade(); return true; }
+ }
+
+ [HarmonyPatch(typeof(Map))]
+ [HarmonyPatch(nameof(Map.MapPostTick))]
+ static class InsiprationTracker
+ {
+ static bool Prefix(Map __instance) { InspirationTracker.Calculate(__instance); return true; }
+ }
+
+ [HarmonyPatch(typeof(Map))]
+ [HarmonyPatch("FinalizeInit")]
+ [HarmonyPatch(new Type[] { })]
+ static class Patch_ShowNews
+ {
+ static void Postfix() { NewsWindow.TryShow();}
+ }
+ }
+}
diff --git a/Source/IncidentWorker_Revolt.cs b/Source/IncidentWorker_Revolt.cs
deleted file mode 100644
index d01db652..00000000
--- a/Source/IncidentWorker_Revolt.cs
+++ /dev/null
@@ -1,129 +0,0 @@
-using System;
-using UnityEngine;
-using Verse;
-using RimWorld;
-using Verse.AI.Group;
-using System.Collections.Generic;
-
-namespace PrisonLabor
-{
- public class IncidentWorker_Revolt : IncidentWorker
- {
- private const float HivePoints = 400f;
- private const float MinMotivationToStart = 0.4f;
-
- protected override bool CanFireNowSub(IncidentParms parms)
- {
- Map map = parms.target as Map;
-
- bool enemyFaction = false;
- float accumulatedMotivation = 0.0f;
- int prisonersCount = 0;
-
- foreach (var pawn in map.mapPawns.PrisonersOfColony)
- {
- if (pawn.Faction.HostileTo(Faction.OfPlayer))
- enemyFaction = true;
-
- var need = pawn.needs.TryGetNeed();
- if (need == null)
- continue;
- accumulatedMotivation += need.CurLevel;
- prisonersCount++;
- }
-
- if (accumulatedMotivation / prisonersCount > MinMotivationToStart)
- return false;
-
- return enemyFaction && PrisonLaborPrefs.EnableRevolts;
- }
-
- protected override bool TryExecuteWorker(IncidentParms parms)
- {
- Map map = (Map)parms.target;
- Pawn t = null;
- var affectedPawns = new List(map.mapPawns.PrisonersOfColony);
- foreach (Pawn pawn in affectedPawns)
- {
- if (pawn.Faction.HostileTo(Faction.OfPlayer))
- {
- parms.faction = pawn.Faction;
- t = pawn;
- break;
- }
- }
- float points = parms.points;
- int prisonersLeft = affectedPawns.Count;
- foreach (Pawn pawn in affectedPawns)
- {
- pawn.ClearMind();
- pawn.guest.SetGuestStatus(null, false);
- pawn.SetFaction(parms.faction);
-
- ThingWithComps weapon = ThingMaker.MakeThing(DefDatabase.GetNamed("MeleeWeapon_Shiv"), ThingDefOf.WoodLog) as ThingWithComps;
- ThingWithComps ammo = null;
- int pointsToRemove = 0;
-
- if (parms.points >= 1000)
- weapon = ThingMaker.MakeThing(DefDatabase.GetNamed("MeleeWeapon_Shiv"), ThingDefOf.Steel) as ThingWithComps;
-
- if (points >= 1000)
- {
- // If combat extended is enabled
- if (DefDatabase.GetNamed("Weapon_GrenadeStickBomb", false) != null)
- {
- if (UnityEngine.Random.value > 0.5f)
- {
- weapon = ThingMaker.MakeThing(DefDatabase.GetNamed("Weapon_GrenadeStickBomb")) as ThingWithComps;
- ammo = ThingMaker.MakeThing(DefDatabase.GetNamed("Weapon_GrenadeStickBomb")) as ThingWithComps;
- ammo.stackCount = 6;
- }
- else
- {
- weapon = ThingMaker.MakeThing(DefDatabase.GetNamed("Weapon_GrenadeMolotov")) as ThingWithComps;
- ammo = ThingMaker.MakeThing(DefDatabase.GetNamed("Weapon_GrenadeMolotov")) as ThingWithComps;
- ammo.stackCount = 6;
- }
- }
- else
- {
- weapon = ThingMaker.MakeThing(DefDatabase.GetNamed("Weapon_GrenadeMolotov")) as ThingWithComps;
- }
-
- pointsToRemove = 500;
- }
- else if (points >= 500)
- {
- weapon = ThingMaker.MakeThing(DefDatabase.GetNamed("Bow_Short")) as ThingWithComps;
-
- if (DefDatabase.GetNamed("Ammo_Arrow_Stone", false) != null)
- {
- ammo = ThingMaker.MakeThing(DefDatabase.GetNamed("Ammo_Arrow_Stone")) as ThingWithComps;
- ammo.stackCount = 30;
- }
-
- pointsToRemove = 100;
- }
- else if (points >= 300)
- {
- weapon = ThingMaker.MakeThing(DefDatabase.GetNamed("MeleeWeapon_Club"), ThingDefOf.Granite) as ThingWithComps;
- pointsToRemove = 100;
- }
-
- if (pawn.equipment.Primary == null)
- {
- pawn.equipment.AddEquipment(weapon);
- if (ammo != null)
- pawn.inventory.innerContainer.TryAdd(ammo);
- points -= pointsToRemove;
- }
- }
- var lordJob = new LordJob_AssaultColony();
- //TODO old code:
- LordMaker.MakeNewLord(parms.faction, lordJob/*(new RaidStrategyWorker_ImmediateAttackSmart()).MakeLordJob(parms, map)*/, map, affectedPawns);
- base.SendStandardLetter(t, null, new string[] { t.Name.ToStringShort, t.Faction.Name });
- Find.TickManager.slower.SignalForceNormalSpeedShort();
- return true;
- }
- }
-}
\ No newline at end of file
diff --git a/Source/Initialization.cs b/Source/Initialization.cs
index f687a9a9..da58a7da 100644
--- a/Source/Initialization.cs
+++ b/Source/Initialization.cs
@@ -1,6 +1,13 @@
-using PrisonLabor.HarmonyPatches;
+using PrisonLabor.Core.Hediffs;
+using PrisonLabor.Core.LaborArea;
+using PrisonLabor.Core.Meta;
+using PrisonLabor.Core.Settings;
+using PrisonLabor.HarmonyPatches;
using PrisonLabor.Tweaks;
+using RimWorld;
using System;
+using System.Collections.Generic;
+using System.Linq;
using Verse;
namespace PrisonLabor
@@ -18,13 +25,12 @@ static Initialization()
SettingsMenu.Init();
VersionUtility.CheckVersion();
Designator_AreaLabor.Initialization();
- Behaviour_MotivationIcon.Initialization();
CompatibilityPatches.Initialization.Run();
HediffManager.Init();
Log.Message($"Enabled Prison Labor v{VersionUtility.versionString}");
}
- catch(Exception e)
+ catch (Exception e)
{
Log.Error($"Prison Labor v{VersionUtility.versionString} caught error during start up:\n{e.Message}");
}
diff --git a/Source/InspirationUtility.cs b/Source/InspirationUtility.cs
deleted file mode 100644
index a0092878..00000000
--- a/Source/InspirationUtility.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-using System.Collections.Generic;
-using Verse;
-
-namespace PrisonLabor
-{
- internal class InspirationUtility
- {
- public static Dictionary> inspirationValues = new Dictionary>();
-
- public static float GetInsiprationValue(Pawn pawn)
- {
- var map = pawn.Map;
- if (!inspirationValues.ContainsKey(map) || !inspirationValues[map].ContainsKey(pawn))
- Calculate(map);
- var value = inspirationValues[map][pawn];
- inspirationValues[map].Remove(pawn);
- return value;
- }
-
- private static void Calculate(Map map)
- {
- var wardens = new List();
- wardens.AddRange(map.mapPawns.FreeColonists);
- var prisoners = new List();
- prisoners.AddRange(map.mapPawns.PrisonersOfColony);
-
- inspirationValues[map] = new Dictionary();
- foreach (var prisoner in prisoners)
- inspirationValues[map][prisoner] = 0f;
-
- var prisonersInRange = new List();
- foreach (var warden in wardens)
- {
- prisonersInRange.Clear();
- foreach (var prisoner in prisoners)
- if (warden.Position.DistanceTo(prisoner.Position) < Need_Motivation.InpirationRange &&
- prisoner.GetRoom() == warden.GetRoom())
- prisonersInRange.Add(prisoner);
-
- var delta = Need_Motivation.InspireRate / prisonersInRange.Count;
-
- foreach (var prisoner in prisonersInRange)
- inspirationValues[map][prisoner] += delta;
- }
- }
- }
-}
\ No newline at end of file
diff --git a/Source/JobGiver_Diet.cs b/Source/JobGiver_Diet.cs
deleted file mode 100644
index d49b50b3..00000000
--- a/Source/JobGiver_Diet.cs
+++ /dev/null
@@ -1,90 +0,0 @@
-using RimWorld;
-using Verse;
-using Verse.AI;
-
-namespace PrisonLabor
-{
- internal class JobGiver_Diet : ThinkNode_JobGiver
- {
- private HungerCategory minCategory = HungerCategory.Hungry;
- private readonly HungerCategory stopWorkingCat = HungerCategory.UrgentlyHungry;
-
- public override ThinkNode DeepCopy(bool resolve = true)
- {
- var jobGiver_Diet = (JobGiver_Diet) base.DeepCopy(resolve);
- jobGiver_Diet.minCategory = minCategory;
- return jobGiver_Diet;
- }
-
- public override float GetPriority(Pawn pawn)
- {
- var food = pawn.needs.food;
- if (food == null)
- return 0f;
- if (pawn.needs.food.CurCategory < HungerCategory.Starving && FoodUtility_Tweak.ShouldBeFedBySomeone(pawn))
- return 0f;
- if (food.CurCategory < minCategory)
- return 0f;
- if (food.CurCategory <= stopWorkingCat)
- return 11f;
- if (food.CurLevelPercentage < pawn.RaceProps.FoodLevelPercentageWantEat)
- return 7f;
- return 0f;
- }
-
- protected override Job TryGiveJob(Pawn pawn)
- {
- var food = pawn.needs.food;
- if (food == null || food.CurCategory < minCategory)
- return null;
- var need = pawn.needs.TryGetNeed();
- if (need != null)
- need.Enabled = false;
- bool flag;
- if (pawn.RaceProps.Animal)
- {
- flag = true;
- }
- else
- {
- var firstHediffOfDef = pawn.health.hediffSet.GetFirstHediffOfDef(HediffDefOf.Malnutrition, false);
- flag = firstHediffOfDef != null && firstHediffOfDef.Severity > 0.4f;
- }
- var desperate = pawn.needs.food.CurCategory == HungerCategory.Starving;
- var allowCorpse = flag;
- Thing thing;
- ThingDef def;
- if (!FoodUtility_Tweak.TryFindBestFoodSourceFor(pawn, pawn, desperate, out thing, out def, true, true, true,
- allowCorpse, false))
- return null;
- var pawn2 = thing as Pawn;
- if (pawn2 != null)
- return new Job(JobDefOf.PredatorHunt, pawn2)
- {
- killIncappedTarget = true
- };
- var building_NutrientPasteDispenser = thing as Building_NutrientPasteDispenser;
- if (building_NutrientPasteDispenser != null &&
- !building_NutrientPasteDispenser.HasEnoughFeedstockInHoppers())
- {
- var building = building_NutrientPasteDispenser.AdjacentReachableHopper(pawn);
- if (building != null)
- {
- var hopperSgp = building as ISlotGroupParent;
- var job = WorkGiver_CookFillHopper.HopperFillFoodJob(pawn, hopperSgp);
- if (job != null)
- return job;
- }
- thing = FoodUtility_Tweak.BestFoodSourceOnMap(pawn, pawn, desperate, FoodPreferability.MealLavish,
- false, !pawn.IsTeetotaler(), false, false, false, false, false);
- if (thing == null)
- return null;
- def = thing.def;
- }
- return new Job(JobDefOf.Ingest, thing)
- {
- count = FoodUtility_Tweak.WillIngestStackCountOf(pawn, def)
- };
- }
- }
-}
\ No newline at end of file
diff --git a/Source/LaborExclusionUtility.cs b/Source/LaborExclusionUtility.cs
deleted file mode 100644
index c469a730..00000000
--- a/Source/LaborExclusionUtility.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using Verse;
-
-namespace PrisonLabor
-{
- internal class LaborExclusionUtility
- {
- public static bool IsDisabledByLabor(IntVec3 pos, Pawn pawn, WorkTypeDef workType)
- {
- if (pos != null && pawn.Map.areaManager.Get() != null &&
- !WorkSettings.WorkDisabled(workType))
- return pawn.Map.areaManager.Get()[pos];
- return false;
- }
- }
-}
\ No newline at end of file
diff --git a/Source/Libraries/0Harmony.dll b/Source/Libraries/0Harmony.dll
new file mode 100644
index 00000000..9654e2ac
Binary files /dev/null and b/Source/Libraries/0Harmony.dll differ
diff --git a/Source/Need_Motivation.cs b/Source/Need_Motivation.cs
deleted file mode 100644
index 62ff6256..00000000
--- a/Source/Need_Motivation.cs
+++ /dev/null
@@ -1,219 +0,0 @@
-using System.Collections.Generic;
-using System.Text;
-using RimWorld;
-using UnityEngine;
-using Verse;
-
-namespace PrisonLabor
-{
- public class Need_Motivation : Need
- {
- public const float InspireRate = 0.015f;
- public const int WardenCapacity = (int)(InspireRate / LazyRate);
- public const float InpirationRange = 10.0f;
-
- private const float LazyLevel = 0.2f;
- private const float NeedInspirationLevel = 0.5f;
- private const float LazyRate = 0.002f;
- private const float HungryRate = 0.006f;
- private const float TiredRate = 0.006f;
- private const float HealthRate = 0.006f;
- private const float JoyRate = 0.006f;
- private const int ReadyToRunLevel = 100;
-
- private static NeedDef def;
-
- private int delta;
- private int impatient;
-
- public Need_Motivation(Pawn pawn) : base(pawn)
- {
- delta = 0;
- impatient = 0;
- ReadyToRun = false;
- Insipred = false;
- }
-
- public bool Enabled { get; set; }
-
- public bool NeedToBeInspired { get; private set; }
-
- public bool IsLazy { get; private set; }
-
- public bool ReadyToRun { get; private set; }
-
- public bool Insipred { get; private set; }
-
- public bool CanEscape { get; set; }
-
- public float PercentageThreshNeedInsipration => NeedInspirationLevel;
-
- public float PercentageThreshLazy => LazyLevel;
-
- //TODO change to lazy category?
- public HungerCategory CurCategory => 0;
-
- public override int GUIChangeArrow => delta;
-
- public bool Motivated
- {
- get
- {
- if (delta == 1)
- return true;
- return false;
- }
- }
-
- public static NeedDef Def
- {
- get
- {
- if (def == null)
- def = DefDatabase.GetNamed("PrisonLabor_Motivation");
- return def;
- }
- }
-
- private float LazinessRate
- {
- get
- {
- if (pawn.IsPrisoner && pawn.IsPrisonerOfColony)
- {
- if (pawn.GetRoomGroup() != null)
- {
- var value = InspirationUtility.GetInsiprationValue(pawn);
-
- if (PrisonLaborUtility.LaborEnabled(pawn))
- {
- if (Enabled)
- {
- value -= LazyRate;
- if (HealthAIUtility.ShouldSeekMedicalRest(pawn))
- value -= HealthRate;
- value -= (int)pawn.needs.food.CurCategory * HungryRate;
- value -= (int)pawn.needs.rest.CurCategory * TiredRate;
- if (value >= 0)
- Insipred = true;
- else
- Insipred = false;
- }
- else if (pawn.timetable != null &&
- pawn.timetable.CurrentAssignment == TimeAssignmentDefOf.Joy)
- {
- if (value != 0)
- Insipred = true;
- else
- Insipred = false;
- value += JoyRate;
- }
- else
- {
- if (value != 0)
- Insipred = true;
- else
- Insipred = false;
- }
- delta = value.CompareTo(0.0f);
- return value;
- }
- else
- {
- if (value != 0)
- Insipred = true;
- else
- Insipred = false;
-
- delta = value.CompareTo(0.0f);
- return value;
- }
- }
- else
- {
- delta = 0;
- return 0.0f;
- }
- }
- delta = 1;
- return +0.01f;
- }
- }
-
- public override void ExposeData()
- {
- base.ExposeData();
- //Scribe_Values.Look(ref this.lastNonStarvingTick, "lastNonStarvingTick", -99999, false);
- }
-
- public override void NeedInterval()
- {
- CurLevel += LazinessRate;
-
- if (CurLevel == MaxLevel)
- NeedToBeInspired = false;
- if (CurLevel <= NeedInspirationLevel && !NeedToBeInspired)
- NeedToBeInspired = true;
- if (CurLevel <= LazyLevel && !IsLazy && delta <= 0)
- {
- IsLazy = true;
- Tutorials.Motivation();
- }
- else if (IsLazy && delta > 0)
- {
- IsLazy = false;
- }
-
- ImpatientTick();
- }
-
- public override void SetInitialLevel()
- {
- CurLevelPercentage = 1.0f;
- Enabled = false;
- }
-
- public override string GetTipString()
- {
- var stringBuilder = new StringBuilder();
- stringBuilder.AppendLine(base.GetTipString());
- stringBuilder.AppendLine();
- stringBuilder.AppendLine("PrisonLabor_WardenResponseThreshold".Translate() + ": " +
- PercentageThreshNeedInsipration.ToStringPercent());
- stringBuilder.AppendLine(
- "PrisonLabor_StoppingWorkThreshold".Translate() + ": " + PercentageThreshLazy.ToStringPercent());
- return stringBuilder.ToString();
- }
-
- public override void DrawOnGUI(Rect rect, int maxThresholdMarkers = 2147483647, float customMargin = -1f,
- bool drawArrows = true, bool doTooltip = true)
- {
- if (threshPercents == null)
- threshPercents = new List();
- threshPercents.Clear();
- threshPercents.Add(PercentageThreshLazy);
- threshPercents.Add(PercentageThreshNeedInsipration);
- base.DrawOnGUI(rect, maxThresholdMarkers, customMargin, drawArrows, doTooltip);
- }
-
- private void ImpatientTick()
- {
- if (Insipred || !CanEscape)
- {
- if (impatient != 0)
- {
- impatient = 0;
- ReadyToRun = false;
- }
- }
- else if (!ReadyToRun)
- {
- impatient++;
- if (impatient >= ReadyToRunLevel)
- {
- ReadyToRun = true;
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/Source/NewsDialog.cs b/Source/NewsDialog.cs
deleted file mode 100644
index 31dd528e..00000000
--- a/Source/NewsDialog.cs
+++ /dev/null
@@ -1,526 +0,0 @@
-using System.Collections.Generic;
-using UnityEngine;
-using Verse;
-
-namespace PrisonLabor
-{
- internal class NewsDialog : Window
- {
- // Constans
- private const float Spacing = 2f;
- private const float GapHeight = 12f;
- private const string MarginText = " - ";
- private readonly float MarginWidth = Text.fontStyles[1].CalcSize(new GUIContent(MarginText)).x;
- private const GameFont TitleFont = GameFont.Medium;
- private const GameFont ItemFont = GameFont.Small;
-
- // Static Properties
- public static bool autoShow;
-
- public static bool showAll = false;
-
- public static bool news_0_5 = false;
- public static bool news_0_6 = false;
- public static bool news_0_7 = false;
- public static bool news_0_8_0 = false;
- public static bool news_0_8_1 = false;
- public static bool news_0_8_3 = false;
- public static bool news_0_8_6 = false;
- public static bool news_0_9_0 = false;
- public static bool news_0_9_1 = false;
- public static bool news_0_9_2 = false;
- public static bool news_0_9_9 = false;
-
- // Fields
- private string[] titles;
- private string[][] items;
-
- private Vector2 position;
-
- public NewsDialog()
- {
- doCloseButton = true;
- doCloseX = true;
- Init();
- }
-
- public void Init()
- {
- List titlesList = new List();
- List itemsList = new List();
-
- // How to insert news:
- // [subtitle] for subtitle
- // [img] ... [/img] for image (inside name of file)
- // [gap] for gap
-
- // 0.9.11
- if (showAll)
- {
- titlesList.Add("Prison Labor Beta v0.9.11");
- string[] itemsArray =
- {
- "fixed compatibility with Fluffy's WorkTab (final)",
- };
- itemsList.Add(itemsArray);
- }
-
- // 0.9.10
- if (showAll)
- {
- titlesList.Add("Prison Labor Beta v0.9.10");
- string[] itemsArray =
- {
- "hotfixed compatibility with Fluffy's WorkTab (still have some visual flaws)",
- };
- itemsList.Add(itemsArray);
- }
-
- // 0.9.9
- if (news_0_9_9 || showAll)
- {
- titlesList.Add("Prison Labor Beta v0.9.9");
- string[] itemsArray =
- {
- "added sub-tabs in \"Work\" Tab and \"Assign\" Tab for \"Colonists\" and \"Prisoners\"",
- "added renaming Prisoners for imprisonment time (pawns will restore old names after releasing)",
- };
- itemsList.Add(itemsArray);
- }
-
- // 0.9.8
- if (showAll)
- {
- titlesList.Add("Prison Labor Beta v0.9.8");
- string[] itemsArray =
- {
- "fixed SeedsPlease compatibility",
- };
- itemsList.Add(itemsArray);
- }
-
- // 0.9.7
- if (showAll)
- {
- titlesList.Add("Prison Labor Beta v0.9.7");
- string[] itemsArray =
- {
- "added warning message before placing labor area for the first time",
- };
- itemsList.Add(itemsArray);
- }
-
- // 0.9.6
- if (showAll)
- {
- titlesList.Add("Prison Labor Beta v0.9.6");
- string[] itemsArray =
- {
- "updated to RimWorld 1.0",
- };
- itemsList.Add(itemsArray);
- }
-
- // 0.9.5
- if (showAll)
- {
- titlesList.Add("Prison Labor Beta v0.9.5");
- string[] itemsArray =
- {
- "updated to RimWorld Beta 19",
- };
- itemsList.Add(itemsArray);
- }
-
- // 0.9.4
- if (showAll)
- {
- titlesList.Add("Prison Labor Beta v0.9.4");
- string[] itemsArray =
- {
- "disabled Warden and Jailor types of work for prisoner labor, it should fix bug, where jailors do not warden inside labor area",
- };
- itemsList.Add(itemsArray);
- }
-
- // 0.9.3
- if (showAll)
- {
- titlesList.Add("Prison Labor Beta v0.9.3");
- string[] itemsArray =
- {
- "fixed compatibility with No Water no Life",
- "fixed compatibility with Dubs Bad Hygiene Mod",
- "fixed error with loading old saves",
- };
- itemsList.Add(itemsArray);
- }
-
- // 0.9.2
- if (news_0_9_2 || showAll)
- {
- titlesList.Add("Prison Labor Beta v0.9.2");
- string[] itemsArray =
- {
- "fixed seeds please compatibility issue",
- "added option to disable revolts",
- };
- itemsList.Add(itemsArray);
- }
- // 0.9.1
- if (news_0_9_1 || showAll)
- {
- titlesList.Add("Prison Labor Beta v0.9.1");
- string[] itemsArray =
- {
- "changed max. skill required for non-advanced growing by prisoners to 6 instead of 0",
- "added new work type Jailor",
- "fixed drawing icons on world map",
- "fixed disabling mod from existing saves",
- "fixed incorrectly showing \"advanced growing by prisoners\" option",
- };
- itemsList.Add(itemsArray);
- }
- // 0.9.0
- if (news_0_9_0 || showAll)
- {
- titlesList.Add("Prison Labor Beta v0.9.0");
- string[] itemsArray =
- {
- "updated to RimWorld beta v18",
- "added option to disable icons above prisoners heads in mod menu",
- "fixed error \"null reference in onGui()\" when loading save",
- };
- itemsList.Add(itemsArray);
- }
- // 0.8.8 (silent)
- if (showAll)
- {
- titlesList.Add("Prison Labor Beta v0.8.8");
- string[] itemsArray =
- {
- "changed slow from prisoners chains to act as factor instead offset",
- "fixed compatibility issues with Seeds Please(again)",
- };
- itemsList.Add(itemsArray);
- }
- // 0.8.7 (silent)
- if (showAll)
- {
- titlesList.Add("Prison Labor Beta v0.8.7");
- string[] itemsArray =
- {
- "fixed bug with dropping motivation while in bed",
- "prisoners will now get different weapons when revolt triggers (molotovs, bows, or clubs)",
- "replaced orginal jobs with \"tweak\" jobs (instead of overriding them, this fix is for users who use \"WorkTab\" by Fluffy)",
- "removed warning message from logs",
- "prisoners will now have 50% of normal speed in chains (instead of 35%)",
- "prisoners will now break chains after some period of time instead of immadiately(matter in incidents, breakouts etc.)",
- "wardens will now try to motivate most prisoners at once, but with priority to motivate lowest motivation first",
- "fixed bug with animals do not respect reservations (and vice versa)",
- };
- itemsList.Add(itemsArray);
- }
- // 0.8.6
- if (news_0_8_6 || showAll)
- {
- titlesList.Add("Prison Labor Beta v0.8.6");
- string[] itemsArray =
- {
- "[img]NewsElement_Locks[/img]Locks mod: \nIf you want to allow prisoners to pass by closed doors, please check out my other mod called Locks ",
- "[gap]",
- "fixed bug that Sowing job do not comply to Labor Area",
- "fixed bug with JoyGiver debris (sorry about that)",
- "reduced number of null reference errors with OnGui() method (fixed in v 0.8.5)",
- "single warden will be able to maintain 7 prisoners, instead of 5 (because of laziness rate reduction) (changed in v 0.8.5)",
- "decreased laziness rate to 0.002, instead of 0.003 (prisoners will get lazy 1.5x slower) (changed in v 0.8.5)",
- "decreased manipulation to 70% (instead of 80%) (changed in v 0.8.5)",
- "fixed null reference exception at loading game (fixed in v 0.8.4)",
- };
- itemsList.Add(itemsArray);
- }
- // 0.8.3
- if (news_0_8_3 || showAll)
- {
- titlesList.Add("Prison Labor Beta v0.8.3");
- string[] itemsArray =
- {
- "fixed bugs with disabling mod(now you can safely disable mod again)",
- "fixed bug with prioritizing work",
- "fixed bug with rendering icons on world map",
- };
- itemsList.Add(itemsArray);
- }
- // 0.8.1
- if (news_0_8_1 || showAll)
- {
- titlesList.Add("Prison Labor Beta v0.8.1");
- string[] itemsArray =
- {
- "[subtitle] Sorry for any inconvenience caused by 0.8.0 update. Some part of mod are very vulnerable to any mods installed",
- "[subtitle] If you encouter any bugs please report it on github . I'm fixing most important ones every day. This is (recently) beta version and it has to consist some bugs. Thank you for understaning.",
- "[subtitle] Also you can always download old version via github, but I think this was last big update",
- "re-enabled button in Bills detail panel",
- "added slider to Bills (temporary fix)",
- "fixed Bill \"Prisoner only\" button (I think, let me know if you still experience errors)",
- "fixed prisoners aren't working when Motivation is disabled (via Settings)",
- "fixed null-reference error on some revolts incidents",
- };
- itemsList.Add(itemsArray);
- }
- // 0.8.0
- if (news_0_8_0 || showAll)
- {
- titlesList.Add("Prison Labor Beta v0.8.0");
- string[] itemsArray =
- {
- "[subtitle]Now in Beta! I would no longer add any more features. Instead I will focus on improving existing ones.",
- "[gap]",
- "[subtitle]Main changes: ",
- "[img]NewsElement_Revolt[/img]Revolts: \nPrisoners will now form organized group under self-elected leader if motivation of prisoners is low. They will try to inflict damage to your colony or they will attemp running to elected enemy faction",
- "[img]NewsElement_InspirationReworked[/img]Insiration reworked: \nQuicker, better and more intuitive.\nYou can now send your prisoners to work outside your walls, but be carefull: they will try to escape if left alone. Prisoners will start thinking about escape after being left for some time.",
- "[img]LaborAreaExpand[/img]Labor area: \nYou can now select area for labor only. Your colonists will no longer go mine with peasants.\nTo access this tool look into \"Architect->Zones\" panel.",
- "[img]NewsElement_PrisonersOnly[/img]Prisoners Only button \nGo to Bill details to mark bills for prisoners only!",
- "[img]NewsElement_WorkAndRecruit[/img]Work and recruit: \nThis feature has been mostly requested by community. I hope it will be well received.",
- "[gap]",
- "[subtitle]Other changes: ",
- "added default prisoner interaction mode option to settings menu",
- "added icons above prisoners indicating whenever he's being motivated/inspired",
- "reduced manipulation capability of prisoners (now they have 80% of normal manipulation, down from 100%)",
- "added tutorials triggers (now all tutorials will be shown)",
- "added watched tutorials to properties (tutorials will no longer be shown after reenabling mod)",
- "fixed forbidden bug with harvesting plants (again)",
- "fixed Toil reservation bug (not respecting prisoners' job)",
- "fixed compatibility with Dubs Hygiene Mod",
- "fixed SeedsPlease compatibility",
- "excluded supervising from labor",
- "rewritten news dialog - now with images and stuff",
- "perfomance and code improvements",
- "translation improvements",
- "[gap]",
- "[subtitle]Also I want to annouce that I will start new mod called Prison Expansion that would be PrisonExtensions remake.The aim of this mod would be improving Prison Labor experience, especially cell doors and fences.",
- };
- itemsList.Add(itemsArray);
- }
- // 0.7
- if (news_0_7 || showAll)
- {
- titlesList.Add("Prison Labor Alpha v0.7");
- string[] itemsArray =
- {
- "Added settings! You can now change almost any aspect of this mod, including:\n * work types\n * motivation mechanics\n * prevention of planting advanced plants.",
- "Added \"uninstaller\" (\"disable\" option in settings), which will allow to disable this mod from existing saves.",
- "\"No more beeping!\". Changed way of informing player what's going on with prisoners. It should be less annoying and more insightful.",
- "Fixed bugs, including bug that prevents prisoners from cleaning and bug that causes warden to stuck in loop of delivering food to prisoner.",
- "\"No more watching while prisoner is sleeping.\"Wardens will no longer watch over not working prisoners.",
- "Prisoners will now stay in bed while waiting for operation",
- "Prisoners will now stop work when starving for default (\"Anything\" time), instead of hungry. They will still get minor debuff.",
- };
- itemsList.Add(itemsArray);
- }
- // 0.6
- if (news_0_6 || showAll)
- {
- titlesList.Add("Prison Labor Alpha v0.6");
- string[] itemsArray =
- {
- "Time restrictions - now you can manage your prisoners time for sleep, work and joy. You can now even force them to work when they're hungry!",
- "Getting food by prisoners - Now prisoners will look for food in much better way, and now (when they desperate enough) they will eat corpses!",
- "\"Laziness\" changed to \"Motivation\" and inverted.\n\n ATTENTION: After PrisonLabor reaches beta all saves with PrisonLabor v0.5a or lower will be corrupted and unplayable. This version (0.6) is safe and converts all older saves.",
- };
- itemsList.Add(itemsArray);
- }
- // 0.5
- if (news_0_5 || showAll)
- {
- titlesList.Add("Prison Labor Alpha v0.5");
- string[] itemsArray =
- {
- "Prisoners can now grow, but only plants that not require any skills.",
- "You can now manage prisoners work types. Just check \"Work\" tab!",
- "Laziness now appear on \"Needs\" tab. Above 50% wardens will watch prisoners. Above 80% prisoners won't work unless supervised.",
- "Wardens will now bring food to prisoners that went too far from his bed.",
- "Prisoners won't gain laziness when not working anymore.",
- "Fixed many bugs",
- };
- itemsList.Add(itemsArray);
- }
-
- // If count of items in both lists aren't equal that means someone (me) fucked up
- if (titlesList.Count != itemsList.Count)
- throw new System.Exception("Prison Labor exception: news lists aren't equal");
-
- // Transfer items: dynamic list => static array, for optimalization
- titles = new string[titlesList.Count];
- for (int i = 0; i < titlesList.Count; i++)
- {
- titles[i] = titlesList[i];
- }
- items = new string[itemsList.Count][];
- for (int i = 0; i < itemsList.Count; i++)
- {
- items[i] = itemsList[i];
- }
- }
-
- public static void TryShow()
- {
- if (autoShow && PrisonLaborPrefs.ShowNews)
- {
- Find.WindowStack.Add(new NewsDialog());
- PrisonLaborPrefs.LastVersion = PrisonLaborPrefs.Version;
- PrisonLaborPrefs.Save();
- autoShow = false;
- }
- }
-
- public static void ForceShow()
- {
- Find.WindowStack.Add(new NewsDialog());
- PrisonLaborPrefs.LastVersion = PrisonLaborPrefs.Version;
- PrisonLaborPrefs.Save();
- autoShow = false;
- }
-
- public override void DoWindowContents(Rect inRect)
- {
- var displayRect = new Rect(inRect.x, inRect.y, inRect.width, inRect.height - 50f);
- var viewRect = new Rect(0, 0, inRect.width - 16f, CalculateHeight(inRect.width - 16f));
-
- Widgets.BeginScrollView(displayRect, ref position, viewRect, true);
-
- for (int i = 0; i < titles.Length; i++)
- {
- // Draw title
- Text.Font = TitleFont;
- Widgets.Label(viewRect, titles[i]);
- viewRect.y += Text.CalcHeight(titles[i], viewRect.width) + Spacing;
-
- // Draw line gap
- Color color = GUI.color;
- GUI.color = GUI.color * new Color(1f, 1f, 1f, 0.4f);
- Widgets.DrawLineHorizontal(viewRect.x, viewRect.y + +GapHeight * 0.5f, viewRect.width);
- GUI.color = color;
- viewRect.y += GapHeight;
-
- // Draw items
- Text.Font = ItemFont;
- for (int j = 0; j < items[i].Length; j++)
- {
- // Draw Image with Text
- if (items[i][j].StartsWith("[img]"))
- {
- int imgLength = items[i][j].IndexOf("[/img]");
- var imageString = items[i][j].Substring(5, imgLength - 5);
- var textToDraw = items[i][j].Substring(imgLength + 6);
-
- var content = new GUIContent();
- content.image = ContentFinder.Get(imageString, false);
- content.text = textToDraw;
- Widgets.Label(viewRect, content);
-
- viewRect.y += GuiStyle(Text.Font).CalcHeight(content, viewRect.width);
- }
- // Draw Gap
- else if (items[i][j].StartsWith("[gap]"))
- {
- color = GUI.color;
- GUI.color = GUI.color * new Color(1f, 1f, 1f, 0.4f);
- Widgets.DrawLineHorizontal(viewRect.x, viewRect.y + +GapHeight * 0.5f, viewRect.width);
- GUI.color = color;
- viewRect.y += GapHeight;
- }
- // Draw Subtitle (without margin)
- else if (items[i][j].StartsWith("[subtitle]"))
- {
- Widgets.Label(viewRect, items[i][j].Substring(10));
- viewRect.y += Text.CalcHeight(items[i][j], viewRect.width) + Spacing;
- }
- // Draw Text
- else
- {
- viewRect.width -= MarginWidth;
- Widgets.Label(viewRect, MarginText);
- viewRect.x += MarginWidth;
- Widgets.Label(viewRect, items[i][j]);
- viewRect.x -= MarginWidth;
- viewRect.y += Text.CalcHeight(items[i][j], viewRect.width) + Spacing;
- viewRect.width += MarginWidth;
- }
- }
-
- // Make gap
- viewRect.y += GapHeight;
- }
-
- Widgets.EndScrollView();
- }
-
- private float CalculateHeight(float width)
- {
- float height = 0;
- Text.Font = TitleFont;
- foreach (var item in titles)
- {
- height += Text.CalcHeight(item, width) + Spacing + GapHeight;
- }
- Text.Font = ItemFont;
- foreach (var array in items)
- foreach (var item in array)
- {
- // Image with Text
- if (item.StartsWith("[img]"))
- {
- int imgLength = item.IndexOf("[/img]");
- var imageString = item.Substring(5, imgLength - 5);
- var textToDraw = item.Substring(imgLength + 6);
-
- var content = new GUIContent();
- content.image = ContentFinder.Get(imageString, false);
- content.text = textToDraw;
-
-
- height += GuiStyle(Text.Font).CalcHeight(content, width);
- }
- // Gap
- else if (item.StartsWith("[gap]"))
- {
- height += GapHeight;
- }
- else if (item.StartsWith("[subtitle]"))
- {
- height += Text.CalcHeight(item, width) + Spacing;
- }
- // Only Text
- else
- {
- height += Text.CalcHeight(item, width - MarginWidth) + Spacing;
- }
- }
- return height;
- }
-
- private static GUIStyle GuiStyle(GameFont font)
- {
- GUIStyle gUIStyle;
- switch (font)
- {
- case GameFont.Tiny:
- gUIStyle = Text.fontStyles[0];
- break;
- case GameFont.Small:
- gUIStyle = Text.fontStyles[1];
- break;
- case GameFont.Medium:
- gUIStyle = Text.fontStyles[2];
- break;
- default:
- return null;
- }
- gUIStyle.alignment = Text.Anchor;
- gUIStyle.wordWrap = Text.WordWrap;
- return gUIStyle;
- }
-
- }
-}
\ No newline at end of file
diff --git a/Source/Organizer/NewsFeed.xml b/Source/Organizer/NewsFeed.xml
new file mode 100644
index 00000000..e0dcd108
--- /dev/null
+++ b/Source/Organizer/NewsFeed.xml
@@ -0,0 +1,254 @@
+
+
+
+
+
+
+