Main Features / Description
This is a mod for adding a level of realism to placement of "artificial" tiles, which in this mod are tiles like stone path, concrete, refined concrete, and the hazard variants. It will also include various other mod added tiles, such as ones that have any of the words "road", "asphalt", "gravel", or "floor" in their names. Landfill is explicitly excluded.
These "artificial" tiles will not be placeable on/under trees (except dead), cliffs, biter nests, worms, miners & pumpjacks, resources, or rocks. Trees, biter nests, and worms have an additional (small, like less than a tile) radius around them to prevent placing of the tiles too close. The reason for this is because of the tile transitions. The tiles this mod is classifying as artificial actually overlap some with their neighboring tiles, so without this extra radius, they would, visually, look no different than if this mod didn't even affect them. Canonically, I'm also looking at it as the trees have roots, the nests have those tendril looking things growing out the sides, and the worms have these holes that are a bit bigger than their bodies.
These same entities from above likewise cannot be placed on/under any tiles classified as "artificial" with the following exceptions:
- In an effort to prevent using artificial tiles as a hard stop/defense against biter spread, but still give it an element of that realism, biter nests and worms can spawn on artificial tiles, but have a chance at failure. The failure chance is based on the entity (small worms have the worst chance; behemoths have the best; nests are in the middle) and the average of the walking speed modifier on all tiles within their area of influence (the small radius mentioned above). The idea here being that the biters need to "break through" the artificial tiles to the ground beneath to create the nests/worms, and the nests/worms break apart any other tiles in their way as they grow/emerge. I use the walking speed modifier on the tiles because it's the best stat I have access to on the tiles (that I can think of) to determine "hardness" without creating a hard-coded table that would require updating for every mod that creates a new tile that isn't already covered.
- The other exception here are rocks: you can place rocks (where enabled by other mods like Dectorio or in /editor) as decoratives on the tiles.
Mod Interaction & Use on Existing Saves
(Mod support is in another section further down.)
In otherwise vanilla gameplay on brand new maps, this mod should have no issues.
Interactions with other mods, however, has the potential of not going as planned (but not game/save breaking). Mods have the ability to create and destroy entities at will (ignoring build restrictions), but the default behavior of the commands used is to not raise an event telling other mods that this has been done. As such, mods that, say, create trees as part of a tree growth/spreading mod, or create biter nests/worms in place of vanilla generation, have the potential of ignoring these new restrictions, placing these entities on the artificial tiles, and not alerting this mod to what they did.
Aside from other mods, introducing this mod into an existing save will not make now invalid combinations (like concrete on resources) automatically resolve themselves.
The "fix" for this lead me to include a "scan" functionality that will look for and correct these. On game save load where this mod is introduced into an existing save or any startup settings have changed that affect the list of entities or tiles affected, and periodically afterwards, it will run a scan of all game surfaces a few chunks at a time (but only 1 scan at a time) and correct anything it finds. This does mean that on existing saves it can take a bit to scan through and correct everything.
It defaults to destroying any artificial tiles rather than the entity in any cases of conflict (so, sorry, not a free nest/worm/cliff remover), with the exception of trees, which it will mine and spill their results onto the ground around where the tree was. Rocks are just ignored as there's no real way to differentiate between a rock that was there first and one that was placed after for decoration (or whatever).
It's also possible, either through when the entity is created in the data stage or the name the mod author gives their entity/tile, for this mod to just miss identify the entity/tile and either apply restrictions to it when it shouldn't, or doesn't when it should. (Modders, please check out the Modding Support section below for how to correct this from your end.)
Settings
Startup
The startup settings are mostly centered around individually controlling the entity types that will conflict with artificial tiles:
- Trees
- Cliffs
- Biter Nests
- Biter Worms
- Miners (& pumpjacks)
- Resources
-
Rocks
-
There's also an option to disable the "Clean Concrete" feature
Map
There are also a few Map settings that you can use to control the scan functionality (and remember: mod map settings can be changed at any time, not just at map creation):
- Enable/disable the periodic scan function (a scan will still occur on save game load if it's the first time this mod is being used with it or if any startup settings change that affects the list of entities or tiles it works with)
- Periodic scan frequency (defaults to 10 minutes; only 1 scan will run at a time, so if the timer runs out and the previous scan is ongoing, it will wait until it completes before starting another)
- Chunks to process per tick of the scan (both periodic and game load scans). I've set this to default to 5, but you may want to adjust this based on your preference and hardware. If you want to minimize UPS impact (and/or it's having a negative impact on your UPS), then you'll want to decrease this (and this supports decimal values, too, primarily for the purpose of having multiple ticks between each chunk processed). If you think your hardware can handle it, or you just want it to hurry up and get the job done (game load), you can increase this.
Known Issues & Future Plans
Issues
-
As mentioned in the Mod Interaction & Use on Existing Saves section:
Adding this mod to an existing save can take a while to fully scan all surfaces and make any needed corrections, depending on the number of chunks and the
Chunks to check per tick during scan
map setting.It's possible for mods to create or destroy entities in a way that doesn't alert this mod of its happening. If enabled, a periodic scan will find and, if necessary, correct these.
It's possible for this mod to misidentify mod added entities or tiles and thus apply restrictions incorrectly to them. It's ideal for the other mod author to make some simple adjustments on their end to correct for this.
-
As I haven't played mods like Space Exploration, I'm unsure what affect it'll have with certain surfaces that use artificial tiles in its generation (like in the spaceship). Provided there isn't the need to also place any of the affected entity types, everything should be fine.
Future
- Collecting feedback for improvement and bug squashing before declaring this as version 1.0 and removing the BETA from the description.
Below this point is more technical / behind the scenes stuff
Modding Support
This mod works through the use of a collision layer generated via collision_mask_util.get_first_unused_layer()
and several other special considerations, and attempts to automatically register all entity and tile types it should affect with these (see the Entity & Tile Auto-Registration section below for the criteria used). As such, I've included a few options for authors of other mods to register their entities and tiles with this mod (in case they're not picked up automatically or you just want them to be affected, too), or to even mark their entities and tiles for exclusion from this mod's handling, without having to recreate (or reverse) the wheel.
- To exclude your entity or tile from getting auto-registered in this mod, simply add the property
RestrictionsOnArtificialTiles_DoNotRegister
to your prototype and set it totrue
. - Tree entities also get a special "placer" entity created for them: a fake
offshore-pump
to make use of the dual collision boxes and masks. This allows for enforcing the same restrictions on placing of the trees around artificial tiles as it does with placing the tiles around the trees. If for some reason you need to exclude your tree from getting this placer entity created for it (like the tree isn't placeable by the player anyway), you can add the propertyRestrictionsOnArtificialTiles_ExcludeFromPlacer
to the tree prototype and set it totrue
. -
To include your entity or tile with this mod's registration if it doesn't pick it up automatically, call one of the below functions from your mod in the data stage from
data-updates.lua
ordata-final-fixes.lua
(in order for it to work fromdata.lua
, your mod would need to load after mine) and pass it the respective entity type or tile prototype. Note that none of the below functions actually validate the type of prototype you pass to it. This allows for some flexibility, but can also cause errors.RestrictionsOnArtificialTiles.Register_tile(tile)
- Adds this mod's custom collision mask to the tile, as well as the"resource-layer"
collision mask if the startup setting to affect resources is enabled.RestrictionsOnArtificialTiles.Register_tree(tree)
- Checks this mod's setting for trees and, if enabled, adds this mod's custom collision mask to the tree as well as creating a special "placer" entity (explained above).RestrictionsOnArtificialTiles.Register_miner(mining_drill)
- Checks this mod's setting for miners and, if enabled, adds this mod's custom collision mask to the mining drill.RestrictionsOnArtificialTiles.Register_worm(worm, fail_factor)
- (fail_factor
is expected to be anumber
) This mod's collision mask is not added to worms. Instead, this function checks this mod's setting for worms and, if enabled, creates a custom named (based on the worm's name)worm-hole
entity for use in control stage. It also creates a dummy item that is then added to a dummy tech to pass thefail_factor
for the worm to the control stage.fail_factor
will be explained below in another section.RestrictionsOnArtificialTiles.Register_spawner(spawner, fail_factor)
- (fail_factor
is expected to be anumber
) This mod's collision mask is not added to nests. Instead, this function checks this mod's setting for nests and, if enabled, creates a custom named (based on the nest's name)spawner-influence
entity for use in control stage. It also creates a dummy item that is then added to a dummy tech to pass thefail_factor
for the nest to the control stage.fail_factor
will be explained below in another section.RestrictionsOnArtificialTiles.Register_cliff(cliff)
- Checks this mod's setting for cliffs and, if enabled, adds this mod's custom collision mask to the cliff.RestrictionsOnArtificialTiles.Register_rock(rock)
- Check this mod's setting for rocks and, if enabled, adding this mod's custom collision mask to the rock as well as creating a custom "placer" entity to bypass the collision mask restrictions for the purposes of placing. Also makes use of theRestrictionsOnArtificialTiles_ExcludeFromPlacer
property that can be set totrue
on the prototype to exclude it from placer generation, just like trees.RestrictionsOnArtificialTiles.Register_other(prototype, setting)
- (setting
is expected to be astring
) Use this function for any other prototype. It will only add this mod's custom collision mask to the prototype. You can also optionally specify asetting
so that your passed prototype will only get registered if the respective mod setting (from this mod) is enabled:"trees"
,"miners"
,"resources"
,"worms"
,"spawners"
,"cliffs"
, and"rocks"
.Example usage of the above functions (code excerpt taken from
data-final-fixes.lua
in my mod Tree Saplings (Redux)):-- Support for "Restrictions on Artificial Tiles" mod if mods["RestrictionsOnArtificialTiles"] then RestrictionsOnArtificialTiles.Register_other(data.raw["assembling-machine"]["planter"], "trees") RestrictionsOnArtificialTiles.Register_tree(data.raw["simple-entity"]["sapling-placer"]) end
This makes it so the
"planter"
entity gets this mod's collision mask added to it, but only when the respective"trees"
setting is enabled.
It also registers the"sapling-placer"
entity as if it were a tree: if the respective setting for tress is enabled, it gets the collision mask added, and a "placer" from this mod for the dual collision box and masks.
Entity & Tile Auto-Registration
Below are the auto-registration criteria for this mod:
- Any tiles that do not have the word
landfill
in its name and has any one of the following: adecorative_removal_probability
property> 0
, the wordartificial
in theorder
property, or any of the wordsconcrete
,path
,road
,asphalt
,gravel
, orfloor
in its name. - Any entities of the type
"tree"
that do not havedead-
ordry-
in their names. - Any entities of the type
"mining-drill"
. - Any entities of the type
"turret"
and have the wordworm
in their name will be further checked as follows:
If the wordsmall
is found, then it is registered with afail_factor
of1
.
If the wordmedium
is found, then it is registered with afail_factor
of2/3
.
If the wordbig
is found, then it is registered with afail_factor
of1/3
.
If the wordbehemoth
is found, then it is registered with afail_factor
of0
. - Any entities of the type
"unit-spawner"
with the wordspawner
and eitherbiter
orspitter
in the name are registered with afail_factor
of0.5
. - Any entities of the type
"cliff"
. - Any entities of the type
"simple-entity"
with the wordrock
in the name and has theminable
property set.
fail_factor for Nests & Worms
To calculate the failure chance for spawning on artificial tiles of a biter nest or worm, I basically average together the walking_speed_modifier
of all tiles that touch the nest's spawner-influence
or worm's worm-hole
entity, and use it in the following formula (in place of "avg"): (avg - 1) + ((avg - 1) * fail_factor)
That gives us this nifty little table (row 1 is fail_factor
values, while column 1 is tile walking_speed_modifier
values):
10 | 1.5 | 1 | 0.667 | 0.5 | 0.333 | 0 | -0.5 | -0.99 | -1 | |
---|---|---|---|---|---|---|---|---|---|---|
1.0 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 | 0.000 |
1.05 | 0.550 | 0.125 | 0.100 | 0.083 | 0.075 | 0.067 | 0.050 | 0.025 | 0.001 | 0.000 |
1.1 | 1.100 | 0.250 | 0.200 | 0.167 | 0.150 | 0.133 | 0.100 | 0.050 | 0.001 | 0.000 |
1.2 | 2.200 | 0.500 | 0.400 | 0.333 | 0.300 | 0.267 | 0.200 | 0.100 | 0.002 | 0.000 |
1.3 | 3.300 | 0.750 | 0.600 | 0.500 | 0.450 | 0.400 | 0.300 | 0.150 | 0.003 | 0.000 |
1.4 | 4.400 | 1.000 | 0.800 | 0.667 | 0.600 | 0.533 | 0.400 | 0.200 | 0.004 | 0.000 |
1.5 | 5.500 | 1.250 | 1.000 | 0.833 | 0.750 | 0.667 | 0.500 | 0.250 | 0.005 | 0.000 |
2.0 | 11.000 | 2.500 | 2.000 | 1.667 | 1.500 | 1.333 | 1.000 | 0.500 | 0.010 | 0.000 |
I then take a random number (math.random()), and if it's less than or equal to the result from that formula, then the nest or worm "fails" to spawn.
The low end of fail_factor
is clamped to -1
. This means that no matter the average tile's walking_speed_modifier
, there's always a small chance at failure. On the opposite end of the spectrum, however, there's definitely average tile walking_speed_modifier
values that will guarantee failure. A part of me wants to change the underlying formula at some point so this won't happen.