1
0
mirror of https://github.com/ComfyFactory/ComfyFactorio.git synced 2025-01-10 00:43:27 +02:00
ComfyFactorio/maps/pirates/api_on_tick.lua
Piratux 2944a907aa features+code cleanup
Changes:
- Changed requirements for placing structures on islands: instead of trying to squeeze in-between trees, now it needs a single corner to be on land (it still can't spawn in ocean). In result, islands with more dense forests should see special structures more often and more scattered around the island, rather than them spawning mostly on beaches (because they had no trees)
- When area in which structure is going to be placed contains water, it's replaced with landfill
- New structure: friendly small-cliff-base with cliffs, gun turrets and car
- Captain's cabin now contains cliff-explosives that can be placed in hold's chests to remove them
- Moved repeating code snippets into seperate functions
2022-06-16 00:00:18 +03:00

1482 lines
54 KiB
Lua

-- This file is part of thesixthroc's Pirate Ship softmod, licensed under GPLv3 and stored at https://github.com/danielmartin0/ComfyFactorio-Pirates.
--luacheck: ignore
--luacheck ignores because tickinterval arguments are a code templating choice...
local Memory = require 'maps.pirates.memory'
local Gui = require 'maps.pirates.gui.gui'
local Ai = require 'maps.pirates.ai'
local Structures = require 'maps.pirates.structures.structures'
local Boats = require 'maps.pirates.structures.boats.boats'
local Islands = require 'maps.pirates.surfaces.islands.islands'
local IslandsCommon = require 'maps.pirates.surfaces.islands.common'
local Surfaces = require 'maps.pirates.surfaces.surfaces'
local PiratesApiEvents = require 'maps.pirates.api_events'
local Roles = require 'maps.pirates.roles.roles'
local Progression = require 'maps.pirates.progression'
local Crowsnest = require 'maps.pirates.surfaces.crowsnest'
local Hold = require 'maps.pirates.surfaces.hold'
local Cabin = require 'maps.pirates.surfaces.cabin'
local Balance = require 'maps.pirates.balance'
local Common = require 'maps.pirates.common'
local CoreData = require 'maps.pirates.coredata'
local Overworld = require 'maps.pirates.overworld'
local Utils = require 'maps.pirates.utils_local'
local Crew = require 'maps.pirates.crew'
local Parrot = require 'maps.pirates.parrot'
local Math = require 'maps.pirates.math'
local _inspect = require 'utils.inspect'.inspect
local Kraken = require 'maps.pirates.surfaces.sea.kraken'
local Quest = require 'maps.pirates.quest'
local ShopDock = require 'maps.pirates.shop.dock'
local QuestStructures = require 'maps.pirates.structures.quest_structures.quest_structures'
local Public = {}
function Public.strobe_player_colors(tickinterval)
local memory = Memory.get_crew_memory()
local strobing_players = memory.speed_boost_characters
if strobing_players and #strobing_players > 0 then
local col = Utils.rgb_from_hsv((game.tick*6) % 360, 0.7, 0.9)
for index, val in pairs(strobing_players) do
if val then
local player = game.players[index]
if Common.validate_player_and_character(player) then
player.color = col
end
end
end
end
end
function Public.prevent_unbarreling_off_ship(tickinterval)
if Common.allow_barreling_off_ship then return end
local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
local boat = memory.boat
local surface_name = boat.surface_name
if not (surface_name and game.surfaces[surface_name] and game.surfaces[surface_name].valid) then return end
local surface = game.surfaces[surface_name]
local assemblers = surface.find_entities_filtered{type = 'assembling-machine', force = memory.force_name}
for _, a in pairs(assemblers) do
if a and a.valid then
local r = a.get_recipe()
if r and r.subgroup and r.subgroup.name and r.subgroup.name == 'fill-barrel' and (not (r.name and r.name == 'fill-water-barrel')) then
if not Boats.on_boat(boat, a.position) then
Common.notify_force_error(memory.force, {'pirates.error_cant_carry_barrels'})
a.set_recipe('fill-water-barrel')
end
end
end
end
end
function Public.prevent_disembark(tickinterval)
local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
local boat = memory.boat
if boat and boat.state and (boat.state == Boats.enum_state.RETREATING or (boat.state == Boats.enum_state.LEAVING_DOCK and (not (memory.crewstatus and memory.crewstatus == Crew.enum.LEAVING_INITIAL_DOCK)))) then
if not destination.dynamic_data.cant_disembark_players then destination.dynamic_data.cant_disembark_players = {} end
local ps = destination.dynamic_data.cant_disembark_players
for _, player in pairs(game.connected_players) do
if player.surface and player.surface.valid and boat.surface_name and player.surface.name == boat.surface_name and Boats.on_boat(boat, player.position) then
ps[player.index] = true
end
end
for _, player in pairs(game.connected_players) do
if player.surface and player.surface.valid and boat.surface_name and player.surface.name == boat.surface_name and ps[player.index] and (not Boats.on_boat(boat, player.position)) and (not (player.controller_type == defines.controllers.spectator)) then
Common.notify_player_error(player, {'pirates.error_disembark'})
-- player.teleport(memory.spawnpoint)
local p = player.surface.find_non_colliding_position('character', memory.spawnpoint, 5, 0.1)
if p then
player.teleport(p)
else
player.teleport(memory.spawnpoint)
end
end
end
end
end
function Public.check_all_spawners_dead(tickinterval)
local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
local boat = memory.boat
if destination.static_params and destination.static_params.base_cost_to_undock and (not (destination.subtype and destination.subtype == Islands.enum.RED_DESERT)) then
if boat and boat.surface_name and boat.surface_name == destination.surface_name then
local surface = game.surfaces[destination.surface_name]
if not (surface and surface.valid) then return end
local spawnerscount = Common.spawner_count(surface)
if spawnerscount == 0 then
destination.static_params.base_cost_to_undock = nil
Common.notify_force(memory.force, {'pirates.destroyed_all_nests'})
end
end
end
end
function Public.raft_raids(tickinterval)
local memory = Memory.get_crew_memory()
if memory.game_lost then return end
local destination = Common.current_destination()
if not destination then return end
if (not destination.static_params) or (not destination.static_params.scheduled_raft_raids) or (not destination.dynamic_data.timer) then return end
local scheduled_raft_raids = destination.static_params.scheduled_raft_raids
local timer = destination.dynamic_data.timer
for k, raid in pairs(scheduled_raft_raids) do
if timer >= raid.timeinseconds and (not scheduled_raft_raids[k].fired) then
local type
if memory.overworldx >= 40*16 then
type = Boats.enum.RAFTLARGE
else
type = Boats.enum.RAFT
end
local boat = Islands.spawn_enemy_boat(type)
if boat then
Ai.spawn_boat_biters(boat, raid.max_evo, Boats.get_scope(boat).Data.capacity, Boats.get_scope(boat).Data.width)
end
scheduled_raft_raids[k].fired = true
end
end
end
function Public.ship_deplete_fuel(tickinterval)
local memory = Memory.get_crew_memory()
if memory.game_lost then return end
if not (memory.stored_fuel and memory.boat.input_chests and memory.boat.input_chests[1]) then return end
local rate = Progression.fuel_depletion_rate()
memory.fuel_depletion_rate_memoized = rate
local boat = memory.boat
local input_chests = boat.input_chests
local inv = input_chests[1].get_inventory(defines.inventory.chest)
local contents = inv.get_contents()
local item_type = 'coal'
local count = contents[item_type] or 0
if count > 0 then
inv.remove{name = 'coal', count = count}
end
memory.stored_fuel = memory.stored_fuel + count + rate*tickinterval/60
if rate < 0 and memory.stored_fuel < 1000 and (not (memory.parrot_fuel_most_recent_warning and memory.parrot_fuel_most_recent_warning >= game.tick - 60*60*12)) then --12 minutes
memory.parrot_fuel_most_recent_warning = game.tick
Common.parrot_speak(memory.force, {'pirates.parrot_fuel_warning'})
end
if memory.stored_fuel < 0 then
Crew.try_lose({'pirates.loss_out_of_fuel'})
end
end
function Public.transfer_pollution(tickinterval)
local memory = Memory.get_crew_memory()
local p = 0
for i = 1, memory.hold_surface_count do
local surface = Hold.get_hold_surface(i)
if not surface then return end
p = p + surface.get_total_pollution()
surface.clear_pollution()
end
if not (p and memory.floating_pollution) then return end
memory.floating_pollution = memory.floating_pollution + p
end
function Public.shop_ratelimit_tick(tickinterval)
-- if memory.mainshop_rate_limit_ticker and memory.mainshop_rate_limit_ticker > 0 then
-- memory.mainshop_rate_limit_ticker = memory.mainshop_rate_limit_ticker - tickinterval
-- end
end
function Public.captain_warn_afk(tickinterval)
local memory = Memory.get_crew_memory()
if memory.game_lost then return end
if memory.playerindex_captain then
for _, player in pairs(game.connected_players) do
if Common.is_captain(player) and #Common.crew_get_nonafk_crew_members() > 1 and player.afk_time >= Common.afk_time - 20*60 - 60 - tickinterval and player.afk_time < Common.afk_time - 20*60 then
Common.notify_player_announce(player, {'pirates.warn_nearly_afk_captain'})
player.play_sound{path = 'utility/scenario_message'}
end
end
end
end
function Public.prune_offline_characters_list(tickinterval)
local memory = Memory.get_crew_memory()
if memory.game_lost then return end
for player_index, tick in pairs(memory.temporarily_logged_off_characters) do
if player_index and game.players[player_index] and game.players[player_index].connected then
--game.print("deleting already online character from list")
memory.temporarily_logged_off_characters[player_index] = nil
else
if player_index and tick < game.tick - 60 * 60 * Common.logged_off_items_preserved_minutes then
local player_inv = {}
player_inv[1] = game.players[player_index].get_inventory(defines.inventory.character_main)
player_inv[2] = game.players[player_index].get_inventory(defines.inventory.character_armor)
player_inv[3] = game.players[player_index].get_inventory(defines.inventory.character_ammo)
player_inv[4] = game.players[player_index].get_inventory(defines.inventory.character_guns)
player_inv[5] = game.players[player_index].get_inventory(defines.inventory.character_trash)
local any = false
for ii = 1, 5, 1 do
if player_inv[ii] and player_inv[ii].valid then
for iii = 1, #player_inv[ii], 1 do
if player_inv[ii][iii] and player_inv[ii][iii].valid and player_inv[ii][iii].valid_for_read then
-- items[#items + 1] = player_inv[ii][iii]
Common.give_items_to_crew(player_inv[ii][iii])
any = true
end
end
end
end
if any then
Common.notify_force_light(memory.force, {'pirates.recover_offline_player_items'})
end
for ii = 1, 5, 1 do
if player_inv[ii].valid then
player_inv[ii].clear()
end
end
memory.temporarily_logged_off_characters[player_index] = nil
end
end
end
end
function Public.periodic_free_resources(tickinterval)
local memory = Memory.get_crew_memory()
if memory.game_lost then return end
local destination = Common.current_destination()
local boat = memory.boat
if not (destination and destination.type and destination.type == Surfaces.enum.ISLAND and boat and boat.surface_name and boat.surface_name == destination.surface_name) then return end
Common.give_items_to_crew(Balance.periodic_free_resources_per_destination_5_seconds())
if game.tick % (300*30) == 0 and (destination and destination.subtype and destination.subtype == Islands.enum.RADIOACTIVE) then -- every 150 seconds
local count = 2
Common.give_items_to_crew{{name = 'sulfuric-acid-barrel', count = count}}
local force = memory.force
if not (force and force.valid) then return end
local message = {'pirates.granted_1', {'pirates.granted_periodic_barrel'}, count .. ' [item=sulfuric-acid-barrel]'}
Common.notify_force_light(force, message)
end
end
function Public.pick_up_tick(tickinterval)
local destination = Common.current_destination()
if not destination then return end
local dynamic_data = destination.dynamic_data
local surface_name = destination.surface_name
if not (surface_name and dynamic_data) then return end
local surface = game.surfaces[surface_name]
if not (surface and surface.valid) then return end
local maps = dynamic_data.treasure_maps or {}
local buried_treasure = dynamic_data.buried_treasure or {}
local ghosts = dynamic_data.ghosts or {}
for i = 1, #maps do
local map = maps[i]
if map.state == 'on_ground' then
local p = map.position
local nearby_characters = surface.find_entities_filtered{position = p, radius = 3, name = 'character'}
local nearby_characters_count = #nearby_characters
if nearby_characters_count > 0 then
local player
local j = 1
while j <= nearby_characters_count do
if nearby_characters[j] and nearby_characters[j].valid and nearby_characters[j].player and Common.validate_player(nearby_characters[j].player) then
player = nearby_characters[j].player
break
end
j = j + 1
end
if player then
local buried_treasure_candidates = {}
for _, t in pairs(buried_treasure) do
if not (t.associated_to_map) then
buried_treasure_candidates[#buried_treasure_candidates + 1] = t
end
end
if #buried_treasure_candidates == 0 then break end
local chosen = buried_treasure_candidates[Math.random(#buried_treasure_candidates)]
chosen.associated_to_map = true
local p2 = chosen.position
map.buried_treasure_position = p2
map.state = 'picked_up'
rendering.destroy(map.mapobject_rendering)
Common.notify_force_light(player.force, {'pirates.find_map',player.name})
map.x_renderings = {
rendering.draw_line{
width = 8,
surface = surface,
from = {p2.x + 3, p2.y + 3},
to = {p2.x - 3, p2.y - 3},
color = {1,0,0},
gap_length = 0.2,
dash_length = 1,
draw_on_ground = true,
-- players = {player},
},
rendering.draw_line{
width = 8,
surface = surface,
from = {p2.x - 3, p2.y + 3},
to = {p2.x + 3, p2.y - 3},
color = {1,0,0},
gap_length = 0.2,
dash_length = 1,
draw_on_ground = true,
-- players = {player},
},
}
end
end
end
end
if not (dynamic_data.quest_type and (not dynamic_data.quest_complete)) then return end
for i = 1, #ghosts do
local ghost = ghosts[i]
if ghost.state == 'on_ground' then
local p = ghost.position
local nearby_characters = surface.find_entities_filtered{position = p, radius = 3, name = 'character'}
local nearby_characters_count = #nearby_characters
if nearby_characters_count > 0 then
local player
local j = 1
while j <= nearby_characters_count do
if nearby_characters[j] and nearby_characters[j].valid and nearby_characters[j].player and Common.validate_player(nearby_characters[j].player) then
player = nearby_characters[j].player
break
end
j = j + 1
end
if player then
rendering.destroy(ghost.ghostobject_rendering)
ghost.state = 'picked_up'
Common.notify_force(player.force, {'pirates.find_ghost',player.name})
dynamic_data.quest_progress = dynamic_data.quest_progress + 1
Quest.try_resolve_quest()
end
end
end
end
end
local function cached_structure_delete_existing_entities_if_needed(surface, position, special)
if (not special.doNotDestroyExistingEntities) then
-- destroy existing entities
local area = {left_top = {position.x - special.width/2, position.y - special.height/2}, right_bottom = {position.x + special.width/2 + 0.5, position.y + special.height/2 + 0.5}}
surface.destroy_decoratives{area=area}
local existing = surface.find_entities_filtered{area = area}
if existing then
for _, e in pairs(existing) do
if not (((special.name == 'small_primitive_mining_base' or special.name == 'small_mining_base') and (e.name == 'iron-ore' or e.name == 'copper-ore' or e.name == 'stone')) or (special.name == 'uranium_miners' and e.name == 'uranium-ore')) then
if not (e.name and e.name == 'rocket-silo') then
e.destroy()
end
end
end
end
end
end
local function cached_structure_delete_existing_unwalkable_tiles_if_needed(surface, position, special)
local area = {left_top = {position.x - special.width/2, position.y - special.height/2}, right_bottom = {position.x + special.width/2 + 0.5, position.y + special.height/2 + 0.5}}
local existing = surface.find_tiles_filtered{area = area, collision_mask = "water-tile"}
if existing then
local tiles = {}
for _, t in pairs(existing) do
tiles[#tiles + 1] = {name = "landfill", position = t.position}
end
if #tiles > 0 then
surface.set_tiles(tiles, true)
end
end
end
function Public.interpret_shorthanded_force_name(shorthanded_name)
local memory = Memory.get_crew_memory()
local ret
if shorthanded_name == 'ancient-friendly' then
ret = memory.ancient_friendly_force_name
elseif shorthanded_name == 'ancient-hostile' then
ret = memory.ancient_enemy_force_name
elseif shorthanded_name == 'crew' then
ret = memory.force_name
elseif shorthanded_name == 'enemy' then
ret = memory.enemy_force_name
else
ret = shorthanded_name
end
return ret
end
function Public.place_cached_structures(tickinterval)
local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
local surface_name = destination.surface_name
if (not destination.dynamic_data) or (not destination.dynamic_data.structures_waiting_to_be_placed) or (not surface_name) or (not game.surfaces[surface_name]) or (not game.surfaces[surface_name].valid) then return end
if not (memory.boat and memory.boat.surface_name and memory.boat.surface_name == surface_name) then
return --We only want to generate structures once the players arrive on the island. Otherwise, the following issue happens. 2x2 structures force-generate nearby chunks. But if the island has many structures, that could cause a domino effect of chunk-generation, lagging the game.
-- Since this change, this function has little conceptual reason to be an on_tick function, but it makes sense to run it a few ticks after you teleport to the island, so it can stay one for now.
end
local surface = game.surfaces[surface_name]
local structures = destination.dynamic_data.structures_waiting_to_be_placed
local num = #structures
for i = num, 1, -1 do
local structure = structures[i]
if game.tick > structure.tick + 5 then
local special = structure.data
local position = special.position
-- Since this structure has cliffs, the positions need to be snapped and floored to nearest set of elements for x and y accordingly:
-- x: {..., -4, 0, 4, 8, ...}
-- y: {..., -4, 0, 4, 8, ...}
-- This is assuming that "cliff.position + offset" already has snapped positions as such:
-- x: {..., -2, 2, 6, ...}
-- y: {..., -1.5, 2.5, 6.5, ...}
-- The position would be corrected in "try_place" function, but it gets adjusted later with "terraingen_coordinates_offset", so have to do it here
if special.name == 'small_cliff_base' then
position.x = position.x - position.x % 4
position.y = position.y - position.y % 4
-- add a small bias to avoid situations such as 7.999999999993
position.x = position.x + 0.01
position.y = position.y + 0.01
end
Common.ensure_chunks_at(surface, position, Common.structure_ensure_chunk_radius)
cached_structure_delete_existing_entities_if_needed(surface, position, special)
cached_structure_delete_existing_unwalkable_tiles_if_needed(surface, position, special)
local saved_components = {}
for k = 1, #special.components do
local c = special.components[k]
local force_name
if c.force then force_name = Public.interpret_shorthanded_force_name(c.force) end
if c.type == 'tiles' then
local tiles = {}
for _, p in pairs(Common.tile_positions_from_blueprint(c.bp_string, c.offset)) do
tiles[#tiles + 1] = {name = c.tile_name, position = Utils.psum{position, p}}
end
if #tiles > 0 then
surface.set_tiles(tiles, true)
end
elseif c.type == 'water_tiles' then
local tiles = {}
for _, p in pairs(c.positions) do
tiles[#tiles + 1] = {name = c.tile_name, position = Utils.psum{position, p, c.offset}}
end
if #tiles > 0 then
surface.set_tiles(tiles, true)
end
elseif c.type == 'cliffs' then
--local c2 = {type = c.type, force_name = force_name, built_entities = {}}
for _, e in pairs(c.instances) do
local p = Utils.psum{position, e.position, c.offset}
local e2 = surface.create_entity{name = c.name, position = p, cliff_orientation = e.cliff_orientation}
-- c2.built_entities[#c2.built_entities + 1] = e2
end
--saved_components[#saved_components + 1] = c2
elseif c.type == 'entities' or c.type == 'entities_minable' then
local c2 = {type = c.type, force_name = force_name, built_entities = {}}
for _, e in pairs(c.instances) do
local p = Utils.psum{position, e.position, c.offset}
local e2 = surface.create_entity{name = c.name, position = p, direction = e.direction, force = force_name, amount = c.amount}
c2.built_entities[#c2.built_entities + 1] = e2
end
saved_components[#saved_components + 1] = c2
elseif c.type == 'vehicles' then
local c2 = {type = c.type, force_name = force_name, built_entities = {}}
for _, e in pairs(c.instances) do
local p = Utils.psum{position, e.position, c.offset}
local e2 = surface.create_entity{name = c.name, position = p, direction = e.direction}
c2.built_entities[#c2.built_entities + 1] = e2
end
saved_components[#saved_components + 1] = c2
elseif c.type == 'entities_grid' then
local c2 = {type = c.type, force_name = force_name, built_entities = {}}
for x = Math.ceil(-c.width/2), Math.ceil(c.width/2), 1 do
for y = Math.ceil(-c.height/2), Math.ceil(c.height/2), 1 do
local p = Utils.psum{position, {x = x, y = y}, c.offset}
local e2 = surface.create_entity{name = c.name, position = p, direction = c.direction, force = force_name}
c2.built_entities[#c2.built_entities + 1] = e2
end
end
saved_components[#saved_components + 1] = c2
elseif c.type == 'entities_randomlyplaced' then
local c2 = {type = c.type, force_name = force_name, built_entities = {}}
for _ = 1, c.count do
local whilesafety = 0
local done = false
while whilesafety < 10 and done == false do
local rng_x = Math.random(-c.r, c.r)
local rng_y = Math.random(-c.r, c.r)
local p = Utils.psum{position, c.offset, {x = rng_x, y = rng_y}}
local name = c.name
if name == 'random-worm' then name = Common.get_random_worm_type(memory.evolution_factor) end
local e = {name = name, position = p, force = force_name}
if surface.can_place_entity(e) then
local e2 = surface.create_entity(e)
c2.built_entities[#c2.built_entities + 1] = e2
done = true
end
whilesafety = whilesafety + 1
end
end
saved_components[#saved_components + 1] = c2
elseif c.type == 'entities_randomlyplaced_border' then
local c2 = {type = c.type, force_name = force_name, built_entities = {}}
for _ = 1, c.count do
local whilesafety = 0
local done = false
while whilesafety < 10 and done == false do
local rng_1 = Math.random(c.small_r, c.large_r)
local rng_2 = Math.random(- c.large_r, c.large_r)
local p
if Math.random(2) == 1 then
if Math.random(2) == 1 then
p = {x = rng_1, y = rng_2}
else
p = {x = -rng_1, y = rng_2}
end
else
if Math.random(2) == 1 then
p = {x = rng_2, y = rng_1}
else
p = {x = rng_2, y = -rng_1}
end
end
local p2 = Utils.psum{position, c.offset, p}
local e = {name = c.name, position = p2, force = force_name}
if surface.can_place_entity(e) then
local e2 = surface.create_entity(e)
c2.built_entities[#c2.built_entities + 1] = e2
done = true
end
whilesafety = whilesafety + 1
end
end
saved_components[#saved_components + 1] = c2
elseif c.bp_string then
local c2 = {type = c.type, force_name = force_name, built_entities = {}}
local es = Common.build_from_blueprint(c.bp_string, surface, Utils.psum{position, c.offset}, game.forces[force_name])
for l = 1, #es do
c2.built_entities[#c2.built_entities + 1] = es[l]
end
saved_components[#saved_components + 1] = c2
end
end
Structures.configure_structure_entities(special.name, saved_components)
QuestStructures.create_quest_structure_entities(special.name)
for j = i, #structures-1 do
structures[j] = structures[j+1]
end
structures[#structures] = nil
end
end
end
function Public.update_boat_stored_resources(tickinterval)
Common.update_boat_stored_resources()
end
function Public.buried_treasure_check(tickinterval)
local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
local remaining = destination.dynamic_data.treasure_remaining
if remaining and remaining > 0 and destination.surface_name and destination.dynamic_data.buried_treasure and #destination.dynamic_data.buried_treasure > 0 then
local surface = game.surfaces[destination.surface_name]
local treasure_table = destination.dynamic_data.buried_treasure
for i = 1, #treasure_table do
local treasure = treasure_table[i]
if not treasure then break end
local p = treasure.position
local free = surface.can_place_entity{name = 'wooden-chest', position = p}
if free then
local inserters = {
surface.find_entities_filtered{
type = 'inserter',
position = {x = p.x - 1, y = p.y},
radius = 0.1,
direction = defines.direction.east
},
surface.find_entities_filtered{
type = 'inserter',
position = {x = p.x + 1, y = p.y},
radius = 0.1,
direction = defines.direction.west
},
surface.find_entities_filtered{
type = 'inserter',
position = {x = p.x, y = p.y - 1},
radius = 0.1,
direction = defines.direction.south
},
surface.find_entities_filtered{
type = 'inserter',
position = {x = p.x, y = p.y + 1},
radius = 0.1,
direction = defines.direction.north
}
}
for j = 1,4 do
if inserters[j] and inserters[j][1] then
local ins = inserters[j][1]
local t = treasure.treasure
-- if #treasure.treasure > 0 then
-- t = treasure.treasure
-- -- t = treasure.treasure[1]
-- end
if not t then break end
if destination.dynamic_data.treasure_remaining > 0 and ins.held_stack.count == 0 and ins.status == defines.entity_status.waiting_for_source_items then
surface.create_entity{name = 'item-on-ground', position = p, stack = {name = t.name, count = 1}}
t.count = t.count - 1
destination.dynamic_data.treasure_remaining = destination.dynamic_data.treasure_remaining - 1
if destination.dynamic_data.treasure_remaining == 0 then
-- destroy all
local buried_treasure = destination.dynamic_data.buried_treasure
for _, t2 in pairs(buried_treasure) do
t2 = nil
end
local maps = destination.dynamic_data.treasure_maps
for _, m in pairs(maps) do
if m.state == 'on_ground' then
rendering.destroy(m.mapobject_rendering)
elseif m.state == 'picked_up' and m.x_renderings and #m.x_renderings > 0 then
rendering.destroy(m.x_renderings[1])
rendering.destroy(m.x_renderings[2])
end
m = nil
end
elseif t.count <= 0 then
treasure.treasure = nil
local maps = destination.dynamic_data.treasure_maps
for _, m in pairs(maps) do
if m.state == 'picked_up' and m.buried_treasure_position and m.buried_treasure_position == p and m.x_renderings and #m.x_renderings > 0 then
m.state = 'inactive'
rendering.destroy(m.x_renderings[1])
rendering.destroy(m.x_renderings[2])
end
end
end
end
end
end
end
end
end
end
function Public.boat_movement_tick(tickinterval)
local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
local enemy_force_name = memory.enemy_force_name
local boat = memory.boat
if boat and boat.surface_name and game.surfaces[boat.surface_name] and game.surfaces[boat.surface_name].valid and boat.speed and boat.speed > 0 and memory.game_lost == false then
local surface_type = destination.type
local ticker_increase = boat.speed / 60 * tickinterval
boat.speedticker1 = boat.speedticker1 + ticker_increase
boat.speedticker2 = boat.speedticker2 + ticker_increase
boat.speedticker3 = boat.speedticker3 + ticker_increase
if boat.speedticker1 >= Common.boat_steps_at_a_time then
boat.speedticker1 = 0
if not Progression.check_for_end_of_boat_movement(boat) then
Structures.Boats.currentdestination_move_boat_natural()
end
elseif boat.speedticker2 >= Common.boat_steps_at_a_time then
if surface_type and surface_type == Surfaces.enum.ISLAND and boat and boat.state and boat.state == Boats.enum_state.APPROACHING then
Structures.Boats.currentdestination_try_move_boat_steered()
end
boat.speedticker2 = 0
end
end
if memory.enemyboats then
for i = 1, #memory.enemyboats do
local eboat = memory.enemyboats[i]
if eboat and eboat.surface_name and game.surfaces[eboat.surface_name] and game.surfaces[eboat.surface_name].valid then
if eboat.state == Boats.enum_state.APPROACHING and eboat.speed and eboat.speed > 0 and memory.game_lost == false then
local ticker_increase = eboat.speed / 60 * tickinterval
eboat.speedticker1 = eboat.speedticker1 + ticker_increase
if eboat.speedticker1 >= 1 then
eboat.speedticker1 = 0
if eboat.state == Boats.enum_state.APPROACHING then
if Progression.check_for_end_of_boat_movement(eboat) then
-- if boat.unit_group and boat.unit_group.ref and boat.unit_group.ref.valid then boat.unit_group.ref.set_command({
-- type = defines.command.attack_area,
-- destination = ({memory.boat.position.x - 32, memory.boat.position.y} or {0,0}),
-- radius = 32,
-- distraction = defines.distraction.by_enemy
-- }) end
local units = game.surfaces[eboat.surface_name].find_units{area = {{eboat.position.x - 12, eboat.position.y - 12}, {eboat.position.x + 12, eboat.position.y + 12}}, force = enemy_force_name, condition = 'same'}
if #units > 0 then
local unit_group = game.surfaces[eboat.surface_name].create_unit_group({position = eboat.position, force = enemy_force_name})
for _, unit in pairs(units) do
unit_group.add_member(unit)
end
boat.unit_group = {ref = unit_group, script_type = 'landing-party'}
boat.unit_group.ref.set_command({
type = defines.command.attack_area,
destination = ({memory.boat.position.x - 32, memory.boat.position.y} or {0,0}),
radius = 32,
distraction = defines.distraction.by_enemy
})
end
else
local p = {x = eboat.position.x + 1, y = eboat.position.y}
Boats.teleport_boat(eboat, nil, p, CoreData.static_boat_floor)
if p.x % 7 < 1 then
Ai.update_landing_party_unit_groups(eboat, 7)
end
end
end
end
elseif eboat.state == Boats.enum_state.LANDED then
do end
end
else
memory.enemyboats[i] = nil
end
end
end
end
function Public.crowsnest_natural_move(tickinterval)
local memory = Memory.get_crew_memory()
if not memory.loadingticks then
if not Public.overworld_check_collisions() then
Overworld.try_overworld_move_v2{x = 1, y = 0}
end
end
end
function Public.overworld_check_collisions(tickinterval)
local memory = Memory.get_crew_memory()
if not memory.loadingticks then
Overworld.check_for_kraken_collisions()
return Overworld.check_for_destination_collisions()
end
return false
end
function Public.loading_update(tickinterval)
local memory = Memory.get_crew_memory()
if memory.game_lost then return end
if not memory.loadingticks then return end
local currentdestination = Common.current_destination()
local destination_index = memory.mapbeingloadeddestination_index
if not destination_index then memory.loadingticks = nil return end
if (not memory.boat.state) or (not (memory.boat.state == Boats.enum_state.LANDED or memory.boat.state == Boats.enum_state.ATSEA_LOADING_MAP or memory.boat.state == Boats.enum_state.LEAVING_DOCK or (memory.boat.state == Boats.enum_state.APPROACHING and destination_index == 1))) then return end
memory.loadingticks = memory.loadingticks + tickinterval
-- if memory.loadingticks % 100 == 0 then game.print(memory.loadingticks) end
local destination_data = memory.destinations[destination_index]
if (not destination_data) then
if memory.boat and currentdestination.type == Surfaces.enum.LOBBY then
if memory.loadingticks >= 350 - Common.loading_interval then
if Boats.players_on_boat_count(memory.boat) > 0 then
if memory.loadingticks < 350 then
Common.notify_game({'', '[' .. memory.name .. '] ', {'pirates.loading_new_game'}})
elseif memory.loadingticks > 410 then
if not Crowsnest.get_crowsnest_surface() then
Crew.initialise_crowsnest_1()
elseif memory.loadingticks >= 470 then
Crew.initialise_crowsnest_2()
Overworld.ensure_lane_generated_up_to(0, 10)
Surfaces.create_surface(memory.destinations[destination_index])
-- PiratesApiEvents.load_some_map_chunks(destination_index, 0.02)
end
end
else
if memory.loadingticks >= 1100 then
Boats.destroy_boat(memory.boat)
Crew.disband_crew()
return
end
end
end
end
return
else
local surface_name = destination_data.surface_name
if not surface_name then return end
local surface = game.surfaces[surface_name]
if not surface then return end
if currentdestination.type == Surfaces.enum.LOBBY then
if memory.loadingticks >= 1260 then
if memory.boat and memory.boat.rendering_crewname_text and rendering.is_valid(memory.boat.rendering_crewname_text) then
rendering.destroy(memory.boat.rendering_crewname_text)
memory.boat.rendering_crewname_text = nil
end
Progression.go_from_starting_dock_to_first_destination()
elseif memory.loadingticks > 1230 then
if memory.boat then
memory.boat.speed = 0
end
elseif memory.loadingticks > 860 then
if Boats.players_on_boat_count(memory.boat) > 0 then
local fraction = 0.07 + 0.7 * (memory.loadingticks - 860) / 400
PiratesApiEvents.load_some_map_chunks(destination_index, fraction)
else
Boats.destroy_boat(memory.boat)
Crew.disband_crew()
return
end
elseif memory.loadingticks > 500 then
local d = (Crowsnest.Data.visibilitywidth/3)*(memory.loadingticks-500)/500
Overworld.ensure_lane_generated_up_to(0, d+26)
Overworld.ensure_lane_generated_up_to(24, d+13)
Overworld.ensure_lane_generated_up_to(-24, d)
-- elseif memory.loadingticks <= 500 and memory.loadingticks >= 100 then
-- local fraction = 0.02 + 0.05 * (memory.loadingticks - 100) / 400
-- PiratesApiEvents.load_some_map_chunks(destination_index, fraction)
end
elseif memory.boat.state == Boats.enum_state.ATSEA_LOADING_MAP then
local total = Common.map_loading_ticks_atsea
if currentdestination.type == Surfaces.enum.DOCK then
total = Common.map_loading_ticks_atsea_dock
elseif currentdestination.type == Surfaces.enum.ISLAND and currentdestination.subtype == Surfaces.Island.enum.MAZE then
total = Common.map_loading_ticks_atsea_maze
end
local eta_ticks = total - (memory.loadingticks - (memory.extra_time_at_sea or 0))
-- log(_inspect{eta_ticks, (memory.active_sea_enemies.krakens and #memory.active_sea_enemies.krakens or 'nil'), memory.loadingticks})
if eta_ticks < 60*20 and memory.active_sea_enemies and (memory.active_sea_enemies.krakens and #memory.active_sea_enemies.krakens > 0) then
memory.loadingticks = memory.loadingticks - tickinterval --reverse the change
else
local fraction = memory.loadingticks / (total + (memory.extra_time_at_sea or 0))
if fraction > Common.fraction_of_map_loaded_at_sea then
Progression.progress_to_destination(destination_index)
memory.loadingticks = 0
else
PiratesApiEvents.load_some_map_chunks_random_order(destination_index, fraction) --random order is good for maze world
end
end
elseif memory.boat.state == Boats.enum_state.LANDED then
local fraction = Common.fraction_of_map_loaded_at_sea + (1 - Common.fraction_of_map_loaded_at_sea) * memory.loadingticks / Common.map_loading_ticks_onisland
if fraction > 1 then
memory.loadingticks = nil
else
PiratesApiEvents.load_some_map_chunks(destination_index, fraction)
end
end
end
end
function Public.crowsnest_steer(tickinterval)
local memory = Memory.get_crew_memory()
if memory.game_lost then return end
if memory.boat and memory.boat.state == Structures.Boats.enum_state.ATSEA_SAILING and memory.game_lost == false and memory.boat.crowsneststeeringchests then
local leftchest, rightchest = memory.boat.crowsneststeeringchests.left, memory.boat.crowsneststeeringchests.right
if leftchest and leftchest.valid and rightchest and rightchest.valid then
local inv_left = leftchest.get_inventory(defines.inventory.chest)
local inv_right = rightchest.get_inventory(defines.inventory.chest)
local count_left = inv_left.get_item_count("rail-signal")
local count_right = inv_right.get_item_count("rail-signal")
if count_left >= 100 and count_right < 100 and memory.overworldy > -24 then
if Overworld.try_overworld_move_v2{x = 0, y = -24} then
local force = memory.force
Common.notify_force(force, {'pirates.steer_left'})
inv_left.remove({name = "rail-signal", count = 100})
end
return
elseif count_right >= 100 and count_left < 100 and memory.overworldy < 24 then
if Overworld.try_overworld_move_v2{x = 0, y = 24} then
local force = memory.force
Common.notify_force(force, {'pirates.steer_right'})
inv_right.remove({name = "rail-signal", count = 100})
end
return
end
end
end
end
function Public.silo_update(tickinterval)
local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
if destination.type == Surfaces.enum.ISLAND then
local dynamic_data = destination.dynamic_data
local silos = dynamic_data.rocketsilos
if silos then
local silo = silos[1]
if silo and silo.valid then
if dynamic_data.silocharged then
if not dynamic_data.rocketlaunched then
silo.launch_rocket()
end
else
local p = silo.position
local e = dynamic_data.energychargedinsilosincelastcheck or 0
dynamic_data.energychargedinsilosincelastcheck = 0
dynamic_data.rocketsiloenergyconsumed = dynamic_data.rocketsiloenergyconsumed + e
dynamic_data.rocketsiloenergyconsumedwithinlasthalfsecond = e
if memory.enemy_force_name then
local ef = memory.enemy_force
if ef and ef.valid then
local extra_evo = Balance.evolution_per_full_silo_charge() * e/dynamic_data.rocketsiloenergyneeded
Common.increment_evo(extra_evo)
dynamic_data.evolution_accrued_silo = dynamic_data.evolution_accrued_silo + extra_evo
end
end
local pollution = e/1000000 * Balance.silo_total_pollution() / Balance.silo_energy_needed_MJ()
if p and pollution then
game.pollution_statistics.on_flow('rocket-silo', pollution)
if not memory.floating_pollution then memory.floating_pollution = 0 end
-- Eventually I want to reformulate pollution not to pull from the map directly, but to pull from pollution_statistics. Previously all the silo pollution went to the map, but this causes a lag ~1-2 minutes. So as a compromise, let's send some to floating_pollution directly, and some to the map:
memory.floating_pollution = memory.floating_pollution + 3*pollution/4
game.surfaces[destination.surface_name].pollute(p, pollution/4)
if memory.overworldx >= 0 and dynamic_data.rocketsiloenergyconsumed >= 0.25 * dynamic_data.rocketsiloenergyneeded and (not dynamic_data.parrot_silo_warned) then
dynamic_data.parrot_silo_warned = true
local spawnerscount = Common.spawner_count(game.surfaces[destination.surface_name])
if spawnerscount > 0 then
Common.parrot_speak(memory.force, {'pirates.parrot_silo_warning'})
end
elseif dynamic_data.rocketsiloenergyconsumed >= dynamic_data.rocketsiloenergyneeded and (not (silo.rocket_parts == 100)) and (dynamic_data.silocharged == false) and (not memory.game_lost) then
-- silo.energy = 0
silo.rocket_parts = 100
dynamic_data.silocharged = true
if CoreData.rocket_silo_death_causes_loss then
-- become immune after launching
silo.destructible = false
end
end
end
end
end
end
end
end
function Public.slower_boat_tick(tickinterval)
local memory = Memory.get_crew_memory()
if memory.game_lost then return end
local destination = Common.current_destination()
if memory.boat.state == Boats.enum_state.LEAVING_DOCK then
memory.boat.speed = Math.min(memory.boat.speed + 40/tickinterval, 12)
end
local p = memory.boat.position
if p and (not (destination.subtype and destination.subtype == IslandsCommon.enum.RADIOACTIVE)) and destination.surface_name and game.surfaces[destination.surface_name] and game.surfaces[destination.surface_name].valid then --no locomotive pollute on radioactive islands
local pollution = Balance.boat_passive_pollution_per_minute(destination.dynamic_data.timer) / 3600 * tickinterval
game.surfaces[destination.surface_name].pollute(p, pollution)
game.pollution_statistics.on_flow('locomotive', pollution)
end
if memory.enemyboats then
for i = 1, #memory.enemyboats do
local b = memory.enemyboats[i]
-- if b.landing_time and destination.dynamic_data.timer and destination.dynamic_data.timer >= b.landing_time and b.spawner and b.spawner.valid then
-- -- if b.landing_time and destination.dynamic_data.timer and destination.dynamic_data.timer >= b.landing_time + 3 and b.spawner and b.spawner.valid then
-- b.spawner.destructible = true
-- b.landing_time = nil
-- end
end
end
end
function Public.LOS_tick(tickinterval)
local memory = Memory.get_crew_memory()
local destination = Common.current_destination()
local force = memory.force
if not destination.surface_name then return end
local surface = game.surfaces[destination.surface_name]
if memory.boat and memory.boat.state == Boats.enum_state.APPROACHING or memory.boat.state == Boats.enum_state.LANDED or memory.boat.state == Boats.enum_state.RETREATING then
local p = memory.boat.position
local BoatData = Boats.get_scope(memory.boat).Data
force.chart(surface, {{p.x - BoatData.width/2 - 70, p.y - 80},{p.x - BoatData.width/2 + 70, p.y + 80}})
end
if CoreData.rocket_silo_death_causes_loss or (destination.static_params and destination.static_params.base_cost_to_undock and destination.static_params.base_cost_to_undock['launch_rocket'] and destination.static_params.base_cost_to_undock['launch_rocket'] == true) then
local silos = destination.dynamic_data.rocketsilos
if silos and silos[1] and silos[1].valid then
local p = silos[1].position
force.chart(surface, {{p.x - 4, p.y - 4},{p.x + 4, p.y + 4}})
end
end
end
function Public.minimap_jam(tickinterval)
local memory = Memory.get_crew_memory()
if memory.overworldx == Common.maze_minimap_jam_league and memory.boat and memory.boat.state == Boats.enum_state.LANDED then
local destination = Common.current_destination()
if destination.type == Surfaces.enum.ISLAND and destination.subtype == Surfaces.Island.enum.MAZE then
if not destination.surface_name then return end
local surface = game.surfaces[destination.surface_name]
local force = memory.force
force.clear_chart(surface)
end
end
end
-- function Public.crewtick_handle_delayed_tasks(tickinterval)
-- local memory = Memory.get_crew_memory()
-- for _, task in pairs(memory.buffered_tasks) do
-- if not (memory.game_lost) then
-- if task == Delay.enum.PAINT_CROWSNEST then
-- Surfaces.Crowsnest.crowsnest_surface_delayed_init()
-- elseif task == Delay.enum.PLACE_DOCK_JETTY_AND_BOATS then
-- Surfaces.Dock.place_dock_jetty_and_boats()
-- local destination = Common.current_destination()
-- ShopDock.create_dock_markets(game.surfaces[destination.surface_name], Surfaces.Dock.Data.markets_position)
-- end
-- end
-- end
-- Delay.clear_buffer()
-- Delay.move_tasks_to_buffer()
-- end
function Public.Kraken_Destroyed_Backup_check(tickinterval) -- a server became stuck when the kraken spawner entity disappeared but the kraken_die had not fired, and I'm not sure why, so this is a backup checker for that case
local memory = Memory.get_crew_memory()
local boat = memory.boat
if boat and boat.surface_name and boat.state and boat.state == Boats.enum_state.ATSEA_LOADING_MAP then
if (memory.active_sea_enemies and memory.active_sea_enemies.krakens and #memory.active_sea_enemies.krakens > 0) then
local surface = game.surfaces[boat.surface_name]
local some_spawners_should_be_alive = false
for i = 1, Kraken.kraken_slots do
if memory.active_sea_enemies.krakens[i] then
local kraken_data = memory.active_sea_enemies.krakens[i]
if kraken_data.step and kraken_data.step >= 3 then
some_spawners_should_be_alive = true
end
end
end
local but_none_are = some_spawners_should_be_alive and #surface.find_entities_flitered{name = 'biter-spawner', force = memory.enemy_force_name} == 0
if but_none_are then
for i = 1, Kraken.kraken_slots do
if memory.active_sea_enemies.krakens[i] then
Kraken.kraken_die(i)
end
end
end
end
end
end
function Public.quest_progress_tick(tickinterval)
local memory = Memory.get_crew_memory()
if memory.game_lost then return end
local destination = Common.current_destination()
local dynamic_data = destination.dynamic_data
if dynamic_data.quest_type then
if dynamic_data.quest_type == Quest.enum.TIME and (not dynamic_data.quest_complete) and dynamic_data.quest_progress > 0 and dynamic_data.quest_progressneeded ~= 1 then
dynamic_data.quest_progress = dynamic_data.quest_progress - tickinterval/60
end
if dynamic_data.quest_type == Quest.enum.RESOURCEFLOW and (not dynamic_data.quest_complete) then
local force = memory.force
if not (force and force.valid and dynamic_data.quest_params) then return end
dynamic_data.quest_progress = force.item_production_statistics.get_flow_count{name = dynamic_data.quest_params.item, input = true, precision_index = defines.flow_precision_index.five_seconds, count = false}
Quest.try_resolve_quest()
end
if dynamic_data.quest_type == Quest.enum.RESOURCECOUNT and (not dynamic_data.quest_complete) then
local force = memory.force
if not (force and force.valid and dynamic_data.quest_params) then return end
dynamic_data.quest_progress = force.item_production_statistics.get_flow_count{name = dynamic_data.quest_params.item, input = true, precision_index = defines.flow_precision_index.one_thousand_hours, count = true} - dynamic_data.quest_params.initial_count
Quest.try_resolve_quest()
end
end
end
function Public.silo_insta_update()
local memory = Memory.get_crew_memory()
if memory.game_lost then return end
local destination = Common.current_destination()
local dynamic_data = destination.dynamic_data
local silos = dynamic_data.rocketsilos
if silos and silos[1] and silos[1].valid then --need the first silo to be alive in order to charge any others
if dynamic_data.silocharged then
for i, silo in ipairs(silos) do
if silo and silo.valid then --sometimes theyre overwritten by other structures e.g. market
silo.energy = silo.electric_buffer_size
end
end
else
for i, silo in ipairs(silos) do
if silo and silo.valid then --sometimes theyre overwritten by other structures e.g. market
local e = silo.energy - 1
local e2 = dynamic_data.rocketsiloenergyneeded - dynamic_data.rocketsiloenergyconsumed
if e > 0 and e2 > 0 then
local absorb = Math.min(e, e2)
dynamic_data.energychargedinsilosincelastcheck = dynamic_data.energychargedinsilosincelastcheck + absorb
silo.energy = silo.energy - absorb
if dynamic_data.rocketsilochargedbools and (not dynamic_data.rocketsilochargedbools[i]) then
dynamic_data.rocketsilochargedbools[i] = true
local inv = silo.get_inventory(defines.inventory.assembling_machine_input)
inv.insert{name = 'rocket-control-unit', count = 10}
inv.insert{name = 'low-density-structure', count = 10}
inv.insert{name = 'rocket-fuel', count = 10}
end
else
silo.energy = 0
end
end
end
end
end
end
-- function Public.parrot_tick(tickinterval)
-- Parrot.parrot_tick()
-- end
function Public.update_recentcrewmember_list(tickinterval)
local memory = Memory.get_crew_memory()
-- don't update this unless someone specifically becomes spectator or is planked:
-- for i = 1, #memory.crewplayerindices do
-- local s = memory.crewplayerindices[i]
-- if s then
-- memory.tempbanned_from_joining_data[s] = game.tick
-- end
-- end
-- for k, v in pairs(memory.tempbanned_from_joining_data or {}) do
-- if v <= game.tick - Common.ban_from_rejoining_crew_ticks then
-- memory.tempbanned_from_joining_data[k] = nil
-- end
-- end
end
-- function Public.globaltick_handle_delayed_tasks(tickinterval)
-- local global_memory = Memory.get_global_memory()
-- for _, task in pairs(global_memory.global_buffered_tasks) do
-- if task == Delay.global_enum.PLACE_LOBBY_JETTY_AND_BOATS then
-- Surfaces.Lobby.place_lobby_jetty_and_boats()
-- elseif task == Delay.global_enum.ADMIN_GO2 then
-- Delay.global_add(Delay.global_enum.ADMIN_GO3)
-- elseif task == Delay.global_enum.ADMIN_GO3 then
-- Memory.set_working_id(1)
-- local memory = Memory.get_crew_memory()
-- Overworld.ensure_generated_up_to_x(Crowsnest.Data.visibilitywidth/2)
-- memory.currentdestination_index = 1
-- Surfaces.create_surface(Common.current_destination())
-- Delay.global_add(Delay.global_enum.ADMIN_GO4)
-- elseif task == Delay.global_enum.ADMIN_GO4 then
-- Memory.set_working_id(1)
-- local memory = Memory.get_crew_memory()
-- Progression.go_from_starting_dock_to_first_destination()
-- memory.mapbeingloadeddestination_index = 1
-- memory.loadingticks = 0
-- end
-- end
-- Delay.global_clear_buffer()
-- Delay.global_move_tasks_to_buffer()
-- end
function Public.update_player_guis(tickinterval)
-- local global_memory = Memory.get_global_memory()
local players = game.connected_players
for _, player in pairs(players) do
local crew_id = Common.get_id_from_force_name(player.force.name)
Memory.set_working_id(crew_id)
Gui.update_gui(player)
end
end
function Public.update_players_second()
local global_memory = Memory.get_global_memory()
local connected_players = game.connected_players
local playerindex_to_time_played_continuously = {}
local playerindex_to_captainhood_priority = {}
for playerindex, time in pairs(global_memory.playerindex_to_time_played_continuously) do
local player = game.players[playerindex]
if player and Common.validate_player(player) then
-- port over
playerindex_to_time_played_continuously[playerindex] = time
end
end
for playerindex, time in pairs(global_memory.playerindex_to_captainhood_priority) do
local player = game.players[playerindex]
if player and Common.validate_player(player) then
-- port over
playerindex_to_captainhood_priority[playerindex] = time
end
end
for _, player in pairs(connected_players) do
local crew_id = Common.get_id_from_force_name(player.force.name)
Memory.set_working_id(crew_id)
if player.afk_time < Common.afk_time then
playerindex_to_time_played_continuously[player.index] = playerindex_to_time_played_continuously[player.index] or 0
playerindex_to_time_played_continuously[player.index] = playerindex_to_time_played_continuously[player.index] + 1
if Common.is_captain(player) then
playerindex_to_captainhood_priority[player.index] = 0
else
playerindex_to_captainhood_priority[player.index] = playerindex_to_captainhood_priority[player.index] or 0
playerindex_to_captainhood_priority[player.index] = playerindex_to_captainhood_priority[player.index] + 1
end
else
playerindex_to_time_played_continuously[player.index] = 0
playerindex_to_captainhood_priority[player.index] = 0
end
end
global_memory.playerindex_to_captainhood_priority = playerindex_to_captainhood_priority
global_memory.playerindex_to_time_played_continuously = playerindex_to_time_played_continuously
local afk_player_indices = {}
for _, player in pairs(connected_players) do
if player.afk_time >= Common.afk_time then
afk_player_indices[#afk_player_indices + 1] = player.index
end
end
global_memory.afk_player_indices = afk_player_indices
-- after updating tables:
for _, index in pairs(afk_player_indices) do
local player = game.players[index]
local crew_id = Common.get_id_from_force_name(player.force.name)
Memory.set_working_id(crew_id)
Roles.afk_player_tick(player)
end
end
function Public.update_alert_sound_frequency_tracker()
local memory = Memory.get_crew_memory()
if memory.seconds_until_alert_sound_can_be_played_again > 0 then
memory.seconds_until_alert_sound_can_be_played_again = memory.seconds_until_alert_sound_can_be_played_again - 1
memory.seconds_until_alert_sound_can_be_played_again = Math.max(0, memory.seconds_until_alert_sound_can_be_played_again)
end
end
function Public.check_for_cliff_explosives_in_hold_wooden_chests()
local memory = Memory.get_crew_memory()
local input_chests = memory.hold_surface_destroyable_wooden_chests
if not input_chests then return end
for i, chest in ipairs(input_chests) do
if chest and chest.valid then
local item_count = chest.get_item_count('cliff-explosives')
if item_count and item_count > 0 then
local surface = chest.surface
local explosion = {
name = 'wooden-chest-explosion',
position = chest.position
}
local remnants = {
name = 'wooden-chest-remnants',
position = chest.position
}
chest.destroy()
surface.create_entity(explosion)
surface.create_entity(remnants)
table.fast_remove(memory.hold_surface_destroyable_wooden_chests, i)
end
end
end
Public.update_boat_stored_resources()
end
return Public