Skip to content

Commit

Permalink
Merge pull request #149 from edbmods/develop
Browse files Browse the repository at this point in the history
Merge to master for v0.17.1.4 release candidate 3
  • Loading branch information
edbmods authored Jun 2, 2017
2 parents b73111f + a96a8be commit 8c9fc51
Show file tree
Hide file tree
Showing 12 changed files with 210 additions and 72 deletions.
3 changes: 2 additions & 1 deletion EdBPrepareCarefully.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
<Compile Include="Source\ExtensionsThing.cs" />
<Compile Include="Source\GenStep_CustomScatterThings.cs" />
<Compile Include="Source\CustomHeadType.cs" />
<Compile Include="Source\GenStep_RemovePrepareCarefullyScenario.cs" />
<Compile Include="Source\IRelationshipWorker.cs" />
<Compile Include="Source\OptionsApparel.cs" />
<Compile Include="Source\OptionsHealth.cs" />
Expand Down Expand Up @@ -101,7 +102,6 @@
<Compile Include="Source\RelationshipGroup.cs" />
<Compile Include="Source\ScenPart_CustomAnimal.cs" />
<Compile Include="Source\ScenPart_CustomScatterThingsNearPlayerStart.cs" />
<Compile Include="Source\ScenPart_PrepareCarefullyPawns.cs" />
<Compile Include="Source\ScrollViewHorizontal.cs" />
<Compile Include="Source\SelectedAnimal.cs" />
<Compile Include="Source\EquipmentSelection.cs" />
Expand All @@ -124,6 +124,7 @@
<Compile Include="Source\CustomBodyPart.cs" />
<Compile Include="Source\CarefullyPawnRelationDef.cs" />
<Compile Include="Source\UtilityCopy.cs" />
<Compile Include="Source\ExtensionsObject.cs" />
<Compile Include="Source\Version3\PresetLoaderVersion3.cs" />
<Compile Include="Source\Version3\SaveRecordParentChildGroupV3.cs" />
<Compile Include="Source\Version3\SaveRecordPawnV3.cs" />
Expand Down
5 changes: 5 additions & 0 deletions Resources/CHANGELOG.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@
- Bug fix: The randomize options now take into account a character's faction.
- Bug fix: Updated preset file format so that sibling-only parent/child
groups are no longer lost when saving a preset.
- Bug fix: Removed the save-game dependency created by customized scenario
parts.
Bug fix: Fixed the "Review Scenario" button in the gameplay menu.
Bug fix: Sibling and some other relationships are no longer lost when
saving a game.

_____________________________________________________________________________

Expand Down
15 changes: 8 additions & 7 deletions Resources/Defs/MapGeneratorDefs/MapGenerators.xml
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<!-- This file is no longer used, but since the workshop doesn't seem to consistently
delete files on mod updates, we're leaving it here to avoid mod corruption errors.
We leave a placeholder pawn relation def since that should leave no side-effects.
-->
<EdB.PrepareCarefully.CarefullyPawnRelationDef>
<defName>EdBPrepareCarefullyDummyDefinitionMapGenerators</defName>
</EdB.PrepareCarefully.CarefullyPawnRelationDef>

<GenStepDef>
<defName>RemovePrepareCarefullyScenParts</defName>
<linkWithMapGenerator>BasicMap</linkWithMapGenerator>
<order>2147483647</order>
<genStep Class="EdB.PrepareCarefully.GenStep_RemovePrepareCarefullyScenario"/>
</GenStepDef>

</Defs>
14 changes: 7 additions & 7 deletions Resources/Defs/ThingDefs/EdBPrepareCarefully.xml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8" ?>
<ThingDefs>
<!-- This file is no longer used, but since the workshop doesn't seem to consistently
delete files on mod updates, we're leaving it here to avoid mod corruption errors.
We leave a placeholder pawn relation def since that should leave no side-effects.
-->
<EdB.PrepareCarefully.CarefullyPawnRelationDef>
<defName>EdBPrepareCarefullyDummyDefinition</defName>
</EdB.PrepareCarefully.CarefullyPawnRelationDef>
<!-- This file is no longer used, but since the workshop doesn't seem to consistently
delete files on mod updates, we're leaving it here to avoid mod corruption errors.
We leave a placeholder pawn relation def since that should leave no side-effects.
-->
<EdB.PrepareCarefully.CarefullyPawnRelationDef>
<defName>EdBPrepareCarefullyDummyDefinition</defName>
</EdB.PrepareCarefully.CarefullyPawnRelationDef>
</ThingDefs>
4 changes: 0 additions & 4 deletions Resources/Languages/English/Keyed/EdBPrepareCarefully.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
<!-- NEW: EdB.PC.Common.Default -->
<!-- NEW: EdB.PC.Common.NoOptionAvailable -->
<!-- NEW: EdB.PC.Dialog.Preset.Error.NoCharacter -->
<!-- NEW: EdB.PC.Page.StartingPawnScenarioPartMessage.Plural -->
<!-- NEW: EdB.PC.Page.StartingPawnScenarioPartMessage.Singular -->

<LanguageData>
<EdB.PC.AddParentChild.Header.AddParent>Add a Parent</EdB.PC.AddParentChild.Header.AddParent>
Expand Down Expand Up @@ -146,8 +144,6 @@
Disable point limits to prepare your starting colonists with no limitations.
</EdB.PC.Page.Points.UsePoints.Tip>
<EdB.PC.Page.Title>Prepare Carefully</EdB.PC.Page.Title>
<EdB.PC.Page.StartingPawnScenarioPartMessage.Plural>Start with {0} carefully prepared characters.</EdB.PC.Page.StartingPawnScenarioPartMessage.Plural>
<EdB.PC.Page.StartingPawnScenarioPartMessage.Singular>Start with 1 carefully prepared character.</EdB.PC.Page.StartingPawnScenarioPartMessage.Singular>

<EdB.PC.Panel.Age.Biological>Biological Age</EdB.PC.Panel.Age.Biological>
<EdB.PC.Panel.Age.Chronological>Chronological Age</EdB.PC.Panel.Age.Chronological>
Expand Down
105 changes: 81 additions & 24 deletions Source/Controller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,37 +113,58 @@ public void PrepareGame() {
// Replace the pawns.
Find.GameInitData.startingPawns = PrepareCarefully.Instance.Colonists;

// Make a copy of the scenario so that we can modify it.
Current.Game.Scenario = UtilityCopy.CopyExposable(Find.Scenario);
Scenario scenario = Current.Game.Scenario;
// This needs some explaining. We need custom scenario parts to handle animal spawning
// and scattered things. However, we don't want the scenario that gets saved with a game
// to include any Prepare Carefully-specific parts (because the save would become bound to
// the mod). We work around this by creating two copies of the actual scenario. The first
// copy includes the customized scenario parts needed to do the spawning. The second contains
// vanilla versions of those parts that can safely be saved without forcing a dependency on
// the mod. The GenStep_RemovePrepareCarefullyScenario class is responsible for switching out
// the actual scenario with the vanilla-friendly version at the end of the map generation process.
Scenario actualScenario = UtilityCopy.CopyExposable(Find.Scenario);
Scenario vanillaFriendlyScenario = UtilityCopy.CopyExposable(Find.Scenario);
Current.Game.Scenario = actualScenario;
PrepareCarefully.OriginalScenario = vanillaFriendlyScenario;

// Remove equipment scenario parts.
ReplaceScenarioParts();
ReplaceScenarioParts(actualScenario, vanillaFriendlyScenario);
}

protected void ReplaceScenarioParts() {
protected void ReplaceScenarioParts(Scenario actualScenario, Scenario vanillaFriendlyScenario) {
// Create a lookup of all of the scenario types that we want to replace.
HashSet<string> equipmentScenarioParts = new HashSet<string>() {
typeof(RimWorld.ScenPart_ConfigPage_ConfigureStartingPawns).FullName,
HashSet<string> scenarioPartsToReplace = new HashSet<string>() {
typeof(RimWorld.ScenPart_StartingThing_Defined).FullName,
typeof(RimWorld.ScenPart_ScatterThingsNearPlayerStart).FullName,
typeof(RimWorld.ScenPart_StartingAnimal).FullName
};
List<ScenPart> newParts = new List<ScenPart>();

// Remove existing scenario parts that match the ones in the lookup.
// Create lists to hold the new scenario parts.
List<ScenPart> actualScenarioParts = new List<ScenPart>();
List<ScenPart> vanillaFriendlyScenarioParts = new List<ScenPart>();

// Get the list of parts from the original scenario. The actual scenario and the vanilla-friendly
// scenario will both be copies of the original scenario and equivalent at this point, so we only
// need to look at the parts in one of them.
FieldInfo partsField = typeof(Scenario).GetField("parts", BindingFlags.NonPublic | BindingFlags.Instance);
List<ScenPart> originalParts = (List<ScenPart>)partsField.GetValue(Find.Scenario);
List<ScenPart> originalParts = (List<ScenPart>)partsField.GetValue(actualScenario);

// Replace the pawn count in the configure pawns scenario part to reflect the number of
// pawns that were selected in Prepare Carefully.
foreach (var part in originalParts) {
if (!equipmentScenarioParts.Contains(part.GetType().FullName)) {
newParts.Add(part);
ScenPart_ConfigPage_ConfigureStartingPawns configurePawnPart = part as ScenPart_ConfigPage_ConfigureStartingPawns;
if (configurePawnPart == null) {
continue;
}
configurePawnPart.pawnCount = Find.GameInitData.startingPawns.Count;
}

// Add a replacement for the starting character config. We do this so that if the player looks at
// the scenario summary, they will see a colonist count that matches the number of characters that
// the set up in Prepare Carefully.
newParts.Add(new ScenPart_PrepareCarefullyStartingPawns(Find.GameInitData.startingPawns.Count));
// Fill in each part list with only the scenario parts that we're not going to replace.
foreach (var part in originalParts) {
if (!scenarioPartsToReplace.Contains(part.GetType().FullName)) {
actualScenarioParts.Add(part);
vanillaFriendlyScenarioParts.Add(part);
}
}

// 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,
Expand All @@ -170,6 +191,13 @@ protected void ReplaceScenarioParts() {
part.StuffDef = e.StuffDef;
part.Count = e.Count;
scatterParts.Add(part);

ScenPart_ScatterThingsNearPlayerStart vanillaPart = new ScenPart_ScatterThingsNearPlayerStart();
vanillaPart.def = ScenPartDefOf.ScatterThingsNearPlayerStart;
vanillaPart.SetPrivateField("thingDef", e.ThingDef);
vanillaPart.SetPrivateField("stuff", e.StuffDef);
vanillaPart.SetPrivateField("count", e.Count);
vanillaFriendlyScenarioParts.Add(vanillaPart);
}
}

Expand All @@ -192,7 +220,6 @@ protected void ReplaceScenarioParts() {
if (e.record.animal) {
continue;
}

if (PlayerStartsWith(e)) {
int scatterCount = 0;
int nearCount = e.Count;
Expand All @@ -216,7 +243,8 @@ protected void ReplaceScenarioParts() {
thingDefField.SetValue(part, e.ThingDef);
stuffField.SetValue(part, e.StuffDef);
countField.SetValue(part, nearCount);
newParts.Add(part);
actualScenarioParts.Add(part);
vanillaFriendlyScenarioParts.Add(part);
}
if (scatterCount > 0) {
scatterCount += Mathf.CeilToInt((float)scatterCount / (float)e.ThingDef.stackLimit);
Expand All @@ -225,26 +253,53 @@ protected void ReplaceScenarioParts() {
part.StuffDef = e.StuffDef;
part.Count = scatterCount;
scatterParts.Add(part);

ScenPart_ScatterThingsNearPlayerStart vanillaPart = new ScenPart_ScatterThingsNearPlayerStart();
vanillaPart.def = ScenPartDefOf.ScatterThingsNearPlayerStart;
vanillaPart.SetPrivateField("thingDef", e.ThingDef);
vanillaPart.SetPrivateField("stuff", e.StuffDef);
vanillaPart.SetPrivateField("count", scatterCount);
vanillaFriendlyScenarioParts.Add(vanillaPart);
}
}
}

// 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<PawnKindDef, int> animalKindCounts = new Dictionary<PawnKindDef, int>();
foreach (var e in PrepareCarefully.Instance.Equipment) {
if (e.record.animal) {
PawnKindDef animalKindDef = (from td in DefDatabase<PawnKindDef>.AllDefs where td.race == e.ThingDef select td).FirstOrDefault();
ScenPart_CustomAnimal part = new ScenPart_CustomAnimal();
part.Count = e.count;
part.Gender = e.Gender;
part.KindDef = (from td in DefDatabase<PawnKindDef>.AllDefs where td.race == e.ThingDef select td).FirstOrDefault();
newParts.Add(part);
part.KindDef = animalKindDef;
actualScenarioParts.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();
vanillaPart.def = ScenPartDefOf.StartingAnimal;
vanillaPart.SetPrivateField("animalKind", animalKindDef);
vanillaPart.SetPrivateField("count", animalKindCounts[animalKindDef]);
vanillaFriendlyScenarioParts.Add(vanillaPart);
}

// We figure out how dense the spawn area will be after spawning all of the scattered things.
// We'll target a maximum density and increase the spawn radius if we're over that density.
stackCount += scatterStackCount;
float originalRadius = 10f;
float originalRadius = 12f;
radius = originalRadius;
maxDensity = 0.35f;
bool evaluate = true;
Expand All @@ -262,10 +317,12 @@ protected void ReplaceScenarioParts() {
// For each scatter part, we set our custom radius before adding the part to the scenario.
foreach (var part in scatterParts) {
part.Radius = addedRadius;
newParts.Add(part);
actualScenarioParts.Add(part);
}

partsField.SetValue(Find.Scenario, newParts);

// Set the new part lists on the two scenarios.
actualScenario.SetPrivateField("parts", actualScenarioParts);
vanillaFriendlyScenario.SetPrivateField("parts", vanillaFriendlyScenarioParts);
}

protected float GetSpawnAreaDensity(float radius, float stackCount) {
Expand Down
29 changes: 29 additions & 0 deletions Source/ExtensionsObject.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using RimWorld;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using Verse;
using Verse.Sound;

namespace EdB.PrepareCarefully {
public static class ExtensionsObject {
public static void SetPrivateField(this object target, string name, object value) {
FieldInfo info = target.GetType().GetField(name, BindingFlags.NonPublic | BindingFlags.Instance);
if (info != null) {
info.SetValue(target, value);
}
}
public static T GetPrivateField<T>(this object target, string name) {
FieldInfo info = target.GetType().GetField(name, BindingFlags.NonPublic | BindingFlags.Instance);
if (info != null) {
return (T)info.GetValue(target);
}
else {
return default(T);
}
}
}
}
24 changes: 24 additions & 0 deletions Source/GenStep_RemovePrepareCarefullyScenario.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using RimWorld;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using Verse;
using Verse.Sound;

namespace EdB.PrepareCarefully {
// Removes the customized scenario (with PrepareCarefully-specific scenario parts) and replaces
// it with a vanilla-friendly version that was prepared earlier. This is a workaround to avoid
// creating a dependency between a saved game and the mod. See Controller.PrepareGame() for
// more details.
// TODO: Re-evaluate to see if it would be better to use method routing instead of a map generator step.
public class GenStep_RemovePrepareCarefullyScenario : GenStep {
public override void Generate(Map map) {
if (PrepareCarefully.OriginalScenario != null) {
Current.Game.Scenario = PrepareCarefully.OriginalScenario;
}
}
}
}
5 changes: 5 additions & 0 deletions Source/PrepareCarefully.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ public State State {
}
}

public static Scenario OriginalScenario {
get; set;
}

public Providers Providers {
get; set;
}
Expand Down Expand Up @@ -99,6 +103,7 @@ public void NextPage() {
}

public void Clear() {
OriginalScenario = null;
this.Active = false;
this.Providers = new Providers();
this.equipmentDatabase = new EquipmentDatabase();
Expand Down
Loading

0 comments on commit 8c9fc51

Please sign in to comment.