public static Blueprint FindRandomBlueprintWithParameters(out string filename, int minArea = 100, float minDensity = 0.01f, int minCost = 0, int maxAttemptsCount = 5, bool removeNonQualified = false) { Blueprint bp = null; filename = null; int attemptCount = 0; while (attemptCount < maxAttemptsCount) { attemptCount++; filename = SnapshotStoreManager.Instance.RandomSnapshotFilename(); bp = BlueprintLoader.LoadWholeBlueprintAtPath(filename); if (bp == null) { Debug.Error(Debug.Store, "Corrupted XML at path {0}, removing", filename); SnapshotStoreManager.Instance.RemoveBlueprintWithName(filename); continue; } bp.UpdateBlueprintStats(includeCost: true); //Debug.Message("size: {0}x{1} (needed {2}). density {3} (needed {4}). cost {5} (needed {6})", bp.width, bp.height, minArea, bp.itemsDensity, minDensity, bp.totalCost, minCost); if (bp.height * bp.width > minArea && bp.itemsDensity > minDensity && bp.totalCost >= minCost) { //Debug.Message("Qualified, using."); return(bp); } else if (removeNonQualified) { Debug.Warning(Debug.Store, "Non-qualified XML at path {0}, removing", filename); SnapshotStoreManager.Instance.RemoveBlueprintWithName(filename); } } filename = null; return(null); }
public void Analyze() { Debug.Log("analyzing blueprint {0} with options {1}", blueprint, options); blueprint.FindRooms(); Debug.Log("Rooms found"); BlueprintPreprocessor.ProcessBlueprint(blueprint, options); Debug.Log("Blueprint processed"); result = new BlueprintAnalyzerResult(); result.totalArea = blueprint.width * blueprint.height; result.roomsCount = blueprint.roomAreas.Count() - 2; result.internalArea = 0; for (int index = 2; index < blueprint.roomAreas.Count; index++) { result.internalArea += blueprint.roomAreas[index]; } blueprint.UpdateBlueprintStats(includeCost: true); Debug.Log(Debug.Analyzer, "Analyzing map"); for (int y = 0; y < blueprint.height; y++) { for (int x = 0; x < blueprint.width; x++) { List <ItemTile> tiles = blueprint.itemsMap[x, y] ?? new List <ItemTile>(); foreach (ItemTile itemTile in tiles) { ProcessTile(itemTile, new IntVec3(x, 0, y)); } } } this.mannableCount = result.mannableCount; determinedType = supposedType(); Debug.Log(Debug.Analyzer, "Type is {0} by {1}", determinedType, result.ToString()); }
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); } } } }
//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 RemoveIncompatibleItems() { //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 if (blueprint.roofMap == null) { Debug.Log(Debug.BlueprintTransfer, "Trying to process blueprint with empty roof map"); } if (map == null) { Debug.Log(Debug.BlueprintTransfer, "Trying to process blueprint but map is still null"); } try { int totalItems = 0; int removedItems = 0; for (int x = 0; x < blueprint.width; x++) { for (int z = 0; z < blueprint.height; z++) { Debug.Extra(Debug.BlueprintTransfer, "Starting cell {0} {1}...", x, z); if (blueprint.itemsMap[x, z] == null) { blueprint.itemsMap[x, z] = new List <ItemTile>(); } //to make thngs easier add empty list to every cell IntVec3 mapLocation = new IntVec3(x + mapOriginX, 0, z + mapOriginZ); if (!mapLocation.InBounds(map)) { continue; } 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; //no terrain def means terrain can't be generated. terrain = null; } } TerrainDef existingTerrain = map.terrainGrid?.TerrainAt(mapLocation); if (existingTerrain != null && terrainDef != null && existingTerrain.affordances != null && terrainDef.terrainAffordanceNeeded != null && !existingTerrain.affordances.Contains(terrainDef.terrainAffordanceNeeded)) { terrainDef = null; blueprint.terrainMap[x, z] = null; //erase terrain if underlying terrain can't support it. blueprint.roofMap[x, z] = false; //removing roof as well just in case } Debug.Extra(Debug.BlueprintTransfer, "Preprocessed cell {0} {1}, moving to items...", x, z); List <ItemTile> itemsToRemove = new List <ItemTile>(); foreach (ItemTile item in items) { totalItems++; ThingDef thingDef = DefDatabase <ThingDef> .GetNamed(item.defName, false); if (thingDef == null) { itemsToRemove.Add(item); continue; } Debug.Extra(Debug.BlueprintTransfer, "Making thorough check for thing {0}", item.defName); if (!options.overwritesEverything && thingDef.terrainAffordanceNeeded != null) { if (thingDef.EverTransmitsPower && options.shouldKeepDefencesAndPower) { continue; //ignore affordances for power transmitters if we need to keep defence systems } if (terrainDef != null && terrainDef.terrainAffordanceNeeded != null && existingTerrain.affordances.Contains(terrainDef.terrainAffordanceNeeded)) { if (!terrainDef.affordances.Contains(thingDef.terrainAffordanceNeeded)) //if new terrain can be placed over existing terrain, checking if an item can be placed over a new terrain { itemsToRemove.Add(item); blueprint.roofMap[x, z] = false; } } else { if (!(existingTerrain.affordances?.Contains(thingDef.terrainAffordanceNeeded) ?? true)) //otherwise checking if the item can be placed over the existing terrain. { itemsToRemove.Add(item); blueprint.roofMap[x, z] = false; } } } } foreach (ItemTile item in itemsToRemove) { if (item.isWall || item.isDoor) { blueprint.RemoveWall(item.location.x, item.location.z); } items.Remove(item); removedItems++; } } } Debug.Extra(Debug.BlueprintTransfer, "Finished check, recalculating stats"); blueprint.UpdateBlueprintStats(true); Debug.Log(Debug.BlueprintTransfer, "Blueprint transfer utility did remove {0}/{1} incompatible items. New cost: {2}", removedItems, totalItems, blueprint.totalCost); } catch (Exception e) { Debug.Error(Debug.BlueprintTransfer, "Exception while trying to cleanup blueprint details. This should not normally happen, so please report this case: {0}", e.ToString()); } }
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(); } }
public void RemoveIncompatibleItems() { //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 int totalItems = 0; int removedItems = 0; for (int x = 0; x < blueprint.width; x++) { for (int z = 0; z < blueprint.height; z++) { if (blueprint.itemsMap[x, z] == null) { blueprint.itemsMap[x, z] = new List <ItemTile>(); } //to make thngs easier add empty list to every cell IntVec3 mapLocation = new IntVec3(x + mapOriginX, 0, z + mapOriginZ); if (!mapLocation.InBounds(map)) { continue; } 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; //no terrain def means terrain can't be generated. terrain = null; } } TerrainDef existingTerrain = map.terrainGrid.TerrainAt(mapLocation); if (terrainDef != null && terrainDef.terrainAffordanceNeeded != null && !existingTerrain.affordances.Contains(terrainDef.terrainAffordanceNeeded)) { terrainDef = null; blueprint.terrainMap[x, z] = null; //erase terrain if underlying terrain can't support it. blueprint.roofMap[x, z] = false; //removing roof as well just in case } List <ItemTile> itemsToRemove = new List <ItemTile>(); foreach (ItemTile item in items) { totalItems++; ThingDef thingDef = DefDatabase <ThingDef> .GetNamed(item.defName, false); if (thingDef == null) { itemsToRemove.Add(item); continue; } if (!options.overwritesEverything && thingDef.terrainAffordanceNeeded != null) { if (thingDef.EverTransmitsPower && options.shouldKeepDefencesAndPower) { continue; //ignore affordances for power transmitters if we need to keep defence systems } if (terrainDef != null && terrainDef.terrainAffordanceNeeded != null && existingTerrain.affordances.Contains(terrainDef.terrainAffordanceNeeded)) { if (!terrainDef.affordances.Contains(thingDef.terrainAffordanceNeeded)) //if new terrain can be placed over existing terrain, checking if an item can be placed over a new terrain { itemsToRemove.Add(item); blueprint.roofMap[x, z] = false; } } else { if (!existingTerrain.affordances.Contains(thingDef.terrainAffordanceNeeded)) //otherwise checking if the item can be placed over the existing terrain. { itemsToRemove.Add(item); blueprint.roofMap[x, z] = false; } } } } foreach (ItemTile item in itemsToRemove) { if (item.isWall || item.isDoor) { blueprint.RemoveWall(item.location.x, item.location.z); } items.Remove(item); removedItems++; } } } blueprint.UpdateBlueprintStats(true); //Debug.Message("Blueprint transfer utility did remove {0}/{1} incompatible items. New cost: {2}", removedItems, totalItems, blueprint.totalCost); }