Example #1
0
        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);
                    }
                }
            }
        }
        void Deteriorate()
        {
            int itemsCount   = 0;
            int removedCount = 0;

            for (int x = 0; x < blueprint.width; x++)
            {
                for (int z = 0; z < blueprint.height; z++)
                {
                    bool            hadWall      = false;
                    bool            retainedWall = false;
                    List <ItemTile> items        = blueprint.itemsMap[x, z];
                    itemsCount += items.Count;

                    if (!Rand.Chance(terrainIntegrity[x, z]))
                    {
                        blueprint.terrainMap[x, z] = null;
                    }

                    if (blueprint.terrainMap[x, z] == null)
                    {
                        blueprint.roofMap[x, z] = false; //no terrain - no roof. just is case. to be sure there won't be hanging roof islands floating in the air.
                    }

                    float           itemsChance = itemsIntegrity[x, z] * (1.0f - options.deteriorationMultiplier);
                    List <ItemTile> newItems    = new List <ItemTile>();
                    if (itemsChance > 0 && itemsChance < 1)
                    {
                        blueprint.roofMap[x, z] = Rand.Chance(itemsChance * 0.3f); //roof will most likely collapse everywhere

                        foreach (ItemTile item in items)
                        {
                            if (options.shouldKeepDefencesAndPower && item.defName.ToLower().Contains("conduit"))   //do not deteriorate conduits if preparing
                            {
                                newItems.Add(item);
                                continue;
                            }

                            if (item.isWall)
                            {
                                hadWall = true;
                            }
                            if (Rand.Chance(itemsChance))
                            {
                                if (item.isWall)
                                {
                                    retainedWall = true;
                                }
                                newItems.Add(item);
                            }

                            var thingDef = DefDatabase <ThingDef> .GetNamedSilentFail(item.defName);

                            if (thingDef != null)
                            {
                                item.stackCount = Rand.Range(1, Math.Min(thingDef.stackLimit, item.stackCount));
                            }
                        }
                        removedCount += items.Count - newItems.Count;
                    }
                    if (itemsChance < 1)
                    {
                        blueprint.itemsMap[x, z] = newItems; //will be empty if chance is 0
                        if (itemsChance <= 0)
                        {
                            blueprint.RemoveWall(x, z);
                        }
                    }

                    if (hadWall && !retainedWall)
                    {
                        blueprint.RemoveWall(x, z);
                    }
                }
            }
            //Debug.Message("Deteriorated {0}/{1}", removedCount, itemsCount);
        }
Example #3
0
        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();
            }
        }
Example #4
0
        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 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);
        }