diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..75e7a15
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,4 @@
+[*.cs]
+
+# IDE0290: Use primary constructor
+csharp_style_prefer_primary_constructors = false
diff --git a/.gitattributes b/.gitattributes
index bdb0cab..1ff0c42 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,17 +1,63 @@
-# Auto detect text files and perform LF normalization
+###############################################################################
+# Set default behavior to automatically normalize line endings.
+###############################################################################
* text=auto
-# Custom for Visual Studio
-*.cs diff=csharp
+###############################################################################
+# Set default behavior for command prompt diff.
+#
+# This is need for earlier builds of msysgit that does not have it on by
+# default for csharp files.
+# Note: This is only used by command line
+###############################################################################
+#*.cs diff=csharp
-# Standard to msysgit
-*.doc diff=astextplain
-*.DOC diff=astextplain
-*.docx diff=astextplain
-*.DOCX diff=astextplain
-*.dot diff=astextplain
-*.DOT diff=astextplain
-*.pdf diff=astextplain
-*.PDF diff=astextplain
-*.rtf diff=astextplain
-*.RTF diff=astextplain
+###############################################################################
+# Set the merge driver for project and solution files
+#
+# Merging from the command prompt will add diff markers to the files if there
+# are conflicts (Merging from VS is not affected by the settings below, in VS
+# the diff markers are never inserted). Diff markers may cause the following
+# file extensions to fail to load in VS. An alternative would be to treat
+# these files as binary and thus will always conflict and require user
+# intervention with every merge. To do so, just uncomment the entries below
+###############################################################################
+#*.sln merge=binary
+#*.csproj merge=binary
+#*.vbproj merge=binary
+#*.vcxproj merge=binary
+#*.vcproj merge=binary
+#*.dbproj merge=binary
+#*.fsproj merge=binary
+#*.lsproj merge=binary
+#*.wixproj merge=binary
+#*.modelproj merge=binary
+#*.sqlproj merge=binary
+#*.wwaproj merge=binary
+
+###############################################################################
+# behavior for image files
+#
+# image files are treated as binary by default.
+###############################################################################
+#*.jpg binary
+#*.png binary
+#*.gif binary
+
+###############################################################################
+# diff behavior for common document formats
+#
+# Convert binary document formats to text before diffing them. This feature
+# is only available from the command line. Turn it on by uncommenting the
+# entries below.
+###############################################################################
+#*.doc diff=astextplain
+#*.DOC diff=astextplain
+#*.docx diff=astextplain
+#*.DOCX diff=astextplain
+#*.dot diff=astextplain
+#*.DOT diff=astextplain
+#*.pdf diff=astextplain
+#*.PDF diff=astextplain
+#*.rtf diff=astextplain
+#*.RTF diff=astextplain
diff --git a/.gitignore b/.gitignore
index a9f9c14..3dd1246 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,51 +1,262 @@
-# Windows image file caches
-Thumbs.db
-ehthumbs.db
-
-# Folder config file
-Desktop.ini
-
-# Recycle Bin used on file shares
-$RECYCLE.BIN/
-
-**/bin
-**/obj
-**/.vs
-
-# Windows Installer files
-*.cab
-*.msi
-*.msm
-*.msp
-
-# Windows shortcuts
-*.lnk
-
-# =========================
-# Operating System Files
-# =========================
-
-# OSX
-# =========================
-
-.DS_Store
-.AppleDouble
-.LSOverride
-
-# Thumbnails
-._*
-
-# Files that might appear on external disk
-.Spotlight-V100
-.Trashes
-
-# Directories potentially created on remote AFP share
-.AppleDB
-.AppleDesktop
-Network Trash Folder
-Temporary Items
-.apdisk
-*.txt
-Source/BetterAnimalsTab/obj/Debug/BetterAnimalsTab.pdb
-*.cache
-*.pxd
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# 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
+
+# DNX
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+*_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
+
+# 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 ignoreable 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
+node_modules/
+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
+
+# 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
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# 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
+1.5/Assemblies/0Harmony.dll
diff --git a/1.1/Assemblies/Numbers.dll b/1.1/Assemblies/Numbers.dll
new file mode 100644
index 0000000..771b61e
Binary files /dev/null and b/1.1/Assemblies/Numbers.dll differ
diff --git a/1.1/Defs/MainTabDefs/kNumbers.xml b/1.1/Defs/MainTabDefs/kNumbers.xml
new file mode 100644
index 0000000..5809ee8
--- /dev/null
+++ b/1.1/Defs/MainTabDefs/kNumbers.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ kNumbersOverviewTab
+ Numbers
+ See every possible stat on every colonist or prisoner in a single table.
+ Numbers.MainTabWindow_Numbers
+ 20
+ Z
+
+
+
\ No newline at end of file
diff --git a/1.1/Defs/PawnColumnDef/PawnColumns_Numbers.xml b/1.1/Defs/PawnColumnDef/PawnColumns_Numbers.xml
new file mode 100644
index 0000000..fe1b4d6
--- /dev/null
+++ b/1.1/Defs/PawnColumnDef/PawnColumns_Numbers.xml
@@ -0,0 +1,275 @@
+
+
+
+
+
+
+ Numbers_Age
+ Numbers.PawnColumnWorker_Age
+ true
+ Age
+
+
+
+ Numbers_AnimalEggProgress
+ Numbers.PawnColumnWorker_AnimalEggProgress
+ true
+ Egg progress
+
+
+
+ Numbers_Milkfullness
+ Numbers.PawnColumnWorker_AnimalMilkFullness
+ true
+ Milk fullness
+
+
+
+ Numbers_AnimalWoolGrowth
+ Numbers.PawnColumnWorker_AnimalWoolGrowth
+ true
+ Wool growth
+
+
+
+ Numbers_LeatherType
+ Numbers.PawnColumnWorker_LeatherType
+ true
+ Leather type
+
+
+
+ Numbers_Race
+ Numbers.PawnColumnWorker_Race
+ true
+ Race
+
+
+
+ Numbers_Backstory
+ Numbers.PawnColumnWorker_Backstory
+ true
+ Title
+
+
+
+ Numbers_Psyfocus
+ Numbers.PawnColumnWorker_Psyfocus
+ true
+ Psyfocus
+
+
+
+
+
+
+ Numbers_Entropy
+ Numbers.PawnColumnWorker_Entropy
+ true
+ Heat
+
+
+
+
+
+
+ Numbers_PsylinkLevel
+ Numbers.PawnColumnWorker_PsylinkLevel
+ true
+ Psylevel
+
+
+
+
+
+
+ Numbers_Equipment
+ Numbers.PawnColumnWorker_Equipment
+ true
+ Equipment
+
+
+
+ Numbers_Inventory
+ Numbers.PawnColumnWorker_Inventory
+ true
+ Inventory
+
+
+
+ Numbers_InventoryDropAll
+ Numbers.PawnColumnWorker_InventoryDropAll
+ true
+ Drop all inventory
+
+
+
+ Numbers_JobCurrent
+ Numbers.PawnColumnWorker_JobCurrent
+ true
+ Current job
+
+
+
+ Numbers_JobQueued
+ Numbers.PawnColumnWorker_JobQueued
+ true
+ Queued job
+
+
+
+ Numbers_MentalState
+ Numbers.PawnColumnWorker_MentalState
+ true
+ Mental state
+
+
+
+ Numbers_PrisonerInteraction
+ Numbers.PawnColumnWorker_PrisonerInteraction
+ true
+ true
+ Prisoner interaction
+
+
+
+ Numbers_PrisonerRecruitmentDifficulty
+ Numbers.PawnColumnWorker_PrisonerRecruitmentDifficulty
+ true
+ Recruitment difficulty
+
+
+
+ Numbers_PrisonerResistance
+ Numbers.PawnColumnWorker_PrisonerResistance
+ true
+ Resistance remaining
+
+
+
+ Numbers_Pain
+ Numbers.PawnColumnWorker_Pain
+ true
+ Pain
+
+
+
+ Numbers_Bleedrate
+ Numbers.PawnColumnWorker_Bleedrate
+ true
+ Bleedrate
+
+
+
+ Numbers_NeedsTreatment
+ Numbers.PawnColumnWorker_NeedsTreatment
+ true
+ Needs treatment
+
+
+
+ Numbers_Operations
+ Numbers.PawnColumnWorker_OperationDropDown
+ true
+ Operations
+
+
+
+ Numbers_Forbidden
+ Numbers.PawnColumnWorker_Forbidden
+ true
+ Forbidden
+ true
+
+
+
+ Numbers_Inspiration
+ Numbers.PawnColumnWorker_Inspiration
+ true
+ Inspiration
+
+
+
+ Numbers_DiseaseProgress
+ Numbers.PawnColumnWorker_DiseaseProgression
+ true
+ Disease immunity margin
+ Disease immunity margin
+ 80
+
+
+
+ Numbers_ManhunterOnTameFailChance
+ Numbers.PawnColumnWorker_ManhunterOnTameFailChance
+ true
+ Revenge chance on tame fail
+ UI/Icons/Animal/Predator
+ 45
+
+
+
+ Numbers_Wildness
+ Numbers.PawnColumnWorker_AnimalWildness
+ true
+ Wildness
+
+
+
+ Numbers_TameChance
+ Numbers.PawnColumnWorker_TameChance
+ true
+ Tame chance
+
+
+
+ Numbers_SelfTend
+ Numbers.PawnColumnWorker_SelfTend
+ true
+ true
+ Self-tend
+ UI/Icons/Trainables/Rescue
+ 45
+
+
+
+ Numbers_HediffList
+ Numbers.PawnColumnWorker_AllHediffs
+ true
+ UI/Buttons/DevRoot/ToggleTweak
+ Hediffs
+ 45
+
+
+
+ Numbers_Faction
+ Numbers.PawnColumnWorker_Faction
+ true
+ Faction
+
+
+
+ Numbers_AnimalFilthRate
+ Numbers.PawnColumnWorker_AnimalFilthRate
+ true
+ Filth rate
+
+
+
+ Numbers_Meditation
+ Numbers.PawnColumnWorker_Meditation
+ true
+ Focus
+
+
+
+
+
+
+ Numbers_Areas
+ Numbers.PawnColumnWorker_AllowedAreaWithMassAssign
+ true
+ allowed area
+ 350
+
+
+
diff --git a/1.1/Defs/PawnColumnOptionDef/Numbers_PawnColumnOptionDef.xml b/1.1/Defs/PawnColumnOptionDef/Numbers_PawnColumnOptionDef.xml
new file mode 100644
index 0000000..c1439f5
--- /dev/null
+++ b/1.1/Defs/PawnColumnOptionDef/Numbers_PawnColumnOptionDef.xml
@@ -0,0 +1,85 @@
+
+
+
+
+ EquipmentBearers
+
+ Numbers_Equipment
+
+
+
+
+ LivingThings
+
+ Numbers_Age
+ Numbers_MentalState
+ Numbers_JobCurrent
+ Numbers_JobQueued
+ Numbers_HediffList
+ Numbers_InventoryDropAll
+
+
+
+
+ Prisoners
+
+ Numbers_PrisonerInteraction
+ Numbers_PrisonerRecruitmentDifficulty
+ Numbers_PrisonerResistance
+ FoodRestriction
+ Numbers_Inventory
+
+
+
+
+ Animals
+
+ Numbers_Milkfullness
+ Numbers_AnimalWoolGrowth
+ Numbers_AnimalEggProgress
+ Numbers_Wildness
+ Numbers_TameChance
+ Numbers_Inventory
+ Numbers_LeatherType
+ Numbers_AnimalFilthRate
+ Numbers_Areas
+
+
+
+
+
+ MainTable
+
+ Numbers_Inspiration
+ Numbers_Inventory
+ Numbers_SelfTend
+ Numbers_Meditation
+ Numbers_Psyfocus
+ Numbers_Entropy
+ Numbers_PsylinkLevel
+ Numbers_Areas
+
+
+
+
+
+ WildAnimals
+
+ Numbers_Wildness
+ Numbers_TameChance
+ Numbers_LeatherType
+ Numbers_ManhunterOnTameFailChance
+
+
+
+
+
+ DeadThings
+
+ Numbers_Forbidden
+ Numbers_LeatherType
+
+
+
+
+
\ No newline at end of file
diff --git a/1.1/Defs/PawnTableDef/Numbers_PawnTableDef.xml b/1.1/Defs/PawnTableDef/Numbers_PawnTableDef.xml
new file mode 100644
index 0000000..b88de36
--- /dev/null
+++ b/1.1/Defs/PawnTableDef/Numbers_PawnTableDef.xml
@@ -0,0 +1,277 @@
+
+
+
+
+ Numbers_MainTable
+ Colonists
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Numbers_Backstory
+ Numbers_RimWorld_StatDef_AimingDelayFactor
+ Numbers_RimWorld_RecordDef_Kills
+ Numbers_RimWorld_StatDef_ShootingAccuracyPawn
+ Numbers_Equipment
+ Numbers_RimWorld_NeedDef_Mood
+ Numbers_RimWorld_SkillDef_Construction
+ MedicalCare
+ Numbers_DiseaseProgress
+ WorkPriority_Patient
+ Numbers_Operations
+ Numbers_Inspiration
+
+
+
+ true
+
+
+ 1148
+
+
+
+ Numbers_Enemies
+ Enemies
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Numbers_RimWorld_StatDef_AimingDelayFactor
+ Numbers_Equipment
+ Numbers_Pain
+ Numbers_RimWorld_StatDef_MeleeDPS
+ Numbers_RimWorld_StatDef_ShootingAccuracyPawn
+ Numbers_RimWorld_StatDef_MoveSpeed
+
+
+
+ true
+
+
+ 1148
+
+
+
+ Numbers_Prisoners
+ Prisoners
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Numbers_Equipment
+ Numbers_RimWorld_StatDef_MarketValue
+ Numbers_PrisonerInteraction
+ Numbers_PrisonerRecruitmentDifficulty
+ Numbers_PrisonerResistance
+ Numbers_MentalState
+ MedicalCare
+ FoodRestriction
+
+
+
+ true
+
+
+
+
+
+ Numbers_Corpses
+ Corpses
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Numbers_Equipment
+ Numbers_Forbidden
+ Numbers_RimWorld_StatDef_FoodPoisonChanceFixedHuman
+ Numbers_RimWorld_StatDef_MeatAmount
+ Numbers_RimWorld_StatDef_LeatherAmount
+
+
+
+ true
+ true
+
+
+ 700
+
+
+
+ Numbers_Guests
+ Guests
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Numbers_Equipment
+
+
+
+
+ true
+
+
+
+
+
+ Numbers_Animals
+ Animals
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Gender
+ LifeStage
+ Pregnant
+ GapTiny
+ GapTiny
+ FollowDrafted
+ FollowFieldwork
+ GapTiny
+ Master
+ Bond
+ Slaughter
+ Numbers_AnimalFilthRate
+ MedicalCare
+ GapTiny
+ Numbers_Areas
+
+
+
+ true
+
+
+
+
+
+ Numbers_WildAnimals
+ Wild animals
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Gender
+ LifeStage
+ GapTiny
+ GapTiny
+ Hunt
+ Tame
+ GapTiny
+ ManhunterOnDamageChance
+ Numbers_ManhunterOnTameFailChance
+ Numbers_Wildness
+ Numbers_TameChance
+ Predator
+ Info
+ Numbers_RimWorld_StatDef_MeatAmount
+ Numbers_RimWorld_StatDef_LeatherAmount
+ Numbers_LeatherType
+ Numbers_RimWorld_RecordDef_Kills
+
+
+
+ true
+
+
+
+
+
+ Numbers_AnimalCorpses
+ Animal corpses
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Numbers_Forbidden
+ Numbers_RimWorld_StatDef_FoodPoisonChanceFixedHuman
+ Numbers_RimWorld_StatDef_MeatAmount
+ Numbers_RimWorld_StatDef_LeatherAmount
+ Numbers_LeatherType
+
+
+
+ true
+ true
+
+
+ 700
+
+
+
+ Numbers_CombatPreset
+ combat
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Numbers_RimWorld_StatDef_AimingDelayFactor
+ Numbers_Equipment
+ Numbers_RimWorld_StatDef_ShootingAccuracyPawn
+ Numbers_RimWorld_RecordDef_Kills
+ Numbers_RimWorld_StatDef_MoveSpeed
+ Numbers_RimWorld_StatDef_MeleeDPS
+ Numbers_RimWorld_NeedDef_Food
+ Numbers_RimWorld_NeedDef_Mood
+ Numbers_Bleedrate
+ Numbers_Pain
+ Numbers_JobCurrent
+
+
+
+ true
+
+
+
+
+
+ Numbers_WorkTabPlusPreset
+ WorkTab plus
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Numbers_RimWorld_StatDef_WorkSpeedGlobal
+ WorkPriority_Patient
+ Numbers_NeedsTreatment
+ WorkPriority_Doctor
+ Numbers_RimWorld_StatDef_MedicalTendQuality
+ WorkPriority_PatientBedRest
+ Numbers_DiseaseProgress
+ WorkPriority_Warden
+ Numbers_RimWorld_StatDef_NegotiationAbility
+ WorkPriority_Handling
+ Numbers_RimWorld_StatDef_TameAnimalChance
+ WorkPriority_Cooking
+ Numbers_RimWorld_StatDef_FoodPoisonChance
+ WorkPriority_Hunting
+ Numbers_RimWorld_StatDef_HuntingStealth
+ WorkPriority_Construction
+ Numbers_RimWorld_StatDef_ConstructSuccessChance
+ WorkPriority_Growing
+ Numbers_RimWorld_StatDef_PlantWorkSpeed
+ WorkPriority_Mining
+ Numbers_RimWorld_StatDef_MiningSpeed
+ WorkPriority_PlantCutting
+ Numbers_RimWorld_StatDef_PlantHarvestYield
+ WorkPriority_Research
+ Numbers_RimWorld_StatDef_ResearchSpeed
+ Numbers_RimWorld_StatDef_GeneralLaborSpeed
+
+
+
+ true
+
+
+
+
+
+ Numbers_ColonistNeedsPreset
+ colonist needs
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Numbers_RimWorld_NeedDef_Mood
+ Numbers_RimWorld_NeedDef_Food
+ Numbers_RimWorld_NeedDef_Rest
+ Numbers_RimWorld_NeedDef_Beauty
+ Numbers_RimWorld_NeedDef_Comfort
+ Numbers_RimWorld_NeedDef_Outdoors
+
+
+
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/1.3/Assemblies/Numbers.dll b/1.3/Assemblies/Numbers.dll
new file mode 100644
index 0000000..c836a06
Binary files /dev/null and b/1.3/Assemblies/Numbers.dll differ
diff --git a/1.3/Defs/MainTabDefs/kNumbers.xml b/1.3/Defs/MainTabDefs/kNumbers.xml
new file mode 100644
index 0000000..5809ee8
--- /dev/null
+++ b/1.3/Defs/MainTabDefs/kNumbers.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ kNumbersOverviewTab
+ Numbers
+ See every possible stat on every colonist or prisoner in a single table.
+ Numbers.MainTabWindow_Numbers
+ 20
+ Z
+
+
+
\ No newline at end of file
diff --git a/1.3/Defs/PawnColumnDef/PawnColumns_Numbers.xml b/1.3/Defs/PawnColumnDef/PawnColumns_Numbers.xml
new file mode 100644
index 0000000..0558e23
--- /dev/null
+++ b/1.3/Defs/PawnColumnDef/PawnColumns_Numbers.xml
@@ -0,0 +1,286 @@
+
+
+
+
+
+
+ Numbers_Age
+ Numbers.PawnColumnWorker_Age
+ true
+ Age
+
+
+
+ Numbers_AnimalEggProgress
+ Numbers.PawnColumnWorker_AnimalEggProgress
+ true
+ Egg progress
+
+
+
+ Numbers_Milkfullness
+ Numbers.PawnColumnWorker_AnimalMilkFullness
+ true
+ Milk fullness
+
+
+
+ Numbers_AnimalWoolGrowth
+ Numbers.PawnColumnWorker_AnimalWoolGrowth
+ true
+ Wool growth
+
+
+
+ Numbers_LeatherType
+ Numbers.PawnColumnWorker_LeatherType
+ true
+ Leather type
+
+
+
+ Numbers_Race
+ Numbers.PawnColumnWorker_Race
+ true
+ Race
+
+
+
+ Numbers_Backstory
+ Numbers.PawnColumnWorker_Backstory
+ true
+ Title
+
+
+
+ Numbers_Psyfocus
+ Numbers.PawnColumnWorker_Psyfocus
+ true
+ Psyfocus
+
+
+
+
+
+
+ Numbers_Entropy
+ Numbers.PawnColumnWorker_Entropy
+ true
+ Heat
+
+
+
+
+
+
+ Numbers_PsylinkLevel
+ Numbers.PawnColumnWorker_PsylinkLevel
+ true
+ Psylevel
+
+
+
+
+
+
+ Numbers_Equipment
+ Numbers.PawnColumnWorker_Equipment
+ true
+ Equipment
+
+
+
+ Numbers_Inventory
+ Numbers.PawnColumnWorker_Inventory
+ true
+ Inventory
+
+
+
+ Numbers_InventoryDropAll
+ Numbers.PawnColumnWorker_InventoryDropAll
+ true
+ Drop all inventory
+
+
+
+ Numbers_JobCurrent
+ Numbers.PawnColumnWorker_JobCurrent
+ true
+ Current job
+
+
+
+ Numbers_JobQueued
+ Numbers.PawnColumnWorker_JobQueued
+ true
+ Queued job
+
+
+
+ Numbers_MentalState
+ Numbers.PawnColumnWorker_MentalState
+ true
+ Mental state
+
+
+
+ Numbers_PrisonerInteraction
+ Numbers.PawnColumnWorker_PrisonerInteraction
+ true
+ true
+ Prisoner interaction
+
+
+
+ Numbers_PrisonerResistance
+ Numbers.PawnColumnWorker_PrisonerResistance
+ true
+ Resistance remaining
+
+
+
+ Numbers_Pain
+ Numbers.PawnColumnWorker_Pain
+ true
+ Pain
+
+
+
+ Numbers_Bleedrate
+ Numbers.PawnColumnWorker_Bleedrate
+ true
+ Bleedrate
+
+
+
+ Numbers_NeedsTreatment
+ Numbers.PawnColumnWorker_NeedsTreatment
+ true
+ Needs treatment
+
+
+
+ Numbers_Operations
+ Numbers.PawnColumnWorker_OperationDropDown
+ true
+ Operations
+
+
+
+ Numbers_Forbidden
+ Numbers.PawnColumnWorker_Forbidden
+ true
+ Forbidden
+ true
+
+
+
+ Numbers_Inspiration
+ Numbers.PawnColumnWorker_Inspiration
+ true
+ Inspiration
+
+
+
+ Numbers_DiseaseProgress
+ Numbers.PawnColumnWorker_DiseaseProgression
+ true
+ Disease immunity margin
+ Disease immunity margin
+ 80
+
+
+
+ Numbers_ManhunterOnTameFailChance
+ Numbers.PawnColumnWorker_ManhunterOnTameFailChance
+ true
+ Revenge chance on tame fail
+ UI/Icons/Animal/Predator
+ 45
+
+
+
+ Numbers_Wildness
+ Numbers.PawnColumnWorker_AnimalWildness
+ true
+ Wildness
+
+
+
+ Numbers_TameChance
+ Numbers.PawnColumnWorker_TameChance
+ true
+ Tame chance
+
+
+
+ Numbers_SelfTend
+ Numbers.PawnColumnWorker_SelfTend
+ true
+ true
+ Self-tend
+ UI/Icons/Trainables/Rescue
+ 45
+
+
+
+ Numbers_HediffList
+ Numbers.PawnColumnWorker_AllHediffs
+ true
+ UI/Buttons/DevRoot/ToggleTweak
+ Hediffs
+ 45
+
+
+
+ Numbers_HediffBadList
+ Numbers.PawnColumnWorker_HediffIsBad
+ true
+ UI/Buttons/DevRoot/ToggleTweak
+ Bad hediffs
+ 45
+
+
+
+ Numbers_Faction
+ Numbers.PawnColumnWorker_Faction
+ true
+ Faction
+
+
+
+ Numbers_AnimalFilthRate
+ Numbers.PawnColumnWorker_AnimalFilthRate
+ true
+ Filth rate
+
+
+
+ Numbers_Meditation
+ Numbers.PawnColumnWorker_Meditation
+ true
+ Focus
+
+
+
+
+
+
+ Numbers_Areas
+ Numbers.PawnColumnWorker_AllowedAreaWithMassAssign
+ true
+ allowed area
+ 350
+
+
+
+ Numbers_Traits
+ Numbers.PawnColumnWorker_Trait
+ true
+ Traits
+ Traits
+ 350
+
+
+
diff --git a/1.3/Defs/PawnColumnOptionDef/Numbers_PawnColumnOptionDef.xml b/1.3/Defs/PawnColumnOptionDef/Numbers_PawnColumnOptionDef.xml
new file mode 100644
index 0000000..6ecca42
--- /dev/null
+++ b/1.3/Defs/PawnColumnOptionDef/Numbers_PawnColumnOptionDef.xml
@@ -0,0 +1,86 @@
+
+
+
+
+ EquipmentBearers
+
+ Numbers_Equipment
+ Numbers_Meditation
+ Numbers_Traits
+
+
+
+
+ LivingThings
+
+ Numbers_Age
+ Numbers_MentalState
+ Numbers_JobCurrent
+ Numbers_JobQueued
+ Numbers_HediffList
+ Numbers_HediffBadList
+ Numbers_InventoryDropAll
+
+
+
+
+ Prisoners
+
+ Numbers_PrisonerInteraction
+ Numbers_PrisonerResistance
+ FoodRestriction
+ Numbers_Inventory
+
+
+
+
+ Animals
+
+ Numbers_Milkfullness
+ Numbers_AnimalWoolGrowth
+ Numbers_AnimalEggProgress
+ Numbers_Wildness
+ Numbers_TameChance
+ Numbers_Inventory
+ Numbers_LeatherType
+ Numbers_AnimalFilthRate
+ Numbers_Areas
+
+
+
+
+
+ MainTable
+
+ Numbers_Inspiration
+ Numbers_Inventory
+ Numbers_SelfTend
+ Numbers_Psyfocus
+ Numbers_Entropy
+ Numbers_PsylinkLevel
+ Numbers_Areas
+
+
+
+
+
+ WildAnimals
+
+ Numbers_Wildness
+ Numbers_TameChance
+ Numbers_LeatherType
+ Numbers_ManhunterOnTameFailChance
+
+
+
+
+
+ DeadThings
+
+ Numbers_Forbidden
+ Numbers_LeatherType
+
+
+
+
+
\ No newline at end of file
diff --git a/1.3/Defs/PawnTableDef/Numbers_PawnTableDef.xml b/1.3/Defs/PawnTableDef/Numbers_PawnTableDef.xml
new file mode 100644
index 0000000..189d6cf
--- /dev/null
+++ b/1.3/Defs/PawnTableDef/Numbers_PawnTableDef.xml
@@ -0,0 +1,276 @@
+
+
+
+
+ Numbers_MainTable
+ Colonists
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Numbers_Backstory
+ Numbers_RimWorld_StatDef_AimingDelayFactor
+ Numbers_RimWorld_RecordDef_Kills
+ Numbers_RimWorld_StatDef_ShootingAccuracyPawn
+ Numbers_Equipment
+ Numbers_RimWorld_NeedDef_Mood
+ Numbers_RimWorld_SkillDef_Construction
+ MedicalCare
+ Numbers_DiseaseProgress
+ WorkPriority_Patient
+ Numbers_Operations
+ Numbers_Inspiration
+
+
+
+ true
+
+
+ 1148
+
+
+
+ Numbers_Enemies
+ Enemies
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Numbers_RimWorld_StatDef_AimingDelayFactor
+ Numbers_Equipment
+ Numbers_Pain
+ Numbers_RimWorld_StatDef_MeleeDPS
+ Numbers_RimWorld_StatDef_ShootingAccuracyPawn
+ Numbers_RimWorld_StatDef_MoveSpeed
+
+
+
+ true
+
+
+ 1148
+
+
+
+ Numbers_Prisoners
+ Prisoners
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Numbers_Equipment
+ Numbers_RimWorld_StatDef_MarketValue
+ Numbers_PrisonerInteraction
+ Numbers_PrisonerResistance
+ Numbers_MentalState
+ MedicalCare
+ FoodRestriction
+
+
+
+ true
+
+
+
+
+
+ Numbers_Corpses
+ Corpses
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Numbers_Equipment
+ Numbers_Forbidden
+ Numbers_RimWorld_StatDef_FoodPoisonChanceFixedHuman
+ Numbers_RimWorld_StatDef_MeatAmount
+ Numbers_RimWorld_StatDef_LeatherAmount
+
+
+
+ true
+ true
+
+
+ 700
+
+
+
+ Numbers_Guests
+ Guests
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Numbers_Equipment
+
+
+
+
+ true
+
+
+
+
+
+ Numbers_Animals
+ Animals
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Gender
+ LifeStage
+ Pregnant
+ GapTiny
+ GapTiny
+ FollowDrafted
+ FollowFieldwork
+ GapTiny
+ Master
+ Bond
+ Slaughter
+ Numbers_AnimalFilthRate
+ MedicalCare
+ GapTiny
+ Numbers_Areas
+
+
+
+ true
+
+
+
+
+
+ Numbers_WildAnimals
+ Wild animals
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Gender
+ LifeStage
+ GapTiny
+ GapTiny
+ Hunt
+ Tame
+ GapTiny
+ ManhunterOnDamageChance
+ Numbers_ManhunterOnTameFailChance
+ Numbers_Wildness
+ Numbers_TameChance
+ Predator
+ Info
+ Numbers_RimWorld_StatDef_MeatAmount
+ Numbers_RimWorld_StatDef_LeatherAmount
+ Numbers_LeatherType
+ Numbers_RimWorld_RecordDef_Kills
+
+
+
+ true
+
+
+
+
+
+ Numbers_AnimalCorpses
+ Animal corpses
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Numbers_Forbidden
+ Numbers_RimWorld_StatDef_FoodPoisonChanceFixedHuman
+ Numbers_RimWorld_StatDef_MeatAmount
+ Numbers_RimWorld_StatDef_LeatherAmount
+ Numbers_LeatherType
+
+
+
+ true
+ true
+
+
+ 700
+
+
+
+ Numbers_CombatPreset
+ combat
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Numbers_RimWorld_StatDef_AimingDelayFactor
+ Numbers_Equipment
+ Numbers_RimWorld_StatDef_ShootingAccuracyPawn
+ Numbers_RimWorld_RecordDef_Kills
+ Numbers_RimWorld_StatDef_MoveSpeed
+ Numbers_RimWorld_StatDef_MeleeDPS
+ Numbers_RimWorld_NeedDef_Food
+ Numbers_RimWorld_NeedDef_Mood
+ Numbers_Bleedrate
+ Numbers_Pain
+ Numbers_JobCurrent
+
+
+
+ true
+
+
+
+
+
+ Numbers_WorkTabPlusPreset
+ WorkTab plus
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Numbers_RimWorld_StatDef_WorkSpeedGlobal
+ WorkPriority_Patient
+ Numbers_NeedsTreatment
+ WorkPriority_Doctor
+ Numbers_RimWorld_StatDef_MedicalTendQuality
+ WorkPriority_PatientBedRest
+ Numbers_DiseaseProgress
+ WorkPriority_Warden
+ Numbers_RimWorld_StatDef_NegotiationAbility
+ WorkPriority_Handling
+ Numbers_RimWorld_StatDef_TameAnimalChance
+ WorkPriority_Cooking
+ Numbers_RimWorld_StatDef_FoodPoisonChance
+ WorkPriority_Hunting
+ Numbers_RimWorld_StatDef_HuntingStealth
+ WorkPriority_Construction
+ Numbers_RimWorld_StatDef_ConstructSuccessChance
+ WorkPriority_Growing
+ Numbers_RimWorld_StatDef_PlantWorkSpeed
+ WorkPriority_Mining
+ Numbers_RimWorld_StatDef_MiningSpeed
+ WorkPriority_PlantCutting
+ Numbers_RimWorld_StatDef_PlantHarvestYield
+ WorkPriority_Research
+ Numbers_RimWorld_StatDef_ResearchSpeed
+ Numbers_RimWorld_StatDef_GeneralLaborSpeed
+
+
+
+ true
+
+
+
+
+
+ Numbers_ColonistNeedsPreset
+ colonist needs
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Numbers_RimWorld_NeedDef_Mood
+ Numbers_RimWorld_NeedDef_Food
+ Numbers_RimWorld_NeedDef_Rest
+ Numbers_RimWorld_NeedDef_Beauty
+ Numbers_RimWorld_NeedDef_Comfort
+ Numbers_RimWorld_NeedDef_Outdoors
+
+
+
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/1.4/Assemblies/0Harmony.dll b/1.4/Assemblies/0Harmony.dll
new file mode 100644
index 0000000..e182535
Binary files /dev/null and b/1.4/Assemblies/0Harmony.dll differ
diff --git a/1.4/Assemblies/Numbers.dll b/1.4/Assemblies/Numbers.dll
new file mode 100644
index 0000000..2d5a821
Binary files /dev/null and b/1.4/Assemblies/Numbers.dll differ
diff --git a/1.4/Defs/MainTabDefs/kNumbers.xml b/1.4/Defs/MainTabDefs/kNumbers.xml
new file mode 100644
index 0000000..5809ee8
--- /dev/null
+++ b/1.4/Defs/MainTabDefs/kNumbers.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ kNumbersOverviewTab
+ Numbers
+ See every possible stat on every colonist or prisoner in a single table.
+ Numbers.MainTabWindow_Numbers
+ 20
+ Z
+
+
+
\ No newline at end of file
diff --git a/1.4/Defs/PawnColumnDef/PawnColumns_Numbers.xml b/1.4/Defs/PawnColumnDef/PawnColumns_Numbers.xml
new file mode 100644
index 0000000..8b6da03
--- /dev/null
+++ b/1.4/Defs/PawnColumnDef/PawnColumns_Numbers.xml
@@ -0,0 +1,361 @@
+
+
+
+
+
+
+ Numbers_Age
+ Numbers.PawnColumnWorker_Age
+ true
+ Age
+
+
+
+ Numbers_AnimalEggProgress
+ Numbers.PawnColumnWorker_AnimalEggProgress
+ true
+ Egg progress
+
+
+
+ Numbers_Milkfullness
+ Numbers.PawnColumnWorker_AnimalMilkFullness
+ true
+ Milk fullness
+
+
+
+ Numbers_AnimalWoolGrowth
+ Numbers.PawnColumnWorker_AnimalWoolGrowth
+ true
+ Wool growth
+
+
+
+ Numbers_LeatherType
+ Numbers.PawnColumnWorker_LeatherType
+ true
+ Leather type
+
+
+
+ Numbers_Race
+ Numbers.PawnColumnWorker_Race
+ true
+ Race
+
+
+
+ Numbers_Backstory
+ Numbers.PawnColumnWorker_Backstory
+ true
+ Title
+
+
+
+ Numbers_Psyfocus
+ Numbers.PawnColumnWorker_Psyfocus
+ true
+ Psyfocus
+
+
+
+
+
+
+ Numbers_Entropy
+ Numbers.PawnColumnWorker_Entropy
+ true
+ Heat
+
+
+
+
+
+
+ Numbers_PsylinkLevel
+ Numbers.PawnColumnWorker_PsylinkLevel
+ true
+ Psylevel
+
+
+
+
+
+
+ Numbers_Equipment
+ Numbers.PawnColumnWorker_Equipment
+ true
+ Equipment
+
+
+
+ Numbers_Inventory
+ Numbers.PawnColumnWorker_Inventory
+ true
+ Inventory
+
+
+
+ Numbers_InventoryDropAll
+ Numbers.PawnColumnWorker_InventoryDropAll
+ true
+ Drop all inventory
+
+
+
+ Numbers_JobCurrent
+ Numbers.PawnColumnWorker_JobCurrent
+ true
+ Current job
+
+
+
+ Numbers_JobQueued
+ Numbers.PawnColumnWorker_JobQueued
+ true
+ Queued job
+
+
+
+ Numbers_MentalState
+ Numbers.PawnColumnWorker_MentalState
+ true
+ Mental state
+
+
+
+ Numbers_PrisonerInteraction
+ Numbers.PawnColumnWorker_PrisonerInteraction
+ true
+ true
+ Prisoner interaction
+
+
+
+ Numbers_PrisonerResistance
+ Numbers.PawnColumnWorker_PrisonerResistance
+ true
+ Resistance remaining
+
+
+
+ Numbers_Pain
+ Numbers.PawnColumnWorker_Pain
+ true
+ Pain
+
+
+
+ Numbers_Bleedrate
+ Numbers.PawnColumnWorker_Bleedrate
+ true
+ Bleedrate
+
+
+
+ Numbers_NeedsTreatment
+ Numbers.PawnColumnWorker_NeedsTreatment
+ true
+ Needs treatment
+
+
+
+ Numbers_Operations
+ Numbers.PawnColumnWorker_OperationDropDown
+ true
+ Operations
+
+
+
+ Numbers_Forbidden
+ Numbers.PawnColumnWorker_Forbidden
+ true
+ Forbidden
+ true
+
+
+
+ Numbers_Inspiration
+ Numbers.PawnColumnWorker_Inspiration
+ true
+ Inspiration
+
+
+
+ Numbers_DiseaseProgress
+ Numbers.PawnColumnWorker_DiseaseProgression
+ true
+ Disease immunity margin
+ Disease immunity margin
+ 80
+
+
+
+ Numbers_Wildness
+ Numbers.PawnColumnWorker_AnimalWildness
+ true
+ Wildness
+
+
+
+ Numbers_TameChance
+ Numbers.PawnColumnWorker_TameChance
+ true
+ Tame chance
+
+
+
+ Numbers_SelfTend
+ Numbers.PawnColumnWorker_SelfTend
+ true
+ true
+ Self-tend
+ UI/Icons/Trainables/Rescue
+ 45
+
+
+
+ Numbers_HediffList
+ Numbers.PawnColumnWorker_AllHediffs
+ true
+ UI/Buttons/DevRoot/ToggleTweak
+ Hediffs
+ 45
+
+
+
+ Numbers_HediffBadList
+ Numbers.PawnColumnWorker_HediffIsBad
+ true
+ UI/Buttons/DevRoot/ToggleTweak
+ Bad hediffs
+ 45
+
+
+
+ Numbers_Faction
+ Numbers.PawnColumnWorker_Faction
+ true
+ Faction
+
+
+
+ Numbers_AnimalFilthRate
+ Numbers.PawnColumnWorker_AnimalFilthRate
+ true
+ Filth rate
+
+
+
+ Numbers_Meditation
+ Numbers.PawnColumnWorker_Meditation
+ true
+ Focus
+
+
+
+
+
+
+ Numbers_Areas
+ Numbers.PawnColumnWorker_AllowedAreaWithMassAssign
+ true
+ allowed area
+ 350
+
+
+
+ Numbers_Traits
+ Numbers.PawnColumnWorker_Trait
+ true
+ Traits
+ Traits
+ 350
+
+
+
+ Numbers_Bandwidth
+ Numbers.PawnColumnWorker_Bandwidth
+ true
+ Bandwidth
+ Bandwidth
+
+
+
+ 200
+
+
+
+ Numbers_Endogenes
+ Numbers.PawnColumnWorker_Endogenes
+ true
+ Germline genes
+ Germline genes
+
+
+
+ 350
+
+
+
+ Numbers_Xenogenes
+ Numbers.PawnColumnWorker_Xenogenes
+ true
+ Xenogenes
+ Xenogenes
+
+
+
+ 350
+
+
+
+ Numbers_GenesRegrowTime
+ Numbers.PawnColumnWorker_GenesRegrowTime
+ true
+ Genes Regrowing
+ Genes Regrowing
+
+
+
+ 80
+
+
+
+ Numbers_Guilt
+ Numbers.PawnColumnWorker_Guilt
+ true
+ Guilt
+ Guilt
+ 50
+
+
+
+ Numbers_Ideology
+ Numbers.PawnColumnWorker_Ideology
+ true
+ Ideology and certainty
+ Ideology
+
+
+
+ 200
+
+
+
+ Numbers_Strip
+ Numbers.PawnColumnWorker_Strip
+ Strip
+ Strip
+ 50
+
+
+
+ Numbers_Recruitable
+ Numbers.PawnColumnWorker_Recruitable
+ Unwaveringly loyal
+ 50
+
+
+
\ No newline at end of file
diff --git a/1.4/Defs/PawnColumnOptionDef/Numbers_PawnColumnOptionDef.xml b/1.4/Defs/PawnColumnOptionDef/Numbers_PawnColumnOptionDef.xml
new file mode 100644
index 0000000..6addce9
--- /dev/null
+++ b/1.4/Defs/PawnColumnOptionDef/Numbers_PawnColumnOptionDef.xml
@@ -0,0 +1,93 @@
+
+
+
+
+ EquipmentBearers
+
+ Numbers_Equipment
+ Numbers_Meditation
+ Numbers_Traits
+ Numbers_Strip
+
+
+
+
+ LivingThings
+
+ Numbers_Age
+ Numbers_MentalState
+ Numbers_JobCurrent
+ Numbers_JobQueued
+ Numbers_HediffList
+ Numbers_HediffBadList
+ Numbers_InventoryDropAll
+ Numbers_Ideology
+ Numbers_Xenogenes
+ Numbers_Endogenes
+ Numbers_GenesRegrowTime
+
+
+
+
+ Prisoners
+
+ Numbers_PrisonerInteraction
+ Numbers_PrisonerResistance
+ FoodRestriction
+ Numbers_Inventory
+ Numbers_Guilt
+ Numbers_Recruitable
+
+
+
+
+ Animals
+
+ Numbers_Milkfullness
+ Numbers_AnimalWoolGrowth
+ Numbers_AnimalEggProgress
+ Numbers_Wildness
+ Numbers_TameChance
+ Numbers_Inventory
+ Numbers_LeatherType
+ Numbers_AnimalFilthRate
+ Numbers_Areas
+
+
+
+
+
+ MainTable
+
+ Numbers_Inspiration
+ Numbers_Inventory
+ Numbers_SelfTend
+ Numbers_Psyfocus
+ Numbers_Entropy
+ Numbers_PsylinkLevel
+ Numbers_Areas
+ Numbers_Bandwidth
+
+
+
+
+
+ WildAnimals
+
+ Numbers_Wildness
+ Numbers_TameChance
+ Numbers_LeatherType
+
+
+
+
+
+ DeadThings
+
+ Numbers_Forbidden
+ Numbers_LeatherType
+
+
+
+
+
\ No newline at end of file
diff --git a/1.4/Defs/PawnTableDef/Numbers_PawnTableDef.xml b/1.4/Defs/PawnTableDef/Numbers_PawnTableDef.xml
new file mode 100644
index 0000000..5402ce4
--- /dev/null
+++ b/1.4/Defs/PawnTableDef/Numbers_PawnTableDef.xml
@@ -0,0 +1,277 @@
+
+
+
+
+ Numbers_MainTable
+ Colonists
+ Numbers.PawnTable_NumbersMain
+
+ LabelShortWithIcon
+ Numbers_Backstory
+ Numbers_RimWorld_StatDef_AimingDelayFactor
+ Numbers_RimWorld_RecordDef_Kills
+ Numbers_RimWorld_StatDef_ShootingAccuracyPawn
+ Numbers_Equipment
+ Numbers_RimWorld_NeedDef_Mood
+ Numbers_RimWorld_SkillDef_Construction
+ MedicalCare
+ Numbers_DiseaseProgress
+ WorkPriority_Patient
+ Numbers_Operations
+ Numbers_Inspiration
+
+
+
+ true
+
+
+ 1148
+
+
+
+ Numbers_Enemies
+ Enemies
+ Numbers.PawnTable_NumbersMain
+
+ LabelShortWithIcon
+ Numbers_RimWorld_StatDef_AimingDelayFactor
+ Numbers_Equipment
+ Numbers_Pain
+ Numbers_RimWorld_StatDef_MeleeDPS
+ Numbers_RimWorld_StatDef_ShootingAccuracyPawn
+ Numbers_RimWorld_StatDef_MoveSpeed
+
+
+
+ true
+
+
+ 1148
+
+
+
+ Numbers_Prisoners
+ Prisoners
+ Numbers.PawnTable_NumbersMain
+
+ LabelShortWithIcon
+ Numbers_Equipment
+ Numbers_RimWorld_StatDef_MarketValue
+ Numbers_PrisonerInteraction
+ Numbers_PrisonerResistance
+ Numbers_Recruitable
+ Numbers_MentalState
+ MedicalCare
+ FoodRestriction
+
+
+
+ true
+
+
+
+
+
+ Numbers_Corpses
+ Corpses
+ Numbers.PawnTable_NumbersMain
+
+ LabelShortWithIcon
+ Numbers_Equipment
+ Numbers_Forbidden
+ Numbers_RimWorld_StatDef_FoodPoisonChanceFixedHuman
+ Numbers_RimWorld_StatDef_MeatAmount
+ Numbers_RimWorld_StatDef_LeatherAmount
+
+
+
+ true
+ true
+
+
+ 700
+
+
+
+ Numbers_Guests
+ Guests
+ Numbers.PawnTable_NumbersMain
+
+ LabelShortWithIcon
+ Numbers_Equipment
+
+
+
+
+ true
+
+
+
+
+
+ Numbers_Animals
+ Animals
+ Numbers.PawnTable_NumbersMain
+
+ LabelWithIcon
+ Gender
+ LifeStage
+ Pregnant
+ GapTiny
+ GapTiny
+ FollowDrafted
+ FollowFieldwork
+ GapTiny
+ Master
+ Bond
+ Slaughter
+ Numbers_AnimalFilthRate
+ MedicalCare
+ GapTiny
+ Numbers_Areas
+
+
+
+ true
+
+
+
+
+
+ Numbers_WildAnimals
+ Wild animals
+ Numbers.PawnTable_NumbersMain
+
+ LabelWithIcon
+ Gender
+ LifeStage
+ GapTiny
+ GapTiny
+ Hunt
+ Tame
+ GapTiny
+ ManhunterOnDamageChance
+ ManhunterOnTameFailChance
+ Numbers_Wildness
+ Numbers_TameChance
+ Predator
+ Info
+ Numbers_RimWorld_StatDef_MeatAmount
+ Numbers_RimWorld_StatDef_LeatherAmount
+ Numbers_LeatherType
+ Numbers_RimWorld_RecordDef_Kills
+
+
+
+ true
+
+
+
+
+
+ Numbers_AnimalCorpses
+ Animal corpses
+ Numbers.PawnTable_NumbersMain
+
+ LabelWithIcon
+ Numbers_Forbidden
+ Numbers_RimWorld_StatDef_FoodPoisonChanceFixedHuman
+ Numbers_RimWorld_StatDef_MeatAmount
+ Numbers_RimWorld_StatDef_LeatherAmount
+ Numbers_LeatherType
+
+
+
+ true
+ true
+
+
+ 700
+
+
+
+ Numbers_CombatPreset
+ combat
+ Numbers.PawnTable_NumbersMain
+
+ LabelShortWithIcon
+ Numbers_RimWorld_StatDef_AimingDelayFactor
+ Numbers_Equipment
+ Numbers_RimWorld_StatDef_ShootingAccuracyPawn
+ Numbers_RimWorld_RecordDef_Kills
+ Numbers_RimWorld_StatDef_MoveSpeed
+ Numbers_RimWorld_StatDef_MeleeDPS
+ Numbers_RimWorld_NeedDef_Food
+ Numbers_RimWorld_NeedDef_Mood
+ Numbers_Bleedrate
+ Numbers_Pain
+ Numbers_JobCurrent
+
+
+
+ true
+
+
+
+
+
+ Numbers_WorkTabPlusPreset
+ WorkTab plus
+ Numbers.PawnTable_NumbersMain
+
+ LabelShortWithIcon
+ Numbers_RimWorld_StatDef_WorkSpeedGlobal
+ WorkPriority_Patient
+ Numbers_NeedsTreatment
+ WorkPriority_Doctor
+ Numbers_RimWorld_StatDef_MedicalTendQuality
+ WorkPriority_PatientBedRest
+ Numbers_DiseaseProgress
+ WorkPriority_Warden
+ Numbers_RimWorld_StatDef_NegotiationAbility
+ WorkPriority_Handling
+ Numbers_RimWorld_StatDef_TameAnimalChance
+ WorkPriority_Cooking
+ Numbers_RimWorld_StatDef_FoodPoisonChance
+ WorkPriority_Hunting
+ Numbers_RimWorld_StatDef_HuntingStealth
+ WorkPriority_Construction
+ Numbers_RimWorld_StatDef_ConstructSuccessChance
+ WorkPriority_Growing
+ Numbers_RimWorld_StatDef_PlantWorkSpeed
+ WorkPriority_Mining
+ Numbers_RimWorld_StatDef_MiningSpeed
+ WorkPriority_PlantCutting
+ Numbers_RimWorld_StatDef_PlantHarvestYield
+ WorkPriority_Research
+ Numbers_RimWorld_StatDef_ResearchSpeed
+ Numbers_RimWorld_StatDef_GeneralLaborSpeed
+
+
+
+ true
+
+
+
+
+
+ Numbers_ColonistNeedsPreset
+ colonist needs
+ Numbers.PawnTable_NumbersMain
+
+ LabelShortWithIcon
+ Numbers_RimWorld_NeedDef_Mood
+ Numbers_RimWorld_NeedDef_Food
+ Numbers_RimWorld_NeedDef_Rest
+ Numbers_RimWorld_NeedDef_Beauty
+ Numbers_RimWorld_NeedDef_Comfort
+ Numbers_RimWorld_NeedDef_Outdoors
+
+
+
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/1.5/Assemblies/Numbers.dll b/1.5/Assemblies/Numbers.dll
new file mode 100644
index 0000000..6b20635
Binary files /dev/null and b/1.5/Assemblies/Numbers.dll differ
diff --git a/1.5/Defs/MainTabDefs/kNumbers.xml b/1.5/Defs/MainTabDefs/kNumbers.xml
new file mode 100644
index 0000000..5809ee8
--- /dev/null
+++ b/1.5/Defs/MainTabDefs/kNumbers.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ kNumbersOverviewTab
+ Numbers
+ See every possible stat on every colonist or prisoner in a single table.
+ Numbers.MainTabWindow_Numbers
+ 20
+ Z
+
+
+
\ No newline at end of file
diff --git a/1.5/Defs/PawnColumnDef/PawnColumns_Numbers.xml b/1.5/Defs/PawnColumnDef/PawnColumns_Numbers.xml
new file mode 100644
index 0000000..704e060
--- /dev/null
+++ b/1.5/Defs/PawnColumnDef/PawnColumns_Numbers.xml
@@ -0,0 +1,381 @@
+
+
+
+
+
+
+ Numbers_Age
+ Numbers.PawnColumnWorker_Age
+ true
+ Age
+
+
+
+ Numbers_AnimalEggProgress
+ Numbers.PawnColumnWorker_AnimalEggProgress
+ true
+ Egg progress
+
+
+
+ Numbers_Milkfullness
+ Numbers.PawnColumnWorker_AnimalMilkFullness
+ true
+ Milk fullness
+
+
+
+ Numbers_AnimalWoolGrowth
+ Numbers.PawnColumnWorker_AnimalWoolGrowth
+ true
+ Wool growth
+
+
+
+ Numbers_LeatherType
+ Numbers.PawnColumnWorker_LeatherType
+ true
+ Leather type
+
+
+
+ Numbers_Race
+ Numbers.PawnColumnWorker_Race
+ true
+ Race
+
+
+
+ Numbers_Backstory
+ Numbers.PawnColumnWorker_Backstory
+ true
+ Title
+
+
+
+ Numbers_Psyfocus
+ Numbers.PawnColumnWorker_Psyfocus
+ true
+ Psyfocus
+
+
+
+
+
+
+ Numbers_Entropy
+ Numbers.PawnColumnWorker_Entropy
+ true
+ Heat
+
+
+
+
+
+
+ Numbers_PsylinkLevel
+ Numbers.PawnColumnWorker_PsylinkLevel
+ true
+ Psylevel
+
+
+
+
+
+
+ Numbers_Equipment
+ Numbers.PawnColumnWorker_Equipment
+ true
+ Equipment
+
+
+
+ Numbers_Inventory
+ Numbers.PawnColumnWorker_Inventory
+ true
+ Inventory
+
+
+
+ Numbers_InventoryDropAll
+ Numbers.PawnColumnWorker_InventoryDropAll
+ true
+ Drop all inventory
+
+
+
+ Numbers_JobCurrent
+ Numbers.PawnColumnWorker_JobCurrent
+ true
+ Current job
+
+
+
+ Numbers_JobQueued
+ Numbers.PawnColumnWorker_JobQueued
+ true
+ Queued job
+
+
+
+ Numbers_MentalState
+ Numbers.PawnColumnWorker_MentalState
+ true
+ Mental state
+
+
+
+ Numbers_PrisonerInteraction
+ Numbers.PawnColumnWorker_PrisonerInteraction
+ true
+ true
+ Prisoner interaction
+
+
+
+ Numbers_PrisonerResistance
+ Numbers.PawnColumnWorker_PrisonerResistance
+ true
+ Resistance remaining
+
+
+
+ Numbers_Pain
+ Numbers.PawnColumnWorker_Pain
+ true
+ Pain
+
+
+
+ Numbers_Bleedrate
+ Numbers.PawnColumnWorker_Bleedrate
+ true
+ Bleedrate
+
+
+
+ Numbers_NeedsTreatment
+ Numbers.PawnColumnWorker_NeedsTreatment
+ true
+ Needs treatment
+
+
+
+ Numbers_Operations
+ Numbers.PawnColumnWorker_OperationDropDown
+ true
+ Operations
+
+
+
+ Numbers_Forbidden
+ Numbers.PawnColumnWorker_Forbidden
+ true
+ Forbidden
+ true
+
+
+
+ Numbers_Inspiration
+ Numbers.PawnColumnWorker_Inspiration
+ true
+ Inspiration
+
+
+
+ Numbers_DiseaseProgress
+ Numbers.PawnColumnWorker_DiseaseProgression
+ true
+ Disease immunity margin
+ Disease immunity margin
+ 80
+
+
+
+ Numbers_Wildness
+ Numbers.PawnColumnWorker_AnimalWildness
+ true
+ Wildness
+
+
+
+ Numbers_TameChance
+ Numbers.PawnColumnWorker_TameChance
+ true
+ Tame chance
+
+
+
+ Numbers_SelfTend
+ Numbers.PawnColumnWorker_SelfTend
+ true
+ true
+ Self-tend
+ UI/Icons/Trainables/Rescue
+ 45
+
+
+
+ Numbers_HediffList
+ Numbers.PawnColumnWorker_AllHediffs
+ true
+ UI/Buttons/DevRoot/ToggleTweak
+ Hediffs
+ 45
+
+
+
+ Numbers_HediffBadList
+ Numbers.PawnColumnWorker_HediffIsBad
+ true
+ UI/Buttons/DevRoot/ToggleTweak
+ Bad hediffs
+ 45
+
+
+
+ Numbers_Faction
+ Numbers.PawnColumnWorker_Faction
+ true
+ Faction
+
+
+
+ Numbers_AnimalFilthRate
+ Numbers.PawnColumnWorker_AnimalFilthRate
+ true
+ Filth rate
+
+
+
+ Numbers_Meditation
+ Numbers.PawnColumnWorker_Meditation
+ true
+ Focus
+
+
+
+
+
+
+ Numbers_Areas
+ Numbers.PawnColumnWorker_AllowedAreaWithMassAssign
+ true
+ allowed area
+ 350
+
+
+
+ Numbers_Traits
+ Numbers.PawnColumnWorker_Trait
+ true
+ Traits
+ Traits
+ 350
+
+
+
+ Numbers_Bandwidth
+ Numbers.PawnColumnWorker_Bandwidth
+ true
+ Bandwidth
+ Bandwidth
+
+
+
+ 200
+
+
+
+ Numbers_Endogenes
+ Numbers.PawnColumnWorker_Endogenes
+ true
+ Germline genes
+ Germline genes
+
+
+
+ 350
+
+
+
+ Numbers_Xenogenes
+ Numbers.PawnColumnWorker_Xenogenes
+ true
+ Xenogenes
+ Xenogenes
+
+
+
+ 350
+
+
+
+ Numbers_GenesRegrowTime
+ Numbers.PawnColumnWorker_GenesRegrowTime
+ true
+ Genes Regrowing
+ Genes Regrowing
+
+
+
+ 80
+
+
+
+ Numbers_Guilt
+ Numbers.PawnColumnWorker_Guilt
+ true
+ Guilt
+ Guilt
+ 50
+
+
+
+ Numbers_Ideology
+ Numbers.PawnColumnWorker_Ideology
+ true
+ Ideology and certainty
+ Ideology
+
+
+
+ 200
+
+
+
+ Numbers_Will
+ Numbers.PawnColumnWorker_Will
+ true
+ Will
+
+
+
+ 50
+
+
+
+ Numbers_FoodConsumption
+ Numbers.PawnColumnWorker_FoodConsumption
+ true
+ Food consumption
+ Food consumption
+ 80
+
+
+
+ Numbers_Strip
+ Numbers.PawnColumnWorker_Strip
+ Strip
+ Strip
+ 50
+
+
+
+ Numbers_Recruitable
+ Numbers.PawnColumnWorker_Recruitable
+ Unwaveringly loyal
+ 50
+
+
+
\ No newline at end of file
diff --git a/1.5/Defs/PawnColumnOptionDef/Numbers_PawnColumnOptionDef.xml b/1.5/Defs/PawnColumnOptionDef/Numbers_PawnColumnOptionDef.xml
new file mode 100644
index 0000000..182633f
--- /dev/null
+++ b/1.5/Defs/PawnColumnOptionDef/Numbers_PawnColumnOptionDef.xml
@@ -0,0 +1,96 @@
+
+
+
+
+ EquipmentBearers
+
+ Numbers_Equipment
+ Numbers_Meditation
+ Numbers_Traits
+ Numbers_Strip
+
+
+
+
+ LivingThings
+
+ Numbers_Age
+ Numbers_MentalState
+ Numbers_JobCurrent
+ Numbers_JobQueued
+ Numbers_HediffList
+ Numbers_HediffBadList
+ Numbers_InventoryDropAll
+ Numbers_Ideology
+ Numbers_Xenogenes
+ Xenotype
+ Numbers_Endogenes
+ Numbers_GenesRegrowTime
+ Numbers_FoodConsumption
+
+
+
+
+ Prisoners
+
+ Numbers_PrisonerInteraction
+ Numbers_PrisonerResistance
+ FoodRestriction
+ Numbers_Inventory
+ Numbers_Guilt
+ Numbers_Recruitable
+ Numbers_Will
+
+
+
+
+ Animals
+
+ Numbers_Milkfullness
+ Numbers_AnimalWoolGrowth
+ Numbers_AnimalEggProgress
+ Numbers_Wildness
+ Numbers_TameChance
+ Numbers_Inventory
+ Numbers_LeatherType
+ Numbers_AnimalFilthRate
+ Numbers_Areas
+
+
+
+
+
+ MainTable
+
+ Numbers_Inspiration
+ Numbers_Inventory
+ Numbers_SelfTend
+ Numbers_Psyfocus
+ Numbers_Entropy
+ Numbers_PsylinkLevel
+ Numbers_Areas
+ Numbers_Bandwidth
+
+
+
+
+
+ WildAnimals
+
+ Numbers_Wildness
+ Numbers_TameChance
+ Numbers_LeatherType
+
+
+
+
+
+ DeadThings
+
+ Numbers_Forbidden
+ Numbers_LeatherType
+
+
+
+
+
\ No newline at end of file
diff --git a/1.5/Defs/PawnTableDef/Numbers_PawnTableDef.xml b/1.5/Defs/PawnTableDef/Numbers_PawnTableDef.xml
new file mode 100644
index 0000000..5af3ca8
--- /dev/null
+++ b/1.5/Defs/PawnTableDef/Numbers_PawnTableDef.xml
@@ -0,0 +1,279 @@
+
+
+
+
+ Numbers_MainTable
+ Colonists
+ Numbers.PawnTable_NumbersMain
+
+ LabelShortWithIcon
+ Numbers_Backstory
+ Numbers_RimWorld_StatDef_AimingDelayFactor
+ Numbers_RimWorld_RecordDef_Kills
+ Numbers_RimWorld_StatDef_ShootingAccuracyPawn
+ Numbers_Equipment
+ Numbers_RimWorld_NeedDef_Mood
+ Numbers_RimWorld_SkillDef_Construction
+ MedicalCare
+ Numbers_DiseaseProgress
+ WorkPriority_Patient
+ Numbers_Operations
+ Numbers_Inspiration
+
+
+
+ true
+
+
+ 1148
+
+
+
+ Numbers_Enemies
+ Enemies
+ Numbers.PawnTable_NumbersMain
+
+ LabelShortWithIcon
+ Numbers_RimWorld_StatDef_AimingDelayFactor
+ Numbers_Equipment
+ Numbers_Pain
+ Numbers_RimWorld_StatDef_MeleeDPS
+ Numbers_RimWorld_StatDef_ShootingAccuracyPawn
+ Numbers_RimWorld_StatDef_MoveSpeed
+
+
+
+ true
+
+
+ 1148
+
+
+
+ Numbers_Prisoners
+ Prisoners
+ Numbers.PawnTable_NumbersMain
+
+ LabelShortWithIcon
+ Numbers_Equipment
+ Numbers_RimWorld_StatDef_MarketValue
+ Numbers_PrisonerInteraction
+ Numbers_PrisonerResistance
+ Numbers_Will
+ Numbers_Recruitable
+ Numbers_MentalState
+ MedicalCare
+ FoodRestriction
+
+
+
+ true
+
+
+
+
+
+ Numbers_Corpses
+ Corpses
+ Numbers.PawnTable_NumbersMain
+
+ LabelShortWithIcon
+ Numbers_Equipment
+ Numbers_Forbidden
+ Numbers_RimWorld_StatDef_FoodPoisonChanceFixedHuman
+ Numbers_RimWorld_StatDef_MeatAmount
+ Numbers_RimWorld_StatDef_LeatherAmount
+
+
+
+ true
+ true
+
+
+ 700
+
+
+
+ Numbers_Guests
+ Guests
+ Numbers.PawnTable_NumbersMain
+
+ LabelShortWithIcon
+ Numbers_Equipment
+
+
+
+
+ true
+
+
+
+
+
+ Numbers_Animals
+ Animals
+ Numbers.PawnTable_NumbersMain
+
+ LabelWithIcon
+ Gender
+ LifeStage
+ Pregnant
+ GapTiny
+ GapTiny
+ FollowDrafted
+ FollowFieldwork
+ GapTiny
+ Master
+ Bond
+ Slaughter
+ Numbers_AnimalFilthRate
+ MedicalCare
+ GapTiny
+ Numbers_Areas
+
+
+
+ true
+
+
+
+
+
+ Numbers_WildAnimals
+ Wild animals
+ Numbers.PawnTable_NumbersMain
+
+ LabelWithIcon
+ Gender
+ LifeStage
+ GapTiny
+ GapTiny
+ Hunt
+ Tame
+ GapTiny
+ ManhunterOnDamageChance
+ ManhunterOnTameFailChance
+ Numbers_Wildness
+ Numbers_TameChance
+ Predator
+ Info
+ Numbers_RimWorld_StatDef_MeatAmount
+ Numbers_RimWorld_StatDef_LeatherAmount
+ Numbers_LeatherType
+ Numbers_FoodConsumption
+ Numbers_RimWorld_RecordDef_Kills
+
+
+
+ true
+
+
+
+
+
+ Numbers_AnimalCorpses
+ Animal corpses
+ Numbers.PawnTable_NumbersMain
+
+ LabelWithIcon
+ Numbers_Forbidden
+ Numbers_RimWorld_StatDef_FoodPoisonChanceFixedHuman
+ Numbers_RimWorld_StatDef_MeatAmount
+ Numbers_RimWorld_StatDef_LeatherAmount
+ Numbers_LeatherType
+
+
+
+ true
+ true
+
+
+ 700
+
+
+
+ Numbers_CombatPreset
+ combat
+ Numbers.PawnTable_NumbersMain
+
+ LabelShortWithIcon
+ Numbers_RimWorld_StatDef_AimingDelayFactor
+ Numbers_Equipment
+ Numbers_RimWorld_StatDef_ShootingAccuracyPawn
+ Numbers_RimWorld_RecordDef_Kills
+ Numbers_RimWorld_StatDef_MoveSpeed
+ Numbers_RimWorld_StatDef_MeleeDPS
+ Numbers_RimWorld_NeedDef_Food
+ Numbers_RimWorld_NeedDef_Mood
+ Numbers_Bleedrate
+ Numbers_Pain
+ Numbers_JobCurrent
+
+
+
+ true
+
+
+
+
+
+ Numbers_WorkTabPlusPreset
+ WorkTab plus
+ Numbers.PawnTable_NumbersMain
+
+ LabelShortWithIcon
+ Numbers_RimWorld_StatDef_WorkSpeedGlobal
+ WorkPriority_Patient
+ Numbers_NeedsTreatment
+ WorkPriority_Doctor
+ Numbers_RimWorld_StatDef_MedicalTendQuality
+ WorkPriority_PatientBedRest
+ Numbers_DiseaseProgress
+ WorkPriority_Warden
+ Numbers_RimWorld_StatDef_NegotiationAbility
+ WorkPriority_Handling
+ Numbers_RimWorld_StatDef_TameAnimalChance
+ WorkPriority_Cooking
+ Numbers_RimWorld_StatDef_FoodPoisonChance
+ WorkPriority_Hunting
+ Numbers_RimWorld_StatDef_HuntingStealth
+ WorkPriority_Construction
+ Numbers_RimWorld_StatDef_ConstructSuccessChance
+ WorkPriority_Growing
+ Numbers_RimWorld_StatDef_PlantWorkSpeed
+ WorkPriority_Mining
+ Numbers_RimWorld_StatDef_MiningSpeed
+ WorkPriority_PlantCutting
+ Numbers_RimWorld_StatDef_PlantHarvestYield
+ WorkPriority_Research
+ Numbers_RimWorld_StatDef_ResearchSpeed
+ Numbers_RimWorld_StatDef_GeneralLaborSpeed
+
+
+
+ true
+
+
+
+
+
+ Numbers_ColonistNeedsPreset
+ colonist needs
+ Numbers.PawnTable_NumbersMain
+
+ LabelShortWithIcon
+ Numbers_RimWorld_NeedDef_Mood
+ Numbers_RimWorld_NeedDef_Food
+ Numbers_RimWorld_NeedDef_Rest
+ Numbers_RimWorld_NeedDef_Beauty
+ Numbers_RimWorld_NeedDef_Comfort
+ Numbers_RimWorld_NeedDef_Outdoors
+
+
+
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/About/About.xml b/About/About.xml
index 5530c5e..e6413a1 100644
--- a/About/About.xml
+++ b/About/About.xml
@@ -1,10 +1,24 @@
- Numbers
- koisama
- http://steamcommunity.com/sharedfiles/filedetails/?id=939597551
- 0.18.1712
-
-Adds a customizable general overview tab, allowing you to see any stats on all your colonists or prisoners in a single window.
-
+ Numbers
+ Mehni
+ https://github.com/Mehni/kNumbers
+ Mehni.Numbers
+
+
+ brrainz.harmony
+ Harmony
+ steam://url/CommunityFilePage/2009463077
+ https://github.com/pardeike/HarmonyRimWorld/releases/latest
+
+
+
+ 1.0
+ 1.1
+ 1.2
+ 1.3
+ 1.4
+ 1.5
+
+ Adds a customizable general overview tab, allowing you to see any stats on all your colonists or prisoners in a single window. Original by koisama. Updated, expanded and maintained by Mehni.
\ No newline at end of file
diff --git a/About/PublishedFileId.txt b/About/PublishedFileId.txt
new file mode 100644
index 0000000..2530495
--- /dev/null
+++ b/About/PublishedFileId.txt
@@ -0,0 +1 @@
+1414302321
\ No newline at end of file
diff --git a/About/preview.png b/About/preview.png
new file mode 100644
index 0000000..9a6a2c6
Binary files /dev/null and b/About/preview.png differ
diff --git a/Assemblies/0Harmony.dll b/Assemblies/0Harmony.dll
new file mode 100644
index 0000000..6c0dd94
Binary files /dev/null and b/Assemblies/0Harmony.dll differ
diff --git a/Assemblies/Numbers.dll b/Assemblies/Numbers.dll
new file mode 100644
index 0000000..085e386
Binary files /dev/null and b/Assemblies/Numbers.dll differ
diff --git a/Assemblies/RWNumbers.dll b/Assemblies/RWNumbers.dll
deleted file mode 100644
index 0dade6e..0000000
Binary files a/Assemblies/RWNumbers.dll and /dev/null differ
diff --git a/CHANGELOG.md b/CHANGELOG.md
deleted file mode 100644
index 5ac0e4c..0000000
--- a/CHANGELOG.md
+++ /dev/null
@@ -1,60 +0,0 @@
-# CHANGELOG
-
-## 0.6.3
-Fixed https://github.com/koisama/kNumbers/issues/21
- - probably related: fixed animals, corpses, enemies and other tabs not displaying contents sometimes
-Implemented https://github.com/koisama/kNumbers/issues/19
- - press alt to select colonist without camera jump
-Implemented https://github.com/koisama/kNumbers/issues/18
- - press shift and click to select multiple colonists
-
-## 0.6.1
-Merged capacities patch - works just like medical info
-Merged Spanish translation
-Merged Russian translation
-
-## 0.6.0
-Updated to A16 (0.16.1393)
-
-## 0.5.2
-Updated to A15b (0.15.1280)
-Added drug needs
-Added mental state (other)
-
-## 0.5.1
-Fixed need bar overflowing
-Built against 0.14.1238, also works on steam 0.14.1241
-
-## 0.5.0
-Updated to A14 (0.14.1234)
-
-## 0.4.4-A13
-Fixed the tab sometimes not loading saved layouts
-Added "age" column definition
-
-## 0.4.3.1-A13
-Fixed current job text not fitting the column
-Minor GUI improvements
-
-## 0.4.3-A13
-Fixed a crash under Linux
-Added "current job" column definition
-Minor GUI improvements - tab will now occupy all available horizontal space and display line count, stats are now alphabetically sorted
-
-## 0.4.1-A13
-Updated to A13
-Added new categories: Guests, Corpses, Animal Corpses
-
-## 0.3-A12d
-Settings (chosen columns) are now stored in a save.
-
-## 0.2.1-A12d:
-Added a tooltip for displayed equipment
-
-## 0.2-A12d:
-Added prisoner interaction and food, medical care level controls
-Added equipment column
-Various bugfixes
-
-## 0.1-A12d:
-Initial release
diff --git a/Defs/MainTabDefs/kNumbers.xml b/Defs/MainTabDefs/kNumbers.xml
index e2bbbe2..5809ee8 100644
--- a/Defs/MainTabDefs/kNumbers.xml
+++ b/Defs/MainTabDefs/kNumbers.xml
@@ -5,8 +5,9 @@
kNumbersOverviewTab
Numbers
See every possible stat on every colonist or prisoner in a single table.
- kNumbers.MainTabWindow_Numbers
+ Numbers.MainTabWindow_Numbers
20
+ Z
-
+
\ No newline at end of file
diff --git a/Defs/PawnColumnDef/PawnColumns_Numbers.xml b/Defs/PawnColumnDef/PawnColumns_Numbers.xml
new file mode 100644
index 0000000..d79cd4d
--- /dev/null
+++ b/Defs/PawnColumnDef/PawnColumns_Numbers.xml
@@ -0,0 +1,206 @@
+
+
+
+
+
+
+ Numbers_Age
+ Numbers.PawnColumnWorker_Age
+ true
+ Age
+
+
+
+ Numbers_AnimalEggProgress
+ Numbers.PawnColumnWorker_AnimalEggProgress
+ true
+ Egg progress
+
+
+
+ Numbers_Milkfullness
+ Numbers.PawnColumnWorker_AnimalMilkFullness
+ true
+ Milk fullness
+
+
+
+ Numbers_AnimalWoolGrowth
+ Numbers.PawnColumnWorker_AnimalWoolGrowth
+ true
+ Wool growth
+
+
+
+ Numbers_Race
+ Numbers.PawnColumnWorker_Race
+ true
+ Race
+
+
+
+ Numbers_Backstory
+ Numbers.PawnColumnWorker_Backstory
+ true
+ Title
+
+
+
+ Numbers_Equipment
+ Numbers.PawnColumnWorker_Equipment
+ true
+ Equipment
+
+
+
+ Numbers_Inventory
+ Numbers.PawnColumnWorker_Inventory
+ true
+ Inventory
+
+
+
+ Numbers_JobCurrent
+ Numbers.PawnColumnWorker_JobCurrent
+ true
+ Current job
+
+
+
+ Numbers_JobQueued
+ Numbers.PawnColumnWorker_JobQueued
+ true
+ Queued job
+
+
+
+ Numbers_MentalState
+ Numbers.PawnColumnWorker_MentalState
+ true
+ Mental state
+
+
+
+ Numbers_PrisonerInteraction
+ Numbers.PawnColumnWorker_PrisonerInteraction
+ true
+ true
+ Prisoner interaction
+
+
+
+ Numbers_PrisonerRecruitmentDifficulty
+ Numbers.PawnColumnWorker_PrisonerRecruitmentDifficulty
+ true
+ Recruitment difficulty
+
+
+
+ Numbers_PrisonerResistance
+ Numbers.PawnColumnWorker_PrisonerResistance
+ true
+ Resistance remaining
+
+
+
+ Numbers_Pain
+ Numbers.PawnColumnWorker_Pain
+ true
+ Pain
+
+
+
+ Numbers_Bleedrate
+ Numbers.PawnColumnWorker_Bleedrate
+ true
+ Bleedrate
+
+
+
+ Numbers_NeedsTreatment
+ Numbers.PawnColumnWorker_NeedsTreatment
+ true
+ Needs treatment
+
+
+
+ Numbers_Operations
+ Numbers.PawnColumnWorker_OperationDropDown
+ true
+ Operations
+
+
+
+ Numbers_Forbidden
+ Numbers.PawnColumnWorker_Forbidden
+ true
+ Forbidden
+ true
+
+
+
+ Numbers_Inspiration
+ Numbers.PawnColumnWorker_Inspiration
+ true
+ Inspiration
+
+
+
+ Numbers_DiseaseProgress
+ Numbers.PawnColumnWorker_DiseaseProgression
+ true
+ Disease immunity margin
+ Disease immunity margin
+ 80
+
+
+
+ Numbers_ManhunterOnTameFailChance
+ Numbers.PawnColumnWorker_ManhunterOnTameFailChance
+ true
+ Revenge chance on tame fail
+ UI/Icons/Animal/Predator
+ 45
+
+
+
+ Numbers_Wildness
+ Numbers.PawnColumnWorker_AnimalWildness
+ true
+ Wildness
+
+
+
+ Numbers_TameChance
+ Numbers.PawnColumnWorker_TameChance
+ true
+ Tame chance
+
+
+
+ Numbers_SelfTend
+ Numbers.PawnColumnWorker_SelfTend
+ true
+ true
+ Self-tend
+ UI/Icons/Trainables/Rescue
+ 45
+
+
+
+ Numbers_HediffList
+ Numbers.PawnColumnWorker_AllHediffs
+ true
+ UI/Buttons/DevRoot/ToggleTweak
+ Hediffs
+ 45
+
+
+
+ Numbers_Faction
+ Numbers.PawnColumnWorker_Faction
+ true
+ Faction
+
+
+
\ No newline at end of file
diff --git a/Defs/PawnTableDef/Numbers_PawnTableDef.xml b/Defs/PawnTableDef/Numbers_PawnTableDef.xml
new file mode 100644
index 0000000..1b53959
--- /dev/null
+++ b/Defs/PawnTableDef/Numbers_PawnTableDef.xml
@@ -0,0 +1,274 @@
+
+
+
+
+ Numbers_MainTable
+ Colonists
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Numbers_Backstory
+ Numbers_RimWorld_StatDef_AimingDelayFactor
+ Numbers_RimWorld_RecordDef_Kills
+ Numbers_RimWorld_StatDef_ShootingAccuracyPawn
+ Numbers_Equipment
+ Numbers_RimWorld_NeedDef_Mood
+ Numbers_RimWorld_SkillDef_Construction
+ MedicalCare
+ Numbers_DiseaseProgress
+ WorkPriority_Patient
+ Numbers_Operations
+ Numbers_Inspiration
+
+
+
+ true
+
+
+ 1032
+
+
+
+ Numbers_Enemies
+ Enemies
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Numbers_RimWorld_StatDef_AimingDelayFactor
+ Numbers_Equipment
+ Numbers_Pain
+ Numbers_RimWorld_StatDef_MeleeDPS
+ Numbers_RimWorld_StatDef_ShootingAccuracyPawn
+ Numbers_RimWorld_StatDef_MoveSpeed
+
+
+
+ true
+
+
+
+
+
+ Numbers_Prisoners
+ Prisoners
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Numbers_Equipment
+ Numbers_RimWorld_StatDef_MarketValue
+ Numbers_PrisonerInteraction
+ Numbers_PrisonerRecruitmentDifficulty
+ Numbers_PrisonerResistance
+ Numbers_MentalState
+ MedicalCare
+ FoodRestriction
+
+
+
+ true
+
+
+
+
+
+ Numbers_Corpses
+ Corpses
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Numbers_Equipment
+ Numbers_Forbidden
+ Numbers_RimWorld_StatDef_FoodPoisonChanceFixedHuman
+ Numbers_RimWorld_StatDef_MeatAmount
+ Numbers_RimWorld_StatDef_LeatherAmount
+
+
+
+ true
+ true
+
+
+ 600
+
+
+
+ Numbers_Guests
+ Guests
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Numbers_Equipment
+
+
+
+
+ true
+
+
+
+
+
+ Numbers_Animals
+ Animals
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Gender
+ LifeStage
+ Pregnant
+ GapTiny
+ GapTiny
+ FollowDrafted
+ FollowFieldwork
+ GapTiny
+ Master
+ Bond
+ Slaughter
+ MedicalCare
+ GapTiny
+ Numbers_Areas
+
+
+
+ true
+
+
+
+
+
+ Numbers_WildAnimals
+ Wild animals
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Gender
+ LifeStage
+ GapTiny
+ GapTiny
+ Hunt
+ Tame
+ GapTiny
+ ManhunterOnDamageChance
+ Numbers_ManhunterOnTameFailChance
+ Numbers_Wildness
+ Numbers_TameChance
+ Predator
+ Info
+ Numbers_RimWorld_StatDef_MeatAmount
+ Numbers_RimWorld_StatDef_LeatherAmount
+ Numbers_RimWorld_RecordDef_Kills
+
+
+
+ true
+
+
+
+
+
+ Numbers_AnimalCorpses
+ Animal corpses
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Numbers_Forbidden
+ Numbers_RimWorld_StatDef_FoodPoisonChanceFixedHuman
+ Numbers_RimWorld_StatDef_MeatAmount
+ Numbers_RimWorld_StatDef_LeatherAmount
+
+
+
+ true
+ true
+
+
+ 600
+
+
+
+ Numbers_CombatPreset
+ combat
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Numbers_RimWorld_StatDef_AimingDelayFactor
+ Numbers_Equipment
+ Numbers_RimWorld_StatDef_ShootingAccuracyPawn
+ Numbers_RimWorld_RecordDef_Kills
+ Numbers_RimWorld_StatDef_MoveSpeed
+ Numbers_RimWorld_StatDef_MeleeDPS
+ Numbers_RimWorld_NeedDef_Food
+ Numbers_RimWorld_NeedDef_Mood
+ Numbers_Bleedrate
+ Numbers_Pain
+ Numbers_JobCurrent
+
+
+
+ true
+
+
+
+
+
+ Numbers_WorkTabPlusPreset
+ WorkTab plus
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Numbers_RimWorld_StatDef_WorkSpeedGlobal
+ WorkPriority_Patient
+ Numbers_NeedsTreatment
+ WorkPriority_Doctor
+ Numbers_RimWorld_StatDef_MedicalTendQuality
+ WorkPriority_PatientBedRest
+ Numbers_DiseaseProgress
+ WorkPriority_Warden
+ Numbers_RimWorld_StatDef_NegotiationAbility
+ WorkPriority_Handling
+ Numbers_RimWorld_StatDef_TameAnimalChance
+ WorkPriority_Cooking
+ Numbers_RimWorld_StatDef_FoodPoisonChance
+ WorkPriority_Hunting
+ Numbers_RimWorld_StatDef_HuntingStealth
+ WorkPriority_Construction
+ Numbers_RimWorld_StatDef_ConstructSuccessChance
+ WorkPriority_Growing
+ Numbers_RimWorld_StatDef_PlantWorkSpeed
+ WorkPriority_Mining
+ Numbers_RimWorld_StatDef_MiningSpeed
+ WorkPriority_PlantCutting
+ Numbers_RimWorld_StatDef_PlantHarvestYield
+ WorkPriority_Smithing
+ Numbers_RimWorld_StatDef_SmithingSpeed
+ WorkPriority_Research
+ Numbers_RimWorld_StatDef_ResearchSpeed
+
+
+
+ true
+
+
+
+
+
+ Numbers_ColonistNeedsPreset
+ colonist needs
+ Numbers.PawnTable_NumbersMain
+
+ Label
+ Numbers_RimWorld_NeedDef_Mood
+ Numbers_RimWorld_NeedDef_Food
+ Numbers_RimWorld_NeedDef_Rest
+ Numbers_RimWorld_NeedDef_Beauty
+ Numbers_RimWorld_NeedDef_Comfort
+ Numbers_RimWorld_NeedDef_Outdoors
+
+
+
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..41a9585
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 Mehni. With acknowledgement of the original idea and first implementation of Numbers by koisama as found on https://github.com/koisama/kNumbers
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/Languages/ChineseSimplified/DefInjected/KeyBindingDef/KeyBindings_Add_MainTab.xml b/Languages/ChineseSimplified/DefInjected/KeyBindingDef/KeyBindings_Add_MainTab.xml
new file mode 100644
index 0000000..6355f15
--- /dev/null
+++ b/Languages/ChineseSimplified/DefInjected/KeyBindingDef/KeyBindings_Add_MainTab.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+ 切换数值标签
+
+
+
\ No newline at end of file
diff --git a/Languages/ChineseSimplified/DefInjected/MainTabDef/kNumbers.xml b/Languages/ChineseSimplified/DefInjected/MainButtonDef/kNumbers.xml
similarity index 75%
rename from Languages/ChineseSimplified/DefInjected/MainTabDef/kNumbers.xml
rename to Languages/ChineseSimplified/DefInjected/MainButtonDef/kNumbers.xml
index c3346c6..614f0ff 100644
--- a/Languages/ChineseSimplified/DefInjected/MainTabDef/kNumbers.xml
+++ b/Languages/ChineseSimplified/DefInjected/MainButtonDef/kNumbers.xml
@@ -1,8 +1,8 @@
-
-
-
- 数值
-
- 查看每个殖民者与囚犯的属性。
-
-
+
+
+
+ 数值
+ 查看每个殖民者与囚犯的属性。
+
+
+
\ No newline at end of file
diff --git a/Languages/ChineseSimplified/DefInjected/PawnColumnDef/PawnColumns_Numbers.xml b/Languages/ChineseSimplified/DefInjected/PawnColumnDef/PawnColumns_Numbers.xml
new file mode 100644
index 0000000..1bc8881
--- /dev/null
+++ b/Languages/ChineseSimplified/DefInjected/PawnColumnDef/PawnColumns_Numbers.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+ 年龄
+ 产蛋进度
+ 产奶进度
+ 毛发生长进度
+ 种族
+ 标题
+ 装备
+ 当前工作
+ 等待处理
+ 精神状况
+ 囚犯互动
+ 招募难度
+ 招募抵抗
+ 疼痛
+ 失血速率
+ 需要治疗
+ 手术
+ 禁止
+ 灵感
+ 免疫裕度
+
+
+
\ No newline at end of file
diff --git a/Languages/ChineseSimplified/DefInjected/PawnTableDef/Numbers_PawnTableDef.xml b/Languages/ChineseSimplified/DefInjected/PawnTableDef/Numbers_PawnTableDef.xml
new file mode 100644
index 0000000..2bf6094
--- /dev/null
+++ b/Languages/ChineseSimplified/DefInjected/PawnTableDef/Numbers_PawnTableDef.xml
@@ -0,0 +1,16 @@
+
+
+
+ 殖民者
+ 敌人
+ 囚犯
+ 尸体
+ 访客
+ 动物
+ 野生动物
+ 动物尸体
+ 战斗
+ 工作
+
+
+
\ No newline at end of file
diff --git a/Languages/ChineseSimplified/Keyed/Keys.xml b/Languages/ChineseSimplified/Keyed/Keys.xml
index 1114073..351e4f7 100644
--- a/Languages/ChineseSimplified/Keyed/Keys.xml
+++ b/Languages/ChineseSimplified/Keyed/Keys.xml
@@ -1,33 +1,34 @@
-
-
-
- 属性
- 名称
- 行数
- 点击切换对象类型。
- 点击以{0}进行排序。
- 右键点击删除列。
- 添加统计
- 添加技能
- 添加需求
- 添加其他
- 预设
-
- 当前工作
- 装备
- 囚犯互动
- 狱卒该如何看待这名囚犯。
- 医疗保健
- 年龄
- 精神状态
-
- 殖民者
- 敌人
- 囚犯
- 访客
- 动物
- 野生动物
- 尸体
- 动物尸体
-
-
+
+
+
+ 数值概览
+ 行数
+ 点击选择 pawn 的类型。
+ 右键点击来移除列。
+ 预设
+
+
+ 载入 {0} 预设
+ 医疗
+ 战斗
+ 工作
+ 殖民者需求
+
+ 设置为默认
+ 设置现在的布局为当前 pawn ({0})的默认布局,新建的殖民地将使用这样的布局。可以在MOD选项中删除。(菜单 => 选项 => Mod选项 => Numbers)。
+ 当前的 pawn 没有保存的默认设置。
+ 载入默认设置
+
+ 显示那些通常会被隐藏的需求和工作。
+ 数值MOD有所有野生动物标签MOD的功能,我想用数值MOD取代野生动物MOD。
+ 允许数值MOD使用的最大屏幕垂直空间。
+ 鼠标左键点击pawn列表来选取他们。(以前的改为Alt加左键)
+ 使选取方式非常直观。按Esc键关闭窗口\n\nShift加左键:多选。\n双击使镜头中心对准目标。\n右键选择单个pawn。 \nAlt加左键将关闭窗口并查看单个pawn的情况。 \n注意:此种选择方式应用于所有有关Pawn的tab,而不仅仅是数值tab。
+ 高亮被选取的pawn。
+ 注意:此种选择方式应用于所有有关Pawn的tab,而不仅仅是数值tab。
+
+ 载入布局存档
+ 没有布局存档
+ 保存当前布局
+
+
\ No newline at end of file
diff --git a/Languages/ChineseTraditional/DefInjected/KeyBindingDef/KeyBindings_Add_MainTab.xml b/Languages/ChineseTraditional/DefInjected/KeyBindingDef/KeyBindings_Add_MainTab.xml
new file mode 100644
index 0000000..31bb220
--- /dev/null
+++ b/Languages/ChineseTraditional/DefInjected/KeyBindingDef/KeyBindings_Add_MainTab.xml
@@ -0,0 +1,4 @@
+
+
+ 切換數字標籤
+
diff --git a/Languages/ChineseTraditional/DefInjected/MainButtonDef/kNumbers.xml b/Languages/ChineseTraditional/DefInjected/MainButtonDef/kNumbers.xml
new file mode 100644
index 0000000..c23f743
--- /dev/null
+++ b/Languages/ChineseTraditional/DefInjected/MainButtonDef/kNumbers.xml
@@ -0,0 +1,5 @@
+
+
+ Numbers
+ 在單一表格中查看每個殖民者或囚犯的所有可能狀態。
+
diff --git a/Languages/ChineseTraditional/DefInjected/MainTabDef/kNumbers.xml b/Languages/ChineseTraditional/DefInjected/MainTabDef/kNumbers.xml
deleted file mode 100644
index a952d57..0000000
--- a/Languages/ChineseTraditional/DefInjected/MainTabDef/kNumbers.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
- 數值
-
- 查看每個殖民者與囚犯的屬性。
-
-
diff --git a/Languages/ChineseTraditional/DefInjected/PawnColumnDef/PawnColumns_Numbers.xml b/Languages/ChineseTraditional/DefInjected/PawnColumnDef/PawnColumns_Numbers.xml
new file mode 100644
index 0000000..9a896da
--- /dev/null
+++ b/Languages/ChineseTraditional/DefInjected/PawnColumnDef/PawnColumns_Numbers.xml
@@ -0,0 +1,63 @@
+
+
+ 年齡
+ 產蛋進度
+ 產奶進度
+ 產毛進度
+ 種族
+ 背景
+ 裝備
+ 物品
+ 目前工作
+ 排程工作
+ 心理狀態
+ 囚犯互動
+ 招募難度
+ 反抗
+ 痛苦
+ 出血率
+ 需要治療
+ 手術
+ 禁止
+ 靈感
+ 免疫程度
+ 免疫程度
+ 訓服失敗反擊機率
+ 野性
+ 訓服機率
+ 自我照顧
+ 心理變化
+ 派系
+
+ 排泄
+ 活動區
+ 頻寬
+ 頻寬
+ 種系基因
+ 種系基因
+ 神經熱
+ 基因再生
+ 基因再生
+ 有罪
+ 有罪
+ 身體狀態(負面)
+ 身體狀態(全部)
+ 理念
+ 理念
+ 丟棄攜帶物品
+ 皮革
+ 冥想類型
+ 靈能
+ 靈能等級
+ 可招募
+ 脫下衣物
+ 脫下衣物
+
+
+ 特性
+ 特性
+
+ 異種基因
+ 異種基因
+
+
diff --git a/Languages/ChineseTraditional/DefInjected/PawnTableDef/Numbers_PawnTableDef.xml b/Languages/ChineseTraditional/DefInjected/PawnTableDef/Numbers_PawnTableDef.xml
new file mode 100644
index 0000000..59f58cf
--- /dev/null
+++ b/Languages/ChineseTraditional/DefInjected/PawnTableDef/Numbers_PawnTableDef.xml
@@ -0,0 +1,14 @@
+
+
+ 殖民者
+ 敵人
+ 囚犯
+ 屍體
+ 訪客
+ 動物
+ 野生動物
+ 動物屍體
+ 戰鬥
+ 工作
+ 預設殖民者需求
+
diff --git a/Languages/ChineseTraditional/Keyed/Keys.xml b/Languages/ChineseTraditional/Keyed/Keys.xml
index 4209de2..eea6b2d 100644
--- a/Languages/ChineseTraditional/Keyed/Keys.xml
+++ b/Languages/ChineseTraditional/Keyed/Keys.xml
@@ -1,33 +1,30 @@
-
+
-
- 屬性
- 名稱
- 行數
- 點擊切換對象類型。
- 點擊以{0}進行排序。
- 右鍵點擊刪除列。
- 添加統計
- 添加技能
- 添加需求
- 添加其他
+ 數字概觀
+ 數量
+ 點擊選擇單位類型。
+ 點擊右鍵移除欄位,拖曳以移動。
預設
-
- 當前工作
- 裝備
- 囚犯互動
- 獄卒該如何看待這名囚犯。
- 醫療保健
- 年齡
- 精神狀態
-
- 殖民者
- 敵人
- 囚犯
- 訪客
- 動物
- 野生動物
- 屍體
- 動物屍體
-
+ 載入 {0} 預設
+ 醫療
+ 戰鬥
+ 工作
+ 殖民者需求
+ 靈能
+ 設定預設檢視
+ 將目前的檢視設為此類型檢視的預設值({0})。\n\n然後此檢視將用於新殖民地。\n\n可以在mod設定中刪除:\n[選單] => [選項] => [模組設定] => [Numbers]。
+ 對於目前檢視的小人類型,沒有儲存的預設值。
+ 載入預設
+ 顯示即使在通常隱藏的需求和工作。
+ 取代內建的「野生動物」選單
+ 取代內建的「動物」選單
+ 最大高度相對於您的螢幕尺寸的數字視窗。
+ 滑鼠點擊在單位上選擇它們(Alt-點擊)
+ 應該像任何其他選擇一樣直覺地表現。按Esc鍵關閉標籤視窗。\n\nShift鍵點擊以選擇/取消選擇多個。雙擊以將相機置中。按右鍵選擇單個卒子。Alt點擊關閉視窗,選擇並轉到該個卒子。注意:適用於*所有*PawnTables,而不僅僅是數字。
+ 高亮選擇的目標。
+ 適用於*所有*的PawnTables,而不僅僅是數字。
+ 使用小字體作為標題(實驗性)
+ 載入已儲存的設置
+ 沒有已儲存的設置
+ 儲存目前設置
diff --git a/Languages/English/Keyed/Keys.xml b/Languages/English/Keyed/Keys.xml
index 4dc1978..31f9b40 100644
--- a/Languages/English/Keyed/Keys.xml
+++ b/Languages/English/Keyed/Keys.xml
@@ -2,33 +2,37 @@
Numbers overview
- Name
Row Count
- Click to select object type.
- Click to sort by {0}.
- Right click to remove column.
- Add stat
- Add skill
- Add need
- Add capacity
- Add other
+ Click to select pawn type.
+ Right click to remove column. Click and drag to move.
Presets
-
- Current Job
- Equipment
- Prisoner Interaction
- How should wardens treat this prisoner.
- Medical Care
- Age
- Mental State
-
- Colonists
- Enemies
- Prisoners
- Guests
- Animals
- Wild Animals
- Corpses
- Animal Corpses
+
+
+ Load {0} preset
+ medical
+ combat
+ worktab plus
+ colonist needs
+ psycasting
+
+ Set current view as default
+ Set the current view as the default for this type of view ({0}).\n\nThis view will then be used for new colonies.\n\nCan be deleted in the mod settings:\n [Menu] => [Options] => [Mod settings] => [Numbers].
+ There are no defaults stored for the currently viewed pawn type.
+ Load default view
+
+ Show needs and jobs even where it would normally be hidden.
+ Numbers is everything I ever wanted from the Wildlife tab and I want it to take over.
+ Numbers is everything I ever wanted from the Animal tab as well and I want it to take over.
+ Maximum height of the Numbers window relative to your screensize.
+ Mouse clicks in pawn tables select them. (Alt-click to work as before)
+ Should behave intuitively like any other selection. Press Esc to close the tab window.\n\nShift-click to select/deselect multiple. Double click to center camera. Right-click to select a single pawn. Alt-click to close window, select and go to that one pawn. Note: Applies to *all* PawnTables, not just Numbers.
+ Highlight selected pawns.
+ Note: Applies to *all* PawnTables, not just Numbers.
+ Use tiny font for headers (experimental)
+ Reset tables to default columns.
+
+ Load saved view
+ Nothing saved
+ Save current view
\ No newline at end of file
diff --git a/Languages/German/DefInjected/MainTabDefs/kNumbers.xml b/Languages/German/DefInjected/MainTabDefs/kNumbers.xml
new file mode 100644
index 0000000..2f28156
--- /dev/null
+++ b/Languages/German/DefInjected/MainTabDefs/kNumbers.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ kNumbersOverviewTab
+ Numbers
+ Sehen Sie alle möglichen Angaben zu jedem Kolonisten oder Gefangenen in einer einzigen Tabelle.
+ Numbers.MainTabWindow_Numbers
+ 20
+ Z
+
+
+
\ No newline at end of file
diff --git a/Languages/German/DefInjected/PawnColumnDef/PawnColumns_Numbers.xml b/Languages/German/DefInjected/PawnColumnDef/PawnColumns_Numbers.xml
new file mode 100644
index 0000000..d8c604f
--- /dev/null
+++ b/Languages/German/DefInjected/PawnColumnDef/PawnColumns_Numbers.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+ Alter
+ Fortschritt der Eier
+ Milchfülle
+ Wollwachstum
+ Rasse
+ Bezeichnung
+ Ausrütsung
+ Inventar
+ Aktueller Job
+ Job in Warteschleife
+ Mentaler Zustand
+ Gefangenen-Interaktion
+ Schwierigkeit der Rekrutierung
+ Verbleibender Widerstand
+ Schmerz
+ Blutungsrate
+ Benötigt Behandlung
+ Operationen
+ Verboten
+ Inspiration
+ Krankheitsfortschritt
+ Wildheit
+ Zähm Chance
+ Selbsthilfe
+ Gesundheitsdifferenz (vom Normalzustand)
+ Fraktionen
+ Verschmutzungsgrad
+ Fokus
+
+
\ No newline at end of file
diff --git a/Languages/German/DefInjected/PawnTableDef/Numbers_PawnTableDef.xml b/Languages/German/DefInjected/PawnTableDef/Numbers_PawnTableDef.xml
new file mode 100644
index 0000000..753fe97
--- /dev/null
+++ b/Languages/German/DefInjected/PawnTableDef/Numbers_PawnTableDef.xml
@@ -0,0 +1,16 @@
+
+
+
+ Kolonisten
+ Feinde
+ Gefangene
+ Leichen
+ Gäste
+ Tiere
+ Wildtiere
+ Tierleichen
+ Kampfvoreinstellung
+ Arbeits Tab+ Voreinstellung
+ Kolonistenbedürfnisse
+
+
\ No newline at end of file
diff --git a/Languages/German/Keyed/Keys.xml b/Languages/German/Keyed/Keys.xml
new file mode 100644
index 0000000..8e80380
--- /dev/null
+++ b/Languages/German/Keyed/Keys.xml
@@ -0,0 +1,35 @@
+
+
+
+ Zahlen Übersicht
+ Anzahl
+ Klicke um einen Kolonisten auszuwählen.
+ Rechtsklick, um Spalte zu entfernen. Klicke und ziehe zum Verschieben.
+ Voreinstellungen
+
+
+ Voreinstellung {0} laden
+ Medizinisch
+ Kampf
+ Arbeits TAB+
+ Bedarf der Kolonisten
+
+ Aktuelle Ansicht als Standard festlegen
+ Legen Sie die aktuelle Ansicht als Standard für diese Art der Ansicht fest ({0}).\n\nDiese Ansicht wird dann für neue Kolonien verwendet.\n\nKann in den Mod-Einstellungen gelöscht werden:\n [Menu] => [Options] => [Mod-Einstellungen] => [Numbers].
+ Es gibt keine Standardwerte für den aktuell ausgewählten Kolonisten-Typ.
+ Standardansicht laden
+
+ Zeige Bedürfnisse und Jobs auch dort, wo es normalerweise ausgeblendet wird.
+ Numbers ist alles, was ich jemals vom Tierwelt-TAB gewollt habe und ich möchte, dass es die Kontrolle übernimmt.
+ Numbers ist alles, was ich je vom Animal-TAB gewünscht habe und ich möchte, dass es die Kontrolle übernimmt.
+ Maximale Höhe des "Numbers" Fensters relativ zur Bildschirmgröße.
+ Mausklicks in den Kolonisten TAB´s, wählt sie aus. (Alt-Klicken um wie zuvor zu arbeiten)
+ Sollte sich intuitiv wie jede andere Auswahl verhalten. Drücken Sie Esc, um das Tabfenster zu schließen.\n\nShift-Klick um Mehrfachauswahl auszuwählen/abzuwählen. Doppelklick um die Kamera zu zentrieren. Rechtsklick, um einen einzelnen Kolonisten auszuwählen. Alt-Klick, um Fenster zu schließen, wähle aus und springe zu diesem Kolonisten. Hinweis: Gilt für *alle* Kolonisten TAB´s, nicht nur "Numbers".
+ Ausgewählte Kolonisten hervorheben.
+ Hinweis: Gilt für *alle* Kolonisten TAB´s, nicht nur "Numbers".
+
+ Lade gespeicherte Ansicht
+ Nichts gespeichert
+ Speichere aktuelle Ansicht
+
+
diff --git a/Languages/Russian/DefInjected/KeyBindingDef/KeyBindings_Add_MainTab.xml b/Languages/Russian/DefInjected/KeyBindingDef/KeyBindings_Add_MainTab.xml
new file mode 100644
index 0000000..c16cd3e
--- /dev/null
+++ b/Languages/Russian/DefInjected/KeyBindingDef/KeyBindings_Add_MainTab.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+ Переключить вкладку Numbers
+
+
+
\ No newline at end of file
diff --git a/Languages/Russian/DefInjected/MainButtonDef/kNumbers.xml b/Languages/Russian/DefInjected/MainButtonDef/kNumbers.xml
new file mode 100644
index 0000000..29c319b
--- /dev/null
+++ b/Languages/Russian/DefInjected/MainButtonDef/kNumbers.xml
@@ -0,0 +1,8 @@
+
+
+
+ Цифры
+ Просмотреть все возможные показатели каждого колониста или других в одной таблице.
+
+
+
\ No newline at end of file
diff --git a/Languages/Russian/DefInjected/MainTabDef/MainTabs.xml b/Languages/Russian/DefInjected/MainTabDef/MainTabs.xml
deleted file mode 100644
index 2e7c80c..0000000
--- a/Languages/Russian/DefInjected/MainTabDef/MainTabs.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
- цифры
- Посмотреть любую доступную статистику по колонистам или заключенным.
-
-
diff --git a/Languages/Russian/DefInjected/PawnColumnDef/PawnColumns_Numbers.xml b/Languages/Russian/DefInjected/PawnColumnDef/PawnColumns_Numbers.xml
new file mode 100644
index 0000000..060509f
--- /dev/null
+++ b/Languages/Russian/DefInjected/PawnColumnDef/PawnColumns_Numbers.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+ Возраст
+ Прогресс яиц
+ Прогресс молока
+ Прогресс шерсти
+ Раса
+ Предыстория
+ Cнаряжение
+ Инвентарь
+ Текущая работа
+ Запланированная работа
+ Психическое состояние
+ Взаимодействие с заключенными
+ Сложность вербовки
+ Сопротивление
+ Боль
+ Кровотечение
+ Необходимо лечение
+ Операции
+ Запрещенное
+ Вдохновение
+
+ Прогресс заболевания
+ Прогресс заболевания
+
+ Шанс на неудачное приручение
+ Дикость
+ Шанс приручения
+ Самолечение
+ Бионика/Травмы
+ Фракция
+
+
+
\ No newline at end of file
diff --git a/Languages/Russian/DefInjected/PawnTableDef/Numbers_PawnTableDef.xml b/Languages/Russian/DefInjected/PawnTableDef/Numbers_PawnTableDef.xml
new file mode 100644
index 0000000..ac7c7bb
--- /dev/null
+++ b/Languages/Russian/DefInjected/PawnTableDef/Numbers_PawnTableDef.xml
@@ -0,0 +1,16 @@
+
+
+
+ Колонисты
+ Враги
+ Заключенные
+ Трупы
+ Гости
+ Животные
+ Дикие животные
+ Трупы животных
+ Наборы снаряжения
+ Работа +
+
+
+
\ No newline at end of file
diff --git a/Languages/Russian/Keyed/Keys.xml b/Languages/Russian/Keyed/Keys.xml
index 1b2f8e5..737d8cb 100644
--- a/Languages/Russian/Keyed/Keys.xml
+++ b/Languages/Russian/Keyed/Keys.xml
@@ -1,33 +1,36 @@
- Цифры
- Имя
- Число строк
- Нажмите для выбора.
- Нажмите для сортировки по {0}.
- ПКМ для удаления колонки.
- Добавить стат
- Добавить умение
- Добавить потребность
- Добавить другое
- Преднастройки
-
- Текущая работа
- Снаряжение
- Отношение к заключенному
- Как относится к этому заключенному
- Лекарства
- Возраст
- Психика
-
- Колонисты
- Враги
- Заключенные
- Гости
- Животные
- Дикие животные
- Трупы
- Трупы животных
+ Numbers overview
+ Количество строк
+ Нажмите, чтобы выбрать тип пешки.
+ Щелкните правой кнопкой, чтобы удалить столбец. Зажмите левую кнопку и перетащите для перемещения.
+ Шаблоны
-
+
+ Загрузить {0} шаблон
+ медицинский
+ боевой
+ работа +
+ потребности колониста
+
+ Установить текущий шаблон по умолчанию
+ Установите текущий шаблон по умолчанию для этого типа вида ({0}).\n\nЭтот вид будет использоваться для новых колоний.\n\nМожно удалить в настройках мода:\n [Меню] => [Настройки] => [Настройки модов] => [Numbers].
+ Для просматриваемого в настоящее время типа пешки по умолчанию нет сохраненных значений.
+ Загрузить шаблон по умолчанию
+
+ Показать потребности и работы, даже там, где они обычно скрыты.
+ Скрыть вкладку "Фауна" (мода "Numbers" достаточно для моих целей).
+ Скрыть вкладку "Питомцы" (мода "Numbers" достаточно для моих целей).
+ Максимальная высота окна "Numbers" привязана к размеру вашего экрана.
+ Клик в таблице выбирает пешку (Alt-клик работает, как и раньше)
+ Должен вести себя интуитивно, как и любой другой выбор. Нажмите клавишу Esc, чтобы закрыть окно вкладок.\n\nШифт-клик чтобы выбрать/отменить выбор нескольких. Двойной клик чтобы сфокусировать камеру на пешке. Правый-клик чтобы выбрать одну пешку. Alt-клик чтобы закрыть окно и перейти к пешке. Примечание: применяется ко всем таблицам существ, не только к Numbers.
+ Подсветить выбранных пешек.
+ Примечание: применяется ко всем таблицам существ, не только к Numbers.
+ Использовать крошечный шрифт для заголовков (экспериментальный)
+
+ Загрузить сохраненный шаблон
+ Нету сохраненных шаблонов
+ Сохранить текущий шаблон
+
+
\ No newline at end of file
diff --git a/Languages/Spanish/DefInjected/KeyBindingDef/KeyBindings_Add_MainTab.xml b/Languages/Spanish/DefInjected/KeyBindingDef/KeyBindings_Add_MainTab.xml
new file mode 100644
index 0000000..68c932e
--- /dev/null
+++ b/Languages/Spanish/DefInjected/KeyBindingDef/KeyBindings_Add_MainTab.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+ Pestaña de Datos
+
+
+
\ No newline at end of file
diff --git a/Languages/Spanish/DefInjected/MainButtonDef/kNumbers.xml b/Languages/Spanish/DefInjected/MainButtonDef/kNumbers.xml
new file mode 100644
index 0000000..ea38586
--- /dev/null
+++ b/Languages/Spanish/DefInjected/MainButtonDef/kNumbers.xml
@@ -0,0 +1,8 @@
+
+
+
+ Datos
+ Vea todas las estadísticas posibles de cada colono o prisionero en una sola tabla.
+
+
+
\ No newline at end of file
diff --git a/Languages/Spanish/DefInjected/PawnColumnDef/PawnColumns_Numbers.xml b/Languages/Spanish/DefInjected/PawnColumnDef/PawnColumns_Numbers.xml
new file mode 100644
index 0000000..cfd0064
--- /dev/null
+++ b/Languages/Spanish/DefInjected/PawnColumnDef/PawnColumns_Numbers.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+ Edad
+ Progreso del huevo
+ Proceso de la leche
+ Crecimiento de la lana
+ Raza
+ Título
+ Equipo
+ Inventario
+ Trabajo actual
+ Trabajo en cola
+ Estado mental
+ Interacción prisionero
+ Dificultad de reclutamiento
+ Resistencia restante
+ Dolor
+ Tasa de sangrado
+ Necesita tratamiento
+ Operaciones
+ Prohibido
+ Inspiración
+
+ Margen de inmunidad de la enfermedad
+ Margen de inmunidad de la enfermedad
+
+ Porcentaje de venganza en doma fallida
+ Ferocidad
+ Oportunidad
+ Autosuficiente
+ Estado físico
+ Facción
+
+
+
\ No newline at end of file
diff --git a/Languages/Spanish/DefInjected/PawnTableDef/Numbers_PawnTableDef.xml b/Languages/Spanish/DefInjected/PawnTableDef/Numbers_PawnTableDef.xml
new file mode 100644
index 0000000..2690584
--- /dev/null
+++ b/Languages/Spanish/DefInjected/PawnTableDef/Numbers_PawnTableDef.xml
@@ -0,0 +1,17 @@
+
+
+
+ Colonos
+ Enemigos
+ Prisoneros
+ Cadáveres
+ Invitados
+ Animales
+ Animales salvajes
+ Cadáveres de animales
+ combate
+ WorkTab plus
+ necesidades del colono
+
+
+
\ No newline at end of file
diff --git a/Languages/Spanish/Keyed/Keys.xml b/Languages/Spanish/Keyed/Keys.xml
index 370884d..8e2a8a7 100644
--- a/Languages/Spanish/Keyed/Keys.xml
+++ b/Languages/Spanish/Keyed/Keys.xml
@@ -1,33 +1,37 @@
-
+
- Numbers overview
- Nombre
+ Resumen de Datos
Número de filas
- Haga clic para seleccionar el tipo de objeto.
- Haga clic para ordenar por {0}.
- Haga clic derecho para eliminar la columna.
- Añadir estadística
- Añadir habilidad
- Añadir necesidad
- Añadir otro tipo
- Preestablecido
+ Haga clic para seleccionar el tipo de peón.
+ Haga clic derecho para eliminar la columna. Haz clic y arrastra para moverte.
+ Configurar
- Trabajo actual
- Equipo
- Interacción con los prisioneros
- Cómo deben los guardias tratar a este prisionero.
- Asistencia Médica
- Edad
- Estado Mental
+
+ Cargar {0} configuración
+ médica
+ combate
+ worktab plus
+ necesidades del colono
+ Psico-foco
- Colonos
- Enemigos
- Prisioneros
- Invitados
- Animales
- Animales Salvajes
- Cadáveres
- Cadáveres de Animales
+ Establecer vista actual como predeterminada
+ Establezca la vista actual como predeterminada para este tipo de vista ({0}).\n\nEsta vista se utilizará para nuevas colonias.\n\nSe puede eliminar en la configuración de mod:\n [Menu] => [Opciones] => [Mod configuración] => [Datos].
+ No hay valores predeterminados almacenados para el tipo de peón visto actualmente.
+ Cargar vista predeterminada
-
+ Mostrar necesidades y trabajos incluso donde normalmente estaría oculto.
+ Numbers es todo lo que siempre quise de la pestaña de Vida Silvestre y quiero que se haga cargo.
+ Numbers es todo lo que siempre quise de la pestaña Animal y quiero que se haga cargo.
+ Altura máxima de la ventana Números en relación con el tamaño de su pantalla.
+ Los clics del mouse en las tablas de empeño los seleccionan. (Alt-clic para trabajar como antes)
+ Debe comportarse intuitivamente como cualquier otra selección. Presione Esc para cerrar la ventana de pestañas.\n\nShift-clic para seleccionar / deseleccionar múltiples. Haga doble clic para centrar la cámara. Haga clic derecho para seleccionar un solo peón. Alt-clic para cerrar la ventana, seleccionar e ir a ese peón. Nota: Se aplica a * todos * PawnTables, no solo a los números.
+ Resalta los peones seleccionados.
+ Nota: Se aplica a * todos * PawnTables, no solo a los números.
+ Usar una fuente diminuta para los encabezados (experimental)
+
+ Cargar vista guardada
+ Nada salvado
+ Guardar vista actual
+
+
\ No newline at end of file
diff --git a/Numbers.sln b/Numbers.sln
new file mode 100644
index 0000000..fef13f7
--- /dev/null
+++ b/Numbers.sln
@@ -0,0 +1,30 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.9.34728.123
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Numbers", "Numbers\Numbers.csproj", "{BA30D06A-3EAF-4302-8FD2-9C6A9BD177C4}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7F2E9F8A-0D2C-42B5-AB83-91851FEA7215}"
+ ProjectSection(SolutionItems) = preProject
+ .editorconfig = .editorconfig
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {BA30D06A-3EAF-4302-8FD2-9C6A9BD177C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BA30D06A-3EAF-4302-8FD2-9C6A9BD177C4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BA30D06A-3EAF-4302-8FD2-9C6A9BD177C4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BA30D06A-3EAF-4302-8FD2-9C6A9BD177C4}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {D8A5575F-E628-421F-B9ED-730E5483CB9D}
+ EndGlobalSection
+EndGlobal
diff --git a/Numbers/DefModExtension_NeedsBioTech.cs b/Numbers/DefModExtension_NeedsBioTech.cs
new file mode 100644
index 0000000..b46d832
--- /dev/null
+++ b/Numbers/DefModExtension_NeedsBioTech.cs
@@ -0,0 +1,9 @@
+namespace Numbers
+{
+ using Verse;
+
+ public class DefModExtension_NeedsBioTech : DefModExtension
+ {
+ //nothing needed, just a tag
+ }
+}
diff --git a/Numbers/DefModExtension_NeedsIdeology.cs b/Numbers/DefModExtension_NeedsIdeology.cs
new file mode 100644
index 0000000..fe23149
--- /dev/null
+++ b/Numbers/DefModExtension_NeedsIdeology.cs
@@ -0,0 +1,9 @@
+namespace Numbers
+{
+ using Verse;
+
+ public class DefModExtension_NeedsIdeology : DefModExtension
+ {
+
+ }
+}
diff --git a/Numbers/DefModExtension_NeedsRoyalty.cs b/Numbers/DefModExtension_NeedsRoyalty.cs
new file mode 100644
index 0000000..0c54635
--- /dev/null
+++ b/Numbers/DefModExtension_NeedsRoyalty.cs
@@ -0,0 +1,9 @@
+namespace Numbers
+{
+ using Verse;
+
+ public class DefModExtension_NeedsRoyalty : DefModExtension
+ {
+ //nothing needed, just a tag
+ }
+}
diff --git a/Numbers/DefModExtension_PawnColumnDefs.cs b/Numbers/DefModExtension_PawnColumnDefs.cs
new file mode 100644
index 0000000..14cec46
--- /dev/null
+++ b/Numbers/DefModExtension_PawnColumnDefs.cs
@@ -0,0 +1,15 @@
+namespace Numbers
+{
+ using RimWorld;
+ using Verse;
+
+ public class DefModExtension_PawnColumnDefs : DefModExtension
+ {
+ public RecordDef record;
+ public PawnCapacityDef capacity;
+ public NeedDef need;
+ public StatDef stat;
+ public SkillDef skill;
+ public AbilityDef ability;
+ }
+}
diff --git a/Numbers/DefModExtension_PawnTableDefs.cs b/Numbers/DefModExtension_PawnTableDefs.cs
new file mode 100644
index 0000000..1b359c2
--- /dev/null
+++ b/Numbers/DefModExtension_PawnTableDefs.cs
@@ -0,0 +1,12 @@
+namespace Numbers
+{
+ using Verse;
+
+ public class DefModExtension_PawnTableDefs : DefModExtension
+ {
+ public bool Corpse = false;
+ public bool Humanlike;
+ public bool Animallike = false;
+ }
+}
+
diff --git a/Numbers/Dialog_IHaveToCreateAnEntireFuckingDialogForANOKAYBUTTONFFS.cs b/Numbers/Dialog_IHaveToCreateAnEntireFuckingDialogForANOKAYBUTTONFFS.cs
new file mode 100644
index 0000000..b2297b6
--- /dev/null
+++ b/Numbers/Dialog_IHaveToCreateAnEntireFuckingDialogForANOKAYBUTTONFFS.cs
@@ -0,0 +1,39 @@
+namespace Numbers
+{
+ using RimWorld;
+ using Verse;
+
+ public class Dialog_IHaveToCreateAnEntireFuckingDialogForAGODDAMNOKAYBUTTONFFS : Dialog_Rename
+ {
+ private readonly PawnTableDef pawnTableDef;
+
+ public Dialog_IHaveToCreateAnEntireFuckingDialogForAGODDAMNOKAYBUTTONFFS(PawnTableDef pawnTableDef) : base(null)
+ {
+ this.pawnTableDef = pawnTableDef;
+ curName = pawnTableDef.label;
+ }
+
+ protected override AcceptanceReport NameIsValid(string name)
+ {
+ AcceptanceReport result = base.NameIsValid(name);
+ if (!result.Accepted)
+ {
+ return result;
+ }
+ if (!new System.Text.RegularExpressions.Regex("^[a-zA-Z0-9\\-_]*$").IsMatch(name)) //sanitize user input
+ {
+ return "Should only contain letters, numbers, underscores, or dashes";
+ }
+ return true;
+ }
+
+ protected override void OnRenamed(string name)
+ {
+ pawnTableDef.label = curName;
+
+ string pawnTableDeftoSave = HorribleStringParsersForSaving.TurnPawnTableDefIntoCommaDelimitedString(pawnTableDef);
+
+ LoadedModManager.GetMod().GetSettings().StoreNewPawnTableDef(pawnTableDeftoSave);
+ }
+ }
+}
diff --git a/Numbers/HorribleStringParsersForSaving.cs b/Numbers/HorribleStringParsersForSaving.cs
new file mode 100644
index 0000000..4d21eb8
--- /dev/null
+++ b/Numbers/HorribleStringParsersForSaving.cs
@@ -0,0 +1,51 @@
+namespace Numbers
+{
+ using System.Linq;
+ using System.Text;
+ using RimWorld;
+ using Verse;
+
+ public static class HorribleStringParsersForSaving
+ {
+ public static string TurnPawnTableDefIntoCommaDelimitedString(PawnTableDef table, bool asDefault = false)
+ {
+ string label = asDefault ? "Default" : table.label;
+
+ return string.Join(",", [table.defName, label, TurnPawnTableColumnsIntoCommaDelimitedString(table)]);
+ }
+
+ public static string CreateDefNameFromType(Def def)
+ => "Numbers_" + def.GetType().ToString().Replace('.', '_') + "_" + def.defName;
+
+ private static string TurnPawnTableColumnsIntoCommaDelimitedString(PawnTableDef table)
+ {
+ StringBuilder stringBuilder = new();
+ foreach (var item in table.columns)
+ {
+ stringBuilder.Append(item.defName);
+ stringBuilder.Append(',');
+ }
+ return stringBuilder.ToString();
+ }
+
+ public static PawnTableDef TurnCommaDelimitedStringIntoPawnTableDef(string ptd)
+ {
+ string[] pawnTableDef = ptd.Split(',');
+
+ PawnTableDef reconstructedPCD = DefDatabase.GetNamedSilentFail(pawnTableDef[0]);
+
+ if (reconstructedPCD != null)
+ {
+ reconstructedPCD.columns.Clear();
+ for (int i = 2; i < pawnTableDef.Length; i++)
+ {
+ PawnColumnDef pcd = DefDatabase.GetNamedSilentFail(pawnTableDef[i]);
+ if (pcd != null)
+ reconstructedPCD.columns.Add(pcd);
+ }
+ return reconstructedPCD;
+ }
+ return WorldComponent_Numbers.PrimaryFilter.First().Key;
+ }
+ }
+}
diff --git a/Numbers/MainTabWindow_Numbers.cs b/Numbers/MainTabWindow_Numbers.cs
new file mode 100644
index 0000000..d8f3a83
--- /dev/null
+++ b/Numbers/MainTabWindow_Numbers.cs
@@ -0,0 +1,272 @@
+namespace Numbers
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Reflection;
+ using RimWorld;
+ using RimWorld.Planet;
+ using UnityEngine;
+ using Verse;
+
+ public class MainTabWindow_Numbers : MainTabWindow_PawnTable
+ {
+ public const float buttonWidth = 110f;
+ public const float buttonHeight = 35f;
+ public const float buttonGap = 4f;
+ public const float extraTopSpace = 83f;
+
+ public static List> filterValidator = [Find.World.GetComponent().primaryFilter.Value];
+
+ private readonly IEnumerable pawnHumanlikeStatDef;
+ private readonly IEnumerable pawnAnimalStatDef;
+ private readonly IEnumerable corpseStatDef;
+ private readonly IEnumerable pawnHumanlikeNeedDef;
+ private readonly IEnumerable pawnAnimalNeedDef;
+
+ private readonly MethodInfo StatsToDraw;
+
+ public readonly OptionsMaker optionsMaker;
+
+ //Code style: Use GetNamedSilentFail in cases where there is null-handling, so any columns that get run through TryGetBestPawnColumnDefLabel() or AddPawnColumnAtBestPositionAndRefresh() can silently fail.
+ //Use GetNamed anywhere a null column would throw a null ref.
+ private static readonly string workTabName = DefDatabase.GetNamed("Work").ShortenedLabelCap;
+
+ private IEnumerable StatDefs => PawnTableDef.Ext().Corpse ? corpseStatDef :
+ PawnTableDef.Ext().Animallike ? pawnAnimalStatDef : pawnHumanlikeStatDef;
+
+ private IEnumerable NeedDefs => PawnTableDef.Ext().Animallike ? pawnAnimalNeedDef : pawnHumanlikeNeedDef;
+
+ private IEnumerable HealthStats =>
+ [
+ DefDatabase.GetNamedSilentFail("Numbers_HediffList"),
+ DefDatabase.GetNamedSilentFail("Numbers_HediffBadList"),
+ DefDatabase.GetNamedSilentFail("Numbers_Pain"),
+ DefDatabase.GetNamedSilentFail("Numbers_Bleedrate"),
+ DefDatabase.GetNamedSilentFail("Numbers_NeedsTreatment"),
+ DefDatabase.GetNamedSilentFail("Numbers_DiseaseProgress")
+ ];
+
+ //ctor to populate lists.
+ public MainTabWindow_Numbers()
+ {
+ optionsMaker = new OptionsMaker(this);
+
+ StatsToDraw = typeof(StatsReportUtility).GetMethod("StatsToDraw",
+ BindingFlags.NonPublic | BindingFlags.Static |
+ BindingFlags.InvokeMethod, null,
+ [typeof(Thing)], null);
+
+ if (StatsToDraw == null)
+ Log.Error("ReflectionTypeLoadException in Numbers: statsToDraw was null. Please contact mod author.");
+
+ pawnHumanlikeNeedDef ??= GetHumanLikeNeedDef();
+
+ pawnHumanlikeStatDef ??= GetHumanLikeStatDefs();
+
+ if (pawnAnimalNeedDef == null || pawnAnimalStatDef == null || corpseStatDef == null)
+ {
+ var statDefs = PopulateLists();
+ pawnAnimalStatDef = statDefs.pawnAnimalStatDef;
+ pawnAnimalNeedDef = statDefs.pawnAnimalNeedDef;
+ corpseStatDef = statDefs.corpseStatDef;
+ }
+
+ PawnTableDef defaultTable = WorldComponent_Numbers.PrimaryFilter.First().Key;
+ if (Find.World.GetComponent().sessionTable.TryGetValue(defaultTable, out List list))
+ {
+ list.RemoveAll(x => x == null);
+ pawnTableDef.columns = list;
+ }
+
+ UpdateFilter();
+ }
+
+ protected internal PawnTableDef pawnTableDef = NumbersDefOf.Numbers_MainTable;
+
+ protected override PawnTableDef PawnTableDef => pawnTableDef;
+
+ protected override IEnumerable Pawns
+ {
+ get
+ {
+ var corpseList = Find.CurrentMap.listerThings.ThingsInGroup(ThingRequestGroup.Corpse).Cast();
+
+ foreach (Corpse corpse in corpseList)
+ {
+ if (filterValidator.All(validator => validator(corpse.InnerPawn)))
+ yield return corpse.InnerPawn;
+ }
+
+ foreach (Pawn pawn in Find.CurrentMap.mapPawns.AllPawnsSpawned)
+ {
+ if (filterValidator.All(validator => validator(pawn)))
+ yield return pawn;
+ }
+ }
+ }
+
+ protected override float ExtraTopSpace => extraTopSpace;
+
+ public override void DoWindowContents(Rect rect)
+ {
+ float x = 0f;
+ Text.Font = GameFont.Small;
+
+ // row count:
+ Rect thingCount = new(3f, 40f, 200f, 30f);
+ Widgets.Label(thingCount, "koisama.Numbers.Count".Translate() + ": " + Pawns.Count());
+
+ //pawn selector
+ Rect sourceButton = new(x, 0f, buttonWidth, buttonHeight);
+ DoButton(PawnTableDef.label, optionsMaker.PawnSelector(), ref x);
+ TooltipHandler.TipRegion(sourceButton, new TipSignal("koisama.Numbers.ClickToToggle".Translate(), sourceButton.GetHashCode()));
+
+ //stats
+ DoButton("TabStats".Translate(), optionsMaker.OptionsMakerForGenericDef(StatDefs), ref x);
+
+ //worktypes
+ if (PawnTableDef == NumbersDefOf.Numbers_MainTable)
+ {
+ DoButton(workTabName, optionsMaker.FloatMenuOptionsFor(DefDatabase.AllDefsListForReading.Where(pcd => pcd.workType != null).Reverse()), ref x);
+ }
+
+ //skills
+ if (new[] { NumbersDefOf.Numbers_Enemies, NumbersDefOf.Numbers_Prisoners, NumbersDefOf.Numbers_MainTable, NumbersDefOf.Numbers_Guests }.Contains(PawnTableDef))
+ {
+ DoButton("Skills".Translate(), optionsMaker.OptionsMakerForGenericDef(DefDatabase.AllDefsListForReading), ref x);
+ }
+
+ //needs btn (for living things)
+ if (!new[] { NumbersDefOf.Numbers_AnimalCorpses, NumbersDefOf.Numbers_Corpses }.Contains(PawnTableDef))
+ {
+ DoButton("TabNeeds".Translate(), optionsMaker.OptionsMakerForGenericDef(NeedDefs), ref x);
+ }
+
+ //cap btn (for living things)
+ if (!new[] { NumbersDefOf.Numbers_AnimalCorpses, NumbersDefOf.Numbers_Corpses }.Contains(PawnTableDef))
+ {
+ List optionalList = [];
+
+ if (new[] { NumbersDefOf.Numbers_MainTable, NumbersDefOf.Numbers_Prisoners, NumbersDefOf.Numbers_Animals }.Contains(PawnTableDef))
+ {
+ optionalList.Add(DefDatabase.GetNamedSilentFail("MedicalCare"));
+ optionalList.Add(DefDatabase.GetNamedSilentFail("Numbers_Operations"));
+
+ if (PawnTableDef == NumbersDefOf.Numbers_MainTable)
+ optionalList.Add(DefDatabase.GetNamedSilentFail("Numbers_SelfTend"));
+ }
+
+ optionalList.AddRange(HealthStats);
+
+ var tmp = optionsMaker.OptionsMakerForGenericDef(DefDatabase.AllDefsListForReading)
+ .Concat(optionsMaker.FloatMenuOptionsFor(optionalList));
+
+ DoButton("TabHealth".Translate(), tmp.ToList(), ref x);
+ }
+
+ //abilities btn
+ var abilities = optionsMaker.OptionsMakerForGenericDef(DefDatabase.AllDefsListForReading.OrderBy(y => y.label));
+ if (abilities.Count > 0)
+ {
+ DoButton("Abilities".Translate(), abilities, ref x);
+ }
+
+ //records btn
+ DoButton("TabRecords".Translate(), optionsMaker.OptionsMakerForGenericDef(DefDatabase.AllDefsListForReading), ref x);
+
+ //other btn
+ DoButton("MiscRecordsCategory".Translate(), optionsMaker.OtherOptionsMaker(), ref x);
+
+ //presets button
+ float startPositionOfPresetsButton = Mathf.Max(rect.xMax - buttonWidth - Margin, x);
+ Rect addPresetBtn = new(startPositionOfPresetsButton, 0f, buttonWidth, buttonHeight);
+ if (Widgets.ButtonText(addPresetBtn, "koisama.Numbers.SetPresetLabel".Translate()))
+ {
+ Find.WindowStack.Add(new FloatMenu(optionsMaker.PresetOptionsMaker()));
+ }
+
+ base.DoWindowContents(rect);
+ }
+
+ void DoButton(string label, List list, ref float x)
+ {
+ Rect addColumnButton = new(x, 0f, buttonWidth, buttonHeight);
+ if (Widgets.ButtonText(addColumnButton, label))
+ {
+ Find.WindowStack.Add(new FloatMenu(list));
+ }
+ x += buttonWidth + buttonGap;
+ }
+
+ public override void PostOpen()
+ {
+ UpdateFilter();
+ base.PostOpen();
+ Find.World.renderer.wantedMode = WorldRenderMode.None;
+ }
+
+ public void RefreshAndStoreSessionInWorldComp()
+ {
+ SetDirty();
+ Notify_ResolutionChanged();
+ Find.World.GetComponent().sessionTable[PawnTableDef] = PawnTableDef.columns;
+ }
+
+ public void UpdateFilter()
+ {
+ filterValidator.Clear();
+ filterValidator.Insert(0, WorldComponent_Numbers.PrimaryFilter[PawnTableDef]);
+ }
+
+ private (List pawnAnimalNeedDef, List pawnAnimalStatDef, List corpseStatDef) PopulateLists()
+ {
+ PawnGenerationRequest request = new(PawnKindDefOf.Thrumbo, Faction.OfPirates, forceNoIdeo: true, forceGenerateNewPawn: true);
+ Pawn tmpPawn = PawnGenerator.GeneratePawn(request);
+
+ var pawnAnimalNeedDef = tmpPawn.needs.AllNeeds.Where(x => x.def.showOnNeedList).Select(x => x.def).ToList();
+
+ var animalStatDef = ((IEnumerable)StatsToDraw?.Invoke(null, new[] { tmpPawn })) ?? [];
+
+ var pawnAnimalStatDef = animalStatDef
+ .Where(s => s.stat != null && s.ShouldDisplay() && s.stat.Worker != null)
+ .Select(s => s.stat)
+ .OrderBy(stat => stat.LabelCap.Resolve()).ToList();
+
+ Corpse corpse = (Corpse)ThingMaker.MakeThing(tmpPawn.RaceProps.corpseDef);
+ corpse.InnerPawn = tmpPawn;
+
+ var deadAnimalStatDef = ((IEnumerable)StatsToDraw?.Invoke(null, new[] { corpse })) ?? [];
+
+ var corpseStatDef = deadAnimalStatDef
+ .Concat(tmpPawn.def.SpecialDisplayStats(StatRequest.For(tmpPawn)))
+ .Where(s => s.stat != null && s.ShouldDisplay() && s.stat.Worker != null)
+ .Select(s => s.stat)
+ .OrderBy(stat => stat.LabelCap.Resolve()).ToList();
+
+ tmpPawn.Destroy(DestroyMode.KillFinalize);
+ corpse.Destroy();
+
+ return (pawnAnimalNeedDef, pawnAnimalStatDef, corpseStatDef);
+ }
+
+ private IEnumerable GetHumanLikeNeedDef() => DefDatabase.AllDefsListForReading.Except(Numbers.DummyNeed);
+
+ private List GetHumanLikeStatDefs()
+ {
+ PawnGenerationRequest request = new(PawnKindDefOf.AncientSoldier, Faction.OfAncients, forceNoIdeo: true, forceGenerateNewPawn: true);
+ Pawn tmpPawn = PawnGenerator.GeneratePawn(request);
+
+ var source = ((IEnumerable)StatsToDraw?.Invoke(null, new[] { tmpPawn })) ?? [];
+
+ var pawnHumanlikeStatDef = source
+ .Concat(tmpPawn.def.SpecialDisplayStats(StatRequest.For(tmpPawn)))
+ .Where(s => s.stat != null && s.ShouldDisplay() && s.stat.Worker != null)
+ .Select(s => s.stat);
+
+ tmpPawn.Destroy(DestroyMode.KillFinalize);
+
+ return [.. pawnHumanlikeStatDef.OrderBy(stat => stat.LabelCap.Resolve())];
+ }
+ }
+}
diff --git a/Numbers/MainTabWindow_NumbersAnimals.cs b/Numbers/MainTabWindow_NumbersAnimals.cs
new file mode 100644
index 0000000..719eaa9
--- /dev/null
+++ b/Numbers/MainTabWindow_NumbersAnimals.cs
@@ -0,0 +1,24 @@
+namespace Numbers
+{
+ using System.Collections.Generic;
+ using RimWorld;
+ using Verse;
+
+ public class MainTabWindow_NumbersAnimals : MainTabWindow_Numbers
+ {
+ public override void PostOpen()
+ {
+ pawnTableDef = NumbersDefOf.Numbers_Animals;
+
+ if (Find.World.GetComponent().sessionTable
+ .TryGetValue(pawnTableDef, out List listPawnColumDef))
+ {
+ pawnTableDef.columns = listPawnColumDef;
+ }
+
+ UpdateFilter();
+ Notify_ResolutionChanged();
+ base.PostOpen();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Numbers/MainTabWindow_NumbersWildLife.cs b/Numbers/MainTabWindow_NumbersWildLife.cs
new file mode 100644
index 0000000..c7bbb1d
--- /dev/null
+++ b/Numbers/MainTabWindow_NumbersWildLife.cs
@@ -0,0 +1,24 @@
+namespace Numbers
+{
+ using System.Collections.Generic;
+ using RimWorld;
+ using Verse;
+
+ public class MainTabWindow_NumbersWildLife : MainTabWindow_Numbers
+ {
+ public override void PostOpen()
+ {
+ pawnTableDef = NumbersDefOf.Numbers_WildAnimals;
+
+ if (Find.World.GetComponent().sessionTable
+ .TryGetValue(pawnTableDef, out List listPawnColumDef))
+ {
+ pawnTableDef.columns = listPawnColumDef;
+ }
+
+ UpdateFilter();
+ Notify_ResolutionChanged();
+ base.PostOpen();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Numbers/Numbers.cs b/Numbers/Numbers.cs
new file mode 100644
index 0000000..8ebd90c
--- /dev/null
+++ b/Numbers/Numbers.cs
@@ -0,0 +1,568 @@
+namespace Numbers
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Reflection;
+ using System.Reflection.Emit;
+ using HarmonyLib;
+ using JetBrains.Annotations;
+ using RimWorld;
+ using RimWorld.Planet;
+ using UnityEngine;
+ using Verse;
+
+ public class Numbers : Mod
+ {
+ private readonly Numbers_Settings settings;
+
+ private static int reorderableGroupId = -1;
+
+ public Numbers(ModContentPack content) : base(content)
+ {
+ Harmony harmony = new("mehni.rimworld.numbers");
+
+#if DEBUG
+ Harmony.DEBUG = true;
+#endif
+
+ harmony.Patch(AccessTools.Method(typeof(DefGenerator), nameof(DefGenerator.GenerateImpliedDefs_PreResolve)),
+ postfix: new HarmonyMethod(typeof(Numbers), nameof(Columndefs)));
+
+ harmony.Patch(AccessTools.Method(typeof(PawnColumnWorker), "HeaderClicked"),
+ prefix: new HarmonyMethod(typeof(Numbers), nameof(RightClickToRemoveHeader)));
+
+ harmony.Patch(AccessTools.Method(typeof(PawnTable), nameof(PawnTable.PawnTableOnGUI)),
+ postfix: new HarmonyMethod(typeof(Numbers), nameof(ApplyColumnsRemoval)),
+ transpiler: new HarmonyMethod(typeof(Numbers), nameof(MakeHeadersReOrderable)));
+
+ harmony.Patch(AccessTools.Method(typeof(PawnColumnWorker), nameof(PawnColumnWorker.DoHeader)),
+ transpiler: new HarmonyMethod(typeof(Numbers), nameof(UseWordWrapOnHeaders)));
+
+ harmony.Patch(AccessTools.Method(typeof(PawnColumnWorker_Text), nameof(PawnColumnWorker_Text.DoCell)),
+ transpiler: new HarmonyMethod(typeof(Numbers), nameof(CentreCell)));
+
+ harmony.Patch(AccessTools.Method(typeof(ReorderableWidget), nameof(ReorderableWidget.Reorderable)),
+ transpiler: new HarmonyMethod(typeof(Numbers), nameof(ReorderWidgetFromEventToInputTranspiler)));
+
+ //we meet again, Fluffy.
+ Type pawnColumWorkerType = GenTypes.GetTypeInAnyAssembly("WorkTab.PawnColumnWorker_WorkType");
+ if (pawnColumWorkerType != null && typeof(PawnColumnWorker).IsAssignableFrom(pawnColumWorkerType))
+ {
+ harmony.Patch(AccessTools.Method(pawnColumWorkerType, "HeaderInteractions"),
+ prefix: new HarmonyMethod(typeof(Numbers), nameof(RightClickToRemoveHeader)));
+ }
+
+ //we meet Uuugggg again too. Credit where it's due:
+ // https://github.com/alextd/RimWorld-EnhancementPack/blob/master/Source/PawnTableHighlightSelected.cs
+ harmony.Patch(AccessTools.Method(typeof(PawnColumnWorker_Label), nameof(PawnColumnWorker_Label.DoCell)),
+ postfix: new HarmonyMethod(typeof(Numbers), nameof(AddHighlightToLabel_PostFix)),
+ transpiler: new HarmonyMethod(typeof(Numbers), nameof(AddHighlightToLabel_Transpiler)));
+
+ settings = GetSettings();
+ }
+
+ private static void Columndefs()
+ {
+ foreach (PawnColumnDef pawnColumnDef in ImpliedPawnColumnDefs())
+ {
+ DefGenerator.AddImpliedDef(pawnColumnDef);
+ }
+ var pred = DefDatabase.GetNamed("Predator");
+ pred.sortable = true;
+ pred.headerTip = "Predator";
+ }
+
+ private static bool RightClickToRemoveHeader(PawnColumnWorker __instance, Rect headerRect, PawnTable table)
+ {
+ if (Event.current.shift)
+ return true;
+
+ if (Event.current.button != 1)
+ return true;
+
+ if (table is not PawnTable_NumbersMain numbersTable)
+ return true;
+
+ if (!Mouse.IsOver(headerRect))
+ return true;
+
+ numbersTable.EnqueueColumnRemoval(x => ReferenceEquals(__instance, x.Worker));
+
+ if (Find.WindowStack.currentlyDrawnWindow is MainTabWindow_Numbers numbers)
+ numbers.RefreshAndStoreSessionInWorldComp();
+
+ return false;
+ }
+
+ private static void ApplyColumnsRemoval(PawnTable __instance)
+ {
+ if (__instance is PawnTable_NumbersMain table)
+ {
+ table.ApplyColumnRemoval();
+ }
+ }
+
+ private static IEnumerable MakeHeadersReOrderable(IEnumerable instructions, ILGenerator generator)
+ {
+ MethodInfo recacheIfDirty = AccessTools.Method(typeof(PawnTable), "RecacheIfDirty");
+ MethodInfo reorderableGroup = AccessTools.Method(typeof(Numbers), nameof(ReorderableGroup));
+ MethodInfo reorderableWidget = AccessTools.Method(typeof(Numbers), nameof(CallReorderableWidget));
+ MethodInfo doHeader = AccessTools.Method(typeof(PawnColumnWorker), nameof(PawnColumnWorker.DoHeader));
+
+ var local = generator.DeclareLocal(typeof(int));
+
+ CodeInstruction[] codeInstructions = instructions.ToArray();
+ for (int i = 0; i < codeInstructions.Length; i++)
+ {
+ CodeInstruction instruction = codeInstructions[i];
+ if (i > 2 && codeInstructions[i - 1].Calls(recacheIfDirty))
+ {
+ yield return new CodeInstruction(OpCodes.Ldarg_0);
+ yield return new CodeInstruction(OpCodes.Call, reorderableGroup);
+ yield return new CodeInstruction(OpCodes.Stloc, local.LocalIndex);
+ }
+
+ //IL_0092: callvirt instance class RimWorld.PawnColumnWorker RimWorld.PawnColumnDef::get_Worker()
+ //IL_0097: ldloc.s 6 <-- insert here.
+ //IL_0099: ldarg.0
+ //IL_009a: callvirt instance void RimWorld.PawnColumnWorker::DoHeader(valuetype [UnityEngine.CoreModule]UnityEngine.Rect, class RimWorld.PawnTable)
+ if (i < codeInstructions.Length + 3 && instruction.opcode == OpCodes.Ldloc_S && codeInstructions[i + 2].Calls(doHeader))
+ {
+ yield return new CodeInstruction(OpCodes.Ldloc, local.LocalIndex);
+ yield return instruction;
+ yield return new CodeInstruction(OpCodes.Call, reorderableWidget);
+ }
+ yield return instruction;
+ }
+ }
+
+ //public because of https://github.com/krypt-lynx/PawnTableGrouped
+ public static int ReorderableGroup(PawnTable pawnTable)
+ {
+ if (pawnTable is not PawnTable_NumbersMain numbersPawnTable)
+ reorderableGroupId = int.MinValue;
+
+ if (Event.current.type == EventType.Repaint)
+ {
+ reorderableGroupId = ReorderableWidget.NewGroup(ReorderAction, ReorderableDirection.Horizontal, new Rect(0f, 0f, UI.screenWidth, UI.screenHeight));
+ }
+ return reorderableGroupId;
+ }
+
+ private static readonly Action ReorderAction = delegate (int from, int to)
+ {
+ var numbers = Find.WindowStack.Windows.OfType().FirstOrDefault();
+
+ if (numbers != null)
+ {
+ PawnColumnDef pawnColumnDef = numbers.pawnTableDef.columns[from];
+ numbers.pawnTableDef.columns.Insert(to, pawnColumnDef);
+ //if it got inserted at a lower number, the index shifted up 1. If not, stick to the old.
+ numbers.pawnTableDef.columns.RemoveAt(from >= to ? from + 1 : from);
+
+ numbers.RefreshAndStoreSessionInWorldComp();
+ }
+ };
+
+ //public because of https://github.com/krypt-lynx/PawnTableGrouped
+ public static void CallReorderableWidget(int groupId, Rect rect)
+ {
+ if (groupId == int.MinValue)
+ return;
+
+ ReorderableWidget.Reorderable(groupId, rect);
+ }
+
+ private static IEnumerable UseWordWrapOnHeaders(IEnumerable instructions)
+ {
+ MethodInfo Truncate = AccessTools.Method(typeof(GenText), nameof(GenText.Truncate), [typeof(string), typeof(float), typeof(Dictionary)]);
+ MethodInfo WordWrap = AccessTools.Method(typeof(Numbers_Utility), nameof(Numbers_Utility.WordWrapAt));
+
+ var instructionList = instructions.ToList();
+ for (int i = 0; i < instructionList.Count; i++)
+ {
+ if (instructionList[i].opcode == OpCodes.Ldnull && instructionList[i + 1].Calls(Truncate))
+ {
+ yield return new CodeInstruction(OpCodes.Ldarg_2);
+ yield return new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(Numbers), nameof(Numbers.GetGameFont)));
+ yield return new CodeInstruction(OpCodes.Call, AccessTools.Property(typeof(Text), nameof(Text.Font)).GetSetMethod());
+
+ instructionList[i].opcode = OpCodes.Ldarg_2;
+ instructionList[i + 1].operand = WordWrap;
+ }
+ yield return instructionList[i];
+ }
+ }
+
+ private static GameFont GetGameFont(PawnTable table)
+ => table is PawnTable_NumbersMain && Numbers_Settings.useTinyFont ? GameFont.Tiny : GameFont.Small;
+
+ private static IEnumerable CentreCell(IEnumerable instructions)
+ {
+ List instructionList = instructions.ToList();
+
+ MethodInfo anchorSetter = AccessTools.Property(typeof(Text), nameof(Text.Anchor)).GetSetMethod();
+ MethodInfo transpilerHelper = AccessTools.Method(typeof(Numbers), nameof(TranspilerHelper));
+
+ for (int i = 0; i < instructionList.Count; i++)
+ {
+ CodeInstruction instruction = instructionList[i];
+ if (instruction.opcode == OpCodes.Call && instructionList[i + 1].Calls(anchorSetter))
+ {
+ yield return new CodeInstruction(OpCodes.Ldarg_3); //put Table on stack
+ instruction = new CodeInstruction(OpCodes.Call, transpilerHelper);
+ }
+ yield return instruction;
+ }
+ }
+
+ //slight issue with job strings. Meh.
+ private static TextAnchor TranspilerHelper(PawnTable table)
+ => table is PawnTable_NumbersMain ? TextAnchor.MiddleCenter : TextAnchor.MiddleLeft;
+
+ ///
+ /// TOO MUCH OF A MESS TO EXPLAIN
+ ///
+ /// Madness.
+ private static IEnumerable ReorderWidgetFromEventToInputTranspiler(IEnumerable instructions, ILGenerator generator)
+ {
+ MethodInfo GetCurrent = AccessTools.Property(typeof(Event), nameof(Event.current)).GetGetMethod();
+ MethodInfo GetRawType = AccessTools.Property(typeof(Event), nameof(Event.rawType)).GetGetMethod();
+ MethodInfo NoMouseButtonsPressed = AccessTools.Method(typeof(Numbers), nameof(Numbers.NoMouseButtonsPressed));
+ MethodInfo WasClicked = AccessTools.Method(typeof(Numbers), nameof(Numbers.WasClicked));
+
+ FieldInfo released = AccessTools.Field(typeof(ReorderableWidget), "released");
+
+ bool yieldNext = true;
+
+ List instructionArr = instructions.ToList();
+ for (int i = 0; i < instructionArr.ToArray().Length; i++)
+ {
+ CodeInstruction instruction = instructionArr[i];
+ if (instruction.Calls(GetCurrent))
+ {
+ if (instructionArr[i + 1].operand != null && instructionArr[i + 1].Calls(GetRawType))
+ {
+ //L_02bc: Label1
+ //L_02bc: call UnityEngine.Event get_current()
+ //L_02c1: callvirt EventType get_rawType()
+ //L_02c6: ldc.i4.1
+ // =>
+ // call Input.GetMouseButtonUp(1) (or 0)
+ yield return new CodeInstruction(OpCodes.Nop)
+ {
+ labels = [generator.DefineLabel()]
+ };
+ instruction.opcode = OpCodes.Call;
+ instruction.operand = NoMouseButtonsPressed;
+ instructionArr.RemoveAt(i + 1);
+ }
+ }
+ if (instruction.StoresField(released))
+ {
+ yield return instruction;
+ CodeInstruction codeInst = new(OpCodes.Ldarg_2)
+ {
+ labels = [generator.DefineLabel()]
+ };
+ codeInst.labels.AddRange(instructionArr[i + 1].labels);
+ yield return codeInst;
+ yield return new CodeInstruction(OpCodes.Call, WasClicked);
+ yieldNext = false;
+ }
+
+ if (!yieldNext && instruction.opcode == OpCodes.Ldarg_1)
+ yieldNext = true;
+
+ if (yieldNext)
+ yield return instruction;
+
+ if (instruction.Calls(AccessTools.Method(typeof(Mouse), nameof(Mouse.IsOver))))
+ yield return new CodeInstruction(OpCodes.And);
+ }
+ }
+
+ [UsedImplicitly]
+ public static bool NoMouseButtonsPressed()
+ => !Input.GetMouseButton(0)
+ && !Input.GetMouseButton(1);
+
+ [UsedImplicitly]
+ public static bool WasClicked(bool useRightButton)
+ => useRightButton && Input.GetMouseButtonDown(1)
+ || !useRightButton && Input.GetMouseButtonDown(0);
+
+
+ private static void AddHighlightToLabel_PostFix(Rect rect, Pawn pawn)
+ {
+ if (!Numbers_Settings.pawnTableHighlightSelected) return;
+
+ if (Find.Selector.IsSelected(pawn))
+ Widgets.DrawHighlightSelected(rect);
+ }
+
+ //again, copied from https://github.com/alextd/RimWorld-EnhancementPack/blob/master/Source/PawnTableHighlightSelected.cs
+ private static IEnumerable AddHighlightToLabel_Transpiler(IEnumerable instructions)
+ {
+ MethodInfo TryJumpAndSelectInfo = AccessTools.Method(typeof(CameraJumper), nameof(CameraJumper.TryJumpAndSelect));
+ MethodInfo EscapeCurrentTabInfo = AccessTools.Method(typeof(MainTabsRoot), nameof(MainTabsRoot.EscapeCurrentTab));
+
+ foreach (CodeInstruction i in instructions)
+ {
+ if (i.Calls(TryJumpAndSelectInfo))
+ i.operand = AccessTools.Method(typeof(Numbers), nameof(ClickPawn));
+ if (i.Calls(EscapeCurrentTabInfo))
+ i.operand = AccessTools.Method(typeof(Numbers), nameof(SodThisImOut));
+
+ yield return i;
+ }
+ }
+
+ public static void ClickPawn(GlobalTargetInfo target, CameraJumper.MovementMode mode = CameraJumper.MovementMode.Pan)
+ {
+ if (!Numbers_Settings.pawnTableClickSelect)
+ {
+ CameraJumper.TryJumpAndSelect(target, mode);
+ return;
+ }
+
+ if (Current.ProgramState != ProgramState.Playing)
+ {
+ return;
+ }
+
+ if (target.Thing is Pawn pawn && pawn.Spawned)
+ {
+ if (Event.current.shift)
+ {
+ if (Find.Selector.IsSelected(pawn))
+ {
+ Find.Selector.Deselect(pawn);
+ }
+ else
+ {
+ Find.Selector.Select(pawn);
+ }
+ }
+ else if (Event.current.alt)
+ {
+ Find.MainTabsRoot.EscapeCurrentTab(false);
+ CameraJumper.TryJumpAndSelect(target, mode);
+ }
+ else
+ {
+ if (Find.Selector.IsSelected(pawn))
+ {
+ CameraJumper.TryJump(target, mode);
+ }
+ if (!Find.Selector.IsSelected(pawn) || Find.Selector.NumSelected > 1 && Event.current.button == 1)
+ {
+ Find.Selector.ClearSelection();
+ Find.Selector.Select(pawn);
+ }
+ }
+ }
+ else //default
+ {
+ CameraJumper.TryJumpAndSelect(target, mode);
+ }
+ }
+
+ public static void SodThisImOut(MainTabsRoot o1, bool o2)
+ {
+ if (!Numbers_Settings.pawnTableClickSelect)
+ o1.EscapeCurrentTab(o2);
+ }
+
+ private static NeedDef _dummyNeed;
+ public static NeedDef DummyNeed => _dummyNeed ??= DefDatabase.GetNamedSilentFail("Authority");
+ // the xml says
+ //
+ // temporary my ass.
+
+ private static IEnumerable ImpliedPawnColumnDefs()
+ => DefDatabase.AllDefsListForReading.Select(GenerateNewPawnColumnDefFor)
+ .Concat(DefDatabase
+ .AllDefsListForReading.Select(GenerateNewPawnColumnDefFor))
+ .Concat(DefDatabase
+ .AllDefsListForReading.Except(DummyNeed).Select(GenerateNewPawnColumnDefFor))
+ .Concat(DefDatabase
+ .AllDefsListForReading.Select(GenerateNewPawnColumnDefFor))
+ .Concat(DefDatabase
+ .AllDefsListForReading.Select(GenerateNewPawnColumnDefFor))
+ .Concat(DefDatabase
+ .AllDefsListForReading.Select(GenerateNewPawnColumnDefFor));
+
+ private static PawnColumnDef GenerateNewPawnColumnDefFor(Def def)
+ {
+ bool prependDescription = def is not PawnCapacityDef;
+ PawnColumnDef pcd = new()
+ {
+ defName = HorribleStringParsersForSaving.CreateDefNameFromType(def),
+ sortable = true,
+ headerTip = (prependDescription ? def.description + "\n\n" : "") + "Numbers_ColumnHeader_Tooltip".Translate(),
+ generated = true,
+ label = def.LabelCap,
+ modContentPack = def.modContentPack,
+ modExtensions = [new DefModExtension_PawnColumnDefs()]
+ };
+ switch (def)
+ {
+ case RecordDef recordDef:
+ pcd.workerClass = typeof(PawnColumnWorker_Record);
+ pcd.GetModExtension().record = recordDef;
+ break;
+ case PawnCapacityDef pawnCapacityDef:
+ pcd.workerClass = typeof(PawnColumnWorker_Capacity);
+ pcd.GetModExtension().capacity = pawnCapacityDef;
+ break;
+ case NeedDef needDef:
+ pcd.workerClass = typeof(PawnColumnWorker_Need);
+ pcd.GetModExtension().need = needDef;
+ break;
+ case StatDef statDef:
+ pcd.workerClass = typeof(PawnColumnWorker_Stat);
+ pcd.GetModExtension().stat = statDef;
+ break;
+ case SkillDef skillDef:
+ pcd.workerClass = typeof(PawnColumnWorker_Skill);
+ pcd.GetModExtension().skill = skillDef;
+ break;
+ case AbilityDef abilityDef:
+ pcd.workerClass = typeof(PawnColumnWorker_Ability);
+ pcd.GetModExtension().ability = abilityDef;
+ break;
+ default:
+ throw new ArgumentException($"Unsupported Def of type {def.GetType()}");
+ }
+
+ return pcd;
+ }
+
+
+ private static Vector2 scrollPosition;
+
+ public override void DoSettingsWindowContents(Rect inRect)
+ {
+ base.DoSettingsWindowContents(inRect);
+
+ Listing_Standard listingStandard = new();
+ listingStandard.Begin(inRect);
+ listingStandard.CheckboxLabeled("Numbers_showMoreInfoThanVanilla".Translate(), ref Numbers_Settings.showMoreInfoThanVanilla);
+ listingStandard.CheckboxLabeled("Numbers_coolerThanTheWildlifeTab".Translate(), ref Numbers_Settings.coolerThanTheWildlifeTab);
+ listingStandard.CheckboxLabeled("Numbers_coolerThanTheAnimalTab".Translate(), ref Numbers_Settings.coolerThanTheAnimalTab);
+ listingStandard.CheckboxLabeled("Numbers_pawnTableClickSelect".Translate(), ref Numbers_Settings.pawnTableClickSelect, "Numbers_pawnTableClickSelect_Desc".Translate());
+ listingStandard.CheckboxLabeled("Numbers_pawnTableHighSelected".Translate(), ref Numbers_Settings.pawnTableHighlightSelected, "Numbers_pawnTableHighSelected_Desc".Translate());
+ listingStandard.CheckboxLabeled("Numbers_useSmolFont".Translate(), ref Numbers_Settings.useTinyFont);
+ listingStandard.SliderLabeled("Numbers_maxTableHeight".Translate(), ref Numbers_Settings.maxHeight, Numbers_Settings.maxHeight.ToStringPercent(), 0.3f);
+ listingStandard.End();
+
+ DrawResetButton(inRect);
+
+ DrawStoredTables(inRect);
+
+ //writing is done by closing the window.
+ }
+
+ private void DrawStoredTables(Rect inRect)
+ {
+ float rowHeight = 20f;
+ float buttonHeight = 16f;
+
+ float height = RegenPawnTableDefsFromSettings().Count * rowHeight;
+ Rect outRect;
+ float num = 0f;
+ int num2 = 0;
+
+ //erdelf unknowningly to the rescue
+ Widgets.BeginScrollView(inRect.BottomPart(0.6f).TopPart(0.8f), ref scrollPosition, outRect = new Rect(inRect.x, inRect.y - rowHeight * 2, inRect.width - 18f, height));
+ List list = RegenPawnTableDefsFromSettings();
+ for (int i = 0; i < list.Count; i++)
+ {
+ string current = list[i];
+
+ if (num + rowHeight >= scrollPosition.y && num <= scrollPosition.y + outRect.height)
+ {
+ Rect rect = new(0f, num, outRect.width, rowHeight);
+ if (num2 % 2 == 0)
+ {
+ Widgets.DrawAltRect(rect);
+ }
+ GUI.BeginGroup(rect);
+ Rect rect2 = new(rect.width - buttonHeight, (rect.height - buttonHeight) / 2f, buttonHeight, buttonHeight);
+ if (Widgets.ButtonImage(rect2, StaticConstructorOnGameStart.DeleteX, Color.white, GenUI.SubtleMouseoverColor))
+ {
+ settings.storedPawnTableDefs.RemoveAt(i);
+ }
+ TooltipHandler.TipRegion(rect2, "delet this");
+
+ Rect rect5 = new(0, 0, rect.width - rowHeight - 2f, rect.height);
+ Text.Anchor = TextAnchor.MiddleLeft;
+ Text.Font = GameFont.Small;
+
+ Widgets.Label(rect5, current);
+ GUI.color = Color.white;
+ Text.Anchor = TextAnchor.UpperLeft;
+ GUI.EndGroup();
+ }
+ num += rowHeight;
+ num2++;
+ }
+ Widgets.EndScrollView();
+ }
+
+ private static void DrawResetButton(Rect inRect)
+ {
+ if (Widgets.ButtonText(inRect.BottomPart(0.5f).TopPart(0.1f), "Numbers_resetToDefault".Translate()))
+ {
+ var worldComp = Find.World.GetComponent();
+ worldComp?.sessionTable.Clear();
+
+ foreach (var (pawnTable, columns) in StaticConstructorOnGameStart.PawnTableDef_Columns)
+ {
+ if (worldComp == null)
+ {
+ break;
+ }
+ var def = DefDatabase.GetNamed(pawnTable);
+
+ List newColumnList = [];
+
+ foreach (var columnName in columns)
+ {
+ var columnDef = DefDatabase.GetNamed(columnName);
+ newColumnList.Add(columnDef);
+ }
+ worldComp.sessionTable[def] = newColumnList;
+ def.columns = newColumnList;
+ }
+ }
+ }
+
+ public override void WriteSettings()
+ {
+ base.WriteSettings();
+ Find.World?.GetComponent()?.NotifySettingsChanged();
+ }
+
+ private readonly List cachedList = [];
+
+ private List RegenPawnTableDefsFromSettings()
+ {
+ cachedList.Clear();
+
+ foreach (string storedPawnTableDef in settings.storedPawnTableDefs)
+ {
+ if (storedPawnTableDef.Split(',')[1] == "Default")
+ cachedList.Add(storedPawnTableDef.Split(',')[0].Split('_')[1] + " (" + storedPawnTableDef.Split(',')[1] + ")");
+ //Numbers_MainTable,Default => MainTable (Default)
+ else
+ cachedList.Add(storedPawnTableDef.Split(',')[1]);
+ }
+ return cachedList;
+ }
+
+ public override string SettingsCategory() => "Numbers";
+ }
+}
diff --git a/Numbers/Numbers.csproj b/Numbers/Numbers.csproj
new file mode 100644
index 0000000..e8c57e5
--- /dev/null
+++ b/Numbers/Numbers.csproj
@@ -0,0 +1,19 @@
+
+
+ net48
+ Numbers
+ Numbers
+ Copyright © Mehni 2024
+ 1.1.0.0
+ none
+ latest
+ ..\1.5\Assemblies\
+ false
+
+
+
+
+ all
+
+
+
\ No newline at end of file
diff --git a/Numbers/Numbers_PawnColumnDef.cs b/Numbers/Numbers_PawnColumnDef.cs
new file mode 100644
index 0000000..8ad722f
--- /dev/null
+++ b/Numbers/Numbers_PawnColumnDef.cs
@@ -0,0 +1,15 @@
+using RimWorld;
+using Verse;
+
+namespace Numbers
+{
+ public class Numbers_PawnColumnDef : PawnColumnDef
+ {
+ public RecordDef record;
+ public PawnCapacityDef capacity;
+ public NeedDef need;
+ public StatDef stat;
+ public SkillDef skill;
+ public PrisonerInteractionModeDef prisonerInteraction;
+ }
+}
diff --git a/Numbers/Numbers_Settings.cs b/Numbers/Numbers_Settings.cs
new file mode 100644
index 0000000..d1c8588
--- /dev/null
+++ b/Numbers/Numbers_Settings.cs
@@ -0,0 +1,56 @@
+namespace Numbers
+{
+ using System.Collections.Generic;
+ using Verse;
+
+ public class Numbers_Settings : ModSettings
+ {
+
+ public static float maxHeight = 1f;
+
+ public static bool showMoreInfoThanVanilla = true;
+ public static bool coolerThanTheWildlifeTab = true; // don't deny it.
+ public static bool coolerThanTheAnimalTab = false; // you flatter me.
+
+ public static bool pawnTableHighlightSelected = true;
+ public static bool pawnTableClickSelect = true;
+
+ public static bool useTinyFont = false;
+
+ public List storedPawnTableDefs = [];
+ private readonly List workingList = [];
+
+ public void StoreNewPawnTableDef(string pawnTableDeftoSave)
+ {
+ workingList.Clear();
+ foreach (string ptd in storedPawnTableDefs)
+ {
+ workingList.Add(ptd.Split(',')[0] + ptd.Split(',')[1]);
+ }
+ //overwrite old one
+ string label = pawnTableDeftoSave.Split(',')[0] + pawnTableDeftoSave.Split(',')[1];
+
+ if (workingList.Contains(label))
+ storedPawnTableDefs.RemoveAll(x => x.Split(',')[0] + x.Split(',')[1] == label);
+
+ if (label == "Default")
+ storedPawnTableDefs.Insert(0, pawnTableDeftoSave);
+ else
+ storedPawnTableDefs.Add(pawnTableDeftoSave);
+
+ Write();
+ }
+
+ public override void ExposeData()
+ {
+ Scribe_Values.Look(ref showMoreInfoThanVanilla, "showMoreInfoThanVanilla", true);
+ Scribe_Values.Look(ref coolerThanTheWildlifeTab, "coolerThanTheWildlifeTab", true);
+ Scribe_Values.Look(ref coolerThanTheAnimalTab, "coolerThanTheAnimalTab");
+ Scribe_Values.Look(ref pawnTableHighlightSelected, "pawnTableHighlightSelected", true);
+ Scribe_Values.Look(ref pawnTableClickSelect, "pawnTableClickSelect", true);
+ Scribe_Values.Look(ref maxHeight, "maxHeight", 1f);
+ Scribe_Values.Look(ref useTinyFont, "useTinyFont");
+ Scribe_Collections.Look(ref storedPawnTableDefs, "numbersPawnTableDefs");
+ }
+ }
+}
diff --git a/Numbers/Numbers_SettingsHelper.cs b/Numbers/Numbers_SettingsHelper.cs
new file mode 100644
index 0000000..f5ff717
--- /dev/null
+++ b/Numbers/Numbers_SettingsHelper.cs
@@ -0,0 +1,39 @@
+namespace Numbers
+{
+ using System;
+ using UnityEngine;
+ using Verse;
+
+ static class Numbers_SettingsHelper
+ {
+ public static void SliderLabeled(this Listing_Standard ls, string label, ref int val, string format, float min = 0f, float max = 100f, string tooltip = null)
+ {
+ float fVal = val;
+ ls.SliderLabeled(label: label, val: ref fVal, format: format, min: min, max: max, tooltip: tooltip);
+ val = (int)fVal;
+ }
+ public static void SliderLabeled(this Listing_Standard ls, string label, ref float val, string format, float min = 0f, float max = 1f, string tooltip = null)
+ {
+ Rect rect = ls.GetRect(height: Text.LineHeight);
+ Rect rect2 = rect.LeftPart(pct: .70f).Rounded();
+ Rect rect3 = rect.RightPart(pct: .30f).Rounded().LeftPart(pct: .67f).Rounded();
+ Rect rect4 = rect.RightPart(pct: .10f).Rounded();
+
+ TextAnchor anchor = Text.Anchor;
+ Text.Anchor = TextAnchor.MiddleLeft;
+ Widgets.Label(rect: rect2, label: label);
+
+ float result = Widgets.HorizontalSlider(rect: rect3, value: val, min: min, max: max, middleAlignment: true);
+ val = result;
+ Text.Anchor = TextAnchor.MiddleRight;
+ Widgets.Label(rect: rect4, label: String.Format(format: format, arg0: val));
+ if (!tooltip.NullOrEmpty())
+ {
+ TooltipHandler.TipRegion(rect: rect, tip: tooltip);
+ }
+
+ Text.Anchor = anchor;
+ ls.Gap(gapHeight: ls.verticalSpacing);
+ }
+ }
+}
diff --git a/Numbers/Numbers_Utility.cs b/Numbers/Numbers_Utility.cs
new file mode 100644
index 0000000..0560b83
--- /dev/null
+++ b/Numbers/Numbers_Utility.cs
@@ -0,0 +1,106 @@
+namespace Numbers
+{
+ using System.Collections.Generic;
+ using System.Linq;
+ using RimWorld;
+ using UnityEngine;
+ using Verse;
+
+ public static class Numbers_Utility
+ {
+ public static bool IsEnemy(this Pawn p)
+ => p.HostileTo(Faction.OfPlayer);
+
+ public static bool IsGuest(this Pawn p)
+ => p.guest != null
+ && !p.guest.IsPrisoner
+ && p.Faction != null
+ && !p.Faction.HostileTo(Faction.OfPlayer)
+ && p.Faction != Faction.OfPlayer;
+
+ public static bool IsVisible(this Pawn p)
+ => p.SpawnedOrAnyParentSpawned
+ && p.PositionHeld != IntVec3.Invalid
+ && !p.PositionHeld.Fogged(p.MapHeld);
+
+ public static bool IsWildAnimal(this Pawn p)
+ => p.Faction == null
+ && p.AnimalOrWildMan();
+
+ public static bool IsAnimal(this Pawn p)
+ => p.RaceProps != null
+ && p.RaceProps.Animal;
+
+ public static DefModExtension_PawnColumnDefs Ext(this PawnColumnDef def, bool logError = true)
+ {
+ var ext = def.GetModExtension();
+
+ if (logError && ext == null)
+ {
+ Log.Error($"Numbers expected DefModExtension PawnColumnDefs, got null for def {def.defName}");
+ }
+
+ return ext;
+ }
+
+ public static DefModExtension_PawnTableDefs Ext(this PawnTableDef def)
+ {
+ var ext = def.GetModExtension();
+
+ if (ext == null)
+ {
+ Log.Error($"Numbers expected DefModExtension PawnTableDef, got null for def {def.defName}");
+ }
+
+ return ext;
+ }
+
+ public static string WordWrapAt(this string text, float length, PawnTable table = null)
+ {
+ if (table != null && table is not PawnTable_NumbersMain)
+ return text.Truncate(length);
+
+ IEnumerable> source = from p in text.Select((c, idx) => new Pair(c, idx))
+ where p.First == ' '
+ select p;
+ if (!source.Any())
+ {
+ return text;
+ }
+ Pair pair = source.MinBy(p => Mathf.Abs(Text.CalcSize(text.Substring(0, p.Second)).x - Text.CalcSize(text.Substring(p.Second + 1)).x));
+ return text.Substring(0, pair.Second) + "\n" + text.Substring(pair.Second + 1);
+ }
+
+ public static bool InfoCardButton(float x, float y, string text)
+ {
+ if (InfoCardButtonWorker(x, y))
+ {
+ Find.WindowStack.Add(new Dialog_MessageBox(text));
+ return true;
+ }
+ return false;
+ }
+
+ private static bool InfoCardButtonWorker(float x, float y)
+ {
+ Rect rect = new(x, y, 24f, 24f);
+ TooltipHandler.TipRegion(rect, "DefInfoTip".Translate());
+ bool result = Widgets.ButtonImage(rect, StaticConstructorOnGameStart.Info, GUI.color);
+ UIHighlighter.HighlightOpportunity(rect, "InfoCard");
+ return result;
+ }
+ }
+
+ [DefOf]
+ public class NumbersDefOf
+ {
+ public static PawnTableDef Numbers_MainTable; //aka Colonists
+ public static PawnTableDef Numbers_Enemies;
+ public static PawnTableDef Numbers_Prisoners;
+ public static PawnTableDef Numbers_Guests;
+ public static PawnTableDef Numbers_Animals;
+ public static PawnTableDef Numbers_WildAnimals;
+ public static PawnTableDef Numbers_Corpses;
+ public static PawnTableDef Numbers_AnimalCorpses;
+ }
+}
diff --git a/Numbers/OptionsMaker.cs b/Numbers/OptionsMaker.cs
new file mode 100644
index 0000000..496a249
--- /dev/null
+++ b/Numbers/OptionsMaker.cs
@@ -0,0 +1,304 @@
+namespace Numbers
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using RimWorld;
+ using UnityEngine;
+ using Verse;
+
+ public class OptionsMaker
+ {
+ private readonly MainTabWindow_Numbers numbers;
+ private readonly Numbers_Settings settings;
+
+ private PawnColumnDef _allowedareawide;
+ private PawnColumnDef AllowedAreaWide => _allowedareawide ??= _allowedareawide = DefDatabase.GetNamedSilentFail("AllowedAreaWide");
+
+ private PawnColumnDef _allowedarea;
+ private PawnColumnDef AllowedArea => _allowedarea ??= _allowedarea = DefDatabase.GetNamedSilentFail("AllowedArea");
+
+ private PawnColumnDef _xenoType;
+ private PawnColumnDef XenoType => _xenoType ??= _xenoType = DefDatabase.GetNamedSilentFail("Xenotype");
+
+ private PawnTableDef PawnTable
+ {
+ get => numbers.pawnTableDef;
+ set => numbers.pawnTableDef = value;
+ }
+
+ private static readonly Func filterRoyalty = pcd => ModLister.RoyaltyInstalled || !pcd.HasModExtension();
+ private static readonly Func filterBioTech = pcd => ModLister.BiotechInstalled || !pcd.HasModExtension();
+ private static readonly Func filterIdeology = pcd => ModLister.IdeologyInstalled || !pcd.HasModExtension();
+
+ public OptionsMaker(MainTabWindow_Numbers mainTabWindow)
+ {
+ numbers = mainTabWindow;
+ settings = LoadedModManager.GetMod().GetSettings();
+ }
+
+ public List PresetOptionsMaker()
+ {
+ var savedLayouts = LoadPlayerCreatedLayouts();
+ List list =
+ [
+ new FloatMenuOption("Numbers_SaveCurrentLayout".Translate(), Save)
+ ];
+ list.AddRange(savedLayouts);
+
+ list.AddRange(
+ [
+ new FloatMenuOption("Numbers_Presets.Load".Translate("Numbers_Presets.Medical".Translate()), () => ChangeMainTableTo(StaticConstructorOnGameStart.medicalPreset)),
+ new FloatMenuOption("Numbers_Presets.Load".Translate("Numbers_Presets.Combat".Translate()), () => ChangeMainTableTo(StaticConstructorOnGameStart.combatPreset)),
+ new FloatMenuOption("Numbers_Presets.Load".Translate("Numbers_Presets.WorkTabPlus".Translate()), () => ChangeMainTableTo(StaticConstructorOnGameStart.workTabPlusPreset)),
+ new FloatMenuOption("Numbers_Presets.Load".Translate("Numbers_Presets.ColonistNeeds".Translate()), () => ChangeMainTableTo(StaticConstructorOnGameStart.colonistNeedsPreset)),
+ //maybe Psycasting here, index 6 + savedLayouts.Count, referenced below
+ new FloatMenuOption("Numbers_SetAsDefault".Translate(), SetAsDefault,
+ extraPartWidth: 29f,
+ extraPartOnGUI: rect => Numbers_Utility.InfoCardButton(rect.x + 5f, rect.y + (rect.height - 24f) / 2, "Numbers_SetAsDefaultExplanation".Translate(PawnTable.LabelCap))),
+ new FloatMenuOption("Numbers_LoadDefault".Translate(), LoadDefault)
+ ]);
+ if (ModLister.RoyaltyInstalled)
+ {
+ list.Insert(6 + savedLayouts.Count, new FloatMenuOption("Numbers_Presets.Load".Translate("Numbers_Presets.Psycasting".Translate()), () => ChangeMainTableTo(StaticConstructorOnGameStart.psycastingPreset)));
+ }
+
+ return list;
+ }
+
+ private IEnumerable General()
+ {
+ yield return new FloatMenuOption("Race".Translate(), () => AddPawnColumnAtBestPositionAndRefresh(DefDatabase.GetNamedSilentFail("Numbers_Race")));
+ yield return new FloatMenuOption("Faction".Translate(), () => AddPawnColumnAtBestPositionAndRefresh(DefDatabase.GetNamedSilentFail("Numbers_Faction")));
+ yield return new FloatMenuOption("Gender", () => AddPawnColumnAtBestPositionAndRefresh(DefDatabase.GetNamedSilentFail("Gender")));
+ }
+
+ public List FloatMenuOptionsFor(IEnumerable pcdList)
+ => pcdList.Select(pcd => new FloatMenuOption(GetBestLabelForPawnColumn(pcd), () => AddPawnColumnAtBestPositionAndRefresh(pcd))).ToList();
+
+ public List OtherOptionsMaker()
+ {
+ List list = [.. General()];
+
+ //equipment bearers
+ if (new[] { NumbersDefOf.Numbers_MainTable,
+ NumbersDefOf.Numbers_Prisoners,
+ NumbersDefOf.Numbers_Enemies,
+ NumbersDefOf.Numbers_Corpses,
+ NumbersDefOf.Numbers_Guests
+ }.Contains(PawnTable))
+ {
+ list.AddRange(FloatMenuOptionsFor(PawnColumnOptionDefOf.EquipmentBearers.options));
+ }
+
+ //all living things
+ if (!new[] { NumbersDefOf.Numbers_AnimalCorpses, NumbersDefOf.Numbers_Corpses }.Contains(PawnTable))
+ {
+ list.AddRange(FloatMenuOptionsFor(PawnColumnOptionDefOf.LivingThings.options));
+ }
+
+ if (PawnTable == NumbersDefOf.Numbers_Prisoners)
+ {
+ list.AddRange(FloatMenuOptionsFor(PawnColumnOptionDefOf.Prisoners.options));
+ }
+
+ if (PawnTable == NumbersDefOf.Numbers_Animals)
+ {
+ list.AddRange(FloatMenuOptionsFor(PawnColumnOptionDefOf.Animals.options
+ .Concat(DefDatabase.GetNamed("Animals").columns)
+ .Where(x => pcdValidator(x))
+ .Except([AllowedArea, AllowedAreaWide])));
+ }
+
+ if (PawnTable == NumbersDefOf.Numbers_MainTable)
+ {
+ list.AddRange(FloatMenuOptionsFor(PawnColumnOptionDefOf.MainTable.options
+ .Concat(DefDatabase.GetNamed("Assign").columns)
+ .Concat(DefDatabase.GetNamed("Restrict").columns)
+ .Where(x => pcdValidator(x))
+ .Except([AllowedArea, AllowedAreaWide, XenoType])));
+ }
+
+ if (PawnTable == NumbersDefOf.Numbers_WildAnimals)
+ {
+ list.RemoveAll(x => x.Label == "Gender"); //duplicate
+ list.AddRange(FloatMenuOptionsFor(PawnColumnOptionDefOf.WildAnimals.options
+ .Concat(DefDatabase.GetNamed("Wildlife").columns)
+ .Where(x => pcdValidator(x))));
+ }
+
+ //all dead things
+ if (new[] { NumbersDefOf.Numbers_AnimalCorpses, NumbersDefOf.Numbers_Corpses }.Contains(PawnTable))
+ {
+ list.AddRange(FloatMenuOptionsFor(PawnColumnOptionDefOf.DeadThings.options));
+ }
+
+ return [.. list.OrderBy(x => x.Label)];
+ }
+
+ public List OptionsMakerForGenericDef(IEnumerable listOfDefs) where T : Def
+ {
+ List list = [];
+
+ foreach (var defCurrent in listOfDefs)
+ {
+ void Action()
+ {
+ PawnColumnDef pcd = DefDatabase.GetNamedSilentFail(HorribleStringParsersForSaving.CreateDefNameFromType(defCurrent));
+ AddPawnColumnAtBestPositionAndRefresh(pcd);
+ }
+ string label = defCurrent.LabelCap;
+ list.Add(new FloatMenuOption(label, Action));
+ }
+
+ return list;
+ }
+
+ public List PawnSelector()
+ {
+ List list = [];
+ foreach (KeyValuePair> filter in WorldComponent_Numbers.PrimaryFilter)
+ {
+ void Action()
+ {
+ if (filter.Value != MainTabWindow_Numbers.filterValidator.First())
+ {
+ PawnTable = filter.Key;
+
+ if (Find.World.GetComponent().sessionTable.TryGetValue(filter.Key, out List listPawnColumDef))
+ PawnTable.columns = listPawnColumDef;
+
+ numbers.UpdateFilter();
+ numbers.Notify_ResolutionChanged();
+ }
+ }
+ list.Add(new FloatMenuOption(filter.Key.label, Action));
+ }
+ return list;
+ }
+
+ private void Save()
+ {
+ //not actually saved like this, just the easiest way to pass it around
+ PawnTableDef ptdPawnTableDef = new()
+ {
+ columns = PawnTable.columns,
+ modContentPack = PawnTable.modContentPack,
+ workerClass = PawnTable.workerClass,
+ defName = PawnTable.defName,
+ label = "NumbersTable" + Rand.Range(0, 10000)
+ };
+ Find.WindowStack.Add(new Dialog_IHaveToCreateAnEntireFuckingDialogForAGODDAMNOKAYBUTTONFFS(ptdPawnTableDef));
+ }
+
+ private List LoadPlayerCreatedLayouts()
+ {
+ List loadOptions = [];
+ foreach (string tableDefToBe in settings.storedPawnTableDefs)
+ {
+ void ApplySetting()
+ {
+ PawnTableDef ptD = HorribleStringParsersForSaving.TurnCommaDelimitedStringIntoPawnTableDef(tableDefToBe);
+
+ PawnTable = DefDatabase.GetNamed(ptD.defName);
+ PawnTable.columns = ptD.columns;
+
+ numbers.UpdateFilter();
+ numbers.RefreshAndStoreSessionInWorldComp();
+ }
+ string label = tableDefToBe.Split(',')[1] == "Default" ? tableDefToBe.Split(',')[0].Split('_')[1] + " (" + tableDefToBe.Split(',')[1] + ")" : tableDefToBe.Split(',')[1];
+ loadOptions.Add(new FloatMenuOption(label, ApplySetting));
+ }
+
+ return loadOptions;
+ }
+
+ private void ChangeMainTableTo(List list)
+ {
+ PawnTable = NumbersDefOf.Numbers_MainTable;
+ PawnTable.columns = new List(list);
+ numbers.UpdateFilter();
+ numbers.Notify_ResolutionChanged();
+ }
+
+ private void SetAsDefault()
+ {
+ string pawnTableDeftoSave = HorribleStringParsersForSaving.TurnPawnTableDefIntoCommaDelimitedString(PawnTable, true);
+ settings.StoreNewPawnTableDef(pawnTableDeftoSave);
+ }
+
+ private void LoadDefault()
+ {
+ bool foundSomething = false;
+ foreach (string tableDefToBe in settings.storedPawnTableDefs)
+ {
+ string[] ptdToBe = tableDefToBe.Split(',');
+ if (ptdToBe[1] == "Default" && PawnTable.defName == ptdToBe[0])
+ {
+ foundSomething = true;
+ PawnTableDef ptD = HorribleStringParsersForSaving.TurnCommaDelimitedStringIntoPawnTableDef(tableDefToBe);
+
+ PawnTable = DefDatabase.GetNamed(ptD.defName);
+ PawnTable.columns = ptD.columns;
+ numbers.UpdateFilter();
+ numbers.RefreshAndStoreSessionInWorldComp();
+ break;
+ }
+ }
+ if (!foundSomething)
+ Messages.Message("Numbers_NoDefaultStoredForThisView".Translate(), MessageTypeDefOf.RejectInput);
+ }
+
+ private static string GetBestLabelForPawnColumn(PawnColumnDef pcd)
+ {
+ if (pcd == null)
+ return string.Empty;
+
+ if (pcd.workType != null)
+ return pcd.workType.labelShort;
+
+ if (!pcd.LabelCap.NullOrEmpty())
+ return pcd.LabelCap;
+
+ if (!pcd.headerTip.NullOrEmpty())
+ return pcd.headerTip;
+
+ return pcd.defName;
+ }
+
+ private void AddPawnColumnAtBestPositionAndRefresh(PawnColumnDef pcd)
+ {
+ if (pcd == null)
+ return;
+ int lastIndex = PawnTable.columns.FindLastIndex(x => x.Worker is PawnColumnWorker_RemainingSpace);
+ PawnTable.columns.Insert(Mathf.Max(1, lastIndex), pcd);
+
+ numbers.RefreshAndStoreSessionInWorldComp();
+ }
+
+ private static readonly Func pcdValidator = pcd =>
+ {
+ try
+ {
+ return pcd.Worker is not PawnColumnWorker_Gap
+ && pcd.Worker is not PawnColumnWorker_Label
+ && pcd.Worker is not PawnColumnWorker_RemainingSpace
+ && pcd.Worker is not PawnColumnWorker_CopyPaste
+ && pcd.Worker is not PawnColumnWorker_MedicalCare
+ && pcd.Worker is not RimWorld.PawnColumnWorker_Ideo //definitely don't want the vanilla one.
+ && pcd.Worker is not RimWorld.PawnColumnWorker_MentalState //definitely don't want the vanilla one.
+ && pcd.Worker is not PawnColumnWorker_Timetable
+ || (!(pcd.label.NullOrEmpty() && pcd.HeaderIcon == null) && !pcd.HeaderInteractable)
+ && filterRoyalty(pcd)
+ && filterIdeology(pcd)
+ && filterBioTech(pcd);
+ }
+ catch (ArgumentNullException ex)
+ {
+ Log.Error($"{pcd.defName} from {pcd.modContentPack.Name} threw ArgumentNullException {ex}");
+ return false;
+ }
+ };
+ //basically all that are already present, don't have an interactable header, and uh
+ }
+}
diff --git a/Numbers/PawnColumnOptionDef.cs b/Numbers/PawnColumnOptionDef.cs
new file mode 100644
index 0000000..63c2cb7
--- /dev/null
+++ b/Numbers/PawnColumnOptionDef.cs
@@ -0,0 +1,28 @@
+namespace Numbers
+{
+ using RimWorld;
+ using System.Collections.Generic;
+ using Verse;
+
+ public class PawnColumnOptionDef : Def
+ {
+ public List options;
+ }
+
+ [DefOf]
+ public class PawnColumnOptionDefOf
+ {
+ public static PawnColumnOptionDef EquipmentBearers;
+ public static PawnColumnOptionDef LivingThings;
+ public static PawnColumnOptionDef Prisoners;
+ public static PawnColumnOptionDef Animals;
+ public static PawnColumnOptionDef MainTable;
+ public static PawnColumnOptionDef WildAnimals;
+ public static PawnColumnOptionDef DeadThings;
+
+ public PawnColumnOptionDefOf()
+ {
+ DefOfHelper.EnsureInitializedInCtor(typeof(PawnColumnOptionDef));
+ }
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_Ability.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_Ability.cs
new file mode 100644
index 0000000..fb90545
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_Ability.cs
@@ -0,0 +1,53 @@
+namespace Numbers
+{
+ using RimWorld;
+ using UnityEngine;
+ using Verse;
+
+ [StaticConstructorOnStartup]
+ public class PawnColumnWorker_Ability : PawnColumnWorker_Icon
+ {
+
+ protected override Texture2D GetIconFor(Pawn pawn)
+ {
+ AbilityDef abilityDef = def.Ext().ability;
+ foreach (Ability a in pawn.abilities.abilities)
+ {
+ if (a.def == abilityDef)
+ {
+ return abilityDef.uiIcon;
+ }
+ }
+ return null;
+ }
+
+ public override int GetMinWidth(PawnTable table)
+ {
+ return 26;
+ }
+
+ protected override string GetHeaderTip(PawnTable table) => def.Ext().ability.GetTooltip() + "\n\n" + "Numbers_ColumnHeader_Tooltip".Translate();
+
+ public override void DoHeader(Rect rect, PawnTable table)
+ {
+ Rect interactableHeaderRect = GetInteractableHeaderRect(rect, table);
+ if (Mouse.IsOver(interactableHeaderRect))
+ {
+ Widgets.DrawHighlight(interactableHeaderRect);
+ string headerTip = GetHeaderTip(table);
+ if (!headerTip.NullOrEmpty())
+ {
+ TooltipHandler.TipRegion(interactableHeaderRect, headerTip);
+ }
+ }
+ if (Widgets.ButtonInvisible(interactableHeaderRect))
+ {
+ HeaderClicked(rect, table);
+ }
+
+ Texture2D abilityIcon = def.Ext().ability.uiIcon;
+ Rect position = new(rect.x, rect.yMax - 26, 26, 26);
+ GUI.DrawTexture(position, abilityIcon);
+ }
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_Age.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_Age.cs
new file mode 100644
index 0000000..a8f13cb
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_Age.cs
@@ -0,0 +1,18 @@
+namespace Numbers
+{
+ using System;
+ using RimWorld;
+ using Verse;
+
+ public class PawnColumnWorker_Age : PawnColumnWorker_Text
+ {
+ protected override string GetTextFor(Pawn pawn)
+ => Math.Round(pawn.ageTracker.AgeBiologicalYearsFloat, 2).ToString("0.00");
+
+ public override int Compare(Pawn a, Pawn b)
+ => a.ageTracker.AgeBiologicalYearsFloat.CompareTo(b.ageTracker.AgeBiologicalYearsFloat);
+
+ public override int GetMinWidth(PawnTable table)
+ => base.GetMinWidth(table) + 20;
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_AllHediffs.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_AllHediffs.cs
new file mode 100644
index 0000000..beda605
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_AllHediffs.cs
@@ -0,0 +1,89 @@
+namespace Numbers
+{
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using RimWorld;
+ using UnityEngine;
+ using Verse;
+
+ public class PawnColumnWorker_AllHediffs : PawnColumnWorker_Icon
+ {
+ protected override Texture2D GetIconFor(Pawn pawn)
+ => VisibleHediffs(pawn).Any()
+ ? StaticConstructorOnGameStart.Plus
+ : null;
+
+ protected override string GetIconTip(Pawn pawn)
+ {
+ StringBuilder icontipBuilder = new();
+ foreach (IGrouping diffs in VisibleHediffGroupsInOrder(pawn))
+ {
+ foreach (IGrouping current in diffs.GroupBy(x => x.UIGroupKey))
+ {
+ int count = current.Count();
+ string text = current.First().LabelCap;
+ if (count != 1)
+ {
+ text = text + " x" + count;
+ }
+ icontipBuilder.AppendWithComma(text);
+ }
+ }
+ return icontipBuilder.ToString();
+ }
+
+ public override int Compare(Pawn a, Pawn b)
+ => VisibleHediffs(a).Count().CompareTo(VisibleHediffs(b).Count());
+
+ protected override string GetHeaderTip(PawnTable table)
+ => base.GetHeaderTip(table) + "\n\n" + "Numbers_ColumnHeader_Tooltip".Translate();
+
+ public override void DoHeader(Rect rect, PawnTable table)
+ {
+ base.DoHeader(rect, table);
+ GUI.color = Color.cyan;
+ float scale = 0.7f;
+ int sizeOfVanillaRescueTex = 24;
+
+ Vector2 headerIconSize = new(
+ Mathf.Min(sizeOfVanillaRescueTex, StaticConstructorOnGameStart.Plus.width) * scale,
+ Mathf.Min(sizeOfVanillaRescueTex, StaticConstructorOnGameStart.Plus.height) * scale);
+
+ int xOffSet = (int)((rect.width - headerIconSize.x) / 4f);
+ Rect position = new(
+ x: rect.x + xOffSet,
+ y: rect.yMax - Mathf.Min(sizeOfVanillaRescueTex, StaticConstructorOnGameStart.Plus.height),
+ width: headerIconSize.x,
+ height: headerIconSize.y);
+
+ GUI.DrawTexture(position, StaticConstructorOnGameStart.Plus);
+ GUI.color = Color.white;
+ }
+
+ protected virtual IEnumerable VisibleHediffs(Pawn pawn)
+ {
+ List mpca = pawn.health.hediffSet.GetMissingPartsCommonAncestors();
+ foreach (Hediff_MissingPart t in mpca)
+ {
+ yield return t;
+ }
+
+ IEnumerable visibleDiffs = pawn.health.hediffSet.hediffs.Where(d => !(d is Hediff_MissingPart) && d.Visible);
+ foreach (Hediff diff in visibleDiffs)
+ {
+ yield return diff;
+ }
+ }
+
+ private IEnumerable> VisibleHediffGroupsInOrder(Pawn pawn)
+ => VisibleHediffs(pawn)
+ .GroupBy(x => x.Part)
+ .OrderByDescending(x => GetListPriority(x.First().Part));
+
+ private static float GetListPriority(BodyPartRecord rec)
+ => rec == null
+ ? 9999999f
+ : (int)rec.height * 10000 + rec.coverageAbsWithChildren;
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_AllowedAreaWithMassAssign.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_AllowedAreaWithMassAssign.cs
new file mode 100644
index 0000000..c2ea0ea
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_AllowedAreaWithMassAssign.cs
@@ -0,0 +1,57 @@
+namespace Numbers
+{
+ using RimWorld;
+ using UnityEngine;
+ using System.Linq;
+ using Verse;
+
+ public class PawnColumnWorker_AllowedAreaWithMassAssign : PawnColumnWorker_AllowedAreaWide
+ {
+ public override int GetOptimalWidth(PawnTable table)
+ {
+ var assignableAreas = Find.CurrentMap.areaManager.AllAreas.Where(area => area.AssignableAsAllowed());
+ var bla = Text.CalcSize(assignableAreas.Select(x => x.Label).ToCommaList());
+ return (int)Mathf.Max(bla.x + 48, def.width);
+ }
+
+ public override int GetMinWidth(PawnTable table)
+ {
+ return Mathf.Min(def.width, GetOptimalWidth(table));
+ }
+
+ protected override string GetHeaderTip(PawnTable table)
+ {
+ return base.GetHeaderTip(table) + "\nShift + control + click: Set all pawns to zone";
+ }
+
+ protected override void HeaderClicked(Rect headerRect, PawnTable table)
+ {
+ base.HeaderClicked(headerRect, table);
+ if (Find.CurrentMap == null)
+ {
+ return;
+ }
+ var allAreas = Find.CurrentMap.areaManager.AllAreas;
+ var assignableAreas = 1 + allAreas.Count(area => area.AssignableAsAllowed());
+ var rectWidth = headerRect.width / assignableAreas;
+ Text.WordWrap = false;
+ Text.Font = GameFont.Tiny;
+ var areaIndexOffset = 1;
+ foreach (var area in allAreas)
+ {
+ if (area.AssignableAsAllowed())
+ {
+ var startPosition = areaIndexOffset * rectWidth;
+ var rect = new Rect(headerRect.x + startPosition, headerRect.y, rectWidth, headerRect.height);
+ if (Mouse.IsOver(rect) && Event.current.control && Event.current.shift && Event.current.button == 0)
+ {
+ table.PawnsListForReading.ForEach(pawn => pawn.playerSettings.AreaRestrictionInPawnCurrentMap = area);
+ }
+ areaIndexOffset++;
+ }
+ }
+ Text.WordWrap = true;
+ Text.Font = GameFont.Small;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_AnimalEggProgress.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_AnimalEggProgress.cs
new file mode 100644
index 0000000..13b2c29
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_AnimalEggProgress.cs
@@ -0,0 +1,37 @@
+namespace Numbers
+{
+ using System.Linq;
+ using HarmonyLib;
+ using RimWorld;
+ using Verse;
+
+ public class PawnColumnWorker_AnimalEggProgress : PawnColumnWorker_Text
+ {
+ protected override string GetTextFor(Pawn pawn)
+ {
+ if (pawn.ageTracker.CurLifeStage.reproductive)
+ {
+ var comp = pawn.AllComps.FirstOrDefault(x => x is CompEggLayer);
+
+ if (comp != null)
+ return ((float)Traverse.Create((CompEggLayer)comp).Field("eggProgress").GetValue()).ToStringPercent();
+ }
+ return null;
+ }
+
+ public override int Compare(Pawn a, Pawn b)
+ => GetScoreFor(a).CompareTo(GetScoreFor(b));
+
+ private float GetScoreFor(Pawn pawn)
+ {
+ if (pawn.ageTracker.CurLifeStage.reproductive)
+ {
+ var comp = pawn.AllComps.FirstOrDefault(x => x is CompEggLayer);
+
+ if (comp != null)
+ return (float)Traverse.Create((CompEggLayer)comp).Field("eggProgress").GetValue();
+ }
+ return -1;
+ }
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_AnimalFilthRate.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_AnimalFilthRate.cs
new file mode 100644
index 0000000..dddcaab
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_AnimalFilthRate.cs
@@ -0,0 +1,21 @@
+namespace Numbers
+{
+ using RimWorld;
+ using Verse;
+
+ public class PawnColumnWorker_AnimalFilthRate : PawnColumnWorker_Text
+ {
+ protected override string GetTextFor(Pawn pawn)
+ {
+ return GetScoreFor(pawn).ToString("F2");
+ }
+
+ public override int Compare(Pawn a, Pawn b)
+ => GetScoreFor(a).CompareTo(GetScoreFor(b));
+
+ private float GetScoreFor(Pawn pawn)
+ {
+ return pawn.GetStatValueForPawn(StatDefOf.FilthRate, pawn);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_AnimalMilkFullness.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_AnimalMilkFullness.cs
new file mode 100644
index 0000000..e89a34b
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_AnimalMilkFullness.cs
@@ -0,0 +1,36 @@
+namespace Numbers
+{
+ using System.Linq;
+ using RimWorld;
+ using Verse;
+
+ public class PawnColumnWorker_AnimalMilkFullness : PawnColumnWorker_Text
+ {
+ protected override string GetTextFor(Pawn pawn)
+ {
+ if (pawn.ageTracker.CurLifeStage.milkable)
+ {
+ var comp = pawn.AllComps.FirstOrDefault(x => x is CompMilkable);
+
+ if (comp != null)
+ return ((CompMilkable)comp).Fullness.ToStringPercent();
+ }
+ return null;
+ }
+
+ public override int Compare(Pawn a, Pawn b)
+ => GetScoreFor(a).CompareTo(GetScoreFor(b));
+
+ private float GetScoreFor(Pawn pawn)
+ {
+ if (pawn.ageTracker.CurLifeStage.milkable)
+ {
+ var comp = pawn.AllComps.FirstOrDefault(x => x is CompMilkable);
+
+ if (comp != null)
+ return ((CompMilkable)comp).Fullness;
+ }
+ return -1;
+ }
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_AnimalWildness.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_AnimalWildness.cs
new file mode 100644
index 0000000..e14db1b
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_AnimalWildness.cs
@@ -0,0 +1,21 @@
+namespace Numbers
+{
+ using RimWorld;
+ using Verse;
+
+ public class PawnColumnWorker_AnimalWildness : PawnColumnWorker_Text
+ {
+ public override int Compare(Pawn a, Pawn b)
+ => (a.AnimalOrWildMan() || b.AnimalOrWildMan())
+ ? GetValue(a).CompareTo(GetValue(b))
+ : 0;
+
+ protected override string GetTextFor(Pawn pawn)
+ => pawn.AnimalOrWildMan()
+ ? GetValue(pawn).ToStringPercent()
+ : string.Empty;
+
+ private float GetValue(Pawn pawn)
+ => pawn.RaceProps.wildness;
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_AnimalWoolGrowth.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_AnimalWoolGrowth.cs
new file mode 100644
index 0000000..1bef1ac
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_AnimalWoolGrowth.cs
@@ -0,0 +1,36 @@
+namespace Numbers
+{
+ using System.Linq;
+ using RimWorld;
+ using Verse;
+
+ public class PawnColumnWorker_AnimalWoolGrowth : PawnColumnWorker_Text
+ {
+ protected override string GetTextFor(Pawn pawn)
+ {
+ if (pawn.ageTracker.CurLifeStage.shearable)
+ {
+ var comp = pawn.AllComps.FirstOrDefault(x => x is CompShearable);
+
+ if (comp != null)
+ return ((CompShearable)comp).Fullness.ToStringPercent();
+ }
+ return null;
+ }
+
+ public override int Compare(Pawn a, Pawn b)
+ => GetScoreFor(a).CompareTo(GetScoreFor(b));
+
+ private float GetScoreFor(Pawn pawn)
+ {
+ if (pawn.ageTracker.CurLifeStage.shearable)
+ {
+ var comp = pawn.AllComps.FirstOrDefault(x => x is CompShearable);
+
+ if (comp != null)
+ return ((CompShearable)comp).Fullness;
+ }
+ return -1;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_Backstory.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_Backstory.cs
new file mode 100644
index 0000000..0746c92
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_Backstory.cs
@@ -0,0 +1,17 @@
+namespace Numbers
+{
+ using RimWorld;
+ using Verse;
+
+ public class PawnColumnWorker_Backstory : PawnColumnWorker_Text
+ {
+ protected override string GetTextFor(Pawn pawn)
+ => pawn.story.TitleShortCap;
+
+ public override int GetMinWidth(PawnTable table)
+ => 80;
+
+ public override int Compare(Pawn a, Pawn b)
+ => a.story.TitleShortCap.CompareTo(b.story.TitleShortCap);
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_Bandwidth.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_Bandwidth.cs
new file mode 100644
index 0000000..50bab62
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_Bandwidth.cs
@@ -0,0 +1,107 @@
+namespace Numbers
+{
+ using System.Collections.Generic;
+ using System.Linq;
+ using RimWorld;
+ using UnityEngine;
+ using Verse;
+
+ [StaticConstructorOnStartup]
+ public class PawnColumnWorker_Bandwidth : PawnColumnWorker
+ {
+ private static readonly Color EmptyBlockColor = new(0.3f, 0.3f, 0.3f, 1f);
+
+ private static readonly Color FilledBlockColor = ColorLibrary.Yellow;
+
+ private static readonly Color ExcessBlockColor = ColorLibrary.Red;
+
+ public override int Compare(Pawn a, Pawn b)
+ {
+ return (a.mechanitor?.TotalBandwidth ?? 0).CompareTo(b.mechanitor?.TotalBandwidth ?? 0);
+ }
+
+ public override void DoCell(Rect rect, Pawn pawn, PawnTable table)
+ {
+ var tracker = pawn.mechanitor;
+ if (tracker == null)
+ {
+ return;
+ }
+ //shamelessly ripped from MechanitorBandwithGizo.
+ int totalBandwidth = tracker.TotalBandwidth;
+ int usedBandwidth = tracker.UsedBandwidth;
+ string text = usedBandwidth.ToString("F0") + " / " + totalBandwidth.ToString("F0");
+ TaggedString taggedString = "Bandwidth".Translate().Colorize(ColoredText.TipSectionTitleColor) + ": " + text + "\n\n" + "BandwidthGizmoTip".Translate();
+ int usedBandwidthFromSubjects = tracker.UsedBandwidthFromSubjects;
+ if (usedBandwidthFromSubjects > 0)
+ {
+ taggedString += (string)("\n\n" + ("BandwidthUsage".Translate() + ": ")) + usedBandwidthFromSubjects;
+ IEnumerable entries = from p in tracker.OverseenPawns
+ where !p.IsGestating()
+ group p by p.kindDef into p
+ select (string)(p.Key.LabelCap + " x") + p.Count() + " (+" + p.Sum((Pawn mech) => mech.GetStatValue(StatDefOf.BandwidthCost)) + ")";
+ taggedString += "\n\n" + entries.ToLineList(" - ");
+ }
+ int usedBandwidthFromGestation = tracker.UsedBandwidthFromGestation;
+ if (usedBandwidthFromGestation > 0)
+ {
+ taggedString += (string)("\n\n" + "MechGestationBandwidthUsed".Translate() + ": ") + usedBandwidthFromGestation;
+ IEnumerable entries2 = from p in tracker.OverseenPawns
+ where p.IsGestating()
+ group p by p.kindDef into p
+ select (string)(p.Key.LabelCap + " x") + p.Count() + " (+" + p.Sum((Pawn mech) => mech.GetStatValue(StatDefOf.BandwidthCost)) + ")";
+ taggedString += "\n\n" + entries2.ToLineList(" - ");
+ }
+ TooltipHandler.TipRegion(rect, taggedString);
+
+ int num = Mathf.Max(usedBandwidth, totalBandwidth);
+
+ int num2 = 2;
+ int num3 = Mathf.FloorToInt(rect.height / (float)num2);
+ int num4 = Mathf.FloorToInt(rect.width / (float)num3);
+ int num5 = 0;
+ while (num2 * num4 < num)
+ {
+ num2++;
+ num3 = Mathf.FloorToInt(rect.height / (float)num2);
+ num4 = Mathf.FloorToInt(rect.width / (float)num3);
+ num5++;
+ if (num5 >= 1000)
+ {
+ Debug.LogError("Failed to fit bandwidth cells into rect.");
+ return;
+ }
+ }
+ int num6 = Mathf.FloorToInt(rect.width / (float)num3);
+ int num7 = num2;
+ float num8 = (rect.width - (float)(num6 * num3)) / 2f;
+ int num9 = 0;
+ int usedBandwidthFromGestation2 = tracker.UsedBandwidthFromGestation;
+ int num10 = ((num7 <= 2) ? 4 : 2);
+ for (int i = 0; i < num7; i++)
+ {
+ for (int j = 0; j < num6; j++)
+ {
+ num9++;
+ Rect rect5 = new Rect(rect.x + (float)(j * num3) + num8, rect.y + (float)(i * num3), num3, num3).ContractedBy(2f);
+ if (num9 <= num)
+ {
+ if (num9 <= usedBandwidthFromGestation2)
+ {
+ Widgets.DrawRectFast(rect5, EmptyBlockColor);
+ Widgets.DrawRectFast(rect5.ContractedBy(num10), FilledBlockColor);
+ }
+ else if (num9 <= usedBandwidth)
+ {
+ Widgets.DrawRectFast(rect5, (num9 <= totalBandwidth) ? FilledBlockColor : ExcessBlockColor);
+ }
+ else
+ {
+ Widgets.DrawRectFast(rect5, EmptyBlockColor);
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_Bleedrate.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_Bleedrate.cs
new file mode 100644
index 0000000..f053391
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_Bleedrate.cs
@@ -0,0 +1,54 @@
+namespace Numbers
+{
+ using RimWorld;
+ using UnityEngine;
+ using Verse;
+
+ public class PawnColumnWorker_Bleedrate : PawnColumnWorker_Text
+ {
+ private static readonly Color MediumPainColor = new(0.9f, 0.9f, 0f);
+
+ private static readonly Color SeverePainColor = new(0.9f, 0.5f, 0f);
+
+ public override int Compare(Pawn a, Pawn b)
+ => a.health.hediffSet.BleedRateTotal.CompareTo(b.health.hediffSet.BleedRateTotal);
+
+ public override void DoCell(Rect rect, Pawn pawn, PawnTable table)
+ {
+ GUI.color = FindPrettyColourForBleedingPawn(pawn);
+ base.DoCell(rect, pawn, table);
+ GUI.color = Color.white;
+ }
+
+ protected override string GetTextFor(Pawn pawn)
+ => pawn.health.hediffSet.BleedRateTotal.ToStringPercent();
+
+ protected override string GetTip(Pawn pawn)
+ {
+ int ticksUntilDeathDueToBloodLoss = HealthUtility.TicksUntilDeathDueToBloodLoss(pawn);
+
+ if (ticksUntilDeathDueToBloodLoss < GenDate.TicksPerDay)
+ return "TimeToDeath".Translate(ticksUntilDeathDueToBloodLoss.ToStringTicksToPeriod());
+
+ return "WontBleedOutSoon".Translate();
+ }
+
+ private Color FindPrettyColourForBleedingPawn(Pawn pawn)
+ {
+ float isThisMyBlood = pawn.health.hediffSet.BleedRateTotal;
+
+ if (isThisMyBlood <= 0f)
+ return HealthUtility.GoodConditionColor;
+
+ if (isThisMyBlood < 0.8f)
+ return Color.gray;
+
+ if (isThisMyBlood < 1f && !Mathf.Approximately(isThisMyBlood, 1f))
+ return MediumPainColor;
+
+ return Mathf.Approximately(isThisMyBlood, 1f)
+ ? SeverePainColor
+ : HealthUtility.RedColor;
+ }
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_Capacity.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_Capacity.cs
new file mode 100644
index 0000000..d03c9fc
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_Capacity.cs
@@ -0,0 +1,32 @@
+namespace Numbers
+{
+ using RimWorld;
+ using UnityEngine;
+ using Verse;
+
+ public class PawnColumnWorker_Capacity : PawnColumnWorker_Text
+ {
+ protected override string GetTextFor(Pawn pawn)
+ => pawn.health.capacities.GetLevel(def.Ext().capacity).ToStringPercent();
+
+ protected override string GetTip(Pawn pawn)
+ => HealthCardUtility.GetPawnCapacityTip(pawn, def.Ext().capacity);
+
+ public override void DoCell(Rect rect, Pawn pawn, PawnTable table)
+ {
+ GUI.color = HealthCardUtility.GetEfficiencyLabel(pawn, def.Ext().capacity).Second;
+ base.DoCell(rect, pawn, table);
+ GUI.color = Color.white;
+ }
+
+ public override int GetMinWidth(PawnTable table)
+ => base.GetMinWidth(table) + 8; //based on Sight column.
+
+ public override int Compare(Pawn a, Pawn b)
+ => a.health.capacities.GetLevel(def.Ext().capacity)
+ .CompareTo(b.health.capacities.GetLevel(def.Ext().capacity));
+
+ public override int GetMinHeaderHeight(PawnTable table)
+ => Mathf.CeilToInt(Text.CalcSize(Numbers_Utility.WordWrapAt(def.LabelCap, GetMinWidth(table))).y);
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_DiseaseProgression.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_DiseaseProgression.cs
new file mode 100644
index 0000000..d3c9a19
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_DiseaseProgression.cs
@@ -0,0 +1,183 @@
+namespace Numbers
+{
+ using System.Collections.Generic;
+ using System.Linq;
+ using HarmonyLib;
+ using RimWorld;
+ using UnityEngine;
+ using Verse;
+
+ [StaticConstructorOnStartup]
+ public class PawnColumnWorker_DiseaseProgression : PawnColumnWorker
+ {
+ private static readonly Color SeverePainColor = new(0.9f, 0.5f, 0f);
+
+ //[TweakValue("AAAADiseaseProgression")] //assumes a perfectly square icon.
+ public static float MaxIconSize = 22f;
+
+ public override void DoCell(Rect rect, Pawn pawn, PawnTable table)
+ {
+ if (pawn.Dead || pawn.health?.hediffSet == null || !pawn.health.hediffSet.HasImmunizableNotImmuneHediff())
+ return;
+
+ HediffWithComps mostSevere = FindMostSevereHediff(pawn);
+
+ if (mostSevere == null)
+ return;
+
+ float deltaSeverity = GetTextFor(mostSevere);
+
+ GUI.color = GetPrettyColorFor(deltaSeverity);
+
+ Rect rect2 = new(rect.x, rect.y, rect.width, Mathf.Min(rect.height, 30f));
+ Text.Font = GameFont.Small;
+ Text.Anchor = TextAnchor.MiddleCenter;
+ Text.WordWrap = false;
+ Widgets.Label(rect2, GetTextFor(mostSevere).ToStringPercent());
+ Text.WordWrap = true;
+ Text.Anchor = TextAnchor.UpperLeft;
+ string tip = GetTip(pawn, mostSevere);
+ if (!tip.NullOrEmpty())
+ {
+ TooltipHandler.TipRegion(rect2, tip);
+ }
+
+ float severityChangePerDayFromImmu = (float)AccessTools.Method(typeof(HediffComp_Immunizable), "SeverityChangePerDay")
+ .Invoke(mostSevere.TryGetComp(), null);
+
+ float severityChangePerDayFromTendD = 0f;
+
+ if (mostSevere.TryGetComp()?.IsTended ?? false)
+ {
+ severityChangePerDayFromTendD =
+ mostSevere.TryGetComp().TProps.severityPerDayTended *
+ mostSevere.TryGetComp().tendQuality;
+ }
+
+ float immunityPerDay = 0f;
+
+ ImmunityRecord immunityRecord = pawn.health.immunity.GetImmunityRecord(mostSevere.def);
+ if (immunityRecord != null)
+ immunityPerDay = immunityRecord.ImmunityChangePerTick(pawn, true, mostSevere) * GenDate.TicksPerDay;
+
+ GUI.color = Color.white;
+
+ bool redFlag = !(severityChangePerDayFromTendD + severityChangePerDayFromImmu > immunityPerDay);
+
+ Texture2D texture2D = redFlag ? StaticConstructorOnGameStart.SortingIcon : StaticConstructorOnGameStart.SortingDescendingIcon;
+ GUI.color = redFlag ? HealthUtility.GoodConditionColor : HealthUtility.RedColor;
+ Rect position2 = new(rect.xMax - texture2D.width - 1f, rect.yMax - texture2D.height - 1f, texture2D.width, texture2D.height);
+ GUI.DrawTexture(position2, texture2D);
+ GUI.color = Color.white;
+ }
+
+ public override void DoHeader(Rect rect, PawnTable table)
+ {
+ Texture2D skull = StaticConstructorOnGameStart.IconDead;
+ Texture2D immune = StaticConstructorOnGameStart.IconImmune;
+
+ //[ [x] | [] ]
+ float oneFourthLeftCenteredOnIconWidth = (rect.width / 6) - MaxIconSize / 2;
+ //[ [] | [x] ]
+ float oneFourthRightCenteredOnIconWidth = (rect.width - (rect.width / 6)) - MaxIconSize / 2;
+
+ //skull vs immunity icon. One left, one right.
+ Rect skullPosition = new(rect.x + oneFourthLeftCenteredOnIconWidth, rect.yMax - MaxIconSize, MaxIconSize, MaxIconSize);
+ GUI.DrawTexture(skullPosition, skull);
+
+ Rect immunePosition = new(rect.x + oneFourthRightCenteredOnIconWidth, rect.yMax - MaxIconSize, MaxIconSize, MaxIconSize);
+ GUI.DrawTexture(immunePosition.ScaledBy(0.8f), immune);
+
+ Rect rect2 = rect;
+ rect2.y += 3f;
+ Text.Anchor = TextAnchor.LowerCenter;
+ Widgets.Label(rect2, "vs");
+ Text.Anchor = TextAnchor.UpperLeft;
+
+ //rest is copypasta
+ if (table.SortingBy == def)
+ {
+ Texture2D texture2D = (!table.SortingDescending) ? StaticConstructorOnGameStart.SortingIcon : StaticConstructorOnGameStart.SortingDescendingIcon;
+ Rect position2 = new(rect.xMax - texture2D.width - 1f, rect.yMax - texture2D.height - 1f, texture2D.width, texture2D.height);
+ GUI.DrawTexture(position2, texture2D);
+ }
+ if (def.HeaderInteractable)
+ {
+ Rect interactableHeaderRect = GetInteractableHeaderRect(rect, table);
+ Widgets.DrawHighlightIfMouseover(interactableHeaderRect);
+ if (interactableHeaderRect.Contains(Event.current.mousePosition))
+ {
+ string headerTip = GetHeaderTip(table);
+ if (!headerTip.NullOrEmpty())
+ {
+ TooltipHandler.TipRegion(interactableHeaderRect, headerTip);
+ }
+ }
+ if (Widgets.ButtonInvisible(interactableHeaderRect))
+ {
+ HeaderClicked(rect, table);
+ }
+ }
+ }
+
+ public override int GetMinWidth(PawnTable table)
+ => def.width;
+
+ private string GetTip(Pawn pawn, HediffWithComps severe)
+ => severe.LabelCap + ": " + severe.SeverityLabel + "\n" + severe.TipStringExtra;
+
+ private float GetTextFor(HediffWithComps hediff)
+ => hediff?.TryGetComp().Immunity - hediff?.Severity ?? -1f; //nullcheck for Comparer.
+
+ private Color GetPrettyColorFor(float deltaSeverity)
+ {
+ if (deltaSeverity <= -0.4f)
+ return HealthUtility.RedColor;
+
+ if (deltaSeverity <= -0.2f)
+ return SeverePainColor;
+
+ if (deltaSeverity <= -0.1f)
+ return HealthUtility.ImpairedColor;
+
+ if (deltaSeverity <= -0.05f)
+ return HealthUtility.SlightlyImpairedColor;
+
+ if (deltaSeverity <= 0.05f)
+ return Color.gray;
+
+ return HealthUtility.GoodConditionColor;
+ }
+
+ private HediffWithComps FindMostSevereHediff(Pawn pawn)
+ {
+ IEnumerable tmplist =
+ pawn.health.hediffSet.hediffs.Where(x => x.Visible && x is HediffWithComps && !x.FullyImmune()).Cast();
+
+ float delta = float.MinValue;
+ HediffWithComps mostSevereHediff = null;
+
+ foreach (HediffWithComps hediff in tmplist)
+ {
+ HediffComp_Immunizable hediffCompImmunizable = hediff.TryGetComp();
+
+ if (hediffCompImmunizable == null)
+ continue;
+
+ if (hediffCompImmunizable.Immunity - hediff.Severity > delta)
+ {
+ delta = hediffCompImmunizable.Immunity - hediff.Severity;
+ mostSevereHediff = hediff;
+ }
+ }
+
+ return mostSevereHediff;
+ }
+
+ public override int GetMinHeaderHeight(PawnTable table)
+ => Mathf.CeilToInt(Text.CalcSize(def.LabelCap.Resolve().WordWrapAt(GetMinWidth(table))).y);
+
+ public override int Compare(Pawn a, Pawn b)
+ => GetTextFor(FindMostSevereHediff(a)).CompareTo(GetTextFor(FindMostSevereHediff(b)));
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_Entropy.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_Entropy.cs
new file mode 100644
index 0000000..0cfceb6
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_Entropy.cs
@@ -0,0 +1,48 @@
+namespace Numbers
+{
+ using RimWorld;
+ using UnityEngine;
+ using Verse;
+
+ [StaticConstructorOnStartup]
+ public class PawnColumnWorker_Entropy : PawnColumnWorker
+ {
+ private static readonly Texture2D EntropyBarTex = SolidColorMaterials.NewSolidColorTexture(new Color(0.46f, 0.34f, 0.35f));
+
+ //mostly from PawnColumnWorker_Need
+
+ public override void DoCell(Rect rect, Pawn pawn, PawnTable table)
+ {
+ if (!pawn.HasPsylink)
+ return;
+
+ float curEntropyLevel = pawn.psychicEntropy.EntropyRelativeValue;
+
+ float barHeight = 14f;
+ float barWidth = barHeight + 15f;
+ if (rect.height < 50f)
+ {
+ barHeight *= Mathf.InverseLerp(0f, 50f, rect.height);
+ }
+
+ Text.Font = (rect.height <= 55f) ? GameFont.Tiny : GameFont.Small;
+ Text.Anchor = TextAnchor.UpperLeft;
+ Rect rect3 = new(rect.x, rect.y + rect.height / 2f, rect.width, rect.height / 2f);
+ rect3 = new Rect(rect3.x + barWidth, rect3.y, rect3.width - barWidth * 2f, rect3.height - barHeight);
+
+ Widgets.FillableBar(rect3, curEntropyLevel, EntropyBarTex);
+
+ Text.Font = GameFont.Small;
+ }
+
+ public override int GetMinWidth(PawnTable table)
+ => Mathf.Max(base.GetMinWidth(table), 110);
+
+ public override int Compare(Pawn a, Pawn b)
+ {
+ int hasPsylink = a.HasPsylink.CompareTo(b.HasPsylink);
+ if (hasPsylink != 0) { return hasPsylink; }
+ return (a.psychicEntropy?.EntropyRelativeValue ?? 0f).CompareTo(b.psychicEntropy?.EntropyRelativeValue ?? 0f);
+ }
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_Faction.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_Faction.cs
new file mode 100644
index 0000000..890a3ff
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_Faction.cs
@@ -0,0 +1,18 @@
+namespace Numbers
+{
+ using RimWorld;
+ using UnityEngine;
+ using Verse;
+
+ class PawnColumnWorker_Faction : PawnColumnWorker_Text
+ {
+ protected override string GetTextFor(Pawn pawn)
+ => pawn.Faction?.Name;
+
+ public override int GetMinWidth(PawnTable table)
+ => Mathf.Max(base.GetMinWidth(table), 150);
+
+ public override int Compare(Pawn a, Pawn b)
+ => GetTextFor(a).CompareTo(GetTextFor(b));
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_FoodConsumption.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_FoodConsumption.cs
new file mode 100644
index 0000000..08a70db
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_FoodConsumption.cs
@@ -0,0 +1,16 @@
+using RimWorld;
+using Verse;
+
+namespace Numbers
+{
+ public class PawnColumnWorker_FoodConsumption : PawnColumnWorker_Text
+ {
+ protected override string GetTextFor(Pawn pawn)
+ => (pawn?.needs?.food?.FoodFallPerTickAssumingCategory(HungerCategory.Fed) * 60000f ?? 0).ToString("0.##");
+
+ public override int Compare(Pawn a, Pawn b)
+ => (a?.needs?.food?.FoodFallPerTickAssumingCategory(HungerCategory.Fed) * 60000f ?? 0)
+ .CompareTo(
+ b?.needs?.food?.FoodFallPerTickAssumingCategory(HungerCategory.Fed) * 60000f ?? 0);
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_Forbidden.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_Forbidden.cs
new file mode 100644
index 0000000..19fc80d
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_Forbidden.cs
@@ -0,0 +1,15 @@
+namespace Numbers
+{
+ using RimWorld;
+ using Verse;
+
+ public class PawnColumnWorker_Forbidden : PawnColumnWorker_Checkbox
+ {
+ protected override bool GetValue(Pawn pawn) => ((Thing)pawn.ParentHolder).IsForbidden(Faction.OfPlayer);
+
+ protected override void SetValue(Pawn pawn, bool value, PawnTable table)
+ {
+ ((Thing)pawn.ParentHolder).SetForbidden(value);
+ }
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_Gear.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_Gear.cs
new file mode 100644
index 0000000..7505e37
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_Gear.cs
@@ -0,0 +1,102 @@
+namespace Numbers
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using RimWorld;
+ using UnityEngine;
+ using Verse;
+ using Verse.AI;
+
+ public class PawnColumnWorker_Equipment : PawnColumnWorker
+ {
+ private int width;
+ private static readonly int baseWidth = 6 * 28; //6 boxes, 28 wide each.
+ private const float gWidth = 28f;
+ private const float gHeight = 28f;
+
+ public override void DoCell(Rect rect, Pawn pawn, PawnTable table)
+ {
+ GUI.BeginGroup(rect);
+
+ float x = 0;
+
+ if (pawn.equipment != null)
+ {
+ foreach (ThingWithComps thing in pawn.equipment.AllEquipmentListForReading)
+ {
+ Rect rect2 = new(x, 0, gWidth, gHeight);
+ DrawThing(rect2, thing, pawn);
+ x += gWidth;
+ }
+ }
+
+ if (pawn.apparel != null)
+ {
+ foreach (Apparel thing in pawn.apparel.WornApparel.OrderByDescending(ap => ap.def.apparel.bodyPartGroups[0].listOrder))
+ {
+ Rect rect2 = new(x, 0, gWidth, gHeight);
+ DrawThing(rect2, thing, pawn);
+ x += gWidth;
+ if (x > width)
+ width = (int)x;
+ }
+ }
+
+ GUI.EndGroup();
+ }
+
+ public override int GetMinWidth(PawnTable table)
+ => Mathf.Max(width, baseWidth);
+
+ private void DrawThing(Rect rect, Thing thing, Pawn selPawn)
+ {
+ if (Widgets.ButtonInvisible(rect) && Event.current.button == 1)
+ {
+ List list =
+ [
+ new FloatMenuOption("ThingInfo".Translate(), () => Find.WindowStack.Add(new Dialog_InfoCard(thing)))
+ ];
+
+ if (selPawn.IsColonistPlayerControlled)
+ {
+ Action dropAction = DropThing(thing, selPawn);
+ list.Add(new FloatMenuOption("DropThing".Translate(), dropAction));
+ }
+ FloatMenu window = new(list, thing.LabelCap);
+ Find.WindowStack.Add(window);
+ }
+ GUI.BeginGroup(rect);
+ if (thing.def.DrawMatSingle?.mainTexture != null)
+ {
+ Widgets.ThingIcon(new Rect(3f, 3f, 27f, 27f), thing);
+ }
+ GUI.EndGroup();
+ TooltipHandler.TipRegion(rect, new TipSignal(thing.LabelCap));
+ }
+
+ private static Action DropThing(Thing thing, Pawn selPawn)
+ {
+ if (thing is Apparel ap && selPawn.apparel != null && selPawn.apparel.WornApparel.Contains(ap))
+ {
+ return () => selPawn.jobs.TryTakeOrderedJob(new Job(JobDefOf.RemoveApparel, ap));
+ }
+
+ if (thing is ThingWithComps eq && selPawn.equipment.AllEquipmentListForReading.Contains(eq))
+ {
+ return() => selPawn.jobs.TryTakeOrderedJob(new Job(JobDefOf.DropEquipment, eq));
+ }
+
+ if (!thing.def.destroyOnDrop)
+ {
+ return () => selPawn.inventory.innerContainer.TryDrop(thing, selPawn.Position, selPawn.Map, ThingPlaceMode.Near, out Thing unused);
+ }
+
+ return null;
+ }
+
+ public override int Compare(Pawn a, Pawn b)
+ => (a.equipment.HasAnything() ? a.equipment.AllEquipmentListForReading.First().LabelCap : string.Empty)
+ .CompareTo(b.equipment.HasAnything() ? b.equipment.AllEquipmentListForReading.First().LabelCap : string.Empty);
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_Genes.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_Genes.cs
new file mode 100644
index 0000000..2d5acf6
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_Genes.cs
@@ -0,0 +1,23 @@
+namespace Numbers
+{
+ using System.Linq;
+ using RimWorld;
+ using Verse;
+
+ //todo: probably better with icons to hover over for a tooltip or smth
+ public class PawnColumnWorker_Endogenes : PawnColumnWorker_Text
+ {
+ protected override string GetTextFor(Pawn pawn)
+ {
+ return pawn.genes?.Endogenes?.Select(gene => gene.Label).ToCommaList();
+ }
+ }
+
+ public class PawnColumnWorker_Xenogenes : PawnColumnWorker_Text
+ {
+ protected override string GetTextFor(Pawn pawn)
+ {
+ return pawn.genes?.Xenogenes?.Select(gene => gene.Label).ToCommaList();
+ }
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_GenesRegrowTime.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_GenesRegrowTime.cs
new file mode 100644
index 0000000..be131b8
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_GenesRegrowTime.cs
@@ -0,0 +1,45 @@
+namespace Numbers
+{
+ using RimWorld;
+ using Verse;
+
+ //todo: probably better with icons to hover over for a tooltip or smth
+ public class PawnColumnWorker_GenesRegrowTime : PawnColumnWorker_Text
+ {
+ private int GetTicksRemaining(Pawn pawn)
+ {
+ var hediff = pawn.health.hediffSet.GetFirstHediffOfDef(HediffDefOf.XenogermReplicating);
+
+ if (hediff == null)
+ {
+ return 0;
+ }
+
+ var disappears = hediff.TryGetComp();
+
+ if (disappears == null)
+ {
+ return 0;
+ }
+
+ return disappears.ticksToDisappear;
+ }
+
+ protected override string GetTextFor(Pawn pawn)
+ {
+ var ticks = GetTicksRemaining(pawn);
+
+ if (ticks == 0)
+ {
+ return null;
+ }
+
+ return ticks.ToStringTicksToPeriod(shortForm: true);
+ }
+
+
+ public override int Compare(Pawn a, Pawn b)
+ => this.GetTicksRemaining(a).CompareTo(this.GetTicksRemaining(b));
+ }
+
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_Guilt.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_Guilt.cs
new file mode 100644
index 0000000..9d9ba38
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_Guilt.cs
@@ -0,0 +1,24 @@
+namespace Numbers
+{
+ using RimWorld;
+ using UnityEngine;
+ using Verse;
+
+ public class PawnColumnWorker_Guilt : PawnColumnWorker_Icon
+ {
+ protected override string GetIconTip(Pawn pawn)
+ {
+ return pawn.guilt?.Tip;
+ }
+
+ public override int Compare(Pawn a, Pawn b)
+ {
+ return a.guilt?.TicksUntilInnocent.CompareTo(b.guilt?.TicksUntilInnocent) ?? base.Compare(a, b);
+ }
+
+ protected override Texture2D GetIconFor(Pawn pawn)
+ {
+ return (pawn.guilt?.IsGuilty ?? false) ? TexUI.GuiltyTex : null;
+ }
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_HediffIsBad.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_HediffIsBad.cs
new file mode 100644
index 0000000..e9f9e31
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_HediffIsBad.cs
@@ -0,0 +1,14 @@
+namespace Numbers
+{
+ using System.Collections.Generic;
+ using System.Linq;
+ using Verse;
+
+ public class PawnColumnWorker_HediffIsBad : PawnColumnWorker_AllHediffs
+ {
+ protected override IEnumerable VisibleHediffs(Pawn pawn)
+ {
+ return base.VisibleHediffs(pawn).Where(x => x.def.isBad);
+ }
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_Ideology.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_Ideology.cs
new file mode 100644
index 0000000..561fe48
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_Ideology.cs
@@ -0,0 +1,28 @@
+namespace Numbers
+{
+ using RimWorld;
+ using UnityEngine;
+ using Verse;
+
+ public class PawnColumnWorker_Ideology : PawnColumnWorker
+ {
+ public override void DoCell(Rect rect, Pawn pawn, PawnTable table)
+ {
+ if (pawn?.Ideo == null)
+ {
+ return;
+ }
+ IdeoUIUtility.DrawIdeoPlate(rect, pawn.Ideo, pawn);
+ }
+
+ public override int GetMinWidth(PawnTable table)
+ {
+ return Mathf.Max(base.GetMinWidth(table), 140);
+ }
+
+ public override int Compare(Pawn a, Pawn b)
+ {
+ return (a.ideo?.Certainty ?? 0).CompareTo(b.ideo?.Certainty ?? 0);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_Inspiration.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_Inspiration.cs
new file mode 100644
index 0000000..6d4b30c
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_Inspiration.cs
@@ -0,0 +1,26 @@
+namespace Numbers
+{
+ using RimWorld;
+ using UnityEngine;
+ using Verse;
+
+ public class PawnColumnWorker_Inspiration : PawnColumnWorker_Text
+ {
+ protected override string GetTextFor(Pawn pawn)
+ => pawn.InspirationDef?.LabelCap;
+
+ public override int Compare(Pawn a, Pawn b)
+ => (a.Inspired ? a.InspirationDef.GetHashCode() : int.MinValue)
+ .CompareTo(b.Inspired ? b.InspirationDef.GetHashCode() : int.MinValue);
+
+ protected override string GetTip(Pawn pawn)
+ {
+ int? inspirationTimeRemaining = (int?)((pawn.InspirationDef?.baseDurationDays - pawn.Inspiration?.AgeDays) * GenDate.TicksPerDay);
+
+ return inspirationTimeRemaining.HasValue ? "ExpiresIn".Translate().Resolve() + ": " + inspirationTimeRemaining.Value.ToStringTicksToPeriod() : string.Empty;
+ }
+
+ public override int GetMinWidth(PawnTable table)
+ => Mathf.Max(base.GetMinWidth(table), 130);
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_Inventory.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_Inventory.cs
new file mode 100644
index 0000000..07f3174
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_Inventory.cs
@@ -0,0 +1,78 @@
+namespace Numbers
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using RimWorld;
+ using UnityEngine;
+ using Verse;
+
+ public class PawnColumnWorker_Inventory : PawnColumnWorker
+ {
+ private int width;
+ private static readonly int baseWidth = 3 * 28; //3 boxes, 28 wide each.
+
+ public override void DoCell(Rect rect, Pawn pawn, PawnTable table)
+ {
+ if (pawn.inventory?.innerContainer == null)
+ return;
+
+ GUI.BeginGroup(rect);
+ float x = 0;
+ float gWidth = 28f;
+ float gHeight = 28f;
+
+ foreach (Thing thing in pawn.inventory.innerContainer)
+ {
+ Rect rect2 = new(x, 0, gWidth, gHeight);
+ DrawThing(rect2, thing, pawn);
+ x += gWidth;
+ if (x > width)
+ width = (int)x;
+ }
+ GUI.EndGroup();
+ }
+
+ public override int GetMinWidth(PawnTable table) => Mathf.Max(width, baseWidth, base.GetMinWidth(table));
+
+ private void DrawThing(Rect rect, Thing thing, Pawn selPawn)
+ {
+ if (Widgets.ButtonInvisible(rect) && Event.current.button == 1)
+ {
+ List list =
+ [
+ new FloatMenuOption("ThingInfo".Translate(), () => Find.WindowStack.Add(new Dialog_InfoCard(thing)))
+ ];
+
+ if (selPawn.IsColonistPlayerControlled)
+ {
+ Action action = null;
+
+ if (!thing.def.destroyOnDrop)
+ {
+ action = delegate
+ {
+ selPawn.inventory.innerContainer.TryDrop(thing, selPawn.Position, selPawn.Map, ThingPlaceMode.Near, out Thing unused);
+ };
+ }
+ list.Add(new FloatMenuOption("DropThing".Translate(), action));
+ }
+ FloatMenu window = new(list, thing.LabelCap);
+ Find.WindowStack.Add(window);
+ }
+
+ GUI.BeginGroup(rect);
+ if (thing.def.DrawMatSingle?.mainTexture != null)
+ {
+ Widgets.ThingIcon(new Rect(3f, 3f, 27f, 27f), thing);
+ }
+ GUI.EndGroup();
+ TooltipHandler.TipRegion(rect, new TipSignal(thing.LabelCap));
+ }
+
+ public override int Compare(Pawn a, Pawn b)
+ => (a.inventory?.innerContainer?.Count() ?? -1)
+ .CompareTo(b.inventory?.innerContainer?.Count() ?? -1);
+ }
+}
+
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_InventoryDropAll.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_InventoryDropAll.cs
new file mode 100644
index 0000000..93c38c6
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_InventoryDropAll.cs
@@ -0,0 +1,26 @@
+using RimWorld;
+using UnityEngine;
+using Verse;
+
+namespace Numbers
+{
+ public class PawnColumnWorker_InventoryDropAll : PawnColumnWorker_Icon
+ {
+ protected override Texture2D GetIconFor(Pawn pawn)
+ {
+ var playerControlled = pawn.Faction != null && (pawn.IsColonistPlayerControlled || pawn.IsPrisonerOfColony || (pawn.AnimalOrWildMan() && pawn.Faction.IsPlayer));
+
+ return playerControlled && pawn.inventory.FirstUnloadableThing != default ? StaticConstructorOnGameStart.Drop : null;
+ }
+
+ protected override void ClickedIcon(Pawn pawn)
+ {
+ var playerControlled = pawn.Faction != null && (pawn.IsColonistPlayerControlled || pawn.IsPrisonerOfColony || (pawn.AnimalOrWildMan() && pawn.Faction.IsPlayer));
+
+ if (playerControlled && pawn.inventory.FirstUnloadableThing != default)
+ {
+ pawn.inventory.DropAllNearPawn(pawn.PositionHeld);
+ }
+ }
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_JobCurrent.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_JobCurrent.cs
new file mode 100644
index 0000000..4ef8fd6
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_JobCurrent.cs
@@ -0,0 +1,35 @@
+namespace Numbers
+{
+ using RimWorld;
+ using UnityEngine;
+ using Verse;
+
+ public class PawnColumnWorker_JobCurrent : PawnColumnWorker_Text
+ {
+ protected override string GetTextFor(Pawn pawn)
+ {
+ if (!Numbers_Settings.showMoreInfoThanVanilla && !(pawn.Faction == Faction.OfPlayer || pawn.HostFaction == Faction.OfPlayer) && !pawn.InMentalState)
+ return null;
+
+ if (pawn.jobs.curDriver != null)
+ {
+ string text = pawn.jobs.curDriver.GetReport().CapitalizeFirst();
+ GenText.SetTextSizeToFit(text, new Rect(0f, 0f, Mathf.CeilToInt(Text.CalcSize(def.LabelCap).x), GetMinCellHeight(pawn)));
+
+ return text;
+ }
+ return null;
+ }
+
+ protected override string GetTip(Pawn pawn) => GetTextFor(pawn);
+
+ public override int Compare(Pawn a, Pawn b)
+ => (a.jobs?.curDriver.GetReport()).CompareTo(b.jobs?.curDriver.GetReport());
+
+ public override int GetMinWidth(PawnTable table)
+ => Mathf.Max(base.GetMinWidth(table), 200);
+
+ public override int GetMinHeaderHeight(PawnTable table)
+ => Mathf.CeilToInt(Text.CalcSize(Numbers_Utility.WordWrapAt(def.LabelCap, GetMinWidth(table))).y);
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_JobQueued.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_JobQueued.cs
new file mode 100644
index 0000000..e78a995
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_JobQueued.cs
@@ -0,0 +1,35 @@
+namespace Numbers
+{
+ using System.Linq;
+ using RimWorld;
+ using UnityEngine;
+ using Verse;
+
+ public class PawnColumnWorker_JobQueued : PawnColumnWorker_Text
+ {
+ protected override string GetTextFor(Pawn pawn)
+ {
+ if (pawn.jobs?.jobQueue.Any() ?? false)
+ {
+ string text = pawn.jobs.jobQueue[0].job.GetReport(pawn).CapitalizeFirst();
+
+ GenText.SetTextSizeToFit(text, new Rect(0f, 0f, Mathf.CeilToInt(Text.CalcSize(def.LabelCap).x), GetMinCellHeight(pawn)));
+
+ return text;
+ }
+ return null;
+ }
+
+ public override int Compare(Pawn a, Pawn b)
+ => (a.jobs?.jobQueue?.Count ?? 0).CompareTo(b.jobs?.jobQueue?.Count ?? 0);
+
+ protected override string GetTip(Pawn pawn)
+ => pawn.jobs?.jobQueue?.Count.ToString();
+
+ public override int GetMinWidth(PawnTable table)
+ => Mathf.Max(base.GetMinWidth(table), 200);
+
+ public override int GetMinHeaderHeight(PawnTable table)
+ => Mathf.CeilToInt(Text.CalcSize(Numbers_Utility.WordWrapAt(def.LabelCap, GetMinWidth(table))).y);
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_LeatherType.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_LeatherType.cs
new file mode 100644
index 0000000..bcc263f
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_LeatherType.cs
@@ -0,0 +1,13 @@
+using RimWorld;
+using Verse;
+
+namespace Numbers
+{
+ public class PawnColumnWorker_LeatherType : PawnColumnWorker_Text
+ {
+ protected override string GetTextFor(Pawn pawn)
+ {
+ return pawn.RaceProps?.leatherDef?.LabelCap;
+ }
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_Meditation.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_Meditation.cs
new file mode 100644
index 0000000..7acb205
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_Meditation.cs
@@ -0,0 +1,17 @@
+using RimWorld;
+using UnityEngine;
+using Verse;
+
+namespace Numbers
+{
+ public class PawnColumnWorker_Meditation : PawnColumnWorker_Text
+ {
+ protected override string GetTextFor(Pawn pawn)
+ {
+ return MeditationUtility.FocusTypesAvailableForPawnString(pawn);
+ }
+
+ public override int GetMinWidth(PawnTable table)
+ => Mathf.Max(base.GetMinWidth(table), 200);
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_MentalState.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_MentalState.cs
new file mode 100644
index 0000000..42b944c
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_MentalState.cs
@@ -0,0 +1,19 @@
+namespace Numbers
+{
+ using RimWorld;
+ using UnityEngine;
+ using Verse;
+
+ public class PawnColumnWorker_MentalState : PawnColumnWorker_Text
+ {
+ protected override string GetTextFor(Pawn pawn)
+ => pawn.MentalState?.InspectLine ?? string.Empty;
+
+ public override int Compare(Pawn a, Pawn b)
+ => ((int?)a.MentalState?.def?.category ?? -1)
+ .CompareTo((int?)b.MentalState?.def?.category ?? -1);
+
+ public override int GetMinHeaderHeight(PawnTable table)
+ => Mathf.CeilToInt(Text.CalcSize(Numbers_Utility.WordWrapAt(def.LabelCap, GetMinWidth(table))).y);
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_Need.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_Need.cs
new file mode 100644
index 0000000..25b89e8
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_Need.cs
@@ -0,0 +1,101 @@
+namespace Numbers
+{
+ using System.Collections.Generic;
+ using System.Reflection;
+ using RimWorld;
+ using UnityEngine;
+ using Verse;
+
+ [StaticConstructorOnStartup]
+ public class PawnColumnWorker_Need : PawnColumnWorker
+ {
+ private static readonly FieldInfo needThreshPercent = typeof(Need).GetField("threshPercents", BindingFlags.NonPublic | BindingFlags.Instance);
+
+ //mostly from Koisama
+ public override void DoCell(Rect rect, Pawn pawn, PawnTable table)
+ {
+ if (pawn.needs == null)
+ return;
+
+ if (pawn.RaceProps.IsMechanoid)
+ return;
+
+ if (!Numbers_Settings.showMoreInfoThanVanilla && pawn.RaceProps.Animal && pawn.Faction == null)
+ return;
+
+ Need need = pawn.needs.TryGetNeed(def.Ext().need);
+
+ if (need == null)
+ return;
+
+ float barHeight = 14f;
+ float barWidth = barHeight + 15f;
+ if (rect.height < 50f)
+ {
+ barHeight *= Mathf.InverseLerp(0f, 50f, rect.height);
+ }
+
+ Text.Font = (rect.height <= 55f) ? GameFont.Tiny : GameFont.Small;
+ Text.Anchor = TextAnchor.UpperLeft;
+ Rect rect3 = new(rect.x, rect.y + rect.height / 2f, rect.width, rect.height / 2f);
+ rect3 = new Rect(rect3.x + barWidth, rect3.y, rect3.width - barWidth * 2f, rect3.height - barHeight);
+
+ Widgets.FillableBar(rect3, need.CurLevelPercentage);
+ Widgets.FillableBarChangeArrows(rect3, need.GUIChangeArrow);
+
+ List threshPercents = (List)needThreshPercent.GetValue(need);
+ if (threshPercents != null)
+ {
+ foreach (float t in threshPercents)
+ {
+ NeedDrawBarThreshold(rect3, t, need.CurLevelPercentage);
+ }
+ }
+
+ float curInstantLevel = need.CurInstantLevelPercentage;
+ if (curInstantLevel >= 0f)
+ {
+ NeedDrawBarInstantMarkerAt(rect3, curInstantLevel);
+ }
+ Text.Font = GameFont.Small;
+ }
+
+ private void NeedDrawBarThreshold(Rect barRect, float threshPct, float curLevel)
+ {
+ float num = (barRect.width <= 60f) ? 1 : 2;
+ Rect position = new(barRect.x + barRect.width * threshPct - (num - 1f), barRect.y + barRect.height / 2f, num, barRect.height / 2f);
+ Texture2D image;
+ if (threshPct < curLevel)
+ {
+ image = BaseContent.BlackTex;
+ GUI.color = new Color(1f, 1f, 1f, 0.9f);
+ }
+ else
+ {
+ image = BaseContent.GreyTex;
+ GUI.color = new Color(1f, 1f, 1f, 0.5f);
+ }
+ GUI.DrawTexture(position, image);
+ GUI.color = Color.white;
+ }
+
+ private void NeedDrawBarInstantMarkerAt(Rect barRect, float pct)
+ {
+ float seekerSize = 12f;
+ if (barRect.width < 150f)
+ {
+ seekerSize /= 2f;
+ }
+ Vector2 vector = new(barRect.x + barRect.width * pct, barRect.y + barRect.height);
+ Rect position = new(vector.x - seekerSize / 2f, vector.y, seekerSize, seekerSize);
+ GUI.DrawTexture(position, StaticConstructorOnGameStart.BarInstantMarkerTex);
+ }
+
+ public override int GetMinWidth(PawnTable table)
+ => Mathf.Max(base.GetMinWidth(table), 110);
+
+ public override int Compare(Pawn a, Pawn b)
+ => (a.needs?.TryGetNeed(def.Ext().need)?.CurLevel ?? 0)
+ .CompareTo(b.needs?.TryGetNeed(def.Ext().need)?.CurLevel ?? 0);
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_NeedsTreatment.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_NeedsTreatment.cs
new file mode 100644
index 0000000..39479c2
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_NeedsTreatment.cs
@@ -0,0 +1,26 @@
+namespace Numbers
+{
+ using System.Linq;
+ using RimWorld;
+ using UnityEngine;
+ using Verse;
+
+ public class PawnColumnWorker_NeedsTreatment : PawnColumnWorker_Icon
+ {
+ protected override Texture2D GetIconFor(Pawn pawn)
+ => pawn.health.HasHediffsNeedingTendByPlayer()
+ ? StaticConstructorOnGameStart.IconTendedNeed
+ : StaticConstructorOnGameStart.IconTendedWell;
+
+ public override int Compare(Pawn a, Pawn b)
+ => a.health.hediffSet.GetHediffsTendable().Count()
+ .CompareTo(b.health.hediffSet.GetHediffsTendable().Count());
+
+ protected override string GetIconTip(Pawn pawn)
+ => pawn.health.hediffSet.GetHediffsTendable()
+ .Select(x => x.LabelCap).ToCommaList();
+
+ public override int GetMinHeaderHeight(PawnTable table)
+ => Mathf.CeilToInt(Text.CalcSize(Numbers_Utility.WordWrapAt(def.LabelCap, GetMinWidth(table))).y);
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_OperationDropDown.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_OperationDropDown.cs
new file mode 100644
index 0000000..6aede2b
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_OperationDropDown.cs
@@ -0,0 +1,84 @@
+namespace Numbers
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Reflection;
+ using RimWorld;
+ using UnityEngine;
+ using Verse;
+
+ public class PawnColumnWorker_OperationDropDown : PawnColumnWorker
+ {
+ public delegate TResult Func
+ (T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
+
+ public static Func, AcceptanceReport, int, BodyPartRecord, FloatMenuOption>
+ GenerateSurgeryOptionFunc =
+ (Func, AcceptanceReport, int, BodyPartRecord, FloatMenuOption>)Delegate
+ .CreateDelegate(
+ typeof(Func, AcceptanceReport, int, BodyPartRecord, FloatMenuOption>),
+ null,
+ typeof(HealthCardUtility).GetMethod("GenerateSurgeryOption",
+ BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.InvokeMethod) ?? throw new InvalidOperationException("GenerateSurgeryOption is null."));
+
+ public override void DoCell(Rect rect, Pawn pawn, PawnTable table)
+ {
+ if (Widgets.ButtonText(rect, "AddBill".Translate()))
+ {
+ Find.WindowStack.Add(new FloatMenu(RecipeOptionsMaker(pawn)));
+ }
+
+ UIHighlighter.HighlightOpportunity(rect, "AddBill");
+ }
+
+ public override int GetMinCellHeight(Pawn pawn)
+ {
+ return 30;
+ }
+
+ public override int GetMinWidth(PawnTable table)
+ {
+ return 150;
+ }
+
+ private static List RecipeOptionsMaker(Pawn pawn)
+ {
+ List list = [];
+ int num = 0;
+ foreach (RecipeDef current in pawn.def.AllRecipes.Where(x => x.AvailableNow))
+ {
+ var acceptanceReport = current.Worker.AvailableReport(pawn);
+ if (acceptanceReport.Accepted || !acceptanceReport.Reason.NullOrEmpty())
+ {
+ List missingIngredientsList = current.PotentiallyMissingIngredients(null, pawn.Map).ToList();
+
+ if (missingIngredientsList.Count == 0 || (!current.dontShowIfAnyIngredientMissing &&
+ !missingIngredientsList.Any(x => x.isTechHediff || x.IsDrug)))
+ {
+ if (current.targetsBodyPart)
+ {
+ foreach (var item in current.Worker.GetPartsToApplyOn(pawn, current))
+ {
+ if (current.AvailableOnNow(pawn, item))
+ {
+ list.Add(GenerateSurgeryOptionFunc(pawn, pawn, current, missingIngredientsList, acceptanceReport, num, item));
+ num++;
+ }
+ }
+
+ }
+ else
+ {
+ list.Add(GenerateSurgeryOptionFunc(pawn, pawn, current, missingIngredientsList, acceptanceReport, num, null));
+ num++;
+ }
+ }
+
+ }
+ }
+
+ return list;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_Pain.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_Pain.cs
new file mode 100644
index 0000000..4febc32
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_Pain.cs
@@ -0,0 +1,38 @@
+namespace Numbers
+{
+ using System.Collections.Generic;
+ using System.Linq;
+ using RimWorld;
+ using UnityEngine;
+ using Verse;
+
+ public class PawnColumnWorker_Pain : PawnColumnWorker_Text
+ {
+ protected override string GetTextFor(Pawn pawn)
+ => pawn.health.hediffSet.PainTotal.ToStringPercent();
+
+ protected override string GetTip(Pawn pawn)
+ => GetPainTip(pawn);
+
+ public override void DoCell(Rect rect, Pawn pawn, PawnTable table)
+ {
+ GUI.color = HealthCardUtility.GetPainLabel(pawn).Second;
+ base.DoCell(rect, pawn, table);
+ GUI.color = Color.white;
+ }
+
+ private static string GetPainTip(Pawn pawn)
+ => PainCausingHediffs(pawn).ToCommaList();
+
+ private static IEnumerable PainCausingHediffs(Pawn pawn)
+ => pawn.health.hediffSet.hediffs
+ .Where(t => t.PainFactor != 1f || t.PainOffset != 0f)
+ .Select(t => t.Part?.LabelCap + ": " + t.LabelCap);
+
+ public override int GetMinWidth(PawnTable table)
+ => base.GetMinWidth(table) + 12;
+
+ public override int Compare(Pawn a, Pawn b)
+ => a.health.hediffSet.PainTotal.CompareTo(b.health.hediffSet.PainTotal);
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_PrisonerInteraction.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_PrisonerInteraction.cs
new file mode 100644
index 0000000..965b09d
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_PrisonerInteraction.cs
@@ -0,0 +1,76 @@
+namespace Numbers
+{
+ using System.Linq;
+ using RimWorld;
+ using UnityEngine;
+ using Verse;
+ using Verse.Sound;
+
+ public class PawnColumnWorker_PrisonerInteraction : PawnColumnWorker
+ {
+ //for mods, like Prison Labour, that add more interactinodefs.
+ private readonly int width = DefDatabase.DefCount * 30;
+ private bool dragging;
+
+ public override void DoCell(Rect rect, Pawn pawn, PawnTable table)
+ {
+ if (pawn.guest == null)
+ return;
+
+ GUI.BeginGroup(rect);
+ float x = 0f;
+
+ foreach (PrisonerInteractionModeDef current in from prisonerinteractionmode in DefDatabase.AllDefsListForReading
+ orderby prisonerinteractionmode.listOrder
+ select prisonerinteractionmode)
+ {
+ DrawInteractionRadioButton(new Rect(x, 3f, 30f, 30f), pawn, current);
+ TooltipHandler.TipRegion(new Rect(x, 0f, 30f, 30f), new TipSignal(current.LabelCap));
+ x += 30f;
+ }
+ GUI.EndGroup();
+ }
+
+ private void DrawInteractionRadioButton(Rect rect, Pawn pawn, PrisonerInteractionModeDef prisonerInteraction)
+ {
+ //inspired by RimWorld.AreaAllowedGUI.DoAreaSelector(Rect rect, Pawn p, Area area)
+ Widgets.RadioButton(rect.x, rect.y, pawn.guest.ExclusiveInteractionMode == prisonerInteraction);
+ {
+ if (Input.GetMouseButtonUp(0))
+ {
+ dragging = false;
+ }
+ if (!Input.GetMouseButtonDown(0))
+ {
+ dragging = false;
+ }
+ if (Mouse.IsOver(rect))
+ {
+ if (Input.GetMouseButton(0))
+ {
+ dragging = true;
+ pawn.guest.SetExclusiveInteraction(prisonerInteraction);
+ }
+ if (dragging && pawn.guest.ExclusiveInteractionMode != prisonerInteraction)
+ {
+ pawn.guest.SetExclusiveInteraction(prisonerInteraction);
+ SoundDefOf.Designate_DragStandard_Changed.PlayOneShotOnCamera();
+ }
+ if (ModsConfig.IdeologyActive && pawn.guest.ExclusiveInteractionMode == PrisonerInteractionModeDefOf.Convert && pawn.guest.ideoForConversion == null)
+ {
+ pawn.guest.ideoForConversion = Faction.OfPlayer.ideos.PrimaryIdeo;
+ }
+ }
+ }
+ }
+
+ public override int Compare(Pawn a, Pawn b)
+ => (a.guest?.ExclusiveInteractionMode?.listOrder ?? 0).CompareTo(b.guest?.ExclusiveInteractionMode?.listOrder ?? 0);
+
+ public override int GetMinWidth(PawnTable table)
+ => width;
+
+ public override int GetMinHeaderHeight(PawnTable table)
+ => Mathf.CeilToInt(Text.CalcSize(Numbers_Utility.WordWrapAt(def.LabelCap, GetMinWidth(table))).y);
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_PrisonerResistance.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_PrisonerResistance.cs
new file mode 100644
index 0000000..6c18847
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_PrisonerResistance.cs
@@ -0,0 +1,18 @@
+namespace Numbers
+{
+ using RimWorld;
+ using UnityEngine;
+ using Verse;
+
+ public class PawnColumnWorker_PrisonerResistance : PawnColumnWorker_Text
+ {
+ protected override string GetTextFor(Pawn pawn)
+ {
+ return pawn.guest.Resistance.ToString("F1");
+ }
+
+ public override int Compare(Pawn a, Pawn b) => a.guest.Resistance.CompareTo(b.guest.Resistance);
+
+ public override int GetMinHeaderHeight(PawnTable table) => Mathf.CeilToInt(Text.CalcSize(Numbers_Utility.WordWrapAt(def.LabelCap, GetMinWidth(table))).y);
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_Psyfocus.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_Psyfocus.cs
new file mode 100644
index 0000000..9751876
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_Psyfocus.cs
@@ -0,0 +1,59 @@
+namespace Numbers
+{
+ using RimWorld;
+ using UnityEngine;
+ using Verse;
+
+ public class PawnColumnWorker_Psyfocus : PawnColumnWorker
+ {
+ //mostly from PawnColumnWorker_Need
+
+ public override void DoCell(Rect rect, Pawn pawn, PawnTable table)
+ {
+ if (!pawn.HasPsylink)
+ return;
+
+ float curPsyfocusLevel = pawn.psychicEntropy.CurrentPsyfocus;
+ float targetPsyfocusLevel = pawn.psychicEntropy.TargetPsyfocus;
+
+ float barHeight = 14f;
+ float barWidth = barHeight + 15f;
+ if (rect.height < 50f)
+ {
+ barHeight *= Mathf.InverseLerp(0f, 50f, rect.height);
+ }
+
+ Text.Font = (rect.height <= 55f) ? GameFont.Tiny : GameFont.Small;
+ Text.Anchor = TextAnchor.UpperLeft;
+ Rect rect3 = new(rect.x, rect.y + rect.height / 2f, rect.width, rect.height / 2f);
+ rect3 = new Rect(rect3.x + barWidth, rect3.y, rect3.width - barWidth * 2f, rect3.height - barHeight);
+
+ Widgets.FillableBar(rect3, curPsyfocusLevel);
+
+ DrawPsyfocusTargetMarkerAt(rect3, targetPsyfocusLevel);
+ Text.Font = GameFont.Small;
+ }
+
+ private void DrawPsyfocusTargetMarkerAt(Rect barRect, float pct)
+ {
+ float seekerSize = 12f;
+ if (barRect.width < 150f)
+ {
+ seekerSize /= 2f;
+ }
+ Vector2 vector = new(barRect.x + barRect.width * pct, barRect.y + barRect.height);
+ Rect position = new(vector.x - seekerSize / 2f, vector.y, seekerSize, seekerSize);
+ GUI.DrawTexture(position, StaticConstructorOnGameStart.BarInstantMarkerTex);
+ }
+
+ public override int GetMinWidth(PawnTable table)
+ => Mathf.Max(base.GetMinWidth(table), 110);
+
+ public override int Compare(Pawn a, Pawn b)
+ {
+ int hasPsylink = a.HasPsylink.CompareTo(b.HasPsylink);
+ if (hasPsylink != 0) { return hasPsylink; }
+ return (a.psychicEntropy?.CurrentPsyfocus ?? 0f).CompareTo(b.psychicEntropy?.CurrentPsyfocus ?? 0f);
+ }
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_PsylinkLevel.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_PsylinkLevel.cs
new file mode 100644
index 0000000..e77f607
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_PsylinkLevel.cs
@@ -0,0 +1,17 @@
+namespace Numbers
+{
+ using RimWorld;
+ using Verse;
+
+ public class PawnColumnWorker_PsylinkLevel : PawnColumnWorker_Text
+ {
+ protected override string GetTextFor(Pawn pawn)
+ => (pawn.psychicEntropy?.Psylink?.level ?? 0).ToString();
+
+ public override int Compare(Pawn a, Pawn b)
+ => (a.psychicEntropy?.Psylink?.level ?? 0).CompareTo((b.psychicEntropy?.Psylink?.level ?? 0));
+
+ public override int GetMinWidth(PawnTable table)
+ => base.GetMinWidth(table) + 10;
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_Race.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_Race.cs
new file mode 100644
index 0000000..5144809
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_Race.cs
@@ -0,0 +1,37 @@
+namespace Numbers
+{
+ using System.Collections.Generic;
+ using System.Linq;
+ using RimWorld;
+ using UnityEngine;
+ using Verse;
+ using System.Reflection;
+
+ public class PawnColumnWorker_Race : PawnColumnWorker_Text
+ {
+ private readonly Dictionary widthsTables = WorldComponent_Numbers.PrimaryFilter.Keys.ToDictionary(x => x, x => 0f);
+
+ protected override string GetTextFor(Pawn pawn)
+ {
+ string text = pawn.kindDef.race.LabelCap.Resolve() ?? string.Empty;
+
+ if (Find.WindowStack.currentlyDrawnWindow is MainTabWindow_Numbers numbers)
+ {
+ float width = Mathf.Max( (int)Text.CalcSize(text).x , widthsTables[numbers.pawnTableDef]);
+ widthsTables[numbers.pawnTableDef] = width;
+ }
+
+ return text;
+ }
+
+ public override int GetMinWidth(PawnTable table)
+ {
+ if (!(typeof(PawnTable).GetField("def", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(table) is PawnTableDef tableDef))
+ throw new System.Exception($"tableDef not found {table}");
+
+ return Mathf.Max(base.GetMinWidth(table), 80, (int)widthsTables[tableDef]);
+ }
+
+ public override int Compare(Pawn a, Pawn b) => a.kindDef.race.LabelCap.Resolve().CompareTo(b.kindDef.race.LabelCap.Resolve());
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_Record.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_Record.cs
new file mode 100644
index 0000000..192c5a8
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_Record.cs
@@ -0,0 +1,38 @@
+namespace Numbers
+{
+ using RimWorld;
+ using UnityEngine;
+ using Verse;
+
+ public class PawnColumnWorker_Record : PawnColumnWorker_Text
+ {
+ protected override string GetTextFor(Pawn pawn)
+ {
+ RecordDef recordDef = def.Ext().record;
+ string recordValue;
+
+ if (recordDef.type == RecordType.Time)
+ recordValue = pawn.records.GetAsInt(recordDef).ToStringTicksToPeriod();
+ else
+ recordValue = pawn.records.GetValue(recordDef).ToString("0.##");
+
+ return recordValue;
+ }
+
+ protected override string GetTip(Pawn pawn) => def.Ext().record.description;
+
+ public override int GetMinWidth(PawnTable table) => Mathf.Max(50, Mathf.CeilToInt(Text.CalcSize(Numbers_Utility.WordWrapAt(def.LabelCap, 150)).x));
+
+ public override int GetMinHeaderHeight(PawnTable table) => Mathf.CeilToInt(Text.CalcSize(Numbers_Utility.WordWrapAt(def.LabelCap, GetMinWidth(table))).y); //not messy at all.
+
+ public override int Compare(Pawn a, Pawn b)
+ {
+ RecordDef recordDef = def.Ext().record;
+
+ if (recordDef.type == RecordType.Time)
+ return a.records.GetAsInt(recordDef).CompareTo(b.records.GetAsInt(recordDef));
+ return a.records.GetValue(recordDef).CompareTo(b.records.GetValue(recordDef));
+ }
+
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_Recruitable.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_Recruitable.cs
new file mode 100644
index 0000000..2d9b006
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_Recruitable.cs
@@ -0,0 +1,29 @@
+using RimWorld;
+using UnityEngine;
+using Verse;
+
+namespace Numbers
+{
+ public class PawnColumnWorker_Recruitable : PawnColumnWorker_Icon
+ {
+ protected override string GetIconTip(Pawn pawn)
+ {
+ if (pawn?.guest?.Recruitable == false)
+ {
+ return "Unrecruitable".Translate().AsTipTitle().CapitalizeFirst() + "\n\n" + "UnrecruitableDesc".Translate(pawn.Named("PAWN")).Resolve();
+ }
+ return null;
+ }
+
+ public override int Compare(Pawn a, Pawn b)
+ {
+ //either do complicated stuff with negation or just swap a and b.
+ return b.guest?.Recruitable.CompareTo(a.guest?.Recruitable) ?? base.Compare(b, a);
+ }
+
+ protected override Texture2D GetIconFor(Pawn pawn)
+ {
+ return (pawn.guest?.Recruitable ?? true) ? null : StaticConstructorOnGameStart.UnwaveringlyLoyal;
+ }
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_SelfTend.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_SelfTend.cs
new file mode 100644
index 0000000..0762b5b
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_SelfTend.cs
@@ -0,0 +1,38 @@
+namespace Numbers
+{
+ using RimWorld;
+ using UnityEngine;
+ using Verse;
+
+ public class PawnColumnWorker_SelfTend : PawnColumnWorker_Checkbox
+ {
+ public static float IconPositionVertical = 35f;
+ public static float IconPositionHorizontal = 5f;
+
+ protected override bool GetValue(Pawn pawn) => pawn.playerSettings.selfTend;
+
+ protected override bool HasCheckbox(Pawn pawn) => pawn.IsColonist && !pawn.Dead && !(pawn.WorkTypeIsDisabled(WorkTypeDefOf.Doctor));
+
+ protected override void SetValue(Pawn pawn, bool value, PawnTable table)
+ {
+ if (value && pawn.workSettings.GetPriority(WorkTypeDefOf.Doctor) == 0)
+ Messages.Message("MessageSelfTendUnsatisfied".Translate(pawn.LabelShort, pawn), MessageTypeDefOf.CautionInput, false);
+
+ pawn.playerSettings.selfTend = value;
+ }
+
+ protected override string GetHeaderTip(PawnTable table) => "SelfTend".Translate() + "\n\n" + "Numbers_ColumnHeader_Tooltip".Translate();
+
+ protected override string GetTip(Pawn pawn) => "SelfTendTip".Translate(Faction.OfPlayer.def.pawnsPlural, TendUtility.SelfTendQualityFactor.ToStringPercent()).CapitalizeFirst();
+
+ public override void DoHeader(Rect rect, PawnTable table)
+ {
+ float scale = 0.3f;
+ base.DoHeader(rect, table);
+ Vector2 headerIconSize = new Vector2(StaticConstructorOnGameStart.Tame.width, StaticConstructorOnGameStart.Tame.height) * scale;
+ int num = (int)((rect.width - headerIconSize.x) * 0.4);
+ Rect position = new(rect.x + num + IconPositionHorizontal, rect.yMax - StaticConstructorOnGameStart.Tame.height + IconPositionVertical, headerIconSize.x, headerIconSize.y);
+ GUI.DrawTexture(position, StaticConstructorOnGameStart.Tame);
+ }
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_Skill.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_Skill.cs
new file mode 100644
index 0000000..da9abc6
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_Skill.cs
@@ -0,0 +1,84 @@
+namespace Numbers
+{
+ using System.Reflection;
+ using RimWorld;
+ using UnityEngine;
+ using Verse;
+
+ [StaticConstructorOnStartup]
+ //mostly from Koisama
+ public class PawnColumnWorker_Skill : PawnColumnWorker
+ {
+
+ private static readonly Texture2D passionMinorIcon = ContentFinder.Get("UI/Icons/PassionMinor");
+ private static readonly Texture2D passionMajorIcon = ContentFinder.Get("UI/Icons/PassionMajor");
+ private static readonly Texture2D SkillBarFillTex = SolidColorMaterials.NewSolidColorTexture(new Color(1f, 1f, 1f, 0.25f));
+ private static readonly Texture2D SkillBarBgTex = SolidColorMaterials.NewSolidColorTexture(new Color(1f, 1f, 1f, 0.07f));
+ private static readonly Color DisabledSkillColor = new(1f, 1f, 1f, 0.5f);
+
+ private static readonly MethodInfo mGetSkillDescription = typeof(SkillUI).GetMethod("GetSkillDescription", BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.InvokeMethod, null, [typeof(SkillRecord)], null);
+
+ public override void DoCell(Rect rect, Pawn pawn, PawnTable table)
+ {
+ if (pawn.RaceProps.IsMechanoid)
+ return;
+ if (pawn.RaceProps.Animal)
+ return;
+
+ SkillRecord skill = pawn.skills?.GetSkill(def.Ext().skill);
+
+ if (skill == null)
+ return;
+
+ GUI.BeginGroup(rect);
+ Rect position = new(3f, 3f, 24f, 24f);
+ if (skill.passion > Passion.None)
+ {
+ Texture2D image = (skill.passion != Passion.Major) ? passionMinorIcon : passionMajorIcon;
+ GUI.DrawTexture(position, image);
+ }
+ if (!skill.TotallyDisabled)
+ {
+ Rect rect3 = new(position.xMax, 0f, rect.width - position.xMax, rect.height);
+ Widgets.FillableBar(rect3, skill.Level / 20f, SkillBarFillTex, SkillBarBgTex, false);
+ }
+ Rect rect4 = new(position.xMax + 4f, 0f, 999f, rect.height);
+ rect4.yMin += 3f;
+ string label;
+ if (skill.TotallyDisabled)
+ {
+ GUI.color = DisabledSkillColor;
+ label = "-";
+ }
+ else
+ {
+ label = skill.Level.ToStringCached();
+ }
+ GenUI.SetLabelAlign(TextAnchor.MiddleLeft);
+ Widgets.Label(rect4, label);
+ GenUI.ResetLabelAlign();
+ GUI.color = Color.white;
+ GUI.EndGroup();
+ string tip = GetTip(skill);
+ if (!tip.NullOrEmpty())
+ {
+ TooltipHandler.TipRegion(rect, tip);
+ }
+ }
+
+ private string GetTip(SkillRecord skill) => (string)mGetSkillDescription.Invoke(null, new[] { skill });
+
+ public override int GetMinWidth(PawnTable table) => Mathf.Max(base.GetMinWidth(table), 120);
+
+ public override int Compare(Pawn a, Pawn b)
+ {
+ // genes give extra levels, so can't just compare by xp anymore.
+ int ret = (a.skills?.GetSkill(def.Ext().skill)?.GetLevel(true) ?? 0).CompareTo(b.skills?.GetSkill(def.Ext().skill)?.GetLevel(true) ?? 0);
+ if (ret == 0)
+ {
+ ret = (a.skills?.GetSkill(def.Ext().skill)?.XpTotalEarned ?? 0).CompareTo(b.skills?.GetSkill(def.Ext().skill)?.XpTotalEarned ?? 0);
+ }
+ return ret;
+ }
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_Stat.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_Stat.cs
new file mode 100644
index 0000000..36cd51e
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_Stat.cs
@@ -0,0 +1,38 @@
+namespace Numbers
+{
+ using RimWorld;
+ using UnityEngine;
+ using Verse;
+
+ public class PawnColumnWorker_Stat : PawnColumnWorker_Text
+ {
+ public const int minWidthBasedOnNarrowestColumnThatColumnBeingMass = 69;
+ public const int maxWidthBasedOnColumnsWithALongAssNameLikeThisInt = 150;
+ public const int margin = 5;
+
+ protected override string GetTextFor(Pawn pawn)
+ {
+ Thing thing = pawn;
+
+ if (pawn.ParentHolder is Corpse tmpCorpse && this.def.Ext().stat != StatDefOf.LeatherAmount) //this is dumb, but corpses don't seem to have leather.
+ thing = tmpCorpse;
+
+ return def.Ext().stat.Worker.IsDisabledFor(thing) ? null
+ : def.Ext().stat.Worker.ValueToString(thing.GetStatValue(def.Ext().stat), true);
+ }
+
+ public override int GetMinWidth(PawnTable table)
+ => Mathf.Max(minWidthBasedOnNarrowestColumnThatColumnBeingMass,
+ Mathf.CeilToInt(Text.CalcSize(Numbers_Utility.WordWrapAt(def.LabelCap, maxWidthBasedOnColumnsWithALongAssNameLikeThisInt)).x))
+ + margin;
+
+ public override int GetMinHeaderHeight(PawnTable table)
+ => Mathf.CeilToInt(Text.CalcSize(Numbers_Utility.WordWrapAt(def.LabelCap, GetMinWidth(table))).y); //not messy at all.
+
+ public override int Compare(Pawn a, Pawn b)
+ => (def.Ext().stat.Worker.IsDisabledFor(a) ? 0 : a.GetStatValue(def.Ext().stat)).CompareTo
+ (def.Ext().stat.Worker.IsDisabledFor(b) ? 0 : b.GetStatValue(def.Ext().stat));
+
+ }
+}
+
\ No newline at end of file
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_Strip.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_Strip.cs
new file mode 100644
index 0000000..8403303
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_Strip.cs
@@ -0,0 +1,20 @@
+namespace Numbers
+{
+ using RimWorld;
+ using Verse;
+
+ public class PawnColumnWorker_Strip : PawnColumnWorker_Designator
+ {
+ protected override DesignationDef DesignationType => DesignationDefOf.Strip;
+
+ protected override bool HasCheckbox(Pawn pawn)
+ {
+ return StrippableUtility.CanBeStrippedByColony(pawn);
+ }
+
+ protected override void Notify_DesignationAdded(Pawn pawn)
+ {
+ StrippableUtility.CheckSendStrippingImpactsGoodwillMessage(pawn);
+ }
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_TameChance.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_TameChance.cs
new file mode 100644
index 0000000..ef681bc
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_TameChance.cs
@@ -0,0 +1,28 @@
+namespace Numbers
+{
+ using System;
+ using RimWorld;
+ using Verse;
+
+ public class PawnColumnWorker_TameChance : PawnColumnWorker_Text
+ {
+ public override int Compare(Pawn a, Pawn b) => GetValue(a).CompareTo(GetValue(b));
+
+ protected override string GetTextFor(Pawn pawn) => pawn.AnimalOrWildMan() ? GetValue(pawn).ToStringPercent() : string.Empty;
+
+ private float GetValue(Pawn pawn) => pawn.AnimalOrWildMan() ? TameChanceFactorCurve_Wildness.Evaluate(pawn.RaceProps.wildness) : 0;
+
+
+#if DEBUG
+ [Obsolete]
+ //RimWorld.InteractionWorker_RecruitAttempt.TameChanceFactorCurve_Wildness is private, so I copy-pasted the simple thing.
+ // Updaters beware: this is likely to be outdated. Verify.
+#endif
+ private static readonly SimpleCurve TameChanceFactorCurve_Wildness =
+ [
+ new CurvePoint(1f, 0f),
+ new CurvePoint(0.5f, 1f),
+ new CurvePoint(0f, 2f)
+ ];
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_Trait.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_Trait.cs
new file mode 100644
index 0000000..50e5464
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_Trait.cs
@@ -0,0 +1,14 @@
+namespace Numbers
+{
+ using System.Linq;
+ using RimWorld;
+ using Verse;
+
+ public class PawnColumnWorker_Trait : PawnColumnWorker_Text
+ {
+ protected override string GetTextFor(Pawn pawn)
+ {
+ return pawn.story?.traits?.allTraits.Select(x => x.Label).ToCommaList();
+ }
+ }
+}
diff --git a/Numbers/PawnColumnWorkers/PawnColumnWorker_Will.cs b/Numbers/PawnColumnWorkers/PawnColumnWorker_Will.cs
new file mode 100644
index 0000000..e8aa89b
--- /dev/null
+++ b/Numbers/PawnColumnWorkers/PawnColumnWorker_Will.cs
@@ -0,0 +1,24 @@
+using RimWorld;
+using Verse;
+
+namespace Numbers
+{
+ public class PawnColumnWorker_Will : PawnColumnWorker_Text
+ {
+ protected override string GetTextFor(Pawn pawn)
+ => pawn.guest.will.ToString("F1");
+
+ protected override string GetTip(Pawn pawn)
+ {
+ var range = pawn.kindDef.initialWillRange.Value;
+
+ return string.Format("{0} : {1}-{2}", "WillFromPawnKind".Translate(pawn.kindDef.LabelCap), range.min, range.max);
+ }
+
+ protected override string GetHeaderTip(PawnTable table)
+ => "WillLevelDesc".Translate();
+
+ public override int Compare(Pawn a, Pawn b)
+ => a.guest?.will.CompareTo(b?.guest?.will) ?? 0;
+ }
+}
diff --git a/Numbers/PawnTable_NumbersMain.cs b/Numbers/PawnTable_NumbersMain.cs
new file mode 100644
index 0000000..5cb7d7f
--- /dev/null
+++ b/Numbers/PawnTable_NumbersMain.cs
@@ -0,0 +1,56 @@
+namespace Numbers
+{
+ using System;
+ using System.Collections.Generic;
+ using RimWorld;
+ using Verse;
+
+ public class PawnTable_NumbersMain : PawnTable
+ {
+ private List _originalColumns;
+
+ public PawnTable_NumbersMain(PawnTableDef def, Func> pawnsGetter, int uiWidth, int uiHeight) : base(def, pawnsGetter, uiWidth, uiHeight)
+ {
+ PawnTableDef = def;
+ _originalColumns = def.columns;
+
+ SetMinMaxSize(def.minWidth, uiWidth, 0, (int)(uiHeight * Numbers_Settings.maxHeight));
+ SetDirty();
+ }
+
+ public PawnTableDef PawnTableDef { get; protected set; }
+
+ Queue> filtersToApply = new();
+
+ protected override IEnumerable LabelSortFunction(IEnumerable input)
+ {
+ if (PawnTableDef == NumbersDefOf.Numbers_MainTable)
+ {
+ return PlayerPawnsDisplayOrderUtility.InOrder(input);
+ }
+ return base.LabelSortFunction(input);
+ }
+
+ public int RemoveColumns(Predicate predicate)
+ {
+ return PawnTableDef.columns.RemoveAll(predicate);
+ }
+
+ // Postponing column removal until PawnTable drawing is finished
+ // Immediate removal causes change of column indexes in middle of drawing process
+ // it makes vanilla game to render nonsense for a single update
+ // and also causes support issues for Grouped Pawn Tables mod (caches mismatch)
+ public void EnqueueColumnRemoval(Predicate predicate)
+ {
+ filtersToApply.Enqueue(predicate);
+ }
+
+ public void ApplyColumnRemoval()
+ {
+ while (filtersToApply.Count > 0)
+ {
+ RemoveColumns(filtersToApply.Dequeue());
+ }
+ }
+ }
+}
diff --git a/Numbers/Properties/AssemblyInfo.cs b/Numbers/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..41d4d82
--- /dev/null
+++ b/Numbers/Properties/AssemblyInfo.cs
@@ -0,0 +1,11 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 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("ba30d06a-3eaf-4302-8fd2-9c6a9bd177c4")]
diff --git a/Numbers/StaticConstructorOnGameStart.cs b/Numbers/StaticConstructorOnGameStart.cs
new file mode 100644
index 0000000..d2be6f7
--- /dev/null
+++ b/Numbers/StaticConstructorOnGameStart.cs
@@ -0,0 +1,162 @@
+namespace Numbers
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using RimWorld;
+ using UnityEngine;
+ using Verse;
+
+ [StaticConstructorOnStartup]
+ static class StaticConstructorOnGameStart
+ {
+ public static readonly Texture2D DeleteX = ContentFinder.Get("UI/Buttons/Delete"),
+ Info = ContentFinder.Get("UI/Buttons/InfoButton"),
+ Predator = ContentFinder.Get("UI/Icons/Animal/Predator"),
+ Tame = ContentFinder.Get("UI/Icons/Animal/Tame"),
+ List = ContentFinder.Get("UI/Buttons/DevRoot/ToggleTweak"),
+ Plus = ContentFinder.Get("UI/Icons/Trainables/Rescue"),
+ IconImmune = ContentFinder.Get("UI/Icons/Medical/IconImmune"),
+ IconDead = ContentFinder.Get("Things/Mote/BattleSymbols/Skull"),
+ IconTendedWell = ContentFinder.Get("UI/Icons/Medical/TendedWell"),
+ IconTendedNeed = ContentFinder.Get("UI/Icons/Medical/TendedNeed"),
+ SortingIcon = ContentFinder.Get("UI/Icons/Sorting"),
+ SortingDescendingIcon = ContentFinder.Get("UI/Icons/SortingDescending"),
+ BarInstantMarkerTex = ContentFinder.Get("UI/Misc/BarInstantMarker"),
+ Drop = ContentFinder.Get("UI/Buttons/Drop"),
+ UnwaveringlyLoyal = ContentFinder.Get("UI/Icons/UnwaveringlyLoyal");
+
+ public static List combatPreset = [],
+ workTabPlusPreset = [],
+ colonistNeedsPreset = [],
+ psycastingPreset = [],
+ medicalPreset = [];
+
+ public static Type animalTab;
+ public static Type wildLifeTab;
+
+ public static Dictionary> PawnTableDef_Columns { get; private set; }
+
+ static StaticConstructorOnGameStart()
+ {
+ AddTrainablesToAnimalTable();
+
+ AddRemainingSpaceToPawnTableDefs();
+
+ AddHeaderToolTipToPawnColumns();
+
+ PopulatePresets();
+
+ //for the sake of compatibility (Better Pawn Control / other mods which subclass it.)
+ animalTab = DefDatabase.GetNamed("Animals").tabWindowClass;
+ wildLifeTab = DefDatabase.GetNamed("Wildlife").tabWindowClass;
+
+ CreateCacheMappingOfDefsForReset();
+ }
+
+ private static void CreateCacheMappingOfDefsForReset()
+ {
+ PawnTableDef_Columns = DefDatabase.AllDefsListForReading.Where(x => x.defName.StartsWith("Numbers"))
+ .ToDictionary(x => x.defName, x => x.columns.Select(x => x.defName).ToList());
+ }
+
+ private static void AddTrainablesToAnimalTable()
+ {
+ PawnTableDef animalsTable = NumbersDefOf.Numbers_Animals;
+
+ foreach (PawnColumnDef item in DefDatabase.AllDefsListForReading.Where(x => x.Worker is PawnColumnWorker_Trainable))
+ {
+ animalsTable.columns.Insert(animalsTable.columns.FindIndex(x => x.Worker is PawnColumnWorker_Checkbox) - 1, item);
+ }
+ }
+
+ private static void AddRemainingSpaceToPawnTableDefs()
+ {
+ IEnumerable allPawntableDefs = DefDatabase.AllDefsListForReading.Where(x => x.HasModExtension());
+
+ PawnColumnDef remainingspace = DefDatabase.AllDefsListForReading.First(x => x.Worker is PawnColumnWorker_RemainingSpace);
+
+ IEnumerable ptsDfromPtDses = allPawntableDefs as PawnTableDef[] ?? allPawntableDefs.ToArray();
+
+ foreach (PawnTableDef PTSDfromPTDs in ptsDfromPtDses)
+ {
+ PTSDfromPTDs.columns.Insert(PTSDfromPTDs.columns.Count, remainingspace);
+ }
+ }
+
+ private static void AddHeaderToolTipToPawnColumns()
+ {
+ foreach (PawnColumnDef pawnColumnDef in DefDatabase
+ .AllDefsListForReading
+ .Where(x => !x.generated
+ && x.defName.StartsWith("Numbers_")
+ && !(x.Worker is PawnColumnWorker_AllHediffs
+ || x.Worker is PawnColumnWorker_SelfTend
+ || x.Worker is PawnColumnWorker_Ability))) //these PawnColumnWorkers override GetHeaderTip.
+ {
+ pawnColumnDef.headerTip += (pawnColumnDef.headerTip.NullOrEmpty() ? "" : "\n\n") + "Numbers_ColumnHeader_Tooltip".Translate();
+ }
+ }
+
+ private static void PopulatePresets()
+ {
+ combatPreset.AddRange(DefDatabase.GetNamed("Numbers_CombatPreset").columns);
+ workTabPlusPreset.AddRange(DefDatabase.GetNamed("Numbers_WorkTabPlusPreset").columns);
+ colonistNeedsPreset.AddRange(DefDatabase.GetNamed("Numbers_ColonistNeedsPreset").columns);
+ PopulatePsycastingPreset();
+ PopulateMedicalPreset();
+ }
+
+ private static void PopulatePsycastingPreset()
+ {
+ psycastingPreset.Add(DefDatabase.GetNamed("LabelShortWithIcon"));
+ psycastingPreset.Add(DefDatabase.GetNamed("Numbers_PsylinkLevel"));
+ psycastingPreset.Add(DefDatabase.GetNamed("Numbers_Psyfocus"));
+ psycastingPreset.Add(DefDatabase.GetNamed("Numbers_Entropy"));
+ psycastingPreset.AddRange(
+ DefDatabase.AllDefsListForReading.Where(pcd => pcd.Ext(logError: false)?.ability != null).ToList()
+ .OrderBy(x => x.Ext().ability.level)
+ .ThenBy(x => x.Ext().ability.PsyfocusCost)
+ .ThenBy(x => x.Ext().ability.EntropyGain)
+ .ThenBy(x => x.Ext().ability.defName)
+ );
+ }
+
+ private static void PopulateMedicalPreset()
+ {
+ medicalPreset.AddRange(new List
+ {
+ DefDatabase.GetNamed("LabelShortWithIcon"),
+ DefDatabase.GetNamed("MedicalCare"),
+ DefDatabase.GetNamed("Numbers_SelfTend"),
+ DefDatabase.GetNamed("Numbers_HediffList"),
+ DefDatabase.GetNamed("Numbers_HediffBadList"),
+ DefDatabase.GetNamed("Numbers_RimWorld_StatDef_MedicalSurgerySuccessChance"),
+ DefDatabase.GetNamed("Numbers_RimWorld_StatDef_MedicalTendQuality"),
+ DefDatabase.GetNamed("Numbers_RimWorld_StatDef_MedicalTendSpeed"),
+ DefDatabase.GetNamed("Numbers_Bleedrate"),
+ DefDatabase.GetNamed("Numbers_Pain")
+ });
+ medicalPreset.AddRange(DefDatabase.AllDefsListForReading
+ .Where(pcd => pcd.workType != null)
+ .Where(x => x.workType.defName == "Patient" ||
+ x.workType.defName == "Doctor" ||
+ x.workType.defName == "PatientBedRest").Reverse());
+
+ medicalPreset
+ .AddRange(DefDatabase
+ .AllDefsListForReading
+ .Select(x => DefDatabase
+ .GetNamed(HorribleStringParsersForSaving.CreateDefNameFromType(x))));
+
+ medicalPreset.RemoveAll(x => x.defName == "Numbers_Verse_PawnCapacityDef_Metabolism"); //I need space
+ medicalPreset.AddRange(new List
+ {
+ DefDatabase.GetNamed("Numbers_NeedsTreatment"),
+ DefDatabase.GetNamed("Numbers_Operations"),
+ DefDatabase.GetNamed("Numbers_DiseaseProgress"),
+ DefDatabase.GetNamed("RemainingSpace")
+ });
+ }
+ }
+}
diff --git a/Numbers/WorldComponent_Numbers.cs b/Numbers/WorldComponent_Numbers.cs
new file mode 100644
index 0000000..56a5dfe
--- /dev/null
+++ b/Numbers/WorldComponent_Numbers.cs
@@ -0,0 +1,99 @@
+namespace Numbers
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using RimWorld;
+ using RimWorld.Planet;
+ using Verse;
+
+ public class WorldComponent_Numbers : WorldComponent
+ {
+ public WorldComponent_Numbers(World world) : base(world)
+ {
+ }
+
+ public override void FinalizeInit()
+ {
+ base.FinalizeInit();
+ primaryFilter = PrimaryFilter.First();
+
+ if (!sessionTable.Any())
+ {
+ List storedPawnTables = LoadedModManager.GetMod().GetSettings().storedPawnTableDefs;
+ foreach (string item in storedPawnTables)
+ {
+ if (item.Split(',')[1] == "Default" && sessionTable.All(x => x.Key.defName != item.Split(',')[0]))
+ {
+ PawnTableDef pawnTableDef = HorribleStringParsersForSaving.TurnCommaDelimitedStringIntoPawnTableDef(item);
+ sessionTable.Add(pawnTableDef, pawnTableDef.columns);
+ }
+ }
+ }
+ NotifySettingsChanged();
+ }
+
+ public void ResetLoadList()
+ {
+ loadList.Clear();
+ }
+
+ public KeyValuePair> primaryFilter;
+
+ private List loadList;
+
+ public Dictionary> sessionTable = [];
+
+ public override void ExposeData()
+ {
+ foreach (PawnTableDef type in DefDatabase.AllDefsListForReading.Where(x => x.HasModExtension()))
+ {
+ switch (Scribe.mode)
+ {
+ case LoadSaveMode.Saving:
+ if (sessionTable.TryGetValue(type, out List workList))
+ {
+ Scribe_Collections.Look(ref workList, "Numbers_" + type, LookMode.Def);
+ sessionTable[type] = workList;
+ }
+ break;
+
+ case LoadSaveMode.LoadingVars:
+ Scribe_Collections.Look(ref loadList, "Numbers_" + type, LookMode.Def);
+ if (!loadList.NullOrEmpty())
+ sessionTable[type] = loadList;
+ break;
+ }
+ }
+ }
+
+ public static readonly Dictionary> PrimaryFilter = new()
+ {
+ //{ "All", (pawn) => true },
+ { NumbersDefOf.Numbers_MainTable, pawn => !pawn.Dead && pawn.IsVisible() && pawn.IsColonist },
+ { NumbersDefOf.Numbers_Enemies, pawn => !pawn.Dead && pawn.IsVisible() && pawn.IsEnemy() },
+ { NumbersDefOf.Numbers_Prisoners, pawn => !pawn.Dead && pawn.IsVisible() && pawn.IsPrisoner },
+ { NumbersDefOf.Numbers_Guests, pawn => !pawn.Dead && pawn.IsVisible() && pawn.IsGuest() },
+ { NumbersDefOf.Numbers_Animals, pawn => !pawn.Dead && pawn.IsVisible() && pawn.IsAnimal() && pawn.Faction == Faction.OfPlayer },
+ { NumbersDefOf.Numbers_WildAnimals, pawn => !pawn.Dead && pawn.IsVisible() && pawn.IsWildAnimal() },
+ { NumbersDefOf.Numbers_Corpses, pawn => pawn.Dead && pawn.IsVisible() && !pawn.IsAnimal() },
+ { NumbersDefOf.Numbers_AnimalCorpses, pawn => pawn.Dead && pawn.IsVisible() && pawn.IsAnimal() }
+ };
+
+ internal void NotifySettingsChanged()
+ {
+ DefDatabase.GetNamed("Wildlife").tabWindowClass
+ = Numbers_Settings.coolerThanTheWildlifeTab
+ ? typeof(MainTabWindow_NumbersWildLife)
+ : StaticConstructorOnGameStart.wildLifeTab;
+
+ DefDatabase.GetNamed("Animals").tabWindowClass
+ = Numbers_Settings.coolerThanTheAnimalTab
+ ? typeof(MainTabWindow_NumbersAnimals)
+ : StaticConstructorOnGameStart.animalTab;
+
+ DefDatabase.GetNamed("Wildlife").Notify_ClearingAllMapsMemory();
+ DefDatabase.GetNamed("Animals").Notify_ClearingAllMapsMemory();
+ }
+ }
+}
diff --git a/README.md b/README.md
index 2fe9cbf..d798ef5 100644
--- a/README.md
+++ b/README.md
@@ -1,38 +1,56 @@
# Numbers!
-Mod for RimWorld.
+Adds a customizable general overview tab, allowing you to see any stats on all your colonists or prisoners in a single window. Quickly compare colonists to see who your best doctor is, or to assign gear optimally.
-Ever wanted to compare multiple colonists? Quickly see who is on the verge of a mental break?
-Now it's possible!

### Features
-- Customizable overview tab, ~~up to 10 values at once~~ displays as many columns as you can fit on your screen
-- Displays Stats, Skills, Needs
-- Prisoner and medical controls, equipment overview, current job
-- Works with colonists, prisoners, enemies, animals, wild animals, and even corpses
+- Customizable overview tab, displays as many columns as you can fit on your screen.
+- Displays Stats, Skills, Needs, Gear, Queued jobs, Pawn Records, etc etc.
+- Prisoner and medical controls, equipment overview, current job.
+- A Medical Tab that's better than Fluffy's Medical Tab.
+- Works with colonists, prisoners, enemies, animals, wild animals, and even corpses.
+- Presets! Save, load, move and share your custom layouts. Store them as default.
-[RimWorld forum](https://ludeon.com/forums/index.php?topic=16558.0)
+### Usability
-### Download
+- Slide and reorganise columns as you see fit. Reorderable headers!
+- Click headers to sort by stat and compare.
+- Click to jump to colonist.
+- Right-click headers to close.
+- Draggable options for things like: prisoner interaction, all checkmarks. Try it on outfits, medical care, hostility response mode and be pleasantly surprised.
-**Last update:** 22/12/2016 (updated to `0.6.0`)
+It can be added to existing saves without problems.
-[Latest release](https://github.com/koisama/kNumbers/releases/latest)
+### Links
-[All the releases](https://github.com/koisama/kNumbers/releases)
+- [Latest release](https://github.com/Mehni/kNumbers/releases/latest)
+- [Steam](https://steamcommunity.com/sharedfiles/filedetails/?id=1414302321)
+- [Ludeon](https://ludeon.com/forums/index.php?topic=35832.0)
+- [GitHub](https://github.com/Mehni/kNumbers)
+- [All the releases](https://github.com/Mehni/kNumbers/releases)
-### Changelog
+## Credits
-[Check changelog here](./CHANGELOG.md).
+Much thanks to [Maarxx](https://github.com/maarxx) for singlehandedly adding support for Royalty. Check out their mods [here](https://ludeon.com/forums/index.php?topic=53539)!
+### Languages
-### Inspired by
+- English: Mehni
+- Chinese (simplified): AlongWY
+- German: Amalek
+- Russian: JasKill
+- Spanish: Crusader
-Inspired by MedicalInfo by Fluffy (l2032)
+## Adding a column (for contributors)
-### License
+1. Add a new class in Numbers\PawnColumnWorkers. Use the `Numbers` namespace. Inherit from the correct PawnColumnWorker.
+1. Create a new PawnColumnDef. Save it in `1.1\Defs\PawnColumnDef\PawnColumns_Numbers.xml`. Adhere to the naming scheme there.
+1. To add your new column to the Misc button, add the defName of your PawnColumnDef to the appropriate Def in `1.1\Defs\PawnColumnOptionDef\Numbers_PawnColumnOptionDef.xml`. This step should not be skipped.
+1. To add your new column to the default table, add the defName of your PawnColumnDef to the approppriate PawnTableDef in `1.1\Defs\PawnTableDef\Numbers_PawnTableDef.xml`.
-This mod can be used by anyone for any purpose as long as it's free and there's proper attribution - mod name, author's name and a link to this page.
+## License
+
+Original idea by koisama: https://github.com/koisama/kNumbers, whose original license I respect by the preceding link. For the license since 2018/11/21, see LICENSE.
\ No newline at end of file
diff --git a/Source/kNumbers.sln b/Source/kNumbers.sln
deleted file mode 100644
index ea99091..0000000
--- a/Source/kNumbers.sln
+++ /dev/null
@@ -1,22 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 14
-VisualStudioVersion = 14.0.23107.0
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "kNumbers", "kNumbers\kNumbers.csproj", "{37259212-9221-451E-9B38-B06865555576}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {37259212-9221-451E-9B38-B06865555576}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {37259212-9221-451E-9B38-B06865555576}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {37259212-9221-451E-9B38-B06865555576}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {37259212-9221-451E-9B38-B06865555576}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
-EndGlobal
diff --git a/Source/kNumbers/KListObject.cs b/Source/kNumbers/KListObject.cs
deleted file mode 100644
index 99f45ac..0000000
--- a/Source/kNumbers/KListObject.cs
+++ /dev/null
@@ -1,583 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using UnityEngine;
-using Verse;
-using RimWorld;
-using System.Reflection;
-using System.Collections;
-
-namespace kNumbers
-{
- [StaticConstructorOnStartup]
- public class KListObject : IExposable
- {
-
- public enum objectType
- {
- Stat,
- Health, //can wait
- Need,
- Skill,
- Gear, //weapon and all apparel
- ControlPrisonerGetsFood,
- ControlMedicalCare,
- ControlPrisonerInteraction,
- CurrentJob,
- AnimalMilkFullness,
- AnimalWoolGrowth,
- Age,
- MentalState,
- Capacity
- }
-
- public objectType oType;
- public string label;
- public object displayObject;
- public float minWidthDesired = 120f;
-
- private static Texture2D passionMinorIcon = ContentFinder.Get("UI/Icons/PassionMinor", true),
- passionMajorIcon = ContentFinder.Get("UI/Icons/PassionMajor", true),
- SkillBarFillTex = SolidColorMaterials.NewSolidColorTexture(new Color(1f, 1f, 1f, 0.25f)),
- SkillBarBgTex = SolidColorMaterials.NewSolidColorTexture(new Color(1f, 1f, 1f, 0.07f)),
- BarInstantMarkerTex = BarInstantMarkerTex = ContentFinder.Get("UI/Misc/BarInstantMarker", true);
-
- public static Texture2D[] careTextures = new Texture2D[]
- {
- ContentFinder.Get("UI/Icons/Medical/NoCare", true),
- ContentFinder.Get("UI/Icons/Medical/NoMeds", true),
- ThingDefOf.HerbalMedicine.uiIcon,
- ThingDefOf.Medicine.uiIcon,
- ThingDefOf.GlitterworldMedicine.uiIcon
- };
-
- private static readonly Color DisabledSkillColor = new Color(1f, 1f, 1f, 0.5f);
- private static readonly Color ThingLabelColor = new Color(0.9f, 0.9f, 0.9f, 1f);
- private static readonly Color HighlightColor = new Color(0.5f, 0.5f, 0.5f, 1f);
-
- private static MethodInfo mGetSkillDescription = typeof(SkillUI).GetMethod("GetSkillDescription", BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.InvokeMethod, null, new[] { typeof(SkillRecord) }, null);
-
- private static FieldInfo needThreshPercent = typeof(Need).GetField("threshPercents", BindingFlags.NonPublic | BindingFlags.Instance);
-
- public void ExposeData()
- {
-
- Scribe_Values.Look