1
0
mirror of https://github.com/Oarcinae/FactorioScenarioMultiplayerSpawn.git synced 2024-12-04 09:43:00 +02:00

Merge pull request #157 from Oarcinae/dev

2.0.2 Minor Fixes and Features (+player list, +moat fish, +more GUI config)
This commit is contained in:
Oarcinae 2024-10-07 23:02:55 -04:00 committed by GitHub
commit 54ab7c0a16
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 1862 additions and 173 deletions

View File

@ -1,4 +1,18 @@
---------------------------------------------------------------------------------------------------
Version: 2.0.2
Date: 2024-10-08
Major Features:
- Added a new GUI config for all surface specific settings that can't be made available in the mod settings. This will let you easily configure all available settings via the in game custom GUI now. It is also possible to import and export settings as a serialized string. (You can still use a custom scenario to configure all settings as well using the template provided.)
- Added a player list tab to the custom GUI so you can see all players on the server, and their locations.
Bugfixes:
- Fixed a minor issue with resource placement not calculating the angle correctly when placing solid resources.
- Removed log spam related to offline enemy protection.
- Fixed an issue with regrowth that was causing log spam about chunks not being tracked properly.
Info:
- Added fish to moats.
- Doubled the default value for "minimum distance to existing chunks". There were some concerns bases might spawn too close together in some cases, this should help alleviate that.
- Changed safe_area radii config to use chunks instead of tiles. Makes more sense for the setting and is easier to visualize distance in chunks. (Older configs should get migrated automatically, but you may want to double check your settings.)
---------------------------------------------------------------------------------------------------
Version: 2.0.1
Date: 2024-09-25
Bugfixes:

View File

@ -68,9 +68,14 @@ end)
--------------------------------------------------------------------------------
-- oarc_new_spawn_created = script.generate_event_name()
-- script.on_configuration_changed(function(data)
-- -- Regenerate event ID:
-- end)
script.on_configuration_changed(function(data)
-- Regenerate event ID:
-- Reset the players GUI
for _,player in pairs(game.players) do
RecreateOarcGui(player)
end
end)
script.on_event(defines.events.on_runtime_mod_setting_changed, function(event)
if (not StringStartsWith(event.setting, "oarc-mod")) then return end
@ -96,6 +101,7 @@ script.on_event(defines.events.on_player_changed_surface, function(event)
SeparateSpawnsPlayerChangedSurface(event)
end)
----------------------------------------
-- Shared chat, so you don't have to type /s
-- But you do lose your player colors across forces.
@ -248,30 +254,24 @@ end)
-- Gui Events
----------------------------------------
script.on_event(defines.events.on_gui_click, function(event)
if not event.element.valid then return end -- Should we ever react to invalid GUI elements?
if not event.element.valid then return end
SeparateSpawnsGuiClick(event)
ClickOarcGuiButton(event)
ServerInfoGuiClick(event)
SpawnCtrlGuiClick(event)
SettingsControlsTabGuiClick(event)
SettingsSurfaceControlsTabGuiClick(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 -- Should we ever react to invalid GUI elements?
if not event.element.valid then return end
SeparateSpawnsGuiCheckedStateChanged(event)
SpawnCtrlGuiOptionsSelect(event)
OarcGuiTabsCheckedStateChanged(event)
end)
script.on_event(defines.events.on_gui_selected_tab_changed, function (event)
if not event.element.valid then return end -- Should we ever react to invalid GUI elements?
if not event.element.valid then return end
OarcGuiSelectedTabChanged(event)
OarcGuiTabsSelectedTabChanged(event)
end)
-- For capturing player escaping custom GUI so we can close it using ESC key.
@ -282,30 +282,36 @@ 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 -- Should we ever react to invalid GUI elements?
if not event.element.valid then return end
SeparateSpawnsGuiValueChanged(event)
SettingsControlsTabGuiValueChanged(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 -- Should we ever react to invalid GUI elements?
if not event.element.valid then return end
SeparateSpawnsGuiSelectionStateChanged(event)
SettingsControlsTabGuiSelectionStateChanged(event)
OarcGuiTabsSelectionStateChanged(event)
end)
script.on_event(defines.events.on_gui_text_changed, function(event)
if not event.element.valid then return end -- Should we ever react to invalid GUI elements?
if not event.element.valid then return end
SettingsControlsTabGuiTextChanged(event)
OarcGuiTabsTextChanged(event)
end)
script.on_event(defines.events.on_gui_confirmed, function(event)
if not event.element.valid then return end -- Should we ever react to invalid GUI elements?
if not event.element.valid then return end
SettingsControlsTabGuiTextconfirmed(event)
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)
----------------------------------------

View File

@ -35,6 +35,46 @@ Major:
- Radar quality affects regrowth safe range?
- Update electric pole connections for shared power if things change in V2.0
- Source: https://forums.factorio.com/115737
- Specifics that I might need to investigate:
Added LuaPlayer::land_on_planet() method.
Added LuaPlayer::enter_space_platform() and leave_space_platform() method.
Added cargo-landing-pad prototype.
Added space-platform-starter-pack, space-location, planet and space-connection prototypes.
Added surface-property and surface prototypes.
Added new controller type (remote), which is to build space platforms, so it allows ghost building but not any physical manipulation.
Added LuaPlayer::physical_surface, physical_surface_index, physical_vehicle and physical_position read.
Electric pole created through LuaSurface::create_entity can be requested to not auto connect.
Added LuaSurface::global_effect read/write.
Added LuaSurface::localised_name read/write.
LuaGameScript::print, LuaPlayer::print, LuaSurface::print and LuaForce::print no longer accept Color as a second parameter.
Added LuaSurface::set_property() and get_property() methods.
Added LuaSurface::execute_lightning() method.
Added LuaSurface::create_global_electric_network() and destroy_global_electric_network() methods.
Added LuaSurface::has_global_electric_network read.
Added LuaSurface::platform read.
Added LuaSurface::pollutant_type read.
Added airborne-pollutant prototype and changed various pollution related properties to support multiple pollution types.
Added LuaSurface::deletable read.
Added LuaForce::set_surface_hidden() and get_surface_hidden() methods.
Added cause argument to LuaSurface::create_entity.
Added LuaSurfacePrototype::surface_properties read.
Added on_player_controller_changed event.
Removed LuaPlayer::open_map, zoom_to_world, and close_map. LuaPlayer::set_controller with type 'remote' replaces these.
oved LuaGameScript::styles to LuaPrototypes::style.
Removed LuaGameScript::active_mods. Use LuaBootstrap::active_mods instead.
Renamed `global` into `storage`.
Added LuaGameScript::technology_notifications_enabled (read/write).
Added LuaGameScript::planets read.
Added LuaGameScript::get_vehicles.
Added LuaForce::platforms read.
Added LuaGameScript::set_win_ending_info() and set_lose_ending_info() methods.
Added LuaForce::unlock_space_location(), lock_space_location() and is_space_location_unlocked() methods.
Added LuaForce::create_space_platform() method.
Added LuaForce::unlock_space_platforms(), lock_space_platforms() and is_space_platforms_unlocked() methods.
Changed LuaForce::evolution_factor, evolution_factor_by_pollution, evolution_factor_by_time and evolution_factor_by_killing_spawners to get_* and set_* methods.
Added LuaForce::copy_from() and copy_chart() methods.
------------------------------------------------------------------------------------------------------------------------
Other Ideas, Not Committed:

View File

@ -1,6 +1,6 @@
{
"name": "oarc-mod",
"version": "2.0.1",
"version": "2.0.2",
"factorio_version": "1.1",
"title": "Oarc Multiplayer Spawn",
"author": "Oarcinae",
@ -14,6 +14,7 @@
"(?) space-exploration",
"(?) alien-biomes",
"(?) sonaxaton-research-queue",
"(?) helmod"
"(?) helmod",
"(?) rso-mod"
]
}

View File

@ -26,6 +26,10 @@ SPAWN_SHAPE_CHOICE_SQUARE = "square"
RESOURCES_SHAPE_CHOICE_CIRCLE = "circle"
RESOURCES_SHAPE_CHOICE_SQUARE = "square"
MAX_CRASHED_SHIP_RESOURCES_ITEMS = 5
MAX_CRASHED_SHIP_WRECKAGE_ITEMS = 1
-- THIS is used as the default starting items on all surfaces if no other settings are provided!
---@type OarcConfigStartingItems
NAUVIS_STARTER_ITEMS =
{
@ -54,6 +58,7 @@ NAUVIS_STARTER_ITEMS =
},
}
-- THIS is used as the default spawn config on all surfaces if no other settings are provided!
---@type OarcConfigSpawn
NAUVIS_SPAWN_CONFIG =
{
@ -62,19 +67,19 @@ NAUVIS_SPAWN_CONFIG =
safe_area =
{
-- Safe area has no aliens
-- This is the radius in tiles of safe area.
safe_radius = CHUNK_SIZE*6,
-- This is the radius in chunks of safe area.
safe_radius = 6,
-- Warning area has significantly reduced aliens
-- This is the radius in tiles of warning area.
warn_radius = CHUNK_SIZE*12,
-- This is the radius in chunks of warning area.
warn_radius = 12,
-- 1 : X (spawners alive : spawners destroyed) in this area
warn_reduction = 20,
-- Danger area has slightly reduced aliens
-- This is the radius in tiles of danger area.
danger_radius = CHUNK_SIZE*32,
-- This is the radius in chunks of danger area.
danger_radius = 32,
-- 1 : X (spawners alive : spawners destroyed) in this area
danger_reduction = 5,
@ -109,24 +114,32 @@ NAUVIS_SPAWN_CONFIG =
["iron-ore"] = {
amount = 1500,
size = 21,
-- These are only used if not using automatic placing.
x_offset = -29,
y_offset = 16
},
["copper-ore"] = {
amount = 1200,
size = 21,
-- These are only used if not using automatic placing.
x_offset = -28,
y_offset = -3
},
["stone"] = {
amount = 1200,
size = 21,
-- These are only used if not using automatic placing.
x_offset = -27,
y_offset = -34
},
["coal"] = {
amount = 1200,
size = 21,
-- These are only used if not using automatic placing.
x_offset = -27,
y_offset = -20
}
@ -141,6 +154,8 @@ NAUVIS_SPAWN_CONFIG =
{
num_patches = 2,
amount = 900000,
-- These are only used if not using automatic placing.
-- Starting position offset (relative to bottom/south of spawn area)
x_offset_start = -3,
y_offset_start = -10,
@ -202,7 +217,7 @@ OCFG = {
-- chunks. It ensures the spawn area isn't too near generated/explored/existing
-- area. The larger you make this, the further away players will spawn from
-- generated map area (even if it is not visible on the map!).
minimum_distance_to_existing_chunks = 10,
minimum_distance_to_existing_chunks = 20,
-- The range in which a player can select how close to the center of the map they want to spawn.
near_spawn_distance = 100,
@ -330,11 +345,11 @@ OCFG = {
-- At what angle (in radians) do resources start.
-- 0 means starts directly east.
-- Resources are placed clockwise from there.
angle_offset = 2.32, -- 2.32 is approx SSW.
angle_offset = 2.09, -- Approx SSW.
-- At what andle do we place the last resource.
-- angle_offset and angle_final determine spacing and placement.
angle_final = 4.46, -- 4.46 is approx NNW.
angle_final = 4.18, -- Approx NNW.
-- Vertical offset in tiles for the deposit resource placement. Starting from top-left corner.
-- Only applicable for square spawns.
@ -364,22 +379,22 @@ OCFG = {
starting_items = NAUVIS_STARTER_ITEMS,
spawn_config = NAUVIS_SPAWN_CONFIG
},
["vulcanus"] = {
starting_items = NAUVIS_STARTER_ITEMS,
spawn_config = NAUVIS_SPAWN_CONFIG
},
["fulgora"] = {
starting_items = NAUVIS_STARTER_ITEMS,
spawn_config = NAUVIS_SPAWN_CONFIG
},
["gleba"] = {
starting_items = NAUVIS_STARTER_ITEMS,
spawn_config = NAUVIS_SPAWN_CONFIG
},
["aquilo"] = {
starting_items = NAUVIS_STARTER_ITEMS,
spawn_config = NAUVIS_SPAWN_CONFIG
}
-- ["vulcanus"] = {
-- starting_items = NAUVIS_STARTER_ITEMS,
-- spawn_config = NAUVIS_SPAWN_CONFIG
-- },
-- ["fulgora"] = {
-- starting_items = NAUVIS_STARTER_ITEMS,
-- spawn_config = NAUVIS_SPAWN_CONFIG
-- },
-- ["gleba"] = {
-- starting_items = NAUVIS_STARTER_ITEMS,
-- spawn_config = NAUVIS_SPAWN_CONFIG
-- },
-- ["aquilo"] = {
-- starting_items = NAUVIS_STARTER_ITEMS,
-- spawn_config = NAUVIS_SPAWN_CONFIG
-- }
},
-- Surfaces blacklist (Ignore these surfaces completely for spawning and regrowth!)
@ -490,10 +505,10 @@ OCFG = {
---@field shape SpawnShapeChoice Spawn a circle/octagon/square of trees around this base outline.
---@class OarcConfigSpawnSafeArea
---@field safe_radius number Safe area has no aliens This is the radius in tiles of safe area.
---@field warn_radius number Warning area has significantly reduced aliens This is the radius in tiles of warning area.
---@field safe_radius number Safe area has no aliens This is the radius in chunks of safe area.
---@field warn_radius number Warning area has significantly reduced aliens This is the radius in chunks of warning area.
---@field warn_reduction number 1 : X (spawners alive : spawners destroyed) in this area
---@field danger_radius number Danger area has slightly reduce aliens This is the radius in tiles of danger area.
---@field danger_radius number Danger area has slightly reduce aliens This is the radius in chunks of danger area.
---@field danger_reduction number 1 : X (spawners alive : spawners destroyed) in this area
---@class OarcConfigSpawnWater

View File

@ -158,7 +158,7 @@ function ValidateAndLoadConfig()
-- Load the template config into the global table.
---@class OarcConfig
global.ocfg = OCFG
global.ocfg = table.deepcopy(OCFG)
-- Check that each entry in OCFG matches the default value of the mod setting. This is just for my own sanity.
-- Helps make sure mod default settings and my internal config are in sync.
@ -178,11 +178,58 @@ function ValidateAndLoadConfig()
GetScenarioOverrideSettings() -- Get any scenario settings and overwrite both the mod settings and OARC_CFG.
SyncModSettingsToOCFG() -- Make sure mod settings are in sync with global.ocfg table.
ValidateSettings() -- These are validation checks that can't be done within the mod settings natively.
end
---DO some basic validation checks on the config settings.
---@return nil
function ValidateSettings()
-- Verify the major sections exist. Not exhaustive but should catch missing sections.
if (global.ocfg["server_info"] == nil) then
log("ERROR - Missing server_info section in config! Loading defaults instead!")
SendBroadcastMsg("ERROR - Missing server_info section in config! Loading defaults instead!")
global.ocfg.server_info = table.deepcopy(OCFG.server_info)
end
if (global.ocfg["gameplay"] == nil) then
log("ERROR - Missing gameplay section in config! Loading defaults instead!")
SendBroadcastMsg("ERROR - Missing gameplay section in config! Loading defaults instead!")
global.ocfg.gameplay = table.deepcopy(OCFG.gameplay)
end
if (global.ocfg["regrowth"] == nil) then
log("ERROR - Missing regrowth section in config! Loading defaults instead!")
SendBroadcastMsg("ERROR - Missing regrowth section in config! Loading defaults instead!")
global.ocfg.regrowth = table.deepcopy(OCFG.regrowth)
end
if (global.ocfg["spawn_general"] == nil) then
log("ERROR - Missing spawn_general section in config! Loading defaults instead!")
SendBroadcastMsg("ERROR - Missing spawn_general section in config! Loading defaults instead!")
global.ocfg.spawn_general = table.deepcopy(OCFG.spawn_general)
end
if (global.ocfg["resource_placement"] == nil) then
log("ERROR - Missing resource_placement section in config! Loading defaults instead!")
SendBroadcastMsg("ERROR - Missing resource_placement section in config! Loading defaults instead!")
global.ocfg.resource_placement = table.deepcopy(OCFG.resource_placement)
end
if (global.ocfg["surfaces_config"] == nil) then
log("ERROR - Missing surfaces_config section in config! Loading defaults instead!")
SendBroadcastMsg("ERROR - Missing surfaces_config section in config! Loading defaults instead!")
global.ocfg.surfaces_config = table.deepcopy(OCFG.surfaces_config)
end
if (global.ocfg["surfaces_blacklist"] == nil) then
log("ERROR - Missing surfaces_blacklist section in config! Loading defaults instead!")
SendBroadcastMsg("ERROR - Missing surfaces_blacklist section in config! Loading defaults instead!")
global.ocfg.surfaces_blacklist = table.deepcopy(OCFG.surfaces_blacklist)
end
if (global.ocfg["surfaces_blacklist_match"] == nil) then
log("ERROR - Missing surfaces_blacklist_match section in config! Loading defaults instead!")
SendBroadcastMsg("ERROR - Missing surfaces_blacklist_match section in config! Loading defaults instead!")
global.ocfg.surfaces_blacklist_match = table.deepcopy(OCFG.surfaces_blacklist_match)
end
-- Validate enable_main_team and enable_separate_teams.
-- Force enable_main_team if both are disabled.
if (not global.ocfg.gameplay.enable_main_team and not global.ocfg.gameplay.enable_separate_teams) then
@ -221,6 +268,17 @@ function ValidateSettings()
if (global.ocfg.surfaces_config["nauvis"] == nil) then
error("nauvis surface config does not exist! Please check your mod settings or config!")
end
-- Very for each surface config that the item counts are valid.
for surface_name,surface_config in pairs(global.ocfg.surfaces_config) do
if (table_size(surface_config.starting_items.crashed_ship_resources) > MAX_CRASHED_SHIP_RESOURCES_ITEMS) then
error("Too many items in crashed_ship_resources for surface: " .. surface_name)
end
if (table_size(surface_config.starting_items.crashed_ship_wreakage) > MAX_CRASHED_SHIP_WRECKAGE_ITEMS) then
error("Too many items in crashed_ship_wreakage for surface: " .. surface_name)
end
end
end
-- Read in the mod settings and copy them to the OARC_CFG table, overwriting the defaults in config.lua.
@ -241,6 +299,8 @@ function CacheModSettings()
global.ocfg.gameplay.main_force_name = settings.startup["oarc-mod-main-force-name"].value --[[@as string]]
end
---Get the scenario settings from the scenario if it exists.
---@return nil
function GetScenarioOverrideSettings()
if remote.interfaces["oarc_scenario"] then
@ -250,27 +310,32 @@ function GetScenarioOverrideSettings()
-- Overwrite the non mod settings with the scenario settings.
global.ocfg = scenario_settings
-- Override the mod settings with the scenario settings!
for _,entry in pairs(OCFG_KEYS) do
if (entry.type ~= "header") and (entry.type ~= "subheader") then
local mod_key = entry.mod_key
local oarc_key = entry.ocfg_keys
local scenario_value = GetGlobalOarcConfigUsingKeyTable(oarc_key)
if (scenario_value ~= nil) then
local ok,result = pcall(function() settings.global[mod_key] = { value = scenario_value } end)
if not ok then
error("Error setting mod setting: " .. mod_key .. " = " .. tostring(scenario_value) .. "\n" .. "If you see this, you probably picked an invalid value for a setting override in the custom scenario.")
end
end
end
end
else
log("No scenario settings found.")
end
end
---Syncs all mod settings to the OARC config table.
---@return nil
function SyncModSettingsToOCFG()
-- Override the mod settings with the the global.ocfg settings.
for _,entry in pairs(OCFG_KEYS) do
if (entry.type ~= "header") and (entry.type ~= "subheader") then
local mod_key = entry.mod_key
local oarc_key = entry.ocfg_keys
local scenario_value = GetGlobalOarcConfigUsingKeyTable(oarc_key)
if (scenario_value ~= nil) then
local ok,result = pcall(function() settings.global[mod_key] = { value = scenario_value } end)
if not ok then
error("Error setting mod setting: " .. mod_key .. " = " .. tostring(scenario_value) .. "\n" .. "If you see this, you probably picked an invalid value for a setting override in the custom scenario.")
end
end
end
end
end
---Handles the event when a mod setting is changed in the mod settings menu.
---@param event EventData.on_runtime_mod_setting_changed
---@return nil

View File

@ -0,0 +1,128 @@
-- Contains the GUI for the player list tab.
---Used by AddOarcGuiTab
---@param tab_container LuaGuiElement
---@param player LuaPlayer
---@return nil
function CreatePlayerListTab(tab_container, player)
local scroll_pane = tab_container.add {
type = "scroll-pane",
direction = "vertical",
vertical_scroll_policy = "always",
}
scroll_pane.style.maximal_height = 500
-- Make a table: player name, force name, home surface, time played, gps button
local player_table = scroll_pane.add {
type = "table",
column_count = 6,
style = "bordered_table",
}
--- Add the header rows
AddLabel(player_table, nil, {"oarc-player-list-tab-column-header-player"}, "caption_label")
AddLabel(player_table, nil, {"oarc-player-list-tab-column-header-force"}, "caption_label")
AddLabel(player_table, nil, {"oarc-player-list-tab-column-header-surface"}, "caption_label")
AddLabel(player_table, nil, {"oarc-player-list-tab-column-header-time-player"}, "caption_label")
AddLabel(player_table, nil, {"oarc-player-list-tab-column-header-location"}, "caption_label")
AddLabel(player_table, nil, {"oarc-player-list-tab-column-header-status"}, "caption_label")
-- List online players first
for _,online_player in pairs(game.connected_players) do
AddPlayerRow(player_table, online_player.name, true)
end
-- List offline players later
for _,player in pairs(game.players) do
if (not player.connected) then
AddPlayerRow(player_table, player.name, false)
end
end
end
---Add a row to the table for a player
---@param table LuaGuiElement
---@param player_name string
---@param online boolean
---@return nil
function AddPlayerRow(table, player_name, online)
local player = game.players[player_name]
if (player) then
if player.admin then
local label = AddLabel(table, nil, player.name, my_player_list_admin_style)
label.tooltip = "Admin"
else
AddLabel(table, nil, player.name, my_label_style)
end
AddLabel(table, nil, player.force.name, my_label_style)
-- List home surface name or holding pen
if (player.surface.name == HOLDING_PEN_SURFACE_NAME) then
AddLabel(table, nil, {"oarc-player-waiting-to-spawn"}, my_label_style)
else
local spawn = FindPlayerHomeSpawn(player.name)
if (spawn) then
AddLabel(table, nil, spawn.surface_name, my_label_style)
else
AddLabel(table, nil, "Unknown?", my_label_style) -- Shouldn't happen
end
end
AddLabel(table, nil, FormatTimeHoursSecs(player.online_time), my_label_style)
CreatePlayerGPSButton(table, player.name)
if online then
local label = AddLabel(table, nil, {"oarc-player-online"}, my_player_list_style)
label.style.font_color = {r=0.1, g=1, b=0.1}
else
AddLabel(table, nil, {"oarc-player-offline"}, my_player_list_offline_style)
end
end
end
---Display a GPS button for a specific location. (For the player list)
---@param container LuaGuiElement
---@param player_name string
---@return nil
function CreatePlayerGPSButton(container, player_name)
local gps_button = container.add {
type = "sprite-button",
sprite = "utility/gps_map_icon",
tags = {
action = "oarc_player_list_tab",
setting = "show_location",
player_name = player_name,
},
style = "slot_button",
tooltip = {"", {"oarc-player-list-tab-location-button-tooltip"}, " (", game.players[player_name].surface.name, ")"},
}
gps_button.style.width = 28
gps_button.style.height = 28
end
---Handle the gui click of the player list tab in the Oarc GUI.
---@param event EventData.on_gui_click
---@return nil
function PlayerListTabGuiClick(event)
if not event.element.valid then return end
local player = game.players[event.player_index]
local tags = event.element.tags
if (tags.action ~= "oarc_player_list_tab") then
return
end
-- Shows the player's current location on the map
if (tags.setting == "show_location") then
local player_name = tags.player_name --[[@as string]]
local target_player = game.players[player_name]
player.open_map(target_player.position, 0.05) -- TODO: Update this for spage age!
end
end

View File

@ -92,7 +92,7 @@ end
---Server info gui click event handler
---@param event EventData.on_gui_click
---@return nil
function ServerInfoGuiClick(event)
function ServerInfoTabGuiClick(event)
if not event.element.valid then return end
local player = game.players[event.player_index]
local tags = event.element.tags

View File

@ -36,7 +36,13 @@ function CreateSettingsControlsTab(tab_container, player)
scroll_pane_right.style.maximal_height = GENERIC_GUI_MAX_HEIGHT
scroll_pane_right.style.padding = 5
scroll_pane_right.style.left_margin = 2
CreateSurfaceSettingsSection(scroll_pane_right, player)
AddSpacerLine(scroll_pane_right)
if (player.admin) then
CreateSettingsExportSection(scroll_pane_right, player)
end
end
---Create the content for the mod settings section
@ -98,6 +104,53 @@ function CreateSurfaceSettingsSection(container, player)
end
---Create the content for the settings export section. Exports the entire global.ocfg table into a string.
---@param container LuaGuiElement
---@param player LuaPlayer
---@return nil
function CreateSettingsExportSection(container, player)
AddLabel(container, nil, { "oarc-settings-tab-title-export" }, my_label_header2_style)
local horizontal_flow = container.add {
type = "flow",
direction = "horizontal",
}
local export_button = horizontal_flow.add {
type = "button",
caption = { "oarc-settings-tab-export-button" },
style = "green_button",
tooltip = { "oarc-settings-tab-export-button-tooltip" },
tags = {
action = "oarc_settings_tab_right_pane",
setting = "oarc_settings_export"
},
}
local import_button = horizontal_flow.add {
type = "button",
caption = { "oarc-settings-tab-import-button" },
tooltip = { "oarc-settings-tab-import-button-tooltip" },
style = "red_button",
tags = {
action = "oarc_settings_tab_right_pane",
setting = "oarc_settings_import"
},
}
local export_textfield = container.add {
type = "textfield",
name = "export_textfield",
text = " ",
tags = {
action = "oarc_settings_tab_right_pane",
setting = "oarc_settings_textfield"
},
}
export_textfield.style.horizontally_stretchable = true
export_textfield.style.maximal_width = 500
end
---Handles the click event for the tab used by AddOarcGuiTab
---@param event EventData.on_gui_click
---@return nil
@ -105,7 +158,7 @@ function SettingsControlsTabGuiClick(event)
if not (event.element.valid) then return end
local gui_elem = event.element
if (gui_elem.tags.action ~= "oarc_settings_tab") then return end
if (gui_elem.tags.action ~= "oarc_settings_tab_left_pane") then return end
local index = gui_elem.tags.setting
local entry = OCFG_KEYS[index]
@ -121,7 +174,7 @@ function SettingsControlsTabGuiTextChanged(event)
if not (event.element.valid) then return end
local gui_elem = event.element
if (gui_elem.tags.action ~= "oarc_settings_tab") then return end
if (gui_elem.tags.action ~= "oarc_settings_tab_left_pane") then return end
local index = gui_elem.tags.setting
local value = gui_elem.text
local entry = OCFG_KEYS[index]
@ -141,7 +194,7 @@ function SettingsControlsTabGuiTextconfirmed(event)
if not (event.element.valid) then return end
local gui_elem = event.element
if (gui_elem.tags.action ~= "oarc_settings_tab") then return end
if (gui_elem.tags.action ~= "oarc_settings_tab_left_pane") then return end
local index = gui_elem.tags.setting
local value = gui_elem.text
local entry = OCFG_KEYS[index]
@ -260,7 +313,7 @@ function AddCheckboxSetting(tab_container, index, entry, enabled)
state = GetGlobalOarcConfigUsingKeyTable(entry.ocfg_keys),
enabled = enabled,
tooltip = { "mod-setting-description."..entry.mod_key },
tags = { action = "oarc_settings_tab", setting = index },
tags = { action = "oarc_settings_tab_left_pane", setting = index },
}
end
@ -293,7 +346,7 @@ function AddTextfieldSetting(tab_container, index, entry, enabled)
text = GetGlobalOarcConfigUsingKeyTable(entry.ocfg_keys),
enabled = enabled,
tooltip = tooltip,
tags = { action = "oarc_settings_tab", setting = index },
tags = { action = "oarc_settings_tab_left_pane", setting = index },
}
end
@ -340,7 +393,7 @@ function AddIntegerSetting(tab_container, index, entry, enabled)
text = GetGlobalOarcConfigUsingKeyTable(entry.ocfg_keys),
enabled = enabled,
tooltip = tooltip,
tags = { action = "oarc_settings_tab", setting = index },
tags = { action = "oarc_settings_tab_left_pane", setting = index },
}
textfield.style.width = 50
end
@ -389,7 +442,7 @@ function AddDoubleSetting(tab_container, index, entry, enabled)
text = string.format("%.2f", GetGlobalOarcConfigUsingKeyTable(entry.ocfg_keys)),
enabled = enabled,
tooltip = tooltip,
tags = { action = "oarc_settings_tab", setting = index },
tags = { action = "oarc_settings_tab_left_pane", setting = index },
}
textfield.style.width = 50
end
@ -431,7 +484,7 @@ function AddStringListDropdownSetting(tab_container, index, entry, enabled)
selected_index = selected_index,
enabled = enabled,
tooltip = { "mod-setting-description."..entry.mod_key },
tags = { action = "oarc_settings_tab", setting = index },
tags = { action = "oarc_settings_tab_left_pane", setting = index },
}
end
@ -448,7 +501,7 @@ function AddSurfaceCheckboxSetting(parent, surface_name, setting_name, state, ad
name = surface_name.."_"..setting_name,
type = "checkbox",
state = state,
tags = { action = "oarc_settings_tab_surfaces", setting = setting_name, surface = surface_name },
tags = { action = "oarc_settings_tab_right_pane", setting = setting_name, surface = surface_name },
enabled = admin,
tooltip = tooltip,
}
@ -461,11 +514,11 @@ function SettingsSurfaceControlsTabGuiClick(event)
if not (event.element.valid) then return end
local gui_elem = event.element
if (gui_elem.tags.action ~= "oarc_settings_tab_surfaces") then return end
if (gui_elem.tags.action ~= "oarc_settings_tab_right_pane") then return end
local setting_name = gui_elem.tags.setting
local surface_name = gui_elem.tags.surface --[[@as string]]
if (setting_name == "spawn_enabled") then
local surface_name = gui_elem.tags.surface --[[@as string]]
global.oarc_surfaces[surface_name] = gui_elem.state
if (#GetAllowedSurfaces() == 0) then
@ -473,7 +526,9 @@ function SettingsSurfaceControlsTabGuiClick(event)
global.oarc_surfaces[global.ocfg.gameplay.default_surface] = true
event.element.parent[global.ocfg.gameplay.default_surface.."_spawn_enabled"].state = true
end
elseif (setting_name == "regrowth_enabled") then
local surface_name = gui_elem.tags.surface --[[@as string]]
if (gui_elem.state) then
if not IsRegrowthEnabledOnSurface(surface_name) then
@ -484,7 +539,34 @@ function SettingsSurfaceControlsTabGuiClick(event)
RegrowthDisableSurface(surface_name)
end
end
elseif (setting_name == "oarc_settings_textfield") then
gui_elem.select_all() -- Select all text when clicked
elseif (setting_name == "oarc_settings_export") then
log("Exported settings!")
local export_textfield = gui_elem.parent.parent["export_textfield"]
export_textfield.text = serpent.line(global.ocfg, {compact = true, sparse = true})
elseif (setting_name == "oarc_settings_import") then
local player = game.players[event.player_index]
local export_textfield = gui_elem.parent.parent["export_textfield"]
local import_text = export_textfield.text
local ok, copy = serpent.load(import_text)
if (not ok) or (type(copy) ~= "table") or (next(copy) == nil) then
log("Error importing settings!")
player.print("Error importing settings!")
else
global.ocfg = table.deepcopy(copy)
ValidateSettings() -- Some basic validation, not 100% foolproof
SyncModSettingsToOCFG() -- Sync the mod settings.
log("Imported settings!")
player.print("Imported settings!")
OarcGuiRefreshContent(player)
end
end
end
@ -495,7 +577,7 @@ function SettingsControlsTabGuiSelectionStateChanged(event)
if not (event.element.valid) then return end
local gui_elem = event.element
if (gui_elem.tags.action ~= "oarc_settings_tab") then return end
if (gui_elem.tags.action ~= "oarc_settings_tab_left_pane") then return end
local index = gui_elem.tags.setting
local entry = OCFG_KEYS[index]

View File

@ -252,7 +252,7 @@ end
---Handle the gui checkboxes & radio buttons of the spawn control tab in the Oarc GUI.
---@param event EventData.on_gui_checked_state_changed
---@return nil
function SpawnCtrlGuiOptionsSelect(event)
function SpawnCtrlGuiOptionsCheckedStateChanged(event)
if not event.element.valid then return end
local player = game.players[event.player_index]
local tags = event.element.tags
@ -282,7 +282,7 @@ end
---Handle the gui click of the spawn control tab in the Oarc GUI.
---@param event EventData.on_gui_click
---@return nil
function SpawnCtrlGuiClick(event)
function SpawnCtrlTabGuiClick(event)
if not event.element.valid then return end
local player = game.players[event.player_index]
local tags = event.element.tags
@ -301,7 +301,7 @@ function SpawnCtrlGuiClick(event)
elseif (tags.setting == "show_location") then
local surface_name = tags.surface --[[@as string]]
local position = tags.position --[[@as MapPosition]]
player.open_map(position, 0.05)
player.open_map(position, 0.05) -- TODO: Update this for spage age!
player.print({"", { "oarc-spawn-gps-location" }, GetGPStext(surface_name, position)})
-- Accept or reject pending player join requests to a shared base

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,8 @@ require("lib/gui_tabs/server_info")
require("lib/gui_tabs/spawn_controls")
require("lib/gui_tabs/settings_controls")
require("lib/gui_tabs/mod_info_faq")
require("lib/gui_tabs/player_list")
require("lib/gui_tabs/surface_config")
--------------------------------------------------------------------------------
-- GUI Tab Handler
@ -18,17 +20,23 @@ OARC_SERVER_INFO_TAB_NAME = "server_info"
OARC_SPAWN_CTRL_TAB_NAME = "spawn_controls"
OARC_CONFIG_CTRL_TAB_NAME = "settings"
OARC_MOD_INFO_CTRL_TAB_NAME = "mod_info"
OARC_MOD_PLAYER_LIST_TAB_NAME = "player_list"
OARC_SURFACE_CONFIG_TAB_NAME = "surface_config"
OARC_SERVER_INFO_TAB_LOCALIZED = {"oarc-server-info-tab-title"}
OARC_SPAWN_CTRL_TAB_LOCALIZED = {"oarc-spawn-ctrls-tab-title"}
OARC_CONFIG_CTRL_TAB_LOCALIZED = {"oarc-settings-tab-title"}
OARC_MOD_INFO_CTRL_TAB_LOCALIZED = {"oarc-mod-info-tab-title"}
OARC_PLAYER_LIST_TAB_LOCALIZED = {"oarc-player-list-tab-title"}
OARC_SURFACE_CONFIG_TAB_LOCALIZED = {"oarc-surface-config-tab-title"}
local OARC_GUI_TAB_CONTENT_FUNCTIONS = {
[OARC_SERVER_INFO_TAB_NAME] = CreateServerInfoTab,
[OARC_SPAWN_CTRL_TAB_NAME] = CreateSpawnControlsTab,
[OARC_MOD_INFO_CTRL_TAB_NAME] = CreateModInfoTab,
[OARC_CONFIG_CTRL_TAB_NAME] = CreateSettingsControlsTab,
[OARC_MOD_PLAYER_LIST_TAB_NAME] = CreatePlayerListTab,
[OARC_SURFACE_CONFIG_TAB_NAME] = CreateSurfaceConfigTab,
}
---@param player LuaPlayer
@ -57,6 +65,16 @@ function InitOarcGuiTabs(player)
AddOarcGuiTab(player, OARC_CONFIG_CTRL_TAB_NAME, OARC_CONFIG_CTRL_TAB_LOCALIZED)
SetOarcGuiTabEnabled(player, OARC_CONFIG_CTRL_TAB_NAME, true)
-- Player list tab
AddOarcGuiTab(player, OARC_MOD_PLAYER_LIST_TAB_NAME, OARC_PLAYER_LIST_TAB_LOCALIZED)
SetOarcGuiTabEnabled(player, OARC_MOD_PLAYER_LIST_TAB_NAME, true)
-- Surface config tab
if (player.admin) then
AddOarcGuiTab(player, OARC_SURFACE_CONFIG_TAB_NAME, OARC_SURFACE_CONFIG_TAB_LOCALIZED)
SetOarcGuiTabEnabled(player, OARC_SURFACE_CONFIG_TAB_NAME, true)
end
HideOarcGui(player)
end
@ -142,12 +160,7 @@ function ClickOarcGuiButton(event)
end
end
---@param event EventData.on_gui_selected_tab_changed
---@return nil
function OarcGuiSelectedTabChanged(event)
if (event.element.name ~= "oarc_tabs") then return end
OarcGuiCreateContentOfTab(game.players[event.player_index])
end
---Set tab content to currently selected tab, clears all other tab content and refreshes the selected tab content!
---Safe to call just to refresh the current tab.
@ -298,9 +311,107 @@ function SwitchOarcGuiTab(player, tab_name)
end
end
--@param event EventData.on_gui_closed
---Completely destroys and recreates the OARC GUI for a player.
---@param player LuaPlayer
---@return nil
function RecreateOarcGui(player)
if (mod_gui.get_button_flow(player).oarc_button ~= nil) then
mod_gui.get_button_flow(player).oarc_button.destroy()
end
if (mod_gui.get_frame_flow(player)[OARC_GUI] ~= nil) then
mod_gui.get_frame_flow(player)[OARC_GUI].destroy()
end
InitOarcGuiTabs(player)
end
--[[
_____ _____ _ _ _____ _ _ _ _ _ ___ _ ___ ___ ___
| __\ \ / / __| \| |_ _| | || | /_\ | \| | \| | | __| _ \/ __|
| _| \ V /| _|| .` | | | | __ |/ _ \| .` | |) | |__| _|| /\__ \
|___| \_/ |___|_|\_| |_| |_||_/_/ \_\_|\_|___/|____|___|_|_\|___/
]]
---Handles the closing of the OARC GUI.
---@param event EventData.on_gui_closed
---@return nil
function OarcGuiClosed(event)
if (event.element and (event.element.name == "oarc_gui")) then
HideOarcGui(game.players[event.player_index])
end
end
---@param event EventData.on_gui_selected_tab_changed
---@return nil
function OarcGuiTabsSelectedTabChanged(event)
if (event.element.name ~= "oarc_tabs") then return end
OarcGuiCreateContentOfTab(game.players[event.player_index])
end
---All gui tabs click event handler
---@param event EventData.on_gui_click
---@return nil
function OarcGuiTabsClick(event)
if not event.element.valid then return end
ClickOarcGuiButton(event)
ServerInfoTabGuiClick(event)
SpawnCtrlTabGuiClick(event)
SettingsControlsTabGuiClick(event)
SettingsSurfaceControlsTabGuiClick(event)
PlayerListTabGuiClick(event)
SurfaceConfigTabGuiClick(event)
end
---All gui tabs on_gui_checked_state_changed event handler
---@param event EventData.on_gui_checked_state_changed
---@return nil
function OarcGuiTabsCheckedStateChanged(event)
if not event.element.valid then return end
SpawnCtrlGuiOptionsCheckedStateChanged(event)
end
---Handles the `on_gui_value_changed` event.
---@param event EventData.on_gui_value_changed
---@return nil
function OarcGuiTabsValueChanged(event)
if not event.element.valid then return end
SettingsControlsTabGuiValueChanged(event)
end
---Handles the `on_gui_selection_state_changed` event.
---@param event EventData.on_gui_selection_state_changed
---@return nil
function OarcGuiTabsSelectionStateChanged(event)
if not event.element.valid then return end
SettingsControlsTabGuiSelectionStateChanged(event)
SurfaceConfigTabGuiSelect(event)
end
---Handles the `on_gui_text_changed` event.
---@param event EventData.on_gui_text_changed
---@return nil
function OarcGuiTabsTextChanged(event)
if not event.element.valid then return end
SettingsControlsTabGuiTextChanged(event)
SurfaceConfigTabGuiTextChanged(event)
end
---Handles the `on_gui_confirmed` event.
---@param event EventData.on_gui_confirmed
---@return nil
function OarcGuiTabsConfirmed(event)
if not event.element.valid then return end
SettingsControlsTabGuiTextconfirmed(event)
SurfaceConfigTabGuiConfirmed(event)
end
---Handles the `on_gui_elem_changed` event.
---@param event EventData.on_gui_elem_changed
---@return nil
function OarcGuiTabsElemChanged(event)
if not event.element.valid then return end
SurfaceConfigTabGuiElemChanged(event)
end

View File

@ -58,6 +58,7 @@ my_note_style = {
top_padding = 0,
bottom_padding = 0
}
---@type LuaStyle
---@diagnostic disable-next-line: missing-fields
my_warning_style = {
@ -99,8 +100,8 @@ my_shared_item_list_fixed_width_style = {
---@diagnostic disable-next-line: missing-fields
my_player_list_admin_style = {
font = "default-semibold",
font_color = {r=1,g=0.5,b=0.5},
minimal_width = 200,
font_color = { r=0.9, g=0.7, b=0.3 },
-- minimal_width = 200,
top_padding = 0,
bottom_padding = 0,
single_line = false,
@ -109,7 +110,7 @@ my_player_list_admin_style = {
---@diagnostic disable-next-line: missing-fields
my_player_list_style = {
font = "default-semibold",
minimal_width = 200,
-- minimal_width = 200,
top_padding = 0,
bottom_padding = 0,
single_line = false,
@ -119,7 +120,7 @@ my_player_list_style = {
my_player_list_offline_style = {
-- font = "default-semibold",
font_color = {r=0.5,g=0.5,b=0.5},
minimal_width = 200,
-- minimal_width = 200,
top_padding = 0,
bottom_padding = 0,
single_line = false,

View File

@ -162,18 +162,6 @@ function ClearTestButtons(player)
end
end
function RecreateOarcGui(player)
if (mod_gui.get_button_flow(player).oarc_button ~= nil) then
mod_gui.get_button_flow(player).oarc_button.destroy()
end
if (mod_gui.get_frame_flow(player)[OARC_GUI] ~= nil) then
mod_gui.get_frame_flow(player)[OARC_GUI].destroy()
end
InitOarcGuiTabs(player)
end
function SetNauvisChunksGenerated()
local nauvis = game.surfaces["nauvis"]

View File

@ -208,12 +208,13 @@ end
---@return string
function FormatTimeHoursSecs(ticks)
local seconds = ticks / 60
local minutes = math.floor((seconds)/60)
local hours = math.floor((minutes)/60)
local minutes = math.floor(minutes - 60*hours)
local total_minutes = math.floor((seconds)/60)
local hours = math.floor((total_minutes)/60)
local minutes = math.floor(total_minutes - 60*hours)
return string.format("%dh:%02dm", hours, minutes)
end
-- -- Simple math clamp
-- function clamp(val, min, max)
-- if (val > max) then
@ -1345,10 +1346,10 @@ function CreateCropCircle(surface, centerPos, chunkArea, tileRadius, fillTile, m
end
end
-- 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
-- -- 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
-- Fill moat with water.
if (moat) then
@ -1357,12 +1358,27 @@ function CreateCropCircle(surface, centerPos, chunkArea, tileRadius, fillTile, m
-- land connections if the spawn is on or near land.
elseif ((distSqr < moat_radius_sqr) and (distSqr > tile_radius_sqr)) then
table.insert(dirtTiles, { name = "water", position = { i, j } })
--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
end
end
end
end
surface.set_tiles(dirtTiles)
--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 distSqr = math.floor((centerPos.x - i) ^ 2 + (centerPos.y - j) ^ 2)
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
end
end
end
---` spawn shape (handles land, trees and moat) (Curtesy of jvmguy)
@ -1398,10 +1414,10 @@ function CreateCropOctagon(surface, centerPos, chunkArea, tileRadius, fillTile,
end
end
-- 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
-- -- 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
-- Fill moat with water
if (moat) then
@ -1410,11 +1426,29 @@ function CreateCropOctagon(surface, centerPos, chunkArea, tileRadius, fillTile,
-- 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 } })
--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
end
end
end
end
surface.set_tiles(dirtTiles)
--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
end
---Square spawn shape (handles land, trees and moat)
@ -1449,10 +1483,10 @@ function CreateCropSquare(surface, centerPos, chunkArea, tileRadius, fillTile, m
end
end
-- 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
-- -- 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
-- Fill moat with water
if (moat) then
@ -1461,12 +1495,27 @@ function CreateCropSquare(surface, centerPos, chunkArea, tileRadius, fillTile, m
-- 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 } })
--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
end
end
end
end
surface.set_tiles(dirtTiles)
--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
end
---Add a circle of water

View File

@ -19,7 +19,7 @@ function OarcModifyEnemyGroup(event)
-- Check validity
if ((group == nil) or (group.command == nil) or not TableContains(ENEMY_FORCES_NAMES, group.force.name)) then
log("OarcModifyEnemyGroup ignoring INVALID group/command")
log("WARN - OarcModifyEnemyGroup ignoring INVALID group/command " .. serpent.block(group))
return
end
@ -72,7 +72,7 @@ function OarcModifyEnemyGroup(event)
-- This is unexpected, not sure under which conditions this would happen.
if (group.command.type == defines.command.attack_area) then
-- SendBroadcastMsg("OarcModifyEnemyGroup find_nearest_enemy attack_area FAILED!?!? " .. GetGPStext(group.surface.name, group.position) .. " Target: " .. GetGPStext(group.surface.name, group.command.destination))
log("ERROR - OarcModifyEnemyGroup find_nearest_enemy attack_area FAILED!?!?")
log("ERROR - OarcModifyEnemyGroup find_nearest_enemy attack_area FAILED!?!?" .. serpent.block(group))
-- for _,member in pairs(group.members) do
-- member.destroy()
-- end
@ -95,7 +95,7 @@ function OarcModifyEnemyGroup(event)
-- I don't think this should happen ever...
if ((target_player == nil) or (not target_player.valid)) then
-- SendBroadcastMsg("ERROR?? target_player nil/invalid " .. GetGPStext(group.surface.name, group.position) .. " Target: " .. GetGPStext(group.surface.name, target_entity.position))
log("ERROR - OarcModifyEnemyGroup target_player nil/invalid?")
log("ERROR - OarcModifyEnemyGroup target_player nil/invalid?" .. serpent.block(group))
-- for _,member in pairs(group.members) do
-- member.destroy()
-- end
@ -125,6 +125,6 @@ function OarcModifyEnemyGroup(event)
member.destroy()
end
-- SendBroadcastMsg("Enemy group deleted: " .. GetGPStext(group.surface.name, group.position) .. " Target: " .. GetGPStext(group.surface.name, target_entity.position) .. " " .. target_player.name)
log("OarcModifyEnemyGroup REMOVED enemy group since nobody was online? " .. target_player.name)
-- log("OarcModifyEnemyGroup REMOVED enemy group since nobody was online? " .. target_player.name)
end

View File

@ -493,6 +493,7 @@ function OarcRegrowthRemoveAllChunks()
-- If it is FORCE removal, then remove it regardless of pollution.
if (c_remove.force) then
game.surfaces[surface_name].delete_chunk(c_pos)
global.rg[surface_name].map[c_pos.x][c_pos.y] = nil
elseif (global.rg[surface_name].map[c_pos.x][c_pos.y] == REGROWTH_FLAG_REMOVAL) then
@ -506,9 +507,12 @@ function OarcRegrowthRemoveAllChunks()
global.rg[surface_name].map[c_pos.x][c_pos.y] = nil
end
end
-- If we hit here, the chunk was probably refreshed or something and so we don't want to delete it.
-- We won't check it again since we clear the removal list after this. This should be correct.
else
-- This should never happen, TODO: check if it does?
error("ERROR - OarcRegrowthRemoveAllChunks: Chunk not in map: " .. c_pos.x .. "," .. c_pos.y .. " on surface: " .. surface_name)
log("WARN - OarcRegrowthRemoveAllChunks: Chunk not in map: " .. c_pos.x .. "," .. c_pos.y .. " on surface: " .. surface_name)
end
-- Remove entry

View File

@ -132,11 +132,11 @@ function DowngradeAndReduceEnemiesOnChunkGenerate(event)
}
-- Make chunks near a spawn safe by removing enemies
if (util.distance(closest_spawn.position, chunkAreaCenter) < spawn_config.safe_area.safe_radius) then
if (util.distance(closest_spawn.position, chunkAreaCenter) < spawn_config.safe_area.safe_radius * CHUNK_SIZE) then
RemoveEnemiesInArea(surface, chunk_area)
-- Create a warning area with heavily reduced enemies
elseif (util.distance(closest_spawn.position, chunkAreaCenter) < spawn_config.safe_area.warn_radius) then
elseif (util.distance(closest_spawn.position, chunkAreaCenter) < spawn_config.safe_area.warn_radius * CHUNK_SIZE) then
-- TODO: Refactor this to reduce calls to find_entities_filtered!
ReduceEnemiesInArea(surface, chunk_area, spawn_config.safe_area.warn_reduction)
@ -144,7 +144,7 @@ function DowngradeAndReduceEnemiesOnChunkGenerate(event)
ConvertEnemiesToOtherForceInArea(surface, chunk_area, ENEMY_FORCE_EASY)
-- Create a third area with moderately reduced enemies
elseif (util.distance(closest_spawn.position, chunkAreaCenter) < spawn_config.safe_area.danger_radius) then
elseif (util.distance(closest_spawn.position, chunkAreaCenter) < spawn_config.safe_area.danger_radius * CHUNK_SIZE) then
-- TODO: Refactor this to reduce calls to find_entities_filtered!
ReduceEnemiesInArea(surface, chunk_area, spawn_config.safe_area.danger_reduction)
@ -287,15 +287,15 @@ function ChangeEnemySpawnersToOtherForceOnBuilt(event)
if (closest_spawn == nil) then return end
-- No enemies inside safe radius!
if (util.distance(enemy_pos, closest_spawn.position) < global.ocfg.surfaces_config[surface.name].spawn_config.safe_area.safe_radius) then
if (util.distance(enemy_pos, closest_spawn.position) < global.ocfg.surfaces_config[surface.name].spawn_config.safe_area.safe_radius * CHUNK_SIZE) then
event.entity.destroy()
-- Warn distance should be EASY
elseif (util.distance(enemy_pos, closest_spawn.position) < global.ocfg.surfaces_config[surface.name].spawn_config.safe_area.warn_radius) then
elseif (util.distance(enemy_pos, closest_spawn.position) < global.ocfg.surfaces_config[surface.name].spawn_config.safe_area.warn_radius * CHUNK_SIZE) then
event.entity.force = game.forces[ENEMY_FORCE_EASY]
-- Danger distance should be MEDIUM
elseif (util.distance(enemy_pos, closest_spawn.position) < global.ocfg.surfaces_config[surface.name].spawn_config.safe_area.danger_radius) then
elseif (util.distance(enemy_pos, closest_spawn.position) < global.ocfg.surfaces_config[surface.name].spawn_config.safe_area.danger_radius * CHUNK_SIZE) then
event.entity.force = game.forces[ENEMY_FORCE_MEDIUM]
-- Otherwise make sure they are on the base enemy force (stops easy enemies from spreading out too far).
@ -328,11 +328,11 @@ function ModifyEnemySpawnsNearPlayerStartingAreas(event)
end
-- No enemies inside safe radius!
if (util.distance(enemy_pos, closest_spawn.position) < global.ocfg.surfaces_config[surface.name].spawn_config.safe_area.safe_radius) then
if (util.distance(enemy_pos, closest_spawn.position) < global.ocfg.surfaces_config[surface.name].spawn_config.safe_area.safe_radius * CHUNK_SIZE) then
event.entity.destroy()
-- Warn distance is all SMALL only.
elseif (util.distance(enemy_pos, closest_spawn.position) < global.ocfg.surfaces_config[surface.name].spawn_config.safe_area.warn_radius) then
elseif (util.distance(enemy_pos, closest_spawn.position) < global.ocfg.surfaces_config[surface.name].spawn_config.safe_area.warn_radius * CHUNK_SIZE) then
if ((enemy_name == "biter-spawner") or (enemy_name == "spitter-spawner")) then
event.entity.force = game.forces["enemy-easy"]
@ -351,7 +351,7 @@ function ModifyEnemySpawnsNearPlayerStartingAreas(event)
end
-- Danger distance is MEDIUM max.
elseif (util.distance(enemy_pos, closest_spawn.position) < global.ocfg.surfaces_config[surface.name].spawn_config.safe_area.danger_radius) then
elseif (util.distance(enemy_pos, closest_spawn.position) < global.ocfg.surfaces_config[surface.name].spawn_config.safe_area.danger_radius * CHUNK_SIZE) then
if ((enemy_name == "big-biter") or (enemy_name == "behemoth-biter")) then
event.entity.destroy()
surface.create_entity { name = "medium-biter", position = enemy_pos, force = game.forces.enemy }

View File

@ -141,10 +141,10 @@ function SeparateSpawnsInitSurface(surface_name)
global.oarc_surfaces[surface_name] = (surface_name == global.ocfg.gameplay.default_surface)
end
-- Make sure it has a surface configuration entry
if (global.oarc_surfaces[surface_name] and global.ocfg.surfaces_config[surface_name] == nil) then
-- Make sure it has a surface configuration entry!
if (global.ocfg.surfaces_config[surface_name] == nil) then
log("Surface does NOT have a config entry, defaulting to nauvis entry for new surface: " .. surface_name)
global.ocfg.surfaces_config[surface_name] = global.ocfg.surfaces_config["nauvis"]
global.ocfg.surfaces_config[surface_name] = table.deepcopy(global.ocfg.surfaces_config["nauvis"])
end
end
@ -272,37 +272,62 @@ function GenerateStartingResources(surface, position)
local amount_mod = global.ocfg.resource_placement.amount_multiplier
-- Generate all resource tile patches
if (not global.ocfg.resource_placement.enabled) then
for r_name, r_data in pairs(global.ocfg.surfaces_config[surface.name].spawn_config.solid_resources --[[@as table<string, OarcConfigSolidResource>]]) do
local pos = { x = position.x + r_data.x_offset, y = position.y + r_data.y_offset }
GenerateResourcePatch(surface, r_name, r_data.size * size_mod, pos, r_data.amount * amount_mod)
end
-- Generate resources in random order around the spawn point. Tweak in config.lua
else
-- Generate resources in random order around the spawn point.
if global.ocfg.resource_placement.enabled then
if (global.ocfg.spawn_general.shape == SPAWN_SHAPE_CHOICE_CIRCLE) or (global.ocfg.spawn_general.shape == SPAWN_SHAPE_CHOICE_OCTAGON) then
PlaceResourcesInSemiCircle(surface, position, size_mod, amount_mod)
elseif (global.ocfg.spawn_general.shape == SPAWN_SHAPE_CHOICE_SQUARE) then
PlaceResourcesInSquare(surface, position, size_mod, amount_mod)
end
-- Generate resources using specified offsets if auto placement is disabled.
else
for r_name, r_data in pairs(global.ocfg.surfaces_config[surface.name].spawn_config.solid_resources --[[@as table<string, OarcConfigSolidResource>]]) do
local pos = { x = position.x + r_data.x_offset, y = position.y + r_data.y_offset }
GenerateResourcePatch(surface, r_name, r_data.size * size_mod, pos, r_data.amount * amount_mod)
end
end
-- Generate special fluid resource patches (oil)
-- Autoplace using spacing and vertical offset.
-- Reference position is the bottom of the spawn area.
local fluid_ref_pos = { x = position.x,
y = position.y + global.ocfg.spawn_general.spawn_radius_tiles }
for r_name, r_data in pairs(global.ocfg.surfaces_config[surface.name].spawn_config.fluid_resources --[[@as table<string, OarcConfigFluidResource>]]) do
local oil_patch_x = fluid_ref_pos.x + r_data.x_offset_start
local oil_patch_y = fluid_ref_pos.y + r_data.y_offset_start
for i = 1, r_data.num_patches do
surface.create_entity({
name = r_name,
amount = r_data.amount,
position = { oil_patch_x, oil_patch_y }
})
oil_patch_x = oil_patch_x + r_data.x_offset_next
oil_patch_y = oil_patch_y + r_data.y_offset_next
if global.ocfg.resource_placement.enabled then
local y_offset = global.ocfg.resource_placement.distance_to_edge
local spacing = 4 -- HARDCODED FLUID PATCH SPACING SIZE!
local fluid_ref_pos = { x = position.x, y = position.y + global.ocfg.spawn_general.spawn_radius_tiles - y_offset }
for r_name, r_data in pairs(global.ocfg.surfaces_config[surface.name].spawn_config.fluid_resources --[[@as table<string, OarcConfigFluidResource>]]) do
local oil_patch_x = fluid_ref_pos.x - (((r_data.num_patches-1) * spacing) / 2)
local oil_patch_y = fluid_ref_pos.y
for i = 1, r_data.num_patches do
surface.create_entity({
name = "crude-oil",
amount = r_data.amount,
position = { oil_patch_x, oil_patch_y }
})
oil_patch_x = oil_patch_x + spacing
end
fluid_ref_pos.y = fluid_ref_pos.y - spacing
end
-- This places using specified offsets if auto placement is disabled.
else
local fluid_ref_pos = { x = position.x, y = position.y + global.ocfg.spawn_general.spawn_radius_tiles }
for r_name, r_data in pairs(global.ocfg.surfaces_config[surface.name].spawn_config.fluid_resources --[[@as table<string, OarcConfigFluidResource>]]) do
local oil_patch_x = fluid_ref_pos.x + r_data.x_offset_start
local oil_patch_y = fluid_ref_pos.y + r_data.y_offset_start
for i = 1, r_data.num_patches do
surface.create_entity({
name = r_name,
amount = r_data.amount,
position = { oil_patch_x, oil_patch_y }
})
oil_patch_x = oil_patch_x + r_data.x_offset_next
oil_patch_y = oil_patch_y + r_data.y_offset_next
end
end
end
end
@ -329,7 +354,7 @@ function PlaceResourcesInSemiCircle(surface, position, size_mod, amount_mod)
-- This places resources in a semi-circle
local angle_offset = global.ocfg.resource_placement.angle_offset
local num_resources = table_size(global.ocfg.surfaces_config[surface.name].spawn_config.solid_resources)
local theta = ((global.ocfg.resource_placement.angle_final - global.ocfg.resource_placement.angle_offset) / num_resources);
local theta = ((global.ocfg.resource_placement.angle_final - global.ocfg.resource_placement.angle_offset) / (num_resources-1));
local count = 0
local radius = global.ocfg.spawn_general.spawn_radius_tiles - global.ocfg.resource_placement.distance_to_edge
@ -392,7 +417,7 @@ function SendPlayerToNewSpawnAndCreateIt(delayed_spawn)
local spawn_config = ocfg.surfaces_config[delayed_spawn.surface].spawn_config
-- DOUBLE CHECK and make sure the area is super safe.
ClearNearbyEnemies(delayed_spawn.position, spawn_config.safe_area.safe_radius,
ClearNearbyEnemies(delayed_spawn.position, spawn_config.safe_area.safe_radius * CHUNK_SIZE,
game.surfaces[delayed_spawn.surface])
-- Generate water strip only if we don't have a moat.
@ -624,7 +649,7 @@ function DowngradeResourcesDistanceBasedOnChunkGenerate(surface, chunkArea)
local distance = util.distance(chunkArea.left_top, closestSpawn.position)
-- Adjust multiplier to bring it in or out
local modifier = (distance / (global.ocfg.surfaces_config[surface.name].spawn_config.safe_area.danger_radius * 1)) ^ 3
local modifier = (distance / (global.ocfg.surfaces_config[surface.name].spawn_config.safe_area.danger_radius * CHUNK_SIZE * 1)) ^ 3
if modifier < 0.1 then modifier = 0.1 end
if modifier > 1 then return end
@ -737,7 +762,7 @@ function RemoveOrResetPlayer(player, remove_player)
end
end
---Searches all unique spawns for the primary one for a player.
---Searches all unique spawns for the primary one for a player. This will return null if they joined someeone else's spawn.
---@param player_name string
---@return OarcUniqueSpawn?
function FindPrimaryUniqueSpawn(player_name)
@ -748,6 +773,19 @@ function FindPrimaryUniqueSpawn(player_name)
end
end
---Find the primary home spawn of a player, if one exists. It could be they joined a shared spawn.
---@param player_name string
---@return OarcUniqueSpawn?
function FindPlayerHomeSpawn(player_name)
for _,spawns in pairs(global.unique_spawns) do
for _,spawn in pairs(spawns) do
if (spawn.primary) and ((spawn.host_name == player_name) or TableContains(spawn.joiners, player_name)) then
return spawn
end
end
end
end
---Searches all unique spawns for a list of secondary ones for a player.
---@param player_name string
---@return table<string, OarcUniqueSpawn> -- Indexed by surface name!

View File

@ -181,6 +181,8 @@ oarc-server-info-tab-title=Server Info
oarc-spawn-ctrls-tab-title=Spawn Controls
oarc-settings-tab-title=Settings
oarc-mod-info-tab-title=Mod Info
oarc-player-list-tab-title=Player List
oarc-surface-config-tab-title=Surface Config
oarc-server-info-tab-welcome-msg-title=Welcome Message
oarc-server-info-tab-discord-invite=Discord Invite:
@ -197,11 +199,24 @@ oarc-player-not-found=Player __1__ is not found?
oarc-player-about-to-spawn=Player __1__ is about to spawn, try again later.
oarc-player-none-selected=No player selected!
oarc-player-list-tab-column-header-player=Player
oarc-player-list-tab-column-header-force=Team (Force)
oarc-player-list-tab-column-header-surface=Home Surface
oarc-player-list-tab-column-header-time-player=Time Played
oarc-player-list-tab-column-header-location=Location
oarc-player-list-tab-column-header-status=Status
oarc-player-online=Online
oarc-player-offline=Offline
oarc-player-waiting-to-spawn=Not Spawned Yet
oarc-player-list-tab-location-button-tooltip=Click to view map location.
oarc-settings-tab-title-mod-settings=Mod Settings
oarc-settings-tab-admin-warning=You are an admin. Changing these settings late in the game may cause issues!\nChanging settings will not modify existing spawns. BE CAREFUL!
oarc-settings-tab-player-warning=You are not an admin. These settings are read-only for you.
oarc-settings-tab-description=This tab contains the same mod settings in the mod settings menu AND some additional settings that can only be changed in game.
oarc-settings-tab-description=This tab contains the same mod settings in the mod settings menu AND some additional settings that can only be changed in game. Check the Surface Config tab for surface-specific settings.
oarc-settings-tab-text-field-enter-tooltip=[color=red]You must press ENTER after typing in a text field to save the value![/color]
oarc-settings-tab-title-surface=Surface Settings
oarc-settings-tab-surface-checkbox-tooltip=Enabling this will allow custom spawn areas (the main feature of this mod) on this surface. You need at least one of these enabled for the mod to work.
@ -209,7 +224,11 @@ oarc-settings-tab-surface-regrowth-checkbox-tooltip=Enabling this will allow the
oarc-settings-tab-surface-column-header=Surface
oarc-settings-tab-surface-spawning-enabled=Custom Spawns
oarc-settings-tab-surface-regrowth-enabled=Regrowth
oarc-settings-tab-title-export=Import/Export Settings
oarc-settings-tab-export-button=Export
oarc-settings-tab-export-button-tooltip=Exports all settings to a string that can be shared with other players or saved for later.
oarc-settings-tab-import-button=Import
oarc-settings-tab-import-button-tooltip=Imports settings from a string. This will overwrite all settings with the imported values!
oarc-settings-section-header-server-info=Server Info
oarc-settings-section-header-gameplay=Gameplay

View File

@ -0,0 +1,16 @@
-- Migrate safe_radius, warn_radius and danger_radius to be chunks instead of tiles.
for surface_name, surface_config in pairs(global.ocfg.surfaces_config) do
-- If the safe_area is greater than 3 chunks, then it's likely in chunks...
if surface_config.spawn_config.safe_area.safe_radius >= (3 * 32) then
log("Oarc-mod: Migrating safe_radius, warn_radius and danger_radius to be chunks instead of tiles.")
local safe_radius = surface_config.spawn_config.safe_area.safe_radius
global.ocfg.surfaces_config[surface_name].spawn_config.safe_area.safe_radius = math.ceil(safe_radius / 32)
local warn_radius = surface_config.spawn_config.safe_area.warn_radius
global.ocfg.surfaces_config[surface_name].spawn_config.safe_area.warn_radius = math.ceil(warn_radius / 32)
local danger_radius = surface_config.spawn_config.safe_area.danger_radius
global.ocfg.surfaces_config[surface_name].spawn_config.safe_area.danger_radius = math.ceil(danger_radius / 32)
end
end

View File

@ -80,9 +80,9 @@ data:extend({
type = "int-setting",
name = "oarc-mod-minimum-distance-to-existing-chunks",
setting_type = "runtime-global",
default_value = 10,
default_value = 20,
minimum_value = 5,
maximum_value = 25,
maximum_value = 50,
order = "c1"
},
{
@ -341,7 +341,7 @@ data:extend({
type = "double-setting",
name = "oarc-mod-resource-placement-angle-offset",
setting_type = "runtime-global",
default_value = 2.32,
default_value = 2.09,
minimum_value = 0,
maximum_value = 6.28,
order = "i3"
@ -350,7 +350,7 @@ data:extend({
type = "double-setting",
name = "oarc-mod-resource-placement-angle-final",
setting_type = "runtime-global",
default_value = 4.46,
default_value = 4.18,
minimum_value = 0,
maximum_value = 6.28,
order = "i4"