public override void Generate(Map map) { if (!PrepareCarefully.Instance.Active) { Find.Scenario.GenerateIntoMap(map); return; } else { ReplaceColonists(); ScenPart_PlayerPawnsArriveMethod arriveMethodPart = null; foreach (ScenPart current in Find.Scenario.AllParts) { ScenPart_StartingThing_Defined startingThings = current as ScenPart_StartingThing_Defined; ScenPart_ScatterThingsNearPlayerStart thingsNearStart = current as ScenPart_ScatterThingsNearPlayerStart; ScenPart_StartingAnimal animal = current as ScenPart_StartingAnimal; ScenPart_PlayerPawnsArriveMethod arriveMethod = current as ScenPart_PlayerPawnsArriveMethod; if (arriveMethod != null) { arriveMethodPart = arriveMethod; } if (startingThings == null && thingsNearStart == null && animal == null && arriveMethod == null) { current.GenerateIntoMap(map); } } SpawnColonistsWithEquipment(map, arriveMethodPart); ApplyColonistHealthCustomizations(); PrepForMapGen(); SpawnStartingResources(map); } }
private static EquipmentDatabaseEntry RandomPet(ScenPart_StartingAnimal startingAnimal) { FieldInfo animalKindField = typeof(ScenPart_StartingAnimal).GetField("animalKind", BindingFlags.Instance | BindingFlags.NonPublic); MethodInfo randomPetsMethod = typeof(ScenPart_StartingAnimal).GetMethod("RandomPets", BindingFlags.Instance | BindingFlags.NonPublic); PawnKindDef animalKindDef = (PawnKindDef)animalKindField.GetValue(startingAnimal); if (animalKindDef == null) { IEnumerable <PawnKindDef> animalKindDefs = (IEnumerable <PawnKindDef>)randomPetsMethod.Invoke(startingAnimal, null); animalKindDef = animalKindDefs.RandomElementByWeight((PawnKindDef td) => td.RaceProps.petness); } List <EquipmentDatabaseEntry> entries = PrepareCarefully.Instance.EquipmentEntries.Animals.FindAll((EquipmentDatabaseEntry e) => { return(e.def == animalKindDef.race); }); if (entries.Count > 0) { EquipmentDatabaseEntry entry = entries.RandomElement(); return(entry); } else { return(null); } }
private static PawnKindDef RandomPet(ScenPart_StartingAnimal startingAnimal) { FieldInfo animalKindField = typeof(ScenPart_StartingAnimal).GetField("animalKind", BindingFlags.Instance | BindingFlags.NonPublic); PawnKindDef animalKindDef = (PawnKindDef)animalKindField.GetValue(startingAnimal); if (animalKindDef == null) { IEnumerable <PawnKindDef> animalKindDefs = Reflection.ScenPart_StartingAnimal.RandomPets(startingAnimal); animalKindDef = animalKindDefs.RandomElementByWeight((PawnKindDef td) => td.RaceProps.petness); } return(animalKindDef); }
public override void Generate() { if (!PrepareCarefully.Instance.Active) { Find.Scenario.GenerateIntoMap(); return; } else { ReplaceColonists(); // TODO: Alpha 14 // Do all of the scenario steps except the ones that place equipment and resources. // Skip those an figure out how to add customized equipment. Need to separate equipment // placed with colonists ("equipment") and equipment scattered near colonists ("resources").\ ScenPart_PlayerPawnsArriveMethod arriveMethodPart = null; foreach (ScenPart current in Find.Scenario.AllParts) { ScenPart_StartingThing_Defined startingThings = current as ScenPart_StartingThing_Defined; ScenPart_ScatterThingsNearPlayerStart thingsNearStart = current as ScenPart_ScatterThingsNearPlayerStart; ScenPart_StartingAnimal animal = current as ScenPart_StartingAnimal; ScenPart_PlayerPawnsArriveMethod arriveMethod = current as ScenPart_PlayerPawnsArriveMethod; if (arriveMethod != null) { arriveMethodPart = arriveMethod; } if (startingThings == null && thingsNearStart == null && animal == null && arriveMethod == null) { current.GenerateIntoMap(); } } SpawnColonistsWithEquipment(arriveMethodPart); ApplyColonistHealthCustomizations(); PrepForMapGen(); SpawnStartingResources(); } }
protected void ReplaceScenarioParts(Scenario actualScenario, Scenario vanillaFriendlyScenario) { // Create a lookup of all of the scenario types that we want to replace. HashSet <string> scenarioPartsToReplace = new HashSet <string>() { typeof(RimWorld.ScenPart_StartingThing_Defined).FullName, typeof(RimWorld.ScenPart_ScatterThingsNearPlayerStart).FullName, typeof(RimWorld.ScenPart_StartingAnimal).FullName }; // 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(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) { ScenPart_ConfigPage_ConfigureStartingPawns configurePawnPart = part as ScenPart_ConfigPage_ConfigureStartingPawns; if (configurePawnPart == null) { continue; } configurePawnPart.pawnCount = Find.GameInitData.startingPawnCount; } // 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, // 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 <ScenPart_CustomScatterThingsNearPlayerStart> scatterParts = new List <ScenPart_CustomScatterThingsNearPlayerStart>(); 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_CustomScatterThingsNearPlayerStart part = new ScenPart_CustomScatterThingsNearPlayerStart(); part.ThingDef = e.ThingDef; 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); } } // Get the non-public fields that we'll need to set on the new starting thing scenario parts // that we're going to add. FieldInfo thingDefField = typeof(ScenPart_StartingThing_Defined).GetField("thingDef", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo stuffField = typeof(ScenPart_StartingThing_Defined).GetField("stuff", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo countField = typeof(ScenPart_StartingThing_Defined).GetField("count", BindingFlags.Instance | BindingFlags.NonPublic); // 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; thingDefField.SetValue(part, e.ThingDef); stuffField.SetValue(part, e.StuffDef); countField.SetValue(part, nearCount); actualScenarioParts.Add(part); vanillaFriendlyScenarioParts.Add(part); } if (scatterCount > 0) { scatterCount += Mathf.CeilToInt((float)scatterCount / (float)e.ThingDef.stackLimit); ScenPart_CustomScatterThingsNearPlayerStart part = new ScenPart_CustomScatterThingsNearPlayerStart(); part.ThingDef = e.ThingDef; 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 = 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 = 12f; radius = originalRadius; maxDensity = 0.35f; bool evaluate = true; while (evaluate) { float density = GetSpawnAreaDensity(radius, stackCount); if (density > maxDensity) { radius += 1f; } else { evaluate = false; } } int addedRadius = (int)(radius - originalRadius); // For each scatter part, we set our custom radius before adding the part to the scenario. foreach (var part in scatterParts) { part.Radius = addedRadius; actualScenarioParts.Add(part); } // Set the new part lists on the two scenarios. actualScenario.SetPrivateField("parts", actualScenarioParts); vanillaFriendlyScenario.SetPrivateField("parts", vanillaFriendlyScenarioParts); }
protected void InitializeDefaultEquipment() { // Go through all of the scenario steps that scatter resources near the player starting location and add // them to the resource/equipment list. foreach (ScenPart part in Verse.Find.Scenario.AllParts) { ScenPart_ScatterThingsNearPlayerStart nearPlayerStart = part as ScenPart_ScatterThingsNearPlayerStart; if (nearPlayerStart != null) { FieldInfo thingDefField = typeof(ScenPart_ScatterThingsNearPlayerStart).GetField("thingDef", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo stuffDefField = typeof(ScenPart_ScatterThingsNearPlayerStart).GetField("stuff", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo countField = typeof(ScenPart_ScatterThingsNearPlayerStart).GetField("count", BindingFlags.Instance | BindingFlags.NonPublic); ThingDef thingDef = (ThingDef)thingDefField.GetValue(nearPlayerStart); ThingDef stuffDef = (ThingDef)stuffDefField.GetValue(nearPlayerStart); int count = (int)countField.GetValue(nearPlayerStart); EquipmentKey key = new EquipmentKey(thingDef, stuffDef); EquipmentDatabaseEntry entry = equipmentDatabase[key]; if (entry == null) { entry = AddNonStandardScenarioEquipmentEntry(key); } if (entry != null) { AddEquipment(entry, count); } } // Go through all of the scenario steps that place starting equipment with the colonists and // add them to the resource/equipment list. ScenPart_StartingThing_Defined startingThing = part as ScenPart_StartingThing_Defined; if (startingThing != null) { FieldInfo thingDefField = typeof(ScenPart_StartingThing_Defined).GetField("thingDef", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo stuffDefField = typeof(ScenPart_StartingThing_Defined).GetField("stuff", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo countField = typeof(ScenPart_StartingThing_Defined).GetField("count", BindingFlags.Instance | BindingFlags.NonPublic); ThingDef thingDef = (ThingDef)thingDefField.GetValue(startingThing); ThingDef stuffDef = (ThingDef)stuffDefField.GetValue(startingThing); int count = (int)countField.GetValue(startingThing); EquipmentKey key = new EquipmentKey(thingDef, stuffDef); EquipmentDatabaseEntry entry = equipmentDatabase[key]; if (entry == null) { entry = AddNonStandardScenarioEquipmentEntry(key); } if (entry != null) { AddEquipment(entry, count); } } // Go through all of the scenario steps that spawn a pet and add the pet to the equipment/resource // list. ScenPart_StartingAnimal animal = part as ScenPart_StartingAnimal; if (animal != null) { FieldInfo animalCountField = typeof(ScenPart_StartingAnimal).GetField("count", BindingFlags.Instance | BindingFlags.NonPublic); int count = (int)animalCountField.GetValue(animal); for (int i = 0; i < count; i++) { AddEquipment(RandomPet(animal)); } } } }
protected void InitializeDefaultEquipment() { // Go through all of the scenario steps that scatter resources near the player starting location and add // them to the resource/equipment list. foreach (ScenPart part in Verse.Find.Scenario.AllParts) { ScenPart_ScatterThingsNearPlayerStart nearPlayerStart = part as ScenPart_ScatterThingsNearPlayerStart; if (nearPlayerStart != null) { FieldInfo thingDefField = typeof(ScenPart_ScatterThingsNearPlayerStart).GetField("thingDef", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo stuffDefField = typeof(ScenPart_ScatterThingsNearPlayerStart).GetField("stuff", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo countField = typeof(ScenPart_ScatterThingsNearPlayerStart).GetField("count", BindingFlags.Instance | BindingFlags.NonPublic); ThingDef thingDef = (ThingDef)thingDefField.GetValue(nearPlayerStart); ThingDef stuffDef = (ThingDef)stuffDefField.GetValue(nearPlayerStart); equipmentDatabase.PreloadDefinition(stuffDef); equipmentDatabase.PreloadDefinition(thingDef); int count = (int)countField.GetValue(nearPlayerStart); EquipmentKey key = new EquipmentKey(thingDef, stuffDef); EquipmentRecord record = equipmentDatabase.LookupEquipmentRecord(key); if (record == null) { Logger.Warning("Couldn't initialize all scenario equipment. Didn't find an equipment entry for " + thingDef.defName); record = AddNonStandardScenarioEquipmentEntry(key); } if (record != null) { AddEquipment(record, count); } } // Go through all of the scenario steps that place starting equipment with the colonists and // add them to the resource/equipment list. ScenPart_StartingThing_Defined startingThing = part as ScenPart_StartingThing_Defined; if (startingThing != null) { FieldInfo thingDefField = typeof(ScenPart_StartingThing_Defined).GetField("thingDef", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo stuffDefField = typeof(ScenPart_StartingThing_Defined).GetField("stuff", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo countField = typeof(ScenPart_StartingThing_Defined).GetField("count", BindingFlags.Instance | BindingFlags.NonPublic); ThingDef thingDef = (ThingDef)thingDefField.GetValue(startingThing); ThingDef stuffDef = (ThingDef)stuffDefField.GetValue(startingThing); equipmentDatabase.PreloadDefinition(stuffDef); equipmentDatabase.PreloadDefinition(thingDef); int count = (int)countField.GetValue(startingThing); EquipmentKey key = new EquipmentKey(thingDef, stuffDef); EquipmentRecord entry = equipmentDatabase.LookupEquipmentRecord(key); if (entry == null) { Logger.Warning("Couldn't initialize all scenario equipment. Didn't find an equipment entry for " + thingDef.defName); entry = AddNonStandardScenarioEquipmentEntry(key); } if (entry != null) { AddEquipment(entry, count); } } // Go through all of the scenario steps that spawn a pet and add the pet to the equipment/resource // list. ScenPart_StartingAnimal animal = part as ScenPart_StartingAnimal; if (animal != null) { FieldInfo animalCountField = typeof(ScenPart_StartingAnimal).GetField("count", BindingFlags.Instance | BindingFlags.NonPublic); int count = (int)animalCountField.GetValue(animal); for (int i = 0; i < count; i++) { PawnKindDef animalKindDef = RandomPet(animal); equipmentDatabase.PreloadDefinition(animalKindDef.race); List <EquipmentRecord> entries = PrepareCarefully.Instance.EquipmentDatabase.Animals.FindAll((EquipmentRecord e) => { return(e.def == animalKindDef.race); }); EquipmentRecord entry = null; if (entries.Count > 0) { entry = entries.RandomElement(); } if (entry != null) { AddEquipment(entry); } else { Logger.Warning("Failed to add the expected scenario animal to list of selected equipment"); } } } } }
// Copy of ScenPart_PlayerPawnsArriveMethod.GenerateIntoMap(), but with changes to spawn custom // equipment. public void SpawnColonistsWithEquipment(Map map, ScenPart_PlayerPawnsArriveMethod arriveMethodPart) { List <List <Thing> > list = new List <List <Thing> >(); foreach (Pawn current in Find.GameInitData.startingPawns) { list.Add(new List <Thing> { current }); } List <Thing> list2 = new List <Thing>(); foreach (ScenPart current2 in Find.Scenario.AllParts) { ScenPart_StartingThing_Defined startingThings = current2 as ScenPart_StartingThing_Defined; ScenPart_StartingAnimal animal = current2 as ScenPart_StartingAnimal; if (startingThings == null && animal == null) { list2.AddRange(current2.PlayerStartingThings()); } } int num = 0; foreach (Thing current3 in list2) { if (current3.def.CanHaveFaction) { current3.SetFactionDirect(Faction.OfPlayer); } list[num].Add(current3); num++; if (num >= list.Count) { num = 0; } } // Spawn custom equipment List <Thing> weapons = new List <Thing>(); List <Thing> food = new List <Thing>(); List <Thing> apparel = new List <Thing>(); List <Thing> animals = new List <Thing>(); List <Thing> other = new List <Thing>(); int maxStack = 50; foreach (var e in PrepareCarefully.Instance.Equipment) { EquipmentDatabaseEntry entry = PrepareCarefully.Instance.EquipmentEntries[e.EquipmentKey]; if (entry == null) { string thing = e.def != null ? e.def.defName : "null"; string stuff = e.stuffDef != null ? e.stuffDef.defName : "null"; Log.Warning(string.Format("Unrecognized resource/equipment. This may be caused by an invalid thing/stuff combination. (thing = {0}, stuff={1})", thing, stuff)); continue; } if (entry.gear) { int count = e.Count; int idealStackCount = count / list.Count; while (count > 0) { int stackCount = idealStackCount; if (stackCount < 1) { stackCount = 1; } if (stackCount > entry.def.stackLimit) { stackCount = entry.def.stackLimit; } if (stackCount > maxStack) { stackCount = maxStack; } if (stackCount > count) { stackCount = count; } Thing thing = null; if (entry.def.MadeFromStuff && entry.stuffDef == null) { ThingDef defaultStuffDef = null; if (entry.def.apparel != null) { defaultStuffDef = ThingDef.Named("Synthread"); } if (thing == null) { Log.Warning("Item " + entry.def.defName + " is \"made from stuff\" but no material was specified. This may be caused by a misconfigured modded item or scenario."); Log.Warning("The item will be spawned into the map and assigned a default material, if possible, but you will see an error if you are in Development Mode."); } thing = ThingMaker.MakeThing(entry.def, defaultStuffDef); } else { thing = ThingMaker.MakeThing(entry.def, entry.stuffDef); } if (thing != null) { thing.stackCount = stackCount; if (entry.def.weaponTags != null && entry.def.weaponTags.Count > 0) { weapons.Add(thing); } else if (entry.def.apparel != null) { apparel.Add(thing); } else if (entry.def.ingestible != null) { food.Add(thing); } else { other.Add(thing); } } // Decrement the count whether we were able to add a valid thing or not. If we don't decrement, // we'll be stuck in an infinite while loop. count -= stackCount; } } else if (entry.animal) { int count = e.Count; for (int i = 0; i < count; i++) { Thing animal = CreateAnimal(entry); if (animal != null) { animals.Add(animal); } } } } List <Thing> combined = new List <Thing>(); combined.AddRange(weapons); combined.AddRange(food); combined.AddRange(apparel); combined.AddRange(animals); combined.AddRange(other); num = 0; foreach (Thing thing in combined) { if (thing.def.CanHaveFaction) { thing.SetFactionDirect(Faction.OfPlayer); } list[num].Add(thing); num++; if (num >= list.Count) { num = 0; } } // Get the arrive method from the scenario part. PlayerPawnsArriveMethod arriveMethod = PlayerPawnsArriveMethod.DropPods; if (arriveMethodPart != null) { arriveMethod = (PlayerPawnsArriveMethod)typeof(ScenPart_PlayerPawnsArriveMethod).GetField("method", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(arriveMethodPart); } bool instaDrop = Find.GameInitData.QuickStarted || arriveMethod != PlayerPawnsArriveMethod.DropPods; DropPodUtility.DropThingGroupsNear(MapGenerator.PlayerStartSpot, map, list, 110, instaDrop, true, true); }