--luacheck:ignore local Public = {} local Server = require 'utils.server' local bb_config = require 'maps.biter_battles_v2.config' local event = require 'utils.event' local Functions = require 'maps.biter_battles_v2.functions' local feed_the_biters = require 'maps.biter_battles_v2.feeding' local Tables = require 'maps.biter_battles_v2.tables' local wait_messages = Tables.wait_messages local food_names = Tables.gui_foods local math_random = math.random require 'maps.biter_battles_v2.spec_spy' local gui_values = { ['north'] = { force = 'north', biter_force = 'north_biters', c1 = bb_config.north_side_team_name, c2 = 'JOIN ', n1 = 'join_north_button', t1 = 'Evolution of north side biters.', t2 = 'Threat causes biters to attack. Reduces when biters are slain.', color1 = { r = 0.55, g = 0.55, b = 0.99 }, color2 = { r = 0.66, g = 0.66, b = 0.99 }, tech_spy = 'spy-north-tech', prod_spy = 'spy-north-prod' }, ['south'] = { force = 'south', biter_force = 'south_biters', c1 = bb_config.south_side_team_name, c2 = 'JOIN ', n1 = 'join_south_button', t1 = 'Evolution of south side biters.', t2 = 'Threat causes biters to attack. Reduces when biters are slain.', color1 = { r = 0.99, g = 0.33, b = 0.33 }, color2 = { r = 0.99, g = 0.44, b = 0.44 }, tech_spy = 'spy-south-tech', prod_spy = 'spy-south-prod' } } local function create_sprite_button(player) if player.gui.top['bb_toggle_button'] then return end local button = player.gui.top.add({ type = 'sprite-button', name = 'bb_toggle_button', sprite = 'entity/big-biter' }) button.style.font = 'default-bold' button.style.minimal_height = 38 button.style.minimal_width = 38 button.style.padding = -1 end local function create_first_join_gui(player) if not storage.game_lobby_timeout then storage.game_lobby_timeout = 5999940 end if storage.game_lobby_timeout - game.tick < 0 then storage.game_lobby_active = false end local frame = player.gui.left.add { type = 'frame', name = 'bb_main_gui', direction = 'vertical' } local b = frame.add { type = 'label', caption = 'Defend your Rocket Silo!' } b.style.font = 'heading-1' b.style.font_color = { r = 0.98, g = 0.66, b = 0.22 } local b = frame.add { type = 'label', caption = "Feed the enemy team's biters to gain advantage!" } b.style.font = 'heading-2' b.style.font_color = { r = 0.98, g = 0.66, b = 0.22 } frame.add { type = 'label', caption = '-----------------------------------------------------------' } for _, gui_value in pairs(gui_values) do local t = frame.add { type = 'table', column_count = 3 } local c = gui_value.c1 if storage.tm_custom_name[gui_value.force] then c = storage.tm_custom_name[gui_value.force] end local l = t.add { type = 'label', caption = c } l.style.font = 'heading-2' l.style.font_color = gui_value.color1 l.style.single_line = false l.style.maximal_width = 290 local l = t.add { type = 'label', caption = ' - ' } local l = t.add { type = 'label', caption = #game.forces[gui_value.force].connected_players .. ' Players ' } l.style.font_color = { r = 0.22, g = 0.88, b = 0.22 } local c = gui_value.c2 local font_color = gui_value.color1 if storage.game_lobby_active then font_color = { r = 0.7, g = 0.7, b = 0.7 } c = c .. ' (waiting for players... ' c = c .. math.ceil((storage.game_lobby_timeout - game.tick) / 60) c = c .. ')' end local t = frame.add { type = 'table', column_count = 4 } for _, p in pairs(game.forces[gui_value.force].connected_players) do local l = t.add({ type = 'label', caption = p.name }) l.style.font_color = { r = p.color.r * 0.6 + 0.4, g = p.color.g * 0.6 + 0.4, b = p.color.b * 0.6 + 0.4, a = 1 } l.style.font = 'heading-2' end local b = frame.add { type = 'sprite-button', name = gui_value.n1, caption = c } b.style.font = 'default-large-bold' b.style.font_color = font_color b.style.minimal_width = 350 frame.add { type = 'label', caption = '-----------------------------------------------------------' } end end local function add_tech_button(elem, gui_value) local tech_button = elem.add { type = 'sprite-button', name = gui_value.tech_spy, sprite = 'item/space-science-pack' } tech_button.style.height = 25 tech_button.style.width = 25 tech_button.style.left_margin = 3 end local function add_prod_button(elem, gui_value) local prod_button = elem.add { type = 'sprite-button', name = gui_value.prod_spy, sprite = 'item/assembling-machine-3' } prod_button.style.height = 25 prod_button.style.width = 25 end function Public.create_main_gui(player) local is_spec = player.force.name == 'spectator' if player.gui.left['bb_main_gui'] then player.gui.left['bb_main_gui'].destroy() end if storage.bb_game_won_by_team then return end if not storage.chosen_team[player.name] then if not storage.tournament_mode then create_first_join_gui(player) return end end local frame = player.gui.left.add { type = 'frame', name = 'bb_main_gui', direction = 'vertical' } -- Science sending GUI if not is_spec then frame.add { type = 'table', name = 'biter_battle_table', column_count = 4 } local t = frame.biter_battle_table for food_name, tooltip in pairs(food_names) do local s = t.add { type = 'sprite-button', name = food_name, sprite = 'item/' .. food_name } s.tooltip = tooltip s.style.minimal_height = 41 s.style.minimal_width = 41 s.style.top_padding = 0 s.style.left_padding = 0 s.style.right_padding = 0 s.style.bottom_padding = 0 end end local first_team = true for k, gui_value in pairs(gui_values) do -- Line separator if not first_team then frame.add { type = 'line', caption = 'this line', direction = 'horizontal' } else first_team = false end -- Team name & Player count local t = frame.add { type = 'table', column_count = 4 } -- Team name local c = gui_value.c1 if storage.tm_custom_name[gui_value.force] then c = storage.tm_custom_name[gui_value.force] end local l = t.add { type = 'label', caption = c } l.style.font = 'default-bold' l.style.font_color = gui_value.color1 l.style.single_line = false l.style.maximal_width = 102 -- Number of players local l = t.add { type = 'label', caption = ' - ' } local c = #game.forces[gui_value.force].connected_players .. ' Player' if #game.forces[gui_value.force].connected_players ~= 1 then c = c .. 's' end local l = t.add { type = 'label', caption = c } l.style.font = 'default' l.style.font_color = { r = 0.22, g = 0.88, b = 0.22 } -- Tech button if is_spec then add_tech_button(t, gui_value) -- add_prod_button(t, gui_value) end -- Player list if storage.bb_view_players[player.name] == true then local t = frame.add { type = 'table', column_count = 4 } for _, p in pairs(game.forces[gui_value.force].connected_players) do local l = t.add { type = 'label', caption = p.name } l.style.font_color = { r = p.color.r * 0.6 + 0.4, g = p.color.g * 0.6 + 0.4, b = p.color.b * 0.6 + 0.4, a = 1 } end end -- Statistics local t = frame.add { type = 'table', name = 'stats_' .. gui_value.force, column_count = 5 } -- Evolution local l = t.add { type = 'label', caption = 'Evo:' } --l.style.minimal_width = 25 local biter_force = game.forces[gui_value.biter_force] local tooltip = gui_value.t1 .. '\nHealth: ' .. Functions.get_health_modifier(biter_force) * 100 .. '%' .. '\nDamage: ' .. (biter_force.get_ammo_damage_modifier('melee') + 1) * 100 .. '%' l.tooltip = tooltip local evo = math.floor(1000 * storage.bb_evolution[gui_value.biter_force]) * 0.1 local l = t.add { type = 'label', caption = evo .. '%' } l.style.minimal_width = 40 l.style.font_color = gui_value.color2 l.style.font = 'default-bold' l.tooltip = tooltip -- Threat local l = t.add { type = 'label', caption = 'Threat: ' } l.style.minimal_width = 25 l.tooltip = gui_value.t2 local l = t.add { type = 'label', name = 'threat_' .. gui_value.force, caption = math.floor(storage.bb_threat[gui_value.biter_force]) } l.style.font_color = gui_value.color2 l.style.font = 'default-bold' l.style.width = 50 l.tooltip = gui_value.t2 end -- Action frame local t = frame.add { type = 'table', column_count = 2 } -- Spectate / Rejoin team if is_spec then local b = t.add { type = 'sprite-button', name = 'bb_leave_spectate', caption = 'Join Team' } else local b = t.add { type = 'sprite-button', name = 'bb_spectate', caption = 'Spectate' } end -- Playerlist button if storage.bb_view_players[player.name] == true then local b = t.add { type = 'sprite-button', name = 'bb_hide_players', caption = 'Playerlist' } else local b = t.add { type = 'sprite-button', name = 'bb_view_players', caption = 'Playerlist' } end local b_width = is_spec and 97 or 86 -- 111 when prod_spy button will be there for _, b in pairs(t.children) do b.style.font = 'default-bold' b.style.font_color = { r = 0.98, g = 0.66, b = 0.22 } b.style.top_padding = 1 b.style.left_padding = 1 b.style.right_padding = 1 b.style.bottom_padding = 1 b.style.maximal_height = 30 b.style.width = b_width end end function Public.refresh() for _, player in pairs(game.connected_players) do if player.gui.left['bb_main_gui'] then Public.create_main_gui(player) end end storage.gui_refresh_delay = game.tick + 5 end function Public.refresh_threat() if storage.gui_refresh_delay > game.tick then return end for _, player in pairs(game.connected_players) do if player.gui.left['bb_main_gui'] then if player.gui.left['bb_main_gui'].stats_north then player.gui.left['bb_main_gui'].stats_north.threat_north.caption = math.floor(storage.bb_threat['north_biters']) player.gui.left['bb_main_gui'].stats_south.threat_south.caption = math.floor(storage.bb_threat['south_biters']) end end end storage.gui_refresh_delay = game.tick + 5 end function join_team(player, force_name, forced_join) if not player.character then return end if not forced_join then if storage.tournament_mode then player.print('The game is set to tournament mode. Teams can only be changed via team manager.', { r = 0.98, g = 0.66, b = 0.22 }) return end end if not force_name then return end local surface = player.surface local enemy_team = 'south' if force_name == 'south' then enemy_team = 'north' end if not storage.training_mode and storage.bb_settings.team_balancing then if not forced_join then if #game.forces[force_name].connected_players > #game.forces[enemy_team].connected_players then if not storage.chosen_team[player.name] then player.print('Team ' .. force_name .. ' has too many players currently.', { r = 0.98, g = 0.66, b = 0.22 }) return end end end end if storage.chosen_team[player.name] then if not forced_join then if game.tick - storage.spectator_rejoin_delay[player.name] < 3600 then player.print( 'Not ready to return to your team yet. Please wait ' .. 60 - (math.floor((game.tick - storage.spectator_rejoin_delay[player.name]) / 60)) .. ' seconds.', { r = 0.98, g = 0.66, b = 0.22 } ) return end end local p = surface.find_non_colliding_position('character', game.forces[force_name].get_spawn_position(surface), 16, 0.5) if not p then game.print('No spawn position found for ' .. player.name .. '!', { 255, 0, 0 }) return end player.teleport(p, surface) player.force = game.forces[force_name] player.character.destructible = true Public.refresh() game.permissions.get_group('Default').add_player(player) local msg = table.concat({ 'Team ', player.force.name, ' player ', player.name, ' is no longer spectating.' }) game.print(msg, { r = 0.98, g = 0.66, b = 0.22 }) Server.to_discord_bold(msg) player.spectator = false return end local pos = surface.find_non_colliding_position('character', game.forces[force_name].get_spawn_position(surface), 8, 1) if not pos then pos = game.forces[force_name].get_spawn_position(surface) end player.teleport(pos) player.force = game.forces[force_name] player.character.destructible = true game.permissions.get_group('Default').add_player(player) if not forced_join then local c = player.force.name if storage.tm_custom_name[player.force.name] then c = storage.tm_custom_name[player.force.name] end local message = table.concat({ player.name, ' has joined team ', c, '!' }) game.print(message, { r = 0.98, g = 0.66, b = 0.22 }) Server.to_discord_bold(message) end local i = player.get_inventory(defines.inventory.character_main) i.clear() player.insert { name = 'pistol', count = 1 } player.insert { name = 'raw-fish', count = 3 } player.insert { name = 'firearm-magazine', count = 32 } player.insert { name = 'iron-gear-wheel', count = 8 } player.insert { name = 'iron-plate', count = 16 } if bb_config.additional_starting_resources then player.insert { name = 'burner-mining-drill', count = 10 } end storage.chosen_team[player.name] = force_name player.spectator = false Public.refresh() end function spectate(player, forced_join) if not player.character then return end if not forced_join then if storage.tournament_mode then player.print('The game is set to tournament mode. Teams can only be changed via team manager.', { r = 0.98, g = 0.66, b = 0.22 }) return end end player.teleport(player.surface.find_non_colliding_position('character', { 0, 0 }, 4, 1)) player.force = game.forces.spectator player.character.destructible = false if not forced_join then local msg = player.name .. ' is spectating.' game.print(msg, { r = 0.98, g = 0.66, b = 0.22 }) Server.to_discord_bold(msg) end game.permissions.get_group('spectator').add_player(player) storage.spectator_rejoin_delay[player.name] = game.tick Public.create_main_gui(player) player.spectator = true end local function join_gui_click(name, player) local team = { ['join_north_button'] = 'north', ['join_south_button'] = 'south' } if not team[name] then return end if storage.game_lobby_active then if player.admin then join_team(player, team[name]) game.print('Lobby disabled, admin override.', { r = 0.98, g = 0.66, b = 0.22 }) storage.game_lobby_active = false return end player.print('Waiting for more players, ' .. wait_messages[math_random(1, #wait_messages)], { r = 0.98, g = 0.66, b = 0.22 }) return end join_team(player, team[name]) end local spy_forces = { { 'north', 'south' }, { 'south', 'north' } } function Public.spy_fish() for _, f in pairs(spy_forces) do if storage.spy_fish_timeout[f[1]] - game.tick > 0 then local r = 96 local surface = game.surfaces['biter_battles'] for _, player in pairs(game.forces[f[2]].connected_players) do game.forces[f[1]].chart(surface, { { player.position.x - r, player.position.y - r }, { player.position.x + r, player.position.y + r } }) end else storage.spy_fish_timeout[f[1]] = 0 end end end local function on_gui_click(event) if not event.element then return end if not event.element.valid then return end local player = game.players[event.player_index] local name = event.element.name if name == 'bb_toggle_button' then if player.gui.left['bb_main_gui'] then player.gui.left['bb_main_gui'].destroy() else Public.create_main_gui(player) end return end if name == 'join_north_button' then join_gui_click(name, player) return end if name == 'join_south_button' then join_gui_click(name, player) return end if name == 'raw-fish' then Functions.spy_fish(player) return end if food_names[name] then feed_the_biters(player, name) return end if name == 'bb_leave_spectate' then join_team(player, storage.chosen_team[player.name]) end if name == 'bb_spectate' then if player.position.y ^ 2 + player.position.x ^ 2 < 12000 then spectate(player) else player.print('You are too far away from spawn to spectate.', { r = 0.98, g = 0.66, b = 0.22 }) end return end if name == 'bb_hide_players' then storage.bb_view_players[player.name] = false Public.create_main_gui(player) end if name == 'bb_view_players' then storage.bb_view_players[player.name] = true Public.create_main_gui(player) end end local function on_player_joined_game(event) local player = game.players[event.player_index] if not storage.bb_view_players then storage.bb_view_players = {} end if not storage.chosen_team then storage.chosen_team = {} end storage.bb_view_players[player.name] = false if #game.connected_players > 1 then storage.game_lobby_timeout = math.ceil(36000 / #game.connected_players) else storage.game_lobby_timeout = 599940 end --if not storage.chosen_team[player.name] then -- if storage.tournament_mode then -- player.force = game.forces.spectator -- else -- player.force = game.forces.player -- end --end create_sprite_button(player) Public.create_main_gui(player) end event.add(defines.events.on_gui_click, on_gui_click) event.add(defines.events.on_player_joined_game, on_player_joined_game) return Public