2024-09-23 04:21:50 +02:00
-- Sep 2024
2024-11-21 20:41:39 +02:00
-- ____ _____ _____
2024-08-16 07:40:16 +02:00
-- / __ \ /\ | __ \ / ____|
2024-11-21 20:41:39 +02:00
-- | | | | / \ | |__) | |
-- | | | |/ /\ \ | _ /| |
-- | |__| / ____ \| | \ \| |____
2024-08-16 07:40:16 +02:00
-- \____/_/ \_\_| \_\\_____|
-- Oarc's Separated Spawn MOD V2
-- I decided to rewrite my old scenario due to the coming changes in Factorio V2.0 and its new Space Age Expansion.
-- Change Overview:
-- Support the scenario "as a mod" ONLY. Scenario merely provides a way to overwrite settings on_init.
-- Removed a lot of unnecessary feature bloat.
-- Move text to locale files where possible.
2024-09-23 04:21:50 +02:00
-- Major Features:
2024-08-16 07:40:16 +02:00
-- Core feature allows for a safe, separate spawn area for each player.
-- Players can choose to spawn with friends (buddy spawn) or join other bases.
-- Offline protection from enemy attacks.
-- Chunk cleanup to keep save file size down.
2024-09-23 04:21:50 +02:00
-- Sharing of electricity and items between players.
2024-08-16 07:40:16 +02:00
require ( " lib/oarc_utils " )
require ( " lib/config " )
require ( " lib/config_parser " )
require ( " lib/regrowth_map " )
require ( " lib/holding_pen " )
require ( " lib/separate_spawns " )
require ( " lib/separate_spawns_guis " )
require ( " lib/oarc_gui_tabs " )
2024-09-14 19:59:28 +02:00
require ( " lib/offline_protection " )
2024-09-17 20:19:41 +02:00
require ( " lib/scaled_enemies " )
2024-09-21 03:39:01 +02:00
require ( " lib/sharing " )
2024-08-16 07:40:16 +02:00
2024-09-23 18:59:22 +02:00
-- TODO: Possibly remove this later?
2024-09-05 19:18:47 +02:00
require ( " lib/oarc_tests " )
2024-11-11 19:00:10 +02:00
require ( " lib/oarc_commands " )
2024-08-16 07:40:16 +02:00
--------------------------------------------------------------------------------
-- On Init - Only runs once the first time the game starts
--------------------------------------------------------------------------------
script.on_init ( function ( event )
2024-11-01 19:24:32 +02:00
-- If init has already been run, we immediately hard error.
-- This is NOT supported. Checking for this force is just an easy dumb way to do it.
if ( game.forces [ ABANDONED_FORCE_NAME ] ~= nil ) then
error ( " It appears as though this mod is trying to re-run on_init. If you removed the mod and re-added it again, this is NOT supported. You have to rollback to a previous save where the mod was enabled. " )
return
end
2024-08-21 20:03:48 +02:00
ValidateAndLoadConfig ( )
RegrowthInit ( )
2024-08-16 07:40:16 +02:00
InitSpawnGlobalsAndForces ( )
CreateHoldingPenSurface ( ) -- Must be after init spawn globals?
-- Useful for debugging and if players choose not to use the provided empty scenario.
if remote.interfaces [ " freeplay " ] then
log ( " Freeplay interface detected. Disabling various freeplay features now! " )
remote.call ( " freeplay " , " set_skip_intro " , true )
remote.call ( " freeplay " , " set_disable_crashsite " , true )
remote.call ( " freeplay " , " set_created_items " , { } )
remote.call ( " freeplay " , " set_respawn_items " , { } )
end
2024-09-14 19:59:28 +02:00
-- If there are any players that already exist, init them now.
for _ , player in pairs ( game.players ) do
SeparateSpawnsInitPlayer ( player.index )
end
2024-11-09 06:00:12 +02:00
game.technology_notifications_enabled = false
2024-08-16 07:40:16 +02:00
end )
2024-10-13 15:31:47 +02:00
--------------------------------------------------------------------------------
-- On Load - Only for setting up some table stuff that shouldn't change during gameplay!
--------------------------------------------------------------------------------
script.on_load ( function ( )
SetupOCFGModKeys ( )
end )
2024-08-21 20:03:48 +02:00
--------------------------------------------------------------------------------
-- On Configuration Changed - Only runs when the mod configuration changes
--------------------------------------------------------------------------------
2024-10-04 19:27:42 +02:00
script.on_configuration_changed ( function ( data )
for _ , player in pairs ( game.players ) do
2024-11-09 06:00:12 +02:00
RecreateOarcGui ( player ) -- Reset the players GUI
2024-10-04 19:27:42 +02:00
end
end )
2024-08-21 20:03:48 +02:00
script.on_event ( defines.events . on_runtime_mod_setting_changed , function ( event )
2024-09-10 17:40:19 +02:00
if ( not StringStartsWith ( event.setting , " oarc-mod " ) ) then return end
2024-08-23 20:16:11 +02:00
RuntimeModSettingChanged ( event )
2024-08-21 20:03:48 +02:00
end )
2024-08-16 07:40:16 +02:00
----------------------------------------
-- Player Events
----------------------------------------
script.on_event ( defines.events . on_player_created , function ( event )
2024-09-06 03:15:56 +02:00
SeparateSpawnsInitPlayer ( event.player_index )
2024-08-16 07:40:16 +02:00
end )
script.on_event ( defines.events . on_player_respawned , function ( event )
SeparateSpawnsPlayerRespawned ( event )
end )
script.on_event ( defines.events . on_player_left_game , function ( event )
SeparateSpawnsPlayerLeft ( event )
end )
2024-10-22 05:11:20 +02:00
-- This does NOT do what I expected, it triggers anytime the VIEW changes, not the character moving.
-- script.on_event(defines.events.on_player_changed_surface, function(event)
-- SeparateSpawnsPlayerChangedSurface(event)
-- end)
2024-09-23 18:59:22 +02:00
2024-10-28 00:04:55 +02:00
script.on_event ( defines.events . on_rocket_launched , function ( event )
log ( " Rocket launched! " )
log ( serpent.block ( event ) )
end )
2024-10-04 19:27:42 +02:00
2024-11-04 19:23:07 +02:00
script.on_event ( defines.events . on_player_driving_changed_state , function ( event )
local entity = event.entity
--If a player gets in or out of a vehicle, mark the area as safe so we don't delete the vehicle by accident.
--Only world eater will clean up these chunks over time if it is enabled.
if storage.ocfg . regrowth.enable_regrowth and ( entity ~= nil ) then
RegrowthMarkAreaSafeGivenTilePos ( entity.surface . name , entity.position , 1 , false )
end
log ( " Player driving changed state " )
log ( serpent.block ( event ) )
-- Track the surfaces whenever driving state changes for a cargo-pod ONLY.
-- This triggers events for surface changes when using the standard space travel method.
if ( entity ~= nil ) and ( entity.name == " cargo-pod " ) then
local player = game.players [ event.player_index ]
-- Check if driving flag is set
if ( player.driving ) then
log ( " Player is driving a cargo-pod " )
end
SeparateSpawnsUpdatePlayerSurface ( player , entity.surface . name )
end
end )
2024-11-09 06:00:12 +02:00
script.on_event ( defines.events . on_research_finished , function ( event )
local research = event.research
2024-11-09 07:19:04 +02:00
-- TODO: Add a non-mod setting to disable this.
2024-11-21 20:41:39 +02:00
SendBroadcastMsg ( { " oarc-research-finished " , research.force . name , research.name } )
2024-11-09 06:00:12 +02:00
end )
2024-10-23 17:19:42 +02:00
----------------------------------------
-- CUSTOM OARC Events (shown here for demo and logging purposes)
----------------------------------------
2024-10-28 00:04:55 +02:00
---@class OarcCustomEventBase
---@field mod_name string
---@field name integer --For custom events, this is the event ID
---@field tick integer
---@class OarcModOnSpawnCreatedEvent: OarcCustomEventBase
---@field spawn_data OarcUniqueSpawn
2024-10-23 17:19:42 +02:00
script.on_event ( " oarc-mod-on-spawn-created " , function ( event )
2024-11-08 05:27:12 +02:00
log ( " EVENT - oarc-mod-on-spawn-created: " .. serpent.block ( event --[[@as OarcModOnSpawnCreatedEvent]] ) )
2024-10-23 17:19:42 +02:00
end )
2024-10-28 00:04:55 +02:00
---@class OarcModOnSpawnRemoveRequestEvent: OarcCustomEventBase
---@field spawn_data OarcUniqueSpawn
2024-10-23 17:19:42 +02:00
script.on_event ( " oarc-mod-on-spawn-remove-request " , function ( event )
2024-11-08 05:27:12 +02:00
log ( " EVENT - oarc-mod-on-spawn-remove-request: " .. serpent.block ( event --[[@as OarcModOnSpawnRemoveRequestEvent]] ) )
2024-10-23 17:19:42 +02:00
end )
2024-10-28 00:04:55 +02:00
---@class OarcModOnPlayerResetEvent: OarcCustomEventBase
---@field player_index integer
2024-10-23 17:19:42 +02:00
script.on_event ( " oarc-mod-on-player-reset " , function ( event )
2024-11-08 05:27:12 +02:00
log ( " EVENT - oarc-mod-on-player-reset: " .. serpent.block ( event --[[@as OarcModOnPlayerResetEvent]] ) )
2024-10-23 17:19:42 +02:00
end )
2024-10-28 00:04:55 +02:00
---@class OarcModOnPlayerSpawnedEvent: OarcCustomEventBase
---@field player_index integer
2024-10-23 17:19:42 +02:00
script.on_event ( " oarc-mod-on-player-spawned " , function ( event )
2024-11-08 05:27:12 +02:00
log ( " EVENT - oarc-mod-on-player-spawned: " .. serpent.block ( event --[[@as OarcModOnPlayerSpawnedEvent]] ) )
2024-10-23 17:19:42 +02:00
end )
2024-10-28 00:04:55 +02:00
---@class OarcModCharacterSurfaceChangedEvent: OarcCustomEventBase
---@field player_index integer
---@field old_surface_name string
---@field new_surface_name string
script.on_event ( " oarc-mod-character-surface-changed " , function ( event )
2024-11-08 05:27:12 +02:00
log ( " EVENT - oarc-mod-character-surface-changed: " .. serpent.block ( event --[[@as OarcModCharacterSurfaceChangedEvent]] ) )
2024-11-21 20:41:39 +02:00
2024-11-04 20:56:31 +02:00
--This is just here so I don't get lua warnings about unused variables.
---@type OarcModCharacterSurfaceChangedEvent
local custom_event = event --[[@as OarcModCharacterSurfaceChangedEvent]]
local player = game.players [ custom_event.player_index ]
SeparateSpawnsPlayerChangedSurface ( player , custom_event.old_surface_name , custom_event.new_surface_name )
2024-10-28 00:04:55 +02:00
end )
-- I raise this event whenever teleporting the player!
script.on_event ( defines.events . script_raised_teleported , function ( event )
log ( " script_raised_teleported " )
log ( serpent.block ( event ) )
local entity = event.entity
if entity.type == " character " and entity.player then
2024-10-28 01:50:52 +02:00
SeparateSpawnsUpdatePlayerSurface ( entity.player , entity.surface . name )
2024-10-28 00:04:55 +02:00
end
end )
2024-08-25 03:06:43 +02:00
----------------------------------------
-- Shared chat, so you don't have to type /s
-- But you do lose your player colors across forces.
----------------------------------------
script.on_event ( defines.events . on_console_chat , function ( event )
2024-10-20 02:48:20 +02:00
if ( storage.ocfg . gameplay.enable_shared_team_chat ) then
2024-08-25 03:06:43 +02:00
if ( event.player_index ~= nil ) then
ShareChatBetweenForces ( game.players [ event.player_index ] , event.message )
end
end
end )
2024-08-16 07:40:16 +02:00
----------------------------------------
-- On tick events. Stuff that needs to happen at regular intervals.
-- Delayed events, delayed spawns, ...
----------------------------------------
script.on_event ( defines.events . on_tick , function ( event )
DelayedSpawnOnTick ( )
FadeoutRenderOnTick ( )
2024-11-09 06:00:12 +02:00
OnTickNilCharacterTeleportQueue ( )
2024-08-16 07:40:16 +02:00
2024-10-20 02:48:20 +02:00
if storage.ocfg . regrowth.enable_regrowth then
2024-08-16 07:40:16 +02:00
RegrowthOnTick ( )
end
2024-09-07 13:00:29 +02:00
RegrowthForceRemovalOnTick ( ) -- Allows for abandoned base cleanup without regrowth enabled.
2024-11-20 03:59:25 +02:00
if storage.ocfg . gameplay.modified_enemy_spawning then
RemoveDemolishersInWarningZone ( event )
end
2024-08-16 07:40:16 +02:00
end )
----------------------------------------
-- Chunk Generation
----------------------------------------
script.on_event ( defines.events . on_chunk_generated , function ( event )
2024-10-20 02:48:20 +02:00
if storage.ocfg . regrowth.enable_regrowth then
2024-08-16 07:40:16 +02:00
RegrowthChunkGenerate ( event )
end
2024-11-21 20:41:39 +02:00
2024-09-24 17:32:58 +02:00
CreateHoldingPenChunks ( event )
2024-09-17 20:19:41 +02:00
2024-10-20 02:48:20 +02:00
if storage.ocfg . gameplay.modified_enemy_spawning then
2024-09-17 20:19:41 +02:00
DowngradeWormsDistanceBasedOnChunkGenerate ( event )
DowngradeAndReduceEnemiesOnChunkGenerate ( event )
end
2024-11-09 06:00:12 +02:00
SeparateSpawnsGenerateChunk ( event )
2024-08-16 07:40:16 +02:00
end )
2024-09-15 04:23:59 +02:00
----------------------------------------
-- Radar Scanning
----------------------------------------
2024-11-21 20:41:39 +02:00
script.on_event ( defines.events . on_sector_scanned , function ( event )
2024-10-20 02:48:20 +02:00
if storage.ocfg . regrowth.enable_regrowth then
2024-08-16 07:40:16 +02:00
RegrowthSectorScan ( event )
end
end )
----------------------------------------
-- Surface Generation
----------------------------------------
-- This is not called when the default surface "nauvis" is created as it will always exist!
script.on_event ( defines.events . on_surface_created , function ( event )
2024-10-21 19:48:42 +02:00
local surface = game.surfaces [ event.surface_index ]
2024-10-22 05:11:20 +02:00
2024-10-21 19:48:42 +02:00
log ( " Surface created: " .. surface.name )
2024-10-22 05:11:20 +02:00
if IsSurfaceBlacklisted ( surface.name ) then return end
2024-10-21 19:48:42 +02:00
2024-08-16 07:40:16 +02:00
SeparateSpawnsSurfaceCreated ( event )
2024-08-21 20:03:48 +02:00
RegrowthSurfaceCreated ( event )
2024-08-16 07:40:16 +02:00
end )
2024-08-21 20:03:48 +02:00
script.on_event ( defines.events . on_pre_surface_deleted , function ( event )
2024-10-22 05:11:20 +02:00
local surface = game.surfaces [ event.surface_index ]
log ( " Surface deleted: " .. surface.name )
if IsSurfaceBlacklisted ( surface.name ) then return end
2024-08-16 07:40:16 +02:00
SeparateSpawnsSurfaceDeleted ( event )
2024-08-21 20:03:48 +02:00
RegrowthSurfaceDeleted ( event )
2024-08-16 07:40:16 +02:00
end )
----------------------------------------
-- Various on "built" events
----------------------------------------
script.on_event ( defines.events . on_built_entity , function ( event )
2024-10-20 02:48:20 +02:00
if storage.ocfg . regrowth.enable_regrowth then
2024-10-21 19:48:42 +02:00
RegrowthMarkAreaSafeGivenTilePos ( event.entity . surface.name , event.entity . position , 2 , false )
2024-08-16 07:40:16 +02:00
end
2024-09-25 04:28:45 +02:00
-- For tracking spidertrons...
RegrowthOnBuiltEntity ( event )
2024-08-16 07:40:16 +02:00
end )
script.on_event ( defines.events . on_robot_built_entity , function ( event )
2024-10-20 02:48:20 +02:00
if storage.ocfg . regrowth.enable_regrowth then
2024-10-21 19:48:42 +02:00
RegrowthMarkAreaSafeGivenTilePos ( event.entity . surface.name , event.entity . position , 2 , false )
2024-08-16 07:40:16 +02:00
end
end )
script.on_event ( defines.events . on_player_built_tile , function ( event )
2024-10-20 02:48:20 +02:00
if storage.ocfg . regrowth.enable_regrowth then
2024-09-12 03:20:00 +02:00
for _ , v in pairs ( event.tiles ) do
2024-08-21 20:03:48 +02:00
RegrowthMarkAreaSafeGivenTilePos ( game.surfaces [ event.surface_index ] . name , v.position , 2 , false )
2024-08-16 07:40:16 +02:00
end
end
end )
----------------------------------------
-- On script_raised_built. This should help catch mods that
-- place items that don't count as player_built and robot_built.
-- Specifically FARL.
----------------------------------------
script.on_event ( defines.events . script_raised_built , function ( event )
2024-10-20 02:48:20 +02:00
if storage.ocfg . regrowth.enable_regrowth then
2024-08-21 20:03:48 +02:00
RegrowthMarkAreaSafeGivenTilePos ( event.entity . surface.name , event.entity . position , 2 , false )
2024-08-16 07:40:16 +02:00
end
end )
----------------------------------------
-- On Entity Spawned and On Biter Base Built
-- This is where I modify biter spawning based on location and other factors.
----------------------------------------
script.on_event ( defines.events . on_entity_spawned , function ( event )
2024-10-20 02:48:20 +02:00
if ( storage.ocfg . gameplay.modified_enemy_spawning ) then
2024-10-18 03:55:10 +02:00
ModifyEnemySpawnsNearPlayerStartingAreas ( event )
end
2024-08-16 07:40:16 +02:00
end )
script.on_event ( defines.events . on_biter_base_built , function ( event )
2024-10-20 02:48:20 +02:00
if ( storage.ocfg . gameplay.modified_enemy_spawning ) then
2024-10-18 03:55:10 +02:00
ModifyEnemySpawnsNearPlayerStartingAreas ( event )
2024-08-16 07:40:16 +02:00
end
end )
2024-11-20 03:59:25 +02:00
script.on_event ( defines.events . on_segment_entity_created , function ( event )
if storage.ocfg . gameplay.modified_enemy_spawning then
TrackDemolishers ( event )
end
end )
2024-09-14 19:59:28 +02:00
----------------------------------------
-- On unit group finished gathering
-- This is where I remove biter waves on offline players
----------------------------------------
script.on_event ( defines.events . on_unit_group_finished_gathering , function ( event )
2024-10-20 02:48:20 +02:00
if ( storage.ocfg . gameplay.enable_offline_protection ) then
2024-09-14 19:59:28 +02:00
OarcModifyEnemyGroup ( event )
end
end )
2024-10-13 03:57:46 +02:00
----------------------------------------
-- On enemies killed
-- For coin generation and stuff
----------------------------------------
script.on_event ( defines.events . on_post_entity_died , function ( event )
2024-10-20 02:48:20 +02:00
if storage.ocfg . coin_generation.enabled then
2024-10-13 03:57:46 +02:00
CoinsFromEnemiesOnPostEntityDied ( event )
end
end ,
{ { filter = " type " , type = " unit " } , { filter = " type " , type = " unit-spawner " } , { filter = " type " , type = " turret " } } )
2024-11-05 18:09:01 +02:00
script.on_event ( defines.events . on_entity_damaged , function ( event )
if storage.ocfg . gameplay.scale_spawner_damage then
ApplySpawnerDamageScaling ( event )
end
end ,
{ { filter = " type " , type = " unit-spawner " } } )
2024-08-16 07:40:16 +02:00
----------------------------------------
-- Gui Events
----------------------------------------
script.on_event ( defines.events . on_gui_click , function ( event )
2024-10-06 21:28:16 +02:00
if not event.element . valid then return end
2024-08-23 20:16:11 +02:00
2024-08-21 20:03:48 +02:00
SeparateSpawnsGuiClick ( event )
2024-10-04 19:27:42 +02:00
OarcGuiTabsClick ( event )
2024-08-16 07:40:16 +02:00
end )
--- Called when LuaGuiElement checked state is changed (related to checkboxes and radio buttons).
script.on_event ( defines.events . on_gui_checked_state_changed , function ( event )
2024-10-06 21:28:16 +02:00
if not event.element . valid then return end
2024-08-23 20:16:11 +02:00
2024-08-21 20:03:48 +02:00
SeparateSpawnsGuiCheckedStateChanged ( event )
2024-10-06 21:28:16 +02:00
OarcGuiTabsCheckedStateChanged ( event )
2024-08-16 07:40:16 +02:00
end )
script.on_event ( defines.events . on_gui_selected_tab_changed , function ( event )
2024-10-06 21:28:16 +02:00
if not event.element . valid then return end
2024-08-23 20:16:11 +02:00
2024-10-06 21:28:16 +02:00
OarcGuiTabsSelectedTabChanged ( event )
2024-08-16 07:40:16 +02:00
end )
-- For capturing player escaping custom GUI so we can close it using ESC key.
script.on_event ( defines.events . on_gui_closed , function ( event )
2024-08-21 20:03:48 +02:00
OarcGuiClosed ( event )
2024-08-16 07:40:16 +02:00
end )
2024-08-21 20:03:48 +02:00
2024-08-28 07:36:02 +02:00
--- For sliders and other value changing elements.
script.on_event ( defines.events . on_gui_value_changed , function ( event )
2024-10-06 21:28:16 +02:00
if not event.element . valid then return end
2024-08-28 07:36:02 +02:00
SeparateSpawnsGuiValueChanged ( event )
2024-10-06 21:28:16 +02:00
OarcGuiTabsValueChanged ( event )
2024-08-28 07:36:02 +02:00
end )
--- For dropdowns and listboxes.
script.on_event ( defines.events . on_gui_selection_state_changed , function ( event )
2024-10-06 21:28:16 +02:00
if not event.element . valid then return end
2024-08-28 07:36:02 +02:00
SeparateSpawnsGuiSelectionStateChanged ( event )
2024-10-06 21:28:16 +02:00
OarcGuiTabsSelectionStateChanged ( event )
2024-08-28 07:36:02 +02:00
end )
2024-09-09 20:20:52 +02:00
script.on_event ( defines.events . on_gui_text_changed , function ( event )
2024-10-06 21:28:16 +02:00
if not event.element . valid then return end
2024-09-09 20:20:52 +02:00
2024-10-06 21:28:16 +02:00
OarcGuiTabsTextChanged ( event )
2024-09-09 20:20:52 +02:00
end )
script.on_event ( defines.events . on_gui_confirmed , function ( event )
2024-10-06 21:28:16 +02:00
if not event.element . valid then return end
OarcGuiTabsConfirmed ( event )
end )
script.on_event ( defines.events . on_gui_elem_changed , function ( event )
if not event.element . valid then return end
2024-09-09 20:20:52 +02:00
2024-10-06 21:28:16 +02:00
OarcGuiTabsElemChanged ( event )
2024-09-09 20:20:52 +02:00
end )
2024-08-21 20:03:48 +02:00
----------------------------------------
-- Remote Interface
----------------------------------------
2024-08-16 07:40:16 +02:00
local oarc_mod_interface =
{
get_mod_settings = function ( )
2024-10-20 02:48:20 +02:00
return storage.ocfg
2024-08-16 07:40:16 +02:00
end
}
2024-11-21 20:41:39 +02:00
remote.add_interface ( " oarc_mod " , oarc_mod_interface )