1
0
mirror of https://github.com/Oarcinae/FactorioScenarioMultiplayerSpawn.git synced 2024-12-12 10:13:58 +02:00
FactorioScenarioMultiplayer.../control.lua
Oarcinae 4cd397f815 Add basic support for Vulcanus secondary spawns.
Have a workaround for demolishers that tracks them and destroys them if they are too close to the spawn. Can definitely be abused, but good enough for now. Fixed issue with joiners being teleported unexpectedly when rerolling a secondary spawn. Removed some old commented out code in utils. Tweak default danger radius to be smaller based on feedback. Fix fuild resources and sharing entities not moving when scaling spawn size. Added locale for welcome home ground text.
2024-11-19 20:59:25 -05:00

458 lines
16 KiB
Lua

-- Sep 2024
-- ____ _____ _____
-- / __ \ /\ | __ \ / ____|
-- | | | | / \ | |__) | |
-- | | | |/ /\ \ | _ /| |
-- | |__| / ____ \| | \ \| |____
-- \____/_/ \_\_| \_\\_____|
-- 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.
-- Major Features:
-- 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.
-- Sharing of electricity and items between players.
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")
require("lib/offline_protection")
require("lib/scaled_enemies")
require("lib/sharing")
-- TODO: Possibly remove this later?
require("lib/oarc_tests")
require("lib/oarc_commands")
--------------------------------------------------------------------------------
-- On Init - Only runs once the first time the game starts
--------------------------------------------------------------------------------
script.on_init(function(event)
-- 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
ValidateAndLoadConfig()
RegrowthInit()
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
-- If there are any players that already exist, init them now.
for _,player in pairs(game.players) do
SeparateSpawnsInitPlayer(player.index)
end
game.technology_notifications_enabled = false
end)
--------------------------------------------------------------------------------
-- On Load - Only for setting up some table stuff that shouldn't change during gameplay!
--------------------------------------------------------------------------------
script.on_load(function()
SetupOCFGModKeys()
end)
--------------------------------------------------------------------------------
-- On Configuration Changed - Only runs when the mod configuration changes
--------------------------------------------------------------------------------
script.on_configuration_changed(function(data)
for _,player in pairs(game.players) do
RecreateOarcGui(player) -- Reset the players GUI
end
end)
script.on_event(defines.events.on_runtime_mod_setting_changed, function(event)
if (not StringStartsWith(event.setting, "oarc-mod")) then return end
RuntimeModSettingChanged(event)
end)
----------------------------------------
-- Player Events
----------------------------------------
script.on_event(defines.events.on_player_created, function(event)
SeparateSpawnsInitPlayer(event.player_index)
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)
-- 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)
script.on_event(defines.events.on_rocket_launched, function(event)
log("Rocket launched!")
log(serpent.block(event))
end)
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)
script.on_event(defines.events.on_research_finished, function(event)
local research = event.research
-- TODO: Add a non-mod setting to disable this.
SendBroadcastMsg({"oarc-research-finished", research.force.name, research.localised_name})
end)
----------------------------------------
-- CUSTOM OARC Events (shown here for demo and logging purposes)
----------------------------------------
---@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
script.on_event("oarc-mod-on-spawn-created", function(event)
log("EVENT - oarc-mod-on-spawn-created:" .. serpent.block(event --[[@as OarcModOnSpawnCreatedEvent]]))
end)
---@class OarcModOnSpawnRemoveRequestEvent: OarcCustomEventBase
---@field spawn_data OarcUniqueSpawn
script.on_event("oarc-mod-on-spawn-remove-request", function(event)
log("EVENT - oarc-mod-on-spawn-remove-request:" .. serpent.block(event --[[@as OarcModOnSpawnRemoveRequestEvent]]))
end)
---@class OarcModOnPlayerResetEvent: OarcCustomEventBase
---@field player_index integer
script.on_event("oarc-mod-on-player-reset", function(event)
log("EVENT - oarc-mod-on-player-reset:" .. serpent.block(event --[[@as OarcModOnPlayerResetEvent]]))
end)
---@class OarcModOnPlayerSpawnedEvent: OarcCustomEventBase
---@field player_index integer
script.on_event("oarc-mod-on-player-spawned", function(event)
log("EVENT - oarc-mod-on-player-spawned:" .. serpent.block(event --[[@as OarcModOnPlayerSpawnedEvent]]))
end)
---@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)
log("EVENT - oarc-mod-character-surface-changed:" .. serpent.block(event --[[@as OarcModCharacterSurfaceChangedEvent]]))
--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)
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
SeparateSpawnsUpdatePlayerSurface(entity.player, entity.surface.name)
end
end)
----------------------------------------
-- 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)
if (storage.ocfg.gameplay.enable_shared_team_chat) then
if (event.player_index ~= nil) then
ShareChatBetweenForces(game.players[event.player_index], event.message)
end
end
end)
----------------------------------------
-- 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()
OnTickNilCharacterTeleportQueue()
if storage.ocfg.regrowth.enable_regrowth then
RegrowthOnTick()
end
RegrowthForceRemovalOnTick() -- Allows for abandoned base cleanup without regrowth enabled.
if storage.ocfg.gameplay.modified_enemy_spawning then
RemoveDemolishersInWarningZone(event)
end
end)
----------------------------------------
-- Chunk Generation
----------------------------------------
script.on_event(defines.events.on_chunk_generated, function(event)
if storage.ocfg.regrowth.enable_regrowth then
RegrowthChunkGenerate(event)
end
CreateHoldingPenChunks(event)
if storage.ocfg.gameplay.modified_enemy_spawning then
DowngradeWormsDistanceBasedOnChunkGenerate(event)
DowngradeAndReduceEnemiesOnChunkGenerate(event)
end
SeparateSpawnsGenerateChunk(event)
end)
----------------------------------------
-- Radar Scanning
----------------------------------------
script.on_event(defines.events.on_sector_scanned, function (event)
if storage.ocfg.regrowth.enable_regrowth then
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)
local surface = game.surfaces[event.surface_index]
log("Surface created: " .. surface.name)
if IsSurfaceBlacklisted(surface.name) then return end
SeparateSpawnsSurfaceCreated(event)
RegrowthSurfaceCreated(event)
end)
script.on_event(defines.events.on_pre_surface_deleted, function(event)
local surface = game.surfaces[event.surface_index]
log("Surface deleted: " .. surface.name)
if IsSurfaceBlacklisted(surface.name) then return end
SeparateSpawnsSurfaceDeleted(event)
RegrowthSurfaceDeleted(event)
end)
----------------------------------------
-- Various on "built" events
----------------------------------------
script.on_event(defines.events.on_built_entity, function(event)
if storage.ocfg.regrowth.enable_regrowth then
RegrowthMarkAreaSafeGivenTilePos(event.entity.surface.name, event.entity.position, 2, false)
end
-- For tracking spidertrons...
RegrowthOnBuiltEntity(event)
end)
script.on_event(defines.events.on_robot_built_entity, function (event)
if storage.ocfg.regrowth.enable_regrowth then
RegrowthMarkAreaSafeGivenTilePos(event.entity.surface.name, event.entity.position, 2, false)
end
end)
script.on_event(defines.events.on_player_built_tile, function (event)
if storage.ocfg.regrowth.enable_regrowth then
for _,v in pairs(event.tiles) do
RegrowthMarkAreaSafeGivenTilePos(game.surfaces[event.surface_index].name, v.position, 2, false)
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)
if storage.ocfg.regrowth.enable_regrowth then
RegrowthMarkAreaSafeGivenTilePos(event.entity.surface.name, event.entity.position, 2, false)
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)
if (storage.ocfg.gameplay.modified_enemy_spawning) then
ModifyEnemySpawnsNearPlayerStartingAreas(event)
end
end)
script.on_event(defines.events.on_biter_base_built, function(event)
if (storage.ocfg.gameplay.modified_enemy_spawning) then
ModifyEnemySpawnsNearPlayerStartingAreas(event)
end
end)
script.on_event(defines.events.on_segment_entity_created, function(event)
if storage.ocfg.gameplay.modified_enemy_spawning then
TrackDemolishers(event)
end
end)
----------------------------------------
-- 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)
if (storage.ocfg.gameplay.enable_offline_protection) then
OarcModifyEnemyGroup(event)
end
end)
----------------------------------------
-- On enemies killed
-- For coin generation and stuff
----------------------------------------
script.on_event(defines.events.on_post_entity_died, function(event)
if storage.ocfg.coin_generation.enabled then
CoinsFromEnemiesOnPostEntityDied(event)
end
end,
{{filter="type", type = "unit"}, {filter="type", type = "unit-spawner"}, {filter="type", type = "turret"}})
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"}})
----------------------------------------
-- Gui Events
----------------------------------------
script.on_event(defines.events.on_gui_click, function(event)
if not event.element.valid then return end
SeparateSpawnsGuiClick(event)
OarcGuiTabsClick(event)
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)
if not event.element.valid then return end
SeparateSpawnsGuiCheckedStateChanged(event)
OarcGuiTabsCheckedStateChanged(event)
end)
script.on_event(defines.events.on_gui_selected_tab_changed, function (event)
if not event.element.valid then return end
OarcGuiTabsSelectedTabChanged(event)
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)
OarcGuiClosed(event)
end)
--- For sliders and other value changing elements.
script.on_event(defines.events.on_gui_value_changed, function(event)
if not event.element.valid then return end
SeparateSpawnsGuiValueChanged(event)
OarcGuiTabsValueChanged(event)
end)
--- For dropdowns and listboxes.
script.on_event(defines.events.on_gui_selection_state_changed, function(event)
if not event.element.valid then return end
SeparateSpawnsGuiSelectionStateChanged(event)
OarcGuiTabsSelectionStateChanged(event)
end)
script.on_event(defines.events.on_gui_text_changed, function(event)
if not event.element.valid then return end
OarcGuiTabsTextChanged(event)
end)
script.on_event(defines.events.on_gui_confirmed, function(event)
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
OarcGuiTabsElemChanged(event)
end)
----------------------------------------
-- Remote Interface
----------------------------------------
local oarc_mod_interface =
{
get_mod_settings = function()
return storage.ocfg
end
}
remote.add_interface("oarc_mod", oarc_mod_interface)