From 30b2519a2d4ee10ecb7873fad30f7b33b95522f1 Mon Sep 17 00:00:00 2001 From: MewMew Date: Mon, 7 Oct 2019 04:37:23 +0200 Subject: [PATCH] mountain map update WIP --- maps/mountain_fortress/main.lua | 24 +++++- maps/mountain_fortress/terrain.lua | 125 ++++++++++++++++++++++++++--- modules/wave_defense.lua | 110 +++++++++++++++++++++---- 3 files changed, 232 insertions(+), 27 deletions(-) diff --git a/maps/mountain_fortress/main.lua b/maps/mountain_fortress/main.lua index 1bf50870..b4e94667 100644 --- a/maps/mountain_fortress/main.lua +++ b/maps/mountain_fortress/main.lua @@ -1,6 +1,7 @@ -- Cave Defender, protect the locomotive! -- by MewMew require "modules.wave_defense" +--require "modules.dense_rocks" require "maps.mountain_fortress.terrain" require "maps.mountain_fortress.locomotive" @@ -21,24 +22,33 @@ local function init_surface() game.create_surface("mountain_fortress", map) end +local function on_entity_died(event) + if not event.entity.valid then return end + if event.entity == global.locomotive_cargo then + game.print("Game Over!") + global.wave_defense.game_lost = true + return + end +end + local function on_player_joined_game(event) local surface = game.surfaces["mountain_fortress"] local player = game.players[event.player_index] if player.online_time == 0 then - player.teleport(surface.find_non_colliding_position("character", {0,0}, 3, 0.5), surface) + player.teleport(surface.find_non_colliding_position("character", game.forces.player.get_spawn_position(surface), 3, 0.5), surface) end end local function on_init(surface) + global.chunk_queue = {} + init_surface() local surface = game.surfaces["mountain_fortress"] surface.request_to_generate_chunks({0,0}, 6) surface.force_generate_chunk_requests() - global.wave_defense.surface = surface - game.map_settings.enemy_evolution.destroy_factor = 0 game.map_settings.enemy_evolution.pollution_factor = 0 game.map_settings.enemy_evolution.time_factor = 0 @@ -49,9 +59,15 @@ local function on_init(surface) game.map_settings.enemy_expansion.settler_group_min_size = 16 game.map_settings.pollution.enabled = false - locomotive_spawn(surface, {x = 0, y = -10}) + game.forces.player.set_spawn_position({-2, 16}, surface) + + locomotive_spawn(surface, {x = 0, y = 16}) + + global.wave_defense.surface = surface + global.wave_defense.target = global.locomotive_cargo end local event = require 'utils.event' event.on_init(on_init) +event.add(defines.events.on_entity_died, on_entity_died) event.add(defines.events.on_player_joined_game, on_player_joined_game) \ No newline at end of file diff --git a/maps/mountain_fortress/terrain.lua b/maps/mountain_fortress/terrain.lua index 99f3a1c8..4dc5293e 100644 --- a/maps/mountain_fortress/terrain.lua +++ b/maps/mountain_fortress/terrain.lua @@ -1,3 +1,7 @@ +local math_random = math.random +local rock_raffle = {"sand-rock-big","sand-rock-big","rock-big","rock-big","rock-big","rock-big","rock-big","rock-big","rock-huge"} +local spawner_raffle = {"biter-spawner", "biter-spawner", "biter-spawner", "spitter-spawner"} + local function process_position(surface, p) local distance_to_center = math.sqrt(p.x^2 + p.y^2) local index = math.floor((distance_to_center / 16) % 18) + 1 @@ -12,18 +16,121 @@ local function process_position(surface, p) end end -local function on_chunk_generated(event) - local left_top = event.area.left_top - local surface = event.surface - - for x = 0.5, 31.5, 1 do - for y = 0.5, 31.5, 1 do - p = {x = left_top.x + x, y = left_top.y + y} - --process_position(surface, p) - +local function rock_chunk(surface, left_top) + for x = 0, 31, 1 do + for y = 0, 31, 1 do + surface.set_tiles({{name = "dirt-7", position = {x = left_top.x + x, y = left_top.y + y}}}) end end end +local function border_chunk(surface, left_top) + for x = 0, 31, 1 do + for y = 0, 31, 1 do + surface.set_tiles({{name = "dirt-3", position = {x = left_top.x + x, y = left_top.y + y}}}) + end + end + + local trees = {"dead-grey-trunk", "dead-grey-trunk", "dry-tree"} + for x = 0, 31, 1 do + for y = 5, 31, 1 do + local pos = {x = left_top.x + x, y = left_top.y + y} + if math_random(1, math.ceil(pos.y + pos.y) + 64) == 1 then + surface.create_entity({name = trees[math_random(1, #trees)], position = pos}) + end + end + end + + for x = 0, 31, 1 do + for y = 0, 31, 1 do + local pos = {x = left_top.x + x, y = left_top.y + y} + if math_random(1, pos.y + 2) == 1 then + surface.create_decoratives{ + check_collision=false, + decoratives={ + {name = "rock-medium", position = pos, amount = math_random(1, 1 + math.ceil(20 - y / 2))} + } + } + end + if math_random(1, pos.y + 2) == 1 then + surface.create_decoratives{ + check_collision=false, + decoratives={ + {name = "rock-small", position = pos, amount = math_random(1, 1 + math.ceil(20 - y / 2))} + } + } + end + if math_random(1, pos.y + 2) == 1 then + surface.create_decoratives{ + check_collision=false, + decoratives={ + {name = "rock-tiny", position = pos, amount = math_random(1, 1 + math.ceil(20 - y / 2))} + } + } + end + if math_random(1, math.ceil(pos.y + pos.y) + 2) == 1 then + surface.create_entity({name = rock_raffle[math_random(1, #rock_raffle)], position = pos}) + end + end + end +end + +local function biter_chunk(surface, left_top) + local tile_positions = {} + for x = 0, 31, 1 do + for y = 0, 31, 1 do + local p = {x = left_top.x + x, y = left_top.y + y} + surface.set_tiles({{name = "sand-3", position = p}}) + tile_positions[#tile_positions + 1] = p + end + end + for i = 1, 4, 1 do + local position = surface.find_non_colliding_position("biter-spawner", tile_positions[math_random(1, #tile_positions)], 16, 2) + if position then + surface.create_entity({name = spawner_raffle[math_random(1, #spawner_raffle)], position = position}) + end + end +end + +local function out_of_map(surface, left_top) + for x = 0, 31, 1 do + for y = 0, 31, 1 do + surface.set_tiles({{name = "out-of-map", position = {x = left_top.x + x, y = left_top.y + y}}}) + end + end +end + +local function process_chunk(left_top) + local surface = game.surfaces["mountain_fortress"] + if left_top.y == 96 and left_top.x == 96 then + local p = global.locomotive.position + for _, entity in pairs(surface.find_entities_filtered({area = {{p.x - 3, p.y - 4},{p.x + 3, p.y + 8}}, force = "neutral"})) do entity.destroy() end + end + if left_top.y < 0 then rock_chunk(surface, left_top) return end + if left_top.y > 128 then out_of_map(surface, left_top) return end + if left_top.y > 64 then biter_chunk(surface, left_top) return end + if left_top.y >= 0 then border_chunk(surface, left_top) return end +end + +local function process_chunk_queue() + for k, left_top in pairs(global.chunk_queue) do + process_chunk(left_top) + global.chunk_queue[k] = nil + return + end +end + +local function on_chunk_generated(event) + if game.surfaces["mountain_fortress"].index ~= event.surface.index then return end + local left_top = event.area.left_top + + if game.tick == 0 then + process_chunk(left_top) + else + global.chunk_queue[#global.chunk_queue + 1] = {x = left_top.x, y = left_top.y} + end +end + local event = require 'utils.event' +event.on_nth_tick(60, process_chunk_queue) event.add(defines.events.on_chunk_generated, on_chunk_generated) \ No newline at end of file diff --git a/modules/wave_defense.lua b/modules/wave_defense.lua index 09b682d8..dffbe880 100644 --- a/modules/wave_defense.lua +++ b/modules/wave_defense.lua @@ -27,8 +27,8 @@ function set_biter_raffle(level) global.wave_defense.biter_raffle = { ["small-biter"] = 1000 - level * 2, ["small-spitter"] = 1000 - level * 2, - ["medium-biter"] = level * 2, - ["medium-spitter"] = level * 2, + ["medium-biter"] = level, + ["medium-spitter"] = level, ["big-biter"] = 0, ["big-spitter"] = 0, ["behemoth-biter"] = 0, @@ -47,6 +47,16 @@ function set_biter_raffle(level) end end +local function time_out_biters() + for k, biter in pairs(global.wave_defense.active_biters) do + if biter.spawn_tick + global.wave_defense.max_biter_age < game.tick then + global.wave_defense.threat = global.wave_defense.threat + threat_values[biter.entity.name] + biter.entity.destroy() + global.wave_defense.active_biters[k] = nil + end + end +end + local function get_random_close_spawner() local spawners = global.wave_defense.surface.find_entities_filtered({type = "unit-spawner"}) if not spawners[1] then return false end @@ -77,7 +87,7 @@ end local function set_group_spawn_position() local spawner = get_random_close_spawner() if not spawner then return end - local position = global.wave_defense.surface.find_non_colliding_position("rocket-silo", spawner.position, 32, 1) + local position = global.wave_defense.surface.find_non_colliding_position("rocket-silo", spawner.position, 48, 1) if not position then return end global.wave_defense.spawn_position = position end @@ -113,16 +123,16 @@ local function spawn_unit_group() if not biter then break end unit_group.add_member(biter) end + global.wave_defense.unit_groups[#global.wave_defense.unit_groups + 1] = unit_group return true end local function spawn_wave() - if game.tick < global.wave_defense.next_wave then return end - global.wave_defense.next_wave = game.tick + global.wave_defense.wave_interval + if global.wave_defense.active_biter_count >= global.wave_defense.max_active_biters then return false end global.wave_defense.wave_number = global.wave_defense.wave_number + 1 - global.wave_defense.group_size = global.wave_defense.wave_number * 4 + global.wave_defense.group_size = global.wave_defense.wave_number * 2 if global.wave_defense.group_size > global.wave_defense.max_group_size then global.wave_defense.group_size = global.wave_defense.max_group_size end - global.wave_defense.threat = global.wave_defense.threat + global.wave_defense.wave_number * 4 + global.wave_defense.threat = global.wave_defense.threat + global.wave_defense.wave_number * 2 set_enemy_evolution() set_biter_raffle(global.wave_defense.wave_number) for a = 1, 16, 1 do @@ -130,6 +140,60 @@ local function spawn_wave() end end +local function give_commands_to_unit_groups() + if #global.wave_defense.unit_groups == 0 then return end + if not global.wave_defense.target then return end + if not global.wave_defense.target.valid then return end + for k, group in pairs(global.wave_defense.unit_groups) do + if group.valid then + group.set_command({ + type = defines.command.compound, + structure_type = defines.compound_command.return_last, + commands = { + { + type = defines.command.attack_area, + destination = global.wave_defense.target.position, + radius = 16, + distraction = defines.distraction.by_enemy + }, + { + type = defines.command.attack, + target = global.wave_defense.target, + distraction = defines.distraction.by_enemy + } + } + }) + else + global.wave_defense.unit_groups[k] = nil + end + end +end + +local function create_gui(player) + local frame = player.gui.top.add({ type = "frame", name = "wave_defense", tooltip = "Click to show map info"}) + frame.style.maximal_height = 38 + + local label = frame.add({ type = "label", caption = " ", name = "label"}) + label.style.font_color = {r=0.88, g=0.88, b=0.88} + label.style.font = "default-listbox" + label.style.left_padding = 4 + label.style.right_padding = 4 + label.style.minimal_width = 68 + label.style.font_color = {r=0.33, g=0.66, b=0.9} + + local progressbar = frame.add({ type = "progressbar", name = "progressbar", value = 0}) + progressbar.style.minimal_width = 128 + progressbar.style.maximal_width = 128 + progressbar.style.top_padding = 10 +end + +local function update_gui(player) + if not player.gui.top.wave_defense then create_gui(player) end + player.gui.top.wave_defense.label.caption = "Wave: " .. global.wave_defense.wave_number + if global.wave_defense.wave_number == 0 then player.gui.top.wave_defense.label.caption = "First wave in " .. math.floor((global.wave_defense.next_wave - game.tick) / 60) + 1 end + player.gui.top.wave_defense.progressbar.value = 1 - math.round((global.wave_defense.next_wave - game.tick) / global.wave_defense.wave_interval, 3) +end + local function on_entity_died(event) if not event.entity.valid then return end if event.entity.type ~= "unit" then return end @@ -139,30 +203,48 @@ local function on_entity_died(event) end local function on_tick() - if game.tick % 60 == 0 then + if global.wave_defense.game_lost then return end + + for _, player in pairs(game.connected_players) do update_gui(player) end + + if game.tick < global.wave_defense.next_wave then return end + + if global.wave_defense.active_biter_count < global.wave_defense.max_active_biters then + global.wave_defense.next_wave = game.tick + global.wave_defense.wave_interval + time_out_biters() set_target() spawn_wave() - end + give_commands_to_unit_groups() + return + end + + if game.tick % 3600 == 0 then + time_out_biters() + set_target() + give_commands_to_unit_groups() + end end local function on_init() global.wave_defense = { surface = game.surfaces["nauvis"], active_biters = {}, - max_active_biters = 2048, + unit_groups = {}, + max_active_biters = 1024, max_group_size = 256, + max_biter_age = 3600 * 30, active_biter_count = 0, spawn_position = {x = 0, y = 48}, - --next_wave = 3600 * 15, - next_wave = 60, - wave_interval = 60, + next_wave = 3600 * 0.15, + wave_interval = 1800, wave_number = 0, + game_lost = false, threat = 0, } end local event = require 'utils.event' -event.on_nth_tick(60, on_tick) +event.on_nth_tick(30, on_tick) event.on_init(on_init) event.add(defines.events.on_entity_died, on_entity_died) \ No newline at end of file