public static CoverageMap EmptyCoverageMap(Map map) { CoverageMap cm = new CoverageMap(); cm.coverageMap = new bool[map.Size.x, map.Size.z]; return(cm); }
//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 void Transfer(CoverageMap coverageMap) { //Planting blueprint float totalCost = 0; int transferredTerrains = 0; int transferredTiles = 0; int totalTerrains = 0; int totalItems = 0; //update rect to actual placement rect using width and height rp.rect = new CellRect(mapOriginX, mapOriginZ, blueprint.width, blueprint.height); Debug.Extra(Debug.BlueprintTransfer, "Clearing map..."); for (int z = 0; z < blueprint.height; z++) { for (int x = 0; x < blueprint.width; x++) { try { IntVec3 mapLocation = new IntVec3(x + mapOriginX, 0, z + mapOriginZ); //Check if thepoint is in allowed bounds of the map if (!mapLocation.InBounds(map) || mapLocation.InNoBuildEdgeArea(map)) { continue; //ignore invalid cells } if (options.overwritesEverything || Rand.Chance(0.6f)) { if (blueprint.terrainMap[x, z] != null || blueprint.itemsMap[x, z].Count > 0 || blueprint.wallMap[x, z] > 1) { ClearCell(mapLocation, map, true); } } } catch (Exception e) { Debug.Warning(Debug.BlueprintTransfer, "Failed to clean cell at {0}, {1} because of {2}", x, z, e); } } } Debug.Extra(Debug.BlueprintTransfer, "Transferring map objects"); for (int z = 0; z < blueprint.height; z++) { for (int x = 0; x < blueprint.width; x++) { IntVec3 mapLocation = new IntVec3(x + mapOriginX, 0, z + mapOriginZ); if (coverageMap != null) { if (coverageMap.isMarked(mapLocation.x, mapLocation.z) == true) //cell was used earlier { continue; //skip already covered tiles } else { if (blueprint.wallMap[x, z] > 1 || blueprint.wallMap[x, z] == -1) { coverageMap.Mark(mapLocation.x, mapLocation.z); //mark cell as used } } } //Check if thepoint is in allowed bounds of the map if (!mapLocation.InBounds(map) || mapLocation.InNoBuildEdgeArea(map)) { continue; //ignore invalid cells } try { //Construct terrain if some specific terrain stored in the blueprint if (blueprint.terrainMap[x, z] != null) { totalTerrains++; TerrainDef blueprintTerrain = TerrainDef.Named(blueprint.terrainMap[x, z].defName); if (!map.terrainGrid.TerrainAt(mapLocation).IsWater) { map.terrainGrid.SetTerrain(mapLocation, blueprintTerrain); totalCost += blueprint.terrainMap[x, z].cost; transferredTerrains++; } } //construct roof evetywhere if we doing complete transfer (ignoring outside: room with index 1). if (blueprint.roofMap[x, z] == true && options.overwritesEverything && blueprint.wallMap[x, z] != 1) { map.roofGrid.SetRoof(mapLocation, RoofDefOf.RoofConstructed); } Debug.Extra(Debug.BlueprintTransfer, "Transferred terrain and roof at cell ({0}, {1})", x, z); } catch (Exception e) { Debug.Warning(Debug.BlueprintTransfer, "Failed to transfer terrain {0} at {1}, {2} because of {3}", blueprint.terrainMap[x, z].defName, x, z, e); } //Add items if (blueprint.itemsMap[x, z] != null && blueprint.itemsMap[x, z].Count > 0 /* && cellUsed[mapLocation.x, mapLocation.z] == false*/) { totalItems += blueprint.itemsMap[x, z].Count; foreach (ItemTile itemTile in blueprint.itemsMap[x, z]) { Debug.Extra(Debug.BlueprintTransfer, "Creating thing {2} at cell ({0}, {1})", x, z, itemTile.defName); Thing thing = MakeThingFromItemTile(itemTile); if (thing != null) { try { Rot4 rotation = new Rot4(itemTile.rot); //check if there is anything we have to despawn in order to spawn our new item //we have to do this, because many dubs bad hygiene items enter endless cycle when removed during mapgen. foreach (IntVec3 occupiedCell in GenAdj.CellsOccupiedBy(mapLocation, rotation, thing.def.Size)) { foreach (Thing existingItem in map.thingGrid.ThingsAt(occupiedCell).ToList()) { if (GenSpawn.SpawningWipes(thing.def, existingItem.def)) { if (thing.def.thingClass.ToString().Contains("DubsBadHygiene")) { throw new Exception("Can't spawn item because it will destroy Dubs Bad Hygiene Item and it will lead to app freeze."); } existingItem.Destroy(DestroyMode.Vanish); } } } GenSpawn.Spawn(thing, mapLocation, map, rotation); Debug.Extra(Debug.BlueprintTransfer, "Spawned"); try { switch (thing.def.tickerType) { case TickerType.Never: break; case TickerType.Normal: thing.Tick(); break; case TickerType.Long: thing.TickLong(); break; case TickerType.Rare: thing.TickRare(); break; } //Debug.Message("Ticked"); } catch (Exception e) { Debug.Log(Debug.BlueprintTransfer, "Exception while tried to perform tick for {0} of cost {1}, retrhrowing...", thing.def.defName, itemTile.cost); thing.Destroy(); throw e; } //Debug.Message("Setting up props"); //Breakdown breakdownables: it't yet impossible to silently breakdown an item which is not spawned. CompBreakdownable b = thing.TryGetComp <CompBreakdownable>(); if (b != null && options.enableDeterioration) { if (Rand.Chance(0.8f)) { b.DoBreakdown(); } } //reduce HP for haulable things in water if (thing.def.EverHaulable) { TerrainDef t = map.terrainGrid.TerrainAt(mapLocation); if (t != null && t.IsWater) { thing.HitPoints = (thing.HitPoints - 10) / Rand.Range(5, 20) + Rand.Range(1, 10); //things in marsh or river are really in bad condition } } Debug.Extra(Debug.BlueprintTransfer, "Item completed"); transferredTiles++; totalCost += itemTile.cost; } catch (Exception e) { Debug.Warning(Debug.BlueprintTransfer, "Failed to spawn item {0} of cost {1} because of exception {2}", thing, itemTile.cost, e.Message); //ignore } } } } } } Debug.Log(Debug.BlueprintTransfer, "Finished transferring"); if (options.shouldKeepDefencesAndPower) { RestoreDefencesAndPower(); } options.uncoveredCost = totalCost; Debug.Log(Debug.BlueprintTransfer, "Transferred blueprint of size {0}x{1}, age {2}, total cost of approximately {3}. Items: {4}/{5}, terrains: {6}/{7}", blueprint.width, blueprint.height, -blueprint.dateShift, totalCost, transferredTiles, totalItems, transferredTerrains, totalTerrains); }
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 void Transfer() { //Planting blueprint float totalCost = 0; int transferredTerrains = 0; int transferredTiles = 0; int totalTerrains = 0; int totalItems = 0; //update rect to actual placement rect using width and height rp.rect = new CellRect(mapOriginX, mapOriginZ, blueprint.width, blueprint.height); CoverageMap coverageMap = null; rp.TryGetCustom <CoverageMap>(Constants.CoverageMap, out coverageMap); for (int z = 0; z < blueprint.height; z++) { for (int x = 0; x < blueprint.width; x++) { IntVec3 mapLocation = new IntVec3(x + mapOriginX, 0, z + mapOriginZ); //Check if thepoint is in allowed bounds of the map if (!mapLocation.InBounds(map) || mapLocation.InNoBuildEdgeArea(map)) { continue; //ignore invalid cells } if (options.overwritesEverything || Rand.Chance(0.6f)) { if (blueprint.terrainMap[x, z] != null || blueprint.itemsMap[x, z].Count > 0 || blueprint.wallMap[x, z] > 1) { ClearCell(mapLocation, map, true); } } } } for (int z = 0; z < blueprint.height; z++) { for (int x = 0; x < blueprint.width; x++) { IntVec3 mapLocation = new IntVec3(x + mapOriginX, 0, z + mapOriginZ); if (coverageMap != null) { if (coverageMap.isMarked(mapLocation.x, mapLocation.z) == true) //cell was used earlier { continue; //skip already covered tiles } else { if (blueprint.wallMap[x, z] > 1 || blueprint.wallMap[x, z] == -1) { coverageMap.Mark(mapLocation.x, mapLocation.z); //mark cell as used } } } //Check if thepoint is in allowed bounds of the map if (!mapLocation.InBounds(map) || mapLocation.InNoBuildEdgeArea(map)) { continue; //ignore invalid cells } //Construct terrain if some specific terrain stored in the blueprint if (blueprint.terrainMap[x, z] != null) { totalTerrains++; TerrainDef blueprintTerrain = TerrainDef.Named(blueprint.terrainMap[x, z].defName); if (!map.terrainGrid.TerrainAt(mapLocation).IsWater) { map.terrainGrid.SetTerrain(mapLocation, blueprintTerrain); totalCost += blueprint.terrainMap[x, z].cost; transferredTerrains++; } } //construct roof evetywhere if we doing complete transfer (ignoring outside: room with index 1). if (blueprint.roofMap[x, z] == true && options.overwritesEverything && blueprint.wallMap[x, z] != 1) { map.roofGrid.SetRoof(mapLocation, RoofDefOf.RoofConstructed); } //Add items if (blueprint.itemsMap[x, z] != null && blueprint.itemsMap[x, z].Count > 0 /* && cellUsed[mapLocation.x, mapLocation.z] == false*/) { totalItems += blueprint.itemsMap[x, z].Count; bool cellIsAlreadyCleared = false; foreach (ItemTile itemTile in blueprint.itemsMap[x, z]) { if (!cellIsAlreadyCleared) //first item to be spawned should also clear place for itself. we can't do it beforehand because we don't know if it will be able and get a chance to be spawned. { bool forceCleaning = (blueprint.wallMap[x, z] > 1) && Rand.Chance(0.9f); if (!ClearCell(mapLocation, map, forceCleaning)) { break; //if cell was not cleared successfully -> break things placement cycle and move on to the next item } else { cellIsAlreadyCleared = true; } } Thing thing = MakeThingFromItemTile(itemTile); if (thing != null) { try { GenSpawn.Spawn(thing, mapLocation, map, new Rot4(itemTile.rot)); try { switch (thing.def.tickerType) { case TickerType.Never: break; case TickerType.Normal: thing.Tick(); break; case TickerType.Long: thing.TickLong(); break; case TickerType.Rare: thing.TickRare(); break; } //Debug.Message("Ticked"); } catch (Exception e) { //Debug.Message("Exception while tried to perform tick for {0} of cost {1}", thing.def.defName, itemTile.cost); thing.Destroy(); throw e; } //Debug.Message("Setting up props"); //Breakdown breakdownables: it't yet impossible to silently breakdown an item which is not spawned. CompBreakdownable b = thing.TryGetComp <CompBreakdownable>(); if (b != null && options.enableDeterioration) { if (Rand.Chance(0.8f)) { b.DoBreakdown(); } } //reduce HP for haulable things in water if (thing.def.EverHaulable) { TerrainDef t = map.terrainGrid.TerrainAt(mapLocation); if (t != null && t.IsWater) { thing.HitPoints = (thing.HitPoints - 10) / Rand.Range(5, 20) + Rand.Range(1, 10); //things in marsh or river are really in bad condition } } transferredTiles++; totalCost += itemTile.cost; } catch (Exception e) { //Debug.Message("Failed to spawn item {0} of cost {1} because of exception", thing, itemTile.cost); //ignore } } } } } if (options.shouldKeepDefencesAndPower) { RestoreDefencesAndPower(); } options.uncoveredCost = totalCost; } Debug.Log(Debug.BlueprintTransfer, "Transferred blueprint of size {0}x{1}, age {2}, total cost of approximately {3}. Items: {4}/{5}, terrains: {6}/{7}", blueprint.width, blueprint.height, -blueprint.dateShift, totalCost, transferredTiles, totalItems, transferredTerrains, totalTerrains); }