static public ItemTile DefaultDoorItemTile(IntVec3 location) { ItemTile tile = new ItemTile(); tile.defName = ThingDefOf.Door.defName; tile.stuffDef = ThingDefOf.WoodLog.defName; tile.isDoor = true; tile.isWall = false; tile.location = location; tile.stackCount = 1; tile.rot = 0; tile.cost = 0; tile.weight = 1.0f; return(tile); }
public static ItemTile WallReplacementItemTile(IntVec3 location) { ItemTile tile = new ItemTile { defName = ThingDefOf.Wall.defName, stuffDef = ThingDefOf.BlocksGranite.defName, isDoor = false, isWall = true, stackCount = 1, rot = 0, cost = 0, weight = 1.0f, location = location }; return(tile); }
private void ProcessTile(ItemTile itemTile, IntVec3 pos) { ThingDef itemDef = DefDatabase <ThingDef> .GetNamed(itemTile.defName, false); if (itemDef == null) { return; } ItemStatRecord stats = null; if (itemStats.ContainsKey(itemTile.defName)) { stats = itemStats[itemTile.defName]; } else { stats = new ItemStatRecord(); itemStats[itemTile.defName] = stats; } stats.stacksCount++; stats.totalCount += itemTile.stackCount; stats.cost += itemTile.cost; result.totalItemsCost += itemTile.cost; result.occupiedTilesCount++; result.itemsCount += itemTile.stackCount; if (itemTile.isWall) { result.wallLength++; } var message = itemTile.defName + " "; if (itemDef.alwaysHaulable) { message += "is haulable "; result.haulableStacksCount++; result.haulableItemsCount += itemTile.stackCount; result.haulableItemsCost += itemTile.cost; } string lowerName = itemTile.defName.ToLower(); if (itemDef.IsShell || itemDef.IsRangedWeapon || lowerName.Contains("turret") || lowerName.Contains("cannon") || lowerName.Contains("gun") /* || lowerName.Contains("laser") || lowerName.Contains("marauder") and obelisk and punisher when RimAtomics will add faction split*/) { result.militaryItemsCount++; message += "military "; if (itemDef.building != null) { message += "non-nul building "; result.defensiveItemsCount++; if (itemDef.building.IsTurret) { message += "turret "; } } } if (itemDef.HasComp(typeof(CompMannable))) { result.mannableCount++; } if (itemDef.IsWorkTable) { message += "worktable "; result.productionItemsCount++; } if (itemDef.IsBed) { message += "bed "; result.bedsCount++; } if (blueprint.wallMap[pos.x, pos.z] > 1 || blueprint.roofMap[pos.x, pos.z] == true) { result.itemsInside++; } if (!itemTile.isWall && !itemTile.isDoor && itemTile.cost > 100) { // Debug.Message(message); } }
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 ItemTile(XmlNode node) { defName = node.Attributes["def"].Value; itemXml = node.OuterXml; XmlAttribute stuffDefAttribute = node.Attributes["stuffDef"]; if (stuffDefAttribute != null) { stuffDef = stuffDefAttribute.Value; } corpseDeathTime = long.Parse(node.Attributes["timeOfDeath"]?.Value ?? "0"); XmlAttribute stackCountAttribute = node.Attributes["stackCount"]; if (stackCountAttribute != null) { stackCount = int.Parse(s: stackCountAttribute.Value); } else { stackCount = 1; } XmlAttribute doorAttribute = node.Attributes["isDoor"]; if (doorAttribute != null || defName.ToLower().Contains("door")) { isDoor = true; } XmlAttribute textAttribute = node.Attributes["text"]; if (textAttribute != null) { attachedText = textAttribute.Value; } XmlAttribute wallAttribute = node.Attributes["actsAsWall"]; if (wallAttribute != null || defName.ToLower().Contains("wall") || defName.Equals("Cooler") || defName.Equals("Vent")) //compatibility { isWall = true; } XmlAttribute rotAttribute = node.Attributes["rot"]; if (rotAttribute != null) { rot = int.Parse(s: rotAttribute.Value); } else { rot = 0; } XmlAttribute artTitleAttribute = node.Attributes["artTitle"]; if (artTitleAttribute != null) { if (art == null) { art = new ItemArt(); } art.title = Uri.UnescapeDataString(artTitleAttribute.Value); } XmlAttribute artAuthorAttribute = node.Attributes["artAuthor"]; if (artAuthorAttribute != null) { if (art == null) { art = new ItemArt(); } art.author = Uri.UnescapeDataString(artAuthorAttribute.Value); } XmlAttribute artDescriptionAttribute = node.Attributes["artDescription"]; if (artDescriptionAttribute != null) { if (art == null) { art = new ItemArt(); } art.text = Uri.UnescapeDataString(artDescriptionAttribute.Value); } if (node.HasChildNodes) { List <ItemTile> innerItems = new List <ItemTile>(); foreach (XmlNode childNode in node.ChildNodes) { if (childNode.Name.Equals("item")) { ItemTile innerTile = new ItemTile(childNode); innerItems.Add(innerTile); } } if (innerItems.Count > 0) { this.innerItems = innerItems; } } }
private Thing MakeThingFromItemTile(ItemTile itemTile, bool enableLogging = false) { try { if (enableLogging) { //Debug.Message("Trying to create new inner item {0}", itemTile.defName); } if (itemTile.defName.ToLower() == "pawn") { //Debug.Message("Now need to instantiate pawn"); return(MakePawnWithRawXml(itemTile.itemXml)); } if (itemTile.defName.ToLower() == "corpse") { if (itemTile.innerItems != null) { //Debug.Message("Creating corpse"); Pawn p = (Pawn)MakeThingFromItemTile(itemTile.innerItems.First()); Corpse corpse = null; if (p.Corpse != null) { corpse = p.Corpse; } else { corpse = (Corpse)ThingMaker.MakeThing(p.RaceProps.corpseDef); corpse.InnerPawn = p; } corpse.timeOfDeath = (int)(itemTile.corpseDeathTime + (ticksInYear * blueprint.dateShift)); CompRottable rottable = corpse.TryGetComp <CompRottable>(); if (rottable != null) { rottable.RotProgress = ticksInYear * (-blueprint.dateShift); } return(corpse); } return(null); } if (itemTile.defName.ToLower().Contains("corpse") || itemTile.defName.ToLower().Contains("minified")) //should bypass older minified things and corpses { if ((!itemTile.innerItems?.Any()) ?? true) { return(null); } } if (itemTile.defName == "Hive") { return(null); //Ignore hives, probably should add more comprehensive ignore list here. } ThingDef thingDef = DefDatabase <ThingDef> .GetNamed(itemTile.defName, false); //here thingDef is definitely not null because it was checked earlier if (thingDef.category == ThingCategory.Ethereal) { return(null); //don't spawn ethereals like drop pod landing sites and so on } ThingDef stuffDef = null; //but stuff can still be null, or can be missing, so we have to check and use default just in case. if (itemTile.stuffDef != null && thingDef.MadeFromStuff) //some mods may alter thing and add stuff parameter to it. this will result in a bug on a vanilla, so need to double-check here { stuffDef = DefDatabase <ThingDef> .GetNamed(itemTile.stuffDef, false); } if (stuffDef == null) { if (itemTile.isWall && thingDef.MadeFromStuff) { stuffDef = ThingDefOf.BlocksGranite; //walls from modded materials becomes granite walls. } else { stuffDef = GenStuff.DefaultStuffFor(thingDef); } } Thing thing = ThingMaker.MakeThing(thingDef, stuffDef); if (thing != null) { if (itemTile.innerItems != null && thing is IThingHolder) { //Debug.Message("Found inners"); foreach (ItemTile innerTile in itemTile.innerItems) { Thing innerThing = MakeThingFromItemTile(innerTile, true); if (innerThing != null) { ((IThingHolder)thing).GetDirectlyHeldThings().TryAdd(innerThing); } } if (thing.GetInnerIfMinified() == null) { return(null); } } if (thingDef.CanHaveFaction) { thing.SetFaction(rp.faction); } //Check quality and attach art CompQuality q = thing.TryGetComp <CompQuality>(); if (q != null) { byte category = (byte)Math.Abs(Math.Round(Rand.Gaussian(0, 2))); if (itemTile.art != null) { if (category > 6) { category = 6; } q.SetQuality((QualityCategory)category, ArtGenerationContext.Outsider); //setquality resets art, so it should go before actual setting art thing.TryGetComp <CompArt>()?.InitializeArt(itemTile.art.author, itemTile.art.title, itemTile.art.TextWithDatesShiftedBy(blueprint.dateShift)); } else { if (category > 6) { category = 6; } q.SetQuality((QualityCategory)category, ArtGenerationContext.Outsider); } } if (itemTile.stackCount > 1) { thing.stackCount = itemTile.stackCount; //Spoil things that can be spoiled. You shouldn't find a fresh meat an the old ruins. CompRottable rottable = thing.TryGetComp <CompRottable>(); if (rottable != null) { //if deterioration degree is > 0.5 you definitely won't find any food. //anyway, there is a chance that you also won't get any food even if deterioriation is relatively low. animalr, raiders, you know. if (options.canHaveFood) { rottable.RotProgress = (Rand.Value * 0.5f + options.deteriorationMultiplier) * (rottable.PropsRot.TicksToRotStart); } else { rottable.RotProgress = rottable.PropsRot.TicksToRotStart + 1; } } } if (itemTile.attachedText != null && thing is ThingWithComps) { ThingWithComps thingWithComps = thing as ThingWithComps; Type CompTextClass = Type.GetType("SaM.CompText, Signs_and_Memorials"); if (CompTextClass != null) { System.Object textComp = null; for (int i = 0; i < thingWithComps.AllComps.Count; i++) { var val = thingWithComps.AllComps[i]; if (val.GetType() == CompTextClass) { textComp = val; } } //var textComp = Activator.CreateInstance(CompTextClass); if (textComp != null) { textComp?.GetType()?.GetField("text").SetValue(textComp, itemTile.attachedText); } //thingWithComps. } } if (thing is UnfinishedThing) { ((UnfinishedThing)thing).workLeft = 10000; ((UnfinishedThing)thing).Creator = Find.WorldPawns.AllPawnsAliveOrDead.RandomElement(); } //Substract some hit points. Most lilkely below 400 (to make really strudy structures stay almost untouched. No more 1% beta poly walls) var maxDeltaHP = 0; if (!options.forceFullHitPoints) { maxDeltaHP = Math.Min(thing.MaxHitPoints - 1, (int)Math.Abs(Rand.Gaussian(0, 200))); } thing.HitPoints = thing.MaxHitPoints - Rand.Range(0, maxDeltaHP); //Forbid haulable stuff if (thing.def.EverHaulable) { thing.SetForbidden(true, false); } if (thing is Building_Storage) { ((Building_Storage)thing).settings.Priority = StoragePriority.Unstored; } } return(thing); } catch (Exception e) { Debug.Log(Debug.BlueprintTransfer, "Failed to spawn item {0} because of {1}", itemTile.defName, e); return(null); } }
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(); } }