public BlueprintTransferUtility(Blueprint blueprint, Map map, ResolveParams rp, ScatterOptions options) { this.blueprint = blueprint; this.map = map; this.rp = rp; this.options = options; Debug.Log("Transferring blueprint of faction {0}", rp.faction?.Name ?? "none"); if (blueprint == null) { Debug.Error(Debug.BlueprintTransfer, "Attempting to configure transfer utility with empty blueprint!"); return; } if (map == null) { Debug.Error(Debug.BlueprintTransfer, "Attempting to configure transfer utility with empty map!"); return; } if (options == null) { Debug.Error(Debug.BlueprintTransfer, "Attempting to configure transfer utility with empty options!"); return; } mapOriginX = rp.rect.minX + rp.rect.Width / 2 - blueprint.width / 2; mapOriginZ = rp.rect.minZ + rp.rect.Height / 2 - blueprint.height / 2; if (mapOriginX < 0) { mapOriginX = 0; } if (mapOriginZ < 0) { mapOriginZ = 0; } if (mapOriginX + blueprint.width >= map.Size.x) { mapOriginX = map.Size.x - blueprint.width - 1; } if (mapOriginZ + blueprint.height >= map.Size.z) { mapOriginZ = map.Size.z - blueprint.height - 1; } if (options.overridePosition != IntVec3.Zero) { if (!options.centerIfExceedsBounds || (options.overridePosition.x + blueprint.width < map.Size.x && options.overridePosition.z + blueprint.height < map.Size.z)) { mapOriginX = options.overridePosition.x; mapOriginZ = options.overridePosition.z; } else { Debug.Warning(Debug.BlueprintTransfer, "Tried to override position, but map exceeded bounds and position was reverted due to corresponding options flag."); Debug.Warning(Debug.BlueprintTransfer, "New position: {0}, {1}", mapOriginX, mapOriginZ); } } }
public override void Generate(Map map, GenStepParams parms) { Debug.Log(Debug.Scatter, "Medium generate"); Find.TickManager.Pause(); currentOptions = RealRuins_ModSettings.defaultScatterOptions.Copy(); //store as instance variable to keep accessible on subsequent ScatterAt calls currentOptions.minRadius = 24; currentOptions.maxRadius = 50; currentOptions.scavengingMultiplier = 0.1f; currentOptions.deteriorationMultiplier = 0.1f; currentOptions.hostileChance = 0.8f; currentOptions.itemCostLimit = 800; currentOptions.minimumCostRequired = 25000; currentOptions.minimumDensityRequired = 0.01f; currentOptions.minimumAreaRequired = 4000; currentOptions.deleteLowQuality = false; //do not delete since we have much higher requirements for base ruins currentOptions.shouldKeepDefencesAndPower = true; ResolveParams rp = default(ResolveParams); BaseGen.globalSettings.map = map; rp.rect = new CellRect(0, 0, map.Size.x, map.Size.z); rp.SetCustom <ScatterOptions>(Constants.ScatterOptions, currentOptions); rp.faction = Find.FactionManager.OfAncientsHostile; BaseGen.symbolStack.Push("chargeBatteries", rp); BaseGen.symbolStack.Push("refuel", rp); BaseGen.symbolStack.Push("scatterRuins", rp); if (Rand.Chance(0.5f * Find.Storyteller.difficulty.threatScale)) { float pointsCost = Math.Abs(Rand.Gaussian()) * 500 * Find.Storyteller.difficulty.threatScale; rp.faction = Find.FactionManager.RandomEnemyFaction(); rp.singlePawnLord = LordMaker.MakeNewLord(rp.faction, new LordJob_AssaultColony(rp.faction, false, false, true, true), map, null); rp.pawnGroupKindDef = (rp.pawnGroupKindDef ?? PawnGroupKindDefOf.Settlement); if (rp.pawnGroupMakerParams == null) { rp.pawnGroupMakerParams = new PawnGroupMakerParms(); rp.pawnGroupMakerParams.tile = map.Tile; rp.pawnGroupMakerParams.faction = rp.faction; PawnGroupMakerParms pawnGroupMakerParams = rp.pawnGroupMakerParams; pawnGroupMakerParams.points = pointsCost; } BaseGen.symbolStack.Push("pawnGroup", rp); } BaseGen.Generate(); }
public override void GenerateStartingParty(Map map, ResolveParams rp) { ScatterOptions currentOptions = rp.GetCustom <ScatterOptions>(Constants.ScatterOptions); float uncoveredCost = currentOptions.uncoveredCost; int points = (int)(uncoveredCost / (10 * militaryPower)); int initialGroup = 0; if (points > 10000) { initialGroup = Rand.Range(5000, 10000); } else { initialGroup = points; } Debug.Log(Debug.ForceGen, "Military gen: uncoveredCost {0}, military power: {1}, total points allowed: {2}", uncoveredCost, militaryPower, points); points -= initialGroup; SpawnGroup((int)ScalePointsToDifficulty(initialGroup), rp.rect, rp.faction, map); Debug.Log(Debug.ForceGen, "Initial group of {0} spawned, {1} points left for triggers", initialGroup, points); while (points > 0) { IntVec3 mapLocation = rp.rect.RandomCell; if (!mapLocation.InBounds(map)) { continue; } ThingDef raidTriggerDef = ThingDef.Named("RaidTrigger"); RaidTrigger trigger = ThingMaker.MakeThing(raidTriggerDef) as RaidTrigger; trigger.faction = rp.faction; trigger.SetTimeouts(0, 300); int raidMaxPoints = (int)(10000 / Math.Max(Math.Sqrt(d: militaryPower), 1.0)); float raidValue = Math.Abs(Rand.Gaussian()) * raidMaxPoints + Rand.Value * raidMaxPoints + 250.0f; if (raidValue > 10000) { raidValue = Rand.Range(8000, 11000); //sanity cap. against some beta-poly bases. } points -= (int)raidValue; trigger.value = ScalePointsToDifficulty(points); GenSpawn.Spawn(trigger, mapLocation, map); Debug.Log(Debug.ForceGen, "Spawned trigger at {0}, {1} for {2} points, autofiring after {3} rare ticks", mapLocation.x, mapLocation.z, trigger.value, 0); } }
public static void Process(Blueprint source, ScatterOptions options) { if (options.enableDeterioration) { DeteriorationProcessor dp = new DeteriorationProcessor(); dp.options = options; dp.blueprint = source; dp.itemsIntegrity = new float[source.width, source.height]; dp.terrainIntegrity = new float[source.width, source.height]; dp.ConstructRoomBasedIntegrityMap(); //Debug.PrintNormalizedFloatMap(dp.terrainIntegrity); //Debug.PrintNormalizedFloatMap(dp.itemsIntegrity); dp.Deteriorate(); } }
private void ResetSettings() { ScatterOptions defaultOptions = ScatterOptions.Default; RealRuins_ModSettings.defaultScatterOptions.claimableBlocks = defaultOptions.claimableBlocks; RealRuins_ModSettings.defaultScatterOptions.decorationChance = defaultOptions.decorationChance; RealRuins_ModSettings.defaultScatterOptions.deteriorationMultiplier = defaultOptions.deteriorationMultiplier; RealRuins_ModSettings.defaultScatterOptions.densityMultiplier = defaultOptions.densityMultiplier; RealRuins_ModSettings.defaultScatterOptions.maxRadius = defaultOptions.maxRadius; RealRuins_ModSettings.defaultScatterOptions.minRadius = defaultOptions.minRadius; RealRuins_ModSettings.defaultScatterOptions.disableSpawnItems = defaultOptions.disableSpawnItems; RealRuins_ModSettings.defaultScatterOptions.enableInstantCaravanReform = defaultOptions.enableInstantCaravanReform; RealRuins_ModSettings.defaultScatterOptions.enableProximity = defaultOptions.enableProximity; RealRuins_ModSettings.defaultScatterOptions.hostileChance = defaultOptions.hostileChance; RealRuins_ModSettings.defaultScatterOptions.itemCostLimit = defaultOptions.itemCostLimit; RealRuins_ModSettings.defaultScatterOptions.scavengingMultiplier = defaultOptions.scavengingMultiplier; RealRuins_ModSettings.defaultScatterOptions.trapChance = defaultOptions.trapChance; RealRuins_ModSettings.defaultScatterOptions.wallsDoorsOnly = defaultOptions.wallsDoorsOnly; }
public BlueprintTransferUtility(Blueprint blueprint, Map map, ResolveParams rp) { this.blueprint = blueprint; this.map = map; this.rp = rp; mapOriginX = rp.rect.minX + rp.rect.Width / 2 - blueprint.width / 2; mapOriginZ = rp.rect.minZ + rp.rect.Height / 2 - blueprint.height / 2; if (mapOriginX < 0) { mapOriginX = 0; } if (mapOriginZ < 0) { mapOriginZ = 0; } if (mapOriginX + blueprint.width >= map.Size.x) { mapOriginX = map.Size.x - blueprint.width - 1; } if (mapOriginZ + blueprint.height >= map.Size.z) { mapOriginZ = map.Size.z - blueprint.height - 1; } options = rp.GetCustom <ScatterOptions>(Constants.ScatterOptions); if (options.overridePosition != IntVec3.Zero) { if (!options.centerIfExceedsBounds || (options.overridePosition.x + blueprint.width < map.Size.x && options.overridePosition.z + blueprint.height < map.Size.z)) { mapOriginX = options.overridePosition.x; mapOriginZ = options.overridePosition.z; } else { Debug.Warning(Debug.BlueprintTransfer, "Tried to override position, but map exceeded bounds and position was reverted due to corresponding options flag."); Debug.Warning(Debug.BlueprintTransfer, "New position: {0}, {1}", mapOriginX, mapOriginZ); } } }
public override void GenerateStartingParty(Map map, ResolveParams rp, ScatterOptions currentOptions) { float uncoveredCost = currentOptions.uncoveredCost; Faction faction = rp.faction; if (this.faction != null) { faction = this.faction; } if (faction == null) { faction = Find.FactionManager.RandomEnemyFaction(true, true, false); } Debug.Log(Debug.ForceGen, "Citizen force gen: uncoveredCost {0}. rect {1}", uncoveredCost, rp.rect); int maxPoints = Math.Min((int)(uncoveredCost / 10), 5000); SpawnGroup((int)ScalePointsToDifficulty(maxPoints), rp.rect, faction, map, Rand.Range(Math.Max(2, (int)(bedCount * 0.7)), (int)(bedCount * 1.5))); currentOptions.uncoveredCost -= maxPoints * 10; }
public ScatterOptions Copy() { ScatterOptions copy = new ScatterOptions { deteriorationMultiplier = deteriorationMultiplier, claimableBlocks = claimableBlocks, decorationChance = decorationChance, densityMultiplier = densityMultiplier, hostileChance = hostileChance, scavengingMultiplier = scavengingMultiplier, trapChance = trapChance, disableSpawnItems = disableSpawnItems, itemCostLimit = itemCostLimit, minRadius = minRadius, maxRadius = maxRadius, wallsDoorsOnly = wallsDoorsOnly, enableProximity = enableProximity, minimumAreaRequired = minimumAreaRequired, minimumDensityRequired = minimumDensityRequired, minimumCostRequired = minimumCostRequired, deleteLowQuality = deleteLowQuality, shouldKeepDefencesAndPower = shouldKeepDefencesAndPower, shouldLoadPartOnly = shouldLoadPartOnly, shouldAddRaidTriggers = shouldAddRaidTriggers, uncoveredCost = uncoveredCost, enableInstantCaravanReform = enableInstantCaravanReform, shouldAddFilth = shouldAddFilth, roomMap = roomMap, bottomLeft = bottomLeft, blueprintRect = blueprintRect, allowFriendlyRaids = allowFriendlyRaids, enableDeterioration = enableDeterioration, forceFullHitPoints = forceFullHitPoints, canHaveFood = canHaveFood, blueprintFileName = blueprintFileName, centerIfExceedsBounds = centerIfExceedsBounds, overwritesEverything = overwritesEverything }; return(copy); }
public override void GenerateStartingParty(Map map, ResolveParams rp, ScatterOptions currentOptions) { float uncoveredCost = currentOptions.uncoveredCost; if (uncoveredCost > 0 || currentOptions.startingPartyPoints > 0) { float pointsCost = 0; if (currentOptions.startingPartyPoints > 0) { pointsCost = currentOptions.startingPartyPoints; } else { pointsCost = uncoveredCost / 10.0f; FloatRange defaultPoints = new FloatRange(pointsCost * 0.7f, Math.Min(12000.0f, pointsCost * 2.0f)); Debug.Log(Debug.ForceGen, "Adding starting party. Remaining points: {0}. Used points range: {1}", currentOptions.uncoveredCost, defaultPoints); } pointsCost = ScalePointsToDifficulty(pointsCost); ScatterStartingParties((int)pointsCost, currentOptions.allowFriendlyRaids, map); } }
public override void GenerateForces(Map map, ResolveParams rp) { Debug.Log(Debug.ForceGen, "Generating mechanoid forces"); ScatterOptions options = rp.GetCustom <ScatterOptions>(Constants.ScatterOptions); CellRect rect = rp.rect; /*if (rect.minX < 15 || rect.minZ < 15 || rect.maxX > map.Size.x - 15 || rect.maxZ > map.Size.z - 15) { * return; //do not add enemies if we're on the map edge * } * * if (!CellFinder.TryFindRandomCellInsideWith(rect, (IntVec3 x) => x.Standable(map) && options.roomMap[x.x - rect.BottomLeft.x, x.z - rect.BottomLeft.z] > 1, out IntVec3 testCell)) { * return; //interrupt if there are no closed cells available * }*/ PawnKindDef pawnKindDef = null; if (powerMax == 0) { powerMax = rect.Area / 30.0f; } powerMax = ScalePointsToDifficulty(powerMax); float powerThreshold = (Math.Abs(Rand.Gaussian(0.5f, 1)) * powerMax) + 1; float cumulativePower = 0; Faction faction = Faction.OfAncientsHostile; Lord lord = LordMaker.MakeNewLord(lordJob: new LordJob_DefendPoint(rect.CenterCell), faction: faction, map: map, startingPawns: null); int tile = map.Tile; while (cumulativePower <= powerThreshold) { PawnKindDef currentPawnKindDef = (from kind in DefDatabase <PawnKindDef> .AllDefsListForReading where kind.RaceProps.IsMechanoid select kind).RandomElementByWeight((PawnKindDef kind) => 1f / kind.combatPower); PawnGenerationRequest request = new PawnGenerationRequest(currentPawnKindDef, faction, PawnGenerationContext.NonPlayer, tile, true, false, false, //allowDead is last false, true, false, 1f, false, true, true, false, false, false, false, null, null, null, null, null, null, null, null); IntVec3 cell = IntVec3.Invalid; // if (!CellFinder.TryFindRandomCellInsideWith(rect, (IntVec3 x) => x.Standable(map) && options.roomMap[x.x - rect.minX, x.z - rect.minZ] > 1, out cell)) { CellFinder.TryFindRandomSpawnCellForPawnNear(rect.CenterCell, map, out cell); // } if (cell != IntVec3.Invalid) { Pawn pawn = PawnGenerator.GeneratePawn(request); FilthMaker.MakeFilth(cell, map, ThingDefOf.Filth_Blood, 5); GenSpawn.Spawn(pawn, cell, map, WipeMode.Vanish); lord.AddPawn(pawn); cumulativePower += pawn.kindDef.combatPower; } else { break; //no more suitable cells } } }
//ResolveParams's GetCustom/SetCustom is broken now, and it is a sealed (WHY??!) struct (WHY??!?!?) so I neither can fix it, nor make a dirty hack. //Seems like I have to abandon this approach and make direct calls. static public void Scatter(ResolveParams rp, ScatterOptions options, CoverageMap coverage, List <AbstractDefenderForcesGenerator> generators) { Debug.Log(Debug.Scatter, "Running stand-alone scatterer"); if (options == null) { Debug.Warning(Debug.Scatter, "Scatter options are null, aborting"); return; } DateTime start = DateTime.UtcNow; Debug.Log(Debug.Scatter, "[0 s]: Loading blueprint of size {0} - {1} to deploy at {2}, {3}", options.minRadius, options.maxRadius, rp.rect.minX, rp.rect.minZ); Blueprint bp = null; Map map = BaseGen.globalSettings.map; //probably should split scattering options into several distinct objects string filename = options.blueprintFileName; if (string.IsNullOrEmpty(filename) || !BlueprintLoader.CanLoadBlueprintAtPath(filename)) { bp = BlueprintFinder.FindRandomBlueprintWithParameters(out filename, options.minimumAreaRequired, options.minimumDensityRequired, 15, removeNonQualified: options.deleteLowQuality); if (string.IsNullOrEmpty(filename)) { //still null = no suitable blueprints, fail. Debug.Warning(Debug.Scatter, "Blueprint name was null and could not find another suitable blueprint, skipping"); return; } } if (!options.shouldLoadPartOnly) //should not cut => load whole { if (bp == null) //if not loaded yet { bp = BlueprintLoader.LoadWholeBlueprintAtPath(filename); } } else { int radius = Rand.Range(options.minRadius, options.maxRadius); if (bp == null) { bp = BlueprintLoader.LoadWholeBlueprintAtPath(filename).RandomPartCenteredAtRoom(new IntVec3(radius, 0, radius)); } else { bp = bp.RandomPartCenteredAtRoom(new IntVec3(radius, 0, radius)); } } if (bp == null) { Debug.Warning(Debug.Scatter, "Blueprint is still null after attempting to load any qualifying, returning"); return; } Debug.Extra(Debug.Scatter, "Blueprint loaded, cutting and searching for rooms"); bp.CutIfExceedsBounds(map.Size); // Here we have our blueprint loaded and ready to action. Doing stuff: BlueprintPreprocessor.ProcessBlueprint(bp, options); //Preprocess: remove missing and unnecessary items according to options bp.FindRooms(); //Traverse blueprint and construct rooms map options.roomMap = bp.wallMap; //Debug.PrintIntMap(bp.wallMap, delta: 1); Debug.Extra(Debug.Scatter, "Rooms traversed, initializing transfer utility"); BlueprintTransferUtility btu = new BlueprintTransferUtility(bp, map, rp, options); //prepare blueprint transferrer Debug.Extra(Debug.Scatter, "Initialized, removing incompatible items..."); btu?.RemoveIncompatibleItems(); //remove incompatible items Debug.Extra(Debug.Scatter, "Recalculating stats..."); bp.UpdateBlueprintStats(true); //Update total cost, items count, etc Debug.Extra(Debug.Scatter, "Deteriorating..."); DeteriorationProcessor.Process(bp, options); //create deterioration maps and do deterioration Debug.Extra(Debug.Scatter, "Scavenging..."); ScavengingProcessor sp = new ScavengingProcessor(); sp.RaidAndScavenge(bp, options); //scavenge remaining items according to scavenge options Debug.Extra(Debug.Scatter, "[{0} s] Prepared, about to start transferring.", DateTime.UtcNow.Subtract(start).TotalSeconds); try { btu.Transfer(coverage); //transfer blueprint Debug.Extra(Debug.Scatter, "[{0} s] Transferred.", DateTime.UtcNow.Subtract(start).TotalSeconds); } catch (Exception e) { Debug.Error(Debug.BlueprintTransfer, "Failed to transfer blueprint due to {0}", e); } if (generators != null) { foreach (AbstractDefenderForcesGenerator generator in generators) { try { generator.GenerateForces(map, rp, options); } catch (Exception e) { Debug.Error(Debug.BlueprintTransfer, "Failed to generate forces: {0}", e); } } } Debug.Log(Debug.Scatter, "[{0} s] Generated forces.", DateTime.UtcNow.Subtract(start).TotalSeconds); if (options.shouldAddFilth) { try { btu.AddFilthAndRubble(); //add filth and rubble } catch (Exception e) { Debug.Warning(Debug.BlueprintTransfer, "Failed to add filth and rubble: {0}", e); } } Debug.Extra(Debug.Scatter, "[{0} s] Spiced up with rubble. Completed.", DateTime.UtcNow.Subtract(start).TotalSeconds); Debug.Log(Debug.Scatter, "Chunk scattering finished, moving"); }
public override void Generate(Map map, GenStepParams parms) { Find.TickManager.Pause(); //Debug.Message("Overridden LARGE generate"); string filename = map.Parent.GetComponent <RuinedBaseComp>()?.blueprintFileName; Debug.Log(Debug.Scatter, "Large Ruins - Preselected file name is {0}", filename); currentOptions = RealRuins_ModSettings.defaultScatterOptions.Copy(); //store as instance variable to keep accessible on subsequent ScatterAt calls currentOptions.minRadius = 200; currentOptions.maxRadius = 200; currentOptions.scavengingMultiplier = 0.1f; currentOptions.deteriorationMultiplier = 0.0f; currentOptions.hostileChance = 1.0f; currentOptions.blueprintFileName = filename; currentOptions.costCap = map.Parent.GetComponent <RuinedBaseComp>()?.currentCapCost ?? -1; currentOptions.startingPartyPoints = (int)(map.Parent.GetComponent <RuinedBaseComp>()?.raidersActivity ?? -1); currentOptions.minimumCostRequired = (int)Math.Min(100000.0f, RealRuins_ModSettings.ruinsCostCap); currentOptions.minimumDensityRequired = 0.015f; currentOptions.minimumAreaRequired = 6400; currentOptions.deleteLowQuality = false; //do not delete since we have much higher requirements for base ruins currentOptions.shouldKeepDefencesAndPower = true; currentOptions.shouldLoadPartOnly = false; currentOptions.shouldAddRaidTriggers = Find.Storyteller.difficulty.allowBigThreats; currentOptions.claimableBlocks = false; currentOptions.enableDeterioration = false; ResolveParams resolveParams = default(ResolveParams); BaseGen.globalSettings.map = map; resolveParams.faction = Find.FactionManager.OfAncientsHostile; resolveParams.rect = new CellRect(0, 0, map.Size.x, map.Size.z); List <AbstractDefenderForcesGenerator> generators = new List <AbstractDefenderForcesGenerator> { new BattleRoyaleForcesGenerator() }; BaseGen.globalSettings.mainRect = resolveParams.rect; float uncoveredCost = currentOptions.uncoveredCost; if (uncoveredCost < 0) { if (Rand.Chance(0.5f)) { uncoveredCost = -uncoveredCost; //adding really small party } } RuinsScatterer.Scatter(resolveParams, currentOptions, null, generators); BaseGen.symbolStack.Push("chargeBatteries", resolveParams); BaseGen.symbolStack.Push("refuel", resolveParams); BaseGen.Generate(); //adding starting party //don't doing it via basegen because of uh oh i don't remember, something with pawn location control if (generators != null) { foreach (AbstractDefenderForcesGenerator generator in generators) { generator.GenerateStartingParty(map, resolveParams, currentOptions); } } }
public static void ProcessBlueprint(Blueprint blueprint, ScatterOptions options) { if (blueprint == null) { return; } //Each item should be checked if it can be placed or not. This should help preventing situations when simulated scavenging removes things which anyway won't be placed. //For each placed item it's cost should be calculated for (int x = 0; x < blueprint.width; x++) { for (int z = 0; z < blueprint.height; z++) { List <ItemTile> items = blueprint.itemsMap[x, z]; TerrainTile terrain = blueprint.terrainMap[x, z]; TerrainDef terrainDef = null; if (terrain != null) { terrainDef = DefDatabase <TerrainDef> .GetNamed(terrain.defName, false); if (terrainDef == null) { blueprint.terrainMap[x, z] = null; //remove unloadable terrain terrain = null; } } List <ItemTile> itemsToRemove = new List <ItemTile>(); if (items == null) { continue; } foreach (ItemTile item in items) { if (item.defName == "Corpse") { continue; //TODO: make some better way of handling corpses } //We can't move further with corpse item, because this item's thingDef is always null (actual corpse's def name depends on it's kind) ThingDef thingDef = DefDatabase <ThingDef> .GetNamed(item.defName, false); if (thingDef == null) { itemsToRemove.Add(item); continue; } //remove items we don't want to see in the ruins if (thingDef == ThingDefOf.Campfire || thingDef == ThingDefOf.TorchLamp) { itemsToRemove.Add(item); continue; } if (options.wallsDoorsOnly) //eleminate almost everything if "doors & walls" setting is active { if (!thingDef.IsDoor && !item.defName.ToLower().Contains("wall")) { itemsToRemove.Add(item); continue; } } if (options.disableSpawnItems) //eleminate haulables if corresponding tick is set { if (thingDef.EverHaulable) { itemsToRemove.Add(item); continue; } } if (thingDef.defName.Contains("Animal") || thingDef.defName.Contains("Spot")) { itemsToRemove.Add(item); continue; //remove animal sleeping beds and spots as wild animals tend to concentrate around. remove wedding, butchering and party spots, caravan spots as well } if (thingDef.IsCorpse || thingDef.Equals(ThingDefOf.MinifiedThing)) //check if corpses and minified things contain inner data, otherwise ignore { if ((item.innerItems?.Count() ?? 0) == 0 && item.itemXml == null) { itemsToRemove.Add(item); continue; } } } foreach (ItemTile item in itemsToRemove) { if (item.isWall || item.isDoor) { blueprint.RemoveWall(item.location.x, item.location.z); } items.Remove(item); } } } //Recalculate cost data after removing some items (should speed up, as cost calculation seems to be cpu-expensive process) blueprint.UpdateBlueprintStats(true); //Perform removing all items exceeding maximum cost for (int x = 0; x < blueprint.width; x++) { for (int z = 0; z < blueprint.height; z++) { List <ItemTile> items = blueprint.itemsMap[x, z]; TerrainTile terrain = blueprint.terrainMap[x, z]; List <ItemTile> itemsToRemove = new List <ItemTile>(); if (terrain != null) { if (terrain.cost > options.itemCostLimit) { blueprint.terrainMap[x, z] = null; } } if (items == null) { continue; } foreach (ItemTile item in items) { if (options.itemCostLimit > 0 && options.itemCostLimit < 1000) //filter too expensive items. limit of 1000 means "no limit" actually { if (item.cost > options.itemCostLimit) //removes only items where at least one item is more expensive than limit we have. limiting stacks is done later. { itemsToRemove.Add(item); } } } foreach (ItemTile item in itemsToRemove) { if (item.isWall || item.isDoor) { blueprint.RemoveWall(item.location.x, item.location.z); } items.Remove(item); } } } }
public BlueprintAnalyzer(Blueprint blueprint, ScatterOptions options = null) { this.blueprint = blueprint; this.options = options ?? ScatterOptions.asIs(); itemStats = new Dictionary <string, ItemStatRecord>(); }
public override void GenerateForces(Map map, ResolveParams rp) { ScatterOptions options = rp.GetCustom <ScatterOptions>(Constants.ScatterOptions); if (options == null) { return; } int addedTriggers = 0; float ratio = 10; float remainingCost = options.uncoveredCost * (Rand.Value + 0.5f); //cost estimation as seen by other factions float initialCost = remainingCost; int triggersAbsoluteMaximum = 100; while (remainingCost > 0) { IntVec3 mapLocation = rp.rect.RandomCell; if (!mapLocation.InBounds(map)) { continue; } ThingDef raidTriggerDef = ThingDef.Named("RaidTrigger"); RaidTrigger trigger = ThingMaker.MakeThing(raidTriggerDef) as RaidTrigger; if (options.allowFriendlyRaids) { if (Rand.Chance(0.2f)) { trigger.faction = Find.FactionManager.RandomNonHostileFaction(); } else { trigger.faction = Find.FactionManager.RandomEnemyFaction(); } } else { trigger.faction = Find.FactionManager.RandomEnemyFaction(); } int raidMaxPoints = (int)(remainingCost / ratio); float raidValue = Math.Abs(Rand.Gaussian()) * raidMaxPoints + Rand.Value * raidMaxPoints + 250.0f; if (raidValue > 10000) { raidValue = Rand.Range(8000, 11000); //sanity cap. against some beta-poly bases. } remainingCost -= raidValue * ratio; trigger.value = ScalePointsToDifficulty(raidValue); GenSpawn.Spawn(trigger, mapLocation, map); Debug.Log(Debug.ForceGen, "Spawned trigger at {0}, {1} for {2} points, autofiring after {3} rare ticks", mapLocation.x, mapLocation.z, trigger.value, 0); addedTriggers++; options.uncoveredCost = Math.Abs(remainingCost); if (addedTriggers > triggersAbsoluteMaximum) { if (remainingCost < initialCost * 0.2f) { if (Rand.Chance(0.1f)) { if (remainingCost > 100000) { remainingCost = Rand.Range(80000, 110000); } return; } } } } }
public override void GenerateForces(Map map, ResolveParams rp, ScatterOptions options) { Debug.Log(Debug.ForceGen, "Animal forces generation"); CellRect rect = rp.rect; /*if (rect.minX < 15 || rect.minZ < 15 || rect.maxX > map.Size.x - 15 || rect.maxZ > map.Size.z - 15) { * return; //do not add enemies if we're on the map edge * } * * if (!CellFinder.TryFindRandomCellInsideWith(rect, (IntVec3 x) => x.Standable(map) && options.roomMap[x.x - rect.BottomLeft.x, x.z - rect.BottomLeft.z] > 1, out IntVec3 testCell)) { * return; //interrupt if there are no closed cells available * }*/ PawnKindDef pawnKindDef = null; pawnKindDef = map.Biome.AllWildAnimals.RandomElementByWeight((PawnKindDef def) => (def.RaceProps.foodType == FoodTypeFlags.CarnivoreAnimal || def.RaceProps.foodType == FoodTypeFlags.OmnivoreAnimal) ? 1 : 0); float powerMax = (float)Math.Sqrt(options.uncoveredCost / 10 * (rect.Area / 30.0f)); Debug.Log(Debug.ForceGen, "Unscaled power is {0} based on cost of {1} and area of {2}", powerMax, options.uncoveredCost, rect.Area); powerMax = ScalePointsToDifficulty(powerMax); float powerThreshold = (Math.Abs(Rand.Gaussian(0.5f, 1)) * powerMax) + 1; float cumulativePower = 0; Faction faction = Faction.OfAncientsHostile; Lord lord = LordMaker.MakeNewLord(lordJob: new LordJob_DefendPoint(rect.CenterCell), faction: faction, map: map, startingPawns: null); int tile = map.Tile; while (cumulativePower <= powerThreshold) { PawnKindDef currentPawnKindDef = pawnKindDef; PawnGenerationRequest request = new PawnGenerationRequest(currentPawnKindDef, faction, PawnGenerationContext.NonPlayer, tile, true, false, false, //allowDead is last false, true, true, 1f, false, true, true, true, false, false, false, true, 0, null, 1, null, null, null, null, null, null, null); IntVec3 cell = IntVec3.Invalid; if (!CellFinder.TryFindRandomCellInsideWith(rect, (IntVec3 x) => x.Standable(map) && options.roomMap[x.x - rect.minX, x.z - rect.minZ] > 1, out cell)) { CellFinder.TryFindRandomSpawnCellForPawnNear(rect.CenterCell, map, out cell); } if (cell != IntVec3.Invalid) { Pawn pawn = PawnGenerator.GeneratePawn(request); FilthMaker.TryMakeFilth(cell, map, ThingDefOf.Filth_Blood, 5); GenSpawn.Spawn(pawn, cell, map, WipeMode.Vanish); lord.AddPawn(pawn); cumulativePower += pawn.kindDef.combatPower; } else { break; //no more suitable cells } } }
public override void GenerateStartingParty(Map map, ResolveParams rp, ScatterOptions options) { }
public abstract void GenerateForces(Map map, ResolveParams rp, ScatterOptions options); //called during map generation //There was some problem with pawns not created properly during basegen stack steps, but all working fine in genstep handler //Probably should investigate this later and combine all generation in one method public abstract void GenerateStartingParty(Map map, ResolveParams rp, ScatterOptions options);
public void RaidAndScavenge(Blueprint blueprint, ScatterOptions options) { //remove the most precious things. smash some other things. //word is spread, so each next raid is more destructive than the previous ones //to make calculations a bit easier we're going to calculate value per cell, not per item. this.options = options; this.blueprint = blueprint; Debug.active = false; float scavengersActivity = Rand.Value * options.scavengingMultiplier + (options.scavengingMultiplier) / 3; //slight variation for scavengers activity for this particular blueprint float elapsedTime = -blueprint.dateShift; int totalRemovedTiles = 0; int totalRemovedTerrains = 0; int totalReplacedTiles = 0; int processedTiles = 0; int processedTerrains = 0; for (int x = 0; x < blueprint.width; x++) { for (int z = 0; z < blueprint.height; z++) { if (blueprint.terrainMap[x, z] != null) { tilesByCost.Add(blueprint.terrainMap[x, z]); totalCost += blueprint.terrainMap[x, z].cost; processedTerrains++; } foreach (ItemTile item in blueprint.itemsMap[x, z]) { tilesByCost.Add(item); totalCost += item.cost; processedTiles++; } } } tilesByCost.Sort(delegate(Tile t1, Tile t2) { return((t1.cost / t1.weight).CompareTo(t2.cost / t2.weight)); }); int ruinsArea = blueprint.width * blueprint.height; //Debug.Message("Scavenging blueprint of area {0}, age {1}, scavengers activity multiplier {2}", ruinsArea, elapsedTime, scavengersActivity); //Debug.Message("Enumerated {0} items", tilesByCost.Count()); //Debug.PrintArray(tilesByCost.ToArray()); int raidsCount = (int)(Math.Log(elapsedTime / 10 + 1) * scavengersActivity); if (raidsCount > 50) { raidsCount = 50; } if (options.scavengingMultiplier > 0.9f && raidsCount <= 0) { raidsCount = 1; //at least one raid for each ruins in case of normal scavenging activity } float baseRaidCapacity = ruinsArea / 10 * scavengersActivity; Debug.Log(Debug.BlueprintTransfer, "Performing {0} raids. Base capacity: {1}", raidsCount, baseRaidCapacity); for (int i = 0; i < raidsCount; i++) { float raidCapacity = baseRaidCapacity * (float)Math.Pow(1.1, i); bool shouldStop = false; Debug.Log(Debug.BlueprintTransfer, "Performing raid {0} of capacity {1}", i, raidCapacity); while (tilesByCost.Count > 0 && raidCapacity > 0 && !shouldStop) { Tile topTile = tilesByCost.Pop(); String msg = string.Format("Inspecting tile \"{0}\" of cost {1} and weight {2}. ", topTile.defName, topTile.cost, topTile.weight); if (topTile.cost / topTile.weight < 7) { shouldStop = true; //nothing to do here, everything valueable has already gone msg += "Too cheap, stopping."; } else { if (Rand.Chance(0.999f)) //there is still chance that even the most expensive thing will be left after raid. ("Big momma said ya shouldn't touch that golden chair, it's cursed") { raidCapacity -= topTile.weight; if (topTile is TerrainTile) { blueprint.terrainMap[topTile.location.x, topTile.location.z] = null; totalRemovedTerrains++; totalCost -= topTile.cost; msg += "Terrain removed."; } else if (topTile is ItemTile) { ItemTile itemTile = topTile as ItemTile; totalCost -= itemTile.cost; blueprint.itemsMap[topTile.location.x, topTile.location.z].Remove(itemTile); if (itemTile.isDoor) //if door is removed it should be replaced with another door, raiders are very polite and always replace expensive doors with cheaper ones. { if (Rand.Chance(0.8f)) //ok, not always. { ItemTile replacementTile = ItemTile.DefaultDoorItemTile(itemTile.location); blueprint.itemsMap[topTile.location.x, topTile.location.z].Add(replacementTile); msg += "Added " + replacementTile.defName + ", original "; totalReplacedTiles++; } else { totalRemovedTiles++; blueprint.RemoveWall(itemTile.location.x, itemTile.location.z); } } else if (itemTile.isWall) //if something like a wall removed (vent or aircon) you usually want to cover the hole to keep wall integrity { ItemTile replacementTile = ItemTile.WallReplacementItemTile(itemTile.location); blueprint.itemsMap[topTile.location.x, topTile.location.z].Add(replacementTile); msg += "Added " + replacementTile.defName + ", original "; totalReplacedTiles++; } else { totalRemovedTiles++; } msg += "Tile removed."; } } } Debug.Log(Debug.BlueprintTransfer, msg); if (shouldStop) { break; } } if (shouldStop) { break; } } //Check that there are no "hanging doors" left bool HasWallsIn(List <ItemTile> list) { foreach (ItemTile tile in list) { if (tile.isWall) { return(true); } } return(false); } for (int z = 1; z < blueprint.height - 1; z++) { for (int x = 1; x < blueprint.width - 1; x++) { ItemTile tileToRemove = null; foreach (ItemTile tile in blueprint.itemsMap[x, z]) { if (tile.isDoor) //check if a particular door tile has both two vertically adjacent walls (or similar) or two horizintally adjacent walls { if (!(HasWallsIn(blueprint.itemsMap[x - 1, z]) && HasWallsIn(blueprint.itemsMap[x + 1, z])) && !(HasWallsIn(blueprint.itemsMap[x, z - 1]) && HasWallsIn(blueprint.itemsMap[x, z + 1]))) { tileToRemove = tile; break; } } } if (tileToRemove != null) { blueprint.itemsMap[x, z].Remove(tileToRemove); totalCost -= tileToRemove.cost; blueprint.RemoveWall(x, z); } } } Debug.active = true; //Debug.Message("Scavenging completed. Terrain removed: {0}/{1}, Tiles removed: {2}/{3}, tiles replaced: {4}.", totalRemovedTerrains, processedTerrains, totalRemovedTiles, processedTiles, totalReplacedTiles); if (options.costCap > 0) { LimitCostToCap(); } }
public abstract void GenerateForces(Map map, ResolveParams rp, ScatterOptions options); //called during map generation
public override void GenerateForces(Map map, ResolveParams rp, ScatterOptions options) { if (options == null) { return; } int addedTriggers = 0; float ratio = 10; float remainingCost = options.uncoveredCost * (Rand.Value + 0.5f); //cost estimation as seen by other factions Debug.Log(Debug.ForceGen, "Running citizen force generation with remaining cost of {0} (while uncovered is {1})", remainingCost, options.uncoveredCost); float initialCost = remainingCost; int triggersAbsoluteMaximum = 100; while (remainingCost > 0) { IntVec3 mapLocation = rp.rect.RandomCell; if (!mapLocation.InBounds(map)) { continue; } ThingDef raidTriggerDef = ThingDef.Named("RaidTrigger"); RaidTrigger trigger = ThingMaker.MakeThing(raidTriggerDef) as RaidTrigger; trigger.faction = rp.faction; int raidMaxPoints = (int)(remainingCost / ratio); float raidValue = Math.Abs(Rand.Gaussian()) * raidMaxPoints + Rand.Value * raidMaxPoints + 250.0f; if (raidValue > 10000) { raidValue = Rand.Range(8000, 11000); //sanity cap. against some beta-poly bases. } remainingCost -= raidValue * ratio; int timeout = (int)Math.Abs(Rand.Gaussian(0, 75)); trigger.value = ScalePointsToDifficulty(raidValue); trigger.SetTimeouts(timeout, 200); GenSpawn.Spawn(trigger, mapLocation, map); Debug.Log(Debug.ForceGen, "Spawned trigger at {0}, {1} for {2} points, autofiring after {3} rare ticks", mapLocation.x, mapLocation.z, trigger.value, timeout); addedTriggers++; options.uncoveredCost = Math.Abs(remainingCost); if (addedTriggers > triggersAbsoluteMaximum) { if (remainingCost < initialCost * 0.2f) { if (Rand.Chance(0.1f)) { if (remainingCost > 100000) { remainingCost = Rand.Range(80000, 110000); } return; } } } } }
public override void Generate(Map map, GenStepParams parms) { Find.TickManager.Pause(); //Debug.Message("Overridden LARGE generate"); RealRuinsPOIComp poiComp = map.Parent.GetComponent <RealRuinsPOIComp>(); string filename = SnapshotStoreManager.Instance.SnapshotNameFor(poiComp.blueprintName, poiComp.gameName); Debug.Log("Spawning POI: Preselected file name is {0}", filename); Debug.Log("Location is {0} {1}", poiComp.originX, poiComp.originZ); currentOptions = RealRuins_ModSettings.defaultScatterOptions.Copy(); //store as instance variable to keep accessible on subsequent ScatterAt calls currentOptions.minRadius = 400; currentOptions.maxRadius = 400; currentOptions.scavengingMultiplier = 0.0f; currentOptions.deteriorationMultiplier = 0.0f; currentOptions.hostileChance = 0.0f; currentOptions.blueprintFileName = filename; currentOptions.costCap = -1; currentOptions.startingPartyPoints = -1; currentOptions.minimumCostRequired = 0; currentOptions.minimumDensityRequired = 0.0f; currentOptions.minimumAreaRequired = 0; currentOptions.deleteLowQuality = false; currentOptions.shouldKeepDefencesAndPower = true; currentOptions.shouldLoadPartOnly = false; currentOptions.shouldAddRaidTriggers = false; currentOptions.claimableBlocks = false; if (poiComp.poiType == (int)POIType.Ruins || map.ParentFaction == null) { /*if (Rand.Chance(0.1f)) { * currentOptions.wallsDoorsOnly = true; * } else { * currentOptions.deteriorationMultiplier = Math.Abs(Rand.Gaussian(0, 0.15f)); * }*/ currentOptions.shouldAddFilth = true; currentOptions.forceFullHitPoints = false; currentOptions.enableDeterioration = true; currentOptions.overwritesEverything = false; currentOptions.costCap = (int)Math.Abs(Rand.Gaussian(0, 10000)); currentOptions.itemCostLimit = Rand.Range(50, 300); } else { currentOptions.shouldAddFilth = false; currentOptions.forceFullHitPoints = true; currentOptions.enableDeterioration = false; currentOptions.overwritesEverything = true; } currentOptions.overridePosition = new IntVec3(poiComp.originX, 0, poiComp.originZ); currentOptions.centerIfExceedsBounds = true; var bp = BlueprintLoader.LoadWholeBlueprintAtPath(filename); //FOR DEBUG LOGGING //var a = new BlueprintAnalyzer(bp, currentOptions); //a.Analyze(); Debug.Log(Debug.BlueprintTransfer, "Trying to place POI map at tile {0}, at {1},{2} to {3},{4} ({5}x{6})", map.Parent.Tile, poiComp.originX, poiComp.originZ, poiComp.originX + bp.width, poiComp.originZ + bp.height, bp.width, bp.height); var generators = GeneratorsForBlueprint(bp, poiComp, map.Parent.Faction); ResolveParams resolveParams = default(ResolveParams); BaseGen.globalSettings.map = map; resolveParams.faction = map.ParentFaction; resolveParams.rect = new CellRect(currentOptions.overridePosition.x, currentOptions.overridePosition.z, map.Size.x - currentOptions.overridePosition.x, map.Size.z - currentOptions.overridePosition.z); BaseGen.globalSettings.mainRect = resolveParams.rect; float uncoveredCost = currentOptions.uncoveredCost; if (resolveParams.faction != null) { //Debug.Log("Mannable count: {0}", poiComp.mannableCount); ManTurrets((int)(poiComp.mannableCount * 1.25f + 1), resolveParams, map); } RuinsScatterer.Scatter(resolveParams, currentOptions, null, generators); //ok, but why LIFO? Queue looks more suitable for map generation. //Looks like it was done for nested symbols resolving, but looks strange anyway. BaseGen.symbolStack.Push("chargeBatteries", resolveParams); BaseGen.symbolStack.Push("ensureCanHoldRoof", resolveParams); BaseGen.symbolStack.Push("refuel", resolveParams); BaseGen.Generate(); if (generators != null) { foreach (AbstractDefenderForcesGenerator generator in generators) { generator.GenerateStartingParty(map, resolveParams, currentOptions); } } }
public override void Generate(Map map, GenStepParams parms) { //add standard ruins along with real. if (RealRuins_ModSettings.preserveStandardRuins) { GenStep scatterOriginalRuins = new GenStep_ScatterRuinsSimple(); scatterOriginalRuins.Generate(map, parms); } //skip generation due to low blueprints count if (SnapshotStoreManager.Instance.StoredSnapshotsCount() < 10) { Debug.Error(Debug.Scatter, "Skipping ruins gerenation due to low blueprints count."); return; } //Skip generation for starting tile if corresponding settings flag is set if (RealRuins_ModSettings.startWithoutRuins) { int homes = 0; var allMaps = Find.Maps; for (int i = 0; i < allMaps.Count; i++) { Map someMap = allMaps[i]; if (someMap.IsPlayerHome) { homes++; } } if (homes == 1 && Find.TickManager.TicksGame < 10) { return; //single home => we're generating that single home => that's starting map => no ruins here if this option is selected. } } //Skip generation on other ruins world objects foreach (WorldObject wo in Find.World.worldObjects.ObjectsAt(map.Tile)) { if (wo is RealRuinsPOIWorldObject || wo is AbandonedBaseWorldObject) { return; } } if (!map.TileInfo.WaterCovered) { float densityMultiplier = 1.0f; float scaleMultiplier = 1.0f; float distanceToSettlement = 0.0f; float totalDensity = RealRuins_ModSettings.defaultScatterOptions.densityMultiplier; currentOptions = RealRuins_ModSettings.defaultScatterOptions.Copy(); //store as instance variable to keep accessible on subsequent ScatterAt calls if (RealRuins_ModSettings.defaultScatterOptions.enableProximity) { distanceToSettlement = CalculateDistanceToNearestSettlement(map); if (distanceToSettlement >= 16 && Rand.Chance(0.5f)) { totalDensity = 0; } if (totalDensity > 0) { densityMultiplier = (float)(Math.Exp(1.0 / (distanceToSettlement / 10.0 + 0.3)) - 0.7); scaleMultiplier = (float)(Math.Exp(1 / (distanceToSettlement / 5 + 0.5)) - 0.3); } else { densityMultiplier = 0.0f; } currentOptions.densityMultiplier *= densityMultiplier; currentOptions.minRadius = Math.Min(60, Math.Max(6, (int)(currentOptions.minRadius * scaleMultiplier))); //keep between 6 and 60 currentOptions.maxRadius = Math.Min(60, Math.Max(6, (int)(currentOptions.maxRadius * scaleMultiplier))); //keep between 6 and 60 currentOptions.scavengingMultiplier *= scaleMultiplier * densityMultiplier; currentOptions.deteriorationMultiplier += Math.Min(0.2f, (1.0f / (scaleMultiplier * densityMultiplier * 3))); if (densityMultiplier > 20.0f) { densityMultiplier = 20.0f; } while (densityMultiplier * currentOptions.maxRadius > 800) { densityMultiplier *= 0.9f; //WHAT? Why not 800/radius? } } //number of ruins based on density settings var num = (int)((float)map.Area / 10000.0f) * Rand.Range(1 * totalDensity, 2 * totalDensity); Debug.Log(Debug.Scatter, "dist {0}, dens {1} (x{2}), scale x{3} ({4}-{5}), scav {6}, deter {7}", distanceToSettlement, currentOptions.densityMultiplier, densityMultiplier, scaleMultiplier, currentOptions.minRadius, currentOptions.maxRadius, currentOptions.scavengingMultiplier, currentOptions.deteriorationMultiplier); Debug.Log(Debug.Scatter, "Spawning {0} ruin chunks", num); BaseGen.globalSettings.map = map; bool shouldUnpause = false; Find.TickManager.Pause(); if (!Find.TickManager.Paused) { Find.TickManager.TogglePaused(); shouldUnpause = true; } CoverageMap coverageMap = CoverageMap.EmptyCoverageMap(map); for (int i = 0; i < num; i++) { try { //We use copy of scatteroptions because each scatteroptions represents separate chunk with separate location, size, maps, etc. //should use struct instead? is it compatible with IExposable? ResolveParams rp = default(ResolveParams); List <AbstractDefenderForcesGenerator> generators = new List <AbstractDefenderForcesGenerator>(); if (Rand.Chance(currentOptions.hostileChance)) { if (Rand.Chance(0.8f)) { generators = new List <AbstractDefenderForcesGenerator> { new AnimalInhabitantsForcesGenerator() }; } else { generators = new List <AbstractDefenderForcesGenerator> { new MechanoidsForcesGenerator(0) }; } } rp.faction = Find.FactionManager.OfAncientsHostile; var center = CellFinder.RandomNotEdgeCell(10, map); rp.rect = new CellRect(center.x, center.z, 1, 1); //after generation will be extended to a real size RuinsScatterer.Scatter(rp, currentOptions.Copy(), coverageMap, generators); } catch { Debug.Warning(Debug.Scatter, "Could not scatter a single ruins chunk."); } } if (shouldUnpause) { //Debug.Message("Finished spawning, unpausing"); Find.TickManager.TogglePaused(); } } }
public override void Resolve(ResolveParams rp) { ScatterOptions options = rp.GetCustom <ScatterOptions>(Constants.ScatterOptions); if (options == null) { return; } //Debug.Message("Loading blueprint of size {0} - {1} to deploy at {2}, {3}", options.minRadius, options.maxRadius, rp.rect.minX, rp.rect.minZ); Blueprint bp = null; Map map = BaseGen.globalSettings.map; //probably should split scattering options into several distinct objects string filename = options.blueprintFileName; if (string.IsNullOrEmpty(filename)) { bp = BlueprintFinder.FindRandomBlueprintWithParameters(out filename, options.minimumAreaRequired, options.minimumDensityRequired, 15, removeNonQualified: true); if (string.IsNullOrEmpty(filename)) { //still null = no suitable blueprints, fail. return; } } if (!options.shouldLoadPartOnly) //should not cut => load whole { if (bp == null) //if not loaded yet { bp = BlueprintLoader.LoadWholeBlueprintAtPath(filename); } } else { int radius = Rand.Range(options.minRadius, options.maxRadius); if (bp == null) { bp = BlueprintLoader.LoadWholeBlueprintAtPath(filename).RandomPartCenteredAtRoom(new IntVec3(radius, 0, radius)); } else { bp = bp.RandomPartCenteredAtRoom(new IntVec3(radius, 0, radius)); } } if (bp == null) { Debug.Warning(Debug.Scatter, "Blueprint is still null after attempting to load any qualifying, returning"); return; } bp.CutIfExceedsBounds(map.Size); // Here we have our blueprint loaded and ready to action. Doing stuff: BlueprintPreprocessor.ProcessBlueprint(bp, options); //Preprocess: remove missing and unnecessary items according to options bp.FindRooms(); //Traverse blueprint and construct rooms map options.roomMap = bp.wallMap; //Debug.PrintIntMap(bp.wallMap, delta: 1); BlueprintTransferUtility btu = new BlueprintTransferUtility(bp, map, rp); //prepare blueprint transferrer btu.RemoveIncompatibleItems(); //remove incompatible items bp.UpdateBlueprintStats(true); //Update total cost, items count, etc DeteriorationProcessor.Process(bp, options); //create deterioration maps and do deterioration ScavengingProcessor sp = new ScavengingProcessor(); sp.RaidAndScavenge(bp, options); //scavenge remaining items according to scavenge options btu.Transfer(); //transfer blueprint List <AbstractDefenderForcesGenerator> generators = rp.GetCustom <List <AbstractDefenderForcesGenerator> >(Constants.ForcesGenerators); if (generators != null) { foreach (AbstractDefenderForcesGenerator generator in generators) { generator.GenerateForces(map, rp); } } if (options.shouldAddFilth) { btu.AddFilthAndRubble(); //add filth and rubble //rp.GetCustom<CoverageMap>(Constants.CoverageMap).DebugPrint(); } }