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()); } }
private void LoadBlueprint() { Debug.Log(Debug.BlueprintTransfer, "Loading blueprint at path {0}", snapshotName); string deflatedName = snapshotName; if (Path.GetExtension(snapshotName).Equals(".bp")) { deflatedName = snapshotName + ".xml"; if (!File.Exists(deflatedName)) { string data = Compressor.UnzipFile(snapshotName); File.WriteAllText(deflatedName, data); } } XmlDocument snapshot = new XmlDocument(); snapshot.Load(deflatedName); XmlNodeList elemList = snapshot.GetElementsByTagName("cell"); int blueprintWidth = int.Parse(snapshot.FirstChild.Attributes["width"].Value); int blueprintHeight = int.Parse(snapshot.FirstChild.Attributes["height"].Value); Version blueprintVersion = new Version(snapshot.FirstChild?.Attributes["version"]?.Value ?? "0.0.0.0"); blueprint = new Blueprint(blueprintWidth, blueprintHeight, blueprintVersion); //Snapshot year is an in-game year when snapshot was taken. Thus, all corpse ages, death times, art events and so on are in between of 5500 and [snapshotYear] blueprint.snapshotYear = int.Parse(snapshot.FirstChild.Attributes["inGameYear"]?.Value ?? "5600"); //To prevent artifacts from future we need to shift all dates by some number to the past by _at_least_ (snaphotYear - 5500) years int itemNodes = 0; int terrainNodes = 0; foreach (XmlNode cellNode in elemList) { int x = int.Parse(cellNode.Attributes["x"].Value); int z = int.Parse(cellNode.Attributes["z"].Value); blueprint.itemsMap[x, z] = new List <ItemTile>(); foreach (XmlNode cellElement in cellNode.ChildNodes) { try { if (cellElement.Name.Equals("terrain")) { terrainNodes++; TerrainTile terrain = new TerrainTile(cellElement); terrain.location = new IntVec3(x, 0, z); blueprint.terrainMap[x, z] = terrain; } else if (cellElement.Name.Equals("item")) { itemNodes++; ItemTile tile = new ItemTile(cellElement); //replace all collapsed rocks with walls if (tile.defName == ThingDefOf.CollapsedRocks.defName) { tile = ItemTile.WallReplacementItemTile(tile.location); } if (tile.defName == ThingDefOf.MinifiedThing.defName && (tile.innerItems?.Count() ?? 0) == 0) { continue; //skip minified things with no inner items } //Trying to load corresponding definition to check if the object is accessible ThingDef thingDef = DefDatabase <ThingDef> .GetNamed(tile.defName, false); if (thingDef != null) { if (thingDef.fillPercent == 1.0f || tile.isWall || tile.isDoor) { blueprint.wallMap[x, z] = -1; //place wall } tile.stackCount = Math.Min(thingDef.stackLimit, tile.stackCount); //limit stack to max stack size to correctly calculate weight and cost later tile.location = new IntVec3(x, 0, z); blueprint.itemsMap[x, z].Add(tile); //save item if it's def is valid. } else { if (tile.isDoor) //replacing unavailable door with abstract default door { tile.defName = ThingDefOf.Door.defName; tile.location = new IntVec3(x, 0, z); blueprint.itemsMap[x, z].Add(tile); //replacement door is ok } else if (tile.isWall || tile.defName.ToLower().Contains("wall")) //replacing unavailable impassable 100% filling block (which was likely a wall) with a wall { tile.defName = ThingDefOf.Wall.defName; tile.location = new IntVec3(x, 0, z); blueprint.itemsMap[x, z].Add(tile); //now it's a wall } else if (tile.defName == "Corpse") { tile.location = new IntVec3(x, 0, z); blueprint.itemsMap[x, z].Add(tile); // corpse is ok } } } else if (cellElement.Name.Equals("roof")) { blueprint.roofMap[x, z] = true; } } catch (Exception) { //ignore invalid or unloadable cells } } } }
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); }