--luacheck:ignore local string_sub = string.sub local math_random = math.random local math_round = math.round local math_abs = math.abs local table_insert = table.insert local table_remove = table.remove local string_find = string.find local balance_functions = { ['flamethrower'] = function(force_name) global.combat_balance[force_name].flamethrower_damage = -0.65 game.forces[force_name].set_turret_attack_modifier('flamethrower-turret', global.combat_balance[force_name].flamethrower_damage) game.forces[force_name].set_ammo_damage_modifier('flamethrower', global.combat_balance[force_name].flamethrower_damage) end, ['refined-flammables'] = function(force_name) global.combat_balance[force_name].flamethrower_damage = global.combat_balance[force_name].flamethrower_damage + 0.05 game.forces[force_name].set_turret_attack_modifier('flamethrower-turret', global.combat_balance[force_name].flamethrower_damage) game.forces[force_name].set_ammo_damage_modifier('flamethrower', global.combat_balance[force_name].flamethrower_damage) end, ['land-mine'] = function(force_name) if not global.combat_balance[force_name].land_mine then global.combat_balance[force_name].land_mine = -0.80 end game.forces[force_name].set_ammo_damage_modifier('landmine', global.combat_balance[force_name].land_mine) end, ['stronger-explosives'] = function(force_name) if not global.combat_balance[force_name].land_mine then global.combat_balance[force_name].land_mine = -0.80 end global.combat_balance[force_name].land_mine = global.combat_balance[force_name].land_mine + 0.05 game.forces[force_name].set_ammo_damage_modifier('landmine', global.combat_balance[force_name].land_mine) end, ['military'] = function(force_name) global.combat_balance[force_name].shotgun = 1 game.forces[force_name].set_ammo_damage_modifier('shotgun-shell', global.combat_balance[force_name].shotgun) end, ['physical-projectile-damage'] = function(force_name) global.combat_balance[force_name].shotgun = global.combat_balance[force_name].shotgun + 0.4 game.forces[force_name].set_ammo_damage_modifier('shotgun-shell', global.combat_balance[force_name].shotgun) end } local no_turret_blacklist = { ['ammo-turret'] = true, ['artillery-turret'] = true, ['electric-turret'] = true, ['fluid-turret'] = true } local landfill_biters_vectors = {{0, 0}, {1, 0}, {0, 1}, {-1, 0}, {0, -1}} local landfill_biters = { ['big-biter'] = true, ['big-spitter'] = true, ['behemoth-biter'] = true, ['behemoth-spitter'] = true } local target_entity_types = { ['assembling-machine'] = true, ['boiler'] = true, ['furnace'] = true, ['generator'] = true, ['lab'] = true, ['mining-drill'] = true, ['radar'] = true, ['reactor'] = true, ['roboport'] = true, ['rocket-silo'] = true, ['ammo-turret'] = true, ['artillery-turret'] = true, ['beacon'] = true, ['electric-turret'] = true, ['fluid-turret'] = true } local spawn_positions = {} local spawn_r = 7 local spawn_r_square = spawn_r ^ 2 for x = spawn_r * -1, spawn_r, 0.5 do for y = spawn_r * -1, spawn_r, 0.5 do if x ^ 2 + y ^ 2 < spawn_r_square then table.insert(spawn_positions, {x, y}) end end end local size_of_spawn_positions = #spawn_positions local Public = {} function Public.add_target_entity(entity) if not entity then return end if not entity.valid then return end if not target_entity_types[entity.type] then return end table_insert(global.target_entities[entity.force.index], entity) end function Public.get_random_target_entity(force_index) local target_entities = global.target_entities[force_index] local size_of_target_entities = #target_entities if size_of_target_entities == 0 then return end for _ = 1, size_of_target_entities, 1 do local i = math_random(1, size_of_target_entities) local entity = target_entities[i] if entity and entity.valid then return entity else table_remove(target_entities, i) size_of_target_entities = size_of_target_entities - 1 if size_of_target_entities == 0 then return end end end end function Public.get_health_modifier(force) if global.bb_evolution[force.name] < 1 then return 1 end return math_round((global.bb_evolution[force.name] - 1) * 3, 3) + 1 end function Public.biters_landfill(entity) if not landfill_biters[entity.name] then return end local position = entity.position if math_abs(position.y) < 8 then return true end local surface = entity.surface for _, vector in pairs(landfill_biters_vectors) do local tile = surface.get_tile({position.x + vector[1], position.y + vector[2]}) if tile.collides_with('resource-layer') then surface.set_tiles({{name = 'landfill', position = tile.position}}) local particle_pos = {tile.position.x + 0.5, tile.position.y + 0.5} for i = 1, 50, 1 do surface.create_particle( { name = 'stone-particle', position = particle_pos, frame_speed = 0.1, vertical_speed = 0.12, height = 0.01, movement = {-0.05 + math_random(0, 100) * 0.001, -0.05 + math_random(0, 100) * 0.001} } ) end end end return true end function Public.combat_balance(event) local research_name = event.research.name local force_name = event.research.force.name local key for b = 1, string.len(research_name), 1 do key = string_sub(research_name, 0, b) if balance_functions[key] then if not global.combat_balance[force_name] then global.combat_balance[force_name] = {} end balance_functions[key](force_name) return end end end function Public.init_player(player) if not player.connected then if player.force.index ~= 1 then player.force = game.forces.player end return end if player.character and player.character.valid then player.character.destroy() player.set_controller({type = defines.controllers.god}) player.create_character() end player.clear_items_inside() player.spectator = true player.force = game.forces.spectator local surface = game.surfaces.biter_battles local p = spawn_positions[math_random(1, size_of_spawn_positions)] if surface.is_chunk_generated({0, 0}) then player.teleport(surface.find_non_colliding_position('character', p, 4, 0.5), surface) else player.teleport(p, surface) end if player.character and player.character.valid then player.character.destructible = false end game.permissions.get_group('spectator').add_player(player) end function Public.no_turret_creep(event) local entity = event.created_entity if not entity.valid then return end if not no_turret_blacklist[event.created_entity.type] then return end local surface = event.created_entity.surface local spawners = surface.find_entities_filtered({type = 'unit-spawner', area = {{entity.position.x - 70, entity.position.y - 70}, {entity.position.x + 70, entity.position.y + 70}}}) if #spawners == 0 then return end local allowed_to_build = true for _, e in pairs(spawners) do if (e.position.x - entity.position.x) ^ 2 + (e.position.y - entity.position.y) ^ 2 < 4096 then allowed_to_build = false break end end if allowed_to_build then return end if event.player_index then game.players[event.player_index].insert({name = entity.name, count = 1}) else local inventory = event.robot.get_inventory(defines.inventory.robot_cargo) inventory.insert({name = entity.name, count = 1}) end surface.create_entity( { name = 'flying-text', position = entity.position, text = 'Turret too close to spawner!', color = {r = 0.98, g = 0.66, b = 0.22} } ) entity.destroy() end --Share chat with spectator force function Public.share_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 tag = player.tag if not tag then tag = '' end local color = player.chat_color if player.force.name == 'north' then game.forces.spectator.print(player.name .. tag .. ' (north): ' .. event.message, color) end if player.force.name == 'south' then game.forces.spectator.print(player.name .. tag .. ' (south): ' .. event.message, color) end if global.tournament_mode then return end if player.force.name == 'player' then game.forces.north.print(player.name .. tag .. ' (spawn): ' .. event.message, color) game.forces.south.print(player.name .. tag .. ' (spawn): ' .. event.message, color) game.forces.spectator.print(player.name .. tag .. ' (spawn): ' .. event.message, color) end if player.force.name == 'spectator' then --Skip messages that would spoil coordinates from spectators local a, b = string_find(event.message, 'gps=', 1, false) if a then return end game.forces.north.print(player.name .. tag .. ' (spectator): ' .. event.message, color) game.forces.south.print(player.name .. tag .. ' (spectator): ' .. event.message, color) end end function Public.spy_fish(player) if not player.character then return end local duration_per_unit = 2700 local i2 = player.get_inventory(defines.inventory.character_main) if not i2 then return end local owned_fishes = i2.get_item_count('raw-fish') owned_fishes = owned_fishes + i2.get_item_count('raw-fish') if owned_fishes == 0 then player.print('You have no fish in your inventory.', {r = 0.98, g = 0.66, b = 0.22}) else local x = i2.remove({name = 'raw-fish', count = 1}) if x == 0 then i2.remove({name = 'raw-fish', count = 1}) end local enemy_team = 'south' if player.force.name == 'south' then enemy_team = 'north' end if global.spy_fish_timeout[player.force.name] - game.tick > 0 then global.spy_fish_timeout[player.force.name] = global.spy_fish_timeout[player.force.name] + duration_per_unit player.print(math.ceil((global.spy_fish_timeout[player.force.name] - game.tick) / 60) .. ' seconds of enemy vision left.', {r = 0.98, g = 0.66, b = 0.22}) else game.print(player.name .. ' sent a fish to spy on ' .. enemy_team .. ' team!', {r = 0.98, g = 0.66, b = 0.22}) global.spy_fish_timeout[player.force.name] = game.tick + duration_per_unit end end end function Public.create_map_intro_button(player) if player.gui.top['map_intro_button'] then return end local b = player.gui.top.add({type = 'sprite-button', caption = '?', name = 'map_intro_button', tooltip = 'Map Info'}) b.style.font_color = {r = 0.5, g = 0.3, b = 0.99} b.style.font = 'heading-1' b.style.minimal_height = 38 b.style.minimal_width = 38 b.style.top_padding = 1 b.style.left_padding = 1 b.style.right_padding = 1 b.style.bottom_padding = 1 end function Public.show_intro(player) if player.gui.center['map_intro_frame'] then player.gui.center['map_intro_frame'].destroy() end local frame = player.gui.center.add {type = 'frame', name = 'map_intro_frame', direction = 'vertical'} local frame = frame.add {type = 'frame'} local l = frame.add {type = 'label', caption = {'biter_battles.map_info'}, name = 'biter_battles_map_intro'} l.style.single_line = false l.style.font = 'heading-2' l.style.font_color = {r = 0.7, g = 0.6, b = 0.99} end function Public.map_intro_click(player, element) if element.name == 'close_map_intro_frame' then player.gui.center['map_intro_frame'].destroy() return true end if element.name == 'biter_battles_map_intro' then player.gui.center['map_intro_frame'].destroy() return true end if element.name == 'map_intro_button' then if player.gui.center['map_intro_frame'] then player.gui.center['map_intro_frame'].destroy() return true else Public.show_intro(player) return true end end end return Public