diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..940794e6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,288 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+**/Properties/launchSettings.json
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# TODO: Comment the next line if you want to checkin your web deploy settings
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/packages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Typescript v1 declaration files
+typings/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush
+.cr/
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
diff --git a/About/About.xml b/About/About.xml
index 93e77331..a2ee2f9e 100644
--- a/About/About.xml
+++ b/About/About.xml
@@ -3,9 +3,9 @@
Prison Labor (Alpha)
Avius
0.17.0
- Version 0.3b
+ Version 0.4
-This mod force prisoners to work if Prisoner Interaction is set to "Work".
+This mod force prisoners to work if Prisoner Interaction is set to "Force to work".
Prisoner must be fed, and rested, or he(she) will refuse to work. Currently prisoners can only cook, mine, cut plants, craft, haul, and clean.
Attention! He can run away if he mine a way out.
@@ -19,19 +19,6 @@ To make prisoners work you must meet these conditions:
- Prisoner can't escape.
- Prisoner can reach work (best way to do that is leaving open doors to work area).
- Prisoner is fed, and rested.
-- Prisoner interaction is set to "Work" (no "Chat and Recruit", or "Friendly Chat").
-
-Changelog:
- 0.3b
- - Fixed "Forbidden" bug
- 0.3a
- - wardens no longer watch over hungry or tired prisoners
- 0.3
- - added work of Warden type that supervise prisoners
- - prisoners will get lazy
- - added version checker
- - added stat laziness
- - added "Work" prisoner interaction mode
- 0.2a
- - added tutorial in "LearningHelper"
+- Prisoner interaction is set to "Force to work" (no "Chat and Recruit", or "Friendly Chat").
+- Laziness bar in "Needs" tab is below 80%
diff --git a/Assemblies/PrisonLabor.dll b/Assemblies/PrisonLabor.dll
index 484899cb..a0cac13c 100644
Binary files a/Assemblies/PrisonLabor.dll and b/Assemblies/PrisonLabor.dll differ
diff --git a/Defs/Tutor.xml b/Defs/ConceptDef.xml
similarity index 94%
rename from Defs/Tutor.xml
rename to Defs/ConceptDef.xml
index e31465c5..08aa4116 100644
--- a/Defs/Tutor.xml
+++ b/Defs/ConceptDef.xml
@@ -1,6 +1,6 @@
-
+
PrisonLabor
Prison labor
45
diff --git a/Defs/Job.xml b/Defs/Job.xml
deleted file mode 100644
index 759cde95..00000000
--- a/Defs/Job.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
- PrisonerSupervise
- PrisonLabor.JobDriver_Supervise
- watching prisoner TargetA.
- true
-
-
- PrisonLabor.Mine_Tweak
- PrisonLabor.JobDriver_Mine_Tweak
- digging at TargetA.
-
-
diff --git a/Defs/JobDef.xml b/Defs/JobDef.xml
new file mode 100644
index 00000000..499ba7a9
--- /dev/null
+++ b/Defs/JobDef.xml
@@ -0,0 +1,24 @@
+
+
+
+ PrisonLabor_PrisonerSupervise
+ PrisonLabor.JobDriver_Supervise
+ watching prisoner TargetA.
+ true
+
+
+ PrisonLabor_Mine_Tweak
+ PrisonLabor.JobDriver_Mine_Tweak
+ digging at TargetA.
+
+
+ PrisonLabor_Harvest_Tweak
+ PrisonLabor.JobDriver_PlantHarvest_Tweak
+ harvesting TargetA.
+
+
+ PrisonLabor_CutPlant_Tweak
+ PrisonLabor.JobDriver_PlantCut_Tweak
+ cutting TargetA.
+
+
diff --git a/Defs/Needs.xml b/Defs/Needs.xml
new file mode 100644
index 00000000..6cbf8dac
--- /dev/null
+++ b/Defs/Needs.xml
@@ -0,0 +1,12 @@
+
+
+
+ PrisonLabor_Laziness
+ PrisonLabor.Need_Laziness
+ laziness
+ Work In Progress
+ 90
+ false
+ false
+
+
diff --git a/Defs/WorkOption.xml b/Defs/PrisonerInteractionModeDef.xml
similarity index 100%
rename from Defs/WorkOption.xml
rename to Defs/PrisonerInteractionModeDef.xml
diff --git a/Defs/ThinkTree.xml b/Defs/ThinkTreeDef.xml
similarity index 100%
rename from Defs/ThinkTree.xml
rename to Defs/ThinkTreeDef.xml
diff --git a/Defs/WorkGiver.xml b/Defs/WorkGiverDef.xml
similarity index 55%
rename from Defs/WorkGiver.xml
rename to Defs/WorkGiverDef.xml
index cbbee073..0577e9b9 100644
--- a/Defs/WorkGiver.xml
+++ b/Defs/WorkGiverDef.xml
@@ -1,7 +1,7 @@
- SupervisePrisonLabor
+ PrisonLabor_SupervisePrisonLabor
watch prisoner
PrisonLabor.WorkGiver_Supervise
Warden
@@ -25,7 +25,7 @@
- PrisonLabor.Mine_Tweak
+ PrisonLabor_Mine_Tweak
mine
PrisonLabor.WorkGiver_Miner_Tweak
Mining
@@ -37,4 +37,30 @@
Manipulation
+
+ PrisonLabor_PlantsCut_Tweak
+ cut plants
+ PrisonLabor.WorkGiver_PlantsCut_Tweak
+ PlantCutting
+ 101
+ cut
+ cutting
+
+ Manipulation
+
+
+
+ PrisonLabor_GrowerHarvest_Tweak
+ harvest crops
+ PrisonLabor.WorkGiver_GrowerHarvest_Tweak
+ Growing
+ 101
+ harvest
+ harvesting
+ false
+ true
+
+ Manipulation
+
+
diff --git a/Languages/English/Keyed/Keys.xml b/Languages/English/Keyed/Keys.xml
new file mode 100644
index 00000000..261baca0
--- /dev/null
+++ b/Languages/English/Keyed/Keys.xml
@@ -0,0 +1,5 @@
+
+
+ Force to work
+ Your prioner got lazy!
+
diff --git a/Languages/German/DefInjected/ConceptDef/Tutorials.xml b/Languages/German/DefInjected/ConceptDef/Tutorials.xml
new file mode 100644
index 00000000..9e134556
--- /dev/null
+++ b/Languages/German/DefInjected/ConceptDef/Tutorials.xml
@@ -0,0 +1,7 @@
+
+
+ Gefängnisarbeit
+ Du kannst deine Gefangenen zum Arbeiten zwingen.\n\n Um dies zu tun, musst du die Option "Zum arbeiten zwingen" vom Reiter "Gefangener" auswählen.\n\nDu musst auserdem darauf achten, dass deine Gefangenen genährt und ausgeruht sind. Du kannst das im Reiter "Bedürfnisse" nachschauen.\n\nGefangene werden Befehle ausführen und Aufgaben erfüllen, die sich in der erlaubten Zone befinden.\nGefangene können nur kochen, Gestein und Erze abbauen, Pflanzen schneiden, Gegenstände herstellen, transportieren und Reinigen.\n\HINWEIS: Wenn du einen Herd in einer Gefängniszelle plazierst, achte darauf, dass sie auch rohes Essen erreichen können.
+ Faule Gefangene
+ Einer deiner Gefangenen ist faul geworden.\nEr wird nicht länger arbeiten, solange er nicht motiviert wird.\nAchte darauf, dass du genug Aufseher hast (schau im Reite "Arbeit" nach) oder ernenne einen deiner Kolonisten und befiehl ihn neben deinen Gefangenen zu stehen.
+
diff --git a/Languages/German/Keyed/Keys.xml b/Languages/German/Keyed/Keys.xml
new file mode 100644
index 00000000..a0194860
--- /dev/null
+++ b/Languages/German/Keyed/Keys.xml
@@ -0,0 +1,5 @@
+
+
+ Zum arbeiten zwingen
+ Dein Gefangener wurde faul!
+
diff --git a/Old sources/PrisonLabor v0.3/Source/ConceptDef_PrisonLaborInstruction.cs b/Old sources/PrisonLabor v0.3/Source/ConceptDef_PrisonLaborInstruction.cs
new file mode 100644
index 00000000..acc52ab7
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3/Source/ConceptDef_PrisonLaborInstruction.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+
+namespace PrisonLabor
+{
+ class ConceptDef_PrisonLaborInstruction : ConceptDef
+ {
+
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3/Source/InfoDialog.cs b/Old sources/PrisonLabor v0.3/Source/InfoDialog.cs
new file mode 100644
index 00000000..2b2a4716
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3/Source/InfoDialog.cs
@@ -0,0 +1,104 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+using UnityEngine;
+
+namespace PrisonLabor
+{
+ class InfoDialog : Window
+ {
+ private string searchName = string.Empty;
+
+ private string[] searchWords;
+
+ private List cachedNames;
+
+ public override Vector2 InitialSize
+ {
+ get
+ {
+ return new Vector2(400f, 650f);
+ }
+ }
+
+ public InfoDialog()
+ {
+ this.doCloseButton = true;
+ this.absorbInputAroundWindow = true;
+ this.cachedNames = (from n in (from b in SolidBioDatabase.allBios
+ select b.name).Concat(PawnNameDatabaseSolid.AllNames())
+ orderby n.Last descending
+ select n).ToList();
+ }
+
+ public override void DoWindowContents(Rect inRect)
+ {
+ Listing_Standard listing_Standard = new Listing_Standard();
+ listing_Standard.Begin(inRect);
+ listing_Standard.Label("TypeFirstNickOrLastName".Translate(), -1f);
+ string text = listing_Standard.TextEntry(this.searchName, 1);
+ if (text.Length < 20)
+ {
+ this.searchName = text;
+ this.searchWords = this.searchName.Replace("'", string.Empty).Split(new char[]
+ {
+ ' '
+ });
+ }
+ listing_Standard.Gap(4f);
+ if (this.searchName.Length > 1)
+ {
+ foreach (NameTriple current in this.cachedNames.Where(new Func(this.FilterMatch)))
+ {
+ if (listing_Standard.ButtonText(current.ToString(), null))
+ {
+ this.TryChooseName(current);
+ }
+ if (listing_Standard.CurHeight + 30f > inRect.height - (this.CloseButSize.y + 8f))
+ {
+ break;
+ }
+ }
+ }
+ listing_Standard.End();
+ }
+
+ private bool FilterMatch(NameTriple n)
+ {
+ if (n.First == "Tynan" && n.Last == "Sylvester")
+ {
+ return false;
+ }
+ if (this.searchWords.Length == 0)
+ {
+ return false;
+ }
+ if (this.searchWords.Length == 1)
+ {
+ return n.Last.StartsWith(this.searchName, StringComparison.OrdinalIgnoreCase) || n.First.StartsWith(this.searchName, StringComparison.OrdinalIgnoreCase) || n.Nick.StartsWith(this.searchName, StringComparison.OrdinalIgnoreCase);
+ }
+ return this.searchWords.Length == 2 && n.First.EqualsIgnoreCase(this.searchWords[0]) && (n.Last.StartsWith(this.searchWords[1], StringComparison.OrdinalIgnoreCase) || n.Nick.StartsWith(this.searchWords[1], StringComparison.OrdinalIgnoreCase));
+ }
+
+ private void TryChooseName(NameTriple name)
+ {
+ if (this.AlreadyPreferred(name))
+ {
+ Messages.Message("MessageAlreadyPreferredName".Translate(), MessageSound.RejectInput);
+ }
+ else
+ {
+ Prefs.PreferredNames.Add(name.ToString());
+ this.Close(true);
+ }
+ }
+
+ private bool AlreadyPreferred(NameTriple name)
+ {
+ return Prefs.PreferredNames.Contains(name.ToString());
+ }
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3/Source/Initialization.cs b/Old sources/PrisonLabor v0.3/Source/Initialization.cs
new file mode 100644
index 00000000..5f5e5dee
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3/Source/Initialization.cs
@@ -0,0 +1,86 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+using Harmony;
+using System.Reflection;
+using System.Reflection.Emit;
+
+namespace PrisonLabor
+{
+ [StaticConstructorOnStartup]
+ class Initialization
+ {
+ public static int version = 3;
+ public static bool oldPlayerNotification = false;
+
+ static Initialization()
+ {
+ var harmony = HarmonyInstance.Create("Harmony_PrisonLabor");
+ harmony.PatchAll(Assembly.GetExecutingAssembly());
+
+ PrisonLaborPrefs.Init();
+ checkVersion();
+ }
+
+ private static void checkVersion()
+ {
+ if(PrisonLaborPrefs.Version < 3)
+ {
+ // only way to check if mod was installed before
+ if (PlayerKnowledgeDatabase.IsComplete(DefDatabase.GetNamed("PrisonLabor")))
+ {
+ Log.Message("Detected older version of PrisonLabor");
+ oldPlayerNotification = true;
+ }
+ }
+ else
+ {
+ Log.Message("Detected PrisonLabor v" + PrisonLaborPrefs.Version);
+ }
+ PrisonLaborPrefs.Version = 3;
+ PrisonLaborPrefs.Save();
+ }
+ }
+
+ [HarmonyPatch(typeof(PrisonerInteractionModeUtility))]
+ [HarmonyPatch("GetLabel")]
+ [HarmonyPatch(new Type[] { typeof(PrisonerInteractionModeDef) })]
+ class Patch
+ {
+ static IEnumerable Transpiler(ILGenerator gen, MethodBase mBase, IEnumerable instr)
+ {
+ // create our WORK label
+ Label jumpTo = gen.DefineLabel();
+ yield return new CodeInstruction(OpCodes.Ldarg_0);
+ yield return new CodeInstruction(OpCodes.Call, typeof(Patch).GetMethod("getLabelWork"));
+ //yield return new CodeInstruction(OpCodes.Dup);
+ yield return new CodeInstruction(OpCodes.Ldstr, "Work");
+ yield return new CodeInstruction(OpCodes.Bne_Un, jumpTo);
+ yield return new CodeInstruction(OpCodes.Ldstr, "Work");
+ yield return new CodeInstruction(OpCodes.Ret);
+
+ bool first = true;
+ foreach (CodeInstruction ci in instr)
+ {
+ if (first)
+ {
+ first = false;
+ ci.labels.Add(jumpTo);
+ }
+ //debug
+ //Log.Message("CODE: ToString():" + ci.ToString() + " || labels:" + ci.labels + " || opcode:" + ci.opcode.ToString());
+ yield return ci;
+ }
+ }
+
+ public static string getLabelWork(PrisonerInteractionModeDef def)
+ {
+ if(def == DefDatabase.GetNamed("PrisonLabor_workOption"))
+ return "Work";
+ return "";
+ }
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3/Source/JobDriver_Supervise.cs b/Old sources/PrisonLabor v0.3/Source/JobDriver_Supervise.cs
new file mode 100644
index 00000000..e6fad431
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3/Source/JobDriver_Supervise.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+using Verse.AI;
+
+namespace PrisonLabor
+{
+ class JobDriver_Supervise : JobDriver
+ {
+ protected Pawn Prisoner
+ {
+ get
+ {
+ return (Pawn)base.CurJob.targetA.Thing;
+ }
+ }
+
+ protected override IEnumerable MakeNewToils()
+ {
+ this.FailOnDespawnedOrNull(TargetIndex.A);
+ this.FailOnMentalState(TargetIndex.A);
+ this.FailOnNotAwake(TargetIndex.A);
+ this.FailOn(() => !Prisoner.IsPrisonerOfColony || !Prisoner.guest.PrisonerIsSecure);
+
+ yield return Toils_Reserve.Reserve(TargetIndex.A, 1, -1, null);
+ //yield return Toils_Interpersonal.GotoPrisoner(this.pawn, this.Prisoner, this.Prisoner.guest.interactionMode);
+ yield return MakeWatchToil(Prisoner);
+ for(int i = 0; i < 80; i++)
+ yield return Toils_General.Wait(10).FailOn(() => Prisoner.GetRoom() != pawn.GetRoom());
+ yield return MakeWatchToil(Prisoner);
+ for (int i = 0; i < 80; i++)
+ yield return Toils_General.Wait(10).FailOn(() => Prisoner.GetRoom() != pawn.GetRoom());
+ yield return Toils_Interpersonal.SetLastInteractTime(TargetIndex.A);
+ }
+
+ protected Toil MakeWatchToil(Pawn prisoner)
+ {
+ Toil toil = new Toil();
+ toil.initAction = delegate
+ {
+ Pawn actor = toil.actor;
+ IntVec3 ind;
+ if (Prisoner.GetRoom().Cells.Any(cell => cell.DistanceTo(Prisoner.InteractionCell) < 7 && cell.DistanceTo(Prisoner.InteractionCell) > 4))
+ ind = prisoner.GetRoom().Cells.Where(cell => cell.DistanceTo(prisoner.InteractionCell) < 7 && cell.DistanceTo(prisoner.InteractionCell) > 4).RandomElement();
+ else
+ ind = prisoner.GetRoom().Cells.RandomElement();
+ actor.pather.StartPath(ind, PathEndMode.OnCell);
+ };
+ toil.defaultCompleteMode = ToilCompleteMode.PatherArrival;
+ return toil;
+ }
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3/Source/JobGiver_Labor.cs b/Old sources/PrisonLabor v0.3/Source/JobGiver_Labor.cs
new file mode 100644
index 00000000..4ab08a8b
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3/Source/JobGiver_Labor.cs
@@ -0,0 +1,281 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+using Verse.AI;
+
+namespace PrisonLabor
+{
+ public class JobGiver_Labor : ThinkNode
+ {
+ public bool emergency;
+
+ public override ThinkNode DeepCopy(bool resolve = true)
+ {
+ JobGiver_Labor jobGiver_Work = (JobGiver_Labor)base.DeepCopy(resolve);
+ jobGiver_Work.emergency = this.emergency;
+ return jobGiver_Work;
+ }
+
+ public override float GetPriority(Pawn pawn)
+ {
+ if (pawn.workSettings == null || !pawn.workSettings.EverWork)
+ {
+ return 0f;
+ }
+ TimeAssignmentDef timeAssignmentDef = (pawn.timetable != null) ? pawn.timetable.CurrentAssignment : TimeAssignmentDefOf.Anything;
+ if (timeAssignmentDef == TimeAssignmentDefOf.Anything)
+ {
+ return 5.5f;
+ }
+ if (timeAssignmentDef == TimeAssignmentDefOf.Work)
+ {
+ return 9f;
+ }
+ if (timeAssignmentDef == TimeAssignmentDefOf.Sleep)
+ {
+ return 2f;
+ }
+ if (timeAssignmentDef == TimeAssignmentDefOf.Joy)
+ {
+ return 2f;
+ }
+ throw new NotImplementedException();
+ }
+
+ public override ThinkResult TryIssueJobPackage(Pawn pawn, JobIssueParams jobParams)
+ {
+ //Check laziness
+ if( Laziness.pawn(pawn).IsLazy)
+ {
+ return ThinkResult.NoJob;
+ }
+ if (this.emergency && pawn.mindState.priorityWork.IsPrioritized)
+ {
+ List workGiversByPriority = pawn.mindState.priorityWork.WorkType.workGiversByPriority;
+ for (int i = 0; i < workGiversByPriority.Count; i++)
+ {
+ WorkGiver worker = workGiversByPriority[i].Worker;
+ Job job = this.GiverTryGiveJobPrioritized(pawn, worker, pawn.mindState.priorityWork.Cell);
+ if (job != null)
+ {
+ job.playerForced = true;
+ return new ThinkResult(job, this, new JobTag?(workGiversByPriority[i].tagToGive));
+ }
+ }
+ pawn.mindState.priorityWork.Clear();
+ }
+ //Work prisoners will do
+ List typeNameList = new List { "Cooking", "Mining", "PlantCutting", "Crafting", "Hauling", "Cleaning" };
+ List typeList = new List();
+ List workList = new List();
+ foreach (String workTypeName in typeNameList)
+ typeList.Add(DefDatabase.GetNamed(workTypeName, true));
+ foreach(WorkTypeDef workType in typeList.OrderBy(type => -pawn.skills.AverageOfRelevantSkillsFor(type)))
+ {
+ if (!pawn.story.WorkTypeIsDisabled(workType))
+ {
+ for (int m = 0; m < workType.workGiversByPriority.Count; m++)
+ {
+ WorkGiver worker = workType.workGiversByPriority[m].Worker;
+ if (!worker.def.emergency)
+ {
+ workList.Add(worker);
+ }
+ }
+ }
+ }
+
+ int num = -999;
+ TargetInfo targetInfo = TargetInfo.Invalid;
+ WorkGiver_Scanner workGiver_Scanner = null;
+ for (int j = 0; j < workList.Count; j++)
+ {
+ WorkGiver workGiver = workList[j];
+ if (workGiver.def.priorityInType != num && targetInfo.IsValid)
+ {
+ break;
+ }
+ if (this.PawnCanUseWorkGiver(pawn, workGiver))
+ {
+ try
+ {
+ Job job2 = workGiver.NonScanJob(pawn);
+ if (job2 != null)
+ {
+ return new ThinkResult(job2, this, new JobTag?(workList[j].def.tagToGive));
+ }
+ WorkGiver_Scanner scanner = workGiver as WorkGiver_Scanner;
+ if (scanner != null)
+ {
+ if (workGiver.def.scanThings)
+ {
+ Predicate predicate = (Thing t) => !t.IsForbidden(pawn) && scanner.HasJobOnThing(pawn, t, false);
+ IEnumerable enumerable = scanner.PotentialWorkThingsGlobal(pawn);
+ Thing thing;
+ if (scanner.Prioritized)
+ {
+ IEnumerable enumerable2 = enumerable;
+ if (enumerable2 == null)
+ {
+ enumerable2 = pawn.Map.listerThings.ThingsMatching(scanner.PotentialWorkThingRequest);
+ }
+ Predicate validator = predicate;
+ thing = GenClosest.ClosestThing_Global_Reachable(pawn.Position, pawn.Map, enumerable2, scanner.PathEndMode, TraverseParms.For(pawn, Danger.Deadly, TraverseMode.ByPawn, false), 9999f, validator, (Thing x) => scanner.GetPriority(pawn, x));
+ }
+ else
+ {
+ Predicate validator = predicate;
+ bool forceGlobalSearch = enumerable != null;
+ thing = GenClosest.ClosestThingReachable(pawn.Position, pawn.Map, scanner.PotentialWorkThingRequest, scanner.PathEndMode, TraverseParms.For(pawn, Danger.Deadly, TraverseMode.ByPawn, false), 9999f, validator, enumerable, 0, scanner.LocalRegionsToScanFirst, forceGlobalSearch, RegionType.Set_Passable, false);
+ }
+ if (thing != null)
+ {
+ targetInfo = thing;
+ workGiver_Scanner = scanner;
+ }
+ }
+ if (workGiver.def.scanCells)
+ {
+ IntVec3 position = pawn.Position;
+ float num2 = 99999f;
+ float num3 = -3.40282347E+38f;
+ bool prioritized = scanner.Prioritized;
+ foreach (IntVec3 current in scanner.PotentialWorkCellsGlobal(pawn))
+ {
+ bool flag = false;
+ float num4 = (float)(current - position).LengthHorizontalSquared;
+ if (prioritized)
+ {
+ if (!current.IsForbidden(pawn) && scanner.HasJobOnCell(pawn, current))
+ {
+ float priority = scanner.GetPriority(pawn, current);
+ if (priority > num3 || (priority == num3 && num4 < num2))
+ {
+ flag = true;
+ num3 = priority;
+ }
+ }
+ }
+ else if (num4 < num2 && !current.IsForbidden(pawn) && scanner.HasJobOnCell(pawn, current))
+ {
+ flag = true;
+ }
+ if (flag)
+ {
+ targetInfo = new TargetInfo(current, pawn.Map, false);
+ workGiver_Scanner = scanner;
+ num2 = num4;
+ }
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Log.Error(string.Concat(new object[]
+ {
+ pawn,
+ " threw exception in WorkGiver ",
+ workGiver.def.defName,
+ ": ",
+ ex.ToString()
+ }));
+ }
+ finally
+ {
+ }
+ if (targetInfo.IsValid)
+ {
+ pawn.mindState.lastGivenWorkType = workGiver.def.workType;
+ Job job3;
+ if (targetInfo.HasThing)
+ {
+ job3 = workGiver_Scanner.JobOnThing(pawn, targetInfo.Thing, false);
+ }
+ else
+ {
+ job3 = workGiver_Scanner.JobOnCell(pawn, targetInfo.Cell);
+ }
+ if (job3 != null)
+ {
+ return new ThinkResult(job3, this, new JobTag?(workList[j].def.tagToGive));
+ }
+ Log.ErrorOnce(string.Concat(new object[]
+ {
+ workGiver_Scanner,
+ " provided target ",
+ targetInfo,
+ " but yielded no actual job for pawn ",
+ pawn,
+ ". The CanGiveJob and JobOnX methods may not be synchronized."
+ }), 6112651);
+ }
+ num = workGiver.def.priorityInType;
+ }
+ }
+ return ThinkResult.NoJob;
+ }
+
+ private bool PawnCanUseWorkGiver(Pawn pawn, WorkGiver giver)
+ {
+ return !giver.ShouldSkip(pawn) && (giver.def.canBeDoneByNonColonists || pawn.IsPrisoner) && (pawn.story == null || !pawn.story.WorkTagIsDisabled(giver.def.workTags)) && giver.MissingRequiredCapacity(pawn) == null;
+ }
+
+ private Job GiverTryGiveJobPrioritized(Pawn pawn, WorkGiver giver, IntVec3 cell)
+ {
+ if (!this.PawnCanUseWorkGiver(pawn, giver))
+ {
+ return null;
+ }
+ try
+ {
+ Job job = giver.NonScanJob(pawn);
+ if (job != null)
+ {
+ Job result = job;
+ return result;
+ }
+ WorkGiver_Scanner scanner = giver as WorkGiver_Scanner;
+ if (scanner != null)
+ {
+ if (giver.def.scanThings)
+ {
+ Predicate predicate = (Thing t) => !t.IsForbidden(pawn) && scanner.HasJobOnThing(pawn, t, false);
+ List thingList = cell.GetThingList(pawn.Map);
+ for (int i = 0; i < thingList.Count; i++)
+ {
+ Thing thing = thingList[i];
+ if (scanner.PotentialWorkThingRequest.Accepts(thing) && predicate(thing))
+ {
+ pawn.mindState.lastGivenWorkType = giver.def.workType;
+ Job result = scanner.JobOnThing(pawn, thing, false);
+ return result;
+ }
+ }
+ }
+ if (giver.def.scanCells && !cell.IsForbidden(pawn) && scanner.HasJobOnCell(pawn, cell))
+ {
+ pawn.mindState.lastGivenWorkType = giver.def.workType;
+ Job result = scanner.JobOnCell(pawn, cell);
+ return result;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Log.Error(string.Concat(new object[]
+ {
+ pawn,
+ " threw exception in GiverTryGiveJobTargeted on WorkGiver ",
+ giver.def.defName,
+ ": ",
+ ex.ToString()
+ }));
+ }
+ return null;
+ }
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3/Source/Laziness.cs b/Old sources/PrisonLabor v0.3/Source/Laziness.cs
new file mode 100644
index 00000000..1d1eae6a
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3/Source/Laziness.cs
@@ -0,0 +1,125 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+
+namespace PrisonLabor
+{
+ class Laziness
+ {
+ internal const double LAZY_LEVEL = 0.8;
+ internal const double NEED_INSPIRATION_LEVEL = 0.5;
+ internal const double LAZY_RATE = 0.03;
+ internal const double INSPIRE_RATE = 0.15;
+ public const int WARDEN_CAPACITY = (int)(INSPIRE_RATE/LAZY_RATE);
+
+ private static Dictionary map = new Dictionary();
+ private static PrisonerInteractionModeDef pimDef;
+
+ private double level;
+ private bool needToBeInspired;
+ private bool isLazy;
+
+ public static PrisonerInteractionModeDef PimDef
+ {
+ get
+ {
+ if (pimDef == null)
+ pimDef = DefDatabase.GetNamed("PrisonLabor_workOption");
+ return pimDef;
+ }
+ }
+
+ private Laziness()
+ {
+ level = 0;
+ needToBeInspired = false;
+ }
+
+ public void change(double value)
+ {
+ level += value;
+ }
+
+ public bool IsLazy
+ {
+ get
+ {
+ return isLazy;
+ }
+ }
+
+ public bool NeedToBeInspired
+ {
+ get
+ {
+ return needToBeInspired;
+ }
+ }
+
+ public static Laziness pawn(Pawn pawn)
+ {
+ if (map.ContainsKey(pawn))
+ {
+ return map[pawn];
+ }
+ else
+ {
+ Laziness laz = new Laziness();
+ map.Add(pawn, laz);
+ return laz;
+ }
+
+ }
+
+ public static void tick(Pawn pawn)
+ {
+ Laziness laz = Laziness.pawn(pawn);
+ double oldValue = laz.level;
+
+ List pawnsInRoom = new List();
+ int prisonersCount = 0;
+ int wardensCount = 0;
+ foreach (IntVec3 cell in pawn.GetRoomGroup().Cells)
+ {
+ foreach (Thing thing in cell.GetThingList(pawn.Map))
+ {
+ if (thing is Pawn)
+ pawnsInRoom.Add((Pawn)thing);
+ }
+ }
+ foreach (Pawn p in pawnsInRoom)
+ {
+ // colonist nearby
+ if (p.IsFreeColonist)
+ wardensCount++;
+ if (p.IsPrisoner && p.guest.interactionMode == PimDef)
+ prisonersCount++;
+ }
+
+ laz.level = laz.level + LAZY_RATE < 1 ? laz.level + LAZY_RATE : 1;
+ double insipre = (wardensCount * INSPIRE_RATE) / prisonersCount;
+ laz.level = laz.level > insipre ? laz.level - insipre : 0;
+
+ if (laz.level == 0)
+ laz.needToBeInspired = false;
+ if (laz.level >= NEED_INSPIRATION_LEVEL && !laz.needToBeInspired)
+ laz.needToBeInspired = true;
+ if (laz.level >= LAZY_LEVEL && !laz.isLazy && wardensCount == 0)
+ {
+ laz.isLazy = true;
+ Messages.Message("Your prioner got lazy!", pawn, MessageSound.Standard);
+ Tutorials.LazyPrisoner();
+ }
+ else if (laz.isLazy && wardensCount > 0)
+ {
+ laz.isLazy = false;
+ }
+
+ // For Denugging
+ //Log.Message("Laziness of " + pawn.Name + " changed from " + oldValue + " to " + laz.level);
+ }
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3/Source/Prefs.cs b/Old sources/PrisonLabor v0.3/Source/Prefs.cs
new file mode 100644
index 00000000..ff07971a
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3/Source/Prefs.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Xml.Linq;
+using Verse;
+
+namespace PrisonLabor
+{
+ public static class PrisonLaborPrefs
+ {
+ private static PrisonLaborPrefsData data;
+ private static string prefsFilePath = Path.Combine(GenFilePaths.ConfigFolderPath, "PrisonData_Prefs.xml");
+
+ public static int Version
+ {
+ get
+ {
+ return PrisonLaborPrefs.data.version;
+ }
+ set
+ {
+ PrisonLaborPrefs.data.version = value;
+ PrisonLaborPrefs.Apply();
+ }
+ }
+
+ public static void Init()
+ {
+ bool flag = !new FileInfo(prefsFilePath).Exists;
+ PrisonLaborPrefs.data = new PrisonLaborPrefsData();
+ PrisonLaborPrefs.data = DirectXmlLoader.ItemFromXmlFile(prefsFilePath, true);
+ if (flag)
+ {
+ ;
+ }
+ }
+
+ public static void Save()
+ {
+ try
+ {
+ XDocument xDocument = new XDocument();
+ XElement content = DirectXmlSaver.XElementFromObject(PrisonLaborPrefs.data, typeof(PrisonLaborPrefsData));
+ xDocument.Add(content);
+ xDocument.Save(prefsFilePath);
+ }
+ catch (Exception ex)
+ {
+ GenUI.ErrorDialog("ProblemSavingFile".Translate(new object[]
+ {
+ prefsFilePath,
+ ex.ToString()
+ }));
+ Log.Error("Exception saving prefs: " + ex);
+ }
+ }
+
+ public static void Apply()
+ {
+ PrisonLaborPrefs.data.Apply();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Old sources/PrisonLabor v0.3/Source/PrefsData.cs b/Old sources/PrisonLabor v0.3/Source/PrefsData.cs
new file mode 100644
index 00000000..a116a45c
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3/Source/PrefsData.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace PrisonLabor
+{
+ public class PrisonLaborPrefsData
+ {
+ public int version = -1;
+
+ public PrisonLaborPrefsData()
+ {
+
+ }
+
+ public void Apply()
+ {
+ }
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3/Source/PrisonLabor.csproj b/Old sources/PrisonLabor v0.3/Source/PrisonLabor.csproj
new file mode 100644
index 00000000..2531773b
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3/Source/PrisonLabor.csproj
@@ -0,0 +1,76 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {97750974-4CB6-4D31-84A1-A3AA77B1E2EE}
+ Library
+ Properties
+ PrisonLabor
+ PrisonLabor
+ v3.5
+ 512
+
+
+
+ false
+ none
+ false
+ ..\Assemblies\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ False
+ ..\Assemblies\0Harmony.dll
+ True
+
+
+ ..\..\..\RimWorldWin_Data\Managed\Assembly-CSharp.dll
+ False
+
+
+
+
+
+
+
+
+ ..\..\..\RimWorldWin_Data\Managed\UnityEngine.dll
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Old sources/PrisonLabor v0.3/Source/PrisonerInteractionModeOf.cs b/Old sources/PrisonLabor v0.3/Source/PrisonerInteractionModeOf.cs
new file mode 100644
index 00000000..a9326e39
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3/Source/PrisonerInteractionModeOf.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+
+//Work in progress
+
+namespace RimWorldd
+{
+ [DefOf]
+ public static class PrisonerInteractionModeDefOf
+ {
+ public static PrisonerInteractionModeDef NoInteraction;
+
+ public static PrisonerInteractionModeDef Chat;
+
+ //public static PrisonerInteractionModeDef Work;
+
+ public static PrisonerInteractionModeDef AttemptRecruit;
+
+ public static PrisonerInteractionModeDef Release;
+
+ public static PrisonerInteractionModeDef Execution;
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3/Source/PrisonerInteractionModeUtility.cs b/Old sources/PrisonLabor v0.3/Source/PrisonerInteractionModeUtility.cs
new file mode 100644
index 00000000..bfbbca45
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3/Source/PrisonerInteractionModeUtility.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+
+//Work in progress
+
+namespace RimWorldd
+{
+ public static class PrisonerInteractionModeUtility
+ {
+ public static string GetLabel(this PrisonerInteractionModeDef mode)
+ {
+ if (mode == PrisonerInteractionModeDefOf.NoInteraction)
+ {
+ return "PrisonerNoInteraction".Translate();
+ }
+ if (mode == PrisonerInteractionModeDefOf.Chat)
+ {
+ return "PrisonerFriendlyChat".Translate();
+ }
+ /*
+ if (mode == PrisonerInteractionModeDefOf.Work)
+ {
+ return "Work";
+ }
+ */
+ if (mode == PrisonerInteractionModeDefOf.AttemptRecruit)
+ {
+ return "PrisonerAttemptRecruit".Translate();
+ }
+ if (mode == PrisonerInteractionModeDefOf.Release)
+ {
+ return "PrisonerRelease".Translate();
+ }
+ if (mode == PrisonerInteractionModeDefOf.Execution)
+ {
+ return "PrisonerExecution".Translate();
+ }
+ return "Mode needs label";
+ }
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3/Source/Properties/AssemblyInfo.cs b/Old sources/PrisonLabor v0.3/Source/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..7aeb72f4
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3/Source/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("PrisonLabor")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("PrisonLabor")]
+[assembly: AssemblyCopyright("Copyright © 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("97750974-4cb6-4d31-84a1-a3aa77b1e2ee")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("0.3.0.0")]
+[assembly: AssemblyFileVersion("0.3.0.0")]
diff --git a/Old sources/PrisonLabor v0.3/Source/ThinkNode_ConditionalIsForced.cs b/Old sources/PrisonLabor v0.3/Source/ThinkNode_ConditionalIsForced.cs
new file mode 100644
index 00000000..a78c2a09
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3/Source/ThinkNode_ConditionalIsForced.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+
+namespace PrisonLabor
+{
+ class ThinkNode_ConditionalIsForced : ThinkNode_Conditional
+ {
+ protected override bool Satisfied(Pawn pawn)
+ {
+ //can work
+ if (pawn.IsPrisoner)
+ {
+ //show tutorial
+ Tutorials.PrisonLabor();
+ if (pawn.guest.interactionMode == DefDatabase.GetNamed("PrisonLabor_workOption"))
+ {
+ //can't escape
+ IntVec3 c;
+ if (pawn.guest.PrisonerIsSecure && !RCellFinder.TryFindBestExitSpot(pawn, out c, TraverseMode.ByPawn))
+ {
+ //shouldn't rest (medical reasons)
+ if (!HealthAIUtility.ShouldSeekMedicalRest(pawn))
+ {
+ // TODO can't or don't want to change
+ if (true)
+ {
+ // needs satisfied (sleep, food, etc.)
+ // TODO add more
+ if (pawn.needs.food.CurCategory == HungerCategory.Fed &&
+ pawn.needs.rest.CurCategory == RestCategory.Rested)
+ {
+ Laziness.tick(pawn);
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3/Source/Tutorials.cs b/Old sources/PrisonLabor v0.3/Source/Tutorials.cs
new file mode 100644
index 00000000..54911857
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3/Source/Tutorials.cs
@@ -0,0 +1,34 @@
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Verse;
+
+namespace PrisonLabor
+{
+ class Tutorials
+ {
+ private static ConceptDef prisonLaborDef = DefDatabase.GetNamed("PrisonLabor", true);
+ private static ConceptDef lazyPrisonerDef = DefDatabase.GetNamed("LazyPrisoner", true);
+
+ public static void PrisonLabor()
+ {
+ if (!PlayerKnowledgeDatabase.IsComplete(prisonLaborDef))
+ Verse.Find.Tutor.learningReadout.TryActivateConcept(prisonLaborDef);
+ //Move it to point after map genration
+ if(Initialization.oldPlayerNotification)
+ {
+ Find.WindowStack.Add(new Dialog_MessageBox("PrisonLabor machanics has changed.\n\n 1. Prisoner Interaction mode must be set to \"Work\" instead of \"No Interaction\".\n 2. Now prisoners will get lazy if not supervised. Warden job will include watching prisoners to prevent that (you can always draft your colonists).", "Ok", null, null, null, "PrisonLabor - New mechanics", false));
+ Initialization.oldPlayerNotification = false;
+ }
+ }
+
+ public static void LazyPrisoner()
+ {
+ if (!PlayerKnowledgeDatabase.IsComplete(lazyPrisonerDef))
+ Verse.Find.Tutor.learningReadout.TryActivateConcept(lazyPrisonerDef);
+ }
+
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3/Source/VersionChecker.cs b/Old sources/PrisonLabor v0.3/Source/VersionChecker.cs
new file mode 100644
index 00000000..8a05cb36
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3/Source/VersionChecker.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+using Harmony;
+using System.Reflection;
+using System.Reflection.Emit;
+
+namespace PrisonLabor
+{
+ [StaticConstructorOnStartup]
+ class VersionChecker
+ {
+ static VersionChecker()
+ {
+ var harmony = HarmonyInstance.Create("Harmony_PrisonLabor");
+ harmony.PatchAll(Assembly.GetExecutingAssembly());
+ }
+ }
+
+ [HarmonyPatch(typeof(PrisonerInteractionModeUtility))]
+ [HarmonyPatch("GetLabel")]
+ [HarmonyPatch(new Type[] { typeof(PrisonerInteractionModeDef) })]
+ class Patch
+ {
+ static IEnumerable Transpiler(ILGenerator gen, MethodBase mBase, IEnumerable instr)
+ {
+ // create our WORK label
+ Label jumpTo = gen.DefineLabel();
+ yield return new CodeInstruction(OpCodes.Ldarg_0);
+ yield return new CodeInstruction(OpCodes.Call, typeof(Patch).GetMethod("getLabelWork"));
+ //yield return new CodeInstruction(OpCodes.Dup);
+ yield return new CodeInstruction(OpCodes.Ldstr, "Work");
+ yield return new CodeInstruction(OpCodes.Bne_Un, jumpTo);
+ yield return new CodeInstruction(OpCodes.Ldstr, "Work");
+ yield return new CodeInstruction(OpCodes.Ret);
+
+ bool first = true;
+ foreach (CodeInstruction ci in instr)
+ {
+ if (first)
+ {
+ first = false;
+ ci.labels.Add(jumpTo);
+ }
+ //debug
+ Log.Message("CODE: ToString():" + ci.ToString() + " || labels:" + ci.labels + " || opcode:" + ci.opcode.ToString());
+ yield return ci;
+ }
+ }
+
+ public static string getLabelWork(PrisonerInteractionModeDef def)
+ {
+ if(def == DefDatabase.GetNamed("PrisonLabor_workOption"))
+ return "Work";
+ return "";
+ }
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3/Source/WorkGiver_Supervise.cs b/Old sources/PrisonLabor v0.3/Source/WorkGiver_Supervise.cs
new file mode 100644
index 00000000..e6eddb98
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3/Source/WorkGiver_Supervise.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+using Verse.AI;
+
+namespace PrisonLabor
+{
+ class WorkGiver_Supervise : WorkGiver_Warden
+ {
+ public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false)
+ {
+ if (!base.ShouldTakeCareOfPrisoner(pawn, t) || !Laziness.pawn((Pawn)t).NeedToBeInspired)
+ {
+ return null;
+ }
+ Pawn pawn2 = (Pawn)t;
+ if (pawn2.guest.interactionMode == DefDatabase.GetNamed("PrisonLabor_workOption") && (!pawn2.Downed || pawn2.InBed()) && pawn.CanReserve(t, 1, -1, null, false) && pawn2.Awake())
+ {
+ return new Job(DefDatabase.GetNamed("PrisonerSupervise"), t);
+ }
+ return null;
+ }
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3a/Source/ConceptDef_PrisonLaborInstruction.cs b/Old sources/PrisonLabor v0.3a/Source/ConceptDef_PrisonLaborInstruction.cs
new file mode 100644
index 00000000..acc52ab7
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3a/Source/ConceptDef_PrisonLaborInstruction.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+
+namespace PrisonLabor
+{
+ class ConceptDef_PrisonLaborInstruction : ConceptDef
+ {
+
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3a/Source/InfoDialog.cs b/Old sources/PrisonLabor v0.3a/Source/InfoDialog.cs
new file mode 100644
index 00000000..2b2a4716
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3a/Source/InfoDialog.cs
@@ -0,0 +1,104 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+using UnityEngine;
+
+namespace PrisonLabor
+{
+ class InfoDialog : Window
+ {
+ private string searchName = string.Empty;
+
+ private string[] searchWords;
+
+ private List cachedNames;
+
+ public override Vector2 InitialSize
+ {
+ get
+ {
+ return new Vector2(400f, 650f);
+ }
+ }
+
+ public InfoDialog()
+ {
+ this.doCloseButton = true;
+ this.absorbInputAroundWindow = true;
+ this.cachedNames = (from n in (from b in SolidBioDatabase.allBios
+ select b.name).Concat(PawnNameDatabaseSolid.AllNames())
+ orderby n.Last descending
+ select n).ToList();
+ }
+
+ public override void DoWindowContents(Rect inRect)
+ {
+ Listing_Standard listing_Standard = new Listing_Standard();
+ listing_Standard.Begin(inRect);
+ listing_Standard.Label("TypeFirstNickOrLastName".Translate(), -1f);
+ string text = listing_Standard.TextEntry(this.searchName, 1);
+ if (text.Length < 20)
+ {
+ this.searchName = text;
+ this.searchWords = this.searchName.Replace("'", string.Empty).Split(new char[]
+ {
+ ' '
+ });
+ }
+ listing_Standard.Gap(4f);
+ if (this.searchName.Length > 1)
+ {
+ foreach (NameTriple current in this.cachedNames.Where(new Func(this.FilterMatch)))
+ {
+ if (listing_Standard.ButtonText(current.ToString(), null))
+ {
+ this.TryChooseName(current);
+ }
+ if (listing_Standard.CurHeight + 30f > inRect.height - (this.CloseButSize.y + 8f))
+ {
+ break;
+ }
+ }
+ }
+ listing_Standard.End();
+ }
+
+ private bool FilterMatch(NameTriple n)
+ {
+ if (n.First == "Tynan" && n.Last == "Sylvester")
+ {
+ return false;
+ }
+ if (this.searchWords.Length == 0)
+ {
+ return false;
+ }
+ if (this.searchWords.Length == 1)
+ {
+ return n.Last.StartsWith(this.searchName, StringComparison.OrdinalIgnoreCase) || n.First.StartsWith(this.searchName, StringComparison.OrdinalIgnoreCase) || n.Nick.StartsWith(this.searchName, StringComparison.OrdinalIgnoreCase);
+ }
+ return this.searchWords.Length == 2 && n.First.EqualsIgnoreCase(this.searchWords[0]) && (n.Last.StartsWith(this.searchWords[1], StringComparison.OrdinalIgnoreCase) || n.Nick.StartsWith(this.searchWords[1], StringComparison.OrdinalIgnoreCase));
+ }
+
+ private void TryChooseName(NameTriple name)
+ {
+ if (this.AlreadyPreferred(name))
+ {
+ Messages.Message("MessageAlreadyPreferredName".Translate(), MessageSound.RejectInput);
+ }
+ else
+ {
+ Prefs.PreferredNames.Add(name.ToString());
+ this.Close(true);
+ }
+ }
+
+ private bool AlreadyPreferred(NameTriple name)
+ {
+ return Prefs.PreferredNames.Contains(name.ToString());
+ }
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3a/Source/Initialization.cs b/Old sources/PrisonLabor v0.3a/Source/Initialization.cs
new file mode 100644
index 00000000..5f5e5dee
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3a/Source/Initialization.cs
@@ -0,0 +1,86 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+using Harmony;
+using System.Reflection;
+using System.Reflection.Emit;
+
+namespace PrisonLabor
+{
+ [StaticConstructorOnStartup]
+ class Initialization
+ {
+ public static int version = 3;
+ public static bool oldPlayerNotification = false;
+
+ static Initialization()
+ {
+ var harmony = HarmonyInstance.Create("Harmony_PrisonLabor");
+ harmony.PatchAll(Assembly.GetExecutingAssembly());
+
+ PrisonLaborPrefs.Init();
+ checkVersion();
+ }
+
+ private static void checkVersion()
+ {
+ if(PrisonLaborPrefs.Version < 3)
+ {
+ // only way to check if mod was installed before
+ if (PlayerKnowledgeDatabase.IsComplete(DefDatabase.GetNamed("PrisonLabor")))
+ {
+ Log.Message("Detected older version of PrisonLabor");
+ oldPlayerNotification = true;
+ }
+ }
+ else
+ {
+ Log.Message("Detected PrisonLabor v" + PrisonLaborPrefs.Version);
+ }
+ PrisonLaborPrefs.Version = 3;
+ PrisonLaborPrefs.Save();
+ }
+ }
+
+ [HarmonyPatch(typeof(PrisonerInteractionModeUtility))]
+ [HarmonyPatch("GetLabel")]
+ [HarmonyPatch(new Type[] { typeof(PrisonerInteractionModeDef) })]
+ class Patch
+ {
+ static IEnumerable Transpiler(ILGenerator gen, MethodBase mBase, IEnumerable instr)
+ {
+ // create our WORK label
+ Label jumpTo = gen.DefineLabel();
+ yield return new CodeInstruction(OpCodes.Ldarg_0);
+ yield return new CodeInstruction(OpCodes.Call, typeof(Patch).GetMethod("getLabelWork"));
+ //yield return new CodeInstruction(OpCodes.Dup);
+ yield return new CodeInstruction(OpCodes.Ldstr, "Work");
+ yield return new CodeInstruction(OpCodes.Bne_Un, jumpTo);
+ yield return new CodeInstruction(OpCodes.Ldstr, "Work");
+ yield return new CodeInstruction(OpCodes.Ret);
+
+ bool first = true;
+ foreach (CodeInstruction ci in instr)
+ {
+ if (first)
+ {
+ first = false;
+ ci.labels.Add(jumpTo);
+ }
+ //debug
+ //Log.Message("CODE: ToString():" + ci.ToString() + " || labels:" + ci.labels + " || opcode:" + ci.opcode.ToString());
+ yield return ci;
+ }
+ }
+
+ public static string getLabelWork(PrisonerInteractionModeDef def)
+ {
+ if(def == DefDatabase.GetNamed("PrisonLabor_workOption"))
+ return "Work";
+ return "";
+ }
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3a/Source/JobDriver_Supervise.cs b/Old sources/PrisonLabor v0.3a/Source/JobDriver_Supervise.cs
new file mode 100644
index 00000000..e6fad431
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3a/Source/JobDriver_Supervise.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+using Verse.AI;
+
+namespace PrisonLabor
+{
+ class JobDriver_Supervise : JobDriver
+ {
+ protected Pawn Prisoner
+ {
+ get
+ {
+ return (Pawn)base.CurJob.targetA.Thing;
+ }
+ }
+
+ protected override IEnumerable MakeNewToils()
+ {
+ this.FailOnDespawnedOrNull(TargetIndex.A);
+ this.FailOnMentalState(TargetIndex.A);
+ this.FailOnNotAwake(TargetIndex.A);
+ this.FailOn(() => !Prisoner.IsPrisonerOfColony || !Prisoner.guest.PrisonerIsSecure);
+
+ yield return Toils_Reserve.Reserve(TargetIndex.A, 1, -1, null);
+ //yield return Toils_Interpersonal.GotoPrisoner(this.pawn, this.Prisoner, this.Prisoner.guest.interactionMode);
+ yield return MakeWatchToil(Prisoner);
+ for(int i = 0; i < 80; i++)
+ yield return Toils_General.Wait(10).FailOn(() => Prisoner.GetRoom() != pawn.GetRoom());
+ yield return MakeWatchToil(Prisoner);
+ for (int i = 0; i < 80; i++)
+ yield return Toils_General.Wait(10).FailOn(() => Prisoner.GetRoom() != pawn.GetRoom());
+ yield return Toils_Interpersonal.SetLastInteractTime(TargetIndex.A);
+ }
+
+ protected Toil MakeWatchToil(Pawn prisoner)
+ {
+ Toil toil = new Toil();
+ toil.initAction = delegate
+ {
+ Pawn actor = toil.actor;
+ IntVec3 ind;
+ if (Prisoner.GetRoom().Cells.Any(cell => cell.DistanceTo(Prisoner.InteractionCell) < 7 && cell.DistanceTo(Prisoner.InteractionCell) > 4))
+ ind = prisoner.GetRoom().Cells.Where(cell => cell.DistanceTo(prisoner.InteractionCell) < 7 && cell.DistanceTo(prisoner.InteractionCell) > 4).RandomElement();
+ else
+ ind = prisoner.GetRoom().Cells.RandomElement();
+ actor.pather.StartPath(ind, PathEndMode.OnCell);
+ };
+ toil.defaultCompleteMode = ToilCompleteMode.PatherArrival;
+ return toil;
+ }
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3a/Source/JobGiver_Labor.cs b/Old sources/PrisonLabor v0.3a/Source/JobGiver_Labor.cs
new file mode 100644
index 00000000..4ab08a8b
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3a/Source/JobGiver_Labor.cs
@@ -0,0 +1,281 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+using Verse.AI;
+
+namespace PrisonLabor
+{
+ public class JobGiver_Labor : ThinkNode
+ {
+ public bool emergency;
+
+ public override ThinkNode DeepCopy(bool resolve = true)
+ {
+ JobGiver_Labor jobGiver_Work = (JobGiver_Labor)base.DeepCopy(resolve);
+ jobGiver_Work.emergency = this.emergency;
+ return jobGiver_Work;
+ }
+
+ public override float GetPriority(Pawn pawn)
+ {
+ if (pawn.workSettings == null || !pawn.workSettings.EverWork)
+ {
+ return 0f;
+ }
+ TimeAssignmentDef timeAssignmentDef = (pawn.timetable != null) ? pawn.timetable.CurrentAssignment : TimeAssignmentDefOf.Anything;
+ if (timeAssignmentDef == TimeAssignmentDefOf.Anything)
+ {
+ return 5.5f;
+ }
+ if (timeAssignmentDef == TimeAssignmentDefOf.Work)
+ {
+ return 9f;
+ }
+ if (timeAssignmentDef == TimeAssignmentDefOf.Sleep)
+ {
+ return 2f;
+ }
+ if (timeAssignmentDef == TimeAssignmentDefOf.Joy)
+ {
+ return 2f;
+ }
+ throw new NotImplementedException();
+ }
+
+ public override ThinkResult TryIssueJobPackage(Pawn pawn, JobIssueParams jobParams)
+ {
+ //Check laziness
+ if( Laziness.pawn(pawn).IsLazy)
+ {
+ return ThinkResult.NoJob;
+ }
+ if (this.emergency && pawn.mindState.priorityWork.IsPrioritized)
+ {
+ List workGiversByPriority = pawn.mindState.priorityWork.WorkType.workGiversByPriority;
+ for (int i = 0; i < workGiversByPriority.Count; i++)
+ {
+ WorkGiver worker = workGiversByPriority[i].Worker;
+ Job job = this.GiverTryGiveJobPrioritized(pawn, worker, pawn.mindState.priorityWork.Cell);
+ if (job != null)
+ {
+ job.playerForced = true;
+ return new ThinkResult(job, this, new JobTag?(workGiversByPriority[i].tagToGive));
+ }
+ }
+ pawn.mindState.priorityWork.Clear();
+ }
+ //Work prisoners will do
+ List typeNameList = new List { "Cooking", "Mining", "PlantCutting", "Crafting", "Hauling", "Cleaning" };
+ List typeList = new List();
+ List workList = new List();
+ foreach (String workTypeName in typeNameList)
+ typeList.Add(DefDatabase.GetNamed(workTypeName, true));
+ foreach(WorkTypeDef workType in typeList.OrderBy(type => -pawn.skills.AverageOfRelevantSkillsFor(type)))
+ {
+ if (!pawn.story.WorkTypeIsDisabled(workType))
+ {
+ for (int m = 0; m < workType.workGiversByPriority.Count; m++)
+ {
+ WorkGiver worker = workType.workGiversByPriority[m].Worker;
+ if (!worker.def.emergency)
+ {
+ workList.Add(worker);
+ }
+ }
+ }
+ }
+
+ int num = -999;
+ TargetInfo targetInfo = TargetInfo.Invalid;
+ WorkGiver_Scanner workGiver_Scanner = null;
+ for (int j = 0; j < workList.Count; j++)
+ {
+ WorkGiver workGiver = workList[j];
+ if (workGiver.def.priorityInType != num && targetInfo.IsValid)
+ {
+ break;
+ }
+ if (this.PawnCanUseWorkGiver(pawn, workGiver))
+ {
+ try
+ {
+ Job job2 = workGiver.NonScanJob(pawn);
+ if (job2 != null)
+ {
+ return new ThinkResult(job2, this, new JobTag?(workList[j].def.tagToGive));
+ }
+ WorkGiver_Scanner scanner = workGiver as WorkGiver_Scanner;
+ if (scanner != null)
+ {
+ if (workGiver.def.scanThings)
+ {
+ Predicate predicate = (Thing t) => !t.IsForbidden(pawn) && scanner.HasJobOnThing(pawn, t, false);
+ IEnumerable enumerable = scanner.PotentialWorkThingsGlobal(pawn);
+ Thing thing;
+ if (scanner.Prioritized)
+ {
+ IEnumerable enumerable2 = enumerable;
+ if (enumerable2 == null)
+ {
+ enumerable2 = pawn.Map.listerThings.ThingsMatching(scanner.PotentialWorkThingRequest);
+ }
+ Predicate validator = predicate;
+ thing = GenClosest.ClosestThing_Global_Reachable(pawn.Position, pawn.Map, enumerable2, scanner.PathEndMode, TraverseParms.For(pawn, Danger.Deadly, TraverseMode.ByPawn, false), 9999f, validator, (Thing x) => scanner.GetPriority(pawn, x));
+ }
+ else
+ {
+ Predicate validator = predicate;
+ bool forceGlobalSearch = enumerable != null;
+ thing = GenClosest.ClosestThingReachable(pawn.Position, pawn.Map, scanner.PotentialWorkThingRequest, scanner.PathEndMode, TraverseParms.For(pawn, Danger.Deadly, TraverseMode.ByPawn, false), 9999f, validator, enumerable, 0, scanner.LocalRegionsToScanFirst, forceGlobalSearch, RegionType.Set_Passable, false);
+ }
+ if (thing != null)
+ {
+ targetInfo = thing;
+ workGiver_Scanner = scanner;
+ }
+ }
+ if (workGiver.def.scanCells)
+ {
+ IntVec3 position = pawn.Position;
+ float num2 = 99999f;
+ float num3 = -3.40282347E+38f;
+ bool prioritized = scanner.Prioritized;
+ foreach (IntVec3 current in scanner.PotentialWorkCellsGlobal(pawn))
+ {
+ bool flag = false;
+ float num4 = (float)(current - position).LengthHorizontalSquared;
+ if (prioritized)
+ {
+ if (!current.IsForbidden(pawn) && scanner.HasJobOnCell(pawn, current))
+ {
+ float priority = scanner.GetPriority(pawn, current);
+ if (priority > num3 || (priority == num3 && num4 < num2))
+ {
+ flag = true;
+ num3 = priority;
+ }
+ }
+ }
+ else if (num4 < num2 && !current.IsForbidden(pawn) && scanner.HasJobOnCell(pawn, current))
+ {
+ flag = true;
+ }
+ if (flag)
+ {
+ targetInfo = new TargetInfo(current, pawn.Map, false);
+ workGiver_Scanner = scanner;
+ num2 = num4;
+ }
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Log.Error(string.Concat(new object[]
+ {
+ pawn,
+ " threw exception in WorkGiver ",
+ workGiver.def.defName,
+ ": ",
+ ex.ToString()
+ }));
+ }
+ finally
+ {
+ }
+ if (targetInfo.IsValid)
+ {
+ pawn.mindState.lastGivenWorkType = workGiver.def.workType;
+ Job job3;
+ if (targetInfo.HasThing)
+ {
+ job3 = workGiver_Scanner.JobOnThing(pawn, targetInfo.Thing, false);
+ }
+ else
+ {
+ job3 = workGiver_Scanner.JobOnCell(pawn, targetInfo.Cell);
+ }
+ if (job3 != null)
+ {
+ return new ThinkResult(job3, this, new JobTag?(workList[j].def.tagToGive));
+ }
+ Log.ErrorOnce(string.Concat(new object[]
+ {
+ workGiver_Scanner,
+ " provided target ",
+ targetInfo,
+ " but yielded no actual job for pawn ",
+ pawn,
+ ". The CanGiveJob and JobOnX methods may not be synchronized."
+ }), 6112651);
+ }
+ num = workGiver.def.priorityInType;
+ }
+ }
+ return ThinkResult.NoJob;
+ }
+
+ private bool PawnCanUseWorkGiver(Pawn pawn, WorkGiver giver)
+ {
+ return !giver.ShouldSkip(pawn) && (giver.def.canBeDoneByNonColonists || pawn.IsPrisoner) && (pawn.story == null || !pawn.story.WorkTagIsDisabled(giver.def.workTags)) && giver.MissingRequiredCapacity(pawn) == null;
+ }
+
+ private Job GiverTryGiveJobPrioritized(Pawn pawn, WorkGiver giver, IntVec3 cell)
+ {
+ if (!this.PawnCanUseWorkGiver(pawn, giver))
+ {
+ return null;
+ }
+ try
+ {
+ Job job = giver.NonScanJob(pawn);
+ if (job != null)
+ {
+ Job result = job;
+ return result;
+ }
+ WorkGiver_Scanner scanner = giver as WorkGiver_Scanner;
+ if (scanner != null)
+ {
+ if (giver.def.scanThings)
+ {
+ Predicate predicate = (Thing t) => !t.IsForbidden(pawn) && scanner.HasJobOnThing(pawn, t, false);
+ List thingList = cell.GetThingList(pawn.Map);
+ for (int i = 0; i < thingList.Count; i++)
+ {
+ Thing thing = thingList[i];
+ if (scanner.PotentialWorkThingRequest.Accepts(thing) && predicate(thing))
+ {
+ pawn.mindState.lastGivenWorkType = giver.def.workType;
+ Job result = scanner.JobOnThing(pawn, thing, false);
+ return result;
+ }
+ }
+ }
+ if (giver.def.scanCells && !cell.IsForbidden(pawn) && scanner.HasJobOnCell(pawn, cell))
+ {
+ pawn.mindState.lastGivenWorkType = giver.def.workType;
+ Job result = scanner.JobOnCell(pawn, cell);
+ return result;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Log.Error(string.Concat(new object[]
+ {
+ pawn,
+ " threw exception in GiverTryGiveJobTargeted on WorkGiver ",
+ giver.def.defName,
+ ": ",
+ ex.ToString()
+ }));
+ }
+ return null;
+ }
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3a/Source/Laziness.cs b/Old sources/PrisonLabor v0.3a/Source/Laziness.cs
new file mode 100644
index 00000000..1d1eae6a
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3a/Source/Laziness.cs
@@ -0,0 +1,125 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+
+namespace PrisonLabor
+{
+ class Laziness
+ {
+ internal const double LAZY_LEVEL = 0.8;
+ internal const double NEED_INSPIRATION_LEVEL = 0.5;
+ internal const double LAZY_RATE = 0.03;
+ internal const double INSPIRE_RATE = 0.15;
+ public const int WARDEN_CAPACITY = (int)(INSPIRE_RATE/LAZY_RATE);
+
+ private static Dictionary map = new Dictionary();
+ private static PrisonerInteractionModeDef pimDef;
+
+ private double level;
+ private bool needToBeInspired;
+ private bool isLazy;
+
+ public static PrisonerInteractionModeDef PimDef
+ {
+ get
+ {
+ if (pimDef == null)
+ pimDef = DefDatabase.GetNamed("PrisonLabor_workOption");
+ return pimDef;
+ }
+ }
+
+ private Laziness()
+ {
+ level = 0;
+ needToBeInspired = false;
+ }
+
+ public void change(double value)
+ {
+ level += value;
+ }
+
+ public bool IsLazy
+ {
+ get
+ {
+ return isLazy;
+ }
+ }
+
+ public bool NeedToBeInspired
+ {
+ get
+ {
+ return needToBeInspired;
+ }
+ }
+
+ public static Laziness pawn(Pawn pawn)
+ {
+ if (map.ContainsKey(pawn))
+ {
+ return map[pawn];
+ }
+ else
+ {
+ Laziness laz = new Laziness();
+ map.Add(pawn, laz);
+ return laz;
+ }
+
+ }
+
+ public static void tick(Pawn pawn)
+ {
+ Laziness laz = Laziness.pawn(pawn);
+ double oldValue = laz.level;
+
+ List pawnsInRoom = new List();
+ int prisonersCount = 0;
+ int wardensCount = 0;
+ foreach (IntVec3 cell in pawn.GetRoomGroup().Cells)
+ {
+ foreach (Thing thing in cell.GetThingList(pawn.Map))
+ {
+ if (thing is Pawn)
+ pawnsInRoom.Add((Pawn)thing);
+ }
+ }
+ foreach (Pawn p in pawnsInRoom)
+ {
+ // colonist nearby
+ if (p.IsFreeColonist)
+ wardensCount++;
+ if (p.IsPrisoner && p.guest.interactionMode == PimDef)
+ prisonersCount++;
+ }
+
+ laz.level = laz.level + LAZY_RATE < 1 ? laz.level + LAZY_RATE : 1;
+ double insipre = (wardensCount * INSPIRE_RATE) / prisonersCount;
+ laz.level = laz.level > insipre ? laz.level - insipre : 0;
+
+ if (laz.level == 0)
+ laz.needToBeInspired = false;
+ if (laz.level >= NEED_INSPIRATION_LEVEL && !laz.needToBeInspired)
+ laz.needToBeInspired = true;
+ if (laz.level >= LAZY_LEVEL && !laz.isLazy && wardensCount == 0)
+ {
+ laz.isLazy = true;
+ Messages.Message("Your prioner got lazy!", pawn, MessageSound.Standard);
+ Tutorials.LazyPrisoner();
+ }
+ else if (laz.isLazy && wardensCount > 0)
+ {
+ laz.isLazy = false;
+ }
+
+ // For Denugging
+ //Log.Message("Laziness of " + pawn.Name + " changed from " + oldValue + " to " + laz.level);
+ }
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3a/Source/Prefs.cs b/Old sources/PrisonLabor v0.3a/Source/Prefs.cs
new file mode 100644
index 00000000..ff07971a
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3a/Source/Prefs.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Xml.Linq;
+using Verse;
+
+namespace PrisonLabor
+{
+ public static class PrisonLaborPrefs
+ {
+ private static PrisonLaborPrefsData data;
+ private static string prefsFilePath = Path.Combine(GenFilePaths.ConfigFolderPath, "PrisonData_Prefs.xml");
+
+ public static int Version
+ {
+ get
+ {
+ return PrisonLaborPrefs.data.version;
+ }
+ set
+ {
+ PrisonLaborPrefs.data.version = value;
+ PrisonLaborPrefs.Apply();
+ }
+ }
+
+ public static void Init()
+ {
+ bool flag = !new FileInfo(prefsFilePath).Exists;
+ PrisonLaborPrefs.data = new PrisonLaborPrefsData();
+ PrisonLaborPrefs.data = DirectXmlLoader.ItemFromXmlFile(prefsFilePath, true);
+ if (flag)
+ {
+ ;
+ }
+ }
+
+ public static void Save()
+ {
+ try
+ {
+ XDocument xDocument = new XDocument();
+ XElement content = DirectXmlSaver.XElementFromObject(PrisonLaborPrefs.data, typeof(PrisonLaborPrefsData));
+ xDocument.Add(content);
+ xDocument.Save(prefsFilePath);
+ }
+ catch (Exception ex)
+ {
+ GenUI.ErrorDialog("ProblemSavingFile".Translate(new object[]
+ {
+ prefsFilePath,
+ ex.ToString()
+ }));
+ Log.Error("Exception saving prefs: " + ex);
+ }
+ }
+
+ public static void Apply()
+ {
+ PrisonLaborPrefs.data.Apply();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Old sources/PrisonLabor v0.3a/Source/PrefsData.cs b/Old sources/PrisonLabor v0.3a/Source/PrefsData.cs
new file mode 100644
index 00000000..a116a45c
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3a/Source/PrefsData.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace PrisonLabor
+{
+ public class PrisonLaborPrefsData
+ {
+ public int version = -1;
+
+ public PrisonLaborPrefsData()
+ {
+
+ }
+
+ public void Apply()
+ {
+ }
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3a/Source/PrisonLabor.csproj b/Old sources/PrisonLabor v0.3a/Source/PrisonLabor.csproj
new file mode 100644
index 00000000..d1a1f2f1
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3a/Source/PrisonLabor.csproj
@@ -0,0 +1,76 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {97750974-4CB6-4D31-84A1-A3AA77B1E2EE}
+ Library
+ Properties
+ PrisonLabor
+ PrisonLabor
+ v3.5
+ 512
+
+
+
+ false
+ none
+ false
+ ..\Assemblies\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ False
+ ..\Assemblies\0Harmony.dll
+ True
+
+
+ ..\..\..\..\Games\SteamLibrary\SteamApps\common\RimWorld\RimWorldWin_Data\Managed\Assembly-CSharp.dll
+ False
+
+
+
+
+
+
+
+
+ ..\..\..\..\Games\SteamLibrary\SteamApps\common\RimWorld\RimWorldWin_Data\Managed\UnityEngine.dll
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Old sources/PrisonLabor v0.3a/Source/PrisonerInteractionModeOf.cs b/Old sources/PrisonLabor v0.3a/Source/PrisonerInteractionModeOf.cs
new file mode 100644
index 00000000..a9326e39
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3a/Source/PrisonerInteractionModeOf.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+
+//Work in progress
+
+namespace RimWorldd
+{
+ [DefOf]
+ public static class PrisonerInteractionModeDefOf
+ {
+ public static PrisonerInteractionModeDef NoInteraction;
+
+ public static PrisonerInteractionModeDef Chat;
+
+ //public static PrisonerInteractionModeDef Work;
+
+ public static PrisonerInteractionModeDef AttemptRecruit;
+
+ public static PrisonerInteractionModeDef Release;
+
+ public static PrisonerInteractionModeDef Execution;
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3a/Source/PrisonerInteractionModeUtility.cs b/Old sources/PrisonLabor v0.3a/Source/PrisonerInteractionModeUtility.cs
new file mode 100644
index 00000000..bfbbca45
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3a/Source/PrisonerInteractionModeUtility.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+
+//Work in progress
+
+namespace RimWorldd
+{
+ public static class PrisonerInteractionModeUtility
+ {
+ public static string GetLabel(this PrisonerInteractionModeDef mode)
+ {
+ if (mode == PrisonerInteractionModeDefOf.NoInteraction)
+ {
+ return "PrisonerNoInteraction".Translate();
+ }
+ if (mode == PrisonerInteractionModeDefOf.Chat)
+ {
+ return "PrisonerFriendlyChat".Translate();
+ }
+ /*
+ if (mode == PrisonerInteractionModeDefOf.Work)
+ {
+ return "Work";
+ }
+ */
+ if (mode == PrisonerInteractionModeDefOf.AttemptRecruit)
+ {
+ return "PrisonerAttemptRecruit".Translate();
+ }
+ if (mode == PrisonerInteractionModeDefOf.Release)
+ {
+ return "PrisonerRelease".Translate();
+ }
+ if (mode == PrisonerInteractionModeDefOf.Execution)
+ {
+ return "PrisonerExecution".Translate();
+ }
+ return "Mode needs label";
+ }
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3a/Source/Properties/AssemblyInfo.cs b/Old sources/PrisonLabor v0.3a/Source/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..7aeb72f4
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3a/Source/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("PrisonLabor")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("PrisonLabor")]
+[assembly: AssemblyCopyright("Copyright © 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("97750974-4cb6-4d31-84a1-a3aa77b1e2ee")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("0.3.0.0")]
+[assembly: AssemblyFileVersion("0.3.0.0")]
diff --git a/Old sources/PrisonLabor v0.3a/Source/ThinkNode_ConditionalIsForced.cs b/Old sources/PrisonLabor v0.3a/Source/ThinkNode_ConditionalIsForced.cs
new file mode 100644
index 00000000..a78c2a09
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3a/Source/ThinkNode_ConditionalIsForced.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+
+namespace PrisonLabor
+{
+ class ThinkNode_ConditionalIsForced : ThinkNode_Conditional
+ {
+ protected override bool Satisfied(Pawn pawn)
+ {
+ //can work
+ if (pawn.IsPrisoner)
+ {
+ //show tutorial
+ Tutorials.PrisonLabor();
+ if (pawn.guest.interactionMode == DefDatabase.GetNamed("PrisonLabor_workOption"))
+ {
+ //can't escape
+ IntVec3 c;
+ if (pawn.guest.PrisonerIsSecure && !RCellFinder.TryFindBestExitSpot(pawn, out c, TraverseMode.ByPawn))
+ {
+ //shouldn't rest (medical reasons)
+ if (!HealthAIUtility.ShouldSeekMedicalRest(pawn))
+ {
+ // TODO can't or don't want to change
+ if (true)
+ {
+ // needs satisfied (sleep, food, etc.)
+ // TODO add more
+ if (pawn.needs.food.CurCategory == HungerCategory.Fed &&
+ pawn.needs.rest.CurCategory == RestCategory.Rested)
+ {
+ Laziness.tick(pawn);
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3a/Source/Tutorials.cs b/Old sources/PrisonLabor v0.3a/Source/Tutorials.cs
new file mode 100644
index 00000000..54911857
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3a/Source/Tutorials.cs
@@ -0,0 +1,34 @@
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Verse;
+
+namespace PrisonLabor
+{
+ class Tutorials
+ {
+ private static ConceptDef prisonLaborDef = DefDatabase.GetNamed("PrisonLabor", true);
+ private static ConceptDef lazyPrisonerDef = DefDatabase.GetNamed("LazyPrisoner", true);
+
+ public static void PrisonLabor()
+ {
+ if (!PlayerKnowledgeDatabase.IsComplete(prisonLaborDef))
+ Verse.Find.Tutor.learningReadout.TryActivateConcept(prisonLaborDef);
+ //Move it to point after map genration
+ if(Initialization.oldPlayerNotification)
+ {
+ Find.WindowStack.Add(new Dialog_MessageBox("PrisonLabor machanics has changed.\n\n 1. Prisoner Interaction mode must be set to \"Work\" instead of \"No Interaction\".\n 2. Now prisoners will get lazy if not supervised. Warden job will include watching prisoners to prevent that (you can always draft your colonists).", "Ok", null, null, null, "PrisonLabor - New mechanics", false));
+ Initialization.oldPlayerNotification = false;
+ }
+ }
+
+ public static void LazyPrisoner()
+ {
+ if (!PlayerKnowledgeDatabase.IsComplete(lazyPrisonerDef))
+ Verse.Find.Tutor.learningReadout.TryActivateConcept(lazyPrisonerDef);
+ }
+
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3a/Source/VersionChecker.cs b/Old sources/PrisonLabor v0.3a/Source/VersionChecker.cs
new file mode 100644
index 00000000..8a05cb36
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3a/Source/VersionChecker.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+using Harmony;
+using System.Reflection;
+using System.Reflection.Emit;
+
+namespace PrisonLabor
+{
+ [StaticConstructorOnStartup]
+ class VersionChecker
+ {
+ static VersionChecker()
+ {
+ var harmony = HarmonyInstance.Create("Harmony_PrisonLabor");
+ harmony.PatchAll(Assembly.GetExecutingAssembly());
+ }
+ }
+
+ [HarmonyPatch(typeof(PrisonerInteractionModeUtility))]
+ [HarmonyPatch("GetLabel")]
+ [HarmonyPatch(new Type[] { typeof(PrisonerInteractionModeDef) })]
+ class Patch
+ {
+ static IEnumerable Transpiler(ILGenerator gen, MethodBase mBase, IEnumerable instr)
+ {
+ // create our WORK label
+ Label jumpTo = gen.DefineLabel();
+ yield return new CodeInstruction(OpCodes.Ldarg_0);
+ yield return new CodeInstruction(OpCodes.Call, typeof(Patch).GetMethod("getLabelWork"));
+ //yield return new CodeInstruction(OpCodes.Dup);
+ yield return new CodeInstruction(OpCodes.Ldstr, "Work");
+ yield return new CodeInstruction(OpCodes.Bne_Un, jumpTo);
+ yield return new CodeInstruction(OpCodes.Ldstr, "Work");
+ yield return new CodeInstruction(OpCodes.Ret);
+
+ bool first = true;
+ foreach (CodeInstruction ci in instr)
+ {
+ if (first)
+ {
+ first = false;
+ ci.labels.Add(jumpTo);
+ }
+ //debug
+ Log.Message("CODE: ToString():" + ci.ToString() + " || labels:" + ci.labels + " || opcode:" + ci.opcode.ToString());
+ yield return ci;
+ }
+ }
+
+ public static string getLabelWork(PrisonerInteractionModeDef def)
+ {
+ if(def == DefDatabase.GetNamed("PrisonLabor_workOption"))
+ return "Work";
+ return "";
+ }
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3a/Source/WorkGiver_Supervise.cs b/Old sources/PrisonLabor v0.3a/Source/WorkGiver_Supervise.cs
new file mode 100644
index 00000000..6c54d5d8
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3a/Source/WorkGiver_Supervise.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+using Verse.AI;
+
+namespace PrisonLabor
+{
+ class WorkGiver_Supervise : WorkGiver_Warden
+ {
+ public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false)
+ {
+ if (!base.ShouldTakeCareOfPrisoner(pawn, t) || !Laziness.pawn((Pawn)t).NeedToBeInspired)
+ {
+ return null;
+ }
+ if (((Pawn)t).needs.food.CurCategory != HungerCategory.Fed && ((Pawn)t).needs.rest.CurCategory != RestCategory.Rested)
+ {
+ return null;
+ }
+ Pawn pawn2 = (Pawn)t;
+ if (pawn2.guest.interactionMode == DefDatabase.GetNamed("PrisonLabor_workOption") && (!pawn2.Downed || pawn2.InBed()) && pawn.CanReserve(t, 1, -1, null, false) && pawn2.Awake())
+ {
+ return new Job(DefDatabase.GetNamed("PrisonerSupervise"), t);
+ }
+ return null;
+ }
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3b/.gitignore b/Old sources/PrisonLabor v0.3b/.gitignore
new file mode 100644
index 00000000..940794e6
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3b/.gitignore
@@ -0,0 +1,288 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+**/Properties/launchSettings.json
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# TODO: Comment the next line if you want to checkin your web deploy settings
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/packages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Typescript v1 declaration files
+typings/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush
+.cr/
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
diff --git a/Old sources/PrisonLabor v0.3b/README.md b/Old sources/PrisonLabor v0.3b/README.md
new file mode 100644
index 00000000..16b2dc0e
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3b/README.md
@@ -0,0 +1,37 @@
+# PrisonLabor
+Prison Labor mod for "RimWorld" game
+
+## Version 0.3b
+
+## Description
+This mod force prisoners to work if Prisoner Interaction is set to "Work".
+Prisoner must be fed, and rested, or he(she) will refuse to work. Currently prisoners can only cook, mine, cut plants, craft, haul, and clean.
+Attention! He can run away if he mine a way out.
+
+Prisoners need to be watched by wardens, or they will get lazy.
+
+This is early alpha version, and it can be buggy.
+
+## Compatibility
+Works with mods that add Jobs of type cook/mine/craft/haul/clean like Quarry, or Haulers Can Haul To Blueprints
+Works with saves. You can enable, re-enable, disable this mod to all saves. (However disabling mod can throw errors, but they just saying they can't find tutorials, no harm)
+No collisions with other mods detected yet. Only mods that changes thinking of humanlike should be considered (Humanlike_PostDuty handle).
+
+## To-do list
+* Make prisoner laziness appear on "Needs" tab
+* Add translations
+* Customize mod to satisfy every user.
+* Add control time of work etc.
+* Change way of getting food by prisoner (currently RimWorld mechanics forcing prisoner to get to bed and wait for warden unless he is very very hungry)
+
+## To make prisoners work you must meet these conditions
+* Prisoner is safe, and don't need medical assistance.
+* Prisoner don't need to recover from injury/sickness in bed.
+* Prisoner can't escape.
+* Prisoner can reach work (best way to do that is leaving open doors to work area).
+* Prisoner is fed, and rested.
+* Prisoner interaction is set to "Work" (no "Chat and Recruit", or "Friendly Chat").
+
+## Translations
+Please contact me if you want help me writing translations. It will take you a few minutes to translate few sentences, and you will help making the mod even better. Thank you in advance!
+Also I would gladly hear about misspellings or grammar mistakes in English version.
diff --git a/Old sources/PrisonLabor v0.3b/Source/ConceptDef_PrisonLaborInstruction.cs b/Old sources/PrisonLabor v0.3b/Source/ConceptDef_PrisonLaborInstruction.cs
new file mode 100644
index 00000000..acc52ab7
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3b/Source/ConceptDef_PrisonLaborInstruction.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+
+namespace PrisonLabor
+{
+ class ConceptDef_PrisonLaborInstruction : ConceptDef
+ {
+
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3b/Source/InfoDialog.cs b/Old sources/PrisonLabor v0.3b/Source/InfoDialog.cs
new file mode 100644
index 00000000..2b2a4716
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3b/Source/InfoDialog.cs
@@ -0,0 +1,104 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+using UnityEngine;
+
+namespace PrisonLabor
+{
+ class InfoDialog : Window
+ {
+ private string searchName = string.Empty;
+
+ private string[] searchWords;
+
+ private List cachedNames;
+
+ public override Vector2 InitialSize
+ {
+ get
+ {
+ return new Vector2(400f, 650f);
+ }
+ }
+
+ public InfoDialog()
+ {
+ this.doCloseButton = true;
+ this.absorbInputAroundWindow = true;
+ this.cachedNames = (from n in (from b in SolidBioDatabase.allBios
+ select b.name).Concat(PawnNameDatabaseSolid.AllNames())
+ orderby n.Last descending
+ select n).ToList();
+ }
+
+ public override void DoWindowContents(Rect inRect)
+ {
+ Listing_Standard listing_Standard = new Listing_Standard();
+ listing_Standard.Begin(inRect);
+ listing_Standard.Label("TypeFirstNickOrLastName".Translate(), -1f);
+ string text = listing_Standard.TextEntry(this.searchName, 1);
+ if (text.Length < 20)
+ {
+ this.searchName = text;
+ this.searchWords = this.searchName.Replace("'", string.Empty).Split(new char[]
+ {
+ ' '
+ });
+ }
+ listing_Standard.Gap(4f);
+ if (this.searchName.Length > 1)
+ {
+ foreach (NameTriple current in this.cachedNames.Where(new Func(this.FilterMatch)))
+ {
+ if (listing_Standard.ButtonText(current.ToString(), null))
+ {
+ this.TryChooseName(current);
+ }
+ if (listing_Standard.CurHeight + 30f > inRect.height - (this.CloseButSize.y + 8f))
+ {
+ break;
+ }
+ }
+ }
+ listing_Standard.End();
+ }
+
+ private bool FilterMatch(NameTriple n)
+ {
+ if (n.First == "Tynan" && n.Last == "Sylvester")
+ {
+ return false;
+ }
+ if (this.searchWords.Length == 0)
+ {
+ return false;
+ }
+ if (this.searchWords.Length == 1)
+ {
+ return n.Last.StartsWith(this.searchName, StringComparison.OrdinalIgnoreCase) || n.First.StartsWith(this.searchName, StringComparison.OrdinalIgnoreCase) || n.Nick.StartsWith(this.searchName, StringComparison.OrdinalIgnoreCase);
+ }
+ return this.searchWords.Length == 2 && n.First.EqualsIgnoreCase(this.searchWords[0]) && (n.Last.StartsWith(this.searchWords[1], StringComparison.OrdinalIgnoreCase) || n.Nick.StartsWith(this.searchWords[1], StringComparison.OrdinalIgnoreCase));
+ }
+
+ private void TryChooseName(NameTriple name)
+ {
+ if (this.AlreadyPreferred(name))
+ {
+ Messages.Message("MessageAlreadyPreferredName".Translate(), MessageSound.RejectInput);
+ }
+ else
+ {
+ Prefs.PreferredNames.Add(name.ToString());
+ this.Close(true);
+ }
+ }
+
+ private bool AlreadyPreferred(NameTriple name)
+ {
+ return Prefs.PreferredNames.Contains(name.ToString());
+ }
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3b/Source/Initialization.cs b/Old sources/PrisonLabor v0.3b/Source/Initialization.cs
new file mode 100644
index 00000000..b997df52
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3b/Source/Initialization.cs
@@ -0,0 +1,105 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+using Harmony;
+using System.Reflection;
+using System.Reflection.Emit;
+using Verse.AI;
+
+namespace PrisonLabor
+{
+ [StaticConstructorOnStartup]
+ class Initialization
+ {
+ public static int version = 3;
+ public static bool oldPlayerNotification = false;
+
+ static Initialization()
+ {
+ var harmony = HarmonyInstance.Create("Harmony_PrisonLabor");
+ harmony.PatchAll(Assembly.GetExecutingAssembly());
+ harmony.Patch(typeof(Pawn_CarryTracker).GetMethod("TryDropCarriedThing", new Type[] { typeof(IntVec3), typeof(ThingPlaceMode), typeof(Thing).MakeByRefType(), typeof(Action) }),
+ new HarmonyMethod(null), new HarmonyMethod(typeof(ForibiddenDropPatch).GetMethod("Postfix")));
+ harmony.Patch(typeof(Pawn_CarryTracker).GetMethod("TryDropCarriedThing", new Type[] { typeof(IntVec3), typeof(int), typeof(ThingPlaceMode), typeof(Thing).MakeByRefType(), typeof(Action) }),
+ new HarmonyMethod(null), new HarmonyMethod(typeof(ForibiddenDropPatch).GetMethod("Postfix2")));
+
+ PrisonLaborPrefs.Init();
+ checkVersion();
+ }
+
+ private static void checkVersion()
+ {
+ if(PrisonLaborPrefs.Version < 3)
+ {
+ // only way to check if mod was installed before
+ if (PlayerKnowledgeDatabase.IsComplete(DefDatabase.GetNamed("PrisonLabor")))
+ {
+ Log.Message("Detected older version of PrisonLabor");
+ oldPlayerNotification = true;
+ }
+ }
+ else
+ {
+ Log.Message("Detected PrisonLabor v" + PrisonLaborPrefs.Version);
+ }
+ PrisonLaborPrefs.Version = 3;
+ PrisonLaborPrefs.Save();
+ }
+ }
+
+ [HarmonyPatch(typeof(PrisonerInteractionModeUtility))]
+ [HarmonyPatch("GetLabel")]
+ [HarmonyPatch(new Type[] { typeof(PrisonerInteractionModeDef) })]
+ class PrisonInteractionPatch
+ {
+ static IEnumerable Transpiler(ILGenerator gen, MethodBase mBase, IEnumerable instr)
+ {
+ // create our WORK label
+ Label jumpTo = gen.DefineLabel();
+ yield return new CodeInstruction(OpCodes.Ldarg_0);
+ yield return new CodeInstruction(OpCodes.Call, typeof(PrisonInteractionPatch).GetMethod("getLabelWork"));
+ //yield return new CodeInstruction(OpCodes.Dup);
+ yield return new CodeInstruction(OpCodes.Ldstr, "Work");
+ yield return new CodeInstruction(OpCodes.Bne_Un, jumpTo);
+ yield return new CodeInstruction(OpCodes.Ldstr, "Work");
+ yield return new CodeInstruction(OpCodes.Ret);
+
+ bool first = true;
+ foreach (CodeInstruction ci in instr)
+ {
+ if (first)
+ {
+ first = false;
+ ci.labels.Add(jumpTo);
+ }
+ //debug
+ //Log.Message("CODE: ToString():" + ci.ToString() + " || labels:" + ci.labels + " || opcode:" + ci.opcode.ToString());
+ yield return ci;
+ }
+ }
+
+ public static string getLabelWork(PrisonerInteractionModeDef def)
+ {
+ if(def == DefDatabase.GetNamed("PrisonLabor_workOption"))
+ return "Work";
+ return "";
+ }
+ }
+
+ class ForibiddenDropPatch
+ {
+ public static void Postfix(Pawn_CarryTracker __instance, IntVec3 dropLoc, ThingPlaceMode mode, Thing resultingThing, Action placedAction = null)
+ {
+ if (resultingThing.IsForbidden(Faction.OfPlayer) && __instance.pawn.IsPrisonerOfColony)
+ resultingThing.SetForbidden(false);
+ }
+
+ public static void Postfix2(Pawn_CarryTracker __instance, int count, IntVec3 dropLoc, ThingPlaceMode mode, Thing resultingThing, Action placedAction = null)
+ {
+ Postfix(__instance, dropLoc, mode, resultingThing, placedAction);
+ }
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3b/Source/JobDriver_Mine_Tweak.cs b/Old sources/PrisonLabor v0.3b/Source/JobDriver_Mine_Tweak.cs
new file mode 100644
index 00000000..68001482
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3b/Source/JobDriver_Mine_Tweak.cs
@@ -0,0 +1,103 @@
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Verse;
+using Verse.AI;
+
+namespace PrisonLabor
+{
+ public class JobDriver_Mine_Tweak : JobDriver
+ {
+ public const int BaseTicksBetweenPickHits = 120;
+
+ private const int BaseDamagePerPickHit = 80;
+
+ private const float MinMiningSpeedForNPCs = 0.5f;
+
+ private int ticksToPickHit = -1000;
+
+ private Effecter effecter;
+
+ private Thing MineTarget
+ {
+ get
+ {
+ return base.CurJob.GetTarget(TargetIndex.A).Thing;
+ }
+ }
+
+ [DebuggerHidden]
+ protected override IEnumerable MakeNewToils()
+ {
+ this.FailOnDespawnedNullOrForbidden(TargetIndex.A);
+ this.FailOnCellMissingDesignation(TargetIndex.A, DesignationDefOf.Mine);
+ yield return Toils_Reserve.Reserve(TargetIndex.A, 1, -1, null);
+ yield return Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.Touch);
+ Toil mine = new Toil();
+ mine.tickAction = delegate
+ {
+ Pawn actor = mine.actor;
+ Thing mineTarget = this.MineTarget;
+ if (this.ticksToPickHit < -100)
+ {
+ this.ResetTicksToPickHit();
+ }
+ if (actor.skills != null)
+ {
+ actor.skills.Learn(SkillDefOf.Mining, 0.11f, false);
+ }
+ this.ticksToPickHit--;
+ if (this.ticksToPickHit <= 0)
+ {
+ IntVec3 position = mineTarget.Position;
+ if (this.effecter == null)
+ {
+ this.effecter = EffecterDefOf.Mine.Spawn();
+ }
+ this.effecter.Trigger(actor, mineTarget);
+ int num = 80;
+ Mineable mineable = mineTarget as Mineable;
+ if (mineable == null || mineTarget.HitPoints > num)
+ {
+ Pawn actor2 = mine.actor;
+ DamageInfo dinfo = new DamageInfo(DamageDefOf.Mining, num, -1f, actor2, null, null, DamageInfo.SourceCategory.ThingOrUnknown);
+ mineTarget.TakeDamage(dinfo);
+ }
+ else
+ {
+ mineable.DestroyMined(actor);
+ }
+ if (mineTarget.Destroyed)
+ {
+ actor.Map.mineStrikeManager.CheckStruckOre(position, mineTarget.def, actor);
+ actor.records.Increment(RecordDefOf.CellsMined);
+ this.ReadyForNextToil();
+ return;
+ }
+ this.ResetTicksToPickHit();
+ }
+ };
+ mine.defaultCompleteMode = ToilCompleteMode.Never;
+ mine.WithProgressBar(TargetIndex.A, () => 1f - (float)this.MineTarget.HitPoints / (float)this.MineTarget.MaxHitPoints, false, -0.5f);
+ mine.FailOnCannotTouch(TargetIndex.A, PathEndMode.Touch);
+ yield return mine;
+ }
+
+ private void ResetTicksToPickHit()
+ {
+ float num = this.pawn.GetStatValue(StatDefOf.MiningSpeed, true);
+ if (num < 0.5f && this.pawn.Faction != Faction.OfPlayer)
+ {
+ num = 0.5f;
+ }
+ this.ticksToPickHit = (int)Math.Round((double)(120f / num));
+ }
+
+ public override void ExposeData()
+ {
+ base.ExposeData();
+ Scribe_Values.Look(ref this.ticksToPickHit, "ticksToPickHit", 0, false);
+ }
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3b/Source/JobDriver_Supervise.cs b/Old sources/PrisonLabor v0.3b/Source/JobDriver_Supervise.cs
new file mode 100644
index 00000000..e6fad431
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3b/Source/JobDriver_Supervise.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+using Verse.AI;
+
+namespace PrisonLabor
+{
+ class JobDriver_Supervise : JobDriver
+ {
+ protected Pawn Prisoner
+ {
+ get
+ {
+ return (Pawn)base.CurJob.targetA.Thing;
+ }
+ }
+
+ protected override IEnumerable MakeNewToils()
+ {
+ this.FailOnDespawnedOrNull(TargetIndex.A);
+ this.FailOnMentalState(TargetIndex.A);
+ this.FailOnNotAwake(TargetIndex.A);
+ this.FailOn(() => !Prisoner.IsPrisonerOfColony || !Prisoner.guest.PrisonerIsSecure);
+
+ yield return Toils_Reserve.Reserve(TargetIndex.A, 1, -1, null);
+ //yield return Toils_Interpersonal.GotoPrisoner(this.pawn, this.Prisoner, this.Prisoner.guest.interactionMode);
+ yield return MakeWatchToil(Prisoner);
+ for(int i = 0; i < 80; i++)
+ yield return Toils_General.Wait(10).FailOn(() => Prisoner.GetRoom() != pawn.GetRoom());
+ yield return MakeWatchToil(Prisoner);
+ for (int i = 0; i < 80; i++)
+ yield return Toils_General.Wait(10).FailOn(() => Prisoner.GetRoom() != pawn.GetRoom());
+ yield return Toils_Interpersonal.SetLastInteractTime(TargetIndex.A);
+ }
+
+ protected Toil MakeWatchToil(Pawn prisoner)
+ {
+ Toil toil = new Toil();
+ toil.initAction = delegate
+ {
+ Pawn actor = toil.actor;
+ IntVec3 ind;
+ if (Prisoner.GetRoom().Cells.Any(cell => cell.DistanceTo(Prisoner.InteractionCell) < 7 && cell.DistanceTo(Prisoner.InteractionCell) > 4))
+ ind = prisoner.GetRoom().Cells.Where(cell => cell.DistanceTo(prisoner.InteractionCell) < 7 && cell.DistanceTo(prisoner.InteractionCell) > 4).RandomElement();
+ else
+ ind = prisoner.GetRoom().Cells.RandomElement();
+ actor.pather.StartPath(ind, PathEndMode.OnCell);
+ };
+ toil.defaultCompleteMode = ToilCompleteMode.PatherArrival;
+ return toil;
+ }
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3b/Source/JobGiver_Labor.cs b/Old sources/PrisonLabor v0.3b/Source/JobGiver_Labor.cs
new file mode 100644
index 00000000..4ab08a8b
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3b/Source/JobGiver_Labor.cs
@@ -0,0 +1,281 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+using Verse.AI;
+
+namespace PrisonLabor
+{
+ public class JobGiver_Labor : ThinkNode
+ {
+ public bool emergency;
+
+ public override ThinkNode DeepCopy(bool resolve = true)
+ {
+ JobGiver_Labor jobGiver_Work = (JobGiver_Labor)base.DeepCopy(resolve);
+ jobGiver_Work.emergency = this.emergency;
+ return jobGiver_Work;
+ }
+
+ public override float GetPriority(Pawn pawn)
+ {
+ if (pawn.workSettings == null || !pawn.workSettings.EverWork)
+ {
+ return 0f;
+ }
+ TimeAssignmentDef timeAssignmentDef = (pawn.timetable != null) ? pawn.timetable.CurrentAssignment : TimeAssignmentDefOf.Anything;
+ if (timeAssignmentDef == TimeAssignmentDefOf.Anything)
+ {
+ return 5.5f;
+ }
+ if (timeAssignmentDef == TimeAssignmentDefOf.Work)
+ {
+ return 9f;
+ }
+ if (timeAssignmentDef == TimeAssignmentDefOf.Sleep)
+ {
+ return 2f;
+ }
+ if (timeAssignmentDef == TimeAssignmentDefOf.Joy)
+ {
+ return 2f;
+ }
+ throw new NotImplementedException();
+ }
+
+ public override ThinkResult TryIssueJobPackage(Pawn pawn, JobIssueParams jobParams)
+ {
+ //Check laziness
+ if( Laziness.pawn(pawn).IsLazy)
+ {
+ return ThinkResult.NoJob;
+ }
+ if (this.emergency && pawn.mindState.priorityWork.IsPrioritized)
+ {
+ List workGiversByPriority = pawn.mindState.priorityWork.WorkType.workGiversByPriority;
+ for (int i = 0; i < workGiversByPriority.Count; i++)
+ {
+ WorkGiver worker = workGiversByPriority[i].Worker;
+ Job job = this.GiverTryGiveJobPrioritized(pawn, worker, pawn.mindState.priorityWork.Cell);
+ if (job != null)
+ {
+ job.playerForced = true;
+ return new ThinkResult(job, this, new JobTag?(workGiversByPriority[i].tagToGive));
+ }
+ }
+ pawn.mindState.priorityWork.Clear();
+ }
+ //Work prisoners will do
+ List typeNameList = new List { "Cooking", "Mining", "PlantCutting", "Crafting", "Hauling", "Cleaning" };
+ List typeList = new List();
+ List workList = new List();
+ foreach (String workTypeName in typeNameList)
+ typeList.Add(DefDatabase.GetNamed(workTypeName, true));
+ foreach(WorkTypeDef workType in typeList.OrderBy(type => -pawn.skills.AverageOfRelevantSkillsFor(type)))
+ {
+ if (!pawn.story.WorkTypeIsDisabled(workType))
+ {
+ for (int m = 0; m < workType.workGiversByPriority.Count; m++)
+ {
+ WorkGiver worker = workType.workGiversByPriority[m].Worker;
+ if (!worker.def.emergency)
+ {
+ workList.Add(worker);
+ }
+ }
+ }
+ }
+
+ int num = -999;
+ TargetInfo targetInfo = TargetInfo.Invalid;
+ WorkGiver_Scanner workGiver_Scanner = null;
+ for (int j = 0; j < workList.Count; j++)
+ {
+ WorkGiver workGiver = workList[j];
+ if (workGiver.def.priorityInType != num && targetInfo.IsValid)
+ {
+ break;
+ }
+ if (this.PawnCanUseWorkGiver(pawn, workGiver))
+ {
+ try
+ {
+ Job job2 = workGiver.NonScanJob(pawn);
+ if (job2 != null)
+ {
+ return new ThinkResult(job2, this, new JobTag?(workList[j].def.tagToGive));
+ }
+ WorkGiver_Scanner scanner = workGiver as WorkGiver_Scanner;
+ if (scanner != null)
+ {
+ if (workGiver.def.scanThings)
+ {
+ Predicate predicate = (Thing t) => !t.IsForbidden(pawn) && scanner.HasJobOnThing(pawn, t, false);
+ IEnumerable enumerable = scanner.PotentialWorkThingsGlobal(pawn);
+ Thing thing;
+ if (scanner.Prioritized)
+ {
+ IEnumerable enumerable2 = enumerable;
+ if (enumerable2 == null)
+ {
+ enumerable2 = pawn.Map.listerThings.ThingsMatching(scanner.PotentialWorkThingRequest);
+ }
+ Predicate validator = predicate;
+ thing = GenClosest.ClosestThing_Global_Reachable(pawn.Position, pawn.Map, enumerable2, scanner.PathEndMode, TraverseParms.For(pawn, Danger.Deadly, TraverseMode.ByPawn, false), 9999f, validator, (Thing x) => scanner.GetPriority(pawn, x));
+ }
+ else
+ {
+ Predicate validator = predicate;
+ bool forceGlobalSearch = enumerable != null;
+ thing = GenClosest.ClosestThingReachable(pawn.Position, pawn.Map, scanner.PotentialWorkThingRequest, scanner.PathEndMode, TraverseParms.For(pawn, Danger.Deadly, TraverseMode.ByPawn, false), 9999f, validator, enumerable, 0, scanner.LocalRegionsToScanFirst, forceGlobalSearch, RegionType.Set_Passable, false);
+ }
+ if (thing != null)
+ {
+ targetInfo = thing;
+ workGiver_Scanner = scanner;
+ }
+ }
+ if (workGiver.def.scanCells)
+ {
+ IntVec3 position = pawn.Position;
+ float num2 = 99999f;
+ float num3 = -3.40282347E+38f;
+ bool prioritized = scanner.Prioritized;
+ foreach (IntVec3 current in scanner.PotentialWorkCellsGlobal(pawn))
+ {
+ bool flag = false;
+ float num4 = (float)(current - position).LengthHorizontalSquared;
+ if (prioritized)
+ {
+ if (!current.IsForbidden(pawn) && scanner.HasJobOnCell(pawn, current))
+ {
+ float priority = scanner.GetPriority(pawn, current);
+ if (priority > num3 || (priority == num3 && num4 < num2))
+ {
+ flag = true;
+ num3 = priority;
+ }
+ }
+ }
+ else if (num4 < num2 && !current.IsForbidden(pawn) && scanner.HasJobOnCell(pawn, current))
+ {
+ flag = true;
+ }
+ if (flag)
+ {
+ targetInfo = new TargetInfo(current, pawn.Map, false);
+ workGiver_Scanner = scanner;
+ num2 = num4;
+ }
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Log.Error(string.Concat(new object[]
+ {
+ pawn,
+ " threw exception in WorkGiver ",
+ workGiver.def.defName,
+ ": ",
+ ex.ToString()
+ }));
+ }
+ finally
+ {
+ }
+ if (targetInfo.IsValid)
+ {
+ pawn.mindState.lastGivenWorkType = workGiver.def.workType;
+ Job job3;
+ if (targetInfo.HasThing)
+ {
+ job3 = workGiver_Scanner.JobOnThing(pawn, targetInfo.Thing, false);
+ }
+ else
+ {
+ job3 = workGiver_Scanner.JobOnCell(pawn, targetInfo.Cell);
+ }
+ if (job3 != null)
+ {
+ return new ThinkResult(job3, this, new JobTag?(workList[j].def.tagToGive));
+ }
+ Log.ErrorOnce(string.Concat(new object[]
+ {
+ workGiver_Scanner,
+ " provided target ",
+ targetInfo,
+ " but yielded no actual job for pawn ",
+ pawn,
+ ". The CanGiveJob and JobOnX methods may not be synchronized."
+ }), 6112651);
+ }
+ num = workGiver.def.priorityInType;
+ }
+ }
+ return ThinkResult.NoJob;
+ }
+
+ private bool PawnCanUseWorkGiver(Pawn pawn, WorkGiver giver)
+ {
+ return !giver.ShouldSkip(pawn) && (giver.def.canBeDoneByNonColonists || pawn.IsPrisoner) && (pawn.story == null || !pawn.story.WorkTagIsDisabled(giver.def.workTags)) && giver.MissingRequiredCapacity(pawn) == null;
+ }
+
+ private Job GiverTryGiveJobPrioritized(Pawn pawn, WorkGiver giver, IntVec3 cell)
+ {
+ if (!this.PawnCanUseWorkGiver(pawn, giver))
+ {
+ return null;
+ }
+ try
+ {
+ Job job = giver.NonScanJob(pawn);
+ if (job != null)
+ {
+ Job result = job;
+ return result;
+ }
+ WorkGiver_Scanner scanner = giver as WorkGiver_Scanner;
+ if (scanner != null)
+ {
+ if (giver.def.scanThings)
+ {
+ Predicate predicate = (Thing t) => !t.IsForbidden(pawn) && scanner.HasJobOnThing(pawn, t, false);
+ List thingList = cell.GetThingList(pawn.Map);
+ for (int i = 0; i < thingList.Count; i++)
+ {
+ Thing thing = thingList[i];
+ if (scanner.PotentialWorkThingRequest.Accepts(thing) && predicate(thing))
+ {
+ pawn.mindState.lastGivenWorkType = giver.def.workType;
+ Job result = scanner.JobOnThing(pawn, thing, false);
+ return result;
+ }
+ }
+ }
+ if (giver.def.scanCells && !cell.IsForbidden(pawn) && scanner.HasJobOnCell(pawn, cell))
+ {
+ pawn.mindState.lastGivenWorkType = giver.def.workType;
+ Job result = scanner.JobOnCell(pawn, cell);
+ return result;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Log.Error(string.Concat(new object[]
+ {
+ pawn,
+ " threw exception in GiverTryGiveJobTargeted on WorkGiver ",
+ giver.def.defName,
+ ": ",
+ ex.ToString()
+ }));
+ }
+ return null;
+ }
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3b/Source/Laziness.cs b/Old sources/PrisonLabor v0.3b/Source/Laziness.cs
new file mode 100644
index 00000000..1d1eae6a
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3b/Source/Laziness.cs
@@ -0,0 +1,125 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+
+namespace PrisonLabor
+{
+ class Laziness
+ {
+ internal const double LAZY_LEVEL = 0.8;
+ internal const double NEED_INSPIRATION_LEVEL = 0.5;
+ internal const double LAZY_RATE = 0.03;
+ internal const double INSPIRE_RATE = 0.15;
+ public const int WARDEN_CAPACITY = (int)(INSPIRE_RATE/LAZY_RATE);
+
+ private static Dictionary map = new Dictionary();
+ private static PrisonerInteractionModeDef pimDef;
+
+ private double level;
+ private bool needToBeInspired;
+ private bool isLazy;
+
+ public static PrisonerInteractionModeDef PimDef
+ {
+ get
+ {
+ if (pimDef == null)
+ pimDef = DefDatabase.GetNamed("PrisonLabor_workOption");
+ return pimDef;
+ }
+ }
+
+ private Laziness()
+ {
+ level = 0;
+ needToBeInspired = false;
+ }
+
+ public void change(double value)
+ {
+ level += value;
+ }
+
+ public bool IsLazy
+ {
+ get
+ {
+ return isLazy;
+ }
+ }
+
+ public bool NeedToBeInspired
+ {
+ get
+ {
+ return needToBeInspired;
+ }
+ }
+
+ public static Laziness pawn(Pawn pawn)
+ {
+ if (map.ContainsKey(pawn))
+ {
+ return map[pawn];
+ }
+ else
+ {
+ Laziness laz = new Laziness();
+ map.Add(pawn, laz);
+ return laz;
+ }
+
+ }
+
+ public static void tick(Pawn pawn)
+ {
+ Laziness laz = Laziness.pawn(pawn);
+ double oldValue = laz.level;
+
+ List pawnsInRoom = new List();
+ int prisonersCount = 0;
+ int wardensCount = 0;
+ foreach (IntVec3 cell in pawn.GetRoomGroup().Cells)
+ {
+ foreach (Thing thing in cell.GetThingList(pawn.Map))
+ {
+ if (thing is Pawn)
+ pawnsInRoom.Add((Pawn)thing);
+ }
+ }
+ foreach (Pawn p in pawnsInRoom)
+ {
+ // colonist nearby
+ if (p.IsFreeColonist)
+ wardensCount++;
+ if (p.IsPrisoner && p.guest.interactionMode == PimDef)
+ prisonersCount++;
+ }
+
+ laz.level = laz.level + LAZY_RATE < 1 ? laz.level + LAZY_RATE : 1;
+ double insipre = (wardensCount * INSPIRE_RATE) / prisonersCount;
+ laz.level = laz.level > insipre ? laz.level - insipre : 0;
+
+ if (laz.level == 0)
+ laz.needToBeInspired = false;
+ if (laz.level >= NEED_INSPIRATION_LEVEL && !laz.needToBeInspired)
+ laz.needToBeInspired = true;
+ if (laz.level >= LAZY_LEVEL && !laz.isLazy && wardensCount == 0)
+ {
+ laz.isLazy = true;
+ Messages.Message("Your prioner got lazy!", pawn, MessageSound.Standard);
+ Tutorials.LazyPrisoner();
+ }
+ else if (laz.isLazy && wardensCount > 0)
+ {
+ laz.isLazy = false;
+ }
+
+ // For Denugging
+ //Log.Message("Laziness of " + pawn.Name + " changed from " + oldValue + " to " + laz.level);
+ }
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3b/Source/Prefs.cs b/Old sources/PrisonLabor v0.3b/Source/Prefs.cs
new file mode 100644
index 00000000..ff07971a
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3b/Source/Prefs.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Xml.Linq;
+using Verse;
+
+namespace PrisonLabor
+{
+ public static class PrisonLaborPrefs
+ {
+ private static PrisonLaborPrefsData data;
+ private static string prefsFilePath = Path.Combine(GenFilePaths.ConfigFolderPath, "PrisonData_Prefs.xml");
+
+ public static int Version
+ {
+ get
+ {
+ return PrisonLaborPrefs.data.version;
+ }
+ set
+ {
+ PrisonLaborPrefs.data.version = value;
+ PrisonLaborPrefs.Apply();
+ }
+ }
+
+ public static void Init()
+ {
+ bool flag = !new FileInfo(prefsFilePath).Exists;
+ PrisonLaborPrefs.data = new PrisonLaborPrefsData();
+ PrisonLaborPrefs.data = DirectXmlLoader.ItemFromXmlFile(prefsFilePath, true);
+ if (flag)
+ {
+ ;
+ }
+ }
+
+ public static void Save()
+ {
+ try
+ {
+ XDocument xDocument = new XDocument();
+ XElement content = DirectXmlSaver.XElementFromObject(PrisonLaborPrefs.data, typeof(PrisonLaborPrefsData));
+ xDocument.Add(content);
+ xDocument.Save(prefsFilePath);
+ }
+ catch (Exception ex)
+ {
+ GenUI.ErrorDialog("ProblemSavingFile".Translate(new object[]
+ {
+ prefsFilePath,
+ ex.ToString()
+ }));
+ Log.Error("Exception saving prefs: " + ex);
+ }
+ }
+
+ public static void Apply()
+ {
+ PrisonLaborPrefs.data.Apply();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Old sources/PrisonLabor v0.3b/Source/PrefsData.cs b/Old sources/PrisonLabor v0.3b/Source/PrefsData.cs
new file mode 100644
index 00000000..a116a45c
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3b/Source/PrefsData.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace PrisonLabor
+{
+ public class PrisonLaborPrefsData
+ {
+ public int version = -1;
+
+ public PrisonLaborPrefsData()
+ {
+
+ }
+
+ public void Apply()
+ {
+ }
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3b/Source/PrisonLabor.csproj b/Old sources/PrisonLabor v0.3b/Source/PrisonLabor.csproj
new file mode 100644
index 00000000..e1a04739
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3b/Source/PrisonLabor.csproj
@@ -0,0 +1,79 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {97750974-4CB6-4D31-84A1-A3AA77B1E2EE}
+ Library
+ Properties
+ PrisonLabor
+ PrisonLabor
+ v3.5
+ 512
+
+
+
+ false
+ none
+ false
+ ..\Assemblies\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ False
+ ..\Assemblies\0Harmony.dll
+ True
+
+
+ ..\..\..\RimWorldWin_Data\Managed\Assembly-CSharp.dll
+ False
+
+
+
+
+
+
+
+
+ ..\..\..\RimWorldWin_Data\Managed\UnityEngine.dll
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Old sources/PrisonLabor v0.3b/Source/PrisonerInteractionModeOf.cs b/Old sources/PrisonLabor v0.3b/Source/PrisonerInteractionModeOf.cs
new file mode 100644
index 00000000..a9326e39
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3b/Source/PrisonerInteractionModeOf.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+
+//Work in progress
+
+namespace RimWorldd
+{
+ [DefOf]
+ public static class PrisonerInteractionModeDefOf
+ {
+ public static PrisonerInteractionModeDef NoInteraction;
+
+ public static PrisonerInteractionModeDef Chat;
+
+ //public static PrisonerInteractionModeDef Work;
+
+ public static PrisonerInteractionModeDef AttemptRecruit;
+
+ public static PrisonerInteractionModeDef Release;
+
+ public static PrisonerInteractionModeDef Execution;
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3b/Source/PrisonerInteractionModeUtility.cs b/Old sources/PrisonLabor v0.3b/Source/PrisonerInteractionModeUtility.cs
new file mode 100644
index 00000000..bfbbca45
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3b/Source/PrisonerInteractionModeUtility.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+
+//Work in progress
+
+namespace RimWorldd
+{
+ public static class PrisonerInteractionModeUtility
+ {
+ public static string GetLabel(this PrisonerInteractionModeDef mode)
+ {
+ if (mode == PrisonerInteractionModeDefOf.NoInteraction)
+ {
+ return "PrisonerNoInteraction".Translate();
+ }
+ if (mode == PrisonerInteractionModeDefOf.Chat)
+ {
+ return "PrisonerFriendlyChat".Translate();
+ }
+ /*
+ if (mode == PrisonerInteractionModeDefOf.Work)
+ {
+ return "Work";
+ }
+ */
+ if (mode == PrisonerInteractionModeDefOf.AttemptRecruit)
+ {
+ return "PrisonerAttemptRecruit".Translate();
+ }
+ if (mode == PrisonerInteractionModeDefOf.Release)
+ {
+ return "PrisonerRelease".Translate();
+ }
+ if (mode == PrisonerInteractionModeDefOf.Execution)
+ {
+ return "PrisonerExecution".Translate();
+ }
+ return "Mode needs label";
+ }
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3b/Source/Properties/AssemblyInfo.cs b/Old sources/PrisonLabor v0.3b/Source/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..7aeb72f4
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3b/Source/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("PrisonLabor")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("PrisonLabor")]
+[assembly: AssemblyCopyright("Copyright © 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("97750974-4cb6-4d31-84a1-a3aa77b1e2ee")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("0.3.0.0")]
+[assembly: AssemblyFileVersion("0.3.0.0")]
diff --git a/Old sources/PrisonLabor v0.3b/Source/ThinkNode_ConditionalIsForced.cs b/Old sources/PrisonLabor v0.3b/Source/ThinkNode_ConditionalIsForced.cs
new file mode 100644
index 00000000..a78c2a09
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3b/Source/ThinkNode_ConditionalIsForced.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+
+namespace PrisonLabor
+{
+ class ThinkNode_ConditionalIsForced : ThinkNode_Conditional
+ {
+ protected override bool Satisfied(Pawn pawn)
+ {
+ //can work
+ if (pawn.IsPrisoner)
+ {
+ //show tutorial
+ Tutorials.PrisonLabor();
+ if (pawn.guest.interactionMode == DefDatabase.GetNamed("PrisonLabor_workOption"))
+ {
+ //can't escape
+ IntVec3 c;
+ if (pawn.guest.PrisonerIsSecure && !RCellFinder.TryFindBestExitSpot(pawn, out c, TraverseMode.ByPawn))
+ {
+ //shouldn't rest (medical reasons)
+ if (!HealthAIUtility.ShouldSeekMedicalRest(pawn))
+ {
+ // TODO can't or don't want to change
+ if (true)
+ {
+ // needs satisfied (sleep, food, etc.)
+ // TODO add more
+ if (pawn.needs.food.CurCategory == HungerCategory.Fed &&
+ pawn.needs.rest.CurCategory == RestCategory.Rested)
+ {
+ Laziness.tick(pawn);
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3b/Source/Tutorials.cs b/Old sources/PrisonLabor v0.3b/Source/Tutorials.cs
new file mode 100644
index 00000000..54911857
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3b/Source/Tutorials.cs
@@ -0,0 +1,34 @@
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Verse;
+
+namespace PrisonLabor
+{
+ class Tutorials
+ {
+ private static ConceptDef prisonLaborDef = DefDatabase.GetNamed("PrisonLabor", true);
+ private static ConceptDef lazyPrisonerDef = DefDatabase.GetNamed("LazyPrisoner", true);
+
+ public static void PrisonLabor()
+ {
+ if (!PlayerKnowledgeDatabase.IsComplete(prisonLaborDef))
+ Verse.Find.Tutor.learningReadout.TryActivateConcept(prisonLaborDef);
+ //Move it to point after map genration
+ if(Initialization.oldPlayerNotification)
+ {
+ Find.WindowStack.Add(new Dialog_MessageBox("PrisonLabor machanics has changed.\n\n 1. Prisoner Interaction mode must be set to \"Work\" instead of \"No Interaction\".\n 2. Now prisoners will get lazy if not supervised. Warden job will include watching prisoners to prevent that (you can always draft your colonists).", "Ok", null, null, null, "PrisonLabor - New mechanics", false));
+ Initialization.oldPlayerNotification = false;
+ }
+ }
+
+ public static void LazyPrisoner()
+ {
+ if (!PlayerKnowledgeDatabase.IsComplete(lazyPrisonerDef))
+ Verse.Find.Tutor.learningReadout.TryActivateConcept(lazyPrisonerDef);
+ }
+
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3b/Source/VersionChecker.cs b/Old sources/PrisonLabor v0.3b/Source/VersionChecker.cs
new file mode 100644
index 00000000..8a05cb36
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3b/Source/VersionChecker.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+using Harmony;
+using System.Reflection;
+using System.Reflection.Emit;
+
+namespace PrisonLabor
+{
+ [StaticConstructorOnStartup]
+ class VersionChecker
+ {
+ static VersionChecker()
+ {
+ var harmony = HarmonyInstance.Create("Harmony_PrisonLabor");
+ harmony.PatchAll(Assembly.GetExecutingAssembly());
+ }
+ }
+
+ [HarmonyPatch(typeof(PrisonerInteractionModeUtility))]
+ [HarmonyPatch("GetLabel")]
+ [HarmonyPatch(new Type[] { typeof(PrisonerInteractionModeDef) })]
+ class Patch
+ {
+ static IEnumerable Transpiler(ILGenerator gen, MethodBase mBase, IEnumerable instr)
+ {
+ // create our WORK label
+ Label jumpTo = gen.DefineLabel();
+ yield return new CodeInstruction(OpCodes.Ldarg_0);
+ yield return new CodeInstruction(OpCodes.Call, typeof(Patch).GetMethod("getLabelWork"));
+ //yield return new CodeInstruction(OpCodes.Dup);
+ yield return new CodeInstruction(OpCodes.Ldstr, "Work");
+ yield return new CodeInstruction(OpCodes.Bne_Un, jumpTo);
+ yield return new CodeInstruction(OpCodes.Ldstr, "Work");
+ yield return new CodeInstruction(OpCodes.Ret);
+
+ bool first = true;
+ foreach (CodeInstruction ci in instr)
+ {
+ if (first)
+ {
+ first = false;
+ ci.labels.Add(jumpTo);
+ }
+ //debug
+ Log.Message("CODE: ToString():" + ci.ToString() + " || labels:" + ci.labels + " || opcode:" + ci.opcode.ToString());
+ yield return ci;
+ }
+ }
+
+ public static string getLabelWork(PrisonerInteractionModeDef def)
+ {
+ if(def == DefDatabase.GetNamed("PrisonLabor_workOption"))
+ return "Work";
+ return "";
+ }
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3b/Source/WorkGiver_Miner_Tweak.cs b/Old sources/PrisonLabor v0.3b/Source/WorkGiver_Miner_Tweak.cs
new file mode 100644
index 00000000..2fe30444
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3b/Source/WorkGiver_Miner_Tweak.cs
@@ -0,0 +1,99 @@
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Verse;
+using Verse.AI;
+
+namespace PrisonLabor
+{
+ public class WorkGiver_Miner_Tweak : WorkGiver_Scanner
+ {
+ public override PathEndMode PathEndMode
+ {
+ get
+ {
+ return PathEndMode.Touch;
+ }
+ }
+
+ [DebuggerHidden]
+ public override IEnumerable PotentialWorkThingsGlobal(Pawn pawn)
+ {
+ foreach (Designation des in pawn.Map.designationManager.SpawnedDesignationsOfDef(DesignationDefOf.Mine))
+ {
+ bool mayBeAccessible = false;
+ for (int i = 0; i < 8; i++)
+ {
+ IntVec3 c = des.target.Cell + GenAdj.AdjacentCells[i];
+ if (c.InBounds(pawn.Map) && c.Walkable(pawn.Map))
+ {
+ mayBeAccessible = true;
+ break;
+ }
+ }
+ if (mayBeAccessible)
+ {
+ Thing j = MineUtility.MineableInCell(des.target.Cell, pawn.Map);
+ if (j != null)
+ {
+ yield return j;
+ }
+ }
+ }
+ }
+
+ public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false)
+ {
+ if (!pawn.IsPrisoner)
+ {
+ return null;
+ }
+ if (!t.def.mineable)
+ {
+ return null;
+ }
+ if (pawn.Map.designationManager.DesignationAt(t.Position, DesignationDefOf.Mine) == null)
+ {
+ return null;
+ }
+ if (!pawn.CanReserve(t, 1, -1, null, false))
+ {
+ return null;
+ }
+ bool flag = false;
+ for (int i = 0; i < 8; i++)
+ {
+ IntVec3 intVec = t.Position + GenAdj.AdjacentCells[i];
+ if (intVec.InBounds(pawn.Map) && intVec.Standable(pawn.Map) && ReachabilityImmediate.CanReachImmediate(intVec, t, pawn.Map, PathEndMode.Touch, pawn))
+ {
+ flag = true;
+ break;
+ }
+ }
+ if (!flag)
+ {
+ for (int j = 0; j < 8; j++)
+ {
+ IntVec3 intVec2 = t.Position + GenAdj.AdjacentCells[j];
+ if (intVec2.InBounds(t.Map))
+ {
+ if (ReachabilityImmediate.CanReachImmediate(intVec2, t, pawn.Map, PathEndMode.Touch, pawn))
+ {
+ if (intVec2.Walkable(t.Map) && !intVec2.Standable(t.Map))
+ {
+ Thing firstHaulable = intVec2.GetFirstHaulable(t.Map);
+ if (firstHaulable != null && firstHaulable.def.passability == Traversability.PassThroughOnly)
+ {
+ return HaulAIUtility.HaulAsideJobFor(pawn, firstHaulable);
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+ return new Job(DefDatabase.GetNamed("PrisonLabor.Mine_Tweak"), t, 1500, true);
+ }
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3b/Source/WorkGiver_Supervise.cs b/Old sources/PrisonLabor v0.3b/Source/WorkGiver_Supervise.cs
new file mode 100644
index 00000000..6c54d5d8
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3b/Source/WorkGiver_Supervise.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+using Verse.AI;
+
+namespace PrisonLabor
+{
+ class WorkGiver_Supervise : WorkGiver_Warden
+ {
+ public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false)
+ {
+ if (!base.ShouldTakeCareOfPrisoner(pawn, t) || !Laziness.pawn((Pawn)t).NeedToBeInspired)
+ {
+ return null;
+ }
+ if (((Pawn)t).needs.food.CurCategory != HungerCategory.Fed && ((Pawn)t).needs.rest.CurCategory != RestCategory.Rested)
+ {
+ return null;
+ }
+ Pawn pawn2 = (Pawn)t;
+ if (pawn2.guest.interactionMode == DefDatabase.GetNamed("PrisonLabor_workOption") && (!pawn2.Downed || pawn2.InBed()) && pawn.CanReserve(t, 1, -1, null, false) && pawn2.Awake())
+ {
+ return new Job(DefDatabase.GetNamed("PrisonerSupervise"), t);
+ }
+ return null;
+ }
+ }
+}
diff --git a/Old sources/PrisonLabor v0.3b/Source/WorkGiver_Warden_DeliverFood_Tweak.cs b/Old sources/PrisonLabor v0.3b/Source/WorkGiver_Warden_DeliverFood_Tweak.cs
new file mode 100644
index 00000000..e77d1c7d
--- /dev/null
+++ b/Old sources/PrisonLabor v0.3b/Source/WorkGiver_Warden_DeliverFood_Tweak.cs
@@ -0,0 +1,122 @@
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Verse;
+using Verse.AI;
+
+namespace PrisonLabor
+{
+ class WorkGiver_Warden_DeliverFood_Tweak : WorkGiver_Warden
+ {
+ public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false)
+ {
+ if (!base.ShouldTakeCareOfPrisoner(pawn, t))
+ {
+ Messages.Message("one", MessageSound.Negative);
+ return null;
+ }
+ Pawn pawn2 = (Pawn)t;
+ if (!pawn2.guest.CanBeBroughtFood)
+ {
+ Messages.Message("two", MessageSound.Negative);
+ return null;
+ }
+ //TODO test this condition
+ IntVec3 c;
+ //if (pawn2.Position.IsInPrisonCell(pawn2.Map) || RCellFinder.TryFindBestExitSpot((Pawn)t, out c, TraverseMode.ByPawn))
+ if(false)
+ {
+ return null;
+ }
+ if (pawn2.needs.food.CurLevelPercentage >= pawn2.needs.food.PercentageThreshHungry + 0.02f)
+ {
+ Messages.Message("three", MessageSound.Negative);
+ return null;
+ }
+ if (WardenFeedUtility.ShouldBeFed(pawn2))
+ {
+ Messages.Message("four", MessageSound.Negative);
+ return null;
+ }
+ Thing thing;
+ ThingDef def;
+ if (!FoodUtility.TryFindBestFoodSourceFor(pawn, pawn2, pawn2.needs.food.CurCategory == HungerCategory.Starving, out thing, out def, false, true, false, false, false))
+ {
+ Messages.Message("five", MessageSound.Negative);
+ return null;
+ }
+ if (thing.GetRoom(RegionType.Set_Passable) == pawn2.GetRoom(RegionType.Set_Passable))
+ {
+ Messages.Message("six", MessageSound.Negative);
+ return null;
+ }
+ if (WorkGiver_Warden_DeliverFood_Tweak.FoodAvailableInRoomTo(pawn2))
+ {
+ Messages.Message("seven", MessageSound.Negative);
+ return null;
+ }
+ return new Job(JobDefOf.DeliverFood, thing, pawn2)
+ {
+ count = FoodUtility.WillIngestStackCountOf(pawn2, def),
+ targetC = RCellFinder.SpotToChewStandingNear(pawn2, thing)
+ };
+ }
+
+ private static bool FoodAvailableInRoomTo(Pawn prisoner)
+ {
+ if (prisoner.carryTracker.CarriedThing != null && WorkGiver_Warden_DeliverFood_Tweak.NutritionAvailableForFrom(prisoner, prisoner.carryTracker.CarriedThing) > 0f)
+ {
+ return true;
+ }
+ float num = 0f;
+ float num2 = 0f;
+ Room room = prisoner.GetRoom(RegionType.Set_Passable);
+ if (room == null)
+ {
+ return false;
+ }
+ for (int i = 0; i < room.RegionCount; i++)
+ {
+ Region region = room.Regions[i];
+ List list = region.ListerThings.ThingsInGroup(ThingRequestGroup.FoodSourceNotPlantOrTree);
+ for (int j = 0; j < list.Count; j++)
+ {
+ Thing thing = list[j];
+ if (!thing.def.IsIngestible || thing.def.ingestible.preferability > FoodPreferability.DesperateOnly)
+ {
+ num2 += WorkGiver_Warden_DeliverFood_Tweak.NutritionAvailableForFrom(prisoner, thing);
+ }
+ }
+ List list2 = region.ListerThings.ThingsInGroup(ThingRequestGroup.Pawn);
+ for (int k = 0; k < list2.Count; k++)
+ {
+ Pawn pawn = list2[k] as Pawn;
+ if (pawn.IsPrisonerOfColony && pawn.needs.food.CurLevelPercentage < pawn.needs.food.PercentageThreshHungry + 0.02f && (pawn.carryTracker.CarriedThing == null || !pawn.RaceProps.WillAutomaticallyEat(pawn.carryTracker.CarriedThing)))
+ {
+ num += pawn.needs.food.NutritionWanted;
+ }
+ }
+ }
+ return num2 + 0.5f >= num;
+ }
+
+ private static float NutritionAvailableForFrom(Pawn p, Thing foodSource)
+ {
+ if (foodSource.def.IsNutritionGivingIngestible && p.RaceProps.WillAutomaticallyEat(foodSource))
+ {
+ return foodSource.def.ingestible.nutrition * (float)foodSource.stackCount;
+ }
+ if (p.RaceProps.ToolUser && p.health.capacities.CapableOf(PawnCapacityDefOf.Manipulation))
+ {
+ Building_NutrientPasteDispenser building_NutrientPasteDispenser = foodSource as Building_NutrientPasteDispenser;
+ if (building_NutrientPasteDispenser != null && building_NutrientPasteDispenser.CanDispenseNow)
+ {
+ return 99999f;
+ }
+ }
+ return 0f;
+ }
+ }
+}
diff --git a/README.md b/README.md
index 16b2dc0e..4ff7ed85 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
# PrisonLabor
Prison Labor mod for "RimWorld" game
-## Version 0.3b
+## Version 0.4
## Description
This mod force prisoners to work if Prisoner Interaction is set to "Work".
@@ -13,16 +13,12 @@ Prisoners need to be watched by wardens, or they will get lazy.
This is early alpha version, and it can be buggy.
## Compatibility
-Works with mods that add Jobs of type cook/mine/craft/haul/clean like Quarry, or Haulers Can Haul To Blueprints
-Works with saves. You can enable, re-enable, disable this mod to all saves. (However disabling mod can throw errors, but they just saying they can't find tutorials, no harm)
-No collisions with other mods detected yet. Only mods that changes thinking of humanlike should be considered (Humanlike_PostDuty handle).
+* Works with mods that add Jobs of type cook/mine/craft/haul/clean like Quarry, or Haulers Can Haul To Blueprints
+Works with saves.
+* You can enable, re-enable, disable this mod to all saves. (However disabling mod can throw errors, but they just saying they can't find tutorials, no harm)
+* No collisions with other mods detected yet. Only mods that changes thinking of humanlike should be considered (Humanlike_PostDuty handle).
-## To-do list
-* Make prisoner laziness appear on "Needs" tab
-* Add translations
-* Customize mod to satisfy every user.
-* Add control time of work etc.
-* Change way of getting food by prisoner (currently RimWorld mechanics forcing prisoner to get to bed and wait for warden unless he is very very hungry)
+## [To-do list](https://github.com/Aviuz/PrisonLabor/projects/1)
## To make prisoners work you must meet these conditions
* Prisoner is safe, and don't need medical assistance.
@@ -30,8 +26,11 @@ No collisions with other mods detected yet. Only mods that changes thinking of h
* Prisoner can't escape.
* Prisoner can reach work (best way to do that is leaving open doors to work area).
* Prisoner is fed, and rested.
-* Prisoner interaction is set to "Work" (no "Chat and Recruit", or "Friendly Chat").
+* Prisoner interaction is set to "Force to work" (no "Chat and Recruit", or "Friendly Chat").
## Translations
-Please contact me if you want help me writing translations. It will take you a few minutes to translate few sentences, and you will help making the mod even better. Thank you in advance!
+Please contact me if you want help me writing translations. It will take you a few minutes to translate few sentences, and you will help making the mod even better. Thank you in advance!
Also I would gladly hear about misspellings or grammar mistakes in English version.
+
+## Tutorial video
+Please contact me if you want to help me by creating tutorial video.
diff --git a/Source/HarmonyPatches.cs b/Source/HarmonyPatches.cs
new file mode 100644
index 00000000..63de3346
--- /dev/null
+++ b/Source/HarmonyPatches.cs
@@ -0,0 +1,139 @@
+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
+{
+ class HarmonyPatches
+ {
+
+ public static void run()
+ {
+ var harmony = HarmonyInstance.Create("Harmony_PrisonLabor");
+ harmony.PatchAll(Assembly.GetExecutingAssembly());
+ harmony.Patch(typeof(Pawn_CarryTracker).GetMethod("TryDropCarriedThing", new Type[] { typeof(IntVec3), typeof(ThingPlaceMode), typeof(Thing).MakeByRefType(), typeof(Action) }),
+ new HarmonyMethod(null), new HarmonyMethod(typeof(ForibiddenDropPatch).GetMethod("Postfix")));
+ harmony.Patch(typeof(Pawn_CarryTracker).GetMethod("TryDropCarriedThing", new Type[] { typeof(IntVec3), typeof(int), typeof(ThingPlaceMode), typeof(Thing).MakeByRefType(), typeof(Action) }),
+ new HarmonyMethod(null), new HarmonyMethod(typeof(ForibiddenDropPatch).GetMethod("Postfix2")));
+ }
+
+ }
+
+ [HarmonyPatch(typeof(PrisonerInteractionModeUtility))]
+ [HarmonyPatch("GetLabel")]
+ [HarmonyPatch(new Type[] { typeof(PrisonerInteractionModeDef) })]
+ class PrisonInteractionPatch
+ {
+ static IEnumerable Transpiler(ILGenerator gen, MethodBase mBase, IEnumerable instr)
+ {
+ // create our WORK label
+ Label jumpTo = gen.DefineLabel();
+ yield return new CodeInstruction(OpCodes.Ldarg_0);
+ yield return new CodeInstruction(OpCodes.Call, typeof(PrisonInteractionPatch).GetMethod("getLabelWork"));
+ //yield return new CodeInstruction(OpCodes.Dup);
+ yield return new CodeInstruction(OpCodes.Ldstr, "Work");
+ yield return new CodeInstruction(OpCodes.Bne_Un, jumpTo);
+ yield return new CodeInstruction(OpCodes.Ldstr, "PrisonLabor_PrisonerWork".Translate());
+ yield return new CodeInstruction(OpCodes.Ret);
+
+ bool first = true;
+ foreach (CodeInstruction ci in instr)
+ {
+ if (first)
+ {
+ first = false;
+ ci.labels.Add(jumpTo);
+ }
+ //debug
+ //Log.Message("CODE: ToString():" + ci.ToString() + " || labels:" + ci.labels.Any());
+ yield return ci;
+ }
+ }
+
+ public static string getLabelWork(PrisonerInteractionModeDef def)
+ {
+ if (def == DefDatabase.GetNamed("PrisonLabor_workOption"))
+ return "Work";
+ return "";
+ }
+ }
+
+ [HarmonyPatch(typeof(Pawn_NeedsTracker))]
+ [HarmonyPatch("ShouldHaveNeed")]
+ [HarmonyPatch(new Type[] { typeof(NeedDef) })]
+ class NeedOnlyByPrisonersPatch
+ {
+ static IEnumerable Transpiler(ILGenerator gen, MethodBase mBase, IEnumerable instr)
+ {
+ //Searches for loadFieldPawn Instruction. Can't create this by generator (don't know why)
+ CodeInstruction loadFieldPawn = null;
+ foreach (CodeInstruction ci in instr)
+ {
+ if (ci.opcode.Value == OpCodes.Ldfld.Value)
+ {
+ loadFieldPawn = ci;
+ break;
+ }
+ }
+
+ // Define label to the begining of the original code
+ Label jumpTo = gen.DefineLabel();
+ //Load argument onto stack
+ yield return new CodeInstruction(OpCodes.Ldarg_1);
+ //Load pawn onto stack
+ yield return new CodeInstruction(OpCodes.Ldarg_0);
+ yield return loadFieldPawn;
+ //Call function
+ yield return new CodeInstruction(OpCodes.Call, typeof(NeedOnlyByPrisonersPatch).GetMethod("ShouldHaveNeedPrisoner"));
+ //If true continue
+ yield return new CodeInstruction(OpCodes.Brtrue, jumpTo);
+ //Load false to stack
+ yield return new CodeInstruction(OpCodes.Ldc_I4_0);
+ //Return
+ yield return new CodeInstruction(OpCodes.Ret);
+
+ bool first = true;
+ foreach (CodeInstruction ci in instr)
+ {
+ if (first)
+ {
+ first = false;
+ ci.labels.Add(jumpTo);
+ }
+ //debug
+ //Log.Message("CODE: ToString():" + ci.ToString() + " || labels:" + ci.labels.Any());
+ yield return ci;
+ }
+ }
+
+
+ public static bool ShouldHaveNeedPrisoner(NeedDef nd, Pawn pawn)
+ {
+ if (nd.defName == "PrisonLabor_Laziness" && !pawn.IsPrisoner)
+ {
+ return false;
+ }
+ return true;
+ }
+ }
+
+ class ForibiddenDropPatch
+ {
+ public static void Postfix(Pawn_CarryTracker __instance, IntVec3 dropLoc, ThingPlaceMode mode, Thing resultingThing, Action placedAction = null)
+ {
+ if (resultingThing.IsForbidden(Faction.OfPlayer) && __instance.pawn.IsPrisonerOfColony)
+ resultingThing.SetForbidden(false);
+ }
+
+ public static void Postfix2(Pawn_CarryTracker __instance, int count, IntVec3 dropLoc, ThingPlaceMode mode, Thing resultingThing, Action placedAction = null)
+ {
+ Postfix(__instance, dropLoc, mode, resultingThing, placedAction);
+ }
+ }
+}
diff --git a/Source/InfoDialog.cs b/Source/InfoDialog.cs
new file mode 100644
index 00000000..2b2a4716
--- /dev/null
+++ b/Source/InfoDialog.cs
@@ -0,0 +1,104 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+using UnityEngine;
+
+namespace PrisonLabor
+{
+ class InfoDialog : Window
+ {
+ private string searchName = string.Empty;
+
+ private string[] searchWords;
+
+ private List cachedNames;
+
+ public override Vector2 InitialSize
+ {
+ get
+ {
+ return new Vector2(400f, 650f);
+ }
+ }
+
+ public InfoDialog()
+ {
+ this.doCloseButton = true;
+ this.absorbInputAroundWindow = true;
+ this.cachedNames = (from n in (from b in SolidBioDatabase.allBios
+ select b.name).Concat(PawnNameDatabaseSolid.AllNames())
+ orderby n.Last descending
+ select n).ToList();
+ }
+
+ public override void DoWindowContents(Rect inRect)
+ {
+ Listing_Standard listing_Standard = new Listing_Standard();
+ listing_Standard.Begin(inRect);
+ listing_Standard.Label("TypeFirstNickOrLastName".Translate(), -1f);
+ string text = listing_Standard.TextEntry(this.searchName, 1);
+ if (text.Length < 20)
+ {
+ this.searchName = text;
+ this.searchWords = this.searchName.Replace("'", string.Empty).Split(new char[]
+ {
+ ' '
+ });
+ }
+ listing_Standard.Gap(4f);
+ if (this.searchName.Length > 1)
+ {
+ foreach (NameTriple current in this.cachedNames.Where(new Func(this.FilterMatch)))
+ {
+ if (listing_Standard.ButtonText(current.ToString(), null))
+ {
+ this.TryChooseName(current);
+ }
+ if (listing_Standard.CurHeight + 30f > inRect.height - (this.CloseButSize.y + 8f))
+ {
+ break;
+ }
+ }
+ }
+ listing_Standard.End();
+ }
+
+ private bool FilterMatch(NameTriple n)
+ {
+ if (n.First == "Tynan" && n.Last == "Sylvester")
+ {
+ return false;
+ }
+ if (this.searchWords.Length == 0)
+ {
+ return false;
+ }
+ if (this.searchWords.Length == 1)
+ {
+ return n.Last.StartsWith(this.searchName, StringComparison.OrdinalIgnoreCase) || n.First.StartsWith(this.searchName, StringComparison.OrdinalIgnoreCase) || n.Nick.StartsWith(this.searchName, StringComparison.OrdinalIgnoreCase);
+ }
+ return this.searchWords.Length == 2 && n.First.EqualsIgnoreCase(this.searchWords[0]) && (n.Last.StartsWith(this.searchWords[1], StringComparison.OrdinalIgnoreCase) || n.Nick.StartsWith(this.searchWords[1], StringComparison.OrdinalIgnoreCase));
+ }
+
+ private void TryChooseName(NameTriple name)
+ {
+ if (this.AlreadyPreferred(name))
+ {
+ Messages.Message("MessageAlreadyPreferredName".Translate(), MessageSound.RejectInput);
+ }
+ else
+ {
+ Prefs.PreferredNames.Add(name.ToString());
+ this.Close(true);
+ }
+ }
+
+ private bool AlreadyPreferred(NameTriple name)
+ {
+ return Prefs.PreferredNames.Contains(name.ToString());
+ }
+ }
+}
diff --git a/Source/Initialization.cs b/Source/Initialization.cs
new file mode 100644
index 00000000..046375c1
--- /dev/null
+++ b/Source/Initialization.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+using Harmony;
+using System.Reflection;
+using System.Reflection.Emit;
+using Verse.AI;
+
+namespace PrisonLabor
+{
+ [StaticConstructorOnStartup]
+ class Initialization
+ {
+ public static int version = 4;
+ public static bool oldPlayerNotification = false;
+
+ static Initialization()
+ {
+ HarmonyPatches.run();
+ PrisonLaborPrefs.Init();
+ checkVersion();
+ }
+
+ private static void checkVersion()
+ {
+ if (PrisonLaborPrefs.Version < 3)
+ {
+ // only way to check if mod was installed before
+ if (PlayerKnowledgeDatabase.IsComplete(DefDatabase.GetNamed("PrisonLabor")))
+ {
+ Log.Message("Detected older version of PrisonLabor");
+ oldPlayerNotification = true;
+ }
+ }
+ else
+ {
+ Log.Message("Detected PrisonLabor v" + PrisonLaborPrefs.Version);
+ }
+ //PrisonLaborPrefs.Version = version;
+ PrisonLaborPrefs.Save();
+ }
+ }
+}
diff --git a/Source/JobDriver_Supervise.cs b/Source/JobDriver_Supervise.cs
new file mode 100644
index 00000000..e6fad431
--- /dev/null
+++ b/Source/JobDriver_Supervise.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+using Verse.AI;
+
+namespace PrisonLabor
+{
+ class JobDriver_Supervise : JobDriver
+ {
+ protected Pawn Prisoner
+ {
+ get
+ {
+ return (Pawn)base.CurJob.targetA.Thing;
+ }
+ }
+
+ protected override IEnumerable MakeNewToils()
+ {
+ this.FailOnDespawnedOrNull(TargetIndex.A);
+ this.FailOnMentalState(TargetIndex.A);
+ this.FailOnNotAwake(TargetIndex.A);
+ this.FailOn(() => !Prisoner.IsPrisonerOfColony || !Prisoner.guest.PrisonerIsSecure);
+
+ yield return Toils_Reserve.Reserve(TargetIndex.A, 1, -1, null);
+ //yield return Toils_Interpersonal.GotoPrisoner(this.pawn, this.Prisoner, this.Prisoner.guest.interactionMode);
+ yield return MakeWatchToil(Prisoner);
+ for(int i = 0; i < 80; i++)
+ yield return Toils_General.Wait(10).FailOn(() => Prisoner.GetRoom() != pawn.GetRoom());
+ yield return MakeWatchToil(Prisoner);
+ for (int i = 0; i < 80; i++)
+ yield return Toils_General.Wait(10).FailOn(() => Prisoner.GetRoom() != pawn.GetRoom());
+ yield return Toils_Interpersonal.SetLastInteractTime(TargetIndex.A);
+ }
+
+ protected Toil MakeWatchToil(Pawn prisoner)
+ {
+ Toil toil = new Toil();
+ toil.initAction = delegate
+ {
+ Pawn actor = toil.actor;
+ IntVec3 ind;
+ if (Prisoner.GetRoom().Cells.Any(cell => cell.DistanceTo(Prisoner.InteractionCell) < 7 && cell.DistanceTo(Prisoner.InteractionCell) > 4))
+ ind = prisoner.GetRoom().Cells.Where(cell => cell.DistanceTo(prisoner.InteractionCell) < 7 && cell.DistanceTo(prisoner.InteractionCell) > 4).RandomElement();
+ else
+ ind = prisoner.GetRoom().Cells.RandomElement();
+ actor.pather.StartPath(ind, PathEndMode.OnCell);
+ };
+ toil.defaultCompleteMode = ToilCompleteMode.PatherArrival;
+ return toil;
+ }
+ }
+}
diff --git a/Source/JobGiver_Labor.cs b/Source/JobGiver_Labor.cs
new file mode 100644
index 00000000..5efc1a59
--- /dev/null
+++ b/Source/JobGiver_Labor.cs
@@ -0,0 +1,281 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+using Verse.AI;
+
+namespace PrisonLabor
+{
+ public class JobGiver_Labor : ThinkNode
+ {
+ public bool emergency;
+
+ public override ThinkNode DeepCopy(bool resolve = true)
+ {
+ JobGiver_Labor jobGiver_Work = (JobGiver_Labor)base.DeepCopy(resolve);
+ jobGiver_Work.emergency = this.emergency;
+ return jobGiver_Work;
+ }
+
+ public override float GetPriority(Pawn pawn)
+ {
+ if (pawn.workSettings == null || !pawn.workSettings.EverWork)
+ {
+ return 0f;
+ }
+ TimeAssignmentDef timeAssignmentDef = (pawn.timetable != null) ? pawn.timetable.CurrentAssignment : TimeAssignmentDefOf.Anything;
+ if (timeAssignmentDef == TimeAssignmentDefOf.Anything)
+ {
+ return 5.5f;
+ }
+ if (timeAssignmentDef == TimeAssignmentDefOf.Work)
+ {
+ return 9f;
+ }
+ if (timeAssignmentDef == TimeAssignmentDefOf.Sleep)
+ {
+ return 2f;
+ }
+ if (timeAssignmentDef == TimeAssignmentDefOf.Joy)
+ {
+ return 2f;
+ }
+ throw new NotImplementedException();
+ }
+
+ public override ThinkResult TryIssueJobPackage(Pawn pawn, JobIssueParams jobParams)
+ {
+ //Check laziness
+ if (pawn.needs.TryGetNeed().IsLazy)
+ {
+ return ThinkResult.NoJob;
+ }
+ if (this.emergency && pawn.mindState.priorityWork.IsPrioritized)
+ {
+ List workGiversByPriority = pawn.mindState.priorityWork.WorkType.workGiversByPriority;
+ for (int i = 0; i < workGiversByPriority.Count; i++)
+ {
+ WorkGiver worker = workGiversByPriority[i].Worker;
+ Job job = this.GiverTryGiveJobPrioritized(pawn, worker, pawn.mindState.priorityWork.Cell);
+ if (job != null)
+ {
+ job.playerForced = true;
+ return new ThinkResult(job, this, new JobTag?(workGiversByPriority[i].tagToGive));
+ }
+ }
+ pawn.mindState.priorityWork.Clear();
+ }
+ //Work prisoners will do
+ List typeNameList = new List { "Cooking", "Mining", "PlantCutting", "Crafting", "Hauling", "Cleaning" };
+ List typeList = new List();
+ List workList = new List();
+ foreach (String workTypeName in typeNameList)
+ typeList.Add(DefDatabase.GetNamed(workTypeName, true));
+ foreach (WorkTypeDef workType in typeList.OrderBy(type => -pawn.skills.AverageOfRelevantSkillsFor(type)))
+ {
+ if (!pawn.story.WorkTypeIsDisabled(workType))
+ {
+ for (int m = 0; m < workType.workGiversByPriority.Count; m++)
+ {
+ WorkGiver worker = workType.workGiversByPriority[m].Worker;
+ if (!worker.def.emergency)
+ {
+ workList.Add(worker);
+ }
+ }
+ }
+ }
+
+ int num = -999;
+ TargetInfo targetInfo = TargetInfo.Invalid;
+ WorkGiver_Scanner workGiver_Scanner = null;
+ for (int j = 0; j < workList.Count; j++)
+ {
+ WorkGiver workGiver = workList[j];
+ if (workGiver.def.priorityInType != num && targetInfo.IsValid)
+ {
+ break;
+ }
+ if (this.PawnCanUseWorkGiver(pawn, workGiver))
+ {
+ try
+ {
+ Job job2 = workGiver.NonScanJob(pawn);
+ if (job2 != null)
+ {
+ return new ThinkResult(job2, this, new JobTag?(workList[j].def.tagToGive));
+ }
+ WorkGiver_Scanner scanner = workGiver as WorkGiver_Scanner;
+ if (scanner != null)
+ {
+ if (workGiver.def.scanThings)
+ {
+ Predicate predicate = (Thing t) => !t.IsForbidden(pawn) && scanner.HasJobOnThing(pawn, t, false);
+ IEnumerable enumerable = scanner.PotentialWorkThingsGlobal(pawn);
+ Thing thing;
+ if (scanner.Prioritized)
+ {
+ IEnumerable enumerable2 = enumerable;
+ if (enumerable2 == null)
+ {
+ enumerable2 = pawn.Map.listerThings.ThingsMatching(scanner.PotentialWorkThingRequest);
+ }
+ Predicate validator = predicate;
+ thing = GenClosest.ClosestThing_Global_Reachable(pawn.Position, pawn.Map, enumerable2, scanner.PathEndMode, TraverseParms.For(pawn, Danger.Deadly, TraverseMode.ByPawn, false), 9999f, validator, (Thing x) => scanner.GetPriority(pawn, x));
+ }
+ else
+ {
+ Predicate validator = predicate;
+ bool forceGlobalSearch = enumerable != null;
+ thing = GenClosest.ClosestThingReachable(pawn.Position, pawn.Map, scanner.PotentialWorkThingRequest, scanner.PathEndMode, TraverseParms.For(pawn, Danger.Deadly, TraverseMode.ByPawn, false), 9999f, validator, enumerable, 0, scanner.LocalRegionsToScanFirst, forceGlobalSearch, RegionType.Set_Passable, false);
+ }
+ if (thing != null)
+ {
+ targetInfo = thing;
+ workGiver_Scanner = scanner;
+ }
+ }
+ if (workGiver.def.scanCells)
+ {
+ IntVec3 position = pawn.Position;
+ float num2 = 99999f;
+ float num3 = -3.40282347E+38f;
+ bool prioritized = scanner.Prioritized;
+ foreach (IntVec3 current in scanner.PotentialWorkCellsGlobal(pawn))
+ {
+ bool flag = false;
+ float num4 = (float)(current - position).LengthHorizontalSquared;
+ if (prioritized)
+ {
+ if (!current.IsForbidden(pawn) && scanner.HasJobOnCell(pawn, current))
+ {
+ float priority = scanner.GetPriority(pawn, current);
+ if (priority > num3 || (priority == num3 && num4 < num2))
+ {
+ flag = true;
+ num3 = priority;
+ }
+ }
+ }
+ else if (num4 < num2 && !current.IsForbidden(pawn) && scanner.HasJobOnCell(pawn, current))
+ {
+ flag = true;
+ }
+ if (flag)
+ {
+ targetInfo = new TargetInfo(current, pawn.Map, false);
+ workGiver_Scanner = scanner;
+ num2 = num4;
+ }
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Log.Error(string.Concat(new object[]
+ {
+ pawn,
+ " threw exception in WorkGiver ",
+ workGiver.def.defName,
+ ": ",
+ ex.ToString()
+ }));
+ }
+ finally
+ {
+ }
+ if (targetInfo.IsValid)
+ {
+ pawn.mindState.lastGivenWorkType = workGiver.def.workType;
+ Job job3;
+ if (targetInfo.HasThing)
+ {
+ job3 = workGiver_Scanner.JobOnThing(pawn, targetInfo.Thing, false);
+ }
+ else
+ {
+ job3 = workGiver_Scanner.JobOnCell(pawn, targetInfo.Cell);
+ }
+ if (job3 != null)
+ {
+ return new ThinkResult(job3, this, new JobTag?(workList[j].def.tagToGive));
+ }
+ Log.ErrorOnce(string.Concat(new object[]
+ {
+ workGiver_Scanner,
+ " provided target ",
+ targetInfo,
+ " but yielded no actual job for pawn ",
+ pawn,
+ ". The CanGiveJob and JobOnX methods may not be synchronized."
+ }), 6112651);
+ }
+ num = workGiver.def.priorityInType;
+ }
+ }
+ return ThinkResult.NoJob;
+ }
+
+ private bool PawnCanUseWorkGiver(Pawn pawn, WorkGiver giver)
+ {
+ return !giver.ShouldSkip(pawn) && (giver.def.canBeDoneByNonColonists || pawn.IsPrisoner) && (pawn.story == null || !pawn.story.WorkTagIsDisabled(giver.def.workTags)) && giver.MissingRequiredCapacity(pawn) == null;
+ }
+
+ private Job GiverTryGiveJobPrioritized(Pawn pawn, WorkGiver giver, IntVec3 cell)
+ {
+ if (!this.PawnCanUseWorkGiver(pawn, giver))
+ {
+ return null;
+ }
+ try
+ {
+ Job job = giver.NonScanJob(pawn);
+ if (job != null)
+ {
+ Job result = job;
+ return result;
+ }
+ WorkGiver_Scanner scanner = giver as WorkGiver_Scanner;
+ if (scanner != null)
+ {
+ if (giver.def.scanThings)
+ {
+ Predicate predicate = (Thing t) => !t.IsForbidden(pawn) && scanner.HasJobOnThing(pawn, t, false);
+ List thingList = cell.GetThingList(pawn.Map);
+ for (int i = 0; i < thingList.Count; i++)
+ {
+ Thing thing = thingList[i];
+ if (scanner.PotentialWorkThingRequest.Accepts(thing) && predicate(thing))
+ {
+ pawn.mindState.lastGivenWorkType = giver.def.workType;
+ Job result = scanner.JobOnThing(pawn, thing, false);
+ return result;
+ }
+ }
+ }
+ if (giver.def.scanCells && !cell.IsForbidden(pawn) && scanner.HasJobOnCell(pawn, cell))
+ {
+ pawn.mindState.lastGivenWorkType = giver.def.workType;
+ Job result = scanner.JobOnCell(pawn, cell);
+ return result;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Log.Error(string.Concat(new object[]
+ {
+ pawn,
+ " threw exception in GiverTryGiveJobTargeted on WorkGiver ",
+ giver.def.defName,
+ ": ",
+ ex.ToString()
+ }));
+ }
+ return null;
+ }
+ }
+}
diff --git a/Source/Need_Laziness.cs b/Source/Need_Laziness.cs
new file mode 100644
index 00000000..4ad0b83a
--- /dev/null
+++ b/Source/Need_Laziness.cs
@@ -0,0 +1,219 @@
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+using Verse;
+
+namespace PrisonLabor
+{
+ public class Need_Laziness : Need
+ {
+ private const float LazyLevel = 0.8f;
+ private const float NeedInspirationLevel = 0.5f;
+ private const float LazyRate = 0.003f;
+ private const float InspireRate = 0.015f;
+ public const int WardenCapacity = (int)(InspireRate / LazyRate);
+
+ private static PrisonerInteractionModeDef pimDef;
+ private static NeedDef def;
+
+ private bool needToBeInspired;
+ private bool isLazy;
+ private int wardensCount;
+ private int prisonersCount;
+
+ private int slowDown;
+
+ public bool NeedToBeInspired
+ {
+ get
+ {
+ return needToBeInspired;
+ }
+ }
+
+ public bool IsLazy
+ {
+ get
+ {
+ return isLazy;
+ }
+ }
+
+ public float PercentageThreshNeedInsipration
+ {
+ get
+ {
+ return NeedInspirationLevel;
+ }
+ }
+
+ public float PercentageThreshLazy
+ {
+ get
+ {
+ return LazyLevel;
+ }
+ }
+
+ //TODO change to lazy category?
+ public HungerCategory CurCategory
+ {
+ get
+ {
+ return 0;
+ }
+ }
+
+ public override int GUIChangeArrow
+ {
+ get
+ {
+ if (wardensCount * WardenCapacity < prisonersCount)
+ return 1;
+ else if (wardensCount * WardenCapacity > prisonersCount)
+ return -1;
+ else
+ return 0;
+ }
+ }
+
+ public static NeedDef Def
+ {
+ get
+ {
+ if (def == null)
+ def = DefDatabase.GetNamed("PrisonLabor_Laziness");
+ return def;
+ }
+ }
+
+ public static PrisonerInteractionModeDef PimDef
+ {
+ get
+ {
+ if (pimDef == null)
+ pimDef = DefDatabase.GetNamed("PrisonLabor_workOption");
+ return pimDef;
+ }
+ }
+
+ private float LazinessRate
+ {
+ get
+ {
+ if (pawn.IsPrisoner && pawn.IsPrisonerOfColony)
+ {
+ if (pawn.GetRoomGroup() != null)
+ {
+ List pawnsInRoom = new List();
+ prisonersCount = 0;
+ wardensCount = 0;
+ foreach (IntVec3 cell in pawn.GetRoomGroup().Cells)
+ {
+ foreach (Thing thing in cell.GetThingList(pawn.Map))
+ {
+ if (thing is Pawn)
+ pawnsInRoom.Add((Pawn)thing);
+ }
+ }
+ foreach (Pawn p in pawnsInRoom)
+ {
+ // colonist nearby
+ if (p.IsFreeColonist)
+ wardensCount++;
+ if (p.IsPrisoner && p.guest.interactionMode == PimDef)
+ prisonersCount++;
+ }
+
+ if (pawn.guest.interactionMode == PimDef)
+ return LazyRate - wardensCount * InspireRate / prisonersCount;
+ else
+ return -wardensCount * InspireRate / (prisonersCount + 1);
+ }
+ else
+ {
+ return 0.0f;
+ }
+ }
+ else
+ {
+ return -0.01f;
+ }
+ }
+ }
+
+ public Need_Laziness(Pawn pawn) : base(pawn)
+ {
+ }
+
+ public override void ExposeData()
+ {
+ base.ExposeData();
+ //Scribe_Values.Look(ref this.lastNonStarvingTick, "lastNonStarvingTick", -99999, false);
+ }
+
+ public override void NeedInterval()
+ {
+ //for perfomance purposes
+ if (slowDown < 5)
+ {
+ CurLevel += LazinessRate;
+
+ if (CurLevel == 0)
+ needToBeInspired = false;
+ if (CurLevel >= NeedInspirationLevel && !needToBeInspired)
+ needToBeInspired = true;
+ if (CurLevel >= LazyLevel && !isLazy && wardensCount == 0)
+ {
+ isLazy = true;
+ Messages.Message("PrisonLabor_LazyPrisonerMessage".Translate(), pawn, MessageSound.Standard);
+ Tutorials.LazyPrisoner();
+ }
+ else if (isLazy && wardensCount > 0)
+ {
+ isLazy = false;
+ }
+
+ slowDown = 0;
+ }
+ else
+ {
+ slowDown++;
+ }
+ }
+
+ public override void SetInitialLevel()
+ {
+ CurLevel = 0.0f;
+ }
+
+ public override string GetTipString()
+ {
+ return string.Concat(new string[]
+ {
+ base.LabelCap,
+ ": ",
+ base.CurLevelPercentage.ToStringPercent(),
+ " (",
+ CurLevel.ToString("0.##"),
+ " / ",
+ MaxLevel.ToString("0.##"),
+ ")\n",
+ Def.description
+ });
+ }
+
+ public override void DrawOnGUI(Rect rect, int maxThresholdMarkers = 2147483647, float customMargin = -1f, bool drawArrows = true, bool doTooltip = true)
+ {
+ if (this.threshPercents == null)
+ {
+ this.threshPercents = new List();
+ }
+ this.threshPercents.Clear();
+ this.threshPercents.Add(this.PercentageThreshLazy);
+ this.threshPercents.Add(this.PercentageThreshNeedInsipration);
+ base.DrawOnGUI(rect, maxThresholdMarkers, customMargin, drawArrows, doTooltip);
+ }
+ }
+}
diff --git a/Source/Prefs.cs b/Source/Prefs.cs
new file mode 100644
index 00000000..ff07971a
--- /dev/null
+++ b/Source/Prefs.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Xml.Linq;
+using Verse;
+
+namespace PrisonLabor
+{
+ public static class PrisonLaborPrefs
+ {
+ private static PrisonLaborPrefsData data;
+ private static string prefsFilePath = Path.Combine(GenFilePaths.ConfigFolderPath, "PrisonData_Prefs.xml");
+
+ public static int Version
+ {
+ get
+ {
+ return PrisonLaborPrefs.data.version;
+ }
+ set
+ {
+ PrisonLaborPrefs.data.version = value;
+ PrisonLaborPrefs.Apply();
+ }
+ }
+
+ public static void Init()
+ {
+ bool flag = !new FileInfo(prefsFilePath).Exists;
+ PrisonLaborPrefs.data = new PrisonLaborPrefsData();
+ PrisonLaborPrefs.data = DirectXmlLoader.ItemFromXmlFile(prefsFilePath, true);
+ if (flag)
+ {
+ ;
+ }
+ }
+
+ public static void Save()
+ {
+ try
+ {
+ XDocument xDocument = new XDocument();
+ XElement content = DirectXmlSaver.XElementFromObject(PrisonLaborPrefs.data, typeof(PrisonLaborPrefsData));
+ xDocument.Add(content);
+ xDocument.Save(prefsFilePath);
+ }
+ catch (Exception ex)
+ {
+ GenUI.ErrorDialog("ProblemSavingFile".Translate(new object[]
+ {
+ prefsFilePath,
+ ex.ToString()
+ }));
+ Log.Error("Exception saving prefs: " + ex);
+ }
+ }
+
+ public static void Apply()
+ {
+ PrisonLaborPrefs.data.Apply();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/PrefsData.cs b/Source/PrefsData.cs
new file mode 100644
index 00000000..a116a45c
--- /dev/null
+++ b/Source/PrefsData.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace PrisonLabor
+{
+ public class PrisonLaborPrefsData
+ {
+ public int version = -1;
+
+ public PrisonLaborPrefsData()
+ {
+
+ }
+
+ public void Apply()
+ {
+ }
+ }
+}
diff --git a/Source/PrisonLabor.csproj b/Source/PrisonLabor.csproj
new file mode 100644
index 00000000..22f64230
--- /dev/null
+++ b/Source/PrisonLabor.csproj
@@ -0,0 +1,84 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {97750974-4CB6-4D31-84A1-A3AA77B1E2EE}
+ Library
+ Properties
+ PrisonLabor
+ PrisonLabor
+ v3.5
+ 512
+
+
+
+ false
+ none
+ false
+ ..\Assemblies\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ False
+ ..\Assemblies\0Harmony.dll
+ True
+
+
+ ..\..\..\RimWorldWin_Data\Managed\Assembly-CSharp.dll
+ False
+
+
+
+
+
+
+
+
+ ..\..\..\RimWorldWin_Data\Managed\UnityEngine.dll
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Source/PrisonerInteractionModeOf.cs b/Source/PrisonerInteractionModeOf.cs
new file mode 100644
index 00000000..a9326e39
--- /dev/null
+++ b/Source/PrisonerInteractionModeOf.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+
+//Work in progress
+
+namespace RimWorldd
+{
+ [DefOf]
+ public static class PrisonerInteractionModeDefOf
+ {
+ public static PrisonerInteractionModeDef NoInteraction;
+
+ public static PrisonerInteractionModeDef Chat;
+
+ //public static PrisonerInteractionModeDef Work;
+
+ public static PrisonerInteractionModeDef AttemptRecruit;
+
+ public static PrisonerInteractionModeDef Release;
+
+ public static PrisonerInteractionModeDef Execution;
+ }
+}
diff --git a/Source/PrisonerInteractionModeUtility.cs b/Source/PrisonerInteractionModeUtility.cs
new file mode 100644
index 00000000..bfbbca45
--- /dev/null
+++ b/Source/PrisonerInteractionModeUtility.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+
+//Work in progress
+
+namespace RimWorldd
+{
+ public static class PrisonerInteractionModeUtility
+ {
+ public static string GetLabel(this PrisonerInteractionModeDef mode)
+ {
+ if (mode == PrisonerInteractionModeDefOf.NoInteraction)
+ {
+ return "PrisonerNoInteraction".Translate();
+ }
+ if (mode == PrisonerInteractionModeDefOf.Chat)
+ {
+ return "PrisonerFriendlyChat".Translate();
+ }
+ /*
+ if (mode == PrisonerInteractionModeDefOf.Work)
+ {
+ return "Work";
+ }
+ */
+ if (mode == PrisonerInteractionModeDefOf.AttemptRecruit)
+ {
+ return "PrisonerAttemptRecruit".Translate();
+ }
+ if (mode == PrisonerInteractionModeDefOf.Release)
+ {
+ return "PrisonerRelease".Translate();
+ }
+ if (mode == PrisonerInteractionModeDefOf.Execution)
+ {
+ return "PrisonerExecution".Translate();
+ }
+ return "Mode needs label";
+ }
+ }
+}
diff --git a/Source/Properties/AssemblyInfo.cs b/Source/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..7aeb72f4
--- /dev/null
+++ b/Source/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("PrisonLabor")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("PrisonLabor")]
+[assembly: AssemblyCopyright("Copyright © 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("97750974-4cb6-4d31-84a1-a3aa77b1e2ee")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("0.3.0.0")]
+[assembly: AssemblyFileVersion("0.3.0.0")]
diff --git a/Source/ThinkNode_ConditionalIsForced.cs b/Source/ThinkNode_ConditionalIsForced.cs
new file mode 100644
index 00000000..313e0639
--- /dev/null
+++ b/Source/ThinkNode_ConditionalIsForced.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+
+namespace PrisonLabor
+{
+ class ThinkNode_ConditionalIsForced : ThinkNode_Conditional
+ {
+ protected override bool Satisfied(Pawn pawn)
+ {
+ //can work
+ if (pawn.IsPrisoner)
+ {
+ //show tutorial
+ Tutorials.PrisonLabor();
+ if (pawn.guest.interactionMode == DefDatabase.GetNamed("PrisonLabor_workOption"))
+ {
+ //can't escape
+ IntVec3 c;
+ if (pawn.guest.PrisonerIsSecure && !RCellFinder.TryFindBestExitSpot(pawn, out c, TraverseMode.ByPawn))
+ {
+ //shouldn't rest (medical reasons)
+ if (!HealthAIUtility.ShouldSeekMedicalRest(pawn))
+ {
+ // TODO can't or don't want to change
+ if (true)
+ {
+ // needs satisfied (sleep, food, etc.)
+ // TODO add more
+ if (pawn.needs.food.CurCategory == HungerCategory.Fed &&
+ pawn.needs.rest.CurCategory == RestCategory.Rested)
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+ }
+}
diff --git a/Source/Tutorials.cs b/Source/Tutorials.cs
new file mode 100644
index 00000000..54911857
--- /dev/null
+++ b/Source/Tutorials.cs
@@ -0,0 +1,34 @@
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Verse;
+
+namespace PrisonLabor
+{
+ class Tutorials
+ {
+ private static ConceptDef prisonLaborDef = DefDatabase.GetNamed("PrisonLabor", true);
+ private static ConceptDef lazyPrisonerDef = DefDatabase.GetNamed("LazyPrisoner", true);
+
+ public static void PrisonLabor()
+ {
+ if (!PlayerKnowledgeDatabase.IsComplete(prisonLaborDef))
+ Verse.Find.Tutor.learningReadout.TryActivateConcept(prisonLaborDef);
+ //Move it to point after map genration
+ if(Initialization.oldPlayerNotification)
+ {
+ Find.WindowStack.Add(new Dialog_MessageBox("PrisonLabor machanics has changed.\n\n 1. Prisoner Interaction mode must be set to \"Work\" instead of \"No Interaction\".\n 2. Now prisoners will get lazy if not supervised. Warden job will include watching prisoners to prevent that (you can always draft your colonists).", "Ok", null, null, null, "PrisonLabor - New mechanics", false));
+ Initialization.oldPlayerNotification = false;
+ }
+ }
+
+ public static void LazyPrisoner()
+ {
+ if (!PlayerKnowledgeDatabase.IsComplete(lazyPrisonerDef))
+ Verse.Find.Tutor.learningReadout.TryActivateConcept(lazyPrisonerDef);
+ }
+
+ }
+}
diff --git a/Source/Tweaks/JobDriver_Mine_Tweak.cs b/Source/Tweaks/JobDriver_Mine_Tweak.cs
new file mode 100644
index 00000000..dd49a541
--- /dev/null
+++ b/Source/Tweaks/JobDriver_Mine_Tweak.cs
@@ -0,0 +1,102 @@
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Verse;
+using Verse.AI;
+
+namespace PrisonLabor
+{
+ public class JobDriver_Mine_Tweak : JobDriver
+ {
+ public const int BaseTicksBetweenPickHits = 120;
+
+ private const int BaseDamagePerPickHit = 80;
+
+ private const float MinMiningSpeedForNPCs = 0.5f;
+
+ private int ticksToPickHit = -1000;
+
+ private Effecter effecter;
+
+ private Thing MineTarget
+ {
+ get
+ {
+ return base.CurJob.GetTarget(TargetIndex.A).Thing;
+ }
+ }
+
+ protected override IEnumerable MakeNewToils()
+ {
+ this.FailOnDespawnedNullOrForbidden(TargetIndex.A);
+ this.FailOnCellMissingDesignation(TargetIndex.A, DesignationDefOf.Mine);
+ yield return Toils_Reserve.Reserve(TargetIndex.A, 1, -1, null);
+ yield return Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.Touch);
+ Toil mine = new Toil();
+ mine.tickAction = delegate
+ {
+ Pawn actor = mine.actor;
+ Thing mineTarget = this.MineTarget;
+ if (this.ticksToPickHit < -100)
+ {
+ this.ResetTicksToPickHit();
+ }
+ if (actor.skills != null)
+ {
+ actor.skills.Learn(SkillDefOf.Mining, 0.11f, false);
+ }
+ this.ticksToPickHit--;
+ if (this.ticksToPickHit <= 0)
+ {
+ IntVec3 position = mineTarget.Position;
+ if (this.effecter == null)
+ {
+ this.effecter = EffecterDefOf.Mine.Spawn();
+ }
+ this.effecter.Trigger(actor, mineTarget);
+ int num = 80;
+ Mineable mineable = mineTarget as Mineable;
+ if (mineable == null || mineTarget.HitPoints > num)
+ {
+ Pawn actor2 = mine.actor;
+ DamageInfo dinfo = new DamageInfo(DamageDefOf.Mining, num, -1f, actor2, null, null, DamageInfo.SourceCategory.ThingOrUnknown);
+ mineTarget.TakeDamage(dinfo);
+ }
+ else
+ {
+ mineable.DestroyMined(actor);
+ }
+ if (mineTarget.Destroyed)
+ {
+ actor.Map.mineStrikeManager.CheckStruckOre(position, mineTarget.def, actor);
+ actor.records.Increment(RecordDefOf.CellsMined);
+ this.ReadyForNextToil();
+ return;
+ }
+ this.ResetTicksToPickHit();
+ }
+ };
+ mine.defaultCompleteMode = ToilCompleteMode.Never;
+ mine.WithProgressBar(TargetIndex.A, () => 1f - (float)this.MineTarget.HitPoints / (float)this.MineTarget.MaxHitPoints, false, -0.5f);
+ mine.FailOnCannotTouch(TargetIndex.A, PathEndMode.Touch);
+ yield return mine;
+ }
+
+ private void ResetTicksToPickHit()
+ {
+ float num = this.pawn.GetStatValue(StatDefOf.MiningSpeed, true);
+ if (num < 0.5f && this.pawn.Faction != Faction.OfPlayer)
+ {
+ num = 0.5f;
+ }
+ this.ticksToPickHit = (int)Math.Round((double)(120f / num));
+ }
+
+ public override void ExposeData()
+ {
+ base.ExposeData();
+ Scribe_Values.Look(ref this.ticksToPickHit, "ticksToPickHit", 0, false);
+ }
+ }
+}
diff --git a/Source/Tweaks/JobDriver_PlantCut_Tweak.cs b/Source/Tweaks/JobDriver_PlantCut_Tweak.cs
new file mode 100644
index 00000000..8d4618a4
--- /dev/null
+++ b/Source/Tweaks/JobDriver_PlantCut_Tweak.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using RimWorld;
+using Verse.AI;
+using Verse;
+
+namespace PrisonLabor
+{
+ public class JobDriver_PlantCut_Tweak : JobDriver_PlantWork_Tweak
+ {
+ protected override void Init()
+ {
+ if (base.Plant.def.plant.harvestedThingDef != null && base.Plant.YieldNow() > 0)
+ {
+ this.xpPerTick = 0.11f;
+ }
+ else
+ {
+ this.xpPerTick = 0f;
+ }
+ }
+
+ protected override IEnumerable MakeNewToils()
+ {
+ foreach (Toil toil in base.MakeNewToils())
+ {
+ yield return toil;
+ }
+ Toil toil2 = new Toil();
+ toil2.initAction = delegate
+ {
+ Pawn actor = toil2.actor;
+ Thing thing = actor.jobs.curJob.GetTarget(TargetIndex.A).Thing;
+ if (!thing.Destroyed)
+ {
+ thing.Destroy(DestroyMode.Vanish);
+ }
+ };
+ yield return toil2;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/Tweaks/JobDriver_PlantHarvest_Tweak.cs b/Source/Tweaks/JobDriver_PlantHarvest_Tweak.cs
new file mode 100644
index 00000000..fc60a6b0
--- /dev/null
+++ b/Source/Tweaks/JobDriver_PlantHarvest_Tweak.cs
@@ -0,0 +1,25 @@
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Verse.AI;
+
+namespace PrisonLabor
+{
+ public class JobDriver_PlantHarvest_Tweak : JobDriver_PlantWork_Tweak
+ {
+ protected override void Init()
+ {
+ this.xpPerTick = 0.11f;
+ }
+
+ protected override IEnumerable MakeNewToils()
+ {
+ foreach (Toil toil in base.MakeNewToils())
+ {
+ yield return toil;
+ }
+ yield return Toils_General.RemoveDesignationsOnThing(TargetIndex.A, DesignationDefOf.HarvestPlant);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/Tweaks/JobDriver_PlantWork_Tweak.cs b/Source/Tweaks/JobDriver_PlantWork_Tweak.cs
new file mode 100644
index 00000000..9fc0d310
--- /dev/null
+++ b/Source/Tweaks/JobDriver_PlantWork_Tweak.cs
@@ -0,0 +1,100 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using UnityEngine;
+using RimWorld;
+using Verse;
+using Verse.AI;
+using Verse.Sound;
+
+namespace PrisonLabor
+{
+ public abstract class JobDriver_PlantWork_Tweak : JobDriver
+ {
+ protected const TargetIndex PlantInd = TargetIndex.A;
+
+ private float workDone;
+
+ protected float xpPerTick;
+
+ protected Plant Plant
+ {
+ get
+ {
+ return (Plant)base.CurJob.targetA.Thing;
+ }
+ }
+
+ [DebuggerHidden]
+ protected override IEnumerable MakeNewToils()
+ {
+ this.Init();
+ yield return Toils_JobTransforms.MoveCurrentTargetIntoQueue(TargetIndex.A);
+ yield return Toils_Reserve.ReserveQueue(TargetIndex.A, 1, -1, null);
+ Toil initExtractTargetFromQueue = Toils_JobTransforms.ClearDespawnedNullOrForbiddenQueuedTargets(TargetIndex.A);
+ yield return initExtractTargetFromQueue;
+ yield return Toils_JobTransforms.ExtractNextTargetFromQueue(TargetIndex.A);
+ Toil checkNextQueuedTarget = Toils_JobTransforms.ClearDespawnedNullOrForbiddenQueuedTargets(TargetIndex.A);
+ yield return Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.Touch).JumpIfDespawnedOrNullOrForbidden(TargetIndex.A, checkNextQueuedTarget);
+ Toil cut = new Toil();
+ cut.tickAction = delegate
+ {
+ Pawn actor = cut.actor;
+ if (actor.skills != null)
+ {
+ actor.skills.Learn(SkillDefOf.Growing, this.xpPerTick, false);
+ }
+ float statValue = actor.GetStatValue(StatDefOf.PlantWorkSpeed, true);
+ float num = statValue;
+ Plant plant = this.Plant;
+ this.workDone += num;
+ if (this.workDone >= plant.def.plant.harvestWork)
+ {
+ if (plant.def.plant.harvestedThingDef != null)
+ {
+ if (actor.RaceProps.Humanlike && plant.def.plant.harvestFailable && Rand.Value > actor.GetStatValue(StatDefOf.PlantHarvestYield, true))
+ {
+ Vector3 loc = (this.pawn.DrawPos + plant.DrawPos) / 2f;
+ MoteMaker.ThrowText(loc, this.Map, "TextMote_HarvestFailed".Translate(), 3.65f);
+ }
+ else
+ {
+ int num2 = plant.YieldNow();
+ if (num2 > 0)
+ {
+ Thing thing = ThingMaker.MakeThing(plant.def.plant.harvestedThingDef, null);
+ thing.stackCount = num2;
+ GenPlace.TryPlaceThing(thing, actor.Position, this.Map, ThingPlaceMode.Near, null);
+ actor.records.Increment(RecordDefOf.PlantsHarvested);
+ }
+ }
+ }
+ plant.def.plant.soundHarvestFinish.PlayOneShot(actor);
+ plant.PlantCollected();
+ this.workDone = 0f;
+ this.ReadyForNextToil();
+ return;
+ }
+ };
+ cut.FailOnDespawnedNullOrForbidden(TargetIndex.A);
+ cut.FailOnCannotTouch(TargetIndex.A, PathEndMode.Touch);
+ cut.defaultCompleteMode = ToilCompleteMode.Never;
+ cut.WithEffect(EffecterDefOf.Harvest, TargetIndex.A);
+ cut.WithProgressBar(TargetIndex.A, () => this.workDone / this.Plant.def.plant.harvestWork, true, -0.5f);
+ cut.PlaySustainerOrSound(() => this.Plant.def.plant.soundHarvesting);
+ yield return cut;
+ yield return checkNextQueuedTarget;
+ yield return Toils_Jump.JumpIfHaveTargetInQueue(TargetIndex.A, initExtractTargetFromQueue);
+ }
+
+ public override void ExposeData()
+ {
+ base.ExposeData();
+ Scribe_Values.Look(ref this.workDone, "workDone", 0f, false);
+ }
+
+ protected virtual void Init()
+ {
+ }
+ }
+}
diff --git a/Source/Tweaks/WorkGiver_GrowerHarvest_Tweak.cs b/Source/Tweaks/WorkGiver_GrowerHarvest_Tweak.cs
new file mode 100644
index 00000000..b6bfcfef
--- /dev/null
+++ b/Source/Tweaks/WorkGiver_GrowerHarvest_Tweak.cs
@@ -0,0 +1,58 @@
+using RimWorld;
+using System;
+using Verse;
+using Verse.AI;
+
+namespace PrisonLabor
+{
+ public class WorkGiver_GrowerHarvest_Tweak : WorkGiver_Grower
+ {
+ public override PathEndMode PathEndMode
+ {
+ get
+ {
+ return PathEndMode.Touch;
+ }
+ }
+
+ public override bool HasJobOnCell(Pawn pawn, IntVec3 c)
+ {
+ if (!pawn.IsPrisoner || !pawn.IsPrisonerOfColony)
+ {
+ return false;
+ }
+ Plant plant = c.GetPlant(pawn.Map);
+ return plant != null && !plant.IsForbidden(pawn) && plant.def.plant.Harvestable && plant.LifeStage == PlantLifeStage.Mature && pawn.CanReserve(plant, 1, -1, null, false);
+ }
+
+ public override Job JobOnCell(Pawn pawn, IntVec3 c)
+ {
+ Job job = new Job(JobDefOf.Harvest);
+ Map map = pawn.Map;
+ Room room = c.GetRoom(map, RegionType.Set_Passable);
+ float num = 0f;
+ for (int i = 0; i < 40; i++)
+ {
+ IntVec3 c2 = c + GenRadial.RadialPattern[i];
+ if (c.GetRoom(map, RegionType.Set_Passable) == room)
+ {
+ if (this.HasJobOnCell(pawn, c2))
+ {
+ Plant plant = c2.GetPlant(map);
+ num += plant.def.plant.harvestWork;
+ if (num > 2400f)
+ {
+ break;
+ }
+ job.AddQueuedTarget(TargetIndex.A, plant);
+ }
+ }
+ }
+ if (job.targetQueueA != null && job.targetQueueA.Count >= 3)
+ {
+ job.targetQueueA.SortBy((LocalTargetInfo targ) => targ.Cell.DistanceToSquared(pawn.Position));
+ }
+ return job;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/Tweaks/WorkGiver_Miner_Tweak.cs b/Source/Tweaks/WorkGiver_Miner_Tweak.cs
new file mode 100644
index 00000000..9b4a7d31
--- /dev/null
+++ b/Source/Tweaks/WorkGiver_Miner_Tweak.cs
@@ -0,0 +1,98 @@
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Verse;
+using Verse.AI;
+
+namespace PrisonLabor
+{
+ public class WorkGiver_Miner_Tweak : WorkGiver_Scanner
+ {
+ public override PathEndMode PathEndMode
+ {
+ get
+ {
+ return PathEndMode.Touch;
+ }
+ }
+
+ public override IEnumerable PotentialWorkThingsGlobal(Pawn pawn)
+ {
+ foreach (Designation des in pawn.Map.designationManager.SpawnedDesignationsOfDef(DesignationDefOf.Mine))
+ {
+ bool mayBeAccessible = false;
+ for (int i = 0; i < 8; i++)
+ {
+ IntVec3 c = des.target.Cell + GenAdj.AdjacentCells[i];
+ if (c.InBounds(pawn.Map) && c.Walkable(pawn.Map))
+ {
+ mayBeAccessible = true;
+ break;
+ }
+ }
+ if (mayBeAccessible)
+ {
+ Thing j = MineUtility.MineableInCell(des.target.Cell, pawn.Map);
+ if (j != null)
+ {
+ yield return j;
+ }
+ }
+ }
+ }
+
+ public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false)
+ {
+ if (!pawn.IsPrisoner || !pawn.IsPrisonerOfColony)
+ {
+ return null;
+ }
+ if (!t.def.mineable)
+ {
+ return null;
+ }
+ if (pawn.Map.designationManager.DesignationAt(t.Position, DesignationDefOf.Mine) == null)
+ {
+ return null;
+ }
+ if (!pawn.CanReserve(t, 1, -1, null, false))
+ {
+ return null;
+ }
+ bool flag = false;
+ for (int i = 0; i < 8; i++)
+ {
+ IntVec3 intVec = t.Position + GenAdj.AdjacentCells[i];
+ if (intVec.InBounds(pawn.Map) && intVec.Standable(pawn.Map) && ReachabilityImmediate.CanReachImmediate(intVec, t, pawn.Map, PathEndMode.Touch, pawn))
+ {
+ flag = true;
+ break;
+ }
+ }
+ if (!flag)
+ {
+ for (int j = 0; j < 8; j++)
+ {
+ IntVec3 intVec2 = t.Position + GenAdj.AdjacentCells[j];
+ if (intVec2.InBounds(t.Map))
+ {
+ if (ReachabilityImmediate.CanReachImmediate(intVec2, t, pawn.Map, PathEndMode.Touch, pawn))
+ {
+ if (intVec2.Walkable(t.Map) && !intVec2.Standable(t.Map))
+ {
+ Thing firstHaulable = intVec2.GetFirstHaulable(t.Map);
+ if (firstHaulable != null && firstHaulable.def.passability == Traversability.PassThroughOnly)
+ {
+ return HaulAIUtility.HaulAsideJobFor(pawn, firstHaulable);
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+ return new Job(DefDatabase.GetNamed("PrisonLabor_Mine_Tweak"), t, 1500, true);
+ }
+ }
+}
diff --git a/Source/Tweaks/WorkGiver_PlantsCut_Tweak.cs b/Source/Tweaks/WorkGiver_PlantsCut_Tweak.cs
new file mode 100644
index 00000000..76fbc73f
--- /dev/null
+++ b/Source/Tweaks/WorkGiver_PlantsCut_Tweak.cs
@@ -0,0 +1,77 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Verse;
+using Verse.AI;
+using RimWorld;
+
+namespace PrisonLabor
+{
+ public class WorkGiver_PlantsCut_Tweak : WorkGiver_Scanner
+ {
+ public override PathEndMode PathEndMode
+ {
+ get
+ {
+ return PathEndMode.Touch;
+ }
+ }
+
+ public override IEnumerable PotentialWorkThingsGlobal(Pawn pawn)
+ {
+ List desList = pawn.Map.designationManager.allDesignations;
+ for (int i = 0; i < desList.Count; i++)
+ {
+ Designation des = desList[i];
+ if (des.def == DesignationDefOf.CutPlant || des.def == DesignationDefOf.HarvestPlant)
+ {
+ yield return des.target.Thing;
+ }
+ }
+ }
+
+ public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false)
+ {
+ if(!pawn.IsPrisoner || !pawn.IsPrisonerOfColony)
+ {
+ return null;
+ }
+ if (t.def.category != ThingCategory.Plant)
+ {
+ return null;
+ }
+ if (!pawn.CanReserve(t, 1, -1, null, false))
+ {
+ return null;
+ }
+ if (t.IsForbidden(pawn))
+ {
+ return null;
+ }
+ if (t.IsBurning())
+ {
+ return null;
+ }
+ foreach (Designation current in pawn.Map.designationManager.AllDesignationsOn(t))
+ {
+ if (current.def == DesignationDefOf.HarvestPlant)
+ {
+ Job result;
+ if (!((Plant)t).HarvestableNow)
+ {
+ result = null;
+ return result;
+ }
+ result = new Job(DefDatabase.GetNamed("PrisonLabor_Harvest_Tweak"), t);
+ return result;
+ }
+ else if (current.def == DesignationDefOf.CutPlant)
+ {
+ Job result = new Job(DefDatabase.GetNamed("PrisonLabor_CutPlant_Tweak"), t);
+ return result;
+ }
+ }
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/Tweaks/WorkGiver_Warden_DeliverFood_Tweak.cs b/Source/Tweaks/WorkGiver_Warden_DeliverFood_Tweak.cs
new file mode 100644
index 00000000..e77d1c7d
--- /dev/null
+++ b/Source/Tweaks/WorkGiver_Warden_DeliverFood_Tweak.cs
@@ -0,0 +1,122 @@
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Verse;
+using Verse.AI;
+
+namespace PrisonLabor
+{
+ class WorkGiver_Warden_DeliverFood_Tweak : WorkGiver_Warden
+ {
+ public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false)
+ {
+ if (!base.ShouldTakeCareOfPrisoner(pawn, t))
+ {
+ Messages.Message("one", MessageSound.Negative);
+ return null;
+ }
+ Pawn pawn2 = (Pawn)t;
+ if (!pawn2.guest.CanBeBroughtFood)
+ {
+ Messages.Message("two", MessageSound.Negative);
+ return null;
+ }
+ //TODO test this condition
+ IntVec3 c;
+ //if (pawn2.Position.IsInPrisonCell(pawn2.Map) || RCellFinder.TryFindBestExitSpot((Pawn)t, out c, TraverseMode.ByPawn))
+ if(false)
+ {
+ return null;
+ }
+ if (pawn2.needs.food.CurLevelPercentage >= pawn2.needs.food.PercentageThreshHungry + 0.02f)
+ {
+ Messages.Message("three", MessageSound.Negative);
+ return null;
+ }
+ if (WardenFeedUtility.ShouldBeFed(pawn2))
+ {
+ Messages.Message("four", MessageSound.Negative);
+ return null;
+ }
+ Thing thing;
+ ThingDef def;
+ if (!FoodUtility.TryFindBestFoodSourceFor(pawn, pawn2, pawn2.needs.food.CurCategory == HungerCategory.Starving, out thing, out def, false, true, false, false, false))
+ {
+ Messages.Message("five", MessageSound.Negative);
+ return null;
+ }
+ if (thing.GetRoom(RegionType.Set_Passable) == pawn2.GetRoom(RegionType.Set_Passable))
+ {
+ Messages.Message("six", MessageSound.Negative);
+ return null;
+ }
+ if (WorkGiver_Warden_DeliverFood_Tweak.FoodAvailableInRoomTo(pawn2))
+ {
+ Messages.Message("seven", MessageSound.Negative);
+ return null;
+ }
+ return new Job(JobDefOf.DeliverFood, thing, pawn2)
+ {
+ count = FoodUtility.WillIngestStackCountOf(pawn2, def),
+ targetC = RCellFinder.SpotToChewStandingNear(pawn2, thing)
+ };
+ }
+
+ private static bool FoodAvailableInRoomTo(Pawn prisoner)
+ {
+ if (prisoner.carryTracker.CarriedThing != null && WorkGiver_Warden_DeliverFood_Tweak.NutritionAvailableForFrom(prisoner, prisoner.carryTracker.CarriedThing) > 0f)
+ {
+ return true;
+ }
+ float num = 0f;
+ float num2 = 0f;
+ Room room = prisoner.GetRoom(RegionType.Set_Passable);
+ if (room == null)
+ {
+ return false;
+ }
+ for (int i = 0; i < room.RegionCount; i++)
+ {
+ Region region = room.Regions[i];
+ List list = region.ListerThings.ThingsInGroup(ThingRequestGroup.FoodSourceNotPlantOrTree);
+ for (int j = 0; j < list.Count; j++)
+ {
+ Thing thing = list[j];
+ if (!thing.def.IsIngestible || thing.def.ingestible.preferability > FoodPreferability.DesperateOnly)
+ {
+ num2 += WorkGiver_Warden_DeliverFood_Tweak.NutritionAvailableForFrom(prisoner, thing);
+ }
+ }
+ List list2 = region.ListerThings.ThingsInGroup(ThingRequestGroup.Pawn);
+ for (int k = 0; k < list2.Count; k++)
+ {
+ Pawn pawn = list2[k] as Pawn;
+ if (pawn.IsPrisonerOfColony && pawn.needs.food.CurLevelPercentage < pawn.needs.food.PercentageThreshHungry + 0.02f && (pawn.carryTracker.CarriedThing == null || !pawn.RaceProps.WillAutomaticallyEat(pawn.carryTracker.CarriedThing)))
+ {
+ num += pawn.needs.food.NutritionWanted;
+ }
+ }
+ }
+ return num2 + 0.5f >= num;
+ }
+
+ private static float NutritionAvailableForFrom(Pawn p, Thing foodSource)
+ {
+ if (foodSource.def.IsNutritionGivingIngestible && p.RaceProps.WillAutomaticallyEat(foodSource))
+ {
+ return foodSource.def.ingestible.nutrition * (float)foodSource.stackCount;
+ }
+ if (p.RaceProps.ToolUser && p.health.capacities.CapableOf(PawnCapacityDefOf.Manipulation))
+ {
+ Building_NutrientPasteDispenser building_NutrientPasteDispenser = foodSource as Building_NutrientPasteDispenser;
+ if (building_NutrientPasteDispenser != null && building_NutrientPasteDispenser.CanDispenseNow)
+ {
+ return 99999f;
+ }
+ }
+ return 0f;
+ }
+ }
+}
diff --git a/Source/VersionChecker.cs b/Source/VersionChecker.cs
new file mode 100644
index 00000000..8a05cb36
--- /dev/null
+++ b/Source/VersionChecker.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+using Harmony;
+using System.Reflection;
+using System.Reflection.Emit;
+
+namespace PrisonLabor
+{
+ [StaticConstructorOnStartup]
+ class VersionChecker
+ {
+ static VersionChecker()
+ {
+ var harmony = HarmonyInstance.Create("Harmony_PrisonLabor");
+ harmony.PatchAll(Assembly.GetExecutingAssembly());
+ }
+ }
+
+ [HarmonyPatch(typeof(PrisonerInteractionModeUtility))]
+ [HarmonyPatch("GetLabel")]
+ [HarmonyPatch(new Type[] { typeof(PrisonerInteractionModeDef) })]
+ class Patch
+ {
+ static IEnumerable Transpiler(ILGenerator gen, MethodBase mBase, IEnumerable instr)
+ {
+ // create our WORK label
+ Label jumpTo = gen.DefineLabel();
+ yield return new CodeInstruction(OpCodes.Ldarg_0);
+ yield return new CodeInstruction(OpCodes.Call, typeof(Patch).GetMethod("getLabelWork"));
+ //yield return new CodeInstruction(OpCodes.Dup);
+ yield return new CodeInstruction(OpCodes.Ldstr, "Work");
+ yield return new CodeInstruction(OpCodes.Bne_Un, jumpTo);
+ yield return new CodeInstruction(OpCodes.Ldstr, "Work");
+ yield return new CodeInstruction(OpCodes.Ret);
+
+ bool first = true;
+ foreach (CodeInstruction ci in instr)
+ {
+ if (first)
+ {
+ first = false;
+ ci.labels.Add(jumpTo);
+ }
+ //debug
+ Log.Message("CODE: ToString():" + ci.ToString() + " || labels:" + ci.labels + " || opcode:" + ci.opcode.ToString());
+ yield return ci;
+ }
+ }
+
+ public static string getLabelWork(PrisonerInteractionModeDef def)
+ {
+ if(def == DefDatabase.GetNamed("PrisonLabor_workOption"))
+ return "Work";
+ return "";
+ }
+ }
+}
diff --git a/Source/WorkGiver_Supervise.cs b/Source/WorkGiver_Supervise.cs
new file mode 100644
index 00000000..7178e4c4
--- /dev/null
+++ b/Source/WorkGiver_Supervise.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+using Verse.AI;
+
+namespace PrisonLabor
+{
+ class WorkGiver_Supervise : WorkGiver_Warden
+ {
+ public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false)
+ {
+ if (!base.ShouldTakeCareOfPrisoner(pawn, t) || ((Pawn)t).needs.TryGetNeed() == null || !((Pawn)t).needs.TryGetNeed().NeedToBeInspired)
+ {
+ return null;
+ }
+ if (((Pawn)t).needs.food.CurCategory != HungerCategory.Fed && ((Pawn)t).needs.rest.CurCategory != RestCategory.Rested)
+ {
+ return null;
+ }
+ Pawn pawn2 = (Pawn)t;
+ if (pawn2.guest.interactionMode == DefDatabase.GetNamed("PrisonLabor_workOption") && (!pawn2.Downed || pawn2.InBed()) && pawn.CanReserve(t, 1, -1, null, false) && pawn2.Awake())
+ {
+ return new Job(DefDatabase.GetNamed("PrisonLabor_PrisonerSupervise"), t);
+ }
+ return null;
+ }
+ }
+}
diff --git a/Translation sheets/Translation-german.xlsx b/Translation sheets/Translation-german.xlsx
new file mode 100644
index 00000000..5a7aaa29
Binary files /dev/null and b/Translation sheets/Translation-german.xlsx differ
diff --git a/changelog.txt b/changelog.txt
new file mode 100644
index 00000000..af605948
--- /dev/null
+++ b/changelog.txt
@@ -0,0 +1,17 @@
+Changelog:
+ 0.4
+ - added "Laziness" bar in "Needs" tab
+ - fixed plant cut / harvest result being forbidden
+ - added German translation
+ 0.3b
+ - fixed "Forbidden" bug
+ 0.3a
+ - wardens no longer watch over hungry or tired prisoners
+ 0.3
+ - added work of Warden type that supervise prisoners
+ - prisoners will get lazy
+ - added version checker
+ - added stat laziness
+ - added "Work" prisoner interaction mode
+ 0.2a
+ - added tutorial in "LearningHelper"