2024-08-10 19:26:52 +02:00
-- My general purpose utility functions and constants for factorio
-- Also contains some constants
require ( " lib/oarc_gui_utils " )
require ( " mod-gui " )
local util = require ( " util " )
--------------------------------------------------------------------------------
-- Useful constants
--------------------------------------------------------------------------------
CHUNK_SIZE = 32
MAX_FORCES = 64
TICKS_PER_SECOND = 60
TICKS_PER_MINUTE = TICKS_PER_SECOND * 60
TICKS_PER_HOUR = TICKS_PER_MINUTE * 60
MAX_INT32_POS = 2147483647
MAX_INT32_NEG = - 2147483648
--------------------------------------------------------------------------------
-- --------------------------------------------------------------------------------
-- -- General Helper Functions
-- --------------------------------------------------------------------------------
-- -- Prints flying text.
-- -- Color is optional
-- function FlyingText(msg, pos, color, surface)
-- if color == nil then
-- surface.create_entity({ name = "flying-text", position = pos, text = msg })
-- else
-- surface.create_entity({ name = "flying-text", position = pos, text = msg, color = color })
-- end
-- end
2024-09-13 02:58:19 +02:00
-- Get a printable GPS string
2024-09-14 19:59:28 +02:00
---@param surface_name string
---@param position MapPosition
2024-09-13 02:58:19 +02:00
---@return string
2024-09-14 19:59:28 +02:00
function GetGPStext ( surface_name , position )
return " [gps= " .. position.x .. " , " .. position.y .. " , " .. surface_name .. " ] "
2024-09-13 02:58:19 +02:00
end
2024-08-10 19:26:52 +02:00
-- -- Requires having an on_tick handler.
-- function DisplaySpeechBubble(player, text, timeout_secs)
2024-10-20 02:48:20 +02:00
-- if (storage.oarc_speech_bubbles == nil) then
-- storage.oarc_speech_bubbles = {}
2024-08-10 19:26:52 +02:00
-- end
-- if (player and player.character) then
-- local sp = player.surface.create_entity{name = "compi-speech-bubble",
-- position = player.position,
-- text = text,
-- source = player.character}
2024-10-20 02:48:20 +02:00
-- table.insert(storage.oarc_speech_bubbles, {entity=sp,
2024-08-10 19:26:52 +02:00
-- timeout_tick=game.tick+(timeout_secs*TICKS_PER_SECOND)})
-- end
-- end
---Render some text on the ground. Visible to all players. Forever.
---@param surface LuaSurface
---@param position MapPosition
---@param scale number
---@param text string
---@param color Color
2024-09-24 17:32:58 +02:00
---@param alignment TextAlign?
2024-08-10 19:26:52 +02:00
---@return nil
2024-09-24 17:32:58 +02:00
function RenderPermanentGroundText ( surface , position , scale , text , color , alignment )
2024-08-11 01:29:50 +02:00
rendering.draw_text { text = text ,
surface = surface ,
target = position ,
color = color ,
scale = scale ,
--Allowed fonts: default-dialog-button default-game compilatron-message-font default-large default-large-semibold default-large-bold heading-1 compi
font = " compi " ,
2024-09-24 17:32:58 +02:00
alignment = alignment ,
2024-08-11 01:29:50 +02:00
draw_on_ground = true }
end
2024-08-10 19:26:52 +02:00
---A standardized helper text that fades out over time
2024-09-21 03:39:01 +02:00
---@param text string|LocalisedString
2024-08-10 19:26:52 +02:00
---@param surface LuaSurface
---@param position MapPosition
---@param ttl number
2024-09-21 03:39:01 +02:00
---@param alignment TextAlign?
2024-08-10 19:26:52 +02:00
---@return nil
2024-09-21 03:39:01 +02:00
function TemporaryHelperText ( text , surface , position , ttl , alignment )
2024-10-21 19:48:42 +02:00
local render_object = rendering.draw_text { text = text ,
2024-08-11 01:29:50 +02:00
surface = surface ,
target = position ,
color = { 0.7 , 0.7 , 0.7 , 0.7 } ,
scale = 1 ,
font = " compi " ,
time_to_live = ttl ,
2024-09-21 03:39:01 +02:00
alignment = alignment ,
2024-08-11 01:29:50 +02:00
draw_on_ground = false }
2024-10-21 19:48:42 +02:00
local rid = render_object.id
2024-10-20 02:48:20 +02:00
table.insert ( storage.oarc_renders_fadeout , rid )
2024-08-10 19:26:52 +02:00
end
-- -- Every second, check a global table to see if we have any speech bubbles to kill.
-- function TimeoutSpeechBubblesOnTick()
-- if ((game.tick % (TICKS_PER_SECOND)) == 3) then
2024-10-20 02:48:20 +02:00
-- if (storage.oarc_speech_bubbles and (#storage.oarc_speech_bubbles > 0)) then
-- for k,sp in pairs(storage.oarc_speech_bubbles) do
2024-08-10 19:26:52 +02:00
-- if (game.tick > sp.timeout_tick) then
-- if (sp.entity ~= nil) and (sp.entity.valid) then
-- sp.entity.start_fading_out()
-- end
2024-10-20 02:48:20 +02:00
-- table.remove(storage.oarc_speech_bubbles, k)
2024-08-10 19:26:52 +02:00
-- end
-- end
-- end
-- end
-- end
---Every tick, check a global table to see if we have any rendered thing that needs fading out.
---@return nil
function FadeoutRenderOnTick ( )
2024-10-20 02:48:20 +02:00
if ( storage.oarc_renders_fadeout and ( # storage.oarc_renders_fadeout > 0 ) ) then
for k , rid in pairs ( storage.oarc_renders_fadeout ) do
2024-10-21 19:48:42 +02:00
local render_object = rendering.get_object_by_id ( rid )
if ( render_object and render_object.valid ) then
local ttl = render_object.time_to_live
2024-08-10 19:26:52 +02:00
if ( ( ttl > 0 ) and ( ttl < 200 ) ) then
2024-10-21 19:48:42 +02:00
local color = render_object.color
2024-08-10 19:26:52 +02:00
if ( color.a > 0.005 ) then
2024-10-21 19:48:42 +02:00
render_object.color = { r = color.r , g = color.g , b = color.b , a = color.a - 0.005 }
2024-08-10 19:26:52 +02:00
end
end
else
2024-10-20 02:48:20 +02:00
storage.oarc_renders_fadeout [ k ] = nil
2024-08-10 19:26:52 +02:00
end
end
end
end
--- Broadcast messages to all connected players
---@param msg LocalisedString
---@return nil
function SendBroadcastMsg ( msg )
2024-08-11 01:29:50 +02:00
for name , player in pairs ( game.connected_players ) do
2024-08-10 19:26:52 +02:00
player.print ( msg )
end
end
2024-08-11 06:19:30 +02:00
---Send a message to a player, safely checks if they exist and are online.
---@param playerName string
---@param msg LocalisedString
---@return nil
function SendMsg ( playerName , msg )
if ( ( game.players [ playerName ] ~= nil ) and ( game.players [ playerName ] . connected ) ) then
game.players [ playerName ] . print ( msg )
end
end
2024-08-10 19:26:52 +02:00
2024-08-23 20:16:11 +02:00
---Checks if a string starts with another string
---@param string string The string to check
---@param start string The starting string to look for
2024-09-10 17:40:19 +02:00
function StringStartsWith ( string , start )
2024-08-23 20:16:11 +02:00
return string : sub ( 1 , # start ) == start
end
2024-10-20 02:48:20 +02:00
---Checks if a surface is blacklisted based on the storage.ocfg settings
2024-09-12 03:20:00 +02:00
---@param surface_name string
---@return boolean --true if blacklisted
function IsSurfaceBlacklisted ( surface_name )
2024-10-22 05:11:20 +02:00
if ( storage.ocfg . surfaces_blacklist ~= nil ) then
2024-10-20 02:48:20 +02:00
for _ , name in pairs ( storage.ocfg . surfaces_blacklist ) do
2024-09-12 03:20:00 +02:00
if ( name == surface_name ) then
return true
end
end
end
2024-10-22 05:11:20 +02:00
if ( storage.ocfg . surfaces_blacklist_match ~= nil ) then
2024-10-20 02:48:20 +02:00
for _ , match in pairs ( storage.ocfg . surfaces_blacklist_match ) do
2024-09-12 03:20:00 +02:00
if ( StringStartsWith ( surface_name , match ) ) then
return true
end
end
end
return false
end
2024-08-10 19:26:52 +02:00
-- -- Simple way to write to a file. Always appends. Only server.
-- -- Has a global setting for enable/disable
-- function ServerWriteFile(filename, msg)
2024-10-20 02:48:20 +02:00
-- if (storage.ocfg.enable_server_write_files) then
2024-08-10 19:26:52 +02:00
-- game.write_file(filename, msg, true, 0)
-- end
-- end
2024-08-11 06:19:30 +02:00
---Useful for displaying game time in mins:secs format
---@param ticks number
---@return string
function FormatTime ( ticks )
local seconds = ticks / 60
local minutes = math.floor ( ( seconds ) / 60 )
local seconds = math.floor ( seconds - 60 * minutes )
return string.format ( " %dm:%02ds " , minutes , seconds )
end
2024-08-10 19:26:52 +02:00
2024-08-11 06:19:30 +02:00
---Useful for displaying game time in hrs:mins format
---@param ticks number
---@return string
function FormatTimeHoursSecs ( ticks )
local seconds = ticks / 60
2024-10-04 19:27:42 +02:00
local total_minutes = math.floor ( ( seconds ) / 60 )
local hours = math.floor ( ( total_minutes ) / 60 )
local minutes = math.floor ( total_minutes - 60 * hours )
2024-08-11 06:19:30 +02:00
return string.format ( " %dh:%02dm " , hours , minutes )
end
2024-08-10 19:26:52 +02:00
2024-10-04 19:27:42 +02:00
2024-08-10 19:26:52 +02:00
-- -- Simple math clamp
-- function clamp(val, min, max)
-- if (val > max) then
-- return max
-- elseif (val < min) then
-- return min
-- end
-- return val
-- end
-- function clampInt32(val)
-- return clamp(val, MAX_INT32_NEG, MAX_INT32_POS)
-- end
-- function MathRound(num)
-- return math.floor(num+0.5)
-- end
-- Fisher-Yares shuffle
-- https://stackoverflow.com/questions/35572435/how-do-you-do-the-fisher-yates-shuffle-in-lua
---@param T table
---@return table
function FYShuffle ( T )
local tReturn = { }
for i = # T , 1 , - 1 do
local j = math.random ( i )
T [ i ] , T [ j ] = T [ j ] , T [ i ]
table.insert ( tReturn , T [ i ] )
end
return tReturn
end
2024-09-07 03:10:29 +02:00
---Check if a table contains a value
---@param table table
---@param val any
---@return boolean
function TableContains ( table , val )
for _ , value in pairs ( table ) do
if value == val then
return true
end
end
return false
end
2024-09-12 03:20:00 +02:00
---Get a key from a table given a value (if it exists)
2024-09-07 03:10:29 +02:00
---@param table table
---@param val any
2024-09-12 03:20:00 +02:00
---@return any
function GetTableKey ( table , val )
for k , v in pairs ( table ) do
if v == val then
return k
end
end
return nil
end
-- ---Remove a value from a table
-- ---@param table table
-- ---@param val any
-- ---@return nil
-- function TableRemove(t, val)
-- for i = #t, 1, -1 do
-- if t[i] == val then
-- table.remove(t, i)
-- end
-- end
-- end
function TableRemoveOneUsingPairs ( t , val )
for k , v in pairs ( t ) do
if v == val then
table.remove ( t , k )
return
2024-09-07 03:10:29 +02:00
end
end
end
2024-10-29 15:44:20 +02:00
---Gets a random point within a circle of a given radius and center point.
---@param radius number
---@param center MapPosition
---@return MapPosition
function GetRandomPointWithinCircle ( radius , center )
local angle = math.random ( ) * 2 * math.pi
local distance = math.random ( ) * radius
local x = center.x + distance * math.cos ( angle )
local y = center.y + distance * math.sin ( angle )
return { x = x , y = y }
end
2024-08-10 19:26:52 +02:00
-- -- Get a random KEY from a table.
-- function GetRandomKeyFromTable(t)
-- local keyset = {}
-- for k,v in pairs(t) do
-- table.insert(keyset, k)
-- end
-- return keyset[math.random(#keyset)]
-- end
-- -- A safer way to attempt to get the next key in a table. CHECK TABLE SIZE BEFORE CALLING THIS!
-- -- Ensures the key points to a valid entry before calling next. Otherwise it restarts.
-- -- If you get nil as a return, it means you hit the return.
-- function NextButChecksKeyIsValidFirst(table_in, key)
-- -- if (table_size(table_in) == 0) then you're fucked end
-- if ((not key) or (not table_in[key])) then
-- return next(table_in, nil)
-- else
-- return next(table_in, key)
-- end
-- end
-- -- Gets the next key, even if we have to start again.
-- function NextKeyInTableIncludingRestart(table_in, key)
-- local next_key = NextButChecksKeyIsValidFirst(table_in, key)
-- if (not next_key) then
-- return NextButChecksKeyIsValidFirst(table_in, next_key)
-- else
-- return next_key
-- end
-- end
-- function GetRandomValueFromTable(t)
-- return t[GetRandomKeyFromTable(t)]
-- end
-- -- Given a table of positions, returns key for closest to given pos.
-- function GetClosestPosFromTable(pos, pos_table)
-- local closest_dist = nil
-- local closest_key = nil
-- for k,p in pairs(pos_table) do
-- local new_dist = util.distance(pos, p)
-- if (closest_dist == nil) then
-- closest_dist = new_dist
-- closest_key = k
-- elseif (closest_dist > new_dist) then
-- closest_dist = new_dist
-- closest_key = k
-- end
-- end
-- if (closest_key == nil) then
-- log("GetClosestPosFromTable ERROR - None found?")
-- return nil
-- end
-- return pos_table[closest_key]
-- end
-- Chart area for a force
---@param force string|integer|LuaForce
---@param position MapPosition
---@param chunkDist number
2024-08-23 20:16:11 +02:00
---@param surface LuaSurface|string|integer
2024-08-10 19:26:52 +02:00
function ChartArea ( force , position , chunkDist , surface )
force.chart ( surface ,
2024-08-11 01:29:50 +02:00
{ { position.x - ( CHUNK_SIZE * chunkDist ) ,
position.y - ( CHUNK_SIZE * chunkDist ) } ,
{ position.x + ( CHUNK_SIZE * chunkDist ) ,
position.y + ( CHUNK_SIZE * chunkDist ) } } )
2024-08-10 19:26:52 +02:00
end
2024-10-13 04:31:19 +02:00
--- Better than util.insert_safe because we also check for 0 count items.
---@param entity LuaEntity|LuaPlayer
---@param item_dict table
---@return nil
function OarcsSaferInsert ( entity , item_dict )
if not ( entity and entity.valid and item_dict ) then return end
2024-10-21 19:48:42 +02:00
local items = prototypes.item
2024-10-13 04:31:19 +02:00
local insert = entity.insert
for name , count in pairs ( item_dict ) do
if items [ name ] and count > 0 then
insert { name = name , count = count }
else
log ( " Item to insert not valid: " .. name )
end
end
end
--- Better than util.remove_safe because we also check for 0 count items.
---@param entity LuaEntity|LuaPlayer
---@param item_dict table
---@return nil
function OarcsSaferRemove ( entity , item_dict )
if not ( entity and entity.valid and item_dict ) then return end
2024-10-21 19:48:42 +02:00
local items = prototypes.item
2024-10-13 04:31:19 +02:00
local remove = entity.remove_item
for name , count in pairs ( item_dict ) do
if items [ name ] and count > 0 then
remove { name = name , count = count }
else
log ( " Item to remove not valid: " .. name )
end
end
end
2024-08-10 19:26:52 +02:00
---Gives the player the respawn items if there are any
---@param player LuaPlayer
---@return nil
function GivePlayerRespawnItems ( player )
2024-10-28 01:50:52 +02:00
local surface_name = player.character . surface.name
2024-10-20 02:48:20 +02:00
if ( storage.ocfg . surfaces_config [ surface_name ] == nil ) then
2024-09-13 02:58:19 +02:00
error ( " GivePlayerRespawnItems - Missing surface config! " .. surface_name )
2024-08-16 07:40:16 +02:00
return
end
2024-10-20 02:48:20 +02:00
local respawnItems = storage.ocfg . surfaces_config [ surface_name ] . starting_items.player_respawn_items
2024-08-16 07:40:16 +02:00
2024-10-13 04:31:19 +02:00
OarcsSaferInsert ( player , respawnItems )
2024-08-10 19:26:52 +02:00
end
---Gives the player the starter items if there are any
---@param player LuaPlayer
---@return nil
function GivePlayerStarterItems ( player )
2024-10-28 01:50:52 +02:00
local surface_name = player.character . surface.name
2024-10-20 02:48:20 +02:00
if ( storage.ocfg . surfaces_config [ surface_name ] == nil ) then
2024-09-13 02:58:19 +02:00
error ( " GivePlayerStarterItems - Missing surface config! " .. surface_name )
2024-08-16 07:40:16 +02:00
return
end
2024-10-20 02:48:20 +02:00
local startItems = storage.ocfg . surfaces_config [ surface_name ] . starting_items.player_start_items
2024-09-06 03:15:56 +02:00
2024-10-13 04:31:19 +02:00
OarcsSaferInsert ( player , startItems )
2024-09-06 03:15:56 +02:00
end
2024-08-16 07:40:16 +02:00
2024-09-13 02:58:19 +02:00
---Half-heartedly attempts to remove starter items from the player. Probably more trouble than it's worth.
2024-09-06 03:15:56 +02:00
---@param player LuaPlayer
---@return nil
2024-09-13 02:58:19 +02:00
function RemovePlayerStarterItems ( player )
2024-10-30 01:48:38 +02:00
if player == nil or player.character == nil then return end
2024-10-28 01:50:52 +02:00
local surface_name = player.character . surface.name
2024-10-20 02:48:20 +02:00
if ( storage.ocfg . surfaces_config [ surface_name ] ) ~= nil then
local startItems = storage.ocfg . surfaces_config [ surface_name ] . starting_items.player_start_items
2024-10-13 04:31:19 +02:00
OarcsSaferRemove ( player , startItems )
2024-08-10 19:26:52 +02:00
end
end
2024-08-13 02:22:11 +02:00
--- Delete all chunks on a surface
--- @param surface LuaSurface
--- @return nil
function DeleteAllChunks ( surface )
for chunk in surface.get_chunks ( ) do
surface.delete_chunk ( { chunk.x , chunk.y } )
end
end
2024-11-08 05:27:12 +02:00
---Get position for buddy spawn (for buddy placement)
---@param position MapPosition
---@param surface_name string
---@param moat_enabled boolean
---@return MapPosition
function GetBuddySpawnPosition ( position , surface_name , moat_enabled )
local spawn_config = storage.ocfg . surfaces_config [ surface_name ] . spawn_config
local x_offset = storage.ocfg . spawn_general.spawn_radius_tiles * spawn_config.radius_modifier * 2
x_offset = x_offset + storage.ocfg . spawn_general.moat_width_tiles
-- distance = distance + 5 -- EXTRA BUFFER?
-- Create that spawn in the global vars
local buddy_position = table.deepcopy ( position )
-- The x_offset must be big enough to ensure the spawns DO NOT overlap!
buddy_position.x = buddy_position.x + x_offset
return buddy_position
end
2024-08-10 19:26:52 +02:00
-- -- Modular armor quick start
-- function GiveQuickStartModularArmor(player)
-- player.insert{name="modular-armor", count = 1}
-- if player and player.get_inventory(defines.inventory.character_armor) ~= nil and player.get_inventory(defines.inventory.character_armor)[1] ~= nil then
-- local p_armor = player.get_inventory(defines.inventory.character_armor)[1].grid
-- if p_armor ~= nil then
-- p_armor.put({name = "personal-roboport-equipment"})
-- p_armor.put({name = "battery-mk2-equipment"})
-- p_armor.put({name = "personal-roboport-equipment"})
-- for i=1,15 do
-- p_armor.put({name = "solar-panel-equipment"})
-- end
-- end
-- player.insert{name="construction-robot", count = 40}
-- end
-- end
-- -- Cheater's quick start
-- function GiveQuickStartPowerArmor(player)
-- player.insert{name="power-armor", count = 1}
-- if player and player.get_inventory(defines.inventory.character_armor) ~= nil and player.get_inventory(defines.inventory.character_armor)[1] ~= nil then
-- local p_armor = player.get_inventory(defines.inventory.character_armor)[1].grid
-- if p_armor ~= nil then
-- p_armor.put({name = "fusion-reactor-equipment"})
-- p_armor.put({name = "exoskeleton-equipment"})
-- p_armor.put({name = "battery-mk2-equipment"})
-- p_armor.put({name = "battery-mk2-equipment"})
-- p_armor.put({name = "personal-roboport-mk2-equipment"})
-- p_armor.put({name = "personal-roboport-mk2-equipment"})
-- p_armor.put({name = "personal-roboport-mk2-equipment"})
-- p_armor.put({name = "battery-mk2-equipment"})
-- for i=1,7 do
-- p_armor.put({name = "solar-panel-equipment"})
-- end
-- end
-- player.insert{name="construction-robot", count = 100}
-- player.insert{name="belt-immunity-equipment", count = 1}
-- end
-- end
-- TEST_KIT = {
-- {name="infinity-chest", count = 50},
-- {name="infinity-pipe", count = 50},
-- {name="electric-energy-interface", count = 50},
-- {name="express-loader", count = 50},
-- {name="express-transport-belt", count = 50},
-- }
-- function GiveTestKit(player)
-- for _,item in pairs(TEST_KIT) do
-- player.insert(item)
-- end
-- end
-- Safer teleport
---@param player LuaPlayer
2024-08-11 06:19:30 +02:00
---@param surface LuaSurface
2024-08-10 19:26:52 +02:00
---@param target_pos MapPosition
function SafeTeleport ( player , surface , target_pos )
2024-10-28 00:04:55 +02:00
local safe_pos = surface.find_non_colliding_position ( " character " , target_pos , CHUNK_SIZE , 1 )
2024-08-10 19:26:52 +02:00
if ( not safe_pos ) then
2024-10-28 00:04:55 +02:00
player.teleport ( target_pos , surface , true )
2024-08-10 19:26:52 +02:00
else
2024-10-28 00:04:55 +02:00
player.teleport ( safe_pos , surface , true )
2024-08-10 19:26:52 +02:00
end
end
---Check if given position is in area bounding box
---@param point MapPosition
---@param area BoundingBox
---@return boolean
function CheckIfInArea ( point , area )
if ( ( point.x >= area.left_top . x ) and ( point.x < area.right_bottom . x ) ) then
if ( ( point.y >= area.left_top . y ) and ( point.y < area.right_bottom . y ) ) then
return true
end
end
return false
end
2024-09-17 20:19:41 +02:00
---Configures the friend and cease fire relationships between all player forces.
---@param cease_fire boolean
---@param friends boolean
2024-08-11 01:29:50 +02:00
---@return nil
2024-09-17 20:19:41 +02:00
function ConfigurePlayerForceRelationships ( cease_fire , friends )
local player_forces = { }
for name , force in pairs ( game.forces ) do
2024-10-18 03:55:10 +02:00
if name ~= ABANDONED_FORCE_NAME and not TableContains ( ENEMY_FORCES_NAMES_INCL_NEUTRAL , name ) then
2024-09-17 20:19:41 +02:00
table.insert ( player_forces , force )
2024-08-11 01:29:50 +02:00
end
end
2024-08-10 19:26:52 +02:00
2024-09-17 20:19:41 +02:00
for _ , force1 in pairs ( player_forces ) do
for _ , force2 in pairs ( player_forces ) do
if force1.name ~= force2.name then
force1.set_cease_fire ( force2 , cease_fire )
force1.set_friend ( force2 , friends )
force2.set_cease_fire ( force1 , cease_fire )
force2.set_friend ( force1 , friends )
2024-08-11 01:29:50 +02:00
end
end
end
end
2024-08-10 19:26:52 +02:00
2024-09-17 20:19:41 +02:00
-- ---Set all forces to ceasefire
-- ---@return nil
-- function SetCeaseFireBetweenAllPlayerForces()
-- for name, team in pairs(game.forces) do
-- if name ~= "neutral" and name ~= ABANDONED_FORCE_NAME and not TableContains(ENEMY_FORCES_NAMES, name) then
-- for x, _ in pairs(game.forces) do
-- if x ~= "neutral" and x ~= ABANDONED_FORCE_NAME and not TableContains(ENEMY_FORCES_NAMES, x) then
-- team.set_cease_fire(x, true)
-- end
-- end
-- end
-- end
-- end
-- ---Set all forces to friendly
-- ---@return nil
-- function SetFriendlyBetweenAllPlayerForces()
-- for name, team in pairs(game.forces) do
-- if name ~= "neutral" and name ~= ABANDONED_FORCE_NAME and not TableContains(ENEMY_FORCES_NAMES, name) then
-- for x, _ in pairs(game.forces) do
-- if x ~= "neutral" and x ~= ABANDONED_FORCE_NAME and not TableContains(ENEMY_FORCES_NAMES, x) then
-- team.set_friend(x, true)
-- end
-- end
-- end
-- end
-- end
2024-08-25 03:06:43 +02:00
---For each other player force, share a chat msg.
---@param player LuaPlayer
---@param msg LocalisedString
---@return nil
function ShareChatBetweenForces ( player , msg )
for _ , force in pairs ( game.forces ) do
if ( force ~= nil ) then
if ( ( force.name ~= " enemy " ) and
( force.name ~= " neutral " ) and
( force.name ~= " player " ) and
( force ~= player.force ) ) then
force.print ( player.name .. " : " .. msg )
end
end
end
end
2024-08-10 19:26:52 +02:00
-- -- Merges force2 INTO force1 but keeps all research between both forces.
-- function MergeForcesKeepResearch(force1, force2)
-- for techName,luaTech in pairs(force2.technologies) do
-- if (luaTech.researched) then
-- force1.technologies[techName].researched = true
-- force1.technologies[techName].level = luaTech.level
-- end
-- end
-- game.merge_forces(force2, force1)
-- end
-- -- Undecorator
-- function RemoveDecorationsArea(surface, area)
-- surface.destroy_decoratives{area=area}
-- end
-- -- Remove fish
-- function RemoveFish(surface, area)
-- for _, entity in pairs(surface.find_entities_filtered{area = area, type="fish"}) do
-- entity.destroy()
-- end
-- end
-- -- Render a path
-- function RenderPath(path, ttl, players)
-- local last_pos = path[1].position
-- local color = {r = 1, g = 0, b = 0, a = 0.5}
-- for i,v in pairs(path) do
-- if (i ~= 1) then
-- color={r = 1/(1+(i%3)), g = 1/(1+(i%5)), b = 1/(1+(i%7)), a = 0.5}
-- rendering.draw_line{color=color,
-- width=2,
-- from=v.position,
-- to=last_pos,
-- surface=game.surfaces[GAME_SURFACE_NAME],
-- players=players,
-- time_to_live=ttl}
-- end
-- last_pos = v.position
-- end
-- end
2024-08-11 01:29:50 +02:00
---Get a random 1 or -1
---@return number
function RandomNegPos ( )
if ( math.random ( 0 , 1 ) == 1 ) then
return 1
else
return - 1
end
end
2024-08-10 19:26:52 +02:00
2024-09-15 19:50:07 +02:00
---Create a random direction vector to look in, returns normalized vector
2024-08-11 01:29:50 +02:00
---@return MapPosition
function GetRandomVector ( )
local randVec = { x = 0 , y = 0 }
while ( ( randVec.x == 0 ) and ( randVec.y == 0 ) ) do
2024-09-15 19:50:07 +02:00
randVec.x = math.random ( ) * 2 - 1
randVec.y = math.random ( ) * 2 - 1
2024-08-11 01:29:50 +02:00
end
2024-09-15 19:50:07 +02:00
-- Normalize the vector
local magnitude = math.sqrt ( ( randVec.x ^ 2 ) + ( randVec.y ^ 2 ) )
randVec.x = randVec.x / magnitude
randVec.y = randVec.y / magnitude
2024-08-11 01:29:50 +02:00
return randVec
end
2024-08-10 19:26:52 +02:00
2024-08-11 01:29:50 +02:00
---Check for ungenerated chunks around a specific chunk +/- chunkDist in x and y directions
---@param chunkPos MapPosition
---@param chunkDist integer
---@param surface LuaSurface
---@return boolean
function IsChunkAreaUngenerated ( chunkPos , chunkDist , surface )
for x =- chunkDist , chunkDist do
for y =- chunkDist , chunkDist do
local checkPos = { x = chunkPos.x + x ,
y = chunkPos.y + y }
if ( surface.is_chunk_generated ( checkPos ) ) then
return false
end
end
end
return true
end
2024-08-10 19:26:52 +02:00
-- Clear out enemies around an area with a certain distance
---@param pos MapPosition
---@param safeDist number
---@param surface LuaSurface
function ClearNearbyEnemies ( pos , safeDist , surface )
2024-08-11 01:29:50 +02:00
local safeArea = {
left_top =
{
x = pos.x - safeDist ,
y = pos.y - safeDist
} ,
right_bottom =
{
x = pos.x + safeDist ,
y = pos.y + safeDist
}
}
for _ , entity in pairs ( surface.find_entities_filtered { area = safeArea , force = " enemy " } ) do
2024-08-10 19:26:52 +02:00
entity.destroy ( )
end
end
2024-09-15 19:50:07 +02:00
-- ---Function to find coordinates of ungenerated map area in a given direction starting from the center of the map
-- ---@param direction_vector MapPosition
-- ---@param surface LuaSurface
-- ---@return MapPosition
-- function FindMapEdge(direction_vector, surface)
-- local position = {x=0,y=0}
-- local chunk_position = {x=0,y=0}
-- -- Keep checking chunks in the direction of the vector
-- while(true) do
-- -- Set some absolute limits.
-- if ((math.abs(chunk_position.x) > 1000) or (math.abs(chunk_position.y) > 1000)) then
-- break
-- -- If chunk is already generated, keep looking
-- elseif (surface.is_chunk_generated(chunk_position)) then
-- chunk_position.x = chunk_position.x + direction_vector.x
-- chunk_position.y = chunk_position.y + direction_vector.y
-- -- Found a possible ungenerated area
-- else
-- chunk_position.x = chunk_position.x + direction_vector.x
-- chunk_position.y = chunk_position.y + direction_vector.y
-- -- Check there are no generated chunks in a 10x10 area.
-- if IsChunkAreaUngenerated(chunk_position, 10, surface) then
-- position.x = (chunk_position.x*CHUNK_SIZE) + (CHUNK_SIZE/2)
-- position.y = (chunk_position.y*CHUNK_SIZE) + (CHUNK_SIZE/2)
-- break
-- end
-- end
-- end
2024-08-10 19:26:52 +02:00
2024-09-15 19:50:07 +02:00
-- -- log("spawn: x=" .. position.x .. ", y=" .. position.y)
-- return position
-- end
2024-08-10 19:26:52 +02:00
2024-09-05 04:21:43 +02:00
---Pick a random direction, go at least the minimum distance, and start looking for ungenerated chunks
2024-09-15 19:50:07 +02:00
---We try a few times (hardcoded) and then try a different random direction if we fail (up to max_tries)
2024-11-09 06:00:12 +02:00
---@param surface_name string Surface name because we might need to force the creation of a new surface
2024-09-15 19:50:07 +02:00
---@param minimum_distance_chunks number Distance in chunks to start looking for ungenerated chunks
---@param max_tries integer Maximum number of tries to find a spawn point
2024-08-11 01:29:50 +02:00
---@return MapPosition
2024-11-09 06:00:12 +02:00
function FindUngeneratedCoordinates ( surface_name , minimum_distance_chunks , max_tries )
local final_position = { x = 0 , y = 0 }
-- If surface is nil, it is probably a planet? Check and create if needed.
local surface = game.surfaces [ surface_name ]
if ( surface == nil ) then
if ( game.planets [ surface_name ] == nil ) then
error ( " ERROR! No surface or planet found for requested player spawn! " )
return final_position
end
surface = game.planets [ surface_name ] . create_surface ( )
if ( surface == nil ) then
error ( " ERROR! Failed to create planet surface for player spawn! " )
return final_position
end
end
2024-09-05 04:21:43 +02:00
--- Get a random vector, figure out how many times to multiply it to get the minimum distance
local direction_vector = GetRandomVector ( )
2024-09-15 19:50:07 +02:00
local start_distance_tiles = minimum_distance_chunks * CHUNK_SIZE
2024-09-05 04:21:43 +02:00
2024-09-15 19:50:07 +02:00
local tries_remaining = max_tries - 1
-- Starting search position
2024-09-05 04:21:43 +02:00
local search_pos = {
2024-09-15 19:50:07 +02:00
x = direction_vector.x * start_distance_tiles ,
y = direction_vector.y * start_distance_tiles
2024-09-05 04:21:43 +02:00
}
2024-08-11 01:29:50 +02:00
2024-09-15 19:50:07 +02:00
-- We check up to THIS many times, each jump moves out by minimum_distance_to_existing_chunks
local jumps_count = 3
2024-10-20 02:48:20 +02:00
local minimum_distance_to_existing_chunks = storage.ocfg . gameplay.minimum_distance_to_existing_chunks
2024-08-11 01:29:50 +02:00
2024-09-05 04:21:43 +02:00
-- Keep checking chunks in the direction of the vector, assumes this terminates...
2024-08-11 01:29:50 +02:00
while ( true ) do
2024-09-15 19:50:07 +02:00
local chunk_position = GetChunkPosFromTilePos ( search_pos )
if ( jumps_count <= 0 ) then
if ( tries_remaining > 0 ) then
2024-11-09 06:00:12 +02:00
return FindUngeneratedCoordinates ( surface_name , minimum_distance_chunks , tries_remaining )
2024-09-15 19:50:07 +02:00
else
log ( " WARNING - FindUngeneratedCoordinates - Hit max distance! " )
break
end
-- If chunk is already generated, keep looking further out
elseif ( surface.is_chunk_generated ( chunk_position ) ) then
2024-08-11 01:29:50 +02:00
2024-09-15 19:50:07 +02:00
-- For debugging, ping the map
-- SendBroadcastMsg("GENERATED: " .. GetGPStext(surface.name, {x=chunk_position.x*32, y=chunk_position.y*32}))
-- Move out a bit more to give some space and then check the surrounding area
search_pos.x = search_pos.x + ( direction_vector.x * CHUNK_SIZE * minimum_distance_to_existing_chunks )
search_pos.y = search_pos.y + ( direction_vector.y * CHUNK_SIZE * minimum_distance_to_existing_chunks )
2024-08-11 01:29:50 +02:00
2024-09-05 04:21:43 +02:00
-- Found a possible ungenerated area
2024-09-15 19:50:07 +02:00
elseif IsChunkAreaUngenerated ( chunk_position , minimum_distance_to_existing_chunks , surface ) then
-- For debugging, ping the map
-- SendBroadcastMsg("SUCCESS: " .. GetGPStext(surface.name, {x=chunk_position.x*32, y=chunk_position.y*32}))
-- Place the spawn in the center of a chunk
final_position.x = ( chunk_position.x * CHUNK_SIZE ) + ( CHUNK_SIZE / 2 )
final_position.y = ( chunk_position.y * CHUNK_SIZE ) + ( CHUNK_SIZE / 2 )
break
-- The area around the chunk is not clear, keep looking
2024-09-05 04:21:43 +02:00
else
2024-09-15 19:50:07 +02:00
-- For debugging, ping the map
-- SendBroadcastMsg("NOT CLEAR: " .. GetGPStext(surface.name, {x=chunk_position.x*32, y=chunk_position.y*32}))
2024-09-05 04:21:43 +02:00
2024-09-15 19:50:07 +02:00
-- Move out a bit more to give some space and then check the surrounding area
search_pos.x = search_pos.x + ( direction_vector.x * CHUNK_SIZE * minimum_distance_to_existing_chunks )
search_pos.y = search_pos.y + ( direction_vector.y * CHUNK_SIZE * minimum_distance_to_existing_chunks )
2024-08-11 01:29:50 +02:00
end
2024-09-15 19:50:07 +02:00
jumps_count = jumps_count - 1
end
if ( final_position.x == 0 and final_position.y == 0 ) then
log ( " WARNING! FindUngeneratedCoordinates - Failed to find a spawn point! " )
2024-08-11 01:29:50 +02:00
end
2024-08-10 19:26:52 +02:00
2024-09-05 04:21:43 +02:00
return final_position
2024-08-11 01:29:50 +02:00
end
2024-08-10 19:26:52 +02:00
-- -- General purpose function for removing a particular recipe
-- function RemoveRecipe(force, recipeName)
-- local recipes = force.recipes
-- if recipes[recipeName] then
-- recipes[recipeName].enabled = false
-- end
-- end
-- -- General purpose function for adding a particular recipe
-- function AddRecipe(force, recipeName)
-- local recipes = force.recipes
-- if recipes[recipeName] then
-- recipes[recipeName].enabled = true
-- end
-- end
-- -- General command for disabling a tech.
-- function DisableTech(force, techName)
-- if force.technologies[techName] then
-- force.technologies[techName].enabled = false
-- force.technologies[techName].visible_when_disabled = true
-- end
-- end
-- -- General command for enabling a tech.
-- function EnableTech(force, techName)
-- if force.technologies[techName] then
-- force.technologies[techName].enabled = true
-- end
-- end
---Get a square area given a position and distance. Square length = 2x distance
---@param pos MapPosition
---@param dist number
---@return BoundingBox
function GetAreaAroundPos ( pos , dist )
2024-08-11 01:29:50 +02:00
return {
left_top =
{
x = pos.x - dist ,
y = pos.y - dist
} ,
right_bottom =
{
x = pos.x + dist ,
y = pos.y + dist
}
}
2024-08-10 19:26:52 +02:00
end
2024-08-11 06:19:30 +02:00
---Gets chunk position of a tile.
---@param tile_pos TilePosition
---@return ChunkPosition
function GetChunkPosFromTilePos ( tile_pos )
return { x = math.floor ( tile_pos.x / 32 ) , y = math.floor ( tile_pos.y / 32 ) }
end
2024-08-10 19:26:52 +02:00
-- function GetCenterTilePosFromChunkPos(c_pos)
-- return {x=c_pos.x*32 + 16, y=c_pos.y*32 + 16}
-- end
-- -- Get the left_top
-- function GetChunkTopLeft(pos)
-- return {x=pos.x-(pos.x % 32), y=pos.y-(pos.y % 32)}
-- end
-- -- Get area given chunk
-- function GetAreaFromChunkPos(chunk_pos)
-- return {left_top={x=chunk_pos.x*32, y=chunk_pos.y*32},
-- right_bottom={x=chunk_pos.x*32+31, y=chunk_pos.y*32+31}}
-- end
2024-09-19 19:19:57 +02:00
-- Removes the entity type from the area given
2024-08-10 19:26:52 +02:00
-- function RemoveInArea(surface, area, type)
-- for key, entity in pairs(surface.find_entities_filtered{area=area, type= type}) do
-- if entity.valid and entity and entity.position then
-- entity.destroy()
-- end
-- end
-- end
---Removes the entity type from the area given. Only if it is within given distance from given position.
---@param surface LuaSurface
---@param area BoundingBox
2024-09-19 19:19:57 +02:00
---@param type string|string[]
2024-08-10 19:26:52 +02:00
---@param pos MapPosition
---@param dist number
---@return nil
function RemoveInCircle ( surface , area , type , pos , dist )
2024-09-19 19:19:57 +02:00
for _ , entity in pairs ( surface.find_entities_filtered { area = area , type = type } ) do
2024-08-10 19:26:52 +02:00
if entity.valid and entity and entity.position then
2024-08-11 01:29:50 +02:00
if ( ( pos.x - entity.position . x ) ^ 2 + ( pos.y - entity.position . y ) ^ 2 < dist ^ 2 ) then
2024-08-10 19:26:52 +02:00
entity.destroy ( )
end
end
end
end
2024-09-19 19:19:57 +02:00
---Removes the entity type from the area given. Only if it is within given distance from given position.
---@param surface LuaSurface
---@param area BoundingBox
---@param type string|string[]
---@param pos MapPosition
---@param dist number
---@return nil
function RemoveInSquare ( surface , area , type , pos , dist )
for _ , entity in pairs ( surface.find_entities_filtered { area = area , type = type } ) do
if entity.valid and entity and entity.position then
local max_distance = math.max ( math.abs ( pos.x - entity.position . x ) , math.abs ( pos.y - entity.position . y ) )
if ( max_distance < dist ) then
entity.destroy ( )
end
end
end
end
2024-08-10 19:26:52 +02:00
-- -- For easy local testing of map gen settings. Just set what you want and uncomment usage in CreateGameSurface!
-- function SurfaceSettingsHelper(settings)
-- settings.terrain_segmentation = 4
-- settings.water = 3
-- settings.starting_area = 0
-- local r_freq = 1.20
-- local r_rich = 5.00
-- local r_size = 0.18
-- settings.autoplace_controls["coal"].frequency = r_freq
-- settings.autoplace_controls["coal"].richness = r_rich
-- settings.autoplace_controls["coal"].size = r_size
-- settings.autoplace_controls["copper-ore"].frequency = r_freq
-- settings.autoplace_controls["copper-ore"].richness = r_rich
-- settings.autoplace_controls["copper-ore"].size = r_size
-- settings.autoplace_controls["crude-oil"].frequency = r_freq
-- settings.autoplace_controls["crude-oil"].richness = r_rich
-- settings.autoplace_controls["crude-oil"].size = r_size
-- settings.autoplace_controls["iron-ore"].frequency = r_freq
-- settings.autoplace_controls["iron-ore"].richness = r_rich
-- settings.autoplace_controls["iron-ore"].size = r_size
-- settings.autoplace_controls["stone"].frequency = r_freq
-- settings.autoplace_controls["stone"].richness = r_rich
-- settings.autoplace_controls["stone"].size = r_size
-- settings.autoplace_controls["uranium-ore"].frequency = r_freq*0.5
-- settings.autoplace_controls["uranium-ore"].richness = r_rich
-- settings.autoplace_controls["uranium-ore"].size = r_size
-- settings.autoplace_controls["enemy-base"].frequency = 0.80
-- settings.autoplace_controls["enemy-base"].richness = 0.70
-- settings.autoplace_controls["enemy-base"].size = 0.70
-- settings.autoplace_controls["trees"].frequency = 1.00
-- settings.autoplace_controls["trees"].richness = 1.00
-- settings.autoplace_controls["trees"].size = 1.00
-- settings.cliff_settings.cliff_elevation_0 = 3
-- settings.cliff_settings.cliff_elevation_interval = 200
-- settings.cliff_settings.richness = 3
-- settings.property_expression_names["control-setting:aux:bias"] = "0.00"
-- settings.property_expression_names["control-setting:aux:frequency:multiplier"] = "5.00"
-- settings.property_expression_names["control-setting:moisture:bias"] = "0.40"
-- settings.property_expression_names["control-setting:moisture:frequency:multiplier"] = "50"
-- return settings
-- end
-- -- Create another surface so that we can modify map settings and not have a screwy nauvis map.
-- function CreateGameSurface()
-- if (GAME_SURFACE_NAME ~= "nauvis") then
-- -- Get starting surface settings.
-- local nauvis_settings = game.surfaces["nauvis"].map_gen_settings
2024-10-20 02:48:20 +02:00
-- if storage.ocfg.enable_vanilla_spawns then
-- nauvis_settings.starting_points = CreateVanillaSpawns(storage.ocfg.vanilla_spawn_count, storage.ocfg.vanilla_spawn_spacing)
2024-08-10 19:26:52 +02:00
-- -- ENFORCE ISLAND MAP GEN
2024-10-20 02:48:20 +02:00
-- if (storage.ocfg.silo_islands) then
2024-08-10 19:26:52 +02:00
-- nauvis_settings.property_expression_names.elevation = "0_17-island"
-- end
-- end
-- -- Enable this to test things out easily.
-- -- nauvis_settings = SurfaceSettingsHelper(nauvis_settings)
-- -- Create new game surface
-- local s = game.create_surface(GAME_SURFACE_NAME, nauvis_settings)
-- end
-- -- Add surface and safe areas
2024-10-20 02:48:20 +02:00
-- if storage.ocfg.enable_regrowth then
2024-08-10 19:26:52 +02:00
-- RegrowthMarkAreaSafeGivenChunkPos({x=0,y=0}, 4, true)
-- end
-- end
-- function CreateTileArrow(surface, pos, type)
-- tiles = {}
-- if (type == "LEFT") then
-- table.insert(tiles, {name = "hazard-concrete-left", position = {pos.x, pos.y}})
-- table.insert(tiles, {name = "hazard-concrete-left", position = {pos.x+1, pos.y}})
-- table.insert(tiles, {name = "hazard-concrete-left", position = {pos.x+2, pos.y}})
-- table.insert(tiles, {name = "hazard-concrete-left", position = {pos.x+3, pos.y}})
-- table.insert(tiles, {name = "hazard-concrete-right", position = {pos.x, pos.y+1}})
-- table.insert(tiles, {name = "hazard-concrete-right", position = {pos.x+1, pos.y+1}})
-- table.insert(tiles, {name = "hazard-concrete-right", position = {pos.x+2, pos.y+1}})
-- table.insert(tiles, {name = "hazard-concrete-right", position = {pos.x+3, pos.y+1}})
-- elseif (type == "RIGHT") then
-- table.insert(tiles, {name = "hazard-concrete-right", position = {pos.x, pos.y}})
-- table.insert(tiles, {name = "hazard-concrete-right", position = {pos.x+1, pos.y}})
-- table.insert(tiles, {name = "hazard-concrete-right", position = {pos.x+2, pos.y}})
-- table.insert(tiles, {name = "hazard-concrete-right", position = {pos.x+3, pos.y}})
-- table.insert(tiles, {name = "hazard-concrete-left", position = {pos.x, pos.y+1}})
-- table.insert(tiles, {name = "hazard-concrete-left", position = {pos.x+1, pos.y+1}})
-- table.insert(tiles, {name = "hazard-concrete-left", position = {pos.x+2, pos.y+1}})
-- table.insert(tiles, {name = "hazard-concrete-left", position = {pos.x+3, pos.y+1}})
-- end
2024-08-11 01:29:50 +02:00
2024-08-10 19:26:52 +02:00
-- surface.set_tiles(tiles, true)
-- end
-- -- Allowed colors: red, green, blue, orange, yellow, pink, purple, black, brown, cyan, acid
-- function CreateFixedColorTileArea(surface, area, color)
-- tiles = {}
-- for i=area.left_top.x,area.right_bottom.x do
-- for j=area.left_top.y,area.right_bottom.y do
-- table.insert(tiles, {name = color.."-refined-concrete", position = {i,j}})
-- end
-- end
-- surface.set_tiles(tiles, true)
-- end
-- -- Find closest player-owned entity
-- function FindClosestPlayerOwnedEntity(player, name, radius)
-- local entities = player.surface.find_entities_filtered{position=player.position,
-- radius=radius,
-- name=name,
-- force=player.force}
-- if (not entities or (#entities == 0)) then return nil end
-- return player.surface.get_closest(player.position, entities)
-- end
-- -- Add Long Reach to Character
-- function GivePlayerLongReach(player)
-- player.character.character_build_distance_bonus = BUILD_DIST_BONUS
-- player.character.character_reach_distance_bonus = REACH_DIST_BONUS
-- -- player.character.character_resource_reach_distance_bonus = RESOURCE_DIST_BONUS
-- end
-- -- General purpose cover an area in tiles.
-- function CoverAreaInTiles(surface, area, tile_name)
-- tiles = {}
-- for x = area.left_top.x,area.left_top.x+31 do
-- for y = area.left_top.y,area.left_top.y+31 do
-- table.insert(tiles, {name = tile_name, position = {x=x, y=y}})
-- end
-- end
-- surface.set_tiles(tiles, true)
-- end
-- --------------------------------------------------------------------------------
-- -- Anti-griefing Stuff & Gravestone (My own version)
-- --------------------------------------------------------------------------------
-- function AntiGriefing(force)
-- force.zoom_to_world_deconstruction_planner_enabled=false
-- SetForceGhostTimeToLive(force)
-- end
-- function SetForceGhostTimeToLive(force)
2024-10-20 02:48:20 +02:00
-- if storage.ocfg.ghost_ttl ~= 0 then
-- force.ghost_time_to_live = storage.ocfg.ghost_ttl+1
2024-08-10 19:26:52 +02:00
-- end
-- end
-- function SetItemBlueprintTimeToLive(event)
-- local type = event.created_entity.type
-- if type == "entity-ghost" or type == "tile-ghost" then
2024-10-20 02:48:20 +02:00
-- if storage.ocfg.ghost_ttl ~= 0 then
-- event.created_entity.time_to_live = storage.ocfg.ghost_ttl
2024-08-10 19:26:52 +02:00
-- end
-- end
-- end
-- --------------------------------------------------------------------------------
-- -- Gravestone soft mod. With my own modifications/improvements.
-- --------------------------------------------------------------------------------
-- -- Return steel chest entity (or nil)
-- function DropEmptySteelChest(player)
-- local pos = player.surface.find_non_colliding_position("steel-chest", player.position, 15, 1)
-- if not pos then
-- return nil
-- end
-- local grave = player.surface.create_entity{name="steel-chest", position=pos, force="neutral"}
-- return grave
-- end
-- function DropGravestoneChests(player)
-- local grave
-- local count = 0
-- -- Make sure we save stuff we're holding in our hands.
-- player.clean_cursor()
-- -- Loop through a players different inventories
-- -- Put it all into a chest.
-- -- If the chest is full, create a new chest.
-- for i, id in ipairs{
-- defines.inventory.character_armor,
-- defines.inventory.character_main,
-- defines.inventory.character_guns,
-- defines.inventory.character_ammo,
-- defines.inventory.character_vehicle,
-- defines.inventory.character_trash} do
-- local inv = player.get_inventory(id)
-- -- No idea how inv can be nil sometimes...?
-- if (inv ~= nil) then
-- if ((#inv > 0) and not inv.is_empty()) then
-- for j = 1, #inv do
-- if inv[j].valid_for_read then
-- -- Create a chest when counter is reset
-- if (count == 0) then
-- grave = DropEmptySteelChest(player)
-- if (grave == nil) then
-- -- player.print("Not able to place a chest nearby! Some items lost!")
-- return
-- end
-- grave_inv = grave.get_inventory(defines.inventory.chest)
-- end
-- count = count + 1
-- -- Copy the item stack into a chest slot.
-- grave_inv[count].set_stack(inv[j])
-- -- Reset counter when chest is full
-- if (count == #grave_inv) then
-- count = 0
-- end
-- end
-- end
-- end
-- -- Clear the player inventory so we don't have duplicate items lying around.
-- inv.clear()
-- end
-- end
-- if (grave ~= nil) then
-- player.print("Successfully dropped your items into a chest! Go get them quick!")
-- end
-- end
-- -- Dump player items into a chest after the body expires.
-- function DropGravestoneChestFromCorpse(corpse)
-- if ((corpse == nil) or (corpse.character_corpse_player_index == nil)) then return end
-- local grave, grave_inv
-- local count = 0
-- local inv = corpse.get_inventory(defines.inventory.character_corpse)
-- -- No idea how inv can be nil sometimes...?
-- if (inv ~= nil) then
-- if ((#inv > 0) and not inv.is_empty()) then
-- for j = 1, #inv do
-- if inv[j].valid_for_read then
-- -- Create a chest when counter is reset
-- if (count == 0) then
-- grave = DropEmptySteelChest(corpse)
-- if (grave == nil) then
-- -- player.print("Not able to place a chest nearby! Some items lost!")
-- return
-- end
-- grave_inv = grave.get_inventory(defines.inventory.chest)
-- end
-- count = count + 1
-- -- Copy the item stack into a chest slot.
-- grave_inv[count].set_stack(inv[j])
-- -- Reset counter when chest is full
-- if (count == #grave_inv) then
-- count = 0
-- end
-- end
-- end
-- end
-- -- Clear the player inventory so we don't have duplicate items lying around.
-- -- inv.clear()
-- end
-- if (grave ~= nil) and (game.players[corpse.character_corpse_player_index] ~= nil)then
-- game.players[corpse.character_corpse_player_index].print("Your corpse got eaten by biters! They kindly dropped your items into a chest! Go get them quick!")
-- end
-- end
-- --------------------------------------------------------------------------------
-- -- Item/Inventory stuff (used in autofill)
-- --------------------------------------------------------------------------------
-- -- Transfer Items Between Inventory
-- -- Returns the number of items that were successfully transferred.
-- -- Returns -1 if item not available.
-- -- Returns -2 if can't place item into destInv (ERROR)
-- function TransferItems(srcInv, destEntity, itemStack)
-- -- Check if item is in srcInv
-- if (srcInv.get_item_count(itemStack.name) == 0) then
-- return -1
-- end
-- -- Check if can insert into destInv
-- if (not destEntity.can_insert(itemStack)) then
-- return -2
-- end
-- -- Insert items
-- local itemsRemoved = srcInv.remove(itemStack)
-- itemStack.count = itemsRemoved
-- return destEntity.insert(itemStack)
-- end
-- -- Attempts to transfer at least some of one type of item from an array of items.
-- -- Use this to try transferring several items in order
-- -- It returns once it successfully inserts at least some of one type.
-- function TransferItemMultipleTypes(srcInv, destEntity, itemNameArray, itemCount)
-- local ret = 0
-- for _,itemName in pairs(itemNameArray) do
-- ret = TransferItems(srcInv, destEntity, {name=itemName, count=itemCount})
-- if (ret > 0) then
-- return ret -- Return the value succesfully transferred
-- end
-- end
-- return ret -- Return the last error code
-- end
-- -- Autofills a turret with ammo
-- function AutofillTurret(player, turret)
-- local mainInv = player.get_main_inventory()
-- if (mainInv == nil) then return end
-- -- Attempt to transfer some ammo
-- local ret = TransferItemMultipleTypes(mainInv, turret, {"uranium-rounds-magazine", "piercing-rounds-magazine", "firearm-magazine"}, AUTOFILL_TURRET_AMMO_QUANTITY)
-- -- Check the result and print the right text to inform the user what happened.
-- if (ret > 0) then
-- -- Inserted ammo successfully
-- -- FlyingText("Inserted ammo x" .. ret, turret.position, my_color_red, player.surface)
-- elseif (ret == -1) then
-- FlyingText("Out of ammo!", turret.position, my_color_red, player.surface)
-- elseif (ret == -2) then
-- FlyingText("Autofill ERROR! - Report this bug!", turret.position, my_color_red, player.surface)
-- end
-- end
-- -- Autofills a vehicle with fuel, bullets and shells where applicable
-- function AutoFillVehicle(player, vehicle)
-- local mainInv = player.get_main_inventory()
-- if (mainInv == nil) then return end
-- -- Attempt to transfer some fuel
-- if ((vehicle.name == "car") or (vehicle.name == "tank") or (vehicle.name == "locomotive")) then
-- TransferItemMultipleTypes(mainInv, vehicle, {"nuclear-fuel", "rocket-fuel", "solid-fuel", "coal", "wood"}, 50)
-- end
-- -- Attempt to transfer some ammo
-- if ((vehicle.name == "car") or (vehicle.name == "tank")) then
-- TransferItemMultipleTypes(mainInv, vehicle, {"uranium-rounds-magazine", "piercing-rounds-magazine", "firearm-magazine"}, 100)
-- end
-- -- Attempt to transfer some tank shells
-- if (vehicle.name == "tank") then
-- TransferItemMultipleTypes(mainInv, vehicle, {"explosive-uranium-cannon-shell", "uranium-cannon-shell", "explosive-cannon-shell", "cannon-shell"}, 100)
-- end
-- end
-- --------------------------------------------------------------------------------
-- -- Resource patch and starting area generation
-- --------------------------------------------------------------------------------
2024-09-19 19:19:57 +02:00
---Circle spawn shape (handles land, trees and moat)
2024-08-10 19:26:52 +02:00
---@param surface LuaSurface
---@param centerPos MapPosition
---@param chunkArea BoundingBox
---@param tileRadius number
---@param fillTile string
2024-09-19 19:19:57 +02:00
---@param moat boolean
---@param bridge boolean
2024-08-10 19:26:52 +02:00
---@return nil
2024-09-19 19:19:57 +02:00
function CreateCropCircle ( surface , centerPos , chunkArea , tileRadius , fillTile , moat , bridge )
local tile_radius_sqr = tileRadius ^ 2
2024-10-20 02:48:20 +02:00
local moat_width = storage.ocfg . spawn_general.moat_width_tiles
2024-09-19 19:19:57 +02:00
local moat_radius_sqr = ( ( tileRadius + moat_width ) ^ 2 )
2024-10-20 02:48:20 +02:00
local tree_width = storage.ocfg . spawn_general.tree_width_tiles
2024-09-19 19:19:57 +02:00
local tree_radius_sqr_inner = ( ( tileRadius - 1 - tree_width ) ^ 2 ) -- 1 less to make sure trees are inside the spawn area
local tree_radius_sqr_outer = ( ( tileRadius - 1 ) ^ 2 )
2024-08-10 19:26:52 +02:00
2024-10-29 15:44:20 +02:00
local surface_config = storage.ocfg . surfaces_config [ surface.name ]
local liquid_tile = surface_config.spawn_config . liquid_tile
local fish_enabled = ( liquid_tile == " water " )
2024-08-10 19:26:52 +02:00
local dirtTiles = { }
2024-08-11 01:29:50 +02:00
for i = chunkArea.left_top . x , chunkArea.right_bottom . x , 1 do
for j = chunkArea.left_top . y , chunkArea.right_bottom . y , 1 do
2024-09-19 19:19:57 +02:00
-- This ( X^2 + Y^2 ) is used to calculate if something is inside a circle area.
-- We avoid using sqrt for performance reasons.
2024-08-11 01:29:50 +02:00
local distSqr = math.floor ( ( centerPos.x - i ) ^ 2 + ( centerPos.y - j ) ^ 2 )
2024-08-10 19:26:52 +02:00
2024-09-19 19:19:57 +02:00
-- Fill in all unexpected water (or force grass)
if ( distSqr <= tile_radius_sqr ) then
2024-10-21 19:48:42 +02:00
if ( surface.get_tile ( i , j ) . collides_with ( " water_tile " ) or
2024-10-20 02:48:20 +02:00
storage.ocfg . spawn_general.force_grass ) then
2024-08-11 01:29:50 +02:00
table.insert ( dirtTiles , { name = fillTile , position = { i , j } } )
2024-08-10 19:26:52 +02:00
end
end
2024-10-07 02:46:32 +02:00
-- -- Create a tree ring
-- if ((distSqr < tree_radius_sqr_outer) and (distSqr > tree_radius_sqr_inner)) then
-- surface.create_entity({ name = "tree-02", amount = 1, position = { i, j } })
-- end
2024-09-19 19:19:57 +02:00
-- Fill moat with water.
if ( moat ) then
if ( bridge and ( ( j == centerPos.y - 1 ) or ( j == centerPos.y ) or ( j == centerPos.y + 1 ) ) ) then
-- This will leave the tiles "as is" on the left and right of the spawn which has the effect of creating
-- land connections if the spawn is on or near land.
elseif ( ( distSqr < moat_radius_sqr ) and ( distSqr > tile_radius_sqr ) ) then
2024-10-29 15:44:20 +02:00
table.insert ( dirtTiles , { name = liquid_tile , position = { i , j } } )
2024-10-05 01:01:30 +02:00
--5% chance of fish in water
2024-10-29 15:44:20 +02:00
if fish_enabled and ( math.random ( 1 , 20 ) == 1 ) then
2024-10-05 01:01:30 +02:00
surface.create_entity ( { name = " fish " , position = { i + 0.5 , j + 0.5 } } )
end
2024-09-19 19:19:57 +02:00
end
end
2024-08-10 19:26:52 +02:00
end
end
surface.set_tiles ( dirtTiles )
2024-10-07 02:46:32 +02:00
--Create trees (needs to be done after setting tiles!)
2024-10-29 15:44:20 +02:00
local tree_entity = surface_config.spawn_config . tree_entity
if ( tree_entity == nil ) then return end
2024-10-07 02:46:32 +02:00
for i = chunkArea.left_top . x , chunkArea.right_bottom . x , 1 do
for j = chunkArea.left_top . y , chunkArea.right_bottom . y , 1 do
local distSqr = math.floor ( ( centerPos.x - i ) ^ 2 + ( centerPos.y - j ) ^ 2 )
if ( ( distSqr < tree_radius_sqr_outer ) and ( distSqr > tree_radius_sqr_inner ) ) then
2024-10-29 15:44:20 +02:00
local pos = surface.find_non_colliding_position ( tree_entity , { i , j } , 2 , 0.5 )
if ( pos ~= nil ) then
surface.create_entity ( { name = tree_entity , amount = 1 , position = pos } )
end
-- surface.create_entity({ name = "tree-02", amount = 1, position = { i, j } })
2024-10-07 02:46:32 +02:00
end
end
end
2024-08-10 19:26:52 +02:00
end
2024-09-19 19:19:57 +02:00
---` spawn shape (handles land, trees and moat) (Curtesy of jvmguy)
2024-08-10 19:26:52 +02:00
---@param surface LuaSurface
---@param centerPos MapPosition
---@param chunkArea BoundingBox
---@param tileRadius number
---@param fillTile string
2024-09-19 19:19:57 +02:00
---@param moat boolean
---@param bridge boolean
2024-08-10 19:26:52 +02:00
---@return nil
2024-09-19 19:19:57 +02:00
function CreateCropOctagon ( surface , centerPos , chunkArea , tileRadius , fillTile , moat , bridge )
2024-09-24 04:48:08 +02:00
2024-10-20 02:48:20 +02:00
local moat_width = storage.ocfg . spawn_general.moat_width_tiles
2024-09-19 19:19:57 +02:00
local moat_width_outer = tileRadius + moat_width
2024-10-20 02:48:20 +02:00
local tree_width = storage.ocfg . spawn_general.tree_width_tiles
2024-09-19 19:19:57 +02:00
local tree_distance_inner = tileRadius - tree_width
2024-11-08 05:27:12 +02:00
local surface_config = storage.ocfg . surfaces_config [ surface.name ]
2024-08-10 19:26:52 +02:00
local dirtTiles = { }
2024-08-11 01:29:50 +02:00
for i = chunkArea.left_top . x , chunkArea.right_bottom . x , 1 do
for j = chunkArea.left_top . y , chunkArea.right_bottom . y , 1 do
2024-09-19 19:19:57 +02:00
2024-08-10 19:26:52 +02:00
local distVar1 = math.floor ( math.max ( math.abs ( centerPos.x - i ) , math.abs ( centerPos.y - j ) ) )
local distVar2 = math.floor ( math.abs ( centerPos.x - i ) + math.abs ( centerPos.y - j ) )
2024-09-19 19:19:57 +02:00
local distVar = math.max ( distVar1 , distVar2 * 0.707 ) ;
2024-08-10 19:26:52 +02:00
2024-09-19 19:19:57 +02:00
-- Fill in all unexpected water (or force grass)
if ( distVar <= tileRadius ) then
2024-10-21 19:48:42 +02:00
if ( surface.get_tile ( i , j ) . collides_with ( " water_tile " ) or
2024-10-20 02:48:20 +02:00
storage.ocfg . spawn_general.force_grass ) then
2024-08-11 01:29:50 +02:00
table.insert ( dirtTiles , { name = fillTile , position = { i , j } } )
2024-08-10 19:26:52 +02:00
end
end
2024-10-07 02:46:32 +02:00
-- -- Create a tree ring
-- if ((distVar < tileRadius) and (distVar >= tree_distance_inner)) then
-- surface.create_entity({ name = "tree-01", amount = 1, position = { i, j } })
-- end
2024-09-19 19:19:57 +02:00
-- Fill moat with water
if ( moat ) then
if ( bridge and ( ( j == centerPos.y - 1 ) or ( j == centerPos.y ) or ( j == centerPos.y + 1 ) ) ) then
-- This will leave the tiles "as is" on the left and right of the spawn which has the effect of creating
-- land connections if the spawn is on or near land.
elseif ( ( distVar > tileRadius ) and ( distVar <= moat_width_outer ) ) then
table.insert ( dirtTiles , { name = " water " , position = { i , j } } )
2024-10-05 01:01:30 +02:00
--5% chance of fish in water
if ( math.random ( 1 , 20 ) == 1 ) then
surface.create_entity ( { name = " fish " , position = { i + 0.5 , j + 0.5 } } )
end
2024-09-19 19:19:57 +02:00
end
end
2024-08-10 19:26:52 +02:00
end
end
surface.set_tiles ( dirtTiles )
2024-10-07 02:46:32 +02:00
2024-11-08 05:27:12 +02:00
--Create trees (needs to be done after setting tiles!)
local tree_entity = surface_config.spawn_config . tree_entity
2024-10-29 15:44:20 +02:00
if ( tree_entity == nil ) then return end
2024-11-08 05:27:12 +02:00
2024-10-07 02:46:32 +02:00
--Create trees (needs to be done after setting tiles!)
for i = chunkArea.left_top . x , chunkArea.right_bottom . x , 1 do
for j = chunkArea.left_top . y , chunkArea.right_bottom . y , 1 do
local distVar1 = math.floor ( math.max ( math.abs ( centerPos.x - i ) , math.abs ( centerPos.y - j ) ) )
local distVar2 = math.floor ( math.abs ( centerPos.x - i ) + math.abs ( centerPos.y - j ) )
local distVar = math.max ( distVar1 , distVar2 * 0.707 ) ;
if ( ( distVar < tileRadius ) and ( distVar >= tree_distance_inner ) ) then
surface.create_entity ( { name = " tree-01 " , amount = 1 , position = { i , j } } )
end
end
end
2024-08-10 19:26:52 +02:00
end
2024-09-19 19:19:57 +02:00
---Square spawn shape (handles land, trees and moat)
---@param surface LuaSurface
---@param centerPos MapPosition
---@param chunkArea BoundingBox
---@param tileRadius number
---@param fillTile string
---@param moat boolean
---@param bridge boolean
---@return nil
function CreateCropSquare ( surface , centerPos , chunkArea , tileRadius , fillTile , moat , bridge )
2024-10-20 02:48:20 +02:00
local moat_width = storage.ocfg . spawn_general.moat_width_tiles
2024-09-19 19:19:57 +02:00
local moat_width_outer = tileRadius + moat_width
2024-10-20 02:48:20 +02:00
local tree_width = storage.ocfg . spawn_general.tree_width_tiles
2024-09-19 19:19:57 +02:00
local tree_distance_inner = tileRadius - tree_width
2024-11-08 05:27:12 +02:00
local surface_config = storage.ocfg . surfaces_config [ surface.name ]
2024-09-19 19:19:57 +02:00
local dirtTiles = { }
for i = chunkArea.left_top . x , chunkArea.right_bottom . x , 1 do
for j = chunkArea.left_top . y , chunkArea.right_bottom . y , 1 do
-- Max distance from center (either x or y)
local max_distance = math.max ( math.abs ( centerPos.x - i ) , math.abs ( centerPos.y - j ) )
-- Fill in all unexpected water (or force grass)
if ( max_distance <= tileRadius ) then
2024-10-21 19:48:42 +02:00
if ( surface.get_tile ( i , j ) . collides_with ( " water_tile " ) or
2024-10-20 02:48:20 +02:00
storage.ocfg . spawn_general.force_grass ) then
2024-09-19 19:19:57 +02:00
table.insert ( dirtTiles , { name = fillTile , position = { i , j } } )
end
end
2024-10-07 02:46:32 +02:00
-- -- Create a tree ring
-- if ((max_distance < tileRadius) and (max_distance >= tree_distance_inner)) then
-- surface.create_entity({ name = "tree-02", amount = 1, position = { i, j } })
-- end
2024-09-19 19:19:57 +02:00
-- Fill moat with water
if ( moat ) then
if ( bridge and ( ( j == centerPos.y - 1 ) or ( j == centerPos.y ) or ( j == centerPos.y + 1 ) ) ) then
-- This will leave the tiles "as is" on the left and right of the spawn which has the effect of creating
-- land connections if the spawn is on or near land.
elseif ( ( max_distance > tileRadius ) and ( max_distance <= moat_width_outer ) ) then
table.insert ( dirtTiles , { name = " water " , position = { i , j } } )
2024-10-05 01:01:30 +02:00
--5% chance of fish in water
if ( math.random ( 1 , 20 ) == 1 ) then
surface.create_entity ( { name = " fish " , position = { i + 0.5 , j + 0.5 } } )
end
2024-09-19 19:19:57 +02:00
end
end
end
end
surface.set_tiles ( dirtTiles )
2024-10-07 02:46:32 +02:00
2024-11-08 05:27:12 +02:00
--Create trees (needs to be done after setting tiles!)
local tree_entity = surface_config.spawn_config . tree_entity
2024-10-29 15:44:20 +02:00
if ( tree_entity == nil ) then return end
2024-11-08 05:27:12 +02:00
2024-10-07 02:46:32 +02:00
--Create trees (needs to be done after setting tiles!)
for i = chunkArea.left_top . x , chunkArea.right_bottom . x , 1 do
for j = chunkArea.left_top . y , chunkArea.right_bottom . y , 1 do
local max_distance = math.max ( math.abs ( centerPos.x - i ) , math.abs ( centerPos.y - j ) )
if ( ( max_distance < tileRadius ) and ( max_distance >= tree_distance_inner ) ) then
surface.create_entity ( { name = " tree-02 " , amount = 1 , position = { i , j } } )
end
end
end
2024-09-19 19:19:57 +02:00
end
2024-08-10 19:26:52 +02:00
---Add a circle of water
---@param surface LuaSurface
---@param centerPos MapPosition
---@param chunkArea BoundingBox
---@param tileRadius number
---@param moatTile string
---@param bridge boolean
2024-09-19 19:19:57 +02:00
---@param shape SpawnShapeChoice
2024-08-10 19:26:52 +02:00
---@return nil
2024-09-19 19:19:57 +02:00
function CreateMoat ( surface , centerPos , chunkArea , tileRadius , moatTile , bridge , shape )
2024-08-11 01:29:50 +02:00
local tileRadSqr = tileRadius ^ 2
2024-08-10 19:26:52 +02:00
local tiles = { }
2024-08-11 01:29:50 +02:00
for i = chunkArea.left_top . x , chunkArea.right_bottom . x , 1 do
for j = chunkArea.left_top . y , chunkArea.right_bottom . y , 1 do
if ( bridge and ( ( j == centerPos.y - 1 ) or ( j == centerPos.y ) or ( j == centerPos.y + 1 ) ) ) then
2024-08-10 19:26:52 +02:00
-- This will leave the tiles "as is" on the left and right of the spawn which has the effect of creating
-- land connections if the spawn is on or near land.
else
-- This ( X^2 + Y^2 ) is used to calculate if something
-- is inside a circle area.
2024-08-11 01:29:50 +02:00
local distVar = math.floor ( ( centerPos.x - i ) ^ 2 + ( centerPos.y - j ) ^ 2 )
2024-08-10 19:26:52 +02:00
-- Create a circle of water
2024-10-20 02:48:20 +02:00
if ( ( distVar < tileRadSqr + ( 1500 * storage.ocfg . spawn_general.moat_width_tiles ) ) and
2024-08-11 01:29:50 +02:00
( distVar > tileRadSqr ) ) then
table.insert ( tiles , { name = moatTile , position = { i , j } } )
2024-08-10 19:26:52 +02:00
end
end
end
end
surface.set_tiles ( tiles )
end
2024-10-29 15:44:20 +02:00
-- Create a horizontal line of tiles (typically used for water)
2024-08-10 19:26:52 +02:00
---@param surface LuaSurface
---@param leftPos TilePosition
---@param length integer
2024-10-29 15:44:20 +02:00
---@param tile_name string
---@return nil
function CreateTileStrip ( surface , leftPos , length , tile_name )
2024-08-10 19:26:52 +02:00
local waterTiles = { }
2024-09-24 17:32:58 +02:00
for i = 0 , length - 1 , 1 do
2024-10-29 15:44:20 +02:00
table.insert ( waterTiles , { name = tile_name , position = { leftPos.x + i , leftPos.y } } )
2024-08-10 19:26:52 +02:00
end
surface.set_tiles ( waterTiles )
end
--- Function to generate a resource patch, of a certain size/amount at a pos.
---@param surface LuaSurface
---@param resourceName string
---@param diameter integer
---@param position TilePosition
---@param amount integer
function GenerateResourcePatch ( surface , resourceName , diameter , position , amount )
2024-08-11 01:29:50 +02:00
local midPoint = math.floor ( diameter / 2 )
2024-08-10 19:26:52 +02:00
if ( diameter == 0 ) then
return
end
2024-09-24 04:48:08 +02:00
-- Right now only 2 shapes are supported. Circle and Square.
2024-10-20 02:48:20 +02:00
local square_shape = ( storage.ocfg . spawn_general.resources_shape == RESOURCES_SHAPE_CHOICE_SQUARE )
2024-09-24 04:48:08 +02:00
2024-08-11 01:29:50 +02:00
for y = - midPoint , midPoint do
for x = - midPoint , midPoint do
2024-09-24 04:48:08 +02:00
-- Either it's a square, or it's a circle so we check if it's inside the circle.
if ( square_shape or ( ( x ) ^ 2 + ( y ) ^ 2 < midPoint ^ 2 ) ) then
2024-08-11 01:29:50 +02:00
surface.create_entity ( {
name = resourceName ,
amount = amount ,
position = { position.x + x , position.y + y }
} )
2024-08-10 19:26:52 +02:00
end
end
end
end
2024-10-29 15:44:20 +02:00
--- Function to generate a resource patch, of a certain size/amount at a pos.
---@param surface LuaSurface
---@param position MapPosition
---@return nil
function PlaceRandomEntities ( surface , position )
local spawn_config = storage.ocfg . surfaces_config [ surface.name ] . spawn_config
local random_entities = spawn_config.random_entities
if ( random_entities == nil ) then return end
local tree_width = storage.ocfg . spawn_general.tree_width_tiles
local radius = storage.ocfg . spawn_general.spawn_radius_tiles * spawn_config.radius_modifier - tree_width
--Iterate through the random entities and place them
for _ , entry in pairs ( random_entities ) do
local entity_name = entry.name
for i = 1 , entry.count do
local random_pos = GetRandomPointWithinCircle ( radius , position )
local open_pos = surface.find_non_colliding_position ( entity_name , random_pos , tree_width , 0.5 )
if ( open_pos ~= nil ) then
surface.create_entity ( {
name = entity_name ,
position = open_pos
} )
end
end
end
end
--- Randomly place lightning attractors specific for Fulgora. This should space them out so they don't overlap too much.
---@param surface LuaSurface
---@param position MapPosition
---@return nil
function PlaceFulgoranLightningAttractors ( surface , position , count )
local spawn_config = storage.ocfg . surfaces_config [ surface.name ] . spawn_config
local radius = storage.ocfg . spawn_general.spawn_radius_tiles * spawn_config.radius_modifier
-- HARDCODED FOR NOW
local ATTRACTOR_NAME = " fulgoran-ruin-attractor "
local ATTRACTOR_RADIUS = 20
--Iterate through and place them and use the largest available entity
for i = 1 , count do
local random_pos = GetRandomPointWithinCircle ( radius , position )
local open_pos = surface.find_non_colliding_position ( " crash-site-spaceship " , random_pos , 1 , 0.5 )
if ( open_pos ~= nil ) then
surface.create_entity ( {
name = ATTRACTOR_NAME ,
2024-11-04 20:57:36 +02:00
position = open_pos ,
force = " player " -- Same as native game
2024-10-29 15:44:20 +02:00
} )
end
end
end
2024-08-10 19:26:52 +02:00
-- --------------------------------------------------------------------------------
-- -- Holding pen for new players joining the map
-- --------------------------------------------------------------------------------
-- function CreateWall(surface, pos)
-- local wall = surface.create_entity({name="stone-wall", position=pos, force=MAIN_TEAM})
-- if wall then
-- wall.destructible = false
-- wall.minable = false
-- end
-- end
-- function CreateHoldingPen(surface, chunkArea)
2024-10-20 02:48:20 +02:00
-- local radiusTiles = storage.ocfg.spawn_config.general.spawn_radius_tiles-10
2024-08-10 19:26:52 +02:00
-- if (((chunkArea.left_top.x >= -(radiusTiles+2*CHUNK_SIZE)) and (chunkArea.left_top.x <= (radiusTiles+2*CHUNK_SIZE))) and
-- ((chunkArea.left_top.y >= -(radiusTiles+2*CHUNK_SIZE)) and (chunkArea.left_top.y <= (radiusTiles+2*CHUNK_SIZE)))) then
-- -- Remove stuff
-- RemoveAliensInArea(surface, chunkArea)
-- RemoveInArea(surface, chunkArea, "tree")
-- RemoveInArea(surface, chunkArea, "resource")
-- RemoveInArea(surface, chunkArea, "cliff")
-- CreateCropCircle(surface, {x=0,y=0}, chunkArea, radiusTiles, "landfill")
-- CreateMoat(surface, {x=0,y=0}, chunkArea, radiusTiles, "water", false)
-- CreateMoat(surface, {x=0,y=0}, chunkArea, radiusTiles+10, "out-of-map", false)
-- CreateMoat(surface, {x=0,y=0}, chunkArea, 2, "out-of-map", false)
-- end
-- end
-- --------------------------------------------------------------------------------
-- -- EVENT SPECIFIC FUNCTIONS
-- --------------------------------------------------------------------------------
-- -- Display messages to a user everytime they join
-- function PlayerJoinedMessages(event)
-- local player = game.players[event.player_index]
2024-10-20 02:48:20 +02:00
-- player.print(storage.ocfg.welcome_msg)
-- if (storage.oarc_announcements) then
-- player.print(storage.oarc_announcements)
2024-08-10 19:26:52 +02:00
-- end
-- end
-- -- Remove decor to save on file size
-- function UndecorateOnChunkGenerate(event)
-- local surface = event.surface
-- local chunkArea = event.area
-- RemoveDecorationsArea(surface, chunkArea)
-- -- If you care to, you can remove all fish with the Undecorator option here:
-- -- RemoveFish(surface, chunkArea)
-- end
-- -- Autofill softmod
-- function Autofill(event)
-- local player = game.players[event.player_index]
-- local eventEntity = event.created_entity
-- -- Make sure player isn't dead?
-- if (player.character == nil) then return end
-- if (eventEntity.name == "gun-turret") then
-- AutofillTurret(player, eventEntity)
-- end
-- if ((eventEntity.name == "car") or (eventEntity.name == "tank") or (eventEntity.name == "locomotive")) then
-- AutoFillVehicle(player, eventEntity)
-- end
-- end