From 10638a99ee2985f6b3699cdfb34afc1a868e6503 Mon Sep 17 00:00:00 2001 From: edbmods Date: Sun, 21 Apr 2024 12:51:53 -0700 Subject: [PATCH 1/2] Refactoring for version 1.5 --- EdBPrepareCarefully.csproj | 210 +- Properties/AssemblyInfo.cs | 4 +- Resources/About/About.xml | 15 +- Resources/About/Manifest.xml | 2 +- Resources/CHANGELOG.txt | 20 + .../English/Keyed/EdBPrepareCarefully.xml | 48 +- .../EdB/PrepareCarefully/ButtonManage.png | Bin 0 -> 2833 bytes .../EdB/PrepareCarefully/ButtonRotateView.png | Bin 0 -> 3250 bytes .../EdB/PrepareCarefully/FieldAtlasWhite.png | Bin 0 -> 2858 bytes .../FieldAtlasWhiteOutline.png | Bin 0 -> 2880 bytes Resources/LoadFolders.xml | 8 + Source/AgeModifier.cs | 94 +- Source/AnimalDatabase.cs | 105 - Source/AnimalRecord.cs | 40 - Source/AnimalRecordKey.cs | 33 - Source/ColonistSaver.cs | 33 - Source/ColorValidator.cs | 55 - Source/Configuration.cs | 13 - Source/Constraints.cs | 9 - Source/Controller.cs | 467 ----- Source/ControllerEquipment.cs | 23 - Source/ControllerPage.cs | 358 ++++ Source/ControllerPawns.cs | 496 ----- Source/ControllerRelationships.cs | 55 - Source/ControllerTabViewEquipment.cs | 35 + Source/ControllerTabViewPawns.cs | 395 ++++ Source/ControllerTabViewRelationships.cs | 56 + Source/CostCalculator.cs | 204 +- Source/CustomFaction.cs | 5 +- Source/CustomPawn.cs | 1748 ----------------- Source/CustomRelationship.cs | 36 - Source/Customizations.cs | 31 + Source/CustomizationsPawn.cs | 122 ++ Source/CustomizedEquipment.cs | 53 + ...{CustomBodyPart.cs => CustomizedHediff.cs} | 9 +- Source/CustomizedPawn.cs | 17 + ...ustomPawnType.cs => CustomizedPawnType.cs} | 6 +- Source/CustomizedRelationship.cs | 51 + Source/DialogAbilities.cs | 62 +- Source/DialogApparel.cs | 609 ++++++ .../{Dialog_Colonist.cs => DialogColonist.cs} | 6 +- .../{Dialog_Confirm.cs => DialogConfirm.cs} | 12 +- Source/DialogFactions.cs | 5 +- Source/DialogFavoriteColor.cs | 43 +- Source/DialogIdeos.cs | 8 +- Source/DialogInitializationError.cs | 4 +- ..._LoadColonist.cs => DialogLoadColonist.cs} | 6 +- ...alog_LoadPreset.cs => DialogLoadPreset.cs} | 6 +- Source/DialogManageImplants.cs | 40 +- Source/DialogManageTraits.cs | 399 ++++ .../{Dialog_Options.cs => DialogOptions.cs} | 6 +- Source/DialogPawnKinds.cs | 1 + Source/{Dialog_Preset.cs => DialogPreset.cs} | 8 +- ..._SaveColonist.cs => DialogSaveColonist.cs} | 6 +- ...alog_SavePreset.cs => DialogSavePreset.cs} | 34 +- Source/DialogSelectParentChildPawn.cs | 82 +- Source/DialogSelectPawn.cs | 261 --- Source/DialogTitles.cs | 2 +- Source/EquipmentDatabase.cs | 640 +++--- Source/EquipmentOption.cs | 39 + Source/EquipmentSelection.cs | 3 +- Source/EquipmentSpawnType.cs | 14 + Source/ExtensionsBackstory.cs | 44 +- Source/ExtensionsObject.cs | 19 +- Source/ExtensionsPawn.cs | 220 +-- Source/ExtensionsRect.cs | 8 +- ...rdExtensions.cs => ExtensionsTabRecord.cs} | 4 +- Source/FilterBackstoryMatchesFaction.cs | 9 +- Source/FilterBackstoryNoPenalties.cs | 10 +- Source/FilterBackstorySkillAdjustment.cs | 20 +- Source/GenStep_CustomScatterThings.cs | 236 --- Source/HarmonyPatches.cs | 69 +- Source/IPanel.cs | 4 +- Source/IRelationshipWorker.cs | 16 - Source/ITabView.cs | 13 +- Source/Implant.cs | 62 +- Source/Injury.cs | 116 +- Source/ManagerEquipment.cs | 203 ++ Source/ManagerPawns.cs | 1159 +++++++++++ Source/ManagerRelationships.cs | 530 +++++ Source/MapperPawnToCustomizations.cs | 296 +++ Source/Mod.cs | 505 +++++ Source/ModState.cs | 30 + Source/OptionsHeadType.cs | 3 - Source/OptionsHealth.cs | 2 +- Source/PagePrepareCarefully.cs | 275 +++ Source/Page_PrepareCarefully.cs | 478 ----- ...ilities.cs => PanelAbilitiesRefactored.cs} | 31 +- Source/{PanelAge.cs => PanelAgeRefactored.cs} | 44 +- Source/PanelApparel.cs | 175 ++ Source/PanelAppearance.cs | 885 +++------ Source/PanelBackstory.cs | 163 +- Source/PanelBase.cs | 9 +- Source/PanelColonyPawnList.cs | 48 - Source/PanelColonyPawnListMinimized.cs | 24 + Source/PanelColonyPawnListRefactored.cs | 36 + Source/PanelEquipmentAvailable.cs | 854 +++++--- Source/PanelEquipmentSelected.cs | 456 +++-- Source/PanelFaction.cs | 88 - Source/PanelFavoriteColor.cs | 76 - Source/PanelHealth.cs | 236 ++- Source/PanelIdeo.cs | 54 +- Source/PanelIncapableOf.cs | 40 +- Source/PanelModule.cs | 12 +- Source/PanelName.cs | 50 +- Source/PanelPawnList.cs | 208 +- Source/PanelPawnListMinimized.cs | 72 + Source/PanelPossessions.cs | 75 + Source/PanelRandomize.cs | 116 +- Source/PanelRelationshipsOther.cs | 199 +- Source/PanelRelationshipsParentChild.cs | 186 +- ...PanelLoadSave.cs => PanelSaveCharacter.cs} | 17 +- Source/PanelScrollingContent.cs | 15 +- Source/PanelSkills.cs | 129 +- Source/PanelTitles.cs | 57 +- Source/PanelTraits.cs | 229 +-- Source/PanelWorldPawnList.cs | 18 +- Source/PanelWorldPawnListMinimized.cs | 24 + Source/PanelXenotype.cs | 14 +- Source/ParentChildGroup.cs | 14 +- Source/PawnBioGenerator.cs | 28 - Source/PawnCompRules.cs | 304 --- Source/PawnCompsLoader.cs | 33 - Source/PawnCompsSaver.cs | 42 - Source/PawnCustomizer.cs | 458 +++++ Source/PawnGenerationRequestBuilder.cs | 36 + Source/PawnGenerationRequestWrapper.cs | 12 +- Source/PawnLayer.cs | 17 +- Source/PawnLayerAlienAddon.cs | 81 +- Source/PawnLayerBeard.cs | 25 +- Source/PawnLayerBody.cs | 48 +- Source/PawnLayerBodyTattoo.cs | 15 +- Source/PawnLayerFaceTattoo.cs | 17 +- Source/PawnLayerHair.cs | 26 +- Source/PawnLayerHead.cs | 58 +- Source/PawnLayerOptionBody.cs | 21 +- Source/PawnLayerOptionHead.cs | 22 +- Source/PawnListMode.cs | 2 +- Source/{ColonistLoader.cs => PawnLoader.cs} | 15 +- Source/PawnLoaderResult.cs | 32 + Source/PawnLoaderV3.cs | 487 +++++ Source/{Version5 => }/PawnLoaderV5.cs | 663 ++++--- Source/PawnRandomizerOptions.cs | 16 + Source/PawnSaver.cs | 213 ++ Source/PrepareCarefully.cs | 701 ------- Source/PresetLoader.cs | 26 +- Source/PresetLoaderReport.cs | 11 + Source/PresetLoaderReportRecord.cs | 16 + Source/PresetLoaderResult.cs | 32 + Source/PresetLoaderV3.cs | 529 +++++ Source/PresetLoaderV5.cs | 386 ++++ Source/PresetSaver.cs | 92 +- Source/ProviderAgeLimits.cs | 2 - Source/ProviderAlienRaces.cs | 4 + Source/ProviderApparel.cs | 148 -- Source/ProviderBackstories.cs | 13 +- Source/ProviderBeards.cs | 10 +- Source/ProviderBodyTatoos.cs | 10 +- Source/ProviderBodyTypes.cs | 7 +- Source/ProviderEquipment.cs | 27 +- Source/ProviderFaceTatoos.cs | 10 +- Source/ProviderFactions.cs | 3 + Source/ProviderHair.cs | 10 +- Source/ProviderHeadTypes.cs | 15 +- Source/ProviderHealthOptions.cs | 19 +- Source/ProviderPawnKinds.cs | 4 + Source/ProviderPawnLayers.cs | 194 +- Source/Providers.cs | 54 - Source/Randomizer.cs | 228 --- Source/Reflection.cs | 6 + Source/ReflectionCache.cs | 4 + Source/RelatedPawn.cs | 20 - Source/RelationshipBuilder.cs | 370 ++-- Source/RelationshipDefinitionHelper.cs | 17 +- Source/RelationshipGroup.cs | 13 - Source/RelationshipList.cs | 12 +- Source/RelationshipManager.cs | 497 ----- Source/ScenPart_CustomAnimal.cs | 6 +- ...Part_CustomScatterThingsNearPlayerStart.cs | 84 - Source/SelectedAnimal.cs | 18 - Source/SelectedPet.cs | 21 - Source/State.cs | 134 -- Source/Style.cs | 4 +- Source/TabViewBase.cs | 13 +- Source/TabViewEquipment.cs | 29 +- Source/TabViewPawns.cs | 237 +-- Source/TabViewRelationships.cs | 27 +- Source/TemporaryPawn.cs | 13 + Source/Textures.cs | 10 +- Source/ThingCache.cs | 46 - Source/UtilityAge.cs | 29 + Source/UtilityApparel.cs | 17 + Source/UtilityColor.cs | 24 + Source/UtilityCopy.cs | 182 -- Source/UtilityEnumerable.cs | 20 + Source/UtilityEquipmentSpawnType.cs | 28 + Source/UtilityGUIState.cs | 33 + Source/UtilityPawns.cs | 93 + Source/UtilityQuality.cs | 18 + Source/UtilitySaveLoad.cs | 20 + Source/UtilityTraits.cs | 19 + Source/Version3/ColonistLoaderVersion3.cs | 66 - Source/Version3/PresetLoaderVersion3.cs | 847 -------- Source/Version3/SaveRecordEquipmentV3.cs | 16 +- Source/Version3/SaveRecordImplantV3.cs | 2 +- Source/Version3/SaveRecordPawnV3.cs | 39 - Source/Version3/SaveRecordRelationshipV3.cs | 10 +- Source/Version4/ColonistLoaderVersion4.cs | 66 - Source/Version4/PresetLoaderVersion4.cs | 821 -------- Source/Version4/SaveRecordPawnV4.cs | 95 - Source/Version5/PresetLoaderV5.cs | 368 ---- Source/Version5/SaveRecordApparelV5.cs | 25 + Source/Version5/SaveRecordGenesV5.cs | 1 - Source/Version5/SaveRecordIdeoV5.cs | 2 +- Source/Version5/SaveRecordImplantV5.cs | 2 +- Source/Version5/SaveRecordPawnV5.cs | 155 +- Source/Version5/SaveRecordPossessionV5.cs | 18 + Source/Version5/SaveRecordPresetV5.cs | 2 + Source/Version5/SaveRecordTemporaryPawnV5.cs | 17 + Source/Version5/SaveRecordTitleV5.cs | 23 + Source/ViewState.cs | 23 + Source/WidgetColorSelector.cs | 172 ++ Source/WidgetDropdown.cs | 38 + Source/WidgetEquipmentField.cs | 116 ++ Source/WidgetEquipmentIcon.cs | 15 +- Source/WidgetEquipmentLoadingProgressBar.cs | 51 + Source/{Field.cs => WidgetField.cs} | 159 +- Source/WidgetInfoButton.cs | 47 + Source/WidgetPortrait.cs | 30 + ...ontal.cs => WidgetScrollViewHorizontal.cs} | 8 +- ...ertical.cs => WidgetScrollViewVertical.cs} | 6 +- Source/WidgetTable.cs | 4 +- dist.bat | 7 +- 233 files changed, 12964 insertions(+), 13710 deletions(-) create mode 100644 Resources/Common/Textures/EdB/PrepareCarefully/ButtonManage.png create mode 100644 Resources/Common/Textures/EdB/PrepareCarefully/ButtonRotateView.png create mode 100644 Resources/Common/Textures/EdB/PrepareCarefully/FieldAtlasWhite.png create mode 100644 Resources/Common/Textures/EdB/PrepareCarefully/FieldAtlasWhiteOutline.png delete mode 100644 Source/AnimalDatabase.cs delete mode 100644 Source/AnimalRecord.cs delete mode 100644 Source/AnimalRecordKey.cs delete mode 100644 Source/ColonistSaver.cs delete mode 100644 Source/ColorValidator.cs delete mode 100644 Source/Configuration.cs delete mode 100644 Source/Constraints.cs delete mode 100644 Source/Controller.cs delete mode 100644 Source/ControllerEquipment.cs create mode 100644 Source/ControllerPage.cs delete mode 100644 Source/ControllerPawns.cs delete mode 100644 Source/ControllerRelationships.cs create mode 100644 Source/ControllerTabViewEquipment.cs create mode 100644 Source/ControllerTabViewPawns.cs create mode 100644 Source/ControllerTabViewRelationships.cs delete mode 100644 Source/CustomPawn.cs delete mode 100644 Source/CustomRelationship.cs create mode 100644 Source/Customizations.cs create mode 100644 Source/CustomizationsPawn.cs create mode 100644 Source/CustomizedEquipment.cs rename Source/{CustomBodyPart.cs => CustomizedHediff.cs} (75%) create mode 100644 Source/CustomizedPawn.cs rename Source/{CustomPawnType.cs => CustomizedPawnType.cs} (70%) create mode 100644 Source/CustomizedRelationship.cs create mode 100644 Source/DialogApparel.cs rename Source/{Dialog_Colonist.cs => DialogColonist.cs} (95%) rename Source/{Dialog_Confirm.cs => DialogConfirm.cs} (91%) rename Source/{Dialog_LoadColonist.cs => DialogLoadColonist.cs} (75%) rename Source/{Dialog_LoadPreset.cs => DialogLoadPreset.cs} (77%) create mode 100644 Source/DialogManageTraits.cs rename Source/{Dialog_Options.cs => DialogOptions.cs} (98%) rename Source/{Dialog_Preset.cs => DialogPreset.cs} (94%) rename Source/{Dialog_SaveColonist.cs => DialogSaveColonist.cs} (93%) rename Source/{Dialog_SavePreset.cs => DialogSavePreset.cs} (67%) delete mode 100644 Source/DialogSelectPawn.cs create mode 100644 Source/EquipmentOption.cs create mode 100644 Source/EquipmentSpawnType.cs rename Source/{TabRecordExtensions.cs => ExtensionsTabRecord.cs} (98%) delete mode 100644 Source/GenStep_CustomScatterThings.cs delete mode 100644 Source/IRelationshipWorker.cs create mode 100644 Source/ManagerEquipment.cs create mode 100644 Source/ManagerPawns.cs create mode 100644 Source/ManagerRelationships.cs create mode 100644 Source/MapperPawnToCustomizations.cs create mode 100644 Source/Mod.cs create mode 100644 Source/ModState.cs create mode 100644 Source/PagePrepareCarefully.cs delete mode 100644 Source/Page_PrepareCarefully.cs rename Source/{PanelAbilities.cs => PanelAbilitiesRefactored.cs} (87%) rename Source/{PanelAge.cs => PanelAgeRefactored.cs} (85%) create mode 100644 Source/PanelApparel.cs delete mode 100644 Source/PanelColonyPawnList.cs create mode 100644 Source/PanelColonyPawnListMinimized.cs create mode 100644 Source/PanelColonyPawnListRefactored.cs delete mode 100644 Source/PanelFaction.cs delete mode 100644 Source/PanelFavoriteColor.cs create mode 100644 Source/PanelPawnListMinimized.cs create mode 100644 Source/PanelPossessions.cs rename Source/{PanelLoadSave.cs => PanelSaveCharacter.cs} (64%) create mode 100644 Source/PanelWorldPawnListMinimized.cs delete mode 100644 Source/PawnBioGenerator.cs delete mode 100644 Source/PawnCompRules.cs delete mode 100644 Source/PawnCompsLoader.cs delete mode 100644 Source/PawnCompsSaver.cs create mode 100644 Source/PawnCustomizer.cs create mode 100644 Source/PawnGenerationRequestBuilder.cs rename Source/{ColonistLoader.cs => PawnLoader.cs} (75%) create mode 100644 Source/PawnLoaderResult.cs create mode 100644 Source/PawnLoaderV3.cs rename Source/{Version5 => }/PawnLoaderV5.cs (52%) create mode 100644 Source/PawnRandomizerOptions.cs create mode 100644 Source/PawnSaver.cs delete mode 100644 Source/PrepareCarefully.cs create mode 100644 Source/PresetLoaderReport.cs create mode 100644 Source/PresetLoaderReportRecord.cs create mode 100644 Source/PresetLoaderResult.cs create mode 100644 Source/PresetLoaderV3.cs create mode 100644 Source/PresetLoaderV5.cs delete mode 100644 Source/ProviderApparel.cs delete mode 100644 Source/Providers.cs delete mode 100644 Source/Randomizer.cs delete mode 100644 Source/RelatedPawn.cs delete mode 100644 Source/RelationshipGroup.cs delete mode 100644 Source/RelationshipManager.cs delete mode 100644 Source/ScenPart_CustomScatterThingsNearPlayerStart.cs delete mode 100644 Source/SelectedAnimal.cs delete mode 100644 Source/SelectedPet.cs delete mode 100644 Source/State.cs create mode 100644 Source/TemporaryPawn.cs delete mode 100644 Source/ThingCache.cs create mode 100644 Source/UtilityAge.cs create mode 100644 Source/UtilityApparel.cs create mode 100644 Source/UtilityColor.cs delete mode 100644 Source/UtilityCopy.cs create mode 100644 Source/UtilityEnumerable.cs create mode 100644 Source/UtilityEquipmentSpawnType.cs create mode 100644 Source/UtilityGUIState.cs create mode 100644 Source/UtilityPawns.cs create mode 100644 Source/UtilityQuality.cs create mode 100644 Source/UtilitySaveLoad.cs create mode 100644 Source/UtilityTraits.cs delete mode 100644 Source/Version3/ColonistLoaderVersion3.cs delete mode 100644 Source/Version3/PresetLoaderVersion3.cs delete mode 100644 Source/Version4/ColonistLoaderVersion4.cs delete mode 100644 Source/Version4/PresetLoaderVersion4.cs delete mode 100644 Source/Version5/PresetLoaderV5.cs create mode 100644 Source/Version5/SaveRecordApparelV5.cs create mode 100644 Source/Version5/SaveRecordPossessionV5.cs create mode 100644 Source/Version5/SaveRecordTemporaryPawnV5.cs create mode 100644 Source/Version5/SaveRecordTitleV5.cs create mode 100644 Source/ViewState.cs create mode 100644 Source/WidgetColorSelector.cs create mode 100644 Source/WidgetEquipmentField.cs create mode 100644 Source/WidgetEquipmentLoadingProgressBar.cs rename Source/{Field.cs => WidgetField.cs} (51%) create mode 100644 Source/WidgetInfoButton.cs create mode 100644 Source/WidgetPortrait.cs rename Source/{ScrollViewHorizontal.cs => WidgetScrollViewHorizontal.cs} (93%) rename Source/{ScrollViewVertical.cs => WidgetScrollViewVertical.cs} (95%) diff --git a/EdBPrepareCarefully.csproj b/EdBPrepareCarefully.csproj index d07e32e..2684ddb 100644 --- a/EdBPrepareCarefully.csproj +++ b/EdBPrepareCarefully.csproj @@ -35,9 +35,9 @@ False - Libraries\Harmony\2.2.2\0Harmony.dll + Libraries\Harmony\2.3.3\0Harmony.dll - + False Libraries\Assembly-CSharp.dll @@ -62,53 +62,72 @@ - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + - - - - - - - - - + + + + + + @@ -120,18 +139,10 @@ - - - - - - - - @@ -150,13 +161,9 @@ - - - - @@ -164,111 +171,120 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + - - + - - + + - - - - + - - - - - - - - - + + + - - - - - - - - + - - - - - - - - - - - - - - + - - diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs index 3e2b6af..dac862f 100644 --- a/Properties/AssemblyInfo.cs +++ b/Properties/AssemblyInfo.cs @@ -6,7 +6,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("EdB Prepare Carefully Mod")] -[assembly: AssemblyCopyright("Copyright © 2014-2023")] +[assembly: AssemblyCopyright("Copyright © 2014-2024")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -14,4 +14,4 @@ [assembly: AssemblyVersion("1.1.1")] // Increment for each new release -[assembly: AssemblyFileVersion("1.4.3")] +[assembly: AssemblyFileVersion("1.5.1")] diff --git a/Resources/About/About.xml b/Resources/About/About.xml index 8b971f8..083f4c6 100644 --- a/Resources/About/About.xml +++ b/Resources/About/About.xml @@ -9,16 +9,25 @@
  • 1.2
  • 1.3
  • 1.4
  • +
  • 1.5
  • + + +
  • + brrainz.harmony + Harmony + steam://url/CommunityFilePage/2009463077 + https://github.com/pardeike/HarmonyRimWorld/releases/latest +
  • +
    +
    Customize your colonists, choose your gear and prepare carefully for your crash landing! If you get a set of starting colonists that you like, save them as a preset so that you can start your game the same way next time. -[Version 1.4.3] +[Version 1.5.1]
  • net.pardeike.rimworld.mod.harmony
  • -
  • UnlimitedHugs.HugsLib
  • -
  • jecrell.jecstools
  • \ No newline at end of file diff --git a/Resources/About/Manifest.xml b/Resources/About/Manifest.xml index 430a0b5..8093c8c 100644 --- a/Resources/About/Manifest.xml +++ b/Resources/About/Manifest.xml @@ -1,7 +1,7 @@ EdB.PrepareCarefully - 1.4.3 + 1.5.1 false https://github.com/edbmods/EdBPrepareCarefully/raw/master/Resources/About/Manifest.xml https://github.com/edbmods/EdBPrepareCarefully/releases/latest diff --git a/Resources/CHANGELOG.txt b/Resources/CHANGELOG.txt index c32fb2d..cddc97f 100644 --- a/Resources/CHANGELOG.txt +++ b/Resources/CHANGELOG.txt @@ -1,3 +1,23 @@ + _____________________________________________________________________________ + + Version 1.5.1 + _____________________________________________________________________________ + + - Compatibility for 1.5 + - Now requires the Harmony mod + - Added new larger layout + - Changed the user interface for editing apparel to better support how + apparel works in the game by adding an Apparel panel + - Added panels for Possession, Titles and Abilities + - Removed the Faction panel + - Moved favorite color selection out of its own panel and into the Backstory + panel + - Added new dropdown filters and a text search filter to the equipment + selection panel + - Added options for quality, hit points and spawn type to equipment selection + panel + - Reworked the way that pawns are modified and added to the world + _____________________________________________________________________________ Version 1.4.3 diff --git a/Resources/Common/Languages/English/Keyed/EdBPrepareCarefully.xml b/Resources/Common/Languages/English/Keyed/EdBPrepareCarefully.xml index 7a5f01d..d5a0c81 100644 --- a/Resources/Common/Languages/English/Keyed/EdBPrepareCarefully.xml +++ b/Resources/Common/Languages/English/Keyed/EdBPrepareCarefully.xml @@ -1,6 +1,26 @@  + + + + + + + + + + + + + + + + + + + + @@ -148,6 +168,7 @@ Select Select Abilities + Psycast Level: {0} Add Filter No Disabled Work Types @@ -180,6 +201,10 @@ You must select a condition {0} ({1}) + All Layers + Add + Update + Load Save Delete this Colonist @@ -200,6 +225,7 @@ There was a problem while saving. You may not be able to load the preset. Could not fully load preset. Check that all required mods are enabled. Could not load saved character. Check that all required mods are enabled. + Failed to load preset {0} Loaded preset {0} Select a severity level @@ -219,6 +245,7 @@ "{0}" {0} {1} + Default Animals Apparel Furniture @@ -230,6 +257,20 @@ Processing materials... Processing items... Finished + Spawn With Colonists + Spawn Near Colonists + Possessions - {0} + Animals + All Categories + All Mods + Cost: {0} + Default + Gender: + Hit Points: + Material: + Quality: + Random Pet + Spawn Type: You've spent too many points. You must have at least one colonist. @@ -291,6 +332,7 @@ Before reporting an error, please try to figure out which mod may be causing the Apparel: {0} {0}: {1} Implants: {0} + Possessions: {0} Disable Point Limits You've spent too many points. Points Remaining: {0} @@ -321,10 +363,13 @@ Disable point limits to prepare your starting colonists with no limitations. This is not the case for faction leaders. If you make this character a faction leader, your apparel customizations will be preserved. + Add Add Equipment All Materials No Materials + Favorite Color: + Colony You must select a location @@ -388,6 +433,7 @@ Click the randomize button to generate a colonist with this xenotypeRelationships World - + + \ No newline at end of file diff --git a/Resources/Common/Textures/EdB/PrepareCarefully/ButtonManage.png b/Resources/Common/Textures/EdB/PrepareCarefully/ButtonManage.png new file mode 100644 index 0000000000000000000000000000000000000000..1137401ca029713e2b98ca0550c328949c8f9f63 GIT binary patch literal 2833 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 IM6N<$f?OSBmjD0& literal 0 HcmV?d00001 diff --git a/Resources/Common/Textures/EdB/PrepareCarefully/FieldAtlasWhiteOutline.png b/Resources/Common/Textures/EdB/PrepareCarefully/FieldAtlasWhiteOutline.png new file mode 100644 index 0000000000000000000000000000000000000000..16a9124cfae980e5cfe594a462fd32370f04a886 GIT binary patch literal 2880 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

  • Common
  • +
  • Pre1.5
  • 1.2
  • Common
  • +
  • Pre1.5
  • 1.2
  • Common
  • +
  • Pre1.5
  • 1.3
  • Common
  • +
  • Pre1.5
  • 1.4
  • + +
  • Common
  • +
  • 1.5
  • +
    \ No newline at end of file diff --git a/Source/AgeModifier.cs b/Source/AgeModifier.cs index 8e5447c..95dc23e 100644 --- a/Source/AgeModifier.cs +++ b/Source/AgeModifier.cs @@ -5,65 +5,71 @@ using System.Threading.Tasks; using RimWorld; using Verse; -using Verse.AI; namespace EdB.PrepareCarefully { public class AgeModifier { public static long TicksPerYear = 3600000L; public static long TicksPerDay = 60000L; public static long DaysPerYear = 60L; + private BackstoryDef newbornBackstory; + private BackstoryDef childBackstory; + public static long TicksFromYearsAndDays(int years, int days) { return ((long)years * TicksPerYear) + ((long)days * TicksPerDay); } + public static int TicksToDays(long ticks) { + return (int)(ticks / TicksPerDay); + } + public static int TicksToDayOfYear(long ticks) { + return TicksToDays(ticks) % (int)AgeModifier.DaysPerYear; + } + public static int TicksOfDay(long ticks) { + return (int)(ticks % TicksPerDay); + } - BackstoryDef newbornBackstory; - BackstoryDef childBackstory; - public void ModifyBiologicalAge(CustomPawn pawn, long ticks) { - if (pawn == null) { + public void ModifyBiologicalAge(CustomizedPawn customizedPawn, long ticks) { + if (customizedPawn == null || customizedPawn.Pawn == null) { return; } - int previousLifeStageIndex = pawn.Pawn.ageTracker.CurLifeStageIndex; - LifeStageDef previousLifeStage = pawn.Pawn.ageTracker.CurLifeStage; - LifeStageAge previousLifeStageAge = pawn.Pawn.ageTracker.CurLifeStageRace; + Pawn pawn = customizedPawn.Pawn; + + int previousLifeStageIndex = pawn.ageTracker.CurLifeStageIndex; + LifeStageDef previousLifeStage = pawn.ageTracker.CurLifeStage; + LifeStageAge previousLifeStageAge = pawn.ageTracker.CurLifeStageRace; - bool wasNewborn = IsNewborn(pawn); - bool wasChild = IsChild(pawn); + bool wasNewborn = UtilityPawns.IsNewborn(pawn); + bool wasChild = UtilityPawns.IsChild(pawn); bool hadChildhoodBackstory = HasChildhoodBackstory(pawn); bool hadAdulthoodBackstory = HasAdulthoodBackstory(pawn); - pawn.Pawn.ageTracker.AgeBiologicalTicks = ticks; + pawn.ageTracker.AgeBiologicalTicks = ticks; bool hasChildhoodBackstory = HasChildhoodBackstory(pawn); bool hasAdulthoodBackstory = HasAdulthoodBackstory(pawn); - bool isNewborn = IsNewborn(pawn); - bool isChild = IsChild(pawn); + bool isNewborn = UtilityPawns.IsNewborn(pawn); + bool isChild = UtilityPawns.IsChild(pawn); if (hasAdulthoodBackstory && !hadAdulthoodBackstory) { - pawn.Pawn.story.Adulthood = pawn.LastSelectedAdulthoodBackstory; - pawn.ResetBackstories(); + pawn.story.Adulthood = customizedPawn.Customizations.AdulthoodBackstory; } else if (!hasAdulthoodBackstory && hadAdulthoodBackstory) { - pawn.Pawn.story.Adulthood = null; - pawn.ResetBackstories(); + pawn.story.Adulthood = null; } - if (isNewborn && pawn.Pawn.story.Childhood != NewbornBackstory) { - pawn.Pawn.story.Childhood = NewbornBackstory; - pawn.ResetBackstories(); + if (isNewborn && pawn.story.Childhood != NewbornBackstory) { + pawn.story.Childhood = NewbornBackstory; } - else if (isChild && pawn.Pawn.story.Childhood != ChildBackstory) { - pawn.Pawn.story.Childhood = ChildBackstory; - pawn.ResetBackstories(); + else if (isChild && pawn.story.Childhood != ChildBackstory) { + pawn.story.Childhood = ChildBackstory; } - pawn.Pawn.ClearCachedLifeStage(); - pawn.Pawn.ClearCachedHealth(); - pawn.MarkPortraitAsDirty(); + pawn.ClearCachedLifeStage(); + pawn.ClearCachedHealth(); - int newLifeStageIndex = pawn.Pawn.ageTracker.CurLifeStageIndex; - LifeStageDef newLifeStage = pawn.Pawn.ageTracker.CurLifeStage; - LifeStageAge newLifeStageAge = pawn.Pawn.ageTracker.CurLifeStageRace; + int newLifeStageIndex = pawn.ageTracker.CurLifeStageIndex; + LifeStageDef newLifeStage = pawn.ageTracker.CurLifeStage; + LifeStageAge newLifeStageAge = pawn.ageTracker.CurLifeStageRace; if (newLifeStage != previousLifeStage) { Logger.Debug("Pawn life stage changes from " + previousLifeStage.defName + " to " + newLifeStage.defName); @@ -71,37 +77,19 @@ public void ModifyBiologicalAge(CustomPawn pawn, long ticks) { } } - public void ModifyChronologicalAge(CustomPawn pawn, long ticks) { - if (pawn == null) { + public void ModifyChronologicalAge(CustomizedPawn pawn, long ticks) { + if (pawn == null || pawn.Pawn == null) { return; } pawn.Pawn.ageTracker.AgeChronologicalTicks = ticks; } - public bool IsNewborn(CustomPawn pawn) { - DevelopmentalStage? d = pawn.Pawn.ageTracker?.CurLifeStage?.developmentalStage; - var value = d.HasValue ? d.Value.Baby() : false; - //Logger.Debug("IsNewborn = " + value); - return value; + public bool HasChildhoodBackstory(Pawn pawn) { + return !UtilityPawns.IsNewborn(pawn) && !UtilityPawns.IsChild(pawn); } - public bool IsChild(CustomPawn pawn) { - DevelopmentalStage? d = pawn.Pawn.ageTracker?.CurLifeStage?.developmentalStage; - var value = d.HasValue ? d.Value.Child() : false; - //Logger.Debug("IsChild = " + value); - return value; + public bool HasAdulthoodBackstory(Pawn pawn) { + return UtilityPawns.IsAdult(pawn); } - public bool IsAdult(CustomPawn pawn) { - DevelopmentalStage? d = pawn.Pawn.ageTracker?.CurLifeStage?.developmentalStage; - return d.HasValue ? d.Value.Adult() : false; - } - - public bool HasChildhoodBackstory(CustomPawn pawn) { - return !IsNewborn(pawn) && !IsChild(pawn); - } - public bool HasAdulthoodBackstory(CustomPawn pawn) { - return IsAdult(pawn); - } - public BackstoryDef NewbornBackstory { get { if (newbornBackstory== null) { diff --git a/Source/AnimalDatabase.cs b/Source/AnimalDatabase.cs deleted file mode 100644 index 58a83bb..0000000 --- a/Source/AnimalDatabase.cs +++ /dev/null @@ -1,105 +0,0 @@ -using RimWorld; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Verse; - -namespace EdB.PrepareCarefully { - public class AnimalDatabase { - private List animals = new List(); - private Dictionary animalDictionary = new Dictionary(); - private CostCalculator costCalculator = new CostCalculator(); - - public AnimalDatabase() { - Initialize(); - } - public IEnumerable AllAnimals { - get { - return animals; - } - } - public AnimalRecord FindAnimal(AnimalRecordKey key) { - AnimalRecord result; - if (animalDictionary.TryGetValue(key, out result)) { - return result; - } - else { - return null; - } - } - protected void Initialize() { - foreach (var def in DefDatabase.AllDefs.Where((ThingDef def) => { - if (def.race != null && def.race.Animal == true) { - return true; - } - else { - return false; - } - })) { - if (def.race.hasGenders) { - AnimalRecord femaleRecord = CreateAnimalRecord(def, Gender.Female); - if (femaleRecord != null) { - AddAnimalRecord(femaleRecord); - } - AnimalRecord maleRecord = CreateAnimalRecord(def, Gender.Male); - if (maleRecord != null) { - AddAnimalRecord(maleRecord); - } - } - else { - AnimalRecord record = CreateAnimalRecord(def, Gender.None); - if (record != null) { - AddAnimalRecord(record); - } - } - } - } - protected void AddAnimalRecord(AnimalRecord animal) { - if (!animalDictionary.ContainsKey(animal.Key)) { - animals.Add(animal); - animalDictionary.Add(animal.Key, animal); - } - } - protected AnimalRecord CreateAnimalRecord(ThingDef def, Gender gender) { - double baseCost = costCalculator.GetBaseThingCost(def, null); - if (baseCost == 0) { - return null; - } - - AnimalRecord result = new AnimalRecord(); - result.ThingDef = def; - result.Gender = gender; - result.Cost = baseCost; - try { - Pawn pawn = CreatePawn(def, gender); - if (pawn == null) { - return null; - } - else { - result.Thing = pawn; - } - } - catch (Exception e) { - Logger.Warning("Failed to create a pawn for animal database record: " + def.defName, e); - return null; - } - return result; - } - - protected Pawn CreatePawn(ThingDef def, Gender gender) { - PawnKindDef kindDef = (from td in DefDatabase.AllDefs - where td.race == def - select td).FirstOrDefault(); - if (kindDef != null) { - Pawn pawn = PawnGenerator.GeneratePawn(kindDef, null); - pawn.gender = gender; - pawn.Drawer.renderer.graphics.ResolveAllGraphics(); - return pawn; - } - else { - return null; - } - } - } -} diff --git a/Source/AnimalRecord.cs b/Source/AnimalRecord.cs deleted file mode 100644 index 7386b7f..0000000 --- a/Source/AnimalRecord.cs +++ /dev/null @@ -1,40 +0,0 @@ -using RimWorld; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Verse; - -namespace EdB.PrepareCarefully { - public class AnimalRecord { - public ThingDef ThingDef; - public Gender Gender; - public double Cost = 0; - public string label; - public Thing Thing; - public AnimalRecord() { - } - public AnimalRecord(ThingDef thingDef, Gender gender) { - this.ThingDef = thingDef; - this.Gender = gender; - } - public AnimalRecordKey Key { - get { - return new AnimalRecordKey(ThingDef, Gender); - } - } - public string Label { - get { - if (label != null) { - return label; - } - if (Gender == Gender.None) { - return "EdB.PC.Animals.LabelWithoutGender".Translate(ThingDef.LabelCap); - } - else { - return "EdB.PC.Animals.LabelWithGender".Translate(ThingDef.LabelCap, Gender.GetLabel()); - } - } - } - } -} diff --git a/Source/AnimalRecordKey.cs b/Source/AnimalRecordKey.cs deleted file mode 100644 index c887a0a..0000000 --- a/Source/AnimalRecordKey.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Verse; - -namespace EdB.PrepareCarefully { - public struct AnimalRecordKey { - public ThingDef ThingDef; - public Gender Gender; - public AnimalRecordKey(ThingDef thingDef, Gender gender) { - this.ThingDef = thingDef; - this.Gender = gender; - } - public override bool Equals(System.Object o) { - if (o == null) { - return false; - } - if (!(o is AnimalRecordKey)) { - return false; - } - AnimalRecordKey pair = (AnimalRecordKey)o; - return (this.ThingDef == pair.ThingDef && this.Gender == pair.Gender); - } - public override int GetHashCode() { - unchecked { - int a = ThingDef != null ? ThingDef.GetHashCode() : 0; - return (31 * a) * 31 + Gender.GetHashCode(); - } - } - } - -} diff --git a/Source/ColonistSaver.cs b/Source/ColonistSaver.cs deleted file mode 100644 index 4cd848d..0000000 --- a/Source/ColonistSaver.cs +++ /dev/null @@ -1,33 +0,0 @@ -using RimWorld; -using System; -using System.Collections.Generic; -using System.Linq; -using Verse; - -namespace EdB.PrepareCarefully { - public static class ColonistSaver { - // - // Static Methods - // - public static void SaveToFile(CustomPawn customPawn, string colonistName) { - SaveRecordPawnV5 pawn = new SaveRecordPawnV5(customPawn); - try { - Scribe.saver.InitSaving(ColonistFiles.FilePathForSavedColonist(colonistName), "character"); - string versionStringFull = "5"; - Scribe_Values.Look(ref versionStringFull, "version", null, false); - string modString = GenText.ToCommaList(Enumerable.Select(LoadedModManager.RunningMods, (Func)(mod => mod.Name)), true); - Scribe_Values.Look(ref modString, "mods", null, false); - - Scribe_Deep.Look(ref pawn, "pawn"); - } - catch (Exception e) { - Logger.Error("Failed to save preset file"); - throw e; - } - finally { - Scribe.saver.FinalizeSaving(); - Scribe.mode = LoadSaveMode.Inactive; - } - } - } -} diff --git a/Source/ColorValidator.cs b/Source/ColorValidator.cs deleted file mode 100644 index 1cd330e..0000000 --- a/Source/ColorValidator.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using UnityEngine; -using Verse; - -namespace EdB.PrepareCarefully { - public static class ColorValidator { - public static Color ColorEmpty = new Color(-1f, -1f, -1f, -1f); - - public static bool Validate(ColorGenerator generator, Color color) { - if (typeof(ColorGenerator_Options).Equals(generator.GetType())) { - return Validate((ColorGenerator_Options)generator, color); - } - else if (typeof(ColorGenerator_Single).Equals(generator.GetType())) { - return Validate((ColorGenerator_Single)generator, color); - } - else if (typeof(ColorGenerator_StandardApparel).Equals(generator.GetType())) { - return Validate((ColorGenerator_StandardApparel)generator, color); - } - else if (typeof(ColorGenerator_White).Equals(generator.GetType())) { - return Validate((ColorGenerator_White)generator, color); - } - else { - return true; - } - } - - public static bool Validate(ColorGenerator_Single generator, Color color) { - return color == generator.color; - } - - public static bool Validate(ColorGenerator_Options generator, Color color) { - foreach (ColorOption allowedColor in generator.options) { - if (allowedColor.only != ColorEmpty) { - if (color == allowedColor.only) { - return true; - } - } - else if (color.r >= allowedColor.min.r && color.g >= allowedColor.min.g && color.b >= allowedColor.min.b && color.a >= allowedColor.min.a - && color.r <= allowedColor.max.r && color.g <= allowedColor.max.g && color.b <= allowedColor.max.b && color.a <= allowedColor.max.a) { - return true; - } - } - return false; - } - - public static bool Validate(ColorGenerator_StandardApparel generator, Color color) { - return true; - } - - public static bool Validate(ColorGenerator_White generator, Color color) { - return (color == Color.white); - } - } -} - diff --git a/Source/Configuration.cs b/Source/Configuration.cs deleted file mode 100644 index bf9c103..0000000 --- a/Source/Configuration.cs +++ /dev/null @@ -1,13 +0,0 @@ -using RimWorld; -using System; -using System.Collections.Generic; -using Verse; - -namespace EdB.PrepareCarefully { - public class Configuration { - public bool showPoints = true; - public int points = 12000; - public int minColonists = 1; - public bool pointsEnabled = false; - } -} diff --git a/Source/Constraints.cs b/Source/Constraints.cs deleted file mode 100644 index 39ade9a..0000000 --- a/Source/Constraints.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; -namespace EdB.PrepareCarefully { - public static class Constraints { - public static int AgeBiologicalMin = 14; - public static int AgeChronologicalMax = 3200; - public static float MaxAgeMultiplier = 1.375f; - public static int MaxTraits = int.MaxValue; - } -} diff --git a/Source/Controller.cs b/Source/Controller.cs deleted file mode 100644 index ba07de5..0000000 --- a/Source/Controller.cs +++ /dev/null @@ -1,467 +0,0 @@ -using RimWorld; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using UnityEngine; -using Verse; -namespace EdB.PrepareCarefully { - public class Controller { - private State state; - private ControllerPawns subcontrollerCharacters; - private ControllerEquipment subcontrollerEquipment; - private ControllerRelationships subcontrollerRelationships; - - public ControllerPawns SubcontrollerCharacters { - get { - return subcontrollerCharacters; - } - } - public ControllerEquipment SubcontrollerEquipment { - get { - return subcontrollerEquipment; - } - } - public ControllerRelationships SubcontrollerRelationships { - get { - return subcontrollerRelationships; - } - } - public Controller(State state) { - this.state = state; - subcontrollerCharacters = new ControllerPawns(state); - subcontrollerEquipment = new ControllerEquipment(state); - subcontrollerRelationships = new ControllerRelationships(state); - subcontrollerCharacters.CheckPawnCapabilities(); - } - - public bool CanDoNext() { - Configuration config = PrepareCarefully.Instance.Config; - if (config.pointsEnabled) { - if (PrepareCarefully.Instance.PointsRemaining < 0) { - Messages.Message("EdB.PC.Error.NotEnoughPoints".Translate(), MessageTypeDefOf.RejectInput, false); - return false; - } - } - int pawnCount = PrepareCarefully.Instance.Pawns.Count; - if (pawnCount < config.minColonists) { - if (config.minColonists == 1) { - Messages.Message("EdB.PC.Error.NotEnoughColonists1".Translate(config.minColonists), MessageTypeDefOf.RejectInput, false); - return false; - } - else { - Messages.Message("EdB.PC.Error.NotEnoughColonists".Translate(config.minColonists), MessageTypeDefOf.RejectInput, false); - return false; - } - } - // TODO: This is no good as it is. In vanilla, if the player only has a nickname, it copies that nickname into the - // first and last names. We need to do something similar and adjust this validation accordingly. - //foreach (CustomPawn current in PrepareCarefully.Instance.Pawns) { - // if (!current.Name.IsValid) { - // Messages.Message("EveryoneNeedsValidName".Translate(), MessageTypeDefOf.RejectInput, false); - // return false; - // } - //} - return true; - } - - public void StartGame() { - if (CanDoNext()) { - PrepareCarefully.Instance.Active = true; - PrepareCarefully.Instance.State.Page.Close(false); - PrepareCarefully.Instance.State.Page = null; - PrepareGame(); - PrepareCarefully.Instance.DoNextInBasePage(); - PrepareCarefully.RemoveInstance(); - PortraitsCache.Clear(); - } - } - - public void LoadPreset(string name) { - if (string.IsNullOrEmpty(name)) { - Logger.Warning("Trying to load a preset without a name"); - return; - } - bool result = PresetLoader.LoadFromFile(PrepareCarefully.Instance, name); - if (result) { - state.AddMessage("EdB.PC.Dialog.Preset.Loaded".Translate(name)); - state.CurrentColonyPawn = state.ColonyPawns.FirstOrDefault(); - state.CurrentWorldPawn = state.WorldPawns.FirstOrDefault(); - } - subcontrollerCharacters.CheckPawnCapabilities(); - } - - public void SavePreset(string name) { - PrepareCarefully.Instance.Filename = name; - if (string.IsNullOrEmpty(name)) { - Logger.Warning("Trying to save a preset without a name"); - return; - } - PresetSaver.SaveToFile(PrepareCarefully.Instance, PrepareCarefully.Instance.Filename); - state.AddMessage("SavedAs".Translate(PrepareCarefully.Instance.Filename)); - } - - public void PrepareGame() { - PrepareRelatedPawns(); - PrepareColonists(); - PrepareWorldPawns(); - PrepareScenario(); - } - - // Replace the originally selected scenario with one that reflects the equipment and pawns chosen in Prepare Carefully. - protected void PrepareScenario() { - // We're going to create two copies of the original scenario. The first one is the one that we'll actually use to spawn the new game. - // This one is potentially going to include custom scenario parts that are specific to Prepare Carefully. Once we've spawned the game, - // we don't want to leave that scenario associated with the save, because we don't want the save to be dependent on Prepare Carefully. - // So we have a second copy of the scenario. This one uses vanilla-friendly alternatives to the Prepare Carefully-specific scenario parts. - // After we spawn the game, we'll swap in this vanilla-friendly version of the scenario. - var actualScenario = CopyScenarioWithoutParts(Find.Scenario); - var vanillaFriendlyScenario = CopyScenarioWithoutParts(Find.Scenario); - (var actualParts, var vanillaFriendlyParts) = ReplaceScenarioParts(Find.Scenario); - - actualScenario.SetPrivateField("parts", actualParts); - Current.Game.Scenario = actualScenario; - - vanillaFriendlyScenario.SetPrivateField("parts", vanillaFriendlyParts); - PrepareCarefully.VanillaFriendlyScenario = vanillaFriendlyScenario; - } - - protected Scenario CopyScenarioWithoutParts(Scenario source) { - Scenario result = new Scenario() { - name = source.name, - summary = source.summary, - description = source.description, - }; - ScenPart_PlayerFaction faction = source.GetPrivateField("playerFaction"); - result.SetPrivateField("playerFaction", faction); - return result; - } - - protected void PrepareColonists() { - List colonists = new List(); - foreach (var customPawn in state.Pawns) { - if (customPawn.Type == CustomPawnType.Colonist) { - customPawn.Pawn.SetFactionDirect(Faction.OfPlayer); - if (customPawn.Pawn.workSettings == null) { - customPawn.Pawn.workSettings = new Pawn_WorkSettings(customPawn.Pawn); - } - customPawn.Pawn.workSettings.EnableAndInitialize(); - colonists.Add(customPawn.Pawn); - Find.GameInitData.startingPossessions[customPawn.Pawn] = new List(); - } - } - Find.GameInitData.startingPawnCount = colonists.Count; - Find.GameInitData.startingAndOptionalPawns = colonists; - } - - protected void PrepareWorldPawns() { - foreach (var customPawn in state.Pawns) { - if (customPawn.Type == CustomPawnType.World) { - Find.GameInitData.startingPossessions[customPawn.Pawn] = new List(); - AddPawnToWorld(customPawn); - } - } - } - - protected void PrepareRelatedPawns() { - // Get all of the related pawns. - List relatedPawns = new RelationshipBuilder(PrepareCarefully.Instance.RelationshipManager.Relationships.ToList(), - PrepareCarefully.Instance.RelationshipManager.ParentChildGroups).Build(); - - // Add related pawns who are not already in the world to the world - foreach (var customPawn in relatedPawns) { - AddPawnToWorld(customPawn); - - // For any pawn for which relationships have been defined, we want to make sure they are protected from world garbage collection. - // We don't have to worry about it for non-colony pawns, but we have to do it manually for all other pawns: - if (customPawn.Type != CustomPawnType.Colonist) { - if (!Find.WorldPawns.ForcefullyKeptPawns.Contains(customPawn.Pawn)) { - Find.WorldPawns.ForcefullyKeptPawns.Add(customPawn.Pawn); - } - } - } - } - - protected void AddPawnToWorld(CustomPawn pawn) { - - // Don't add colonists to the world - if (pawn.Type == CustomPawnType.Colonist) { - return; - } - - // Don't add hidden pawns to the world--they should already be there - if (pawn.Type == CustomPawnType.Hidden) { - return; - } - - // Killing a pawn adds it to the world - if (pawn.Type == CustomPawnType.Temporary) { - if (!pawn.Pawn.Dead) { - pawn.Pawn.Kill(null, null); - } - return; - } - - //Logger.Debug("Adding pawn to the world: " + pawn.LabelShort); - - // If we have a custom faction setting, handle that. - if (pawn.Faction != null && pawn.Faction != PrepareCarefully.Instance.Providers.Factions.RandomFaction) { - // If someone has gone to the trouble of defining a custom faction for a world pawn, make sure that - // the pawn isn't going to get garbage collected - if (!Find.WorldPawns.ForcefullyKeptPawns.Contains(pawn.Pawn)) { - Find.WorldPawns.ForcefullyKeptPawns.Add(pawn.Pawn); - } - - // If they are assigned to a specific faction, assign them either as a leader or as a regular pawn. - if (pawn.Faction.Faction != null) { - if (pawn.Faction.Leader) { - MakePawnIntoFactionLeader(pawn); - } - try { - pawn.Pawn.SetFaction(pawn.Faction.Faction, null); - } - catch (Exception) { - Logger.Warning("Failed to add a world pawn to the expected faction"); - } - } - // If they are assigned to a random faction of a specific def, choose the random faction and assign it. - else { - try { - List availableFactions = PrepareCarefully.Instance.Providers.Factions.GetFactions(pawn.Faction.Def); - if (availableFactions != null && availableFactions.Count > 0) { - Faction faction = availableFactions.RandomElement(); - pawn.Pawn.SetFaction(faction, null); - } - else { - Logger.Warning(String.Format("Couldn't assign pawn {0} to specified faction. Faction not available in world", pawn.LabelShort)); - pawn.Pawn.SetFactionDirect(null); - } - } - catch (Exception) { - Logger.Warning("Failed to add a world pawn to the expected faction"); - } - } - } - // If they are assigned to a completely random faction, set their faction to null. It will get reassigned automatically. - else { - pawn.Pawn.SetFactionDirect(null); - } - - // Don't add pawns to the world if they have already been added. - if (Find.World.worldPawns.Contains(pawn.Pawn) || Find.GameInitData.startingAndOptionalPawns.Contains(pawn.Pawn)) { - Logger.Message("Didn't add pawn " + pawn.ShortName + " to the world because they've already been added"); - return; - } - else { - Find.GameInitData.startingAndOptionalPawns.Add(pawn.Pawn); - } - - } - - protected void MakePawnIntoFactionLeader(CustomPawn pawn) { - FactionDef factionDef = pawn.Faction.Def; - List source = new List(); - foreach (PawnGroupMaker pawnGroupMaker in factionDef.pawnGroupMakers.Where((Func)(x => x.kindDef == PawnGroupKindDefOf.Combat))) { - foreach (PawnGenOption option in pawnGroupMaker.options) { - if (option.kind.factionLeader) - source.Add(option.kind); - } - } - PawnKindDef result; - if (source.TryRandomElement(out result)) { - Pawn randomPawn = PawnGenerator.GeneratePawn(result, pawn.Faction.Faction); - pawn.Pawn.kindDef = randomPawn.kindDef; - pawn.Pawn.relations.everSeenByPlayer = true; - - List inventory = new List(); - foreach (var thing in randomPawn.inventory.innerContainer) { - inventory.Add(thing); - } - foreach (var thing in inventory) { - randomPawn.inventory.innerContainer.Remove(thing); - pawn.Pawn.inventory.innerContainer.TryAdd(thing, true); - } - List equipment = new List(); - foreach (var thing in randomPawn.equipment.AllEquipmentListForReading) { - equipment.Add(thing); - } - foreach (var thing in equipment) { - randomPawn.equipment.Remove(thing); - pawn.Pawn.equipment.AddEquipment(thing); - } - } - // Make the pawn into the faction leader. - pawn.Faction.Faction.leader = pawn.Pawn; - } - - protected Tuple, List> ReplaceScenarioParts(Scenario originalScenario) { - // Get a list of all of the original scenario parts - List originalParts = ReflectionUtil.GetFieldValue>(Find.Scenario, "parts"); - List actualParts = new List(); - List vanillaFriendlyParts = new List(); - - // Fill in the part lists with the scenario parts that we're not going to replace. We won't need to modify any of these. - int index = -1; - foreach (var part in originalParts) { - index++; - bool partReplaced = PrepareCarefully.Instance.ReplacedScenarioParts.Contains(part); - if (!partReplaced) { - actualParts.Add(originalParts[index]); - vanillaFriendlyParts.Add(originalParts[index]); - } - //Logger.Debug(String.Format("[{0}] Replaced? {1}: {2} {3}", index, partReplaced, part.Label, String.Join(", ", part.GetSummaryListEntries("PlayerStartsWith")))); - } - - // Replace the pawn count in the configure pawns scenario parts to reflect the number of - // pawns that were selected in Prepare Carefully. - ScenPart_ConfigPage_ConfigureStartingPawns originalStartingPawnsPart = originalParts.FirstOrDefault(p => p is ScenPart_ConfigPage_ConfigureStartingPawns) as ScenPart_ConfigPage_ConfigureStartingPawns; - if (originalStartingPawnsPart != null) { - ScenPart_ConfigPage_ConfigureStartingPawns actualStartingPawnsPart = UtilityCopy.CopyExposable(originalStartingPawnsPart); - int pawnCount = PrepareCarefully.Instance.ColonyPawns.Count; - actualStartingPawnsPart.pawnCount = pawnCount; - actualStartingPawnsPart.pawnChoiceCount = pawnCount; - actualParts.Add(actualStartingPawnsPart); - vanillaFriendlyParts.Add(actualStartingPawnsPart); - } - - // Sort the equipment from highest count to lowest so that gear is less likely to get blocked - // if there's a bulk item included. If you don't do this, then a large number of an item (meals, - // for example) could fill up the spawn area right away and then the rest of the items would have - // nowhere to spawn. - PrepareCarefully.Instance.Equipment.Sort((EquipmentSelection a, EquipmentSelection b) => { - return a.Count.CompareTo(b.Count); - }); - - // Create all of the scatter things scenario parts that we need. Make note of the maximum number of stacks - // that could be created. We must use a custom scatter scenario part because we need to customize the spawn - // radius when there are large numbers of resources. - //List scatterParts = new List(); - List scatterParts = new List(); - int scatterStackCount = 0; - foreach (var e in PrepareCarefully.Instance.Equipment) { - if (e.record.animal) { - continue; - } - if (!PlayerStartsWith(e)) { - int stacks = Mathf.CeilToInt((float)e.Count / (float)e.ThingDef.stackLimit); - scatterStackCount += stacks; - ScenPart_ScatterThingsNearPlayerStart part = new ScenPart_ScatterThingsNearPlayerStart(); - part.def = ScenPartDefOf.ScatterThingsNearPlayerStart; - part.SetPrivateField("thingDef", e.ThingDef); - part.SetPrivateField("stuff", e.StuffDef); - part.SetPrivateField("count", e.Count); - actualParts.Add(part); - vanillaFriendlyParts.Add(part); - } - } - - // Go through all of the equipment that's meant to spawn as a starting thing. We'll try to add - // a starting thing scenario part for each, but we're keeping track of the overall density of - // things that will be spawned into the area (based on stack count and spawn area radius). If - // the density is too high, we'll add the things as scattered nearby scenario parts instead. - float radius = 7.5f; - float area = Mathf.PI * radius * radius; - float maxDensity = 0.25f; - int maxStacks = Mathf.FloorToInt(maxDensity * area); - int stackCount = 0; - foreach (var e in PrepareCarefully.Instance.Equipment) { - if (e.record.animal) { - continue; - } - if (PlayerStartsWith(e)) { - int scatterCount = 0; - int nearCount = e.Count; - - // If the number of stacks added by this part will push us over the density - // limit, then we split the stacks into two scenario parts, one that spawns - // as a starting thing and the other that scatters nearby. - int nearStacks = Mathf.CeilToInt((float)nearCount / (float)e.ThingDef.stackLimit); - if (nearStacks + stackCount > maxStacks) { - int availableStacks = maxStacks - stackCount; - nearCount = availableStacks * e.ThingDef.stackLimit; - scatterCount = e.Count - nearCount; - } - if (nearCount > 0) { - stackCount += Mathf.CeilToInt((float)nearCount / (float)e.ThingDef.stackLimit); - ScenPart_StartingThing_Defined part = new ScenPart_StartingThing_Defined(); - // Be sure to set the def, since that doesn't happen automatically. Failing to do so will - // cause null pointer exceptions when trying to sort the scenario parts when creating the - // description to display in the "Scenario Summary." - part.def = ScenPartDefOf.StartingThing_Defined; - part.SetPrivateField("thingDef", e.ThingDef); - part.SetPrivateField("stuff", e.StuffDef); - part.SetPrivateField("count", nearCount); - actualParts.Add(part); - vanillaFriendlyParts.Add(part); - } - if (scatterCount > 0) { - scatterCount += Mathf.CeilToInt((float)scatterCount / (float)e.ThingDef.stackLimit); - ScenPart_ScatterThingsNearPlayerStart part = new ScenPart_ScatterThingsNearPlayerStart(); - part.def = ScenPartDefOf.ScatterThingsNearPlayerStart; - part.SetPrivateField("thingDef", e.ThingDef); - part.SetPrivateField("stuff", e.StuffDef); - part.SetPrivateField("count", scatterCount); - actualParts.Add(part); - vanillaFriendlyParts.Add(part); - } - } - } - - // Create parts to spawn the animals. We can't use the default starting animal scenario part, - // because it doesn't allow us to choose a gender. - Dictionary animalKindCounts = new Dictionary(); - foreach (var e in PrepareCarefully.Instance.Equipment) { - if (e.record.animal) { - PawnKindDef animalKindDef = (from td in DefDatabase.AllDefs where td.race == e.ThingDef select td).FirstOrDefault(); - ScenPart_CustomAnimal part = new ScenPart_CustomAnimal() { - Count = e.count, - Gender = e.Gender, - KindDef = animalKindDef - }; - actualParts.Add(part); - - if (animalKindCounts.ContainsKey(animalKindDef)) { - int count = animalKindCounts[animalKindDef]; - animalKindCounts[animalKindDef] = count + e.count; - } - else { - animalKindCounts.Add(animalKindDef, e.count); - } - } - } - - // The vanilla starting animal part does not distinguish between genders, so we combine - // the custom parts into a single vanilla part for each animal kind. - foreach (var animalKindDef in animalKindCounts.Keys) { - ScenPart_StartingAnimal vanillaPart = new ScenPart_StartingAnimal() { - def = ScenPartDefOf.StartingAnimal - }; - vanillaPart.SetPrivateField("animalKind", animalKindDef); - vanillaPart.SetPrivateField("count", animalKindCounts[animalKindDef]); - vanillaFriendlyParts.Add(vanillaPart); - } - - return Tuple.Create(actualParts, vanillaFriendlyParts); - } - - protected float GetSpawnAreaDensity(float radius, float stackCount) { - float area = Mathf.PI * radius * radius; - return stackCount / area; - } - - protected bool PlayerStartsWith(EquipmentSelection s) { - if (s.record.gear) { - return true; - } - else { - return false; - } - } - - public void PreparePawnForNewGame(Pawn pawn) { - // Do nothing for now. This can be used as a hook for harmony patches for other mods. - } - } -} diff --git a/Source/ControllerEquipment.cs b/Source/ControllerEquipment.cs deleted file mode 100644 index 3f80a0e..0000000 --- a/Source/ControllerEquipment.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using Verse; -namespace EdB.PrepareCarefully { - public class ControllerEquipment { - private State state; - private Randomizer randomizer = new Randomizer(); - public ControllerEquipment(State state) { - this.state = state; - } - - public void AddEquipment(EquipmentRecord entry) { - PrepareCarefully.Instance.AddEquipment(entry); - } - public void RemoveEquipment(EquipmentSelection equipment) { - PrepareCarefully.Instance.RemoveEquipment(equipment); - } - public void UpdateEquipmentCount(EquipmentSelection equipment, int count) { - if (count >= 0) { - equipment.Count = count; - } - } - } -} diff --git a/Source/ControllerPage.cs b/Source/ControllerPage.cs new file mode 100644 index 0000000..97a8389 --- /dev/null +++ b/Source/ControllerPage.cs @@ -0,0 +1,358 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using RimWorld; +using UnityEngine; +using Verse; + +namespace EdB.PrepareCarefully { + public class ControllerPage { + public ModState State { get; set; } + public ViewState ViewState { get; set; } + public ControllerTabViewPawns PawnTabViewController { get; set; } + public ControllerTabViewRelationships RelationshipTabViewController { get; set; } + public CostCalculator CostCalculator { get; set; } + public PawnCustomizer PawnCustomizer { get; set; } + public ManagerPawns ManagerPawns { get; set; } + public ManagerEquipment ManagerEquipment { get; set; } + public ManagerRelationships ManagerRelationships { get; set; } + public PresetLoader PresetLoader { get; set; } + public PresetSaver PresetSaver { get; set; } + + public void PostConstruct() { + PawnTabViewController.Initialize(); + } + + public bool UseLargeUI() { + Logger.Debug("Prepare Carefully window size: [" + Page.StandardSize.x + " x " + Page.StandardSize.y + "]"); + Logger.Debug("Screen: [" + Screen.width + ", " + Screen.height + "], dpi = " + Screen.dpi + ", resolution = " + Screen.currentResolution); + Logger.Debug("Screen safe area: " + Screen.safeArea); + Logger.Debug("UI scale: " + Prefs.UIScale); + + Vector2 maxSize = new Vector2(Screen.safeArea.width / Prefs.UIScale, Screen.safeArea.height / Prefs.UIScale); + Vector2 padding = new Vector2(64, 64) / Prefs.UIScale; + maxSize -= padding; + + Vector2 largeSize = new Vector2(1350, Page.StandardSize.y); + if (maxSize.x >= largeSize.x && maxSize.y >= largeSize.y) { + return true; + } + else { + return false; + } + } + + public bool CancellingRequiresConfirmation() { + return true; + // TODO: Revisit this + //if (State.OriginalPawnCustomizations.Count != State.Customizations.AllPawns.Count()) { + // return true; + //} + //foreach (var startingPawn in State.OriginalPawnCustomizations.Keys) { + // if (!State.Customizations.AllPawns.Select(p => p.Pawn).Contains(startingPawn)) { + // return true; + // } + //} + //foreach (var customizedPawn in State.Customizations.AllPawns) { + // if (!State.OriginalPawnCustomizations.TryGetValue(customizedPawn.Pawn, out var customizations)) { + // return true; + // } + // if (!Equals(customizedPawn.Customizations, customizations)) { + // return true; + // } + //} + //return false; + } + + public void CancelCustomizations() { + foreach (var pawn in Find.GameInitData.startingAndOptionalPawns) { + if (State.OriginalPawnCustomizations.TryGetValue(pawn, out var customization)) { + PawnCustomizer.ApplyAllCustomizationsToPawn(pawn, customization); + } + else { + Logger.Warning(string.Format("There may have been a problem undoing all pawn customizations. The original state of a pawn was not stored properly")); + } + } + foreach (var pawn in State.Customizations.AllPawns) { + if (pawn.Pawn != null && !Find.GameInitData.startingAndOptionalPawns.Contains(pawn.Pawn)) { + ManagerPawns.DestroyPawn(pawn.Pawn); + } + } + } + + // TODO: Move into Validator class? + public bool Validate(/*out string confirmationType*/) { + + // TODO: This is no good as it is. In vanilla, if the player only has a nickname, it copies that nickname into the + // first and last names. We need to do something similar and adjust this validation accordingly. + //foreach (CustomPawn current in PrepareCarefully.Instance.Pawns) { + // if (!current.Name.IsValid) { + // Messages.Message("EveryoneNeedsValidName".Translate(), MessageTypeDefOf.RejectInput, false); + // return false; + // } + //} + //confirmationType = "Standard"; + return true; + } + + public void StartGame() { + + PreparePawns(); + PrepareRelationships(); + PrepareEquipment(); + + // Copy of method logic for Page.DoNext() + // Performs the logic from the Page.DoNext() method in the base Page class instead of calling the DoNext() + // override in Page_ConfigureStartingPawns. We want to prevent the missing required work type dialog from + // appearing in the context of the configure pawns page. + var page = State.OriginalPage; + if (page != null) { + Page next = page.next; + Action nextAction = page.nextAct; + if (next != null) { + Verse.Find.WindowStack.Add(next); + } + nextAction?.Invoke(); + TutorSystem.Notify_Event("PageClosed"); + TutorSystem.Notify_Event("GoToNextPage"); + page.Close(true); + } + } + + public void PreparePawns() { + List pawns = new List(); + HashSet pawnLookup = new HashSet(); + foreach (var customPawn in State.Customizations.AllPawns) { + customPawn.Pawn.SetFactionDirect(Faction.OfPlayer); + if (customPawn.Type == CustomizedPawnType.Colony) { + if (customPawn.Pawn.workSettings == null) { + customPawn.Pawn.workSettings = new Pawn_WorkSettings(customPawn.Pawn); + } + customPawn.Pawn.workSettings.EnableAndInitialize(); + } + pawns.Add(customPawn.Pawn); + pawnLookup.Add(customPawn.Pawn); + var possessions = customPawn.Customizations.Possessions.Where(p => p.ThingDef != null && p.Count > 0); + Find.GameInitData.startingPossessions[customPawn.Pawn] = possessions.Select(p => new ThingDefCount(p.ThingDef, p.Count)).ToList(); + } + // Remove any starting possessions that don't belong to one of our customized pawns + List keysToRemove = new List(); + foreach (var key in Find.GameInitData.startingPossessions.Keys) { + if (!pawnLookup.Contains(key)) { + keysToRemove.Add(key); + } + } + foreach (var key in keysToRemove) { + Find.GameInitData.startingPossessions.Remove(key); + } + // Destroy any starting pawn that are not in our customized pawn list + foreach (var pawn in Find.GameInitData.startingAndOptionalPawns) { + if (!State.Customizations.AllPawns.Select(p => p.Pawn).Contains(pawn)) { + Logger.Debug("Destroyed starting pawn: " + pawn.LabelCap); + ManagerPawns.DestroyPawn(pawn); + } + else { + Logger.Debug("Kept starting pawn: " + pawn.LabelCap); + } + } + Find.GameInitData.startingPawnCount = State.Customizations.ColonyPawns.Count; + Find.GameInitData.startingAndOptionalPawns = pawns; + } + + public void PrepareRelationships() { + var builder = RelationshipTabViewController.RelationshipManager.GetRelationshipBuilder(); + builder.Build(); + } + + public void PrepareEquipment() { + List originalScenarioParts = ReflectionUtil.GetFieldValue>(Find.Scenario, "parts"); + + // Create a list of scenario parts that we're going to use to spawn into the map + List scenarioPartsToUse = new List(); + + // Sort the equipment from highest count to lowest so that gear is less likely to get blocked + // if there's a bulk item included. If you don't do this, then a large number of an item (meals, + // for example) could fill up the spawn area right away and then the rest of the items would have + // nowhere to spawn. + // TODO: Do we really need this? + //State.Equipment.Sort((CustomizedEquipment a, CustomizedEquipment b) => { + // return a.Count.CompareTo(b.Count); + //}); + + // Remove any scenario parts that we're going to replace, i.e. parts that add equipment + foreach (var part in originalScenarioParts) { + //Logger.Debug("Scenario part defName: " + part.def?.defName); + if (!State.ReplacedScenarioParts.Contains(part)) { + scenarioPartsToUse.Add(part); + } + } + + // Add scenario parts for all of our customized equipment selections + foreach (var equipment in State.Customizations.Equipment) { + ScenPart part = CreateScenarioPartForCustomizedEquipment(equipment); + if (part != null) { + scenarioPartsToUse.Add(part); + } + } + + ReflectionUtil.SetFieldValue(Find.Scenario, "parts", scenarioPartsToUse); + } + public bool ShouldReplaceScenarioPart(ScenPart part) { + if (part.GetType() == typeof(ScenPart_ScatterThingsNearPlayerStart)) { + return true; + } + if (part.GetType() == typeof(ScenPart_StartingThing_Defined)) { + return true; + } + return false; + } + + public ScenPart CreateScenarioPartForCustomizedEquipment(CustomizedEquipment equipment) { + Logger.Debug(string.Format("AddScenarioPartForCustomizedEquipment({0}), Animal = {1}", equipment.EquipmentOption?.ThingDef?.defName, equipment.Animal)); + if (equipment.Animal) { + return CreateStartingAnimalScenarioPart(equipment); + } + if (equipment.SpawnType == EquipmentSpawnType.SpawnsWith) { + return CreateStartsWithScenarioPart(equipment); + } + else if (equipment.SpawnType == EquipmentSpawnType.SpawnsNear) { + return CreateScatterThingsNearScenarioPart(equipment); + } + else { + return null; + } + } + + public ScenPart CreateStartsWithScenarioPart(CustomizedEquipment equipment) { + ScenPart_StartingThing_Defined part = new ScenPart_StartingThing_Defined() { + def = DefDatabase.GetNamedSilentFail("StartingThing_Defined") + }; + part.SetPrivateField("thingDef", equipment.EquipmentOption?.ThingDef); + part.SetPrivateField("stuff", equipment.StuffDef); + part.SetPrivateField("count", equipment.Count); + part.SetPrivateField("quality", equipment.Quality); + return part; + } + + public ScenPart CreateScatterThingsNearScenarioPart(CustomizedEquipment equipment) { + var part = new ScenPart_ScatterThingsNearPlayerStart() { + def = DefDatabase.GetNamedSilentFail("ScatterThingsNearPlayerStart") + }; + part.SetPrivateField("thingDef", equipment.EquipmentOption?.ThingDef); + part.SetPrivateField("stuff", equipment.StuffDef); + part.SetPrivateField("count", equipment.Count); + part.SetPrivateField("quality", equipment.Quality); + return part; + } + + public ScenPart CreateStartingAnimalScenarioPart(CustomizedEquipment equipment) { + if (equipment.EquipmentOption.RandomAnimal) { + return CreateRandomStartingAnimalScenarioPart(equipment); + } + else if (equipment.Gender.HasValue) { + return CreateStartingAnimalWithSpecificGenderScenarioPart(equipment); + } + else { + return CreateStartingAnimalWithRandomGenderScenarioPart(equipment); + } + } + public ScenPart CreateRandomStartingAnimalScenarioPart(CustomizedEquipment equipment) { + ScenPartDef scenPartDef = DefDatabase.GetNamedSilentFail("StartingAnimal"); + if (scenPartDef == null) { + Logger.Warning("Could not find definition for starting animal scenario part. Cannot add scenario part"); + return null; + } + var part = new ScenPart_StartingAnimal() { + def = scenPartDef + }; + part.SetPrivateField("count", equipment.Count); + return part; + } + public ScenPart CreateStartingAnimalWithRandomGenderScenarioPart(CustomizedEquipment equipment) { + ScenPartDef scenPartDef = DefDatabase.GetNamedSilentFail("StartingAnimal"); + if (scenPartDef == null) { + Logger.Warning("Could not find definition for starting animal scenario part. Cannot add scenario part"); + return null; + } + PawnKindDef pawnKindDef = FindPawnKindDefForAnimal(equipment); + if (pawnKindDef == null) { + Logger.Warning(string.Format("Could not spawn selected animal ({0}). Could not find matching pawn kind", equipment.EquipmentOption?.ThingDef?.defName)); + return null; + } + var part = new ScenPart_StartingAnimal() { + def = scenPartDef + }; + part.SetPrivateField("animalKind", pawnKindDef); + part.SetPrivateField("count", equipment.Count); + return part; + } + public ScenPart CreateStartingAnimalWithSpecificGenderScenarioPart(CustomizedEquipment equipment) { + PawnKindDef pawnKindDef = FindPawnKindDefForAnimal(equipment); + if (pawnKindDef == null) { + Logger.Warning(string.Format("Could not spawn selected animal ({0}). Could not find matching pawn kind", equipment.EquipmentOption?.ThingDef?.defName)); + return null; + } + ScenPart_CustomAnimal part = new ScenPart_CustomAnimal() { + Count = equipment.Count, + Gender = equipment.Gender.Value, + KindDef = pawnKindDef + }; + return part; + } + public PawnKindDef FindPawnKindDefForAnimal(CustomizedEquipment equipment) { + return (from td in DefDatabase.AllDefs where td.race == equipment.EquipmentOption.ThingDef select td).FirstOrDefault(); + } + + public void MarkCostsForRecalculation() { + ViewState.CostCalculationDirtyFlag = true; + } + + public void RecalculateCosts() { + State.PointCost = CostCalculator.Calculate(State.Customizations.ColonyPawns, State.Customizations.Equipment); + } + + public void LoadPreset(string filename) { + PresetLoaderResult result = PresetLoader.LoadFromFile(filename); + Customizations customizations = result?.Customizations; + + result.Problems?.ForEach(p => { + if (p.Severity == 1) { + Logger.Warning(p.Message); + } + else { + Logger.Debug(p.Message); + } + }); + + if (customizations == null) { + Messages.Message("EdB.PC.Dialog.Preset.Error.FailedToLoad".Translate(filename), MessageTypeDefOf.ThreatBig); + return; + } + if (customizations.ColonyPawns.Count < 1) { + Messages.Message("EdB.PC.Dialog.Preset.Error.FailedToLoad".Translate(filename), MessageTypeDefOf.ThreatBig); + Logger.Warning("Could not load preset because no colony pawns were loaded"); + return; + } + + Messages.Message("EdB.PC.Dialog.Preset.Loaded".Translate(filename), MessageTypeDefOf.TaskCompletion); + + ManagerPawns.ClearPawns(); + foreach (var customizedPawn in customizations.AllPawns) { + customizedPawn.Pawn = ManagerPawns.Customizer.CreatePawnFromCustomizations(customizedPawn.Customizations); + ManagerPawns.AddPawnToPawnList(customizedPawn); + } + ManagerEquipment.ClearEquipment(); + foreach (var customizedEquipment in customizations.Equipment) { + ManagerEquipment.AddEquipment(customizedEquipment); + } + ManagerRelationships.Clear(); + State.Customizations.Relationships = customizations.Relationships; + State.Customizations.ParentChildGroups = customizations.ParentChildGroups; + PawnTabViewController.SelectPawn(result.Customizations.ColonyPawns.FirstOrDefault()); + } + + public void SavePreset(string filename) { + PresetSaver.SaveToFile(State, filename); + } + } +} diff --git a/Source/ControllerPawns.cs b/Source/ControllerPawns.cs deleted file mode 100644 index 0b266ec..0000000 --- a/Source/ControllerPawns.cs +++ /dev/null @@ -1,496 +0,0 @@ -using RimWorld; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text.RegularExpressions; -using UnityEngine; -using Verse; -namespace EdB.PrepareCarefully { - public class ControllerPawns { - public delegate void PawnAddedHandler(CustomPawn pawn); - public delegate void PawnReplacedHandler(CustomPawn pawn); - public delegate void ColonyPawnsMaximizedHandler(); - public delegate void WorldPawnsMaximizedHandler(); - public delegate void PawnListsSplitHandler(); - public event PawnAddedHandler PawnAdded; - public event PawnReplacedHandler PawnReplaced; - - private State state; - private Randomizer randomizer = new Randomizer(); - private ProviderAgeLimits ProviderAgeLimits = PrepareCarefully.Instance.Providers.AgeLimits; - private AgeModifier ageModifier = new AgeModifier(); - public ControllerPawns(State state) { - this.state = state; - } - - public void CheckPawnCapabilities() { - List missingWorkTypes = null; - foreach (WorkTypeDef w in DefDatabase.AllDefs) { - // If it's a required work type, then check to make sure at least one pawn can do it. - if (w.requireCapableColonist) { - bool workTypeEnabledOnAtLeastOneColonist = false; - foreach (CustomPawn pawn in PrepareCarefully.Instance.Pawns.Where((pawn) => { return pawn.Type == CustomPawnType.Colonist; })) { - if (!pawn.Pawn.WorkTypeIsDisabled(w)) { - workTypeEnabledOnAtLeastOneColonist = true; - break; - } - } - // If the work type is not enabled on at least one pawn, then add it to the missing work types list. - if (!workTypeEnabledOnAtLeastOneColonist) { - if (missingWorkTypes == null) { - missingWorkTypes = new List(); - } - missingWorkTypes.Add(w.gerundLabel.CapitalizeFirst()); - } - } - } - state.MissingWorkTypes = missingWorkTypes; - } - - public void RandomizeAll() { - // Create the pawn. - Pawn pawn = ModsConfig.BiotechActive ? CreateRandomizedPawnForBiotech() : randomizer.GenerateKindOfPawn(state.CurrentPawn.Pawn.kindDef); - if (pawn.Faction != Faction.OfPlayer) { - pawn.SetFactionDirect(Faction.OfPlayer); - } - bool randomizeAny = state.CurrentPawn.RandomizeAnyNonArchite; - state.CurrentPawn.InitializeWithPawn(pawn); - state.CurrentPawn.GenerateId(); - PawnReplaced(state.CurrentPawn); - if (randomizeAny) { - state.CurrentPawn.RandomizeAnyNonArchite = true; - state.CurrentPawn.RandomizeCustomXenotype = null; - state.CurrentPawn.RandomizeXenotype = null; - } - } - - public Pawn CreateRandomizedPawnForBiotech() { - var wrapper = new PawnGenerationRequestWrapper() { - Faction = Find.FactionManager.OfPlayer, - KindDef = state.CurrentPawn.Pawn.kindDef - }; - Ideo ideo = Find.FactionManager.OfPlayer.ideos.GetRandomIdeoForNewPawn(); - if (ideo != null) { - wrapper.FixedIdeology = ideo; - } - if (state.CurrentPawn.RandomizeDevelopmentalStage != DevelopmentalStage.Adult) { - wrapper.DevelopmentalStage = state.CurrentPawn.RandomizeDevelopmentalStage; - } - if (state.CurrentPawn.RandomizeXenotype != null) { - wrapper.ForcedXenotype = state.CurrentPawn.RandomizeXenotype; - } - else if (state.CurrentPawn.RandomizeCustomXenotype != null) { - wrapper.ForcedCustomXenotype = state.CurrentPawn.RandomizeCustomXenotype; - } - else { - wrapper.AllowedXenotypes = DefDatabase.AllDefs.Where((XenotypeDef x) => !x.Archite && x != XenotypeDefOf.Baseliner).ToList(); - wrapper.ForceBaselinerChance = 0.5f; - } - Pawn pawn = randomizer.AttemptToGeneratePawn(wrapper.Request); - - // Fix bad head type for Alien races - if (pawn.def.defName != "Human") { - var provider = PrepareCarefully.Instance.Providers.HeadTypes; - var headTypes = provider.GetHeadTypes(pawn); - if (headTypes.FirstOrDefault() != null && !headTypes.Contains(pawn.story.headType)) { - var replacement = provider.GetHeadTypes(pawn).First(); - Logger.Warning("Swapped out missing head type (" + pawn.story.headType?.defName + ") with first valid head type (" + replacement?.defName + ") for alien race (" + pawn.def.defName + ")"); - pawn.story.headType = replacement; - } - } - - // Fix bad body type for Alien races - if (pawn.def.defName != "Human") { - var provider = PrepareCarefully.Instance.Providers.BodyTypes; - var bodyTypes = provider.GetBodyTypesForPawn(pawn); - if (!bodyTypes.Contains(pawn.story.bodyType)) { - Logger.Warning("Alien race (" + pawn.def.defName + ") does not include the generated body type (" + pawn.story.bodyType?.defName + ")"); - if (bodyTypes.FirstOrDefault() != null) { - var replacement = provider.GetBodyTypesForPawn(pawn).First(); - Logger.Warning("Swapped out missing body type (" + pawn.story.bodyType?.defName + ") with first valid body type (" + replacement?.defName + ") for alien race (" + pawn.def.defName + ")"); - pawn.story.bodyType = replacement; - } - else { - Logger.Warning("No body types available in the alien race (" + pawn.def.defName + ") definition"); - } - } - - } - - return pawn; - } - - // Name-related actions. - public void UpdateFirstName(string name) { - if (name.Length <= 12 && CharacterCardUtility.ValidNameRegex.IsMatch(name)) { - state.CurrentPawn.FirstName = name; - } - } - public void UpdateNickName(string name) { - if (name.Length <= 9 && CharacterCardUtility.ValidNameRegex.IsMatch(name)) { - state.CurrentPawn.NickName = name; - } - } - public void UpdateLastName(string name) { - if (name.Length <= 12 && CharacterCardUtility.ValidNameRegex.IsMatch(name)) { - state.CurrentPawn.LastName = name; - } - } - public void RandomizeName() { - Pawn sourcePawn = randomizer.GenerateSameKindAndGenderOfPawn(state.CurrentPawn); - Name name = PawnBioAndNameGenerator.GeneratePawnName(sourcePawn, NameStyle.Full, null); - NameTriple nameTriple = name as NameTriple; - state.CurrentPawn.Name = nameTriple; - } - - // Backstory-related actions. - public void UpdateBackstory(BackstorySlot slot, BackstoryDef backstory) { - if (slot == BackstorySlot.Childhood) { - state.CurrentPawn.Childhood = backstory; - } - else if (slot == BackstorySlot.Adulthood) { - state.CurrentPawn.Adulthood = backstory; - } - } - - public void RandomizeBackstories() { - CustomPawn currentPawn = state.CurrentPawn; - PawnKindDef kindDef = currentPawn.Pawn.kindDef; - FactionDef factionDef = kindDef?.defaultFactionType; - if (factionDef == null) { - factionDef = Faction.OfPlayer.def; - } - - var providerAlienRaces = PrepareCarefully.Instance.Providers.AlienRaces; - AlienRace alienRace = providerAlienRaces.GetAlienRace(currentPawn.Pawn.def); - float adultStoryAge = alienRace == null ? providerAlienRaces.DefaultMinAgeForAdulthood : alienRace.MinAgeForAdulthood; - //Logger.Debug(String.Format("Adulthood age for {0} is {1}", state.CurrentPawn.Pawn.def.defName, adultStoryAge)); - - List backstoryCategoryFiltersFor = Reflection.ReflectorPawnBioAndNameGenerator.GetBackstoryCategoryFiltersFor(currentPawn.Pawn, factionDef); - // Generate a bio from which to get the backstories - if (!Reflection.ReflectorPawnBioAndNameGenerator.TryGetRandomUnusedSolidBioFor(backstoryCategoryFiltersFor, kindDef, currentPawn.Gender, null, out PawnBio pawnBio)) { - // Other mods are patching the vanilla method in ways that cause it to return false. If that happens, - // we use our duplicate implementation instead. - var providerBackstories = PrepareCarefully.Instance.Providers.Backstories; - if (!PawnBioGenerator.TryGetRandomUnusedSolidBioFor(backstoryCategoryFiltersFor, kindDef, currentPawn.Gender, null, out pawnBio)) { - // If we still can't get a bio with our duplicate implementation, we pick backstories completely at random. - //Logger.Debug(String.Format("Using fallback method to get solid random backstories for kindDef {0} \"{1}\" and faction {2} \"{3}\"", - // kindDef.defName, kindDef.LabelCap, factionDef.defName, factionDef.LabelCap)); - currentPawn.Childhood = providerBackstories.GetChildhoodBackstoriesForPawn(currentPawn).RandomElement(); - if (currentPawn.BiologicalAge >= (int) adultStoryAge) { - currentPawn.Adulthood = providerBackstories.GetAdulthoodBackstoriesForPawn(currentPawn).RandomElement(); - } - return; - } - } - currentPawn.Childhood = pawnBio.childhood; - if (currentPawn.BiologicalAge >= (int) adultStoryAge) { - currentPawn.Adulthood = pawnBio.adulthood; - } - } - - // Trait-related actions. - public void AddTrait(Trait trait) { - state.CurrentPawn.AddTrait(trait); - } - - public void UpdateTrait(int index, Trait trait) { - state.CurrentPawn.SetTrait(index, trait); - } - - public void RemoveTrait(Trait trait) { - state.CurrentPawn.RemoveTrait(trait); - } - - public void RandomizeTraits() { - Pawn pawn = randomizer.GeneratePawnAsCloseToAsPossible(state.CurrentPawn.Pawn); - List traits = pawn.story.traits.allTraits; - state.CurrentPawn.ClearTraits(); - foreach (var trait in traits) { - state.CurrentPawn.AddTrait(trait); - } - } - - public void RemoveAbility(Ability ability) { - state.CurrentPawn.Pawn.abilities?.RemoveAbility(ability.def); - } - public void AddAbility(AbilityDef def) { - state.CurrentPawn.Pawn.abilities?.GainAbility(def); - } - public void SetAbilities(IEnumerable abilities) { - List toRemove = new List(state.CurrentPawn.Pawn.abilities.abilities.Select(a => a.def)); - foreach (var def in toRemove) { - state.CurrentPawn.Pawn.abilities.RemoveAbility(def); - } - foreach (var a in abilities) { - state.CurrentPawn.Pawn.abilities.GainAbility(a); - } - } - - // Age-related actions. - public void UpdateBiologicalAge(int? ageYears, int? ageDays) { - int years = ageYears ?? state.CurrentPawn.BiologicalAgeInYears; - int days = ageDays ?? (int)(state.CurrentPawn.BiologicalAgeInDays % AgeModifier.DaysPerYear); - int min = ProviderAgeLimits.MinAgeForPawn(state.CurrentPawn.Pawn); - int max = ProviderAgeLimits.MaxAgeForPawn(state.CurrentPawn.Pawn); - if (years < min) { - years = min; - days = 0; - } - else if (years > max) { - years = max; - days = 59; - } - long ticks = AgeModifier.TicksFromYearsAndDays(years, days); - ticks += state.CurrentPawn.BiologicalAgeInTicks % AgeModifier.TicksPerDay; - ageModifier.ModifyBiologicalAge(state.CurrentPawn, ticks); - if (ticks > state.CurrentPawn.ChronologicalAgeInTicks) { - ageModifier.ModifyChronologicalAge(state.CurrentPawn, ticks); - } - } - - public void UpdateChronologicalAge(int? ageYears, int? ageDays) { - int years = ageYears ?? state.CurrentPawn.ChronologicalAgeInYears; - int days = ageDays ?? (int)(state.CurrentPawn.ChronologicalAgeInDays % AgeModifier.DaysPerYear); - int min = ProviderAgeLimits.MinAgeForPawn(state.CurrentPawn.Pawn); - if (years < min) { - years = min; - days = 0; - } - long ticks = AgeModifier.TicksFromYearsAndDays(years, days); - ticks += state.CurrentPawn.ChronologicalAgeInTicks % AgeModifier.TicksPerDay; - ageModifier.ModifyChronologicalAge(state.CurrentPawn, ticks); - if (ticks < state.CurrentPawn.BiologicalAgeInTicks) { - ageModifier.ModifyBiologicalAge(state.CurrentPawn, ticks); - } - } - - // Appearance-related actions. - public void RandomizeAppearance() { - CustomPawn currentPawn = state.CurrentPawn; - Pawn pawn = randomizer.GenerateSameKindAndGenderOfPawn(currentPawn); - currentPawn.CopyAppearance(pawn); - } - - // Skill-related actions. - public void ResetSkills() { - state.CurrentPawn.RestoreSkillLevelsAndPassions(); - } - public void ClearSkills() { - state.CurrentPawn.ClearSkills(); - state.CurrentPawn.ClearPassions(); - } - public void UpdateSkillLevel(SkillDef skill, int level) { - - } - public void UpdateSkillPassion(SkillDef skill, Passion level) { - state.CurrentPawn.SetPassion(skill, level); - } - - // Pawn-related actions. - public void SelectPawn(CustomPawn pawn) { - state.CurrentPawn = pawn; - - } - public void AddingPawn(bool startingPawn) { - CustomPawn pawn = new CustomPawn(randomizer.GenerateColonist()); - pawn.Type = startingPawn ? CustomPawnType.Colonist : CustomPawnType.World; - PrepareCarefully.Instance.AddPawn(pawn); - state.CurrentPawn = pawn; - PawnAdded(pawn); - } - public void SwapPawn(CustomPawn pawn) { - int worldPawnIndex = PrepareCarefully.Instance.WorldPawns.IndexOf(pawn); - int colonyPawnIndex = PrepareCarefully.Instance.ColonyPawns.IndexOf(pawn); - PrepareCarefully.Instance.Pawns.Remove(pawn); - if (state.CurrentWorldPawn == pawn) { - List worldPawns = PrepareCarefully.Instance.WorldPawns; - if (worldPawnIndex > -1 && worldPawnIndex < worldPawns.Count) { - state.CurrentWorldPawn = worldPawns[worldPawnIndex]; - } - else { - state.CurrentWorldPawn = worldPawns.LastOrDefault(); - } - } - if (state.CurrentColonyPawn == pawn) { - List colonyPawns = PrepareCarefully.Instance.ColonyPawns; - if (colonyPawnIndex > -1 && colonyPawnIndex < colonyPawns.Count) { - state.CurrentColonyPawn = colonyPawns[colonyPawnIndex]; - } - else { - state.CurrentColonyPawn = colonyPawns.LastOrDefault(); - } - } - if (pawn.Type == CustomPawnType.Colonist) { - pawn.Type = CustomPawnType.World; - state.CurrentWorldPawn = pawn; - } - else { - pawn.Type = CustomPawnType.Colonist; - state.CurrentColonyPawn = pawn; - } - PrepareCarefully.Instance.Pawns.Add(pawn); - } - public void DeletePawn(CustomPawn pawn) { - int worldPawnIndex = PrepareCarefully.Instance.WorldPawns.IndexOf(pawn); - int colonyPawnIndex = PrepareCarefully.Instance.ColonyPawns.IndexOf(pawn); - PrepareCarefully.Instance.Pawns.Remove(pawn); - if (state.CurrentWorldPawn == pawn) { - List worldPawns = PrepareCarefully.Instance.WorldPawns; - if (worldPawnIndex > -1 && worldPawnIndex < worldPawns.Count) { - state.CurrentWorldPawn = worldPawns[worldPawnIndex]; - } - else { - state.CurrentWorldPawn = worldPawns.LastOrDefault(); - } - } - if (state.CurrentColonyPawn == pawn) { - List colonyPawns = PrepareCarefully.Instance.ColonyPawns; - if (colonyPawnIndex > -1 && colonyPawnIndex < colonyPawns.Count) { - state.CurrentColonyPawn = colonyPawns[colonyPawnIndex]; - } - else { - state.CurrentColonyPawn = colonyPawns.LastOrDefault(); - } - } - PrepareCarefully.Instance.RelationshipManager.DeletePawn(pawn); - } - public void LoadCharacter(string name) { - if (string.IsNullOrEmpty(name)) { - Logger.Warning("Trying to load a character without a name"); - return; - } - CustomPawn pawn = ColonistLoader.LoadFromFile(PrepareCarefully.Instance, name); - if (pawn != null) { - state.AddMessage("EdB.PC.Dialog.PawnPreset.Loaded".Translate(name)); - } - else { - state.AddError("Failed to load pawn"); - return; - } - bool colonyPawn = state.PawnListMode == PawnListMode.ColonyPawnsMaximized; - pawn.Type = colonyPawn ? CustomPawnType.Colonist : CustomPawnType.World; - // Regenerate a unique id in case the user is loading the same pawn more than once. - pawn.GenerateId(); - PrepareCarefully.Instance.AddPawn(pawn); - state.CurrentPawn = pawn; - PawnAdded(pawn); - } - public void SaveCharacter(CustomPawn pawn, string filename) { - if (string.IsNullOrEmpty(filename)) { - Logger.Warning("Trying to save a character without a name"); - return; - } - ColonistSaver.SaveToFile(pawn, filename); - state.AddMessage("SavedAs".Translate(filename)); - } - public void AddFactionPawn(PawnKindOption option, bool startingPawn) { - Pawn pawn = null; - try { - //Logger.Debug("Adding new pawn " + option); - var wrapper = new PawnGenerationRequestWrapper() { - Faction = Find.World.factionManager.OfPlayer, - KindDef = option.KindDef, - Context = PawnGenerationContext.NonPlayer, - WorldPawnFactionDoesntMatter = true, - }; - Ideo ideo = Find.FactionManager.OfPlayer?.ideos?.GetRandomIdeoForNewPawn(); - //Logger.Debug("Pawn kind xenotype set: " + option?.KindDef?.xenotypeSet?.ToStringSafe()); - //Logger.Debug("Faction xenotype set: " + option?.FactionDef?.xenotypeSet?.ToStringSafe()); - XenotypeSet setToGenerateWith = option?.FactionDef?.xenotypeSet; - if (setToGenerateWith == null) { - setToGenerateWith = option?.KindDef?.xenotypeSet; - } - if (setToGenerateWith != null) { - wrapper.ForcedXenotype = this.randomizer.RandomXenotypeFromSet(setToGenerateWith); - } - if (ideo != null) { - wrapper.FixedIdeology = ideo; - } - pawn = randomizer.GeneratePawn(wrapper.Request); - } - catch (Exception e) { - Logger.Warning("Failed to create faction pawn: " + option, e); - if (pawn != null) { - pawn.Destroy(); - } - state.AddError("EdB.PC.Panel.PawnList.Error.FactionPawnFailed".Translate()); - return; - } - - // Reset the quality and damage of all apparel. - foreach (var a in pawn.apparel.WornApparel) { - a.SetQuality(QualityCategory.Normal); - a.HitPoints = a.MaxHitPoints; - } - - // TODO: Revisit this if we add a UI to edit titles. - // Clear out all titles. - //if (pawn.royalty != null) { - // pawn.royalty = new Pawn_RoyaltyTracker(pawn); - //} - - CustomPawn customPawn = new CustomPawn(pawn); - customPawn.OriginalKindDef = option.KindDef; - FactionDef factionDef = option.FactionDef; - if (factionDef == null) { - factionDef = option?.KindDef?.defaultFactionType; - } - customPawn.OriginalFactionDef = factionDef; - if (pawn.Faction != Faction.OfPlayer) { - pawn.SetFaction(Faction.OfPlayer); - } - - customPawn.Type = startingPawn ? CustomPawnType.Colonist : CustomPawnType.World; - if (!startingPawn) { - CustomFaction customFaction = PrepareCarefully.Instance.Providers.Factions.FindRandomCustomFactionByDef(factionDef); - if (customFaction != null) { - customPawn.Faction = customFaction; - } - } - - PrepareCarefully.Instance.AddPawn(customPawn); - state.CurrentPawn = customPawn; - PawnAdded(customPawn); - } - - // Gender-related actions. - public void UpdateGender(Gender gender) { - state.CurrentPawn.Gender = gender; - } - - // Health-related actions. - public void AddInjury(Injury injury) { - CustomPawn currentPawn = state.CurrentPawn; - if (currentPawn != null) { - state.CurrentPawn.AddInjury(injury); - } - } - - public void AddImplant(Implant implant) { - CustomPawn currentPawn = state.CurrentPawn; - if (currentPawn != null) { - currentPawn.AddImplant(implant); - } - } - - public void RemoveHediff(Hediff hediff) { - CustomPawn currentPawn = state.CurrentPawn; - if (currentPawn != null) { - Injury injury = currentPawn.Injuries.FirstOrDefault(i => i.Hediff == hediff); - Implant implant = currentPawn.Implants.FirstOrDefault(i => i.Hediff == hediff); - if (injury != null) { - currentPawn.RemoveCustomBodyParts(injury); - } - if (implant != null) { - currentPawn.RemoveCustomBodyParts(implant); - } - } - } - - public void UpdateFavoriteColor(Color? color) { - state.CurrentPawn.Pawn.story.favoriteColor = color; - } - } -} diff --git a/Source/ControllerRelationships.cs b/Source/ControllerRelationships.cs deleted file mode 100644 index 5820c45..0000000 --- a/Source/ControllerRelationships.cs +++ /dev/null @@ -1,55 +0,0 @@ -using RimWorld; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Verse; -namespace EdB.PrepareCarefully { - public class ControllerRelationships { - protected State state; - public ControllerRelationships(State state) { - this.state = state; - } - public void AddRelationship(PawnRelationDef def, CustomPawn source, CustomPawn target) { - PrepareCarefully.Instance.RelationshipManager.AddRelationship(def, source, target); - } - public void RemoveRelationship(CustomRelationship relationship) { - PrepareCarefully.Instance.RelationshipManager.DeleteRelationship(relationship); - } - public void DeleteAllPawnRelationships(CustomPawn pawn) { - PrepareCarefully.Instance.RelationshipManager.DeletePawn(pawn); - } - public void AddPawn(CustomPawn pawn) { - PrepareCarefully.Instance.RelationshipManager.AddVisibleParentChildPawn(pawn); - } - public void ReplacePawn(CustomPawn pawn) { - PrepareCarefully.Instance.RelationshipManager.DeletePawn(pawn); - PrepareCarefully.Instance.RelationshipManager.AddVisibleParentChildPawn(pawn); - } - public void AddParentToParentChildGroup(ParentChildGroup group, CustomPawn pawn) { - if (!group.Parents.Contains(pawn) && !group.Children.Contains(pawn)) { - group.Parents.Add(pawn); - } - } - public void RemoveParentFromParentChildGroup(ParentChildGroup group, CustomPawn pawn) { - group.Parents.Remove(pawn); - if (group.Parents.Count == 0 && group.Children.Count == 0) { - PrepareCarefully.Instance.RelationshipManager.RemoveParentChildGroup(group); - } - } - public void AddChildToParentChildGroup(ParentChildGroup group, CustomPawn pawn) { - if (!group.Parents.Contains(pawn) && !group.Children.Contains(pawn)) { - group.Children.Add(pawn); - } - } - public void RemoveChildFromParentChildGroup(ParentChildGroup group, CustomPawn pawn) { - group.Children.Remove(pawn); - if (group.Parents.Count == 0 && group.Children.Count == 0) { - PrepareCarefully.Instance.RelationshipManager.RemoveParentChildGroup(group); - } - } - public void AddParentChildGroup(ParentChildGroup group) { - PrepareCarefully.Instance.RelationshipManager.ParentChildGroups.Add(group); - } - } -} diff --git a/Source/ControllerTabViewEquipment.cs b/Source/ControllerTabViewEquipment.cs new file mode 100644 index 0000000..055f0ba --- /dev/null +++ b/Source/ControllerTabViewEquipment.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EdB.PrepareCarefully { + public class ControllerTabViewEquipment { + public ModState State { get; set; } + public ViewState ViewState { get; set; } + public ManagerEquipment EquipmentManager { get; set; } + + public void UpdateEquipmentCount(CustomizedEquipment equipment, int count) { + EquipmentManager.UpdateEquipmentCount(equipment, count); + } + + public void RemoveEquipment(CustomizedEquipment equipment) { + EquipmentManager.RemoveEquipment(equipment); + } + + public void AddEquipment(CustomizedEquipment equipment) { + EquipmentManager.AddEquipment(equipment); + } + + //public void EquipmentCountIncrementButtonClicked(CustomizedEquipment equipment) { + // EquipmentManager.UpdateEquipmentCount(equipment, equipment.Count + 1); + //} + + //public void EquipmentCountDecrementButtonClicked(CustomizedEquipment equipment) { + // if (equipment.Count > 0) { + // EquipmentManager.UpdateEquipmentCount(equipment, equipment.Count - 1); + // } + //} + } +} diff --git a/Source/ControllerTabViewPawns.cs b/Source/ControllerTabViewPawns.cs new file mode 100644 index 0000000..c7554c7 --- /dev/null +++ b/Source/ControllerTabViewPawns.cs @@ -0,0 +1,395 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using RimWorld; +using UnityEngine; +using Verse; + +namespace EdB.PrepareCarefully { + public class ControllerTabViewPawns { + public delegate void PawnLayerOptionUpdatedHandler(PawnLayer pawnLayer, CustomizedPawn pawn, PawnLayerOption pawnLayerOption); + public delegate void PawnLayerColorUpdatedHandler(PawnLayer pawnLayer, CustomizedPawn pawn, Color color); + + public ModState State { get; set; } + public ViewState ViewState { get; set; } + public PawnCustomizer Customizer { get; set; } + public ManagerPawns PawnManager { get; set; } + + protected Dictionary PawnLayerOptionUpdateHandlers { get; set; } = new Dictionary(); + protected Dictionary PawnLayerColorUpdateHandlers { get; set; } = new Dictionary(); + + + public void Initialize() { + // TODO: Move to page controller? + ViewState.CurrentPawn = State.Customizations.ColonyPawns.FirstOrDefault(); + } + + + public void SelectPawn(CustomizedPawn pawn) { + ViewState.CurrentPawn = pawn; + } + + public void MaximizeColonyPawnList() { + ViewState.PawnListMode = PawnListMode.ColonyPawnsMaximized; + } + + public void MaximizeWorldPawnList() { + ViewState.PawnListMode = PawnListMode.WorldPawnsMaximized; + } + + public void AddColonyPawn() { + AddPawnWithPawnKind(null, true); + } + + + public void AddWorldPawn() { + AddPawnWithPawnKind(null, false); + } + + public void AddPawnWithPawnKind(PawnKindOption option, bool startingPawn) { + if (startingPawn) { + CustomizedPawn customizedPawn = PawnManager.AddPawn(CustomizedPawnType.Colony, option); + SelectPawn(customizedPawn); + if (ViewState.PawnListMode != PawnListMode.ColonyPawnsMaximized) { + ViewState.PawnListMode = PawnListMode.ColonyPawnsMaximized; + } + } + else { + CustomizedPawn customizedPawn = PawnManager.AddPawn(CustomizedPawnType.World, option); + SelectPawn(customizedPawn); + if (ViewState.PawnListMode != PawnListMode.WorldPawnsMaximized) { + ViewState.PawnListMode = PawnListMode.WorldPawnsMaximized; + } + } + } + + public void DeletePawn(CustomizedPawn pawn) { + List pawnList = null; + if (pawn.Type == CustomizedPawnType.Colony) { + pawnList = State.Customizations.ColonyPawns; + if (pawnList.Count < 2) { + return; + } + } + else if (pawn.Type == CustomizedPawnType.World) { + pawnList = State.Customizations.WorldPawns; + } + int index = pawnList.IndexOf(pawn); + if (index == -1) { + return; + } + if (PawnManager.RemovePawn(pawn)) { + if (pawnList.Count > 0) { + if (index >= pawnList.Count) { + index = pawnList.Count - 1; + } + SelectPawn(pawnList[index]); + } + else { + SelectPawn(null); + } + ViewState.PawnRandomizerOptions.Remove(pawn); + } + } + + public void MoveColonyPawnToWorldPawnList(CustomizedPawn pawn, bool activatePawn) { + if (State.Customizations.ColonyPawns.Count < 2) { + return; + } + PawnManager.ChangeWorldPawnToColonyPawn(pawn); + if (activatePawn) { + SelectPawn(pawn); + ViewState.PawnListMode = PawnListMode.WorldPawnsMaximized; + } + } + + public void MoveWorldPawnToColonyPawnList(CustomizedPawn pawn, bool activatePawn) { + PawnManager.ChangeWorldPawnToColonyPawn(pawn); + if (activatePawn) { + SelectPawn(pawn); + ViewState.PawnListMode = PawnListMode.ColonyPawnsMaximized; + } + } + public void LoadColonyPawn(string file) { + LoadPawn(CustomizedPawnType.Colony, file); + } + public void LoadWorldPawn(string file) { + LoadPawn(CustomizedPawnType.World, file); + } + + public void LoadPawn(CustomizedPawnType type, string file) { + var result = PawnManager.LoadPawn(type, file); + result.Problems?.ForEach(p => { + if (p.Severity == 1) { + Logger.Warning(p.Message); + } + else { + Logger.Debug(p.Message); + } + }); + if (result?.Pawn?.Pawn != null) { + Messages.Message("EdB.PC.Dialog.PawnPreset.Loaded".Translate(file), MessageTypeDefOf.TaskCompletion); + SelectPawn(result.Pawn); + } + else { + Messages.Message("EdB.PC.Dialog.PawnPreset.Error.Failed".Translate(), MessageTypeDefOf.ThreatBig); + } + } + + public void SavePawn(string filename) { + if (string.IsNullOrEmpty(filename)) { + Logger.Warning("Trying to save a character without a name"); + return; + } + PawnManager.SavePawn(ViewState.CurrentPawn, filename); + // TODO + //state.AddMessage("SavedAs".Translate(filename)); + } + + public void RandomizeCurrentPawn() { + if (ViewState.PawnRandomizerOptions.TryGetValue(ViewState.CurrentPawn, out var randomizerOptions)) { + PawnManager.RandomizePawn(ViewState.CurrentPawn, randomizerOptions); + } + else { + PawnManager.RandomizePawn(ViewState.CurrentPawn); + } + } + + public void UpdateFirstName(string name) { + PawnManager.UpdateFirstName(ViewState.CurrentPawn, name); + } + public void UpdateNickName(string name) { + PawnManager.UpdateNickName(ViewState.CurrentPawn, name); + } + public void UpdateLastName(string name) { + PawnManager.UpdateLastName(ViewState.CurrentPawn, name); + } + + public void UpdateBiologicalAge(int? ageYears, int? ageDays) { + CustomizedPawn customizedPawn = ViewState.CurrentPawn; + if (customizedPawn == null || customizedPawn.Pawn == null) { + return; + } + int years = ageYears ?? customizedPawn.Pawn.ageTracker.AgeBiologicalYears; + int days = ageDays ?? AgeModifier.TicksToDayOfYear(customizedPawn.Pawn.ageTracker.AgeBiologicalTicks); + PawnManager.UpdatePawnBiologicalAge(ViewState.CurrentPawn, years, days); + } + + public void UpdateChronologicalAge(int? ageYears, int? ageDays) { + CustomizedPawn customizedPawn = ViewState.CurrentPawn; + if (customizedPawn == null || customizedPawn.Pawn == null) { + return; + } + int years = ageYears ?? customizedPawn.Pawn.ageTracker.AgeChronologicalYears; + int days = ageDays ?? AgeModifier.TicksToDayOfYear(customizedPawn.Pawn.ageTracker.AgeChronologicalTicks); + PawnManager.UpdatePawnChronologicalAge(ViewState.CurrentPawn, years, days); + } + + public void UpdateBackstoryHandler(BackstorySlot slot, BackstoryDef backstory) { + PawnManager.UpdatePawnBackstory(ViewState.CurrentPawn, slot, backstory); + } + + public void RandomizeBackstory() { + PawnManager.RandomizePawnBackstories(ViewState.CurrentPawn); + } + + public void IncrementSkill(SkillDef skill) { + AdjustSkillLevelByOne(skill, 1); + } + + public void DecrementSkill(SkillDef skill) { + AdjustSkillLevelByOne(skill, -1); + } + + public void AdjustSkillLevelByOne(SkillDef skill, int plusOrMinusOne) { + CustomizedPawn customizedPawn = ViewState.CurrentPawn; + Pawn pawn = customizedPawn?.Pawn; + if (pawn == null) { + return; + } + CustomizationsPawn customizations = customizedPawn?.Customizations; + if (customizations == null) { + return; + } + var record = pawn.skills.GetSkill(skill); + if (record != null) { + PawnManager.SetSkillLevel(ViewState?.CurrentPawn, skill, record.Level + plusOrMinusOne); + } + } + + public void SetSkillLevel(SkillDef skill, int value) { + PawnManager.SetSkillLevel(ViewState?.CurrentPawn, skill, value); + } + + public void UpdateSkillPassion(SkillDef skill) { + // Left-click increases passion. Right-click decreases it + bool increase = Event.current.button != 1 ? true : false; + + CustomizedPawn customizedPawn = ViewState.CurrentPawn; + if (customizedPawn == null) { + return; + } + Pawn pawn = customizedPawn.Pawn; + if (pawn == null) { + return; + } + SkillRecord record = pawn.skills.GetSkill(skill); + if (record == null) { + return; + } + Passion currentPassion = record.passion; + Passion nextPassion = currentPassion; + if (currentPassion == Passion.None) { + nextPassion = increase ? Passion.Minor : Passion.Major; + } + else if (currentPassion == Passion.Minor) { + nextPassion = increase ? Passion.Major : Passion.None; + } + else if (currentPassion == Passion.Major) { + nextPassion = increase ? Passion.None : Passion.Minor; + } + PawnManager.UpdatePawnSkillPassion(customizedPawn, skill, nextPassion); + } + + public void ClearSkillsAndPassions() { + CustomizedPawn customizedPawn = ViewState.CurrentPawn; + if (customizedPawn == null) { + return; + } + PawnManager.ClearSkillLevels(customizedPawn); + foreach (var skill in customizedPawn.Customizations.Skills) { + PawnManager.UpdatePawnSkillPassion(customizedPawn, skill.SkillDef, Passion.None); + } + } + public void ResetAddedSkillLevelsAndPassions() { + CustomizedPawn customizedPawn = ViewState.CurrentPawn; + if (customizedPawn == null) { + return; + } + PawnManager.ResetSkillLevels(customizedPawn); + foreach (var skill in customizedPawn.Customizations.Skills) { + PawnManager.UpdatePawnSkillPassion(customizedPawn, skill.SkillDef, skill.OriginalPassion); + } + } + + public void AddTrait(Trait trait) { + PawnManager.AddTrait(ViewState?.CurrentPawn, trait); + } + + public void RemoveTrait(Trait trait) { + PawnManager.RemoveTrait(ViewState?.CurrentPawn, trait); + } + + public void RandomizeTraits() { + PawnManager.RandomizeTraits(ViewState?.CurrentPawn); + } + + public void UpdateTrait(int index, Trait trait) { + Pawn pawn = ViewState?.CurrentPawn?.Pawn; + if (pawn == null) { + return; + } + var traits = pawn.story.traits.allTraits; + if (index >= traits.Count) { + return; + } + PawnManager.ReplaceTrait(ViewState?.CurrentPawn, traits[index], trait); + } + public void AddInjury(Injury injury) { + PawnManager.AddPawnInjury(ViewState?.CurrentPawn, injury); + } + public void AddImplant(Implant implant) { + PawnManager.AddPawnImplant(ViewState?.CurrentPawn, implant); + } + public void RemoveHediff(Hediff hediff) { + PawnManager.RemovePawnHediff(ViewState?.CurrentPawn, hediff); + } + public void RemoveHediffs(IEnumerable hediffs) { + PawnManager.RemovePawnHediffs(ViewState?.CurrentPawn, hediffs); + } + + public void RandomizeAppearance() { + PawnManager.RandomizeAppearance(ViewState?.CurrentPawn); + } + + public void UpdateSkinColor(Color color) { + PawnManager.UpdateSkinColor(ViewState?.CurrentPawn, color); + } + public void UpdateGender(Gender gender) { + PawnManager.UpdateGender(ViewState?.CurrentPawn, gender); + } + + public void UpdateImplants(IEnumerable implants) { + PawnManager.UpdateImplants(ViewState?.CurrentPawn, implants); + } + + public void SetTraits(IEnumerable traits) { + PawnManager.SetTraits(ViewState?.CurrentPawn, traits); + } + + public void RemoveApparel(Thing thing) { + PawnManager.RemoveApparel(ViewState?.CurrentPawn, thing); + } + public void AddApparel(CustomizationsApparel apparel) { + PawnManager.AddApparel(ViewState?.CurrentPawn, apparel); + } + + public void SetApparel(List apparelList) { + PawnManager.SetApparel(ViewState?.CurrentPawn, apparelList); + } + public void RemovePossession(CustomizedPawn pawn, ThingDef thingDef) { + PawnManager.RemovePossession(pawn, thingDef); + } + public void UpdatePossessionCount(CustomizedPawn pawn, ThingDef thingDef, int count) { + PawnManager.UpdatePossessionCount(pawn, thingDef, count); + } + public void RemoveAbility(Ability ability) { + PawnManager.RemoveAbility(ViewState?.CurrentPawn, ability); + } + public void AddAbility(AbilityDef def) { + PawnManager.AddAbility(ViewState?.CurrentPawn, def); + } + public void SetAbilities(IEnumerable abilities) { + PawnManager.SetAbilities(ViewState?.CurrentPawn, abilities); + } + + public void UpdateIdeo(Ideo ideo) { + PawnManager.UpdateIdeo(ViewState?.CurrentPawn, ideo); + } + + public void RandomizeIdeo() { + PawnManager.RandomizeIdeo(ViewState?.CurrentPawn); + } + + public void UpdateCertainty(float value) { + PawnManager.UpdateCertainty(ViewState?.CurrentPawn, value); + } + public void RegisterPawnLayerUpdateHandlers(Type type, PawnLayerOptionUpdatedHandler optionHandler, PawnLayerColorUpdatedHandler colorHandler) { + RegisterPawnLayerOptionUpdateHandler(type, optionHandler); + RegisterPawnLayerColorUpdateHandler(type, colorHandler); + } + + public void RegisterPawnLayerOptionUpdateHandler(Type type, PawnLayerOptionUpdatedHandler handler) { + PawnLayerOptionUpdateHandlers[type] = handler; + } + + public void RegisterPawnLayerColorUpdateHandler(Type type, PawnLayerColorUpdatedHandler handler) { + PawnLayerColorUpdateHandlers[type] = handler; + } + + public void UpdatePawnLayerOption(PawnLayer pawnLayer, PawnLayerOption option) { + if (PawnLayerOptionUpdateHandlers.TryGetValue(pawnLayer.GetType(), out PawnLayerOptionUpdatedHandler handler)) { + handler(pawnLayer, ViewState?.CurrentPawn, option); + } + } + public void UpdatePawnLayerColor(PawnLayer pawnLayer, Color color) { + if (PawnLayerColorUpdateHandlers.TryGetValue(pawnLayer.GetType(), out PawnLayerColorUpdatedHandler handler)) { + handler(pawnLayer, ViewState?.CurrentPawn, color); + } + } + + public void UpdateFavoriteColor(Color? color) { + PawnManager.UpdateFavoriteColor(ViewState?.CurrentPawn, color); + } + } +} diff --git a/Source/ControllerTabViewRelationships.cs b/Source/ControllerTabViewRelationships.cs new file mode 100644 index 0000000..7cc4386 --- /dev/null +++ b/Source/ControllerTabViewRelationships.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using RimWorld; + +namespace EdB.PrepareCarefully { + public class ControllerTabViewRelationships { + public ModState State { get; set; } + public ViewState ViewState { get; set; } + public ManagerRelationships RelationshipManager { get; set; } + + public void AddRelationship(PawnRelationDef def, CustomizedPawn source, CustomizedPawn target) { + RelationshipManager.AddRelationship(def, source, target); + } + public void RemoveRelationship(CustomizedRelationship relationship) { + RelationshipManager.DeleteRelationship(relationship); + } + public void DeleteAllPawnRelationships(CustomizedPawn pawn) { + RelationshipManager.DeletePawn(pawn); + } + public void AddPawn(CustomizedPawn pawn) { + RelationshipManager.AddVisibleParentChildPawn(pawn); + } + public void ReplacePawn(CustomizedPawn pawn) { + RelationshipManager.DeletePawn(pawn); + RelationshipManager.AddVisibleParentChildPawn(pawn); + } + public void AddParentToParentChildGroup(ParentChildGroup group, CustomizedPawn pawn) { + if (!group.Parents.Contains(pawn) && !group.Children.Contains(pawn)) { + group.Parents.Add(pawn); + } + } + public void RemoveParentFromParentChildGroup(ParentChildGroup group, CustomizedPawn pawn) { + group.Parents.Remove(pawn); + if (group.Parents.Count == 0 && group.Children.Count == 0) { + RelationshipManager.RemoveParentChildGroup(group); + } + } + public void AddChildToParentChildGroup(ParentChildGroup group, CustomizedPawn pawn) { + if (!group.Parents.Contains(pawn) && !group.Children.Contains(pawn)) { + group.Children.Add(pawn); + } + } + public void RemoveChildFromParentChildGroup(ParentChildGroup group, CustomizedPawn pawn) { + group.Children.Remove(pawn); + if (group.Parents.Count == 0 && group.Children.Count == 0) { + RelationshipManager.RemoveParentChildGroup(group); + } + } + public void AddParentChildGroup(ParentChildGroup group) { + State.Customizations.ParentChildGroups.Add(group); + } + } +} diff --git a/Source/CostCalculator.cs b/Source/CostCalculator.cs index 483569b..fb554bb 100644 --- a/Source/CostCalculator.cs +++ b/Source/CostCalculator.cs @@ -6,7 +6,7 @@ using Verse; namespace EdB.PrepareCarefully { - public class ColonistCostDetails { + public class PawnCostDetailsRefactored { public string name; public double total = 0; public double passionCount = 0; @@ -14,6 +14,7 @@ public class ColonistCostDetails { public double traits = 0; public double apparel = 0; public double bionics = 0; + public double possessions = 0; public double animals = 0; public double marketValue = 0; public void Clear() { @@ -22,11 +23,12 @@ public void Clear() { traits = 0; apparel = 0; bionics = 0; + possessions = 0; animals = 0; marketValue = 0; } public void ComputeTotal() { - total = Math.Ceiling(passions + traits + apparel + bionics + marketValue + animals); + total = Math.Ceiling(passions + traits + apparel + bionics + possessions + marketValue + animals); } public void Multiply(double amount) { passions = Math.Ceiling(passions * amount); @@ -36,11 +38,12 @@ public void Multiply(double amount) { } } - public class CostDetails { + public class CostDetailsRefactored { public double total = 0; - public List colonistDetails = new List(); + public List colonistDetails = new List(); public double colonists = 0; public double colonistApparel = 0; + public double colonistPossessions = 0; public double colonistBionics = 0; public double equipment = 0; public double animals = 0; @@ -51,6 +54,7 @@ public void Clear(int colonistCount) { animals = 0; colonists = 0; colonistApparel = 0; + colonistPossessions = 0; colonistBionics = 0; int listSize = colonistDetails.Count; if (colonistCount != listSize) { @@ -61,7 +65,7 @@ public void Clear(int colonistCount) { else { int diff = colonistCount - listSize; for (int i = 0; i < diff; i++) { - colonistDetails.Add(new ColonistCostDetails()); + colonistDetails.Add(new PawnCostDetailsRefactored()); } } } @@ -75,59 +79,66 @@ public void ComputeTotal() { colonists += cost.total; colonistApparel += cost.apparel; colonistBionics += cost.bionics; + colonistPossessions += cost.possessions; } total = Math.Ceiling(total); colonists = Math.Ceiling(colonists); colonistApparel = Math.Ceiling(colonistApparel); + colonistPossessions = Math.Ceiling(colonistPossessions); colonistBionics = Math.Ceiling(colonistBionics); } } public class CostCalculator { + public ProviderHealthOptions ProviderHealthOptions { get; set; } protected HashSet freeApparel = new HashSet(); protected HashSet cheapApparel = new HashSet(); - StatWorker statWorker = null; + public StatWorker MarketValueStatWorker { get; set; } + public float CostForRandomAnimal { get; set; } = 250f; + public CostCalculator() { cheapApparel.Add("Apparel_Pants"); cheapApparel.Add("Apparel_BasicShirt"); cheapApparel.Add("Apparel_Jacket"); + MarketValueStatWorker = StatDefOf.MarketValue.Worker; } - - public void Calculate(CostDetails cost, List pawns, List equipment, List animals) { - cost.Clear(pawns.Where(pawn => pawn.Type == CustomPawnType.Colonist).Count()); + public CostDetailsRefactored Calculate(IEnumerable customizedPawns, IEnumerable equipment) { + CostDetailsRefactored result = new CostDetailsRefactored(); int i = 0; - foreach (var pawn in pawns) { - if (pawn.Type == CustomPawnType.Colonist) { - CalculatePawnCost(cost.colonistDetails[i++], pawn); + foreach (var customizedPawn in customizedPawns) { + if (customizedPawn.Type == CustomizedPawnType.Colony) { + if (i >= result.colonistDetails.Count) { + } + result.colonistDetails.Add(CalculatePawnCost(customizedPawn)); } } foreach (var e in equipment) { - cost.equipment += CalculateEquipmentCost(e); + result.equipment += CalculateEquipmentCost(e); } - cost.ComputeTotal(); + result.ComputeTotal(); + return result; } - public void CalculatePawnCost(ColonistCostDetails cost, CustomPawn pawn) { + public PawnCostDetailsRefactored CalculatePawnCost(CustomizedPawn pawn) { + PawnCostDetailsRefactored cost = new PawnCostDetailsRefactored(); cost.Clear(); - cost.name = pawn.NickName; + cost.name = pawn.Pawn.LabelShortCap; - // Start with the market value plus a bit of a mark-up. - cost.marketValue = pawn.Pawn.MarketValue; + //// Start with the market value plus a bit of a mark-up. + cost.marketValue = MarketValueStatWorker.GetValue(pawn.Pawn); cost.marketValue += 300; // Calculate passion cost. Each passion above 8 makes all passions // cost more. Minor passion counts as one passion. Major passion // counts as 3. - double skillCount = pawn.currentPassions.Keys.Count(); double passionLevelCount = 0; double passionLevelCost = 20; double passionateSkillCount = 0; - foreach (SkillDef def in pawn.currentPassions.Keys) { - Passion passion = pawn.currentPassions[def]; - int level = pawn.GetSkillLevel(def); - + foreach (SkillRecord skillRecord in pawn.Pawn.skills.skills.Where(r => r.passion != Passion.None)) { + Passion passion = skillRecord.passion; + int level = skillRecord.levelInt; if (passion == Passion.Major) { passionLevelCount += 3.0; passionateSkillCount += 1.0; @@ -144,53 +155,28 @@ public void CalculatePawnCost(ColonistCostDetails cost, CustomPawn pawn) { } cost.marketValue += levelCost * passionLevelCount; + // Calculate trait cost. - if (pawn.TraitCount > 3) { - int extraTraitCount = pawn.TraitCount - 3; + int traitCount = pawn.Pawn.story.traits.allTraits.Count; + if (traitCount > 3) { + int extraTraitCount = traitCount - 3; double extraTraitCost = 100; - for (int i=0; i< extraTraitCount; i++) { + for (int i = 0; i < extraTraitCount; i++) { cost.marketValue += extraTraitCost; extraTraitCost = Math.Ceiling(extraTraitCost * 2.5); } } // Calculate cost of worn apparel. - foreach (var layer in PrepareCarefully.Instance.Providers.PawnLayers.GetLayersForPawn(pawn)) { - if (layer.Apparel) { - var def = pawn.GetAcceptedApparel(layer); - if (def == null) { - continue; - } - EquipmentKey key = new EquipmentKey(); - key.ThingDef = def; - key.StuffDef = pawn.GetSelectedStuff(layer); - EquipmentRecord record = PrepareCarefully.Instance.EquipmentDatabase.Find(key); - if (record == null) { - continue; - } - EquipmentSelection selection = new EquipmentSelection(record, 1); - double c = CalculateEquipmentCost(selection); - if (def != null) { - // TODO: Discounted materials should be based on the faction, not hard-coded. - // TODO: Should we continue with the discounting? - if (key.StuffDef != null) { - if (key.StuffDef.defName == "Synthread") { - if (freeApparel.Contains(key.ThingDef.defName)) { - c = 0; - } - else if (cheapApparel.Contains(key.ThingDef.defName)) { - c = c * 0.15d; - } - } - } - } - cost.apparel += c; - } + foreach (var apparel in pawn.Pawn.apparel.WornApparel) { + double c = MarketValueStatWorker.GetValue(apparel, pawn.Pawn); + cost.apparel += c; + //Logger.Debug(string.Format("Market value for pawn apparel; pawn = {0}, apparel = {1}, cost = {2}", pawn.Pawn.LabelShortCap, apparel.def.defName, c)); } // Calculate cost for any materials needed for implants. - OptionsHealth healthOptions = PrepareCarefully.Instance.Providers.Health.GetOptions(pawn); - foreach (Implant option in pawn.Implants) { + OptionsHealth healthOptions = ProviderHealthOptions.GetOptions(pawn); + foreach (Implant option in pawn.Customizations.Implants) { // Check if there are any ancestor parts that override the selection. UniqueBodyPart uniquePart = healthOptions.FindBodyPartsForRecord(option.BodyPartRecord); @@ -198,37 +184,25 @@ public void CalculatePawnCost(ColonistCostDetails cost, CustomPawn pawn) { Logger.Warning("Could not find body part record when computing the cost of an implant: " + option.BodyPartRecord.def.defName); continue; } - if (pawn.AtLeastOneImplantedPart(uniquePart.Ancestors.Select((UniqueBodyPart p) => { return p.Record; }))) { - continue; - } - // Figure out the cost of the part replacement based on its recipe's ingredients. - if (option.recipe != null) { - RecipeDef def = option.recipe; - foreach (IngredientCount amount in def.ingredients) { - int count = 0; - double totalCost = 0; - bool skip = false; - foreach (ThingDef ingredientDef in amount.filter.AllowedThingDefs) { - if (ingredientDef.IsMedicine) { - skip = true; - break; - } - count++; - EquipmentRecord entry = PrepareCarefully.Instance.EquipmentDatabase.LookupEquipmentRecord(new EquipmentKey(ingredientDef, null)); - if (entry != null) { - totalCost += entry.cost * (double)amount.GetBaseCount(); - } - } - if (skip || count == 0) { - continue; - } - cost.bionics += (int)(totalCost / (double)count); + bool foundOverridingAncestor = false; + foreach (var ancestorPart in uniquePart.Ancestors.Select((UniqueBodyPart p) => { return p.Record; })) { + if (pawn.Customizations.Implants.Any(i => i.BodyPartRecord == ancestorPart)) { + foundOverridingAncestor = true; + break; } } + if (foundOverridingAncestor) { + continue; + } + } + + foreach (CustomizedPossession possession in pawn.Customizations.Possessions) { + cost.possessions += MarketValueStatWorker.GetValue(StatRequest.For(possession.ThingDef, null)) * possession.Count; } cost.apparel = Math.Ceiling(cost.apparel); + cost.possessions = Math.Ceiling(cost.possessions); cost.bionics = Math.Ceiling(cost.bionics); // Use a multiplier to balance pawn cost vs. equipment cost. @@ -236,61 +210,27 @@ public void CalculatePawnCost(ColonistCostDetails cost, CustomPawn pawn) { cost.Multiply(1.0); cost.ComputeTotal(); + return cost; } - public double CalculateEquipmentCost(EquipmentSelection equipment) { - EquipmentRecord entry = PrepareCarefully.Instance.EquipmentDatabase.LookupEquipmentRecord(equipment.Key); - if (entry != null) { - return (double)equipment.Count * entry.cost; - } - else { - return 0; - } - } - - public double GetAnimalCost(Thing thing) { - if (statWorker == null) { - StatDef marketValueStatDef = StatDefOf.MarketValue; - statWorker = marketValueStatDef.Worker; - } - float value = statWorker.GetValue(StatRequest.For(thing)); - return value; - } - - public double GetBaseThingCost(ThingDef def, ThingDef stuffDef) { - double value = 0; - if (stuffDef != null) { - value = StatWorker_MarketValue.CalculatedBaseMarketValue(def, stuffDef); - } - if (value == 0) { - value = def.BaseMarketValue; - } - if (value > 100) { - value = Math.Round(value / 5.0) * 5.0; - } - value = Math.Round(value, 2); - return value; - } - - public double CalculateStackCost(ThingDef def, ThingDef stuffDef, double baseCost) { - double cost = baseCost; - - if (def.MadeFromStuff) { - if (def.IsApparel) { - cost *= 1; + public double CalculateEquipmentCost(CustomizedEquipment equipment) { + double cost; + if (equipment.EquipmentOption.ThingDef != null) { + if (equipment.Quality.HasValue) { + cost = MarketValueStatWorker.GetValue(StatRequest.For(equipment.EquipmentOption.ThingDef, equipment.StuffDef, equipment.Quality.Value)); } else { - cost *= 0.5; + cost = MarketValueStatWorker.GetValue(StatRequest.For(equipment.EquipmentOption.ThingDef, equipment.StuffDef)); } + //Logger.Debug(string.Format("Market value for equipment; item = {0}, stuff = {1}, quality = {2}, cost = {3} x {4} = {5}", equipment.ThingDef?.LabelCap, equipment.StuffDef?.LabelCap, equipment.Quality?.GetLabel(), cost, equipment.Count, cost * equipment.Count)); + return cost * equipment.Count; } - - if (def.IsRangedWeapon) { - cost *= 2; + else if (equipment.EquipmentOption.RandomAnimal) { + return CostForRandomAnimal * equipment.Count; + } + else { + return 0; } - - cost = Math.Round(cost, 2); - - return cost; } } } diff --git a/Source/CustomFaction.cs b/Source/CustomFaction.cs index b9c2d2f..e9e684e 100644 --- a/Source/CustomFaction.cs +++ b/Source/CustomFaction.cs @@ -1,10 +1,11 @@ -using RimWorld; +using RimWorld; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Verse; namespace EdB.PrepareCarefully { + // TODO: Revisit public class CustomFaction { private String name; public FactionDef Def { @@ -20,7 +21,7 @@ public string Name { } } public Nullable Index { - get; + get; set; } public Faction Faction { diff --git a/Source/CustomPawn.cs b/Source/CustomPawn.cs deleted file mode 100644 index b2c327c..0000000 --- a/Source/CustomPawn.cs +++ /dev/null @@ -1,1748 +0,0 @@ -using RimWorld; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using UnityEngine; -using Verse; -using System.Text; - -namespace EdB.PrepareCarefully { - public class ApparelConflict { - public ThingDef def; - public ThingDef conflict; - } - - public class CustomPawn { - // The pawn's skill values before customization, without modifiers for backstories and traits. - // These values are saved so that the user can click the "Reset" button to restore them. - protected Dictionary originalSkillLevels = new Dictionary(); - - // The pawn's current skill levels, without modifiers for backstories and traits. - protected Dictionary currentSkillLevels = new Dictionary(); - - // The pawn's skill value modifiers from selected backstories and traits. - protected Dictionary skillLevelModifiers = new Dictionary(); - - public Dictionary originalPassions = new Dictionary(); - public Dictionary currentPassions = new Dictionary(); - - protected string incapable = null; - protected Pawn pawn; - - protected Dictionary colors = new Dictionary(); - - protected Dictionary selectedApparel = new Dictionary(); - protected Dictionary acceptedApparel = new Dictionary(); - protected Dictionary selectedStuff = new Dictionary(); - protected Dictionary colorCache = new Dictionary(); - protected string apparelConflictText = null; - protected List apparelConflicts = new List(); - - // Keep track of the most recently selected adulthood option so that if the user updates the pawn's - // age in a way that switches them back and forth from adult to child (which nulls out the adulthood - // value in the Pawn), we can remember what the value was and restore it. - public BackstoryDef LastSelectedAdulthoodBackstory = null; - public BackstoryDef LastSelectedChildhoodBackstory = null; - - // A GUID provides a unique identifier for the CustomPawn. - protected string id; - - //protected CustomHeadType headType; - - protected List implants = new List(); - protected List injuries = new List(); - public List bodyParts = new List(); - protected ThingCache thingCache = new ThingCache(); - protected bool portraitDirty = true; - protected AlienRace alienRace = null; - protected CustomFaction faction = null; - protected PawnKindDef originalKindDef = null; - protected FactionDef originalFactionDef = null; - - public CustomPawn() { - GenerateId(); - } - - public CustomPawn(Pawn pawn) { - GenerateId(); - InitializeWithPawn(pawn); - } - - public string Id { - get { - return id; - } - set { - id = value; - } - } - - public CustomPawnType Type { - get; - set; - } - - // For hidden or temporary pawns, keep track of an index number. - public int? Index { - get; - set; - } - - public bool Hidden { - get { - return Type == CustomPawnType.Hidden || Type == CustomPawnType.Temporary; - } - } - - public XenotypeDef RandomizeXenotype { get; set; } - public CustomXenotype RandomizeCustomXenotype { get; set; } - public bool RandomizeAnyNonArchite { get; set; } - public DevelopmentalStage RandomizeDevelopmentalStage { get; set; } - - public CustomFaction Faction { - get { - return faction; - } - set { - faction = value; - } - } - - // Stores the original FactionDef of a pawn that was created from a faction template. - public FactionDef OriginalFactionDef { - get { - return originalFactionDef; - } - set { - originalFactionDef = value; - } - } - - public BodyTypeDef BodyType { - get { - return pawn.story.bodyType; - } - set { - this.pawn.story.bodyType = value; - MarkPortraitAsDirty(); - } - } - - public AlienRace AlienRace { - get { - return alienRace; - } - } - - public bool IsAlien { - get { - return alienRace != null && Pawn.def.defName != "Human"; - } - } - - public bool HasCustomBodyParts { - get { - return bodyParts.Count > 0; - } - } - - public List Injuries { - get { return injuries; } - set { injuries = value; } - } - - public List BodyParts { - get { - return bodyParts; - } - } - - public float Certainty { - get { - return pawn.ideo?.Certainty ?? 0.75f; - } - set { - if (pawn.ideo != null) { - float current = pawn.ideo.Certainty; - if (current != value) { - pawn.ideo.Debug_ReduceCertainty(current - value); - } - } - } - } - - public Color HairColor { - get { - return pawn.story.HairColor; - } - set { - pawn.story.HairColor = value; - MarkPortraitAsDirty(); - } - } - - public BeardDef Beard { - get { - return pawn.style.beardDef; - } - set { - pawn.style.beardDef = value; - MarkPortraitAsDirty(); - } - } - - public TattooDef FaceTattoo { - get { - return pawn.style.FaceTattoo; - } - set { - if (ModLister.IdeologyInstalled) { - pawn.style.FaceTattoo = value; - MarkPortraitAsDirty(); - } - } - } - - public TattooDef BodyTattoo { - get { - return pawn.style.BodyTattoo; - } - set { - if (ModLister.IdeologyInstalled) { - pawn.style.BodyTattoo = value; - MarkPortraitAsDirty(); - } - } - } - - public void GenerateId() { - this.id = Guid.NewGuid().ToStringSafe(); - } - - // We use a dirty flag for the portrait to avoid calling ClearCachedPortrait() every frame. - protected void CheckPortraitCache() { - if (portraitDirty) { - portraitDirty = false; - pawn.ClearCachedPortraits(); - } - } - - public void MarkPortraitAsDirty() { - portraitDirty = true; - } - - public void UpdatePortrait() { - CheckPortraitCache(); - } - - public RenderTexture GetPortrait(Vector2 size) { - return PortraitsCache.Get(Pawn, size, Rot4.South, new Vector3(0, 0, 0), 1.0f, supersample: true, compensateForUIScale: true, renderClothes: true, renderHeadgear: true, overrideApparelColors: null, overrideHairColor: null, stylingStation: true); - } - - public void InitializeWithPawn(Pawn pawn) { - this.pawn = pawn; - this.pawn.ClearCaches(); - - this.originalKindDef = pawn.kindDef; - this.originalFactionDef = pawn.Faction != null ? pawn.Faction.def : null; - - PrepareCarefully.Instance.Providers.Health.GetOptions(this); - - // Set the skills. - InitializeSkillLevelsAndPassions(); - ComputeSkillLevelModifiers(); - - // Clear all of the pawn layer colors. The apparel colors will be set a little later - // when we initialize the apparel layer. - colors.Clear(); - foreach (var layer in PrepareCarefully.Instance.Providers.PawnLayers.GetLayersForPawn(this)) { - colors.Add(layer, Color.white); - } - - // Clear all of the apparel and alien addon layers that we're tracking in the CustomPawn. - selectedApparel.Clear(); - acceptedApparel.Clear(); - selectedStuff.Clear(); - foreach (var layer in PrepareCarefully.Instance.Providers.PawnLayers.GetLayersForPawn(this)) { - if (layer.Apparel) { - selectedApparel.Add(layer, null); - acceptedApparel.Add(layer, null); - selectedStuff.Add(layer, null); - } - } - - // Store the current value of each apparel layer based on the apparel worn by the Pawn. - foreach (Apparel current in this.pawn.apparel.WornApparel) { - Color color = current.DrawColor; - CompColorable colorable = current.TryGetComp(); - if (colorable != null) { - //Logger.Debug(String.Format("{0} {1}, CompColorable: color={2}, desiredColor={3}, active={4}", current.def.defName, current.Stuff?.defName, colorable?.Color, colorable?.DesiredColor, colorable?.Active)); - if (colorable.Active) { - color = colorable.Color; - } - } - PawnLayer layer = PrepareCarefully.Instance.Providers.PawnLayers.FindLayerForApparel(current.def); - if (layer != null) { - SetSelectedApparelInternal(layer, current.def); - acceptedApparel[layer] = current.def; - SetSelectedStuffInternal(layer, current.Stuff); - SetColorInternal(layer, color); - } - } - - // Reset CustomPawn cached values. - ResetApparel(); - ResetCachedIncapableOf(); - - // Copy the adulthood backstory or set a random one if it's null. - this.LastSelectedAdulthoodBackstory = pawn.story.Adulthood; - - // Evaluate all hediffs. - InitializeInjuriesAndImplantsFromPawn(pawn); - - if (ModsConfig.BiotechActive) { - customXenotype = pawn.MatchGenesToCustomXenotype(); - if (customXenotype != null) { - RandomizeCustomXenotype = customXenotype; - RandomizeXenotype = null; - xenotype = null; - } - else { - RandomizeCustomXenotype = null; - xenotype = pawn.genes.Xenotype; - RandomizeXenotype = xenotype; - } - RandomizeDevelopmentalStage = pawn.DevelopmentalStage; - } - - // Set the alien race, if any. - alienRace = PrepareCarefully.Instance.Providers.AlienRaces.GetAlienRace(pawn.def); - - // Clear all of the pawn caches. - ClearPawnCaches(); - } - - public void InitializeInjuriesAndImplantsFromPawn(Pawn pawn) { - OptionsHealth healthOptions = PrepareCarefully.Instance.Providers.Health.GetOptions(this); - List injuries = new List(); - List implants = new List(); - - // Create a lookup of all of the body parts that are missing - HashSet missingParts = new HashSet(); - foreach (var hediff in pawn.health.hediffSet.hediffs) { - if (hediff is Hediff_MissingPart || hediff is Hediff_AddedPart) { - missingParts.Add(hediff.Part); - } - } - - foreach (var hediff in pawn.health.hediffSet.hediffs) { - InjuryOption option = healthOptions.FindInjuryOptionByHediffDef(hediff.def); - if (option != null) { - //Logger.Debug("Found injury option for {" + hediff.def.defName + "} for part {" + hediff.Part?.LabelCap + "}"); - - // If the hediff is a missing part and the part's parent is also missing, we don't add a missing part hediff for the child part. - if (hediff is Hediff_MissingPart) { - if (hediff.Part.parent != null && missingParts.Contains(hediff.Part.parent)) { - continue; - } - } - - Injury injury = new Injury(); - injury.BodyPartRecord = hediff.Part; - injury.Option = option; - injury.Severity = hediff.Severity; - injury.Hediff = hediff; - HediffComp_GetsPermanent getsPermanent = hediff.TryGetComp(); - if (getsPermanent != null) { - injury.PainFactor = getsPermanent.PainFactor; - } - - injury.Chemical = (hediff as Hediff_ChemicalDependency)?.chemical; - - injuries.Add(injury); - } - else { - //Logger.Debug("Did not find injury option for {" + hediff.def.defName + "} for part {" + hediff.Part?.LabelCap + "}"); - RecipeDef implantRecipe = healthOptions.FindImplantRecipesThatAddHediff(hediff).RandomElementWithFallback(null); - if (implantRecipe != null) { - Implant implant = new Implant(); - implant.recipe = implantRecipe; - implant.BodyPartRecord = hediff.Part; - implant.Hediff = hediff; - implant.HediffDef = hediff?.def; - implants.Add(implant); - //Logger.Debug("Found implant recipes for {" + hediff.def.defName + "} for part {" + hediff.Part?.LabelCap + "}"); - } - else if (hediff.def.defName == "MechlinkImplant") { - Implant implant = new Implant(); - implant.BodyPartRecord = hediff.Part; - implant.Hediff = hediff; - implant.HediffDef = hediff?.def; - implants.Add(implant); - } - else if (hediff.def.defName != "MissingBodyPart") { - Logger.Warning("Could not add hediff {" + hediff.def.defName + "} to the pawn because no recipe adds it to the body part {" + (hediff.Part?.def?.defName ?? "WholeBody") + "}"); - } - else { - Logger.Warning("Could not add hediff {" + hediff.def.defName + "} to the pawn. It is not currently supported"); - } - } - } - this.injuries.Clear(); - this.implants.Clear(); - this.bodyParts.Clear(); - foreach (var injury in injuries) { - this.injuries.Add(injury); - this.bodyParts.Add(injury); - } - foreach (var implant in implants) { - this.implants.Add(implant); - this.bodyParts.Add(implant); - } - } - - protected void InitializeSkillLevelsAndPassions() { - - if (pawn.skills == null) { - Logger.Warning("Could not initialize skills for the pawn. No pawn skill tracker for " + pawn.def.defName + ", " + pawn.kindDef.defName); - } - - // Save the original passions and set the current values to the same. - foreach (SkillRecord record in pawn.skills.skills) { - originalPassions[record.def] = record.passion; - currentPassions[record.def] = record.passion; - } - - // Compute and save the original unmodified skill levels. - // If the user's original, modified skill level was zero, we dont actually know what - // their original unadjusted value was. For example, if they have the brawler trait - // (-6 shooting) and their shooting level is zero, what was the original skill level? - // We don't know. It could have been anywhere from 0 to 6. - // We could maybe borrow some code from Pawn_StoryTracker.FinalLevelOfSkill() to be - // smarter about computing the value (i.e. factoring in the pawn's age, etc.), but - // instead we'll just pick a random number from the correct range if this happens. - foreach (var record in pawn.skills.skills) { - int negativeAdjustment = 0; - int positiveAdjustment = 0; - int modifier = ComputeSkillModifier(record.def); - if (modifier < 0) { - negativeAdjustment = -modifier; - } - else if (modifier > 0) { - positiveAdjustment = modifier; - } - - // When figuring out the unadjusted value, take into account the special - // case where the adjusted value is 0 or 20. - int value = record.Level; - if (value == 0 && negativeAdjustment > 0) { - value = Rand.RangeInclusive(1, negativeAdjustment); - } - else if (value == 20 && positiveAdjustment > 0) { - value = Rand.RangeInclusive(20 - positiveAdjustment, 20); - } - else { - value -= positiveAdjustment; - value += negativeAdjustment; - } - - originalSkillLevels[record.def] = value; - } - - // Set the current values to the original values. - foreach (SkillRecord record in pawn.skills.skills) { - currentSkillLevels[record.def] = originalSkillLevels[record.def]; - } - } - - //public BackstoryDef LastSelectedAdulthoodBackstory { - // get { - // if (lastSelectedAdulthoodBackstory != null) { - // return lastSelectedAdulthoodBackstory; - // } - // else { - // lastSelectedAdulthoodBackstory = Randomizer.RandomAdulthood(this); - // return lastSelectedAdulthoodBackstory; - // } - // } - // set { - // lastSelectedAdulthoodBackstory = value; - // } - //} - - public void ClearApparel() { - this.pawn.apparel.DestroyAll(); - } - - public void CopyAppearance(Pawn pawn) { - this.HairDef = pawn.story.hairDef; - this.pawn.story.HairColor = pawn.story.HairColor; - this.pawn.story.bodyType = pawn.story.bodyType; - if (pawn.style != null && this.Pawn.style != null) { - this.Beard = pawn.style.beardDef; - this.FaceTattoo = pawn.style.FaceTattoo; - this.BodyTattoo = pawn.style.BodyTattoo; - } - //this.HeadGraphicPath = pawn.story.HeadGraphicPath; - this.HeadType = pawn.story.headType; - this.MelaninLevel = pawn.genes.GetMelaninGene().minMelanin; - this.Pawn.apparel.DestroyAll(); - foreach (var layer in PrepareCarefully.Instance.Providers.PawnLayers.GetLayersForPawn(this)) { - if (layer.Apparel) { - this.SetSelectedStuff(layer, null); - this.SetSelectedApparel(layer, null); - } - } - foreach (Apparel current in pawn.apparel.WornApparel) { - PawnLayer layer = PrepareCarefully.Instance.Providers.PawnLayers.FindLayerForApparel(current.def); - if (layer != null) { - this.SetSelectedStuff(layer, current.Stuff); - this.SetSelectedApparel(layer, current.def); - } - } - MarkPortraitAsDirty(); - } - - public void RestoreSkillLevelsAndPassions() { - // Restore the original passions. - foreach (SkillRecord record in pawn.skills.skills) { - currentPassions[record.def] = originalPassions[record.def]; - } - - // Restore the original skill levels. - ApplyOriginalSkillLevels(); - } - - // Restores the current skill level values to the saved, original values. - public void ApplyOriginalSkillLevels() { - foreach (var record in pawn.skills.skills) { - currentSkillLevels[record.def] = originalSkillLevels[record.def]; - } - CopySkillsAndPassionsToPawn(); - } - - public void UpdateSkillLevelsForNewBackstoryOrTrait() { - ComputeSkillLevelModifiers(); - ResetCachedIncapableOf(); - ClearPawnCaches(); - CopySkillsAndPassionsToPawn(); - } - - // Computes the skill level modifiers that the pawn gets from the selected backstories and traits. - public void ComputeSkillLevelModifiers() { - foreach (var record in pawn.skills.skills) { - skillLevelModifiers[record.def] = ComputeSkillModifier(record.def); - } - CopySkillsAndPassionsToPawn(); - } - - protected int ComputeSkillModifier(SkillDef def) { - int value = 0; - if (pawn.story != null && pawn.story.Childhood != null && pawn.story.Childhood.skillGains != null) { - if (pawn.story.Childhood.skillGains.ContainsKey(def)) { - value += pawn.story.Childhood.skillGains[def]; - } - } - if (pawn.story != null && pawn.story.Adulthood != null && pawn.story.Adulthood.skillGains != null) { - if (pawn.story.Adulthood.skillGains.ContainsKey(def)) { - value += pawn.story.Adulthood.skillGains[def]; - } - } - foreach (Trait trait in this.Pawn.story.traits.allTraits) { - if (trait != null && trait.def != null && trait.def.degreeDatas != null) { - foreach (TraitDegreeData data in trait.def.degreeDatas) { - if (data.degree == trait.Degree) { - if (data.skillGains != null) { - foreach (var pair in data.skillGains) { - if (pair.Key != null) { - SkillDef skillDef = pair.Key; - if (skillDef == def) { - value += pair.Value; - } - } - } - } - break; - } - } - } - } - return value; - } - - public void DecrementSkillLevel(SkillDef def) { - SetSkillLevel(def, GetSkillLevel(def) - 1); - } - - public void IncrementSkillLevel(SkillDef def) { - SetSkillLevel(def, GetSkillLevel(def) + 1); - } - - public int GetSkillLevel(SkillDef def) { - if (this.IsSkillDisabled(def)) { - return 0; - } - else { - int value = 0; - if (currentSkillLevels.ContainsKey(def)) { - value = currentSkillLevels[def]; - if (skillLevelModifiers.ContainsKey(def)) { - value += skillLevelModifiers[def]; - } - } - if (value < SkillRecord.MinLevel) { - return SkillRecord.MinLevel; - } - else if (value > SkillRecord.MaxLevel) { - value = SkillRecord.MaxLevel; - } - return value; - } - } - - public void SetSkillLevel(SkillDef def, int value) { - if (value > 20) { - value = 20; - } - else if (value < 0) { - value = 0; - } - int modifier = skillLevelModifiers[def]; - if (value < modifier) { - currentSkillLevels[def] = 0; - } - else { - currentSkillLevels[def] = value - modifier; - } - CopySkillsAndPassionsToPawn(); - } - - // Any time a skill changes, update the underlying pawn with the new values. - public void CopySkillsAndPassionsToPawn() { - foreach (var record in pawn.skills.skills) { - record.Level = GetSkillLevel(record.def); - // Reset the XP level based on the current value of the skill. - record.xpSinceLastLevel = Rand.Range(record.XpRequiredForLevelUp * 0.1f, record.XpRequiredForLevelUp * 0.9f); - if (!record.TotallyDisabled) { - record.passion = currentPassions[record.def]; - } - else { - record.passion = Passion.None; - } - } - } - - // Set all unmodified skill levels to zero. - public void ClearSkills() { - foreach (var record in pawn.skills.skills) { - currentSkillLevels[record.def] = 0; - } - CopySkillsAndPassionsToPawn(); - } - - public void ClearPassions() { - foreach (var record in pawn.skills.skills) { - currentPassions[record.def] = Passion.None; ; - } - CopySkillsAndPassionsToPawn(); - } - - public bool IsSkillDisabled(SkillDef def) { - return pawn.skills.GetSkill(def).TotallyDisabled == true; - } - - public int GetSkillModifier(SkillDef def) { - return skillLevelModifiers[def]; - } - - public int GetUnmodifiedSkillLevel(SkillDef def) { - return currentSkillLevels[def]; - } - - public void SetUnmodifiedSkillLevel(SkillDef def, int value) { - currentSkillLevels[def] = value; - CopySkillsAndPassionsToPawn(); - } - - public int GetOriginalSkillLevel(SkillDef def) { - return originalSkillLevels[def]; - } - - public void SetOriginalSkillLevel(SkillDef def, int value) { - originalSkillLevels[def] = value; - } - - private CustomXenotype customXenotype = null; - private XenotypeDef xenotype = null; - - public CustomXenotype CustomXenotype { - get { - return customXenotype; - } - } - public XenotypeDef Xenotype { - get { - return xenotype; - } - } - - public NameTriple Name { - get { - return pawn.Name as NameTriple; - } - set { - pawn.Name = value; - } - } - - public string FirstName { - get { - NameTriple nameTriple = pawn.Name as NameTriple; - if (nameTriple != null) { - return nameTriple.First; - } - else { - return null; - } - } - set { - pawn.Name = new NameTriple(value, NickName, LastName); - } - } - - public string NickName { - get { - NameTriple nameTriple = pawn.Name as NameTriple; - if (nameTriple != null) { - return nameTriple.Nick; - } - else { - return null; - } - } - set { - pawn.Name = new NameTriple(FirstName, value, LastName); - } - } - - public string LastName { - get { - NameTriple nameTriple = pawn.Name as NameTriple; - if (nameTriple != null) { - return nameTriple.Last; - } - else { - return null; - } - } - set { - pawn.Name = new NameTriple(FirstName, NickName, value); - } - } - - public string ShortName { - get { - if (Type == CustomPawnType.Hidden) { - return "EdB.PC.Pawn.HiddenPawnNameShort".Translate(Index.Value); - } - else if (Type == CustomPawnType.Temporary) { - return "EdB.PC.Pawn.TemporaryPawnNameShort".Translate(Index.Value); - } - else { - if (pawn == null) { - Logger.Warning("Pawn was null"); - return ""; - } - return pawn.LabelShortCap; - } - } - } - - public string FullName { - get { - if (Type == CustomPawnType.Hidden) { - if (Index.HasValue) { - return "EdB.PC.Pawn.HiddenPawnNameFull".Translate(Index.Value); - } - else { - return "EdB.PC.Pawn.HiddenPawnNameFull".Translate(); - } - } - else if (Type == CustomPawnType.Temporary) { - if (Index.HasValue) { - return "EdB.PC.Pawn.TemporaryPawnNameFull".Translate(Index.Value); - } - else { - return "EdB.PC.Pawn.TemporaryPawnNameFull".Translate(); - } - } - else { - return pawn.Name.ToStringFull; - } - } - } - - public Pawn Pawn { - get { - return pawn; - } - } - - public string Label { - get { - NameTriple name = pawn.Name as NameTriple; - if (pawn.story.Adulthood == null) { - return name.Nick; - } - return name.Nick + ", " + pawn.story.Adulthood.TitleShortFor(Gender); - } - } - - public string LabelShort { - get { - return pawn.LabelShort; - } - } - - public IEnumerable Implants { - get { - return implants; - } - } - - public bool IsBodyPartReplaced(BodyPartRecord record) { - Implant implant = implants.FirstOrDefault((Implant i) => { - return i.BodyPartRecord == record; - }); - return implant != null; - } - - public bool IsAdult { - get { - var provider = PrepareCarefully.Instance.Providers.AlienRaces; - if (this.alienRace != null) { - return this.BiologicalAge >= (int) this.alienRace.MinAgeForAdulthood; - } - else { - return this.BiologicalAge >= (int)provider.DefaultMinAgeForAdulthood; - } - } - } - - // Stores the original PawnKindDef of the pawn. This value automatically changes when you assign - // a pawn to the FactionOf.Colony, so we want to preserve it for faction pawns that are created from - // a different PawnKindDef. - public PawnKindDef OriginalKindDef { - get { - return originalKindDef; - } - set { - originalKindDef = value; - } - } - - public void SetPassion(SkillDef def, Passion level) { - if (IsSkillDisabled(def)) { - return; - } - currentPassions[def] = level; - SkillRecord record = pawn.skills.GetSkill(def); - if (record != null) { - record.passion = level; - } - } - - public void IncreasePassion(SkillDef def) { - if (IsSkillDisabled(def)) { - return; - } - if (currentPassions[def] == Passion.None) { - currentPassions[def] = Passion.Minor; - } - else if (currentPassions[def] == Passion.Minor) { - currentPassions[def] = Passion.Major; - } - else if (currentPassions[def] == Passion.Major) { - currentPassions[def] = Passion.None; - } - pawn.skills.GetSkill(def).passion = currentPassions[def]; - CopySkillsAndPassionsToPawn(); - } - - public void DecreasePassion(SkillDef def) { - if (IsSkillDisabled(def)) { - return; - } - if (currentPassions[def] == Passion.None) { - currentPassions[def] = Passion.Major; - } - else if (currentPassions[def] == Passion.Minor) { - currentPassions[def] = Passion.None; - } - else if (currentPassions[def] == Passion.Major) { - currentPassions[def] = Passion.Minor; - } - pawn.skills.GetSkill(def).passion = currentPassions[def]; - CopySkillsAndPassionsToPawn(); - } - - public List AllAcceptedApparel { - get { - List result = new List(); - foreach (var layer in PrepareCarefully.Instance.Providers.PawnLayers.GetLayersForPawn(this)) { - ThingDef def = this.acceptedApparel[layer]; - if (def != null) { - result.Add(def); - } - } - return result; - } - } - - public ThingDef GetAcceptedApparel(PawnLayer layer) { - return this.acceptedApparel[layer]; - } - - public Color GetColor(PawnLayer layer) { - if (colors.TryGetValue(layer, out Color color)) { - return color; - } - else { - return Color.white; - } - } - - public void ClearColorCache() { - colorCache.Clear(); - } - - public Color GetStuffColor(PawnLayer layer) { - ThingDef apparelDef = this.selectedApparel[layer]; - if (apparelDef != null) { - Color color = GetColor(layer); - if (apparelDef.MadeFromStuff) { - ThingDef stuffDef = this.selectedStuff[layer]; - if (stuffDef != null && stuffDef.stuffProps != null) { - if (!stuffDef.stuffProps.allowColorGenerators) { - return stuffDef.stuffProps.color; - } - } - } - } - return Color.white; - } - - public void SetColor(PawnLayer layer, Color color) { - SetColorInternal(layer, color); - ResetApparel(); - } - - // Separate method that can be called internally without clearing the graphics caches or copying - // to the target pawn. - public void SetColorInternal(PawnLayer layer, Color color) { - this.colors[layer] = color; - if (layer.Apparel) { - colorCache[new EquipmentKey(selectedApparel[layer], selectedStuff[layer])] = color; - } - } - - public bool ColorMatches(Color a, Color b) { - if (a.r > b.r - 0.001f && a.r < b.r + 0.001f - && a.r > b.r - 0.001f && a.r < b.r + 0.001f - && a.r > b.r - 0.001f && a.r < b.r + 0.001f) { - return true; - } - else { - return false; - } - } - - private void ResetApparel() { - CopyApparelToPawn(this.pawn); - MarkPortraitAsDirty(); - } - - public ThingDef GetSelectedApparel(PawnLayer layer) { - return this.selectedApparel[layer]; - } - - public void SetSelectedApparel(PawnLayer layer, ThingDef def) { - SetSelectedApparelInternal(layer, def); - ResetApparel(); - } - - // Separate method that can be called internally without clearing the graphics caches or copying - // to the target pawn. - private void SetSelectedApparelInternal(PawnLayer layer, ThingDef def) { - if (layer == null) { - return; - } - this.selectedApparel[layer] = def; - if (def != null) { - ThingDef stuffDef = this.GetSelectedStuff(layer); - EquipmentKey pair = new EquipmentKey(def, stuffDef); - if (colorCache.ContainsKey(pair)) { - this.colors[layer] = colorCache[pair]; - } - else { - if (stuffDef == null) { - if (def.colorGenerator != null) { - if (!ColorValidator.Validate(def.colorGenerator, this.colors[layer])) { - this.colors[layer] = def.colorGenerator.NewRandomizedColor(); - } - } - else { - this.colors[layer] = Color.white; - } - } - else { - this.colors[layer] = stuffDef.stuffProps.color; - } - } - } - this.acceptedApparel[layer] = def; - ApparelAcceptanceTest(); - } - - public ThingDef GetSelectedStuff(PawnLayer layer) { - return this.selectedStuff[layer]; - } - - public void SetSelectedStuff(PawnLayer layer, ThingDef stuffDef) { - SetSelectedStuffInternal(layer, stuffDef); - ResetApparel(); - } - - public string ProfessionLabel { - get { - if (IsAdult) { - return Adulthood.TitleCapFor(Gender); - } - else { - return Childhood.TitleCapFor(Gender); - } - } - } - - public string ProfessionLabelShort { - get { - if (IsAdult) { - return Adulthood.TitleShortFor(Gender).CapitalizeFirst(); - } - else { - return Childhood.TitleShortFor(Gender).CapitalizeFirst(); - } - } - } - - public void SetSelectedStuffInternal(PawnLayer layer, ThingDef stuffDef) { - if (layer == null) { - return; - } - if (selectedStuff[layer] == stuffDef) { - return; - } - selectedStuff[layer] = stuffDef; - if (stuffDef != null) { - ThingDef apparelDef = this.GetSelectedApparel(layer); - if (apparelDef != null) { - EquipmentKey pair = new EquipmentKey(apparelDef, stuffDef); - Color color; - if (colorCache.TryGetValue(pair, out color)) { - colors[layer] = color; - } - else { - colors[layer] = stuffDef.stuffProps.color; - } - } - } - } - - protected void ApparelAcceptanceTest() { - // Clear out any conflicts from a previous check. - apparelConflicts.Clear(); - - // Assume that each peice of apparel will be accepted. - foreach (var layer in PrepareCarefully.Instance.Providers.PawnLayers.GetLayersForPawn(this).AsEnumerable().Where((layer) => { return layer.Apparel; }).Reverse()) { - this.acceptedApparel[layer] = selectedApparel[layer]; - } - foreach (var i in PrepareCarefully.Instance.Providers.PawnLayers.GetLayersForPawn(this).AsEnumerable().Where((layer) => { return layer.Apparel; }).Reverse()) { - // If no apparel was selected for this layer, go to the next layer. - if (selectedApparel[i] == null) { - continue; - } - - ThingDef apparel = selectedApparel[i]; - if (apparel.apparel != null && apparel.apparel.layers != null && apparel.apparel.layers.Count > 1) { - foreach (ApparelLayerDef apparelLayer in apparel.apparel.layers) { - // If the apparel's layer matches the current layer, go to the apparel's next layer. - if (apparelLayer == i.ApparelLayer) { - continue; - } - - // If the apparel covers another layer as well as the current one, check to see - // if the user has selected another piece of apparel for that layer. If so, check - // to see if it covers any of the same body parts. If it does, it's a conflict. - PawnLayer disallowedLayer = PrepareCarefully.Instance.Providers.PawnLayers.FindLayerForApparelLayer(apparelLayer); - if (disallowedLayer != null && this.selectedApparel[disallowedLayer] != null) { - foreach (var group in this.selectedApparel[disallowedLayer].apparel.bodyPartGroups) { - if (apparel.apparel.bodyPartGroups.Contains(group)) { - ApparelConflict conflict = new ApparelConflict(); - conflict.def = selectedApparel[i]; - conflict.conflict = selectedApparel[disallowedLayer]; - apparelConflicts.Add(conflict); - this.acceptedApparel[disallowedLayer] = null; - break; - } - } - } - } - } - } - - if (apparelConflicts.Count > 0) { - HashSet defs = new HashSet(); - foreach (ApparelConflict conflict in apparelConflicts) { - defs.Add(conflict.def); - } - List sortedDefs = new List(defs); - /* - sortedDefs.Sort((ThingDef a, ThingDef b) => { - int c = PawnLayers.ToPawnLayerIndex(a.apparel); - int d = PawnLayers.ToPawnLayerIndex(b.apparel); - if (c > d) { - return -1; - } - else if (c < d) { - return 1; - } - else { - return 0; - } - }); - */ - - StringBuilder builder = new StringBuilder(); - int index = 0; - foreach (ThingDef def in sortedDefs) { - string label = def.label; - string message = "EdB.PC.Panel.Appearance.ApparelConflict.Description".Translate(); - message = message.Replace("{0}", label); - builder.Append(message); - builder.AppendLine(); - foreach (ApparelConflict conflict in apparelConflicts.FindAll((ApparelConflict c) => { return c.def == def; })) { - builder.Append("EdB.PC.Panel.Appearance.ApparelConflict.LineItem".Translate().Replace("{0}", conflict.conflict.label)); - builder.AppendLine(); - } - if (++index < sortedDefs.Count) { - builder.AppendLine(); - } - } - this.apparelConflictText = builder.ToString(); - } - else { - this.apparelConflictText = null; - } - } - - public string ApparelConflict { - get { - return apparelConflictText; - } - } - - public BackstoryDef Childhood { - get { - return pawn.story.Childhood; - } - set { - pawn.story.Childhood = value; - ResetBackstories(); - } - } - - public BackstoryDef Adulthood { - get { - return pawn.story.Adulthood; - } - set { - if (value != null) { - LastSelectedAdulthoodBackstory = value; - } - if (IsAdult) { - pawn.story.Adulthood = value; - } - else { - pawn.story.Adulthood = null; - } - ResetBackstories(); - } - } - - public bool HasAdulthoodBackstory { - get { - return Adulthood != null; - } - } - - public void ResetBackstories() { - UpdateSkillLevelsForNewBackstoryOrTrait(); - } - - public HeadTypeDef HeadType { - get { - return pawn.story.headType; - } - set { - pawn.story.headType = value; - MarkPortraitAsDirty(); - } - } - protected string FilterHeadPathForGender(string path) { - if (pawn.gender == Gender.Male) { - return path.Replace("Female", "Male"); - } - else { - return path.Replace("Male", "Female"); - } - } - - public void ClearTraits() { - this.Pawn.story.traits.allTraits.Clear(); - ResetTraits(); - } - - public void AddTrait(Trait trait) { - this.Pawn.story.traits.allTraits.Add(trait); - ResetTraits(); - } - - public Trait GetTrait(int index) { - return this.Pawn.story.traits.allTraits[index]; - } - - public void SetTrait(int index, Trait trait) { - this.Pawn.story.traits.allTraits[index] = trait; - ResetTraits(); - } - - public void RemoveTrait(Trait trait) { - this.Pawn.story.traits.allTraits.Remove(trait); - ResetTraits(); - } - - public IEnumerable Traits { - get { - return this.Pawn.story.traits.allTraits; - } - } - - public int TraitCount { - get { - return this.Pawn.story.traits.allTraits.Count; - } - } - - protected void ResetTraits() { - ApplyInjuriesAndImplantsToPawn(); - UpdateSkillLevelsForNewBackstoryOrTrait(); - } - - public bool HasTrait(Trait trait) { - return this.Pawn.story.traits.allTraits.Find((Trait t) => { - if (t == null && trait == null) { - return true; - } - else if (trait == null || t == null) { - return false; - } - else if (trait.Label.Equals(t.Label)) { - return true; - } - else { - return false; - } - }) != null; - } - - public string IncapableOf { - get { - return incapable; - } - } - - public Gender Gender { - get { - return pawn.gender; - } - set { - if (pawn.gender != value) { - pawn.gender = value; - ResetGender(); - MarkPortraitAsDirty(); - } - } - } - - public Color? FavoriteColor { - get { - if (ModsConfig.IdeologyActive) { - return Pawn.story.favoriteColor; - } - else { - return null; - } - } - set { - if (ModsConfig.IdeologyActive) { - Pawn.story.favoriteColor = value; - } - } - } - - public Color SkinColor { - get { - return pawn.story.SkinColor; - } - set { - if (alienRace != null) { - ThingComp alienComp = ProviderAlienRaces.FindAlienCompForPawn(pawn); - if (alienComp == null) { - return; - } - - MarkPortraitAsDirty(); - - // Pre 1.2 - QuietReflectionUtil.SetFieldValue(alienComp, "skinColor", value); - QuietReflectionUtil.SetFieldValue(alienComp, "skinColorSecond", value); - - // 1.2 and later - object dictionaryObject = QuietReflectionUtil.GetPropertyValue(alienComp, "ColorChannels"); - if (dictionaryObject == null) { - return; - } - System.Collections.IDictionary colorChannelsDictionary = dictionaryObject as System.Collections.IDictionary; - if (colorChannelsDictionary == null) { - return; - } - if (colorChannelsDictionary.Contains("skin")) { - object skinColorObject = colorChannelsDictionary["skin"]; - ReflectionUtil.SetFieldValue(skinColorObject, "first", value); - if (!alienRace.HasSecondaryColor) { - ReflectionUtil.SetFieldValue(skinColorObject, "second", value); - } - } - } - else { - bool removeOverride = false; - var melaninGeneDef = pawn.genes.GetMelaninGene(); - Gene activeSkinColorGene = null; - if (pawn?.genes?.GenesListForReading != null) { - activeSkinColorGene = pawn.genes.GenesListForReading.Where(g => g.Active && g.def.skinColorOverride.HasValue && g.overriddenByGene == null).FirstOrDefault(); - } - if (activeSkinColorGene == null && melaninGeneDef?.skinColorBase != null && melaninGeneDef.skinColorBase == value) { - removeOverride = true; - } - if (removeOverride) { - this.pawn.story.skinColorOverride = null; - } - else { - this.pawn.story.skinColorOverride = value; - } - MarkPortraitAsDirty(); - } - } - } - - public float MelaninLevel { - get { - return pawn.genes.GetMelaninGene().minMelanin; - } - set { - SkinColor = PawnSkinColors.GetSkinColor(value); - MarkPortraitAsDirty(); - } - } - - public HairDef HairDef { - get { - return pawn.story.hairDef; - } - set { - pawn.story.hairDef = value; - MarkPortraitAsDirty(); - } - } - - public int ChronologicalAge { - get { - return pawn.ageTracker.AgeChronologicalYears; - } - set { - long years = pawn.ageTracker.AgeChronologicalYears; - long diff = value - years; - pawn.ageTracker.BirthAbsTicks -= diff * 3600000L; - pawn.ClearCachedLifeStage(); - pawn.ClearCachedHealth(); - } - } - - public int BiologicalAge { - get { - return pawn.ageTracker.AgeBiologicalYears; - } - set { - bool wasNewborn = IsNewborn(); - bool wasJuvenile = IsJuvenile(); - bool wasAdult = IsAdult; - bool hadBothBackstories = !wasNewborn && !wasJuvenile; - - long years = pawn.ageTracker.AgeBiologicalYears; - long diff = value - years; - pawn.ageTracker.AgeBiologicalTicks += diff * 3600000L; - if (IsAdult && pawn.story.Adulthood == null) { - pawn.story.Adulthood = LastSelectedAdulthoodBackstory; - ResetBackstories(); - } - else if (!IsAdult && pawn.story.Adulthood != null) { - pawn.story.Adulthood = null; - ResetBackstories(); - } - pawn.ClearCachedLifeStage(); - pawn.ClearCachedHealth(); - MarkPortraitAsDirty(); - } - } - public long BiologicalAgeInTicks { - get { - return pawn.ageTracker.AgeBiologicalTicks; - } - } - - public long ChronologicalAgeInTicks { - get { - return pawn.ageTracker.AgeChronologicalTicks; - } - } - public int BiologicalAgeInYears { - get { - return pawn.ageTracker.AgeBiologicalYears; - } - } - - public int ChronologicalAgeInYears { - get { - return pawn.ageTracker.AgeChronologicalYears; - } - } - - public int ChronologicalAgeInDays { - get { - return (int)(pawn.ageTracker.AgeChronologicalTicks / AgeModifier.TicksPerDay); - } - } - public int BiologicalAgeInDays { - get { - return (int)(pawn.ageTracker.AgeBiologicalTicks / AgeModifier.TicksPerDay); - } - } - - public bool IsNewborn() { - DevelopmentalStage? d = Pawn.ageTracker?.CurLifeStage?.developmentalStage; - return d.HasValue ? d.Value.Newborn() : false; - } - public bool IsJuvenile() { - DevelopmentalStage? d = Pawn.ageTracker?.CurLifeStage?.developmentalStage; - return d.HasValue ? d.Value.Juvenile() : false; - } - //public bool IsAdult() { - // DevelopmentalStage? d = Pawn.ageTracker?.CurLifeStage?.developmentalStage; - // return d.HasValue ? d.Value.Adult() : false; - //} - - protected void ResetGender() { - List bodyTypes = PrepareCarefully.Instance.Providers.BodyTypes.GetBodyTypesForPawn(this); - if (pawn.gender == Gender.Female) { - if (HairDef.styleGender == StyleGender.Male) { - HairDef = DefDatabase.AllDefsListForReading.Find((HairDef def) => { - return def.styleGender != StyleGender.Male; - }); - } - if (BodyType == BodyTypeDefOf.Male) { - if (bodyTypes.Contains(BodyTypeDefOf.Female)) { - BodyType = BodyTypeDefOf.Female; - } - } - if (HeadType.gender == Gender.Male) { - HeadType = PrepareCarefully.Instance.Providers.HeadTypes.FindMatchingHeadTypeForOtherGenderOrDefault(HeadType, pawn.gender); - } - } - else { - if (HairDef.styleGender == StyleGender.Female) { - HairDef = DefDatabase.AllDefsListForReading.Find((HairDef def) => { - return def.styleGender != StyleGender.Female; - }); - } - if (BodyType == BodyTypeDefOf.Female) { - if (bodyTypes.Contains(BodyTypeDefOf.Male)) { - BodyType = BodyTypeDefOf.Male; - } - } - if (HeadType.gender == Gender.Female) { - HeadType = PrepareCarefully.Instance.Providers.HeadTypes.FindMatchingHeadTypeForOtherGenderOrDefault(HeadType, pawn.gender); - } - } - } - - public string ResetCachedIncapableOf() { - pawn.ClearCachedDisabledSkillRecords(); - List incapableList = new List(); - WorkTags combinedDisabledWorkTags = pawn.story.DisabledWorkTagsBackstoryAndTraits; - if (combinedDisabledWorkTags != WorkTags.None) { - IEnumerable list = Reflection.ReflectorCharacterCardUtility.WorkTagsFrom(combinedDisabledWorkTags); - foreach (var tag in list) { - incapableList.Add(WorkTypeDefsUtility.LabelTranslated(tag).CapitalizeFirst()); - } - if (incapableList.Count > 0) { - incapable = string.Join(", ", incapableList.ToArray()); - } - } - else { - incapable = null; - } - return incapable; - } - - public bool IsApparelConflict() { - return false; - } - - protected void CopyApparelToPawn(Pawn pawn) { - // Removes all apparel on the pawn and puts it in the thing cache for potential later re-use. - List apparel = pawn.apparel.WornApparel; - foreach (var a in apparel) { - a.holdingOwner = null; - thingCache.Put(a); - } - apparel.Clear(); - - // Set each piece of apparel on the underlying Pawn from the CustomPawn. - foreach (var layer in selectedApparel.Keys) { - if (layer.Apparel) { - AddApparelToPawn(pawn, layer); - } - } - } - - public void AddApparelToPawn(Pawn targetPawn, PawnLayer layer) { - if (acceptedApparel[layer] != null) { - Apparel a; - Color color; - bool madeFromStuff = acceptedApparel[layer].MadeFromStuff; - - if (madeFromStuff) { - a = (Apparel)thingCache.Get(selectedApparel[layer], selectedStuff[layer]); - color = colors[layer] * GetStuffColor(layer); - } - else { - a = (Apparel)thingCache.Get(selectedApparel[layer]); - color = colors[layer]; - } - - if (acceptedApparel[layer].HasComp(typeof(CompColorable))) { - CompColorable colorable = a.TryGetComp(); - if (colorable != null) { - Color originalColor = Color.white; - if (madeFromStuff) { - originalColor = GetStuffColor(layer); - } - if (originalColor != colors[layer]) { - colorable.SetColor(colors[layer]); - } - else { - colorable.Disable(); - } - } - } - else { - a.DrawColor = Color.white; - } - - // This post-process will set the quality and damage on the apparel based on the - // pawn kind definition, so after we call it, we need to reset the quality and damage. - PawnGenerator.PostProcessGeneratedGear(a, targetPawn); - a.SetQuality(QualityCategory.Normal); - a.HitPoints = a.MaxHitPoints; - if (ApparelUtility.HasPartsToWear(targetPawn, a.def)) { - targetPawn.apparel.Wear(a, false); - } - } - } - - public void AddInjury(Injury injury) { - AddInjuryDirect(injury); - ApplyInjuriesAndImplantsToPawn(); - InitializeInjuriesAndImplantsFromPawn(this.pawn); - } - - public void AddInjuryDirect(Injury injury) { - injuries.Add(injury); - bodyParts.Add(injury); - } - - public void UpdateImplants(List implants) { - List implantsToRemove = new List(); - foreach (var bodyPart in bodyParts) { - Implant asImplant = bodyPart as Implant; - implantsToRemove.Add(asImplant); - } - foreach (var implant in implantsToRemove) { - bodyParts.Remove(implant); - } - this.implants.Clear(); - foreach (var implant in implants) { - bodyParts.Add(implant); - this.implants.Add(implant); - } - ApplyInjuriesAndImplantsToPawn(); - InitializeInjuriesAndImplantsFromPawn(this.pawn); - } - - public void ApplyInjuriesAndImplantsToPawn() { - this.pawn.health.Reset(); - List injuriesToRemove = new List(); - foreach (var injury in injuries) { - try { - injury.AddToPawn(this, pawn); - } - catch (Exception e) { - Logger.Warning("Failed to add injury {" + injury.Option?.HediffDef?.defName + "} to part {" + injury.BodyPartRecord?.def?.defName + "}", e); - injuriesToRemove.Add(injury); - } - } - foreach (var injury in injuriesToRemove) { - injuries.Remove(injury); - } - List implantsToRemove = new List(); - foreach (var implant in implants) { - try { - implant.AddToPawn(this, pawn); - } - catch (Exception e) { - Logger.Warning("Failed to add implant {" + implant.label + "} to part {" + implant.BodyPartRecord?.def?.defName + "}", e); - implantsToRemove.Add(implant); - } - } - foreach (var implant in implantsToRemove) { - implants.Remove(implant); - } - ClearPawnCaches(); - MarkPortraitAsDirty(); - } - - public void RemoveCustomBodyParts(CustomBodyPart part) { - Implant implant = part as Implant; - Injury injury = part as Injury; - if (implant != null) { - implants.Remove(implant); - } - if (injury != null) { - injuries.Remove(injury); - } - bodyParts.Remove(part); - ApplyInjuriesAndImplantsToPawn(); - } - - public void RemoveCustomBodyParts(BodyPartRecord part) { - bodyParts.RemoveAll((CustomBodyPart p) => { - return part == p.BodyPartRecord; - }); - implants.RemoveAll((Implant i) => { - return part == i.BodyPartRecord; - }); - ApplyInjuriesAndImplantsToPawn(); - } - - public void AddImplant(Implant implant) { - if (AddImplantDirect(implant)) { - ApplyInjuriesAndImplantsToPawn(); - InitializeInjuriesAndImplantsFromPawn(this.pawn); - } - } - public bool AddImplantDirect(Implant implant) { - if (implant != null && implant.BodyPartRecord != null) { - implants.Add(implant); - bodyParts.Add(implant); - return true; - } - else { - Logger.Warning("Discarding implant because of missing body part: " + implant.BodyPartRecord.def.defName); - return false; - } - } - - public void RemoveImplant(Implant implant) { - implants.Remove(implant); - bodyParts.Remove(implant); - ApplyInjuriesAndImplantsToPawn(); - } - public void RemoveImplants(IEnumerable implants) { - foreach (var implant in implants) { - this.implants.Remove(implant); - this.bodyParts.Remove(implant); - } - ApplyInjuriesAndImplantsToPawn(); - } - - public bool AtLeastOneImplantedPart(IEnumerable records) { - foreach (var record in records) { - if (IsImplantedPart(record)) { - return true; - } - } - return false; - } - public bool HasSameImplant(Implant implant) { - return implants.FirstOrDefault((Implant i) => { - return i.BodyPartRecord == implant.BodyPartRecord && i.Recipe == implant.Recipe; - }) != null; - } - public bool HasSameImplant(BodyPartRecord part, RecipeDef def) { - return implants.FirstOrDefault((Implant i) => { - return i.BodyPartRecord == part && i.Recipe == def; - }) != null; - } - public bool IsImplantedPart(BodyPartRecord record) { - return FindImplant(record) != null; - } - public bool HasAtLeastOnePartBeenReplaced(IEnumerable records) { - foreach (var record in records) { - if (HasPartBeenReplaced(record)) { - return true; - } - } - return false; - } - public bool HasPartBeenReplaced(BodyPartRecord record) { - Implant implant = FindImplant(record); - if (implant == null) { - return false; - } - return implant.ReplacesPart; - } - public Implant FindImplant(BodyPartRecord record) { - if (implants.Count == 0) { - return null; - } - return implants.FirstOrDefault((Implant i) => { - return i.BodyPartRecord == record; - }); - } - - public void ClearPawnCaches() { - pawn.ClearCaches(); - } - } -} diff --git a/Source/CustomRelationship.cs b/Source/CustomRelationship.cs deleted file mode 100644 index 7f304ba..0000000 --- a/Source/CustomRelationship.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Collections.Generic; -using RimWorld; -using Verse; - -namespace EdB.PrepareCarefully { - public class CustomRelationship { - public CustomPawn source; - public CustomPawn target; - public PawnRelationDef def; - public PawnRelationDef inverseDef; - - public CustomRelationship() { - } - - public CustomRelationship(PawnRelationDef def, CustomPawn source, CustomPawn target) { - this.def = def; - this.inverseDef = null; - this.source = source; - this.target = target; - } - - public CustomRelationship(PawnRelationDef def, PawnRelationDef inverseDef, CustomPawn source, CustomPawn target) { - this.def = def; - this.inverseDef = inverseDef; - this.source = source; - this.target = target; - } - - public override string ToString() { - return (source != null ? source.Name.ToStringShort : "null") - + (target != null ? target.Name.ToStringShort : "null") - + (def != null ? def.defName : "null"); - } - } -} diff --git a/Source/Customizations.cs b/Source/Customizations.cs new file mode 100644 index 0000000..084167d --- /dev/null +++ b/Source/Customizations.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using RimWorld; + +namespace EdB.PrepareCarefully { + public class Customizations { + + public List ColonyPawns { get; set; } = new List(); + public List WorldPawns { get; set; } = new List(); + public List TemporaryPawns { get; set; } = new List(); + public IEnumerable AllPawns { + get { + foreach (var p in ColonyPawns) { + yield return p; + } + foreach (var p in WorldPawns) { + yield return p; + } + } + } + public List Equipment { get; set; } = new List(); + + public RelationshipList Relationships { get; set; } = new RelationshipList(); + + public List ParentChildGroups { get; set; } = new List(); + + } +} diff --git a/Source/CustomizationsPawn.cs b/Source/CustomizationsPawn.cs new file mode 100644 index 0000000..347d522 --- /dev/null +++ b/Source/CustomizationsPawn.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using RimWorld; +using UnityEngine; +using Verse; + +namespace EdB.PrepareCarefully { + + public class CustomizationsSkill { + public SkillDef SkillDef { get; set; } + public int Level { get; set; } + public Passion Passion { get; set; } + public Passion OriginalPassion { get; set; } + public int OriginalLevel { get; set; } + } + + public class CustomizationsTrait { + public TraitDef TraitDef { get; set; } + public int Degree { get; set; } + } + + public class CustomizationTitle { + public Faction Faction { get; set; } + public RoyalTitleDef TitleDef { get; set; } + public int Honor { get; set; } + } + + public class CustomizationsApparel { + public ThingDef ThingDef { get; set; } + public ThingDef StuffDef { get; set; } + public QualityCategory? Quality { get; set; } + public Color? Color { get; set; } + public float? HitPoints { get; set; } + } + + public class CustomizedPossession { + public ThingDef ThingDef { get; set; } + public int Count { get; set;} + } + + public class CustomizedAbility { + public AbilityDef AbilityDef { get; set; } + } + + public class CustomizedGenes { + public List Endogenes { get; set; } + public List Xenogenes { get; set; } + } + + public class CustomizedGene { + public GeneDef GeneDef { get; set; } + } + + public class CustomizationsPawn { + + public PawnKindDef PawnKind { get; set; } + public XenotypeDef XenotypeDef { get; set; } + public CustomXenotype CustomXenotype { get; set; } + public AlienRace AlienRace { get; set; } + + public Gender Gender { get; set; } + + // Name + public string NameType = "Triple"; + public string FirstName { get; set; } + public string NickName { get; set; } + public string LastName { get; set; } + public string SingleName { get; set; } + + // Appearance + public HairDef Hair { get; set; } + public Color HairColor { get; set; } + public HeadTypeDef HeadType { get; set; } + public BodyTypeDef BodyType { get; set; } + public BeardDef Beard { get; set; } + public FurDef Fur { get; set; } + + public TattooDef FaceTattoo { get; set; } + public TattooDef BodyTattoo { get; set; } + + public Color SkinColor { get; set; } + public Color? SkinColorOverride { get; set; } + + // Backstory + public BackstoryDef ChildhoodBackstory { get; set; } + public BackstoryDef AdulthoodBackstory { get; set; } + public Color? FavoriteColor { get; set; } + + // Traits + + public List Traits { get; set; } = new List(); + + // Age + public long BiologicalAgeInTicks { get; set; } + public long ChronologicalAgeInTicks { get; set; } + + // Skills + public List Skills { get; set; } = new List(); + + // Genes + public CustomizedGenes Genes { get; set; } = null; + + // Apparel and Equipment + public List Apparel { get; set; } = new List(); + public List Possessions { get; set; } = new List(); + + // Abilities + public List Abilities { get; set; } = new List(); + + // Health + public List Injuries { get; set; } = new List(); + public List Implants { get; set; } = new List(); + + public List BodyParts = new List(); + + // Titles + public List Titles { get; set; } = new List(); + + public Ideo Ideo { get; set; } + public float? Certainty { get; set; } + } +} diff --git a/Source/CustomizedEquipment.cs b/Source/CustomizedEquipment.cs new file mode 100644 index 0000000..c2d2907 --- /dev/null +++ b/Source/CustomizedEquipment.cs @@ -0,0 +1,53 @@ +using RimWorld; +using System; +using System.Collections.Generic; +using UnityEngine; +using Verse; + +namespace EdB.PrepareCarefully { + public class CustomizedEquipment { + public EquipmentOption EquipmentOption { get; set; } + public ThingDef StuffDef { get; set; } + public QualityCategory? Quality { get; set; } + public EquipmentSpawnType? SpawnType { get; set; } + public int Count { get; set; } + public Gender? Gender { get; set; } + + public bool Animal { + get { + return EquipmentOption.RandomAnimal || (EquipmentOption.ThingDef?.race?.Animal ?? false); + } + } + + public CustomizedEquipment CreateCopy() { + return new CustomizedEquipment() { + EquipmentOption = this.EquipmentOption, + StuffDef = this.StuffDef, + Quality = this.Quality, + Count = this.Count, + SpawnType = this.SpawnType, + Gender = this.Gender, + }; + } + + public override bool Equals(object obj) { + return obj is CustomizedEquipment equipment && + EqualityComparer.Default.Equals(EquipmentOption, equipment.EquipmentOption) && + EqualityComparer.Default.Equals(StuffDef, equipment.StuffDef) && + Quality == equipment.Quality && + SpawnType == equipment.SpawnType && + Gender == equipment.Gender; + } + + public override int GetHashCode() { + var hashCode = -719122440; + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(EquipmentOption); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(StuffDef); + hashCode = hashCode * -1521134295 + Quality.GetHashCode(); + hashCode = hashCode * -1521134295 + SpawnType.GetHashCode(); + hashCode = hashCode * -1521134295 + Gender.GetHashCode(); + return hashCode; + } + } +} + diff --git a/Source/CustomBodyPart.cs b/Source/CustomizedHediff.cs similarity index 75% rename from Source/CustomBodyPart.cs rename to Source/CustomizedHediff.cs index 9901b63..6720f07 100644 --- a/Source/CustomBodyPart.cs +++ b/Source/CustomizedHediff.cs @@ -5,11 +5,8 @@ using Verse; namespace EdB.PrepareCarefully { - public abstract class CustomBodyPart { - public abstract BodyPartRecord BodyPartRecord { - get; - set; - } + public abstract class CustomizedHediff { + public abstract BodyPartRecord BodyPartRecord { get; set; } public virtual string PartName { get { @@ -25,8 +22,6 @@ abstract public Color LabelColor { get; } - abstract public void AddToPawn(CustomPawn customPawn, Pawn pawn); - public virtual bool HasTooltip { get { return false; diff --git a/Source/CustomizedPawn.cs b/Source/CustomizedPawn.cs new file mode 100644 index 0000000..ef501b7 --- /dev/null +++ b/Source/CustomizedPawn.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using RimWorld; +using Verse; + +namespace EdB.PrepareCarefully { + public class CustomizedPawn { + public string Id { get; set; } + public CustomizedPawnType Type { get; set; } + public Pawn Pawn { get; set; } + public CustomizationsPawn Customizations { get; set; } + public TemporaryPawn TemporaryPawn { get; set; } + } +} diff --git a/Source/CustomPawnType.cs b/Source/CustomizedPawnType.cs similarity index 70% rename from Source/CustomPawnType.cs rename to Source/CustomizedPawnType.cs index 3f3afe4..a8f84d8 100644 --- a/Source/CustomPawnType.cs +++ b/Source/CustomizedPawnType.cs @@ -1,11 +1,11 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace EdB.PrepareCarefully { - public enum CustomPawnType { - Colonist, + public enum CustomizedPawnType { + Colony, World, Hidden, Temporary diff --git a/Source/CustomizedRelationship.cs b/Source/CustomizedRelationship.cs new file mode 100644 index 0000000..5dd67df --- /dev/null +++ b/Source/CustomizedRelationship.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using RimWorld; +using Verse; + +namespace EdB.PrepareCarefully { + public class CustomizedRelationship { + public CustomizedPawn Source { get; set; } + public CustomizedPawn Target { get; set; } + public PawnRelationDef Def { get; set; } + public PawnRelationDef InverseDef { get; set; } + + public CustomizedRelationship() { + } + + public CustomizedRelationship(PawnRelationDef def, CustomizedPawn source, CustomizedPawn target) { + this.Def = def; + this.InverseDef = null; + this.Source = source; + this.Target = target; + } + + public CustomizedRelationship(PawnRelationDef def, PawnRelationDef inverseDef, CustomizedPawn source, CustomizedPawn target) { + this.Def = def; + this.InverseDef = inverseDef; + this.Source = source; + this.Target = target; + } + + public override string ToString() { + return Source?.Pawn?.Name?.ToStringShort + " + " + + Target?.Pawn?.Name?.ToStringShort + " = " + + Def?.defName; + } + + public override bool Equals(object obj) { + return obj is CustomizedRelationship relationship && + ReferenceEquals(Source, relationship.Source) && + ReferenceEquals(Target, relationship.Target) && + ReferenceEquals(Def, relationship.Def); + } + + public override int GetHashCode() { + var hashCode = 648056908; + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Source); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Target); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Def); + return hashCode; + } + } +} diff --git a/Source/DialogAbilities.cs b/Source/DialogAbilities.cs index a3831cc..3bbaf43 100644 --- a/Source/DialogAbilities.cs +++ b/Source/DialogAbilities.cs @@ -39,10 +39,10 @@ public class Option { protected WidgetTable