--tank battles (royale)-- mewmew made this -- local event = require 'utils.event' local table_insert = table.insert local math_random = math.random local map_functions = require "maps.tools.map_functions" local simplex_noise = require 'utils.simplex_noise' simplex_noise = simplex_noise.d2 local arena_size = 160 local function shuffle(tbl) local size = #tbl for i = size, 1, -1 do local rand = math.random(size) tbl[i], tbl[rand] = tbl[rand], tbl[i] end return tbl end local function get_noise(name, pos) local seed = global.noise_seed local noise = {} local noise_seed_add = 25000 if name == "rocks" then noise[1] = simplex_noise(pos.x * 0.02, pos.y * 0.02, seed) seed = seed + noise_seed_add noise[2] = simplex_noise(pos.x * 0.1, pos.y * 0.1, seed) seed = seed + noise_seed_add local noise = noise[1] + noise[2] * 0.2 return noise end seed = seed + noise_seed_add seed = seed + noise_seed_add if name == "rocks_2" then noise[1] = simplex_noise(pos.x * 0.02, pos.y * 0.02, seed) seed = seed + noise_seed_add noise[2] = simplex_noise(pos.x * 0.1, pos.y * 0.1, seed) seed = seed + noise_seed_add local noise = noise[1] + noise[2] * 0.2 return noise end seed = seed + noise_seed_add seed = seed + noise_seed_add if name == "terrain" then noise[1] = simplex_noise(pos.x * 0.02, pos.y * 0.02, seed) seed = seed + noise_seed_add noise[2] = simplex_noise(pos.x * 0.1, pos.y * 0.1, seed) seed = seed + noise_seed_add local noise = noise[1] + noise[2] * 0.2 return noise end end local function create_tank_battle_score_gui() for _, player in pairs (game.connected_players) do if player.gui.left["tank_battle_score"] then player.gui.left["tank_battle_score"].destroy() end local frame = player.gui.left.add({type = "frame", name = "tank_battle_score", direction = "vertical"}) local l = frame.add({type = "label", caption = "Won rounds"}) l.style.font_color = {r=0.98, g=0.66, b=0.22} l.style.font = "default-listbox" local t = frame.add({type = "table", column_count = 2}) local scores = {} for _, player in pairs(game.connected_players) do if global.tank_battles_score[player.index] then table_insert(scores, {name = player.name, score = global.tank_battles_score[player.index]}) end end for x = 1, #scores, 1 do for y = 1, #scores, 1 do if not scores[y + 1] then break end if scores[y]["score"] < scores[y + 1]["score"] then local key = scores[y] scores[y] = scores[y + 1] scores[y + 1] = key end end end for i = 1, 8, 1 do if scores[i] then local l = t.add({type = "label", caption = scores[i].name}) local color = game.players[scores[i].name].color color = {r = color.r * 0.6 + 0.4, g = color.g * 0.6 + 0.4, b = color.b * 0.6 + 0.4, a = 1} l.style.font_color = color l.style.font = "default-bold" t.add({type = "label", caption = scores[i].score}) end end end end local loot = { --{{name = "submachine-gun", count = 1}, weight = 2}, --{{name = "combat-shotgun", count = 1}, weight = 2}, --{{name = "flamethrower", count = 1}, weight = 1}, --{{name = "rocket-launcher", count = 1}, weight = 2}, {{name = "flamethrower-ammo", count = 16}, weight = 2}, {{name = "piercing-shotgun-shell", count = 16}, weight = 2}, --{{name = "piercing-rounds-magazine", count = 16}, weight = 2}, --{{name = "uranium-rounds-magazine", count = 8}, weight = 1}, {{name = "explosive-rocket", count = 8}, weight = 2}, {{name = "rocket", count = 8}, weight = 2}, {{name = "grenade", count = 16}, weight = 2}, {{name = "cluster-grenade", count = 8}, weight = 2}, --{{name = "poison-capsule", count = 4}, weight = 1}, {{name = "defender-capsule", count = 6}, weight = 1}, {{name = "distractor-capsule", count = 3}, weight = 1}, --{{name = "destroyer-capsule", count = 1}, weight = 1}, {{name = "cannon-shell", count = 8}, weight = 16}, {{name = "explosive-cannon-shell", count = 8}, weight = 16}, {{name = "uranium-cannon-shell", count = 8}, weight = 6}, {{name = "explosive-uranium-cannon-shell", count = 8}, weight = 6}, --{{name = "light-armor", count = 1}, weight = 5}, --{{name = "heavy-armor", count = 1}, weight = 2}, {{name = "modular-armor", count = 1}, weight = 3}, {{name = "power-armor", count = 1}, weight = 2}, {{name = "power-armor-mk2", count = 1}, weight = 1}, --{{name = "battery-mk2-equipment", count = 1}, weight = 2}, {{name = "energy-shield-equipment", count = 1}, weight = 2}, {{name = "exoskeleton-equipment", count = 1}, weight = 2}, {{name = "fusion-reactor-equipment", count = 1}, weight = 2}, {{name = "repair-pack", count = 1}, weight = 6}, {{name = "coal", count = 16}, weight = 3}, --{{name = "solid-fuel", count = 8}, weight = 2}, {{name = "nuclear-fuel", count = 1}, weight = 1}, {{name = "gate", count = 16}, weight = 2}, {{name = "stone-wall", count = 16}, weight = 2} } local loot_raffle = {} for _, item in pairs(loot) do for x = 1, item.weight, 1 do table.insert(loot_raffle, item[1]) end end local function get_valid_random_spawn_position(surface) local chunks = {} for chunk in surface.get_chunks() do table_insert(chunks, {x = chunk.x, y = chunk.y}) end chunks = shuffle(chunks) for _, chunk in pairs(chunks) do if chunk.x * 32 < arena_size and chunk.y * 32 < arena_size and chunk.x * 32 >= arena_size * -1 and chunk.y * 32 >= arena_size * -1 then local area = {{chunk.x * 32 - 64, chunk.y * 32 - 64}, {chunk.x * 32 + 64, chunk.y * 32 + 64}} if surface.count_entities_filtered({name = "tank", area = area}) == 0 then local pos = surface.find_non_colliding_position("tank", {chunk.x * 32 + 16, chunk.y * 32 + 16}, 16, 8) return pos end end end local pos = surface.find_non_colliding_position("tank", {0, 0}, 32, 4) if pos then return pos end return {0, 0} end local function put_players_into_arena() for _, player in pairs(game.connected_players) do local permissions_group = game.permissions.get_group("Default") permissions_group.add_player(player.name) if player.character then player.character.destroy() player.character = nil end player.create_character() player.insert({name = "combat-shotgun", count = 1}) player.insert({name = "rocket-launcher", count = 1}) player.insert({name = "flamethrower", count = 1}) local surface = game.surfaces["nauvis"] local pos = get_valid_random_spawn_position(surface) player.force.chart(surface,{{x = -1 * arena_size, y = -1 * arena_size}, {x = arena_size, y = arena_size}}) player.teleport(pos, surface) local tank = surface.create_entity({name = "tank", force = game.forces[player.index], position = pos}) tank.insert({name = "coal", count = 24}) tank.insert({name = "cannon-shell", count = 16}) tank.set_driver(player) end end local function get_arena_layout_modifiers() global.arena_layout_modifiers = {} local tree_raffle = {} for _, e in pairs(game.entity_prototypes) do if e.type == "tree" then table.insert(tree_raffle, e.name) end end global.arena_layout_modifiers.arena_tree = tree_raffle[math_random(1, #tree_raffle)] global.arena_layout_modifiers.arena_tree_chance = math_random(4, 20) global.arena_layout_modifiers.arena_tree_noise = math_random(0, 75) * 0.01 local entity_raffle = {} local types = {"furnace", "assembling-machine", "power-switch", "programmable-speaker", "reactor"} for _, e in pairs(game.entity_prototypes) do for _, t in pairs(types) do if e.type == t then table.insert(entity_raffle, e.name) end end end global.arena_layout_modifiers.secret_entity = entity_raffle[math_random(1, #entity_raffle)] local tile_raffle = {} local tile_blacklist = { ["water"] = true, ["deepwater"] = true, ["water-green"] = true, ["deepwater-green"] = true, ["out-of-map"] = true, ["hazard-concrete-left"] = true, ["hazard-concrete-right"] = true, ["lab-dark-1"] = true, ["lab-dark-2"] = true, ["lab-white"] = true, ["refined-hazard-concrete-left"] = true, ["refined-hazard-concrete-right"] = true, ["tutorial-grid"] = true } for _, t in pairs(game.tile_prototypes) do if not tile_blacklist[t.name] then table.insert(tile_raffle, t.name) end end global.arena_layout_modifiers.arena_tile_1 = tile_raffle[math_random(1, #tile_raffle)] global.arena_layout_modifiers.arena_tile_2 = tile_raffle[math_random(1, #tile_raffle)] end local function regenerate_arena() local surface = game.surfaces["nauvis"] for chunk in surface.get_chunks() do surface.set_chunk_generated_status(chunk, defines.chunk_generated_status.custom_tiles ) end global.noise_seed = nil surface.request_to_generate_chunks({0,0}, math.ceil(arena_size / 32) + 3) get_arena_layout_modifiers() surface.force_generate_chunk_requests() surface.daytime = 1 surface.freeze_daytime = 1 global.current_arena_size = arena_size put_players_into_arena() global.game_stage = "ongoing_game" end function shrink_arena() local surface = game.surfaces["nauvis"] if global.current_arena_size < 0 then return end local shrink_width = 8 local current_arena_size = global.current_arena_size local tiles = {} for x = arena_size * -1, arena_size, 1 do for y = current_arena_size * -1 - shrink_width, current_arena_size * -1, 1 do local pos = {x = x, y = y} if surface.get_tile(pos).name ~= "water" and surface.get_tile(pos).name ~= "deepwater" then if x > current_arena_size or y > current_arena_size or x < current_arena_size * -1 or y < current_arena_size * -1 then if math_random(1, 3) ~= 1 then table_insert(tiles, {name = "water", position = pos}) end end end end end for x = arena_size * -1, arena_size, 1 do for y = current_arena_size, current_arena_size + shrink_width, 1 do local pos = {x = x, y = y} if surface.get_tile(pos).name ~= "water" and surface.get_tile(pos).name ~= "deepwater" then if x > current_arena_size or y > current_arena_size or x < current_arena_size * -1 or y < current_arena_size * -1 then if math_random(1, 3) ~= 1 then table_insert(tiles, {name = "water", position = pos}) end end end end end for x = current_arena_size * -1 - shrink_width, current_arena_size * -1, 1 do for y = arena_size * -1, arena_size, 1 do local pos = {x = x, y = y} if surface.get_tile(pos).name ~= "water" and surface.get_tile(pos).name ~= "deepwater" then if x > current_arena_size or y > current_arena_size or x < current_arena_size * -1 or y < current_arena_size * -1 then if math_random(1, 3) ~= 1 then table_insert(tiles, {name = "water", position = pos}) end end end end end for x = current_arena_size, current_arena_size + shrink_width, 1 do for y = arena_size * -1, arena_size, 1 do local pos = {x = x, y = y} if surface.get_tile(pos).name ~= "water" and surface.get_tile(pos).name ~= "deepwater" then if x > current_arena_size or y > current_arena_size or x < current_arena_size * -1 or y < current_arena_size * -1 then if math_random(1, 3) ~= 1 then table_insert(tiles, {name = "water", position = pos}) end end end end end global.current_arena_size = global.current_arena_size - 1 surface.set_tiles(tiles, true) end local function get_arena_entity(surface, pos) local entity = false local noise = get_noise("rocks", pos) local noise2 = get_noise("rocks_2", pos) if noise > -0.1 and noise < 0.1 and noise2 > -0.3 and noise2 < 0.3 then return {name = "rock-big", position = pos} end if math_random(1, 16) == 1 and noise2 > 0.78 then if surface.can_place_entity({name = "wooden-chest", position = pos, force = "enemy"}) then return {name = "wooden-chest", position = pos, force = "enemy"} end end if math_random(1, 16) == 1 and noise2 < -0.78 then if surface.can_place_entity({name = "wooden-chest", position = pos, force = "enemy"}) then return {name = "wooden-chest", position = pos, force = "enemy"} end end if math_random(1, global.arena_layout_modifiers.arena_tree_chance) == 1 and noise > global.arena_layout_modifiers.arena_tree_noise then return {name = global.arena_layout_modifiers.arena_tree, position = pos} end if math_random(1, 1024) == 1 then if math_random(1, 16) == 1 then if surface.can_place_entity({name = global.arena_layout_modifiers.secret_entity, position = pos, force = "enemy"}) then return {name = global.arena_layout_modifiers.secret_entity, position = pos, force = "enemy"} end end if math_random(1, 64) == 1 then if surface.can_place_entity({name = "big-worm-turret", position = pos, force = "enemy"}) then return {name = "big-worm-turret", position = pos, force = "enemy"} end end if math_random(1, 32) == 1 then if surface.can_place_entity({name = "medium-worm-turret", position = pos, force = "enemy"}) then return {name = "medium-worm-turret", position = pos, force = "enemy"} end end if math_random(1, 512) == 1 then if surface.can_place_entity({name = "behemoth-biter", position = pos, force = "enemy"}) then return {name = "behemoth-biter", position = pos, force = "enemy"} end end if math_random(1, 64) == 1 then if surface.can_place_entity({name = "big-biter", position = pos, force = "enemy"}) then return {name = "big-biter", position = pos, force = "enemy"} end end end end local function render_arena_chunk(event) if not global.noise_seed then global.noise_seed = math_random(1, 2097152) end local surface = event.surface local left_top = event.area.left_top for _, entity in pairs(surface.find_entities_filtered({area = event.area})) do if entity.name ~= "player" then entity.destroy() end end local tiles = {} 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 pos.x > arena_size or pos.y > arena_size or pos.x < arena_size * -1 or pos.y < arena_size * -1 then table_insert(tiles, {name = "water", position = pos}) else local noise = get_noise("terrain", pos) if noise > 0 then table_insert(tiles, {name = global.arena_layout_modifiers.arena_tile_1, position = pos}) else table_insert(tiles, {name = global.arena_layout_modifiers.arena_tile_2, position = pos}) end local entity = get_arena_entity(surface, pos) if entity then surface.create_entity(entity) end end end end surface.set_tiles(tiles, true) end local function kill_idle_players() for _, player in pairs(game.connected_players) do if player.character then if player.afk_time > 600 then local area = {{player.position.x - 1, player.position.y - 1}, {player.position.x + 1, player.position.y + 1}} local water_tile_count = player.surface.count_tiles_filtered({name = {"water", "deepwater"}, area = area}) if water_tile_count > 3 then player.character.die() game.print(player.name .. " drowned.", {r = 150, g = 150, b = 0}) else if player.afk_time > 9000 then player.character.die() game.print(player.name .. " was idle for too long.", {r = 150, g = 150, b = 0}) end end end end end end local function check_for_game_over() local surface = game.surfaces["nauvis"] kill_idle_players() local alive_players = 0 for _, player in pairs(game.connected_players) do if player.character then alive_players = alive_players + 1 end end if alive_players > 1 then return end ----------------- local player for _, p in pairs(game.connected_players) do if p.character then player = p end end if alive_players == 1 then if not global.tank_battles_score[player.index] then global.tank_battles_score[player.index] = 1 else global.tank_battles_score[player.index] = global.tank_battles_score[player.index] + 1 end game.print(player.name .. " has won the battle!", {r = 150, g = 150, b = 0}) create_tank_battle_score_gui() end if alive_players == 0 then game.print("No alive players! Round ends in a draw!", {r = 150, g = 150, b = 0}) end global.game_stage = "lobby" end local function set_unique_player_force(player) if not game.forces[player.index] then game.create_force(player.index) game.forces[player.index].technologies["follower-robot-count-1"].researched = true game.forces[player.index].technologies["follower-robot-count-2"].researched = true game.forces[player.index].technologies["follower-robot-count-3"].researched = true game.forces[player.index].technologies["follower-robot-count-4"].researched = true game.forces[player.index].technologies["follower-robot-count-5"].researched = true end player.force = game.forces[player.index] end local function on_player_joined_game(event) local player = game.players[event.player_index] set_unique_player_force(player) if not global.map_init_done then local spectator_permission_group = game.permissions.create_group("Spectator") for action_name, _ in pairs(defines.input_action) do spectator_permission_group.set_allows_action(defines.input_action[action_name], false) end spectator_permission_group.set_allows_action(defines.input_action.write_to_console, true) spectator_permission_group.set_allows_action(defines.input_action.gui_click, true) spectator_permission_group.set_allows_action(defines.input_action.gui_selection_state_changed, true) spectator_permission_group.set_allows_action(defines.input_action.start_walking, true) spectator_permission_group.set_allows_action(defines.input_action.open_kills_gui, true) spectator_permission_group.set_allows_action(defines.input_action.open_character_gui, true) --spectator_permission_group.set_allows_action(defines.input_action.open_equipment_gui, true) spectator_permission_group.set_allows_action(defines.input_action.edit_permission_group, true) spectator_permission_group.set_allows_action(defines.input_action.toggle_show_entity_info, true) get_arena_layout_modifiers() global.tank_battles_score = {} global.game_stage = "lobby" global.map_init_done = true end if #global.tank_battles_score > 0 then create_tank_battle_score_gui() end if game.surfaces["nauvis"] then player.character.destroy() player.character = nil local permissions_group = game.permissions.get_group("Spectator") permissions_group.add_player(player.name) player.teleport({0, 0}, game.surfaces["nauvis"]) if global.lobby_timer then global.lobby_timer = 1200 end end end local function on_marked_for_deconstruction(event) event.entity.cancel_deconstruction(game.players[event.player_index].force.name) end local function on_chunk_generated(event) render_arena_chunk(event) end local function on_player_respawned(event) local player = game.players[event.player_index] local permissions_group = game.permissions.get_group("Spectator") permissions_group.add_player(player.name) player.character.destroy() player.character = nil player.print("You are now spectating.", {r = 0, g = 150, b = 150}) end local function lobby() local connected_players_count = 0 local permissions_group = game.permissions.get_group("Default") for _, player in pairs(game.connected_players) do connected_players_count = connected_players_count + 1 end if connected_players_count < 2 then --game.print("Waiting for players.", {r = 0, g = 150, b = 150}) return end if not global.lobby_timer then global.lobby_timer = 1200 end if global.lobby_timer % 600 == 0 then if global.lobby_timer <= 0 then game.print("Round has started!", {r = 0, g = 150, b = 150}) else game.print("Round will begin in " .. global.lobby_timer / 60 .. " seconds.", {r = 0, g = 150, b = 150}) end end global.lobby_timer = global.lobby_timer - 300 if global.lobby_timer >= 0 then return end global.lobby_timer = nil global.game_stage = "regenerate_arena" end local function on_tick(event) if game.tick % 300 == 0 then if global.game_stage == "lobby" then lobby() end if global.game_stage == "regenerate_arena" then regenerate_arena() end if global.game_stage == "ongoing_game" then shrink_arena() check_for_game_over() end end end local function on_entity_died(event) if event.entity.name == "wooden-chest" then local loot = loot_raffle[math_random(1, #loot_raffle)] event.entity.surface.spill_item_stack(event.entity.position, loot, true) --event.entity.surface.create_entity({name = "flying-text", position = event.entity.position, text = loot.name, color = {r=0.98, g=0.66, b=0.22}}) end end local function on_player_died(event) local player = game.players[event.player_index] local str = " " if event.cause then if event.cause.name ~= nil then str = " by " .. event.cause.name end if event.cause.name == "player" then str = " by " .. event.cause.player.name end if event.cause.name == "tank" then local driver = event.cause.get_driver() if driver.player then str = " by " .. driver.player.name end end end for _, target_player in pairs(game.connected_players) do if target_player.name ~= player.name then player.print(player.name .. " was killed" .. str, { r=0.99, g=0.0, b=0.0}) end end end ----------share chat with player and spectator force------------------- local function on_console_chat(event) if not event.message then return end if not event.player_index then return end local player = game.players[event.player_index] local color = {} color = player.color color.r = color.r * 0.6 + 0.35 color.g = color.g * 0.6 + 0.35 color.b = color.b * 0.6 + 0.35 color.a = 1 for _, target_player in pairs(game.connected_players) do if target_player.name ~= player.name then target_player.print(player.name .. ": ".. event.message, color) end end end event.add(defines.events.on_tick, on_tick) event.add(defines.events.on_console_chat, on_console_chat) event.add(defines.events.on_entity_died, on_entity_died) event.add(defines.events.on_entity_damaged, on_entity_damaged) event.add(defines.events.on_player_died, on_player_died) event.add(defines.events.on_marked_for_deconstruction, on_marked_for_deconstruction) event.add(defines.events.on_player_joined_game, on_player_joined_game) event.add(defines.events.on_player_respawned, on_player_respawned) event.add(defines.events.on_chunk_generated, on_chunk_generated)