mirror of
https://github.com/Refactorio/RedMew.git
synced 2025-03-03 14:53:01 +02:00
merged #261
This commit is contained in:
commit
39125b1f24
433
map_gen/misc/creep_spread.lua
Normal file
433
map_gen/misc/creep_spread.lua
Normal file
@ -0,0 +1,433 @@
|
||||
--[[
|
||||
Some inspiration and bits of code taken from Nightfall by Yehn and dangOreus by Mylon.
|
||||
Both under their respective MIT licenses.
|
||||
|
||||
This softmod uses the Starcraft concept of "creep" as a mechanic. In essence, players can only
|
||||
build on certain creep tiles (in particular, the defined `creep_expansion_tile`s).
|
||||
Pollution will naturally expand the creep. The creep will also naturally regress if pollution
|
||||
is not sustained, affecting player structures in the process.
|
||||
|
||||
This module does not create an initial world state, so you will not have a guaranteed safe placed
|
||||
to build, and any creep_expansion_tiles will be overtaken by creep_retraction_tiles as there is
|
||||
no initial pollution cloud.
|
||||
|
||||
Todo: make the expansion less "blocky"/constrained to chunk borders
|
||||
]]--
|
||||
|
||||
local Event = require 'utils.event'
|
||||
local Game = require 'utils.game'
|
||||
local random = math.random
|
||||
local insert = table.insert
|
||||
|
||||
--how many chunks to process in a tick
|
||||
local processchunk = 5
|
||||
|
||||
-- how often to recap the deaths from lack of creep (in ticks)
|
||||
local death_recap_timer = 1200 -- 20 secs
|
||||
|
||||
-- how often to check for players' positions
|
||||
local player_pos_check_time = 300 -- 5 secs
|
||||
|
||||
-- force that is restricted to the creep
|
||||
local creep_force = "player"
|
||||
|
||||
-- the 3 chunk states are: 0% creep tiles and unpolluted, 100% creep and polluted, unknown or transitional creep state
|
||||
local NOT_CREEP = 1
|
||||
local FULL_CREEP = 2
|
||||
local CREEP_RETRACTION = 3
|
||||
local CREEP_EXPANDING = 4
|
||||
local CREEP_UNKNOWN = 5 -- a special case for newly-generated chunks
|
||||
|
||||
-- the threshold above which creep expands
|
||||
local pollution_threshold = 200
|
||||
|
||||
-- the number of tiles that change to/from creep at once
|
||||
local random_factor = 0.1
|
||||
|
||||
-- which tiles to use for creep expansion
|
||||
local creep_expansion_tiles = {
|
||||
'grass-1',
|
||||
'grass-2'
|
||||
}
|
||||
|
||||
-- which tiles to use when creep retracts
|
||||
local creep_retraction_tiles = {
|
||||
'dirt-1',
|
||||
'dirt-2',
|
||||
'dry-dirt',
|
||||
'sand-1',
|
||||
'sand-2',
|
||||
'sand-3'
|
||||
}
|
||||
|
||||
|
||||
-- which tiles players can build on/count as creep
|
||||
local creep_tiles = {
|
||||
'grass-1',
|
||||
'grass-2',
|
||||
'concrete',
|
||||
'hazard-concrete-left',
|
||||
'hazard-concrete-right',
|
||||
'lab-dark-2',
|
||||
'lab-white',
|
||||
'refined-concrete',
|
||||
'refined-hazard-concrete-left',
|
||||
'refined-hazard-concrete-right',
|
||||
'stone-path',
|
||||
'tutorial-grid'
|
||||
}
|
||||
|
||||
-- which tiles creep can expand to
|
||||
local creep_expandable_tiles = {
|
||||
'lab-dark-1',
|
||||
'dirt-1',
|
||||
'dirt-2',
|
||||
'dirt-3',
|
||||
'dirt-4',
|
||||
'dirt-5',
|
||||
'dirt-6',
|
||||
'dirt-7',
|
||||
'dry-dirt',
|
||||
'grass-3',
|
||||
'grass-4',
|
||||
'red-desert-0',
|
||||
'red-desert-1',
|
||||
'red-desert-2',
|
||||
'red-desert-3',
|
||||
'sand-1',
|
||||
'sand-2',
|
||||
'sand-3'
|
||||
}
|
||||
|
||||
-- list of all tiles currently in the game (0.16.51)
|
||||
local all_tiles ={
|
||||
'concrete',
|
||||
'deepwater',
|
||||
'deepwater-green',
|
||||
'dirt-1',
|
||||
'dirt-2',
|
||||
'dirt-3',
|
||||
'dirt-4',
|
||||
'dirt-5',
|
||||
'dirt-6',
|
||||
'dirt-7',
|
||||
'dry-dirt',
|
||||
'grass-1',
|
||||
'grass-2',
|
||||
'grass-3',
|
||||
'grass-4',
|
||||
'hazard-concrete-left',
|
||||
'hazard-concrete-right',
|
||||
'lab-dark-1',
|
||||
'lab-dark-2',
|
||||
'lab-white',
|
||||
'out-of-map',
|
||||
'red-desert-0',
|
||||
'red-desert-1',
|
||||
'red-desert-2',
|
||||
'red-desert-3',
|
||||
'refined-concrete',
|
||||
'refined-hazard-concrete-left',
|
||||
'refined-hazard-concrete-right',
|
||||
'sand-1',
|
||||
'sand-2',
|
||||
'sand-3',
|
||||
'stone-path',
|
||||
'tutorial-grid',
|
||||
'water',
|
||||
'water-green'
|
||||
}
|
||||
|
||||
local function get_tiles(tile_filter)
|
||||
tiles = find_tiles_filtered({area={{chunkcoord.x-16, chunkcoord.y-16},{chunkcoord.x+16, chunkcoord.y+16}},
|
||||
name = creep_expandable_tiles})
|
||||
return tiles
|
||||
end
|
||||
|
||||
local function convert_tiles(tile_table, tiles)
|
||||
local set_tiles = game.surfaces[1].set_tiles
|
||||
local tile_set = {}
|
||||
local target_tile = tile_table[random(1,#tile_table)]
|
||||
-- convert the LuaTiles table into a new one we can edit
|
||||
for _, tiledata in ipairs(tiles) do
|
||||
if random() < random_factor then
|
||||
tile_set[#tile_set+1] = {name = target_tile, position = tiledata.position}
|
||||
end
|
||||
end
|
||||
-- change the tiles to the target_tile
|
||||
set_tiles(tile_set)
|
||||
end
|
||||
|
||||
local function check_chunk_for_entities(chunk)
|
||||
local find_entities_filtered = game.surfaces[1].find_entities_filtered
|
||||
local entities_found
|
||||
entities_found = {}
|
||||
entities_found = find_entities_filtered{area = {{chunk.x-16, chunk.y-16},{chunk.x+16, chunk.y+16}},
|
||||
force = creep_force}
|
||||
for _, entity in ipairs(entities_found) do
|
||||
kill_invalid_builds(entity, false)
|
||||
end
|
||||
end
|
||||
|
||||
local function change_creep_state(state, i)
|
||||
-- this function changes the state and tiles of chunks when they meet the creep expansion/retraction criteria
|
||||
local find_tiles_filtered = game.surfaces[1].find_tiles_filtered
|
||||
-- expand = 1, retract = 2
|
||||
local tile_table = {}
|
||||
local tiles_to_set = {}
|
||||
local debug_message
|
||||
local chunk_end_state
|
||||
local chunk_transition_state
|
||||
if state == 1 then
|
||||
tiles_to_find = creep_expandable_tiles
|
||||
tiles_to_set = creep_expansion_tiles
|
||||
debug_message = "Creep expanding"
|
||||
chunk_end_state = FULL_CREEP
|
||||
chunk_transition_state = CREEP_EXPANDING
|
||||
elseif state == 2 then
|
||||
tiles_to_find = creep_tiles
|
||||
tiles_to_set = creep_retraction_tiles
|
||||
debug_message = "Creep retracting"
|
||||
chunk_end_state = NOT_CREEP
|
||||
chunk_transition_state = CREEP_RETRACTION
|
||||
end
|
||||
|
||||
global.chunklist[i].is_creep = chunk_transition_state
|
||||
local chunklist = global.chunklist
|
||||
local chunkcoord = chunklist[i]
|
||||
local tiles = {}
|
||||
-- check to see if there are any tiles to act on
|
||||
tiles = find_tiles_filtered({area={{chunkcoord.x-16, chunkcoord.y-16},{chunkcoord.x+16, chunkcoord.y+16}},
|
||||
name = tiles_to_find})
|
||||
if (#tiles > 0) then
|
||||
convert_tiles(tiles_to_set, tiles)
|
||||
--if _DEBUG then game.print(debug_message) end
|
||||
else
|
||||
-- if there are 0 tiles to convert, they're either fully creep or fully non-creep
|
||||
global.chunklist[i].is_creep = chunk_end_state
|
||||
-- if a chunk has lost all creep, do a final check to see if there are any buildings to kill
|
||||
if state == 2 then
|
||||
check_chunk_for_entities(global.chunklist[i])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function on_tick()
|
||||
local get_pollution = game.surfaces[1].get_pollution
|
||||
|
||||
-- localize globals
|
||||
local chunklist = global.chunklist
|
||||
local maxindex = #chunklist
|
||||
for i=global.c_index, global.c_index+processchunk, 1 do
|
||||
if i > maxindex then
|
||||
-- we've iterated through all chunks
|
||||
global.c_index = 1
|
||||
break
|
||||
end
|
||||
if get_pollution(chunklist[i]) > pollution_threshold and chunklist[i].is_creep ~= FULL_CREEP then
|
||||
change_creep_state(1, i) -- expand = 1, retract = 2
|
||||
elseif get_pollution(chunklist[i]) == 0 and chunklist[i].is_creep ~= NOT_CREEP then
|
||||
change_creep_state(2, i) -- expand = 1, retract = 2
|
||||
end
|
||||
if chunklist[i].is_creep == CREEP_RETRACTION then
|
||||
-- if a chunk's creep is retracting, we need to check if there are entities to kill
|
||||
check_chunk_for_entities(chunklist[i])
|
||||
end
|
||||
end
|
||||
global.c_index = global.c_index + processchunk
|
||||
end
|
||||
|
||||
local function make_constrast_tiles_table()
|
||||
-- this creates a table of "contrast tiles", that is, tiles that are not creep nor creep retraction, nor water, nor void tiles
|
||||
-- we might want to use this list to pass to map gen tools to filter out tiles we don't want
|
||||
global.contrast_tiles = {}
|
||||
-- table of tiles that are already "anti-creep"
|
||||
local retraction_tiles = creep_retraction_tiles
|
||||
-- table of tiles we want to keep (water and void)
|
||||
local unremovable_tiles = {
|
||||
'deepwater',
|
||||
'deepwater-green',
|
||||
'out-of-map',
|
||||
'water',
|
||||
'water-green'
|
||||
}
|
||||
local omit = 0
|
||||
|
||||
-- create a list of all non-creep tiles for the purpose of filtering in kill_invalid_builds
|
||||
for _, contrast_tile in ipairs(all_tiles) do
|
||||
for _, retraction_tile in ipairs(retraction_tiles) do
|
||||
if contrast_tile == retraction_tile then
|
||||
omit = 1
|
||||
end
|
||||
end
|
||||
for _, unremovable_tile in ipairs(unremovable_tiles) do
|
||||
if contrast_tile == unremovable_tile then
|
||||
omit = 1
|
||||
end
|
||||
end
|
||||
|
||||
if omit == 1 then
|
||||
omit = 0
|
||||
else
|
||||
insert(global.contrast_tiles, contrast_tile)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function on_chunk_generated(event)
|
||||
-- Track when new chunks are generated and add them to the chunklist
|
||||
if event.surface == game.surfaces[1] then
|
||||
local chunk = {}
|
||||
local coords = event.area.left_top
|
||||
chunk.x = coords.x+16
|
||||
chunk.y = coords.y+16
|
||||
chunk.is_creep = CREEP_UNKNOWN
|
||||
insert(global.chunklist, chunk)
|
||||
end
|
||||
end
|
||||
|
||||
function kill_invalid_builds(entity, from_build_event)
|
||||
--Auto-destroy buildings created outside of creep
|
||||
if not (entity and entity.valid) then
|
||||
return
|
||||
end
|
||||
-- don't kill players
|
||||
if entity.type == 'player' then
|
||||
return
|
||||
end
|
||||
-- don't kill vehicles
|
||||
if entity.type == 'car' or entity.type == 'tank' or not entity.health then
|
||||
return
|
||||
end
|
||||
-- Some entities have no bounding box area. Not sure which.
|
||||
if entity.bounding_box.left_top.x == entity.bounding_box.right_bottom.x or entity.bounding_box.left_top.y == entity.bounding_box.right_bottom.y then
|
||||
return
|
||||
end
|
||||
-- don't kill trains
|
||||
if entity.type == "locomotive"
|
||||
or entity.type == "fluid-wagon"
|
||||
or entity.type == "cargo-wagon"
|
||||
or entity.type == "artillery-wagon "
|
||||
then
|
||||
return
|
||||
end
|
||||
local last_user = entity.last_user
|
||||
local unacceptable_tiles = global.unacceptable_tiles
|
||||
local ceil = math.ceil
|
||||
local floor = math.floor
|
||||
|
||||
-- expand the bounding box to enclose full tiles rather than the subtile size most bounding boxes are
|
||||
local bounding_box = {
|
||||
{floor(entity.bounding_box.left_top.x), floor(entity.bounding_box.left_top.y)},
|
||||
{ceil(entity.bounding_box.right_bottom.x), ceil(entity.bounding_box.right_bottom.y)}
|
||||
}
|
||||
local tiles = entity.surface.count_tiles_filtered{name = unacceptable_tiles, area = bounding_box, limit = 1}
|
||||
if tiles > 0 then
|
||||
--Need to turn off ghosts left by dead buildings so construction bots won't keep placing buildings and having them blow up.
|
||||
local force = entity.force
|
||||
local ttl = force.ghost_time_to_live
|
||||
entity.force.ghost_time_to_live = 0
|
||||
entity.die()
|
||||
force.ghost_time_to_live = ttl
|
||||
global.death_count = global.death_count + 1
|
||||
if from_build_event and last_user then
|
||||
last_user.print("You may only build on the creep!")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function print_death_toll()
|
||||
if global.death_count > 1 then
|
||||
game.print(global.death_count .. " buildings have died outside of the creep recently.")
|
||||
global.death_count = 0
|
||||
end
|
||||
end
|
||||
|
||||
local function kill_on_built (event)
|
||||
local entity = event.created_entity
|
||||
kill_invalid_builds(entity, true)
|
||||
end
|
||||
|
||||
local function apply_creep_effects_on_players()
|
||||
--Penalize players for not being on creep by slowing their movement
|
||||
local radius = 10 --not actually a radius
|
||||
local unacceptable_tiles = global.unacceptable_tiles
|
||||
|
||||
for _, p in ipairs(game.connected_players) do
|
||||
if not p.character then --Spectator or admin
|
||||
return
|
||||
end
|
||||
local count = p.surface.count_tiles_filtered{name=unacceptable_tiles, area={{p.position.x-radius, p.position.y-radius}, {p.position.x+radius, p.position.y+radius}}}
|
||||
if count == (radius * 2)^2 * 1 then
|
||||
if p.vehicle then
|
||||
return
|
||||
else
|
||||
p.surface.create_entity{name="acid-projectile-purple", target=p.character, position=p.character.position, speed=10}
|
||||
p.character.health = p.character.health - 20
|
||||
p.print("You are taking damage from being too far from the creep.")
|
||||
end
|
||||
p.character_running_speed_modifier = 0
|
||||
if _DEBUG then game.print("Speed modifier 0 and damage") end
|
||||
elseif count > (radius * 2)^2 * 0.8 then
|
||||
|
||||
p.character_running_speed_modifier = 0
|
||||
if _DEBUG then game.print("Speed modifier 0") end
|
||||
else
|
||||
p.character_running_speed_modifier = 0.2
|
||||
if _DEBUG then game.print("Speed modifier 0.2") end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function on_init()
|
||||
global.chunklist = {}
|
||||
global.c_index=1
|
||||
global.unacceptable_tiles = {}
|
||||
global.death_count = 0
|
||||
local omit = 0
|
||||
|
||||
-- create a list of all non-creep tiles for the purpose of filtering in kill_invalid_builds
|
||||
for _, all_tile in ipairs(all_tiles) do
|
||||
for _, creep_tile in ipairs(creep_tiles) do
|
||||
if all_tile == creep_tile then
|
||||
omit = 1
|
||||
end
|
||||
end
|
||||
|
||||
if omit == 1 then
|
||||
omit = 0
|
||||
else
|
||||
insert(global.unacceptable_tiles, all_tile)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Event.add(defines.events.on_tick, on_tick)
|
||||
Event.add(defines.events.on_chunk_generated, on_chunk_generated)
|
||||
Event.on_init(on_init)
|
||||
Event.add(defines.events.on_built_entity, kill_on_built)
|
||||
Event.add(defines.events.on_robot_built_entity, kill_on_built)
|
||||
Event.on_nth_tick(death_recap_timer, print_death_toll)
|
||||
Event.on_nth_tick(player_pos_check_time, apply_creep_effects_on_players)
|
||||
|
||||
-- a couple little debug command to generate and clear pollution to test things without having to mess with world gen
|
||||
if _DEBUG then
|
||||
commands.add_command(
|
||||
'cloud',
|
||||
'Use your vape rig to create a pollution cloud around you',
|
||||
function()
|
||||
if game.player then
|
||||
game.player.surface.pollute(game.player.position, 10000)
|
||||
end
|
||||
end
|
||||
)
|
||||
commands.add_command(
|
||||
'clean',
|
||||
'Use your vacuum to suck up the pollution cloud around you',
|
||||
function()
|
||||
if game.player then
|
||||
game.player.surface.clear_pollution()
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
95
map_gen/presets/creepy.lua
Normal file
95
map_gen/presets/creepy.lua
Normal file
@ -0,0 +1,95 @@
|
||||
local b = require 'map_gen.shared.builders'
|
||||
|
||||
local function value(base, mult)
|
||||
return function(x, y)
|
||||
return mult * (math.abs(x) + math.abs(y)) + base
|
||||
end
|
||||
end
|
||||
|
||||
local function no_resources(x, y, world, tile)
|
||||
for _, e in ipairs(
|
||||
world.surface.find_entities_filtered(
|
||||
{type = 'resource', area = {{world.x, world.y}, {world.x + 1, world.y + 1}}}
|
||||
)
|
||||
) do
|
||||
e.destroy()
|
||||
end
|
||||
for _, e in ipairs(
|
||||
world.surface.find_entities_filtered(
|
||||
{type = 'tree', area = {{world.x, world.y}, {world.x + 1, world.y + 1}}}
|
||||
)
|
||||
) do
|
||||
e.destroy()
|
||||
end
|
||||
for _, e in ipairs(
|
||||
world.surface.find_entities_filtered(
|
||||
{type = 'rock', area = {{world.x, world.y}, {world.x + 1, world.y + 1}}}
|
||||
)
|
||||
) do
|
||||
e.destroy()
|
||||
end
|
||||
|
||||
return tile
|
||||
end
|
||||
|
||||
--[[local function no_trees(x, y, world, tile)
|
||||
for _, e in ipairs(
|
||||
world.surface.find_entities_filtered(
|
||||
{type = 'tree', area = {{world.x, world.y}, {world.x + 1, world.y + 1}}}
|
||||
)
|
||||
) do
|
||||
e.destroy()
|
||||
end
|
||||
|
||||
return tile
|
||||
end]]--
|
||||
|
||||
-- create a square on which to place each ore
|
||||
local square = b.rectangle(12,12)
|
||||
square = b.change_tile(square, true, 'concrete')
|
||||
|
||||
-- set the ore weights and sizes
|
||||
local iron = b.resource(b.rectangle(12,12), 'iron-ore', value(200, 1))
|
||||
local copper = b.resource(b.rectangle(12,12), 'copper-ore', value(150, 0.8))
|
||||
local stone = b.resource(b.rectangle(12,12), 'stone', value(100, .5))
|
||||
local coal = b.resource(b.rectangle(12,12), 'coal', value(100, 0.6))
|
||||
|
||||
-- place each ore on the square
|
||||
local iron_sq = b.apply_entity(square, iron)
|
||||
local copper_sq = b.apply_entity(square, copper)
|
||||
local stone_sq = b.apply_entity(square, stone)
|
||||
local coal_sq = b.apply_entity(square, coal)
|
||||
|
||||
-- create starting water square and change the type to water
|
||||
local water_start =
|
||||
b.any {
|
||||
b.rectangle(12, 12)
|
||||
}
|
||||
water_start = b.change_tile(water_start, true, 'water')
|
||||
|
||||
-- create the large concrete square
|
||||
local concrete_square = b.rectangle(80, 80)
|
||||
concrete_square = b.change_tile(concrete_square, true, 'concrete')
|
||||
|
||||
-- create the start area using the ore, water and concrete squares
|
||||
local ore_distance = 24
|
||||
local start_area =
|
||||
b.any {
|
||||
b.translate(iron_sq, -ore_distance, -ore_distance),
|
||||
b.translate(copper_sq, -ore_distance, ore_distance),
|
||||
b.translate(stone_sq, ore_distance, -ore_distance),
|
||||
b.translate(coal_sq, ore_distance, ore_distance),
|
||||
water_start,
|
||||
concrete_square
|
||||
}
|
||||
start_area = b.apply_effect(start_area, no_resources)
|
||||
--start_area = b.apply_effect(start_area, no_trees)
|
||||
|
||||
|
||||
local map = start_area
|
||||
map = b.change_map_gen_collision_tile(map, 'water-tile', 'grass-1')
|
||||
map = b.change_tile(map, false, 'sand-1')
|
||||
map = b.translate(map, 6, -10) -- translate the whole map away, otherwise we'll spawn in the water
|
||||
|
||||
--return start_area
|
||||
return map
|
@ -83,6 +83,7 @@ local tiles_per_tick = 32
|
||||
--shape = require "map_gen.presets.spiral"
|
||||
--shape = require "map_gen.presets.hub_spiral"
|
||||
--shape = require "map_gen.presets.diagonal_ribbon"
|
||||
--shape = require "map_gen.presets.creepy"
|
||||
--shape = require "map_gen.presets.vanilla"
|
||||
--shape = require "map_gen.presets.test"
|
||||
|
||||
@ -133,6 +134,7 @@ local terrain_modules = {
|
||||
--require ('map_gen.misc.restrict_landfill_tile')({['water'] = true})
|
||||
--require "map_gen.ores.rso.rso_control"
|
||||
--require 'map_gen.misc.nightfall'
|
||||
--require 'map_gen.misc.creep_spread'
|
||||
--require 'map_gen.misc.car_body'
|
||||
|
||||
if #entity_modules > 0 then
|
||||
|
Loading…
x
Reference in New Issue
Block a user