2022-06-01 20:50:36 +02:00
-- This file is part of thesixthroc's Pirate Ship softmod, licensed under GPLv3 and stored at https://github.com/danielmartin0/ComfyFactorio-Pirates.
2021-10-13 10:21:53 +02:00
local Math = require ' maps.pirates.math '
2022-05-14 00:35:12 +02:00
local Raffle = require ' maps.pirates.raffle '
2022-05-05 10:55:48 +02:00
local Server = require ' utils.server '
2021-10-13 10:21:53 +02:00
local Utils = require ' maps.pirates.utils_local '
local CoreData = require ' maps.pirates.coredata '
local Memory = require ' maps.pirates.memory '
2022-03-19 23:20:55 +02:00
local _inspect = require ' utils.inspect ' . inspect
-- local simplex_noise = require 'utils.simplex_noise'.d2
-- local perlin_noise = require 'utils.perlin_noise'
2022-03-07 11:50:25 +02:00
-- local Force_health_booster = require 'modules.force_health_booster'
2021-10-13 10:21:53 +02:00
2022-05-09 01:36:48 +02:00
-- == Common variables and functions used throughout pirate ship files
2021-10-13 10:21:53 +02:00
local Public = { }
2022-03-11 00:11:51 +02:00
-- Public.active_crews_cap = 1
2022-05-14 00:35:12 +02:00
Public.activeCrewsCap = 2
Public.minimumCapacitySliderValue = 1
2022-06-03 01:15:54 +02:00
Public.minimum_run_capacity_to_enforce_space_for = 22
2022-03-11 00:11:51 +02:00
-- auto-disbanding when there are no players left in the crew:
2022-03-11 00:28:53 +02:00
Public.autodisband_ticks = nil
2022-03-11 00:11:51 +02:00
-- Public.autodisband_ticks = 30*60*60
2022-03-11 00:28:53 +02:00
-- Public.autodisband_ticks = 30 --the reason this is low is because the comfy server runs very slowly when no-one is on it
2021-10-13 10:21:53 +02:00
Public.boat_steps_at_a_time = 1
Public.seconds_after_landing_to_enable_AI = 45
Public.boat_default_starting_distance_from_shore = 22
-- Public.mapedge_distance_from_boat_starting_position = 136
Public.mapedge_distance_from_boat_starting_position = 272 -- to accommodate horseshoe
Public.deepwater_distance_from_leftmost_shore = 32
Public.lobby_spawnpoint = { x = - 72 , y = - 8 }
2022-05-29 13:36:27 +02:00
Public.structure_ensure_chunk_radius = 2
2021-10-13 10:21:53 +02:00
2022-03-11 18:46:02 +02:00
Public.allow_barreling_off_ship = true
2022-03-09 01:36:03 +02:00
2022-06-01 20:50:36 +02:00
Public.coin_tax_percentage = 10
2022-05-14 00:35:12 +02:00
Public.fraction_of_map_loaded_at_sea = 1
2022-02-28 18:36:46 +02:00
Public.map_loading_ticks_atsea = 68 * 60
2022-03-04 19:57:58 +02:00
Public.map_loading_ticks_atsea_maze = 80 * 60
2022-02-28 18:36:46 +02:00
Public.map_loading_ticks_atsea_dock = 20 * 60
2021-10-13 10:21:53 +02:00
Public.map_loading_ticks_onisland = 2 * 60 * 60
Public.loading_interval = 5
2022-05-29 13:36:27 +02:00
Public.first_cost_to_leave_macrox = 7
2021-10-13 10:21:53 +02:00
Public.minimum_ore_placed_per_tile = 10
2022-03-14 23:38:44 +02:00
Public.maze_minimap_jam_league = 960
2022-03-05 02:11:11 +02:00
2021-10-13 10:21:53 +02:00
Public.ban_from_rejoining_crew_ticks = 45 * 60 --to prevent observing map and rejoining
2022-03-14 14:44:30 +02:00
Public.afk_time = 60 * 60 * 5
Public.afk_warning_time = 60 * 60 * 4.5
2022-03-09 23:39:47 +02:00
Public.logged_off_items_preserved_minutes = 5
2022-05-29 13:36:27 +02:00
Public.logout_unprotected_items = { ' uranium-235 ' , ' uranium-238 ' , ' fluid-wagon ' , ' coal ' , ' electric-engine-unit ' , ' flying-robot-frame ' , ' advanced-circuit ' , ' beacon ' , ' speed-module-3 ' , ' speed-module-2 ' , ' roboport ' , ' construction-robot ' } --internal inventories of these will not be preserved
2022-03-07 11:50:25 +02:00
2021-10-13 10:21:53 +02:00
-- Public.mainshop_rate_limit_ticks = 11
function Public . ore_real_to_abstract ( amount )
2021-10-31 22:48:59 +02:00
return amount / 1800
2021-10-13 10:21:53 +02:00
end
function Public . ore_abstract_to_real ( amount )
2021-10-31 22:48:59 +02:00
return Math.ceil ( amount * 1800 )
2021-10-13 10:21:53 +02:00
end
2022-02-28 18:36:46 +02:00
-- 3000 oil resource is '1% yield'
2021-10-13 10:21:53 +02:00
function Public . oil_real_to_abstract ( amount )
2022-02-28 18:36:46 +02:00
return amount / ( 3000 )
2021-10-13 10:21:53 +02:00
end
function Public . oil_abstract_to_real ( amount )
2022-02-28 18:36:46 +02:00
return Math.ceil ( amount * 3000 )
2021-10-13 10:21:53 +02:00
end
2022-05-03 14:52:21 +02:00
function Public . difficulty_scale ( )
local memory = Memory.get_crew_memory ( )
if memory.overworldx > 0 then
return memory.difficulty
else
2022-05-05 10:55:48 +02:00
return 0.75
2022-05-03 14:52:21 +02:00
end
end
2021-10-13 10:21:53 +02:00
function Public . capacity ( ) return Memory.get_crew_memory ( ) . capacity end
-- function Public.mode() return Memory.get_crew_memory().mode end
function Public . overworldx ( ) return Memory.get_crew_memory ( ) . overworldx end
2022-02-28 22:35:45 +02:00
function Public . game_completion_progress ( ) return Public.overworldx ( ) / CoreData.victory_x end
2022-06-01 20:50:36 +02:00
function Public . game_completion_progress_capped ( ) return Math.clamp ( 0 , 1 , Public.overworldx ( ) / CoreData.victory_x ) end
2021-10-13 10:21:53 +02:00
function Public . capacity_scale ( )
local capacity = Public.capacity ( )
if not capacity then --e.g. for EE wattage on boats not owned by a crew
return 1
elseif capacity <= 1 then
return 0.5
elseif capacity <= 4 then
return 0.75
elseif capacity <= 8 then
return 1
elseif capacity <= 16 then
return 1.3
else
return 1.5
end
end
function Public . activecrewcount ( )
local global_memory = Memory.get_global_memory ( )
local memory = Memory.get_crew_memory ( )
if memory.id == 0 then return 0 end
local count = 0
for _ , id in pairs ( memory.crewplayerindices ) do
local player = game.players [ id ]
if player and player.valid and not Utils.contains ( global_memory.afk_player_indices , player.index ) then
count = count + 1
end
end
return count
end
function Public . notify_game ( message , color_override )
color_override = color_override or CoreData.colors . notify_game
2022-05-29 13:36:27 +02:00
game.print ( { " " , ' >> ' , message } , color_override )
2021-10-13 10:21:53 +02:00
end
2022-03-11 00:11:51 +02:00
function Public . notify_lobby ( message , color_override )
color_override = color_override or CoreData.colors . notify_lobby
2022-05-29 13:36:27 +02:00
game.forces [ ' player ' ] . print ( { " " , ' >> ' , message } , color_override )
2022-03-11 00:11:51 +02:00
end
2021-10-13 10:21:53 +02:00
function Public . notify_force ( force , message , color_override )
color_override = color_override or CoreData.colors . notify_force
2022-05-29 13:36:27 +02:00
force.print ( { " " , ' >> ' , message } , color_override )
2021-10-13 10:21:53 +02:00
end
function Public . notify_force_light ( force , message , color_override )
color_override = color_override or CoreData.colors . notify_force_light
2022-05-29 13:36:27 +02:00
force.print ( { " " , ' >> ' , message } , color_override )
2021-10-13 10:21:53 +02:00
end
2022-03-11 00:11:51 +02:00
function Public . notify_force_error ( force , message , color_override )
color_override = color_override or CoreData.colors . notify_error
2022-05-29 13:36:27 +02:00
force.print ( { " " , ' >> ' , message } , color_override )
2022-05-30 17:51:08 +02:00
force.play_sound { path = " utility/cannot_build " }
2021-10-13 10:21:53 +02:00
end
2022-03-11 00:11:51 +02:00
function Public . notify_player_error ( player , message , color_override )
2022-03-07 14:57:07 +02:00
color_override = color_override or CoreData.colors . notify_error
2022-05-29 13:36:27 +02:00
player.print ( { " " , ' ## ' , { ' pirates.notify_whisper ' } , ' ' , message } , color_override )
2022-05-30 17:51:08 +02:00
player.play_sound { path = " utility/cannot_build " }
2022-02-27 18:42:25 +02:00
end
function Public . notify_player_expected ( player , message , color_override )
color_override = color_override or CoreData.colors . notify_player_expected
2022-05-29 13:36:27 +02:00
player.print ( { " " , ' ## ' , { ' pirates.notify_whisper ' } , ' ' , message } , color_override )
2021-10-13 10:21:53 +02:00
end
2022-03-15 20:50:19 +02:00
function Public . notify_player_announce ( player , message , color_override )
color_override = color_override or CoreData.colors . notify_player_announce
2022-05-29 13:36:27 +02:00
player.print ( { " " , ' ## ' , { ' pirates.notify_whisper ' } , ' ' , message } , color_override )
2022-03-15 20:50:19 +02:00
end
2021-10-13 10:21:53 +02:00
function Public . parrot_speak ( force , message )
2022-05-29 13:36:27 +02:00
force.print ( { " " , { ' pirates.notify_parrot ' } , ' ' , message } , CoreData.colors . parrot )
2022-05-05 10:55:48 +02:00
2022-06-01 22:01:00 +02:00
local memory = Memory.get_crew_memory ( )
2022-06-02 20:54:47 +02:00
Server.to_discord_embed_raw ( { " " , ' [ ' .. memory.name .. ' ] ' , { ' pirates.notify_parrot ' } , ' ' , message } , true )
2021-10-13 10:21:53 +02:00
end
function Public . flying_text ( surface , position , text )
surface.create_entity (
{
name = ' flying-text ' ,
position = { position.x - 0.7 , position.y - 3.05 } ,
text = text ,
}
)
end
2022-03-07 11:50:25 +02:00
function Public . flying_text_small ( surface , position , text ) --differs just in the location of the text, more suitable for small things like '+'
2021-10-13 10:21:53 +02:00
surface.create_entity (
{
name = ' flying-text ' ,
2022-03-07 22:15:47 +02:00
position = { position.x - 0.08 , position.y - 1.5 } ,
-- position = {position.x - 0.06, position.y - 1.5},
2021-10-13 10:21:53 +02:00
text = text ,
}
)
end
2022-02-28 18:36:46 +02:00
function Public . processed_loot_data ( raw_data )
local ret = { }
for i = 1 , # raw_data do
local loot_data_item = raw_data [ i ]
ret [ # ret + 1 ] = {
weight = loot_data_item [ 1 ] ,
game_completion_progress_min = loot_data_item [ 2 ] ,
game_completion_progress_max = loot_data_item [ 3 ] ,
scaling = loot_data_item [ 4 ] ,
name = loot_data_item [ 5 ] ,
min_count = loot_data_item [ 6 ] ,
max_count = loot_data_item [ 7 ] ,
map_subtype = loot_data_item [ 8 ]
}
end
return ret
end
--=='raw' data is in the form e.g.
-- {
-- {100, 0, 1, false, 'steel-plate', 140, 180},
-- {50, 0, 1, false, 'defender-capsule', 15, 25},
-- {20, 0, 1, false, 'flying-robot-frame', 20, 35},
-- }
2022-05-30 17:51:08 +02:00
--@TODO: Replace this old function with the newer code in raffle.lua
2022-02-28 18:36:46 +02:00
function Public . raffle_from_processed_loot_data ( processed_loot_data , how_many , game_completion_progress )
local ret = { }
local loot_types , loot_weights = { } , { }
for i = 1 , # processed_loot_data , 1 do
local data = processed_loot_data [ i ]
table.insert ( loot_types , { [ ' name ' ] = data.name , [ ' min_count ' ] = data.min_count , [ ' max_count ' ] = data.max_count } )
local destination = Public.current_destination ( )
if not ( destination and destination.subtype and data.map_subtype and data.map_subtype == destination.subtype ) then
if data.scaling then -- scale down weights away from the midpoint 'peak' (without changing the mean)
local midpoint = ( data.game_completion_progress_max + data.game_completion_progress_min ) / 2
local difference = ( data.game_completion_progress_max - data.game_completion_progress_min )
local w = 2 * data.weight * Math.max ( 0 , 1 - ( Math.abs ( game_completion_progress - midpoint ) / ( difference / 2 ) ) )
table.insert ( loot_weights , w )
else -- no scaling
if data.game_completion_progress_min <= game_completion_progress and data.game_completion_progress_max >= game_completion_progress then
table.insert ( loot_weights , data.weight )
else
table.insert ( loot_weights , 0 )
end
end
end
end
for _ = 1 , how_many do
2022-05-14 00:35:12 +02:00
local loot = Raffle.raffle ( loot_types , loot_weights )
2022-05-05 10:55:48 +02:00
if loot then
local low = Math.max ( 1 , Math.ceil ( loot.min_count ) )
local high = Math.max ( 1 , Math.ceil ( loot.max_count ) )
local _count = Math.random ( low , high )
local lucky = Math.random ( 1 , 220 )
if lucky == 1 then --lucky
_count = _count * 3
elseif lucky <= 12 then
_count = _count * 2
end
ret [ # ret + 1 ] = { name = loot.name , count = _count }
end
2022-02-28 18:36:46 +02:00
end
return ret
end
2021-10-13 10:21:53 +02:00
2022-05-29 13:36:27 +02:00
function Public . give ( player , stacks , spill_position , short_form , spill_surface , flying_text_position )
2021-10-13 10:21:53 +02:00
-- stack elements of form {name = '', count = '', color = {r = , g = , b = }}
-- to just spill on the ground, pass player and nill and give a position and surface directly
spill_position = spill_position or player.position
spill_surface = spill_surface or player.surface
2022-04-30 04:15:50 +02:00
flying_text_position = flying_text_position or spill_position
2021-10-13 10:21:53 +02:00
local text1 = ' '
local text2 = ' '
local stacks2 = stacks
table.sort ( stacks2 , function ( a , b ) return a.name < b.name end )
if not ( spill_surface and spill_surface.valid ) then return end
local inv
if player then
inv = player.get_inventory ( defines.inventory . character_main )
2022-02-27 20:47:53 +02:00
if not ( inv and inv.valid ) then return end
2021-10-13 10:21:53 +02:00
end
for j = 1 , # stacks2 do
local stack = stacks2 [ j ]
local itemname , itemcount , flying_text_color = stack.name , stack.count or 1 , stack.color or ( CoreData.colors [ stack.name ] or { r = 1 , g = 1 , b = 1 } )
local itemcount_remember = itemcount
2022-03-19 23:20:55 +02:00
2021-10-13 10:21:53 +02:00
if not itemname then return end
if itemcount > 0 then
if player then
local a = inv.insert { name = itemname , count = itemcount }
itemcount = itemcount - a
if itemcount >= 50 then
for i = 1 , Math.floor ( itemcount / 50 ) , 1 do
local e = spill_surface.create_entity { name = ' item-on-ground ' , position = spill_position , stack = { name = itemname , count = 50 } }
if e and e.valid then
e.to_be_looted = true
end
itemcount = itemcount - 50
end
end
if itemcount > 0 then
2022-04-30 00:48:34 +02:00
-- if itemcount < 5 then
-- spill_surface.spill_item_stack(spill_position, {name = itemname, count = itemcount}, true)
-- else
-- local e = spill_surface.create_entity{name = 'item-on-ground', position = spill_position, stack = {name = itemname, count = itemcount}}
-- if e and e.valid then
-- e.to_be_looted = true
-- end
-- end
spill_surface.spill_item_stack ( spill_position , { name = itemname , count = itemcount } , true )
2021-10-13 10:21:53 +02:00
end
else
2022-04-30 00:48:34 +02:00
-- local e = spill_surface.create_entity{name = 'item-on-ground', position = spill_position, stack = {name = itemname, count = itemcount}}
-- if e and e.valid then
-- e.to_be_looted = true
-- end
spill_surface.spill_item_stack ( spill_position , { name = itemname , count = itemcount } , true )
2021-10-13 10:21:53 +02:00
end
end
2022-06-03 15:15:42 +02:00
if itemcount_remember >= 0 then
2022-05-29 13:36:27 +02:00
if short_form then
text1 = text1 .. ' [color= ' .. flying_text_color.r .. ' , ' .. flying_text_color.g .. ' , ' .. flying_text_color.b .. ' ] ' .. ' + ' .. itemcount_remember .. ' [/color] '
2022-04-30 00:48:34 +02:00
else
text1 = text1 .. ' [color=1,1,1] '
text1 = text1 .. ' + '
text1 = text1 .. itemcount_remember .. ' [/color] [item= ' .. itemname .. ' ] '
end
2021-10-13 10:21:53 +02:00
else
2022-05-29 13:36:27 +02:00
if short_form then
text1 = text1 .. ' [color= ' .. flying_text_color.r .. ' , ' .. flying_text_color.g .. ' , ' .. flying_text_color.b .. ' ] ' .. ' - ' .. - itemcount_remember .. ' [/color] '
else
text1 = text1 .. ' [color=1,1,1] '
text1 = text1 .. ' - '
text1 = text1 .. - itemcount_remember .. ' [/color] [item= ' .. itemname .. ' ] '
end
2021-10-13 10:21:53 +02:00
end
2022-04-30 00:48:34 +02:00
2022-05-29 13:36:27 +02:00
if player and ( not short_form ) then
2022-04-30 00:48:34 +02:00
-- count total of that item they have:
local new_total_count = 0
2022-03-03 02:19:20 +02:00
local cursor_stack = player.cursor_stack
if cursor_stack and cursor_stack.valid_for_read and cursor_stack.name == itemname and cursor_stack.count and cursor_stack.count > 0 then
new_total_count = new_total_count + cursor_stack.count
end
2022-04-30 00:48:34 +02:00
if inv and inv.get_item_count ( itemname ) and inv.get_item_count ( itemname ) > 0 then
new_total_count = new_total_count + inv.get_item_count ( itemname )
end
2022-03-01 23:59:48 +02:00
2022-04-30 00:48:34 +02:00
if # stacks2 > 1 then
text2 = text2 .. ' [color= ' .. flying_text_color.r .. ' , ' .. flying_text_color.g .. ' , ' .. flying_text_color.b .. ' ] ' .. new_total_count .. ' [/color] '
else
text2 = ' [color= ' .. flying_text_color.r .. ' , ' .. flying_text_color.g .. ' , ' .. flying_text_color.b .. ' ]( ' .. new_total_count .. ' )[/color] '
end
if j < # stacks2 then
text2 = text2 .. ' , '
end
2021-10-13 10:21:53 +02:00
end
if j < # stacks2 then
text1 = text1 .. ' , '
end
end
if text2 ~= ' ' then
if # stacks2 > 1 then
text2 = ' ( ' .. text2 .. ' ) '
end
2022-04-30 04:15:50 +02:00
Public.flying_text ( spill_surface , flying_text_position , text1 .. ' [font=count-font] ' .. text2 .. ' [/font] ' )
2021-10-13 10:21:53 +02:00
else
2022-04-30 04:15:50 +02:00
Public.flying_text ( spill_surface , flying_text_position , text1 )
2021-10-13 10:21:53 +02:00
end
end
2022-03-01 23:59:48 +02:00
function Public . is_captain ( player )
local memory = Memory.get_crew_memory ( )
if memory.playerindex_captain and memory.playerindex_captain == player.index then
return true
else
return false
end
end
2022-03-28 01:40:32 +02:00
-- lifted shamelessly from biter battles, since I haven't done balancing work on this:
2022-03-04 19:57:58 +02:00
function Public . surplus_evo_biter_damage_modifier ( surplus_evo )
2022-03-28 01:40:32 +02:00
return Math.floor ( surplus_evo / 2 * 1000 ) / 1000 --is this floor needed?
2022-03-01 23:59:48 +02:00
end
2022-05-06 02:47:27 +02:00
-- function Public.surplus_evo_biter_health_fractional_modifier(surplus_evo)
-- return surplus_evo*3
-- -- return Math.floor(surplus_evo*3*1000)/1000
-- end
2022-03-01 23:59:48 +02:00
2022-05-30 17:51:08 +02:00
2022-03-04 19:57:58 +02:00
function Public . set_biter_surplus_evo_modifiers ( )
2022-03-01 23:59:48 +02:00
local memory = Memory.get_crew_memory ( )
2022-03-04 19:57:58 +02:00
local enemy_force = memory.enemy_force
2022-03-01 23:59:48 +02:00
2022-03-04 19:57:58 +02:00
if not ( memory.evolution_factor and enemy_force and enemy_force.valid ) then
2022-03-01 23:59:48 +02:00
return nil
end
local surplus = memory.evolution_factor - 1
2022-03-04 19:57:58 +02:00
local damage_fractional_mod
2022-03-19 23:20:55 +02:00
-- local health_fractional_mod
2022-03-01 23:59:48 +02:00
2022-03-07 11:50:25 +02:00
if surplus > 0 then
2022-03-04 19:57:58 +02:00
damage_fractional_mod = Public.surplus_evo_biter_damage_modifier ( surplus )
2022-03-19 23:20:55 +02:00
-- health_fractional_mod = Public.surplus_evo_biter_health_fractional_modifier(surplus)
2022-03-04 19:57:58 +02:00
else
damage_fractional_mod = 0
2022-03-19 23:20:55 +02:00
-- health_fractional_mod = 0
2022-03-04 19:57:58 +02:00
end
enemy_force.set_ammo_damage_modifier ( ' melee ' , damage_fractional_mod )
enemy_force.set_ammo_damage_modifier ( ' biological ' , damage_fractional_mod )
enemy_force.set_ammo_damage_modifier ( ' artillery-shell ' , damage_fractional_mod )
enemy_force.set_ammo_damage_modifier ( ' flamethrower ' , damage_fractional_mod )
2022-03-19 23:20:55 +02:00
2022-03-07 11:50:25 +02:00
-- this event is behaving really weirdly, e.g. messing up samurai damage:
-- Force_health_booster.set_health_modifier(enemy_force.index, 1 + health_fractional_mod)
2022-03-01 23:59:48 +02:00
end
function Public . set_evo ( evolution )
local memory = Memory.get_crew_memory ( )
memory.evolution_factor = evolution
if memory.enemy_force_name then
2022-03-04 19:57:58 +02:00
local ef = memory.enemy_force
2022-03-01 23:59:48 +02:00
if ef and ef.valid then
ef.evolution_factor = memory.evolution_factor
2022-03-04 19:57:58 +02:00
Public.set_biter_surplus_evo_modifiers ( )
2022-03-01 23:59:48 +02:00
end
end
end
function Public . increment_evo ( evolution )
local memory = Memory.get_crew_memory ( )
memory.evolution_factor = memory.evolution_factor + evolution
if memory.enemy_force_name then
2022-03-04 19:57:58 +02:00
local ef = memory.enemy_force
2022-03-01 23:59:48 +02:00
if ef and ef.valid then
ef.evolution_factor = memory.evolution_factor
2022-03-04 19:57:58 +02:00
Public.set_biter_surplus_evo_modifiers ( )
2022-03-01 23:59:48 +02:00
end
end
end
2021-10-13 10:21:53 +02:00
function Public . current_destination ( )
local memory = Memory.get_crew_memory ( )
2022-03-19 23:20:55 +02:00
2021-10-13 10:21:53 +02:00
if memory.currentdestination_index then
return memory.destinations [ memory.currentdestination_index ]
else
return CoreData.fallthrough_destination
end
end
2022-03-14 14:44:30 +02:00
function Public . time_adjusted_departure_cost ( cost )
local memory = Memory.get_crew_memory ( )
local ret = cost
-- 1.5s memoization since the gui update will call this function:
if ( not memory.time_adjusted_departure_cost_memoized ) or ( memory.time_adjusted_departure_cost_memoized . tick < game.tick - 90 ) then
local destination = Public.current_destination ( )
local dynamic_data = destination.dynamic_data
local timer = dynamic_data.timer
local time_remaining = dynamic_data.time_remaining
2022-03-19 23:20:55 +02:00
2022-03-14 14:44:30 +02:00
if timer and time_remaining and timer >= 0 and time_remaining >= 0 then
local total_time = timer + time_remaining
local elapsed_fraction = timer / total_time
local cost_fraction = 1 - elapsed_fraction
2022-03-19 23:20:55 +02:00
2022-03-14 14:44:30 +02:00
local new_cost = { }
for name , count in pairs ( cost ) do
if type ( count ) == " number " then
2022-03-29 14:09:06 +02:00
new_cost [ name ] = Math.floor ( count * cost_fraction )
-- new_cost[name] = Math.ceil(count * cost_fraction)
2022-03-14 14:44:30 +02:00
else
new_cost [ name ] = count
end
end
2022-03-19 23:20:55 +02:00
2022-03-14 14:44:30 +02:00
ret = new_cost
end
local resources_strings1 = ' '
local j = 1
for name , count in pairs ( cost ) do
if name ~= ' launch_rocket ' then
if j > 1 then
resources_strings1 = resources_strings1 .. ' , '
end
resources_strings1 = resources_strings1 .. count .. ' [item= ' .. name .. ' ] '
j = j + 1
end
end
local resources_strings2 = ' '
j = 1
for name , count in pairs ( ret ) do
if name ~= ' launch_rocket ' then
if j > 1 then
resources_strings2 = resources_strings2 .. ' , '
end
resources_strings2 = resources_strings2 .. count .. ' [item= ' .. name .. ' ] '
j = j + 1
end
end
memory.time_adjusted_departure_cost_memoized = {
tick = game.tick ,
cost = ret ,
resources_strings = { resources_strings1 , resources_strings2 }
}
else
ret = memory.time_adjusted_departure_cost_memoized . cost
end
return ret
end
function Public . time_adjusted_departure_cost_resources_strings ( memory )
-- written to be efficient... only called in the gui after Public.time_adjusted_departure_cost()
return memory.time_adjusted_departure_cost_memoized . resources_strings
end
2022-03-04 19:57:58 +02:00
function Public . query_can_pay_cost_to_leave ( )
2021-10-13 10:21:53 +02:00
local memory = Memory.get_crew_memory ( )
local boat = memory.boat
local destination = Public.current_destination ( )
if not ( boat and destination ) then return end
2022-03-14 14:44:30 +02:00
local cost = destination.static_params . base_cost_to_undock
2021-10-13 10:21:53 +02:00
if not cost then return true end
2022-03-14 14:44:30 +02:00
local adjusted_cost = Public.time_adjusted_departure_cost ( cost )
2022-03-04 19:57:58 +02:00
local can_leave = true
2022-03-14 14:44:30 +02:00
for name , count in pairs ( adjusted_cost ) do
2022-03-04 19:57:58 +02:00
if name == ' launch_rocket ' then
if not destination.dynamic_data . rocketlaunched then
can_leave = false
end
else
local stored = ( memory.boat . stored_resources and memory.boat . stored_resources [ name ] ) or 0
if stored < count then
can_leave = false
end
2021-10-13 10:21:53 +02:00
end
end
2022-03-04 19:57:58 +02:00
return can_leave
2021-10-13 10:21:53 +02:00
end
2022-06-18 16:45:38 +02:00
-- This function assumes you're placing obstacle boxes in the hold
2022-03-12 00:53:36 +02:00
function Public . surface_place_random_obstacle_boxes ( surface , center , width , height , spacing_entity , box_size_table , contents )
contents = contents or { }
local memory = Memory.get_crew_memory ( )
if not surface then return end
local function boxposition ( )
local p1 = { x = center.x - width / 2 + Math.random ( Math.ceil ( width ) ) , y = center.y - height / 2 + Math.random ( Math.ceil ( height ) ) }
local p2 = surface.find_non_colliding_position ( spacing_entity , p1 , 32 , 2 , true ) or p1
return { x = p2.x , y = p2.y }
end
local placed = 0
for size , count in pairs ( box_size_table ) do
if count >= 1 then
for i = 1 , count do
placed = placed + 1
local p = boxposition ( )
for j = 1 , size ^ 2 do
2022-06-18 16:45:38 +02:00
local p2 = surface.find_non_colliding_position ( ' wooden-chest ' , p , 5 , 0.1 , true ) or p
2022-03-12 00:53:36 +02:00
local e = surface.create_entity { name = ' wooden-chest ' , position = p2 , force = memory.force_name , create_build_effect_smoke = false }
2022-06-15 23:00:18 +02:00
memory.hold_surface_destroyable_wooden_chests [ # memory.hold_surface_destroyable_wooden_chests + 1 ] = e
2022-03-12 00:53:36 +02:00
e.destructible = false
e.minable = false
e.rotatable = false
if contents [ placed ] and j == 1 then
local inventory = e.get_inventory ( defines.inventory . chest )
2022-03-12 13:51:11 +02:00
for name , count2 in pairs ( contents [ placed ] ) do
2022-03-12 00:53:36 +02:00
inventory.insert { name = name , count = count2 }
end
end
end
end
end
end
end
2021-10-13 10:21:53 +02:00
function Public . update_boat_stored_resources ( )
local memory = Memory.get_crew_memory ( )
local boat = memory.boat
2022-03-13 20:19:59 +02:00
if not boat.stored_resources then return end
2021-10-13 10:21:53 +02:00
local input_chests = boat.input_chests
if not input_chests then return end
2022-03-19 23:20:55 +02:00
2021-10-13 10:21:53 +02:00
for i , chest in ipairs ( input_chests ) do
2022-02-24 21:39:03 +02:00
if i > 1 and CoreData.cost_items [ i - 1 ] then
local inv = chest.get_inventory ( defines.inventory . chest )
local contents = inv.get_contents ( )
2022-03-19 23:20:55 +02:00
2022-02-24 21:39:03 +02:00
local item_type = CoreData.cost_items [ i - 1 ] . name
local count = contents [ item_type ] or 0
2022-03-19 23:20:55 +02:00
2022-03-13 20:19:59 +02:00
boat.stored_resources [ item_type ] = count
2022-02-24 21:39:03 +02:00
end
2021-10-13 10:21:53 +02:00
end
end
function Public . spend_stored_resources ( to_spend )
to_spend = to_spend or { }
local memory = Memory.get_crew_memory ( )
local boat = memory.boat
if not memory.boat . stored_resources then return end
local input_chests = boat.input_chests
if not input_chests then return end
2022-03-19 23:20:55 +02:00
2021-10-13 10:21:53 +02:00
for i , chest in ipairs ( input_chests ) do
2022-02-24 21:39:03 +02:00
if i > 1 then
local inv = chest.get_inventory ( defines.inventory . chest )
local item_type = CoreData.cost_items [ i - 1 ] . name
local to_spend_i = to_spend [ item_type ] or 0
2022-03-19 23:20:55 +02:00
2022-02-24 21:39:03 +02:00
if to_spend_i > 0 then
inv.remove { name = item_type , count = to_spend_i }
end
2021-10-13 10:21:53 +02:00
end
end
Public.update_boat_stored_resources ( )
end
2022-05-06 02:47:27 +02:00
function Public . new_healthbar ( text , target_entity , max_health , optional_id , health , size , extra_offset , location_override )
2021-10-13 10:21:53 +02:00
health = health or max_health
size = size or 0.5
text = text or false
2022-05-06 02:47:27 +02:00
extra_offset = extra_offset or 0
2022-05-05 14:08:19 +02:00
location_override = location_override or Memory.get_crew_memory ( )
2021-10-13 10:21:53 +02:00
local render1 = rendering.draw_sprite (
{
sprite = ' virtual-signal/signal-white ' ,
tint = { 0 , 200 , 0 } ,
x_scale = size * 15 ,
y_scale = size ,
render_layer = ' light-effect ' ,
target = target_entity ,
2022-05-06 02:47:27 +02:00
target_offset = { 0 , - 2.5 + extra_offset } ,
2021-10-13 10:21:53 +02:00
surface = target_entity.surface ,
}
)
local render2
if text then
render2 = rendering.draw_text (
{
color = { 255 , 255 , 255 } ,
2022-05-06 02:47:27 +02:00
scale = 1.2 + size * 2 ,
2021-10-13 10:21:53 +02:00
render_layer = ' light-effect ' ,
target = target_entity ,
2022-05-06 02:47:27 +02:00
target_offset = { 0 , - 3.6 - size * 0.6 + extra_offset } ,
2021-10-13 10:21:53 +02:00
surface = target_entity.surface ,
2022-05-05 10:55:48 +02:00
alignment = ' center ' ,
2021-10-13 10:21:53 +02:00
}
)
end
local new_healthbar = {
2022-05-05 10:55:48 +02:00
health = health ,
2021-10-13 10:21:53 +02:00
max_health = max_health ,
2022-05-05 10:55:48 +02:00
size = size ,
2022-05-06 02:47:27 +02:00
extra_offset = extra_offset ,
2021-10-13 10:21:53 +02:00
render1 = render1 ,
render2 = render2 ,
2022-03-15 20:50:19 +02:00
id = optional_id ,
2021-10-13 10:21:53 +02:00
}
2022-05-05 14:08:19 +02:00
if not location_override.healthbars then location_override.healthbars = { } end
location_override.healthbars [ target_entity.unit_number ] = new_healthbar
2021-10-13 10:21:53 +02:00
Public.update_healthbar_rendering ( new_healthbar , health )
return new_healthbar
end
2022-05-05 14:08:19 +02:00
function Public . transfer_healthbar ( old_unit_number , new_entity , location_override )
location_override = location_override or Memory.get_crew_memory ( )
if not location_override.healthbars then return end
local old_healthbar = location_override.healthbars [ old_unit_number ]
2022-05-07 22:56:16 +02:00
-- local new_unit_number = new_entity.unit_number
2022-05-05 10:55:48 +02:00
-- if new_surface_bool then
-- Public.new_healthbar(old_healthbar.render2, new_entity, old_healthbar.max_health, old_healthbar.id, old_healthbar.health, rendering.get_y_scale(old_healthbar.render1))
-- else
-- rendering.set_target(old_healthbar.render1, new_entity)
-- if old_healthbar.render2 then
-- rendering.set_target(old_healthbar.render2, new_entity)
-- end
-- memory.healthbars[new_unit_number] = old_healthbar
-- end
2022-05-06 02:47:27 +02:00
Public.new_healthbar ( old_healthbar.render2 , new_entity , old_healthbar.max_health , old_healthbar.id , old_healthbar.health , old_healthbar.size , old_healthbar.extra_offset , location_override )
2022-05-05 10:55:48 +02:00
if rendering.is_valid ( old_healthbar.render1 ) then
rendering.destroy ( old_healthbar.render1 )
end
if rendering.is_valid ( old_healthbar.render2 ) then
rendering.destroy ( old_healthbar.render2 )
end
2022-05-05 14:08:19 +02:00
location_override.healthbars [ old_unit_number ] = nil
2022-05-05 10:55:48 +02:00
end
2022-05-06 02:47:27 +02:00
function Public . entity_damage_healthbar ( entity , damage , location_override )
location_override = location_override or Memory.get_crew_memory ( )
2022-03-15 20:50:19 +02:00
local unit_number = entity.unit_number
2022-05-23 00:11:52 +02:00
if not ( location_override.healthbars ) then return end
2022-03-15 20:50:19 +02:00
2022-05-06 02:47:27 +02:00
local healthbar = location_override.healthbars [ unit_number ]
2022-03-15 20:50:19 +02:00
if not healthbar then return 0 end
local new_health = healthbar.health - damage
healthbar.health = new_health
Public.update_healthbar_rendering ( healthbar , new_health )
if entity and entity.valid then
entity.health = entity.prototype . max_health
end
return healthbar.health
end
2021-10-13 10:21:53 +02:00
function Public . update_healthbar_rendering ( new_healthbar , health )
local max_health = new_healthbar.max_health
local render1 = new_healthbar.render1
local render2 = new_healthbar.render2
if health > 0 then
local m = health / max_health
local x_scale = rendering.get_y_scale ( render1 ) * 15
rendering.set_x_scale ( render1 , x_scale * m )
rendering.set_color ( render1 , { Math.floor ( 255 - 255 * m ) , Math.floor ( 200 * m ) , 0 } )
2022-03-19 23:20:55 +02:00
2021-10-13 10:21:53 +02:00
if render2 then
rendering.set_text ( render2 , string.format ( ' HP: %d/%d ' , Math.ceil ( health ) , Math.ceil ( max_health ) ) )
end
else
rendering.destroy ( render1 )
if render2 then
rendering.destroy ( render2 )
end
end
end
function Public . spawner_count ( surface )
local memory = Memory.get_crew_memory ( )
2022-03-19 23:20:55 +02:00
2021-10-13 10:21:53 +02:00
local spawners = surface.find_entities_filtered ( { type = ' unit-spawner ' , force = memory.enemy_force_name } )
2022-05-30 17:51:08 +02:00
return # spawners or 0
2021-10-13 10:21:53 +02:00
end
function Public . create_poison_clouds ( surface , position )
local random_angles = { Math.rad ( Math.random ( 359 ) ) , Math.rad ( Math.random ( 359 ) ) }
surface.create_entity ( { name = ' poison-cloud ' , position = { x = position.x , y = position.y } } )
surface.create_entity ( { name = ' poison-cloud ' , position = { x = position.x + 12 * Math.cos ( random_angles [ 1 ] ) , y = position.y + 12 * Math.sin ( random_angles [ 1 ] ) } } )
surface.create_entity ( { name = ' poison-cloud ' , position = { x = position.x + 12 * Math.cos ( random_angles [ 2 ] ) , y = position.y + 12 * Math.sin ( random_angles [ 2 ] ) } } )
end
function Public . crew_get_crew_members ( )
local memory = Memory.get_crew_memory ( )
if memory.id == 0 then return { } end
local playerlist = { }
for _ , id in pairs ( memory.crewplayerindices ) do
local player = game.players [ id ]
if player and player.valid then playerlist [ # playerlist + 1 ] = player end
end
return playerlist
end
function Public . crew_get_crew_members_and_spectators ( )
local memory = Memory.get_crew_memory ( )
if memory.id == 0 then return { } end
local playerlist = { }
for _ , id in pairs ( memory.crewplayerindices ) do
local player = game.players [ id ]
if player and player.valid then playerlist [ # playerlist + 1 ] = player end
end
for _ , id in pairs ( memory.spectatorplayerindices ) do
local player = game.players [ id ]
if player and player.valid then playerlist [ # playerlist + 1 ] = player end
end
return playerlist
end
function Public . crew_get_nonafk_crew_members ( )
local global_memory = Memory.get_global_memory ( )
local memory = Memory.get_crew_memory ( )
if memory.id == 0 then return { } end
local playerlist = { }
for _ , id in pairs ( memory.crewplayerindices ) do
local player = game.players [ id ]
if player and player.valid and not Utils.contains ( global_memory.afk_player_indices , player.index ) then
playerlist [ # playerlist + 1 ] = player
end
end
return playerlist
end
function Public . destroy_decoratives_in_area ( surface , area , offset )
local area2 = { { area [ 1 ] [ 1 ] + offset.x , area [ 1 ] [ 2 ] + offset.y } , { area [ 2 ] [ 1 ] + offset.x , area [ 2 ] [ 2 ] + offset.y } }
surface.destroy_decoratives { area = area2 }
end
2022-07-03 16:58:44 +02:00
function Public . can_place_silo_setup ( surface , p , points_to_avoid , silo_count , generous , build_check_type_name )
-- game.print('checking silo pos: ' .. p.x .. ', ' .. p.y)
points_to_avoid = points_to_avoid or { }
2021-10-13 10:21:53 +02:00
Public.ensure_chunks_at ( surface , p , 0.2 )
build_check_type_name = build_check_type_name or ' manual '
local build_check_type = defines.build_check_type [ build_check_type_name ]
2022-02-24 21:39:03 +02:00
local s = true
2022-07-03 16:58:44 +02:00
local allowed = true
2022-02-24 21:39:03 +02:00
for i = 1 , silo_count do
2022-07-03 16:58:44 +02:00
local pos = { x = p.x + 9 * ( i - 1 ) , y = p.y }
s = ( surface.can_place_entity { name = ' rocket-silo ' , position = pos , build_check_type = build_check_type } or ( generous and i > 2 ) ) and s
for _ , pa in pairs ( points_to_avoid ) do
if Math.distance ( { x = pa.x , y = pa.y } , pos ) < pa.r then
allowed = false
break
end
end
2022-02-24 21:39:03 +02:00
end
2021-10-13 10:21:53 +02:00
2022-07-03 16:58:44 +02:00
return s and allowed
2021-10-13 10:21:53 +02:00
end
2022-03-19 23:20:55 +02:00
function Public . ensure_chunks_at ( surface , pos , radius ) --WARNING: THIS DOES NOT PLAY NICELY WITH DELAYED TASKS. log(_inspect{global_memory.working_id}) was observed to vary before and after this function.
-- local global_memory = Memory.get_global_memory()
2021-10-13 10:21:53 +02:00
if surface and surface.valid then
surface.request_to_generate_chunks ( pos , radius )
2022-03-19 23:20:55 +02:00
surface.force_generate_chunk_requests ( ) --WARNING: THIS DOES NOT PLAY NICELY WITH DELAYED TASKS. log(_inspect{global_memory.working_id}) was observed to vary before and after this function.
2021-10-13 10:21:53 +02:00
end
2022-03-19 23:20:55 +02:00
2021-10-13 10:21:53 +02:00
end
function Public . default_map_gen_settings ( width , height , seed )
width = width or 512
height = height or 512
seed = seed or Math.random ( 1 , 1000000 )
2022-03-19 23:20:55 +02:00
2021-10-13 10:21:53 +02:00
local map_gen_settings = {
[ ' seed ' ] = seed ,
[ ' width ' ] = width ,
[ ' height ' ] = height ,
[ ' water ' ] = 0 ,
--FIXME: Back when this was at x=2000, a crash was caused once by a player spawning at x=2000. So there will be a crash in future under unknown circumstances if there is no space at x=0,y=0.
[ ' starting_points ' ] = { { x = 0 , y = 0 } } ,
[ ' cliff_settings ' ] = { cliff_elevation_interval = 0 , cliff_elevation_0 = 0 } ,
[ ' default_enable_all_autoplace_controls ' ] = true ,
[ ' autoplace_settings ' ] = {
[ ' entity ' ] = { treat_missing_as_default = false } ,
[ ' tile ' ] = { treat_missing_as_default = true } ,
[ ' decorative ' ] = { treat_missing_as_default = true } ,
} ,
[ ' property_expression_names ' ] = { } ,
}
return map_gen_settings
end
2022-03-20 13:41:23 +02:00
2021-10-13 10:21:53 +02:00
function Public . build_from_blueprint ( bp_string , surface , pos , force , flipped )
flipped = flipped or false
local bp_entity = game.surfaces [ ' nauvis ' ] . create_entity { name = ' item-on-ground ' , position = { x = 158.5 , y = 158.5 } , stack = ' blueprint ' }
bp_entity.stack . import_stack ( bp_string )
local direction = flipped and defines.direction . south or defines.direction . north
local entities = bp_entity.stack . build_blueprint { surface = surface , force = force , position = { x = pos.x , y = pos.y } , force_build = true , skip_fog_of_war = false , direction = direction }
bp_entity.destroy ( )
local rev_entities = { }
for _ , e in pairs ( entities ) do
if e and e.valid then
2022-03-19 23:20:55 +02:00
local _collisions , revived_entity = e.silent_revive ( )
2021-10-13 10:21:53 +02:00
rev_entities [ # rev_entities + 1 ] = revived_entity
end
end
-- once again, to revive wagons:
for _ , e in pairs ( entities ) do
if e and e.valid and e.type and e.type == ' entity-ghost ' then
2022-03-19 23:20:55 +02:00
local _collisions , revived_entity = e.silent_revive ( )
2021-10-13 10:21:53 +02:00
rev_entities [ # rev_entities + 1 ] = revived_entity
if revived_entity and revived_entity.valid and revived_entity.name == ' locomotive ' then
revived_entity.color = { 255 , 106 , 52 }
revived_entity.get_inventory ( defines.inventory . fuel ) . insert ( { name = ' wood ' , count = 16 } )
revived_entity.operable = false
end
end
end
2022-03-19 23:20:55 +02:00
2021-10-13 10:21:53 +02:00
return rev_entities
end
function Public . build_small_loco ( surface , pos , force , color )
local p1 = { x = pos.x , y = pos.y }
local p2 = { x = pos.x , y = pos.y - 2 }
local p3 = { x = pos.x , y = pos.y + 2 }
local es = { }
es [ 1 ] = surface.create_entity ( { name = ' straight-rail ' , position = p1 , force = force , create_build_effect_smoke = false } )
es [ 2 ] = surface.create_entity ( { name = ' straight-rail ' , position = p2 , force = force , create_build_effect_smoke = false } )
es [ 3 ] = surface.create_entity ( { name = ' straight-rail ' , position = p3 , force = force , create_build_effect_smoke = false } )
es [ 4 ] = surface.create_entity ( { name = ' locomotive ' , position = p1 , force = force , create_build_effect_smoke = false } )
for _ , e in pairs ( es ) do
if e and e.valid then
e.destructible = false
e.minable = false
e.rotatable = false
e.operable = false
end
end
if es [ 4 ] and es [ 4 ] . valid then
es [ 4 ] . color = color
es [ 4 ] . get_inventory ( defines.inventory . fuel ) . insert ( { name = ' wood ' , count = 16 } )
end
end
2022-03-07 20:41:42 +02:00
function Public . add_tiles_from_blueprint ( tilesTable , bp_string , tile_name , offset )
local bp_entity = game.surfaces [ ' nauvis ' ] . create_entity { name = ' item-on-ground ' , position = { x = 158.5 , y = 158.5 } , stack = ' blueprint ' }
bp_entity.stack . import_stack ( bp_string )
local bp_tiles = bp_entity.stack . get_blueprint_tiles ( )
if bp_tiles then
for _ , tile in pairs ( bp_tiles ) do
tilesTable [ # tilesTable + 1 ] = { name = tile_name , position = { x = tile.position . x + offset.x , y = tile.position . y + offset.y } }
end
end
2022-03-19 23:20:55 +02:00
2022-03-07 20:41:42 +02:00
bp_entity.destroy ( )
2022-03-19 23:20:55 +02:00
2022-03-07 20:41:42 +02:00
return tilesTable
end
2021-10-13 10:21:53 +02:00
function Public . tile_positions_from_blueprint ( bp_string , offset )
2022-05-30 17:51:08 +02:00
-- May '22 change: There seems to be a base game bug(?) which causes the tiles to be offset. We now correct for that (with ` - (max_x - min_x)/2` and ` - (max_y - min_y)/2`).
2021-10-13 10:21:53 +02:00
local bp_entity = game.surfaces [ ' nauvis ' ] . create_entity { name = ' item-on-ground ' , position = { x = 158.5 , y = 158.5 } , stack = ' blueprint ' }
bp_entity.stack . import_stack ( bp_string )
local bp_tiles = bp_entity.stack . get_blueprint_tiles ( )
2022-05-29 13:36:27 +02:00
local min_x
local min_y
local max_x
local max_y
2021-10-13 10:21:53 +02:00
local positions = { }
if bp_tiles then
for _ , tile in pairs ( bp_tiles ) do
2022-05-29 13:36:27 +02:00
positions [ # positions + 1 ] = { x = tile.position . x , y = tile.position . y }
if not min_x or tile.position . x < min_x then
min_x = tile.position . x
end
if not min_y or tile.position . y < min_y then
min_y = tile.position . y
end
if not max_x or tile.position . x > max_x then
max_x = tile.position . x
end
if not max_y or tile.position . y > max_y then
max_y = tile.position . y
end
end
end
if min_x and min_y and max_x and max_y then
for _ , pos in pairs ( positions ) do
pos.x = pos.x - ( max_x - min_x ) / 2 + offset.x
pos.y = pos.y - ( max_y - min_y ) / 2 + offset.y
2021-10-13 10:21:53 +02:00
end
end
2022-03-19 23:20:55 +02:00
2021-10-13 10:21:53 +02:00
bp_entity.destroy ( )
2022-03-19 23:20:55 +02:00
2021-10-13 10:21:53 +02:00
return positions
end
function Public . tile_positions_from_blueprint_arrayform ( bp_string , offset )
2022-05-30 17:51:08 +02:00
-- does not include the above May '22 fix yet, so may give different results
2021-10-13 10:21:53 +02:00
local bp_entity = game.surfaces [ ' nauvis ' ] . create_entity { name = ' item-on-ground ' , position = { x = 158.5 , y = 158.5 } , stack = ' blueprint ' }
bp_entity.stack . import_stack ( bp_string )
local bp_tiles = bp_entity.stack . get_blueprint_tiles ( )
local positions = { }
if bp_tiles then
for _ , tile in pairs ( bp_tiles ) do
local x = tile.position . x + offset.x
local y = tile.position . y + offset.y
if not positions [ x ] then positions [ x ] = { } end
positions [ x ] [ y ] = true
end
end
2022-03-19 23:20:55 +02:00
2021-10-13 10:21:53 +02:00
bp_entity.destroy ( )
2022-03-19 23:20:55 +02:00
2021-10-13 10:21:53 +02:00
return positions
end
function Public . entity_positions_from_blueprint ( bp_string , offset )
local bp_entity = game.surfaces [ ' nauvis ' ] . create_entity { name = ' item-on-ground ' , position = { x = 158.5 , y = 158.5 } , stack = ' blueprint ' }
bp_entity.stack . import_stack ( bp_string )
local es = bp_entity.stack . get_blueprint_entities ( )
local positions = { }
if es then
for _ , e in pairs ( es ) do
positions [ # positions + 1 ] = { x = e.position . x + offset.x , y = e.position . y + offset.y }
end
end
2022-03-19 23:20:55 +02:00
2021-10-13 10:21:53 +02:00
bp_entity.destroy ( )
2022-03-19 23:20:55 +02:00
2021-10-13 10:21:53 +02:00
return positions
end
function Public . get_random_unit_type ( evolution )
2022-03-28 01:40:32 +02:00
-- designed to approximate https://wiki.factorio.com/Enemies
2021-10-13 10:21:53 +02:00
local r = Math.random ( )
if Math.random ( 5 ) == 1 then
if r < 1 - 1 / 0.15 * ( evolution - 0.25 ) then
return ' small-biter '
elseif r < 1 - 1 / 0.3 * ( evolution - 0.4 ) then
return ' small-spitter '
elseif r < 1 - 0.85 / 0.5 * ( evolution - 0.5 ) then
return ' medium-spitter '
elseif r < 1 - 0.4 / 0.1 * ( evolution - 0.9 ) then
return ' big-spitter '
else
return ' behemoth-spitter '
end
else
if r < 1 - 1 / 0.4 * ( evolution - 0.2 ) then
return ' small-biter '
elseif r < 1 - 0.8 / 0.5 * ( evolution - 0.5 ) then
return ' medium-biter '
elseif r < 1 - 0.4 / 0.1 * ( evolution - 0.9 ) then
return ' big-biter '
else
return ' behemoth-biter '
end
end
end
function Public . get_random_biter_type ( evolution )
2022-03-28 01:40:32 +02:00
-- designed to approximate https://wiki.factorio.com/Enemies
2021-10-13 10:21:53 +02:00
local r = Math.random ( )
if r < 1 - 1 / 0.4 * ( evolution - 0.2 ) then
return ' small-biter '
elseif r < 1 - 0.8 / 0.5 * ( evolution - 0.5 ) then
return ' medium-biter '
elseif r < 1 - 0.4 / 0.1 * ( evolution - 0.9 ) then
return ' big-biter '
else
return ' behemoth-biter '
end
end
function Public . get_random_spitter_type ( evolution )
2022-03-28 01:40:32 +02:00
-- designed to approximate https://wiki.factorio.com/Enemies
2021-10-13 10:21:53 +02:00
local r = Math.random ( )
if r < 1 - 1 / 0.3 * ( evolution - 0.4 ) then
return ' small-spitter '
elseif r < 1 - 0.85 / 0.5 * ( evolution - 0.5 ) then
return ' medium-spitter '
elseif r < 1 - 0.4 / 0.1 * ( evolution - 0.9 ) then
return ' big-spitter '
else
return ' behemoth-spitter '
end
end
function Public . get_random_worm_type ( evolution )
-- custom
local r = Math.random ( )
if r < 1 - 1 / 0.7 * ( evolution + 0.1 ) then
return ' small-worm-turret '
2022-02-28 22:35:45 +02:00
elseif r < 1 - 0.75 / 0.75 * ( evolution - 0.25 ) then
2021-10-13 10:21:53 +02:00
return ' medium-worm-turret '
elseif r < 1 - 0.4 / 0.4 * ( evolution - 0.6 ) then
return ' big-worm-turret '
else
return ' behemoth-worm-turret '
end
end
function Public . maximumUnitPollutionCost ( evolution )
if evolution < 0.2 then return 4
elseif evolution < 0.5 then return 20
elseif evolution < 0.9 then return 80
else return 400
end
end
function Public . averageUnitPollutionCost ( evolution )
local sum_biters = 0
local f1 = Math.slopefromto ( 1 - 1 / 0.4 * ( evolution - 0.2 ) , 0 , 1 )
local f2 = Math.slopefromto ( 1 - 0.8 / 0.5 * ( evolution - 0.5 ) , 0 , 1 )
local f3 = Math.slopefromto ( 1 - 0.4 / 0.1 * ( evolution - 0.9 ) , 0 , 1 )
sum_biters = sum_biters + 4 * f1
sum_biters = sum_biters + 20 * ( f2 - f1 )
sum_biters = sum_biters + 80 * ( f3 - f2 )
sum_biters = sum_biters + 400 * ( 1 - f3 )
local sum_spitters = 0
2022-02-27 18:42:25 +02:00
local g1 = Math.slopefromto ( 1 - 1 / 0.15 * ( evolution - 0.25 ) , 0 , 1 )
local g2 = Math.slopefromto ( 1 - 1 / 0.3 * ( evolution - 0.4 ) , 0 , 1 )
local g3 = Math.slopefromto ( 1 - 0.85 / 0.5 * ( evolution - 0.5 ) , 0 , 1 )
local g4 = Math.slopefromto ( 1 - 0.4 / 0.1 * ( evolution - 0.9 ) , 0 , 1 )
sum_spitters = sum_spitters + 4 * g1
sum_spitters = sum_spitters + 4 * ( g2 - g1 )
sum_spitters = sum_spitters + 12 * ( g3 - g2 )
sum_spitters = sum_spitters + 30 * ( g4 - g3 )
sum_spitters = sum_spitters + 200 * ( 1 - g4 )
2021-10-13 10:21:53 +02:00
return ( 5 * sum_biters + sum_spitters ) / 6
end
function Public . orthog_positions_in_orthog_area ( area )
local positions = { }
for y = area [ 1 ] [ 2 ] + 0.5 , area [ 2 ] [ 2 ] - 0.5 , 1 do
for x = area [ 1 ] [ 1 ] + 0.5 , area [ 2 ] [ 1 ] - 0.5 , 1 do
positions [ # positions + 1 ] = { x = x , y = y }
end
end
return positions
end
function Public . tileslist_add_area_offset ( tiles_list_to_add_to , area , offset , tile_type )
for _ , p in pairs ( Public.orthog_positions_in_orthog_area ( area ) ) do
tiles_list_to_add_to [ # tiles_list_to_add_to + 1 ] = { name = tile_type , position = { x = offset.x + p.x , y = offset.y + p.y } }
end
end
function Public . central_positions_within_area ( area , offset )
local offsetx = offset.x or 0
local offsety = offset.y or 0
local xr1 , xr2 , yr1 , yr2 = offsetx + Math.ceil ( area [ 1 ] [ 1 ] - 0.5 ) , offsetx + Math.floor ( area [ 2 ] [ 1 ] + 0.5 ) , offsety + Math.ceil ( area [ 1 ] [ 2 ] - 0.5 ) , offsety + Math.floor ( area [ 2 ] [ 2 ] + 0.5 )
local positions = { }
for y = yr1 + 0.5 , yr2 - 0.5 , 1 do
for x = xr1 + 0.5 , xr2 - 0.5 , 1 do
positions [ # positions + 1 ] = { x = x , y = y }
end
end
return positions
end
function Public . tiles_from_area ( tiles_list_to_add_to , area , offset , tile_type )
for _ , p in pairs ( Public.central_positions_within_area ( area , offset ) ) do
tiles_list_to_add_to [ # tiles_list_to_add_to + 1 ] = { name = tile_type , position = { x = p.x , y = p.y } }
end
end
function Public . tiles_horizontally_flipped ( tiles , x_to_flip_about )
local tiles2 = { }
for _ , t in pairs ( tiles ) do
local t2 = Utils.deepcopy ( t )
t2.position = { x = 2 * x_to_flip_about - t2.position . x , y = t2.position . y }
tiles2 [ # tiles2 + 1 ] = t2
end
return tiles2
end
function Public . validate_player ( player )
if player and player.valid and player.connected and game.players [ player.name ] then
2022-03-13 03:44:32 +02:00
return true
else
if _DEBUG then
log ( ' player validation fail: ' .. ( player.name or ' noname ' ) )
end
return false
2021-10-13 10:21:53 +02:00
end
end
function Public . validate_player_and_character ( player )
local ret = Public.validate_player ( player )
ret = ret and player.character and player.character . valid
return ret
end
2022-03-09 01:36:03 +02:00
function Public . send_important_items_from_player_to_crew ( player , all_items )
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_guns )
player_inv [ 4 ] = game.players [ player.index ] . get_inventory ( defines.inventory . character_ammo )
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 ] . valid then
-- local to_keep = {}
local to_remove = { }
for iii = 1 , # player_inv [ ii ] , 1 do
-- local item_stack = player_inv[ii][iii] --don't do this as LuaItemStack is a reference!
2022-03-14 19:37:18 +02:00
if player_inv [ ii ] and player_inv [ ii ] [ iii ] . valid and player_inv [ ii ] [ iii ] . valid_for_read then
2022-05-05 10:55:48 +02:00
if all_items or ( player_inv [ ii ] [ iii ] . name and Utils.contains ( Public.logout_unprotected_items , player_inv [ ii ] [ iii ] . name ) ) then
2022-03-09 01:36:03 +02:00
to_remove [ # to_remove + 1 ] = player_inv [ ii ] [ iii ]
any = true
-- else
-- to_keep[#to_keep + 1] = Utils.deepcopy(player_inv[ii][iii])
end
end
end
if # to_remove > 0 then
for iii = 1 , # to_remove , 1 do
if to_remove [ iii ] . valid_for_read then
Public.give_items_to_crew { { name = to_remove [ iii ] . name , count = to_remove [ iii ] . count } }
to_remove [ iii ] . clear ( )
end
end
-- clear and move over from to_keep if necessary?
end
end
end
return any
end
2022-05-29 13:36:27 +02:00
function Public . give_items_to_crew ( items )
2021-10-13 10:21:53 +02:00
local memory = Memory.get_crew_memory ( )
local boat = memory.boat
if not boat then return end
local surface_name = boat.surface_name
if not surface_name then return end
local surface = game.surfaces [ surface_name ]
if not ( surface and surface.valid ) then return end
2022-05-06 14:00:57 +02:00
local chest , chest2
2022-05-29 13:36:27 +02:00
if items.name and items.name == ' coin ' then
2022-05-06 14:00:57 +02:00
chest = boat.backup_output_chest
if not ( chest and chest.valid ) then return end
chest2 = boat.output_chest
if not ( chest2 and chest2.valid ) then return end
else
chest = boat.output_chest
if not ( chest and chest.valid ) then return end
chest2 = boat.backup_output_chest
if not ( chest2 and chest2.valid ) then return end
end
2021-10-13 10:21:53 +02:00
local inventory = chest.get_inventory ( defines.inventory . chest )
2022-03-09 01:36:03 +02:00
if items.name then --1 item
if not ( items.count and items.count > 0 ) then return end
local inserted = inventory.insert ( items )
if items.count - inserted > 0 then
2021-10-13 10:21:53 +02:00
local inventory2 = chest2.get_inventory ( defines.inventory . chest )
2022-03-09 01:36:03 +02:00
local i2 = Utils.deepcopy ( items )
2022-03-11 12:40:55 +02:00
if i2.name then
i2.count = items.count - inserted
local inserted2 = inventory2.insert ( i2 )
if items.count - inserted - inserted2 > 0 then
local force = memory.force
if not ( force and force.valid ) then return end
Public.notify_force ( force , ' Warning: captain \' s cabin chests are full! ' )
end
else
2022-03-13 03:44:32 +02:00
if _DEBUG then
2022-03-19 23:20:55 +02:00
log ( ' give_items_to_crew: i2.name is nil. _inspect: ' )
log ( _inspect ( items ) )
log ( _inspect ( i2 ) )
2022-03-13 03:44:32 +02:00
end
2021-10-13 10:21:53 +02:00
end
end
2022-03-09 01:36:03 +02:00
else
for _ , i in pairs ( items ) do
if not ( i.count and i.count > 0 ) then return end
local inserted = inventory.insert ( i )
if i.count - inserted > 0 then
local inventory2 = chest2.get_inventory ( defines.inventory . chest )
local i2 = Utils.deepcopy ( i )
i2.count = i.count - inserted
local inserted2 = inventory2.insert ( i2 )
if i.count - inserted - inserted2 > 0 then
local force = memory.force
if not ( force and force.valid ) then return end
Public.notify_force ( force , ' Warning: captain \' s cabin chests are full! ' )
end
end
end
2021-10-13 10:21:53 +02:00
end
end
2022-05-29 13:36:27 +02:00
function Public . version_to_array ( v )
local vArray = { }
if type ( v ) == ' number ' then --this is a legacy form
local vs = tostring ( v )
for i = 1 , string.len ( vs ) do
local char = vs : sub ( i , i )
if i ~= 2 then
vArray [ # vArray + 1 ] = char
end
end
else
for i = 1 , string.len ( v ) do
local char = v : sub ( i , i )
if char ~= ' . ' then
vArray [ # vArray + 1 ] = char
end
end
end
return vArray
end
function Public . version_greater_than ( v1 , v2 )
local v1Array = Public.version_to_array ( v1 )
local v2Array = Public.version_to_array ( v2 )
for i = 1 , math.max ( # v1Array , # v2Array ) do
local v1i = tonumber ( v1Array [ i ] )
local v2i = tonumber ( v2Array [ i ] )
if v1i ~= nil and v2i ~= nil then
if v1i < v2i then
return false
elseif v1i > v2i then
return true
end
elseif v1i == nil then
return false
else
return true
end
end
end
2021-10-13 10:21:53 +02:00
function Public . init_game_settings ( technology_price_multiplier )
2022-02-26 15:55:36 +02:00
--== Tuned for Pirate Ship ==--
2022-03-19 23:20:55 +02:00
2021-10-13 10:21:53 +02:00
global.friendly_fire_history = { }
global.landfill_history = { }
global.mining_history = { }
game.difficulty_settings . technology_price_multiplier = technology_price_multiplier
game.map_settings . enemy_evolution.pollution_factor = 0
game.map_settings . enemy_evolution.time_factor = 0
game.map_settings . enemy_evolution.destroy_factor = 0
game.map_settings . unit_group.min_group_gathering_time = 60 * 5
game.map_settings . unit_group.max_group_gathering_time = 60 * 210
game.map_settings . unit_group.max_wait_time_for_late_members = 60 * 15
game.map_settings . unit_group.member_disown_distance = 5000
game.map_settings . unit_group.max_group_radius = 70
game.map_settings . unit_group.min_group_radius = 0.5 --seems to govern biter 'attack area' stopping distance
-- (0,2) for a symmetric search:
game.map_settings . path_finder.goal_pressure_ratio = - 0.1 --small pressure for stupid paths
2022-03-04 19:57:58 +02:00
game.map_settings . path_finder.fwd2bwd_ratio = 2 -- on experiments I found that this value was symmetric, despite the vanilla game comments saying it is 1...
2021-10-13 10:21:53 +02:00
game.map_settings . max_failed_behavior_count = 2
game.map_settings . path_finder.max_work_done_per_tick = 20000
game.map_settings . path_finder.short_cache_min_algo_steps_to_cache = 100
game.map_settings . path_finder.cache_accept_path_start_distance_ratio = 0.1
game.map_settings . enemy_expansion.enabled = true
2022-02-26 15:55:36 +02:00
-- faster expansion:
2022-03-19 23:20:55 +02:00
-- game.map_settings.enemy_expansion.min_expansion_cooldown = 4 * 3600
-- game.map_settings.enemy_expansion.max_expansion_cooldown = 30 * 3600
-- slowed down due to the effect on single-player games:
game.map_settings . enemy_expansion.min_expansion_cooldown = 6 * 3600
game.map_settings . enemy_expansion.max_expansion_cooldown = 45 * 3600
2022-02-26 15:55:36 +02:00
game.map_settings . enemy_expansion.settler_group_max_size = 24
game.map_settings . enemy_expansion.settler_group_min_size = 6
-- maybe should be 3.5 if possible:
game.map_settings . enemy_expansion.max_expansion_distance = 4
2021-10-13 10:21:53 +02:00
-- could turn off default AI attacks:
game.map_settings . pollution.enemy_attack_pollution_consumption_modifier = 1
2022-03-19 23:20:55 +02:00
--
2021-10-13 10:21:53 +02:00
game.map_settings . pollution.enabled = true
game.map_settings . pollution.expected_max_per_chunk = 120
game.map_settings . pollution.min_to_show_per_chunk = 10
game.map_settings . pollution.min_pollution_to_damage_trees = 20
game.map_settings . pollution.pollution_per_tree_damage = 0.2
game.map_settings . pollution.max_pollution_to_restore_trees = 0.04
game.map_settings . pollution.pollution_restored_per_tree_damage = 0.01
game.map_settings . pollution.pollution_with_max_forest_damage = 80
game.map_settings . pollution.ageing = 0.1
game.map_settings . pollution.diffusion_ratio = 0.035
--
-- game.forces.neutral.character_inventory_slots_bonus = 500
game.forces . enemy.evolution_factor = 0
end
2022-06-15 23:00:18 +02:00
-- prefer memory.force_name if possible
function Public . get_crew_force_name ( id )
return string.format ( ' crew-%03d ' , id )
end
-- prefer memory.enemy_force_name if possible
function Public . get_enemy_force_name ( id )
return string.format ( ' enemy-%03d ' , id )
end
-- prefer memory.ancient_friendly_force_name if possible
function Public . get_ancient_friendly_force_name ( id )
return string.format ( ' ancient-friendly-%03d ' , id )
end
-- prefer memory.ancient_enemy_force_name if possible
function Public . get_ancient_hostile_force_name ( id )
return string.format ( ' ancient-hostile-%03d ' , id )
end
function Public . get_id_from_force_name ( force_name )
return tonumber ( string.sub ( force_name , - 3 , - 1 ) ) or nil
end
2021-10-13 10:21:53 +02:00
return Public