mirror of
https://github.com/Oarcinae/FactorioScenarioMultiplayerSpawn.git
synced 2025-01-05 22:53:48 +02:00
Merge pull request #200 from Oarcinae/dev_2.0.4_pre_sa_release
2.0.4 Pre Space Age Release
This commit is contained in:
commit
6e5788b29a
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1 @@
|
||||
releases
|
||||
scenarios
|
@ -1,4 +1,20 @@
|
||||
---------------------------------------------------------------------------------------------------
|
||||
Version: 2.0.4
|
||||
Date: 2024-10-19
|
||||
Bugfixes:
|
||||
- Reverting the enemy scaling that introduced new enemy forces due to being a suspected source of desyncs.
|
||||
- Fixed an issue where unexpected chunks were being removed when abandoned bases were being cleaned up, even though regrowth was disabled.
|
||||
- Fixed a bug where if you use the custom scenario, it would ignore mod settings even when not overriding them.
|
||||
- Removing accidentally included scenario from the mod package. (Gitignore doesn't stop it from being included in the mod package when using FMTK.)
|
||||
Minor Features:
|
||||
- Added a section in the custom GUI for settings that don't appear in the mod settings, but might still be good to have easy access to. Currently, this just adds a couple of settings related to coins generated by killing enemies. I may expand this in the future.
|
||||
- Added settings for enabling primary and secondary spawning per surface in the in game GUI settings. Previously you couldn't choose which surfaces allow primary and which allow secondary spawning. Now you can pick which surfaces players are allowed to start on, and which surfaces will generate custom secondary spawns when a player first travels to them. This will be useful for space age support.
|
||||
Changes:
|
||||
- Re-added the code that turns on the research queue by default. This is the default vanilla behavior.
|
||||
- Changed resource placement to use degrees instead of radians. Only applies to circle/octagon bases. Should be more intuitive. Watch out for this change if you modified the resource placement mod settings.
|
||||
Info:
|
||||
- Due to the nature of the changes, I would recommend restarting fresh to avoid potential migration issues. I did add migration scripts, but can't guarantee they will work perfectly in all cases.
|
||||
---------------------------------------------------------------------------------------------------
|
||||
Version: 2.0.3
|
||||
Date: 2024-10-13
|
||||
Major Features:
|
||||
|
19
control.lua
19
control.lua
@ -134,10 +134,6 @@ script.on_event(defines.events.on_tick, function(event)
|
||||
RegrowthOnTick()
|
||||
end
|
||||
RegrowthForceRemovalOnTick() -- Allows for abandoned base cleanup without regrowth enabled.
|
||||
|
||||
if global.ocfg.gameplay.modified_enemy_spawning then
|
||||
RestrictEnemyEvolutionOnTick()
|
||||
end
|
||||
end)
|
||||
|
||||
----------------------------------------
|
||||
@ -192,10 +188,6 @@ script.on_event(defines.events.on_built_entity, function(event)
|
||||
|
||||
-- For tracking spidertrons...
|
||||
RegrowthOnBuiltEntity(event)
|
||||
|
||||
-- if global.ocfg.enable_anti_grief then
|
||||
-- SetItemBlueprintTimeToLive(event)
|
||||
-- end
|
||||
end)
|
||||
|
||||
script.on_event(defines.events.on_robot_built_entity, function (event)
|
||||
@ -236,15 +228,14 @@ end)
|
||||
-- This is where I modify biter spawning based on location and other factors.
|
||||
----------------------------------------
|
||||
script.on_event(defines.events.on_entity_spawned, function(event)
|
||||
-- if (global.ocfg.gameplay.modified_enemy_spawning) then
|
||||
-- ModifyEnemySpawnsNearPlayerStartingAreas(event)
|
||||
-- end
|
||||
if (global.ocfg.gameplay.modified_enemy_spawning) then
|
||||
ModifyEnemySpawnsNearPlayerStartingAreas(event)
|
||||
end
|
||||
end)
|
||||
|
||||
script.on_event(defines.events.on_biter_base_built, function(event)
|
||||
if (global.ocfg.gameplay.modified_enemy_spawning) then
|
||||
-- ModifyEnemySpawnsNearPlayerStartingAreas(event)
|
||||
ChangeEnemySpawnersToOtherForceOnBuilt(event)
|
||||
ModifyEnemySpawnsNearPlayerStartingAreas(event)
|
||||
end
|
||||
end)
|
||||
|
||||
@ -339,7 +330,7 @@ end)
|
||||
local oarc_mod_interface =
|
||||
{
|
||||
get_mod_settings = function()
|
||||
return OCFG
|
||||
return global.ocfg
|
||||
end
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "oarc-mod",
|
||||
"version": "2.0.3",
|
||||
"version": "2.0.4",
|
||||
"factorio_version": "1.1",
|
||||
"title": "Oarc Multiplayer Spawn",
|
||||
"author": "Oarcinae",
|
||||
|
@ -302,7 +302,7 @@ OCFG = {
|
||||
-- Default setting for enabling spawning on other surfaces other than the default_surface.
|
||||
-- This is a STARTUP setting, so it can't be changed in game!!
|
||||
-- This is a STARTUP setting, so it can't be changed in game!!
|
||||
default_allow_spawning_on_other_surfaces = true,
|
||||
default_allow_spawning_on_other_surfaces = false,
|
||||
|
||||
-- The name of the main force.
|
||||
-- This is a STARTUP setting, so it can't be changed in game!!
|
||||
@ -375,11 +375,11 @@ OCFG = {
|
||||
-- If you're trying out the vanilla spawning, you might want to disable this.
|
||||
modified_enemy_spawning = true,
|
||||
|
||||
-- Enemy evolution factor for the easy force (inside warning area).
|
||||
modified_enemy_easy_evo = 0.0,
|
||||
-- -- Enemy evolution factor for the easy force (inside warning area).
|
||||
-- modified_enemy_easy_evo = 0.0,
|
||||
|
||||
-- Enemy evolution factor for the medium force (inside danger area).
|
||||
modified_enemy_medium_evo = 0.3,
|
||||
-- -- Enemy evolution factor for the medium force (inside danger area).
|
||||
-- modified_enemy_medium_evo = 0.3,
|
||||
|
||||
-- Require playes to be online for at least X minutes
|
||||
-- Else their character is removed and their spawn point is freed up for use
|
||||
@ -456,14 +456,14 @@ OCFG = {
|
||||
-- Distance in tiles from the edge of spawn that resources are placed. Only applicable for circular spawns.
|
||||
distance_to_edge = 20,
|
||||
|
||||
-- At what angle (in radians) do resources start.
|
||||
-- At what angle (in degrees) do resources start.
|
||||
-- 0 means starts directly east.
|
||||
-- Resources are placed clockwise from there.
|
||||
angle_offset = 2.09, -- Approx SSW.
|
||||
angle_offset = 120, -- Approx SSW.
|
||||
|
||||
-- At what andle do we place the last resource.
|
||||
-- angle_offset and angle_final determine spacing and placement.
|
||||
angle_final = 4.18, -- Approx NNW.
|
||||
angle_final = 240, -- Approx NNW.
|
||||
|
||||
-- Vertical offset in tiles for the deposit resource placement. Starting from top-left corner.
|
||||
-- Only applicable for square spawns.
|
||||
@ -590,8 +590,8 @@ OCFG = {
|
||||
---@field enable_secondary_spawns boolean Enable secondary spawns for players. This automatically creates a new spawn point when they first move to a separate spawns enabled surface.
|
||||
---@field scale_resources_around_spawns boolean Scales resources so that even if you spawn "far away" from the center of the map, resources near to your spawn point scale so you aren't surrounded by 100M patches or something. This is useful depending on what map gen settings you pick.
|
||||
---@field modified_enemy_spawning boolean Adjust enemy spawning based on distance to spawns. All it does it make things more balanced based on your distance and makes the game a little easier. No behemoth worms everywhere just because you spawned far away.
|
||||
---@field modified_enemy_easy_evo number Enemy evolution factor for the easy force (inside warning area).
|
||||
---@field modified_enemy_medium_evo number Enemy evolution factor for the medium force (inside danger area).
|
||||
----@field modified_enemy_easy_evo number Enemy evolution factor for the easy force (inside warning area).
|
||||
----@field modified_enemy_medium_evo number Enemy evolution factor for the medium force (inside danger area).
|
||||
---@field minimum_online_time number Require playes to be online for at least X minutes Else their character is removed and their spawn point is freed up for use
|
||||
---@field respawn_cooldown_min number Respawn cooldown in minutes.
|
||||
---@field enable_shared_power boolean Enable shared power between bases. Creates a special power pole for cross surface connections.
|
||||
@ -648,8 +648,8 @@ OCFG = {
|
||||
---@class OarcConfigSpawnResourcePlacementSettings
|
||||
---@field enabled boolean Autoplace resources. This will ignore the fixed x_offset/y_offset values in solid_resources. Only works for solid_resources at the moment, not oil patches/water.
|
||||
---@field distance_to_edge number Distance in tiles from the edge of spawn that resources are placed. Only applicable for circular spawns.
|
||||
---@field angle_offset number At what angle (in radians) do resources start. 0 means starts directly east. Resources are placed clockwise from there. Only applicable for circular spawns.
|
||||
---@field angle_final number At what andle do we place the last resource. angle_offset and angle_final determine spacing and placement. Only applicable for circular spawns.
|
||||
---@field angle_offset integer At what angle (in degrees) do resources start. 0 means starts directly east. Resources are placed clockwise from there. Only applicable for circular spawns.
|
||||
---@field angle_final integer At what andle do we place the last resource. angle_offset and angle_final determine spacing and placement. Only applicable for circular spawns.
|
||||
---@field vertical_offset number Vertical offset in tiles for the deposit resource placement. Only applicable for square spawns.
|
||||
---@field horizontal_offset number Horizontal offset in tiles for the deposit resource placement. Only applicable for square spawns.
|
||||
---@field linear_spacing number Spacing between resource deposits in tiles. Only applicable for square spawns.
|
||||
|
@ -2,7 +2,7 @@
|
||||
-- DON'T JUDGE ME! I wanted to try and make a nice in game setting GUI since the native mod settings GUI is so limited.
|
||||
|
||||
---Provides a way to look up the config settings key from the mod settings key.
|
||||
---@alias OarcSettingsLookup { mod_key: string, ocfg_keys: table<integer, string>, type: string, text: LocalisedString? }
|
||||
---@alias OarcSettingsLookup { mod_key: string, ocfg_keys: table<integer, string>, type: string, text: LocalisedString?, caption: LocalisedString?, tooltip: LocalisedString? }
|
||||
|
||||
---@type table<string, OarcSettingsLookup>
|
||||
OCFG_KEYS =
|
||||
@ -33,8 +33,8 @@ OCFG_KEYS =
|
||||
["gameplay.enable_offline_protection"] = {mod_key = "oarc-mod-enable-offline-protection" , ocfg_keys = {"gameplay", "enable_offline_protection"}, type = "boolean"},
|
||||
["gameplay.scale_resources_around_spawns"] = {mod_key = "oarc-mod-scale-resources-around-spawns" , ocfg_keys = {"gameplay", "scale_resources_around_spawns"}, type = "boolean"},
|
||||
["gameplay.modified_enemy_spawning"] = {mod_key = "oarc-mod-modified-enemy-spawning" , ocfg_keys = {"gameplay", "modified_enemy_spawning"}, type = "boolean"},
|
||||
["gameplay.modified_enemy_easy_evo"] = {mod_key = "oarc-mod-modified-enemy-easy-evo" , ocfg_keys = {"gameplay", "modified_enemy_easy_evo"}, type = "double"},
|
||||
["gameplay.modified_enemy_medium_evo"] = {mod_key = "oarc-mod-modified-enemy-medium-evo" , ocfg_keys = {"gameplay", "modified_enemy_medium_evo"}, type = "double"},
|
||||
-- ["gameplay.modified_enemy_easy_evo"] = {mod_key = "oarc-mod-modified-enemy-easy-evo" , ocfg_keys = {"gameplay", "modified_enemy_easy_evo"}, type = "double"},
|
||||
-- ["gameplay.modified_enemy_medium_evo"] = {mod_key = "oarc-mod-modified-enemy-medium-evo" , ocfg_keys = {"gameplay", "modified_enemy_medium_evo"}, type = "double"},
|
||||
|
||||
["gameplay_misc_SUBHEADER"] = {mod_key = "" , ocfg_keys = {""}, type = "subheader", text = {"oarc-settings-section-subheader-gameplay-misc"}},
|
||||
["gameplay.enable_friendly_fire"] = {mod_key = "oarc-mod-enable-friendly-fire" , ocfg_keys = {"gameplay", "enable_friendly_fire"}, type = "boolean"},
|
||||
@ -48,7 +48,6 @@ OCFG_KEYS =
|
||||
["gameplay.enable_shared_chest"] = {mod_key = "oarc-mod-enable-shared-chest" , ocfg_keys = {"gameplay", "enable_shared_chest"}, type = "boolean"},
|
||||
["gameplay.enable_coin_shop"] = {mod_key = "oarc-mod-enable-coin-shop" , ocfg_keys = {"gameplay", "enable_coin_shop"}, type = "boolean"},
|
||||
|
||||
|
||||
["regrowth_HEADER"] = {mod_key = "" , ocfg_keys = {""}, type = "header", text = {"oarc-settings-section-header-regrowth"}},
|
||||
["regrowth_SUBHEADER"] = {mod_key = "" , ocfg_keys = {""}, type = "subheader", text = {"oarc-settings-section-subheader-regrowth-warning"}},
|
||||
["regrowth.enable_regrowth"] = {mod_key = "oarc-mod-enable-regrowth" , ocfg_keys = {"regrowth", "enable_regrowth"}, type = "boolean"},
|
||||
@ -71,13 +70,19 @@ OCFG_KEYS =
|
||||
|
||||
["resource_placement_circle_SUBHEADER"] = {mod_key = "" , ocfg_keys = {""}, type = "subheader", text = {"oarc-settings-section-subheader-resource-placement-circular"}},
|
||||
["resource_placement.distance_to_edge"] = {mod_key = "oarc-mod-resource-placement-distance-to-edge" , ocfg_keys = {"resource_placement", "distance_to_edge"}, type = "integer"},
|
||||
["resource_placement.angle_offset"] = {mod_key = "oarc-mod-resource-placement-angle-offset" , ocfg_keys = {"resource_placement", "angle_offset"}, type = "double"},
|
||||
["resource_placement.angle_final"] = {mod_key = "oarc-mod-resource-placement-angle-final" , ocfg_keys = {"resource_placement", "angle_final"}, type = "double"},
|
||||
["resource_placement.angle_offset"] = {mod_key = "oarc-mod-resource-placement-degrees-offset" , ocfg_keys = {"resource_placement", "angle_offset"}, type = "integer"},
|
||||
["resource_placement.angle_final"] = {mod_key = "oarc-mod-resource-placement-degrees-final" , ocfg_keys = {"resource_placement", "angle_final"}, type = "integer"},
|
||||
|
||||
["resource_placement_square_SUBHEADER"] = {mod_key = "" , ocfg_keys = {""}, type = "subheader", text = {"oarc-settings-section-subheader-resource-placement-square"}},
|
||||
["resource_placement.vertical_offset"] = {mod_key = "oarc-mod-resource-placement-vertical-offset" , ocfg_keys = {"resource_placement", "vertical_offset"}, type = "integer"},
|
||||
["resource_placement.horizontal_offset"] = {mod_key = "oarc-mod-resource-placement-horizontal-offset" , ocfg_keys = {"resource_placement", "horizontal_offset"}, type = "integer"},
|
||||
["resource_placement.linear_spacing"] = {mod_key = "oarc-mod-resource-placement-linear-spacing" , ocfg_keys = {"resource_placement", "linear_spacing"}, type = "integer"},
|
||||
|
||||
-- These are settings that aren't included in the games mod settings but are still nice to have easy access to.
|
||||
["non_mod_settings_HEADER"] = {mod_key = "" , ocfg_keys = {""}, type = "header", text = "Additional Settings (Not available in the mod settings menu.)"},
|
||||
["coin_generation_SUBHEADER"] = {mod_key = "" , ocfg_keys = {""}, type = "subheader", text = "Coin Generation"},
|
||||
["coin_generation.enabled"] = {mod_key = "" , ocfg_keys = {"coin_generation", "enabled"}, type = "boolean", caption = "Coin Generation", tooltip = "Enemies drop coins when killed."},
|
||||
["coin_generation.auto_decon_coins"] = {mod_key = "" , ocfg_keys = {"coin_generation", "auto_decon_coins"}, type = "boolean", caption = "Auto Decon Coins", tooltip = "Automatically marks coins dropped by enemies for deconstruction so robots will pick them up."},
|
||||
}
|
||||
|
||||
---Easy reverse lookup for mod settings keys.
|
||||
@ -118,7 +123,7 @@ function ValidateAndLoadConfig()
|
||||
-- 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.
|
||||
for _,entry in pairs(OCFG_KEYS) do
|
||||
if (entry.type ~= "header") and (entry.type ~= "subheader") then
|
||||
if (entry.mod_key ~= "") then
|
||||
local mod_key = entry.mod_key
|
||||
local oarc_key = entry.ocfg_keys
|
||||
local mod_value = game.mod_setting_prototypes[mod_key].default_value
|
||||
@ -249,7 +254,7 @@ function CacheModSettings()
|
||||
-- Copy the global settings from the mod settings.
|
||||
-- Find the matching OARC setting and update it.
|
||||
for _,entry in pairs(OCFG_KEYS) do
|
||||
if (entry.type ~= "header") and (entry.type ~= "subheader") then
|
||||
if (entry.mod_key ~= "") then
|
||||
SetGlobalOarcConfigUsingKeyTable(entry.ocfg_keys, settings.global[entry.mod_key].value)
|
||||
end
|
||||
end
|
||||
@ -281,7 +286,7 @@ 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
|
||||
if (entry.mod_key ~= "") then
|
||||
local mod_key = entry.mod_key
|
||||
local oarc_key = entry.ocfg_keys
|
||||
local scenario_value = GetGlobalOarcConfigUsingKeyTable(oarc_key)
|
||||
@ -384,7 +389,7 @@ function ApplyRuntimeChanges(oarc_setting_index)
|
||||
---Handle changing enable_shared_team_vision
|
||||
if (oarc_setting_index == "gameplay.enable_shared_team_vision") then
|
||||
for _,force in pairs(game.forces) do
|
||||
if (force.name ~= "neutral") and (force.name ~= "enemy") and (force.name ~= "enemy-easy") then
|
||||
if (not TableContains(ENEMY_FORCES_NAMES_INCL_NEUTRAL, force.name)) then
|
||||
force.share_chart = global.ocfg.gameplay.enable_shared_team_vision
|
||||
end
|
||||
end
|
||||
@ -392,7 +397,7 @@ function ApplyRuntimeChanges(oarc_setting_index)
|
||||
---Handle changing enable_friendly_fire
|
||||
elseif (oarc_setting_index == "gameplay.enable_friendly_fire") then
|
||||
for _,force in pairs(game.forces) do
|
||||
if (force.name ~= "neutral") and (force.name ~= "enemy") and (force.name ~= "enemy-easy") then
|
||||
if (not TableContains(ENEMY_FORCES_NAMES_INCL_NEUTRAL, force.name)) then
|
||||
force.friendly_fire = global.ocfg.gameplay.enable_friendly_fire
|
||||
end
|
||||
end
|
||||
|
@ -36,7 +36,7 @@ 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)
|
||||
|
||||
@ -83,20 +83,24 @@ function CreateSurfaceSettingsSection(container, player)
|
||||
local surface_table = container.add {
|
||||
type = "table",
|
||||
name = "surface_table",
|
||||
column_count = 3,
|
||||
column_count = 4,
|
||||
style = "bordered_table",
|
||||
}
|
||||
|
||||
--- Add the header row
|
||||
AddLabel(surface_table, nil, {"oarc-settings-tab-surface-column-header"}, "caption_label")
|
||||
AddLabel(surface_table, nil, {"oarc-settings-tab-surface-spawning-enabled"}, "caption_label")
|
||||
AddLabel(surface_table, nil, {"oarc-settings-tab-surface-secondary-enabled"}, "caption_label")
|
||||
AddLabel(surface_table, nil, {"oarc-settings-tab-surface-regrowth-enabled"}, "caption_label")
|
||||
|
||||
--- Add the rows
|
||||
for name, allowed in pairs(global.oarc_surfaces --[[@as table<string, boolean>]]) do
|
||||
for name, allowed in pairs(global.oarc_surfaces) do
|
||||
AddLabel(surface_table, nil, name, my_label_style)
|
||||
AddSurfaceCheckboxSetting(surface_table, name, "spawn_enabled", allowed, player.admin,
|
||||
AddSurfaceCheckboxSetting(surface_table, name, "spawn_enabled", allowed.primary, player.admin,
|
||||
{ "oarc-settings-tab-surface-checkbox-tooltip" })
|
||||
AddSurfaceCheckboxSetting(surface_table, name, "secondary_enabled", allowed.secondary, player.admin,
|
||||
{ "oarc-settings-tab-surface-secondary-checkbox-tooltip" })
|
||||
|
||||
local regrowth_enabled = TableContains(global.rg.active_surfaces, name)
|
||||
AddSurfaceCheckboxSetting(surface_table, name, "regrowth_enabled", regrowth_enabled, player.admin,
|
||||
{"oarc-settings-tab-surface-regrowth-checkbox-tooltip"})
|
||||
@ -163,7 +167,11 @@ function SettingsControlsTabGuiClick(event)
|
||||
|
||||
local entry = OCFG_KEYS[index]
|
||||
if (entry.type == "boolean") then
|
||||
settings.global[entry.mod_key] = { value = gui_elem.state }
|
||||
if (entry.mod_key ~= "") then
|
||||
settings.global[entry.mod_key] = { value = gui_elem.state }
|
||||
else
|
||||
SetGlobalOarcConfigUsingKeyTable(entry.ocfg_keys, gui_elem.state)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -307,16 +315,33 @@ end
|
||||
---@param enabled boolean
|
||||
---@return nil
|
||||
function AddCheckboxSetting(tab_container, index, entry, enabled)
|
||||
local caption, tooltip = GetCaptionAndTooltip(entry)
|
||||
tab_container.add{
|
||||
type = "checkbox",
|
||||
caption = { "mod-setting-name."..entry.mod_key },
|
||||
caption = caption,
|
||||
state = GetGlobalOarcConfigUsingKeyTable(entry.ocfg_keys),
|
||||
enabled = enabled,
|
||||
tooltip = { "mod-setting-description."..entry.mod_key },
|
||||
tooltip = tooltip,
|
||||
tags = { action = "oarc_settings_tab_left_pane", setting = index },
|
||||
}
|
||||
end
|
||||
|
||||
---Gets the caption and tooltip for a setting entry whether it is a mod setting or not.
|
||||
---@param entry OarcSettingsLookup
|
||||
---@return LocalisedString, LocalisedString
|
||||
function GetCaptionAndTooltip(entry)
|
||||
local caption
|
||||
local tooltip
|
||||
if (entry.mod_key == "") then
|
||||
caption = entry.caption
|
||||
tooltip = entry.tooltip
|
||||
else
|
||||
caption = { "mod-setting-name."..entry.mod_key }
|
||||
tooltip = { "mod-setting-description."..entry.mod_key }
|
||||
end
|
||||
return caption, tooltip
|
||||
end
|
||||
|
||||
---Creates a textfield setting
|
||||
---@param tab_container LuaGuiElement
|
||||
---@param index string
|
||||
@ -519,14 +544,19 @@ function SettingsSurfaceControlsTabGuiClick(event)
|
||||
|
||||
if (setting_name == "spawn_enabled") then
|
||||
local surface_name = gui_elem.tags.surface --[[@as string]]
|
||||
global.oarc_surfaces[surface_name] = gui_elem.state
|
||||
global.oarc_surfaces[surface_name].primary = gui_elem.state
|
||||
|
||||
if (#GetAllowedSurfaces() == 0) then
|
||||
log("Warning - GetAllowedSurfaces() - No surfaces found! Forcing default surface!")
|
||||
global.oarc_surfaces[global.ocfg.gameplay.default_surface] = true
|
||||
global.oarc_surfaces[global.ocfg.gameplay.default_surface].primary = true
|
||||
event.element.parent[global.ocfg.gameplay.default_surface.."_spawn_enabled"].state = true
|
||||
end
|
||||
|
||||
|
||||
elseif (setting_name == "secondary_enabled") then
|
||||
local surface_name = gui_elem.tags.surface --[[@as string]]
|
||||
global.oarc_surfaces[surface_name].secondary = gui_elem.state
|
||||
|
||||
|
||||
elseif (setting_name == "regrowth_enabled") then
|
||||
local surface_name = gui_elem.tags.surface --[[@as string]]
|
||||
|
||||
|
@ -553,7 +553,7 @@ function ConfigurePlayerForceRelationships(cease_fire, friends)
|
||||
local player_forces = {}
|
||||
|
||||
for name, force in pairs(game.forces) do
|
||||
if name ~= "neutral" and name ~= ABANDONED_FORCE_NAME and not TableContains(ENEMY_FORCES_NAMES, name) then
|
||||
if name ~= ABANDONED_FORCE_NAME and not TableContains(ENEMY_FORCES_NAMES_INCL_NEUTRAL, name) then
|
||||
table.insert(player_forces, force)
|
||||
end
|
||||
end
|
||||
|
@ -18,33 +18,25 @@ function OarcModifyEnemyGroup(event)
|
||||
local group = event.group
|
||||
|
||||
-- Check validity
|
||||
if ((group == nil) or (group.command == nil) or not TableContains(ENEMY_FORCES_NAMES, group.force.name)) then
|
||||
if ((group.command == nil) or (group.force.name ~= "enemy")) then
|
||||
log("WARN - OarcModifyEnemyGroup ignoring INVALID group/command " .. serpent.block(group))
|
||||
return
|
||||
end
|
||||
|
||||
-- Make sure the attack is of a TYPE that we care about.
|
||||
if ((group.command.type == defines.command.attack_area) or
|
||||
(group.command.type == defines.command.build_base)) then
|
||||
-- log("OarcModifyEnemyGroup MODIFYING command TYPE=" .. group.command.type)
|
||||
else
|
||||
if ((group.command.type ~= defines.command.attack_area) and
|
||||
(group.command.type ~= defines.command.build_base)) then
|
||||
-- log("OarcModifyEnemyGroup ignoring command TYPE=" .. group.command.type)
|
||||
return
|
||||
end
|
||||
|
||||
-- (group.command.type == defines.command.attack) or
|
||||
-- defines.command.attack --> target --> target.position
|
||||
-- if (group.command.type == defines.command.attack) then
|
||||
-- log("OarcModifyEnemyGroup defines.command.attack NOT IMPLEMENTED YET!")
|
||||
-- return
|
||||
-- end
|
||||
|
||||
-- defines.command.attack_area --> destination --> closest enemy (within 3 chunk radius?)
|
||||
-- defines.command.build_base --> destination --> closest enemy (expansion chunk distance?)
|
||||
-- For these 2 commands, we look around to find the nearest player.
|
||||
-- defines.command.attack_area --> destination --> closest player (within 3 chunk radius?)
|
||||
-- defines.command.build_base --> destination --> closest player (expansion chunk distance?)
|
||||
|
||||
local destination = group.command.destination
|
||||
|
||||
local search_radius = CHUNK_SIZE*3
|
||||
local search_radius = CHUNK_SIZE * 3 -- Just a reasonable default search size I think?
|
||||
if (group.command.type == defines.command.build_base) then
|
||||
search_radius = CHUNK_SIZE * (game.map_settings.enemy_expansion.max_expansion_distance)
|
||||
end
|
||||
@ -73,10 +65,7 @@ function OarcModifyEnemyGroup(event)
|
||||
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!?!?" .. serpent.block(group))
|
||||
-- for _,member in pairs(group.members) do
|
||||
-- member.destroy()
|
||||
-- end
|
||||
|
||||
|
||||
-- This is fine, as the enemy group is just expanding / building bases
|
||||
else
|
||||
-- log("OarcModifyEnemyGroup find_nearest_enemy did not find anything!")
|
||||
@ -87,14 +76,8 @@ function OarcModifyEnemyGroup(event)
|
||||
-- Most common target will be a built entity with a "last_user"
|
||||
local target_player = target_entity.last_user
|
||||
|
||||
-- -- Target could also be a player character (more rare)
|
||||
-- if (target_player == nil) and (target_entity.type == "character") then
|
||||
-- target_player = target_entity.player
|
||||
-- end
|
||||
|
||||
-- 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?" .. serpent.block(group))
|
||||
-- for _,member in pairs(group.members) do
|
||||
-- member.destroy()
|
||||
@ -104,7 +87,6 @@ function OarcModifyEnemyGroup(event)
|
||||
|
||||
-- Is the target player online? Then the attack can go through.
|
||||
if (target_player.connected) then
|
||||
-- SendBroadcastMsg("Enemy group released (player): " .. GetGPStext(group.surface.name, group.position) .. " Target: " .. GetGPStext(group.surface.name, target_entity.position) .. " " .. target_player.name)
|
||||
-- log("OarcModifyEnemyGroup RELEASING enemy group since player is ONLINE " .. target_player.name)
|
||||
return
|
||||
end
|
||||
@ -115,7 +97,6 @@ function OarcModifyEnemyGroup(event)
|
||||
|
||||
-- Is someone in the group online?
|
||||
if (#online_players > 0) then
|
||||
-- SendBroadcastMsg("Enemy group released (shared): " .. GetGPStext(group.surface.name, group.position) .. " Target: " .. GetGPStext(group.surface.name, target_entity.position) .. " " .. target_player.name)
|
||||
-- log("OarcModifyEnemyGroup RELEASING enemy group since someone in the group is ONLINE " .. target_player.name)
|
||||
return
|
||||
end
|
||||
@ -124,7 +105,5 @@ function OarcModifyEnemyGroup(event)
|
||||
for _,member in pairs(group.members) do
|
||||
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)
|
||||
|
||||
end
|
@ -137,6 +137,9 @@ function IsRegrowthEnabledOnSurface(surface_name)
|
||||
return global.rg[surface_name].active
|
||||
end
|
||||
|
||||
---Enables a surface by initializing it.
|
||||
---@param surface_name string - The surface name to act on
|
||||
---@return nil
|
||||
function RegrowthEnableSurface(surface_name)
|
||||
InitSurface(surface_name)
|
||||
end
|
||||
@ -497,8 +500,12 @@ function OarcRegrowthRemoveAllChunks()
|
||||
|
||||
elseif (global.rg[surface_name].map[c_pos.x][c_pos.y] == REGROWTH_FLAG_REMOVAL) then
|
||||
|
||||
-- If regrowth is disabled, remove the chunnk from the map without deleting it.
|
||||
if (not global.ocfg.regrowth.enable_regrowth or not global.rg[surface_name].active) then
|
||||
global.rg[surface_name].map[c_pos.x][c_pos.y] = nil
|
||||
|
||||
-- If it is a normal timeout removal, don't do it if there is pollution in the chunk.
|
||||
if (game.surfaces[surface_name].get_pollution({ c_pos.x * 32, c_pos.y * 32 }) > 0) then
|
||||
elseif (game.surfaces[surface_name].get_pollution({ c_pos.x * 32, c_pos.y * 32 }) > 0) then
|
||||
global.rg[surface_name].map[c_pos.x][c_pos.y] = game.tick
|
||||
|
||||
-- Else delete the chunk
|
||||
|
@ -5,94 +5,11 @@
|
||||
-- TODO: Plan for new enemies in space DLC.
|
||||
-- TODO: Plan for new enemies in space DLC.
|
||||
|
||||
|
||||
ENEMY_FORCE_EASY = "enemy-easy"
|
||||
ENEMY_FORCE_MEDIUM = "enemy-medium"
|
||||
ENEMY_FORCES_NAMES = {"enemy", ENEMY_FORCE_EASY, ENEMY_FORCE_MEDIUM}
|
||||
ENEMY_FORCES_NAMES_INCL_NEUTRAL = {"enemy", ENEMY_FORCE_EASY, ENEMY_FORCE_MEDIUM, "neutral"}
|
||||
ENEMY_FORCES_NAMES = { "enemy" }
|
||||
ENEMY_FORCES_NAMES_INCL_NEUTRAL = { "enemy", "neutral" }
|
||||
|
||||
ENEMY_BUILT_TYPES = { "biter-spawner", "spitter-spawner", "small-worm-turret", "medium-worm-turret", "big-worm-turret", "behemoth-worm-turret" }
|
||||
|
||||
---Create a few extra enemy forces with fixed evolution factors for scaling down near player bases.
|
||||
---@return nil
|
||||
function CreateEnemyForces()
|
||||
|
||||
-- Create the enemy forces if they don't exist
|
||||
for _,force_name in pairs(ENEMY_FORCES_NAMES) do
|
||||
if (game.forces[force_name] == nil) then
|
||||
game.create_force(force_name)
|
||||
end
|
||||
|
||||
local enemy_force = game.forces[force_name]
|
||||
enemy_force.ai_controllable = true
|
||||
end
|
||||
|
||||
ConfigureEnemyForceRelationships()
|
||||
end
|
||||
|
||||
---Configures the friend and cease fire relationships between all forces and enemy forces.
|
||||
---@return nil
|
||||
function ConfigureEnemyForceRelationships()
|
||||
for _,force in pairs(game.forces) do
|
||||
|
||||
-- If this is an enemy force
|
||||
if (TableContains(ENEMY_FORCES_NAMES, force.name)) then
|
||||
|
||||
-- Make sure it IS friends with all other enemy forces.
|
||||
for _,enemy_force_name in pairs(ENEMY_FORCES_NAMES) do
|
||||
if (force.name ~= enemy_force_name) then -- Exclude self
|
||||
local enemy_force = game.forces[enemy_force_name]
|
||||
|
||||
force.set_friend(enemy_force, true)
|
||||
force.set_cease_fire(enemy_force, true)
|
||||
|
||||
enemy_force.set_friend(force, true)
|
||||
enemy_force.set_cease_fire(force, true)
|
||||
end
|
||||
end
|
||||
|
||||
-- If this is a non-enemy force
|
||||
else
|
||||
-- Make sure this force is NOT friends with any enemy forces.
|
||||
for _,enemy_force_name in pairs(ENEMY_FORCES_NAMES) do
|
||||
local enemy_force = game.forces[enemy_force_name]
|
||||
|
||||
force.set_friend(enemy_force, false)
|
||||
force.set_cease_fire(enemy_force, false)
|
||||
|
||||
enemy_force.set_friend(force, false)
|
||||
enemy_force.set_cease_fire(force, false)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
---Configures the friend and cease fire relationships between all enemy forces and a new player force.
|
||||
---@param new_player_force LuaForce
|
||||
---@return nil
|
||||
function ConfigureEnemyForceRelationshipsForNewPlayerForce(new_player_force)
|
||||
for _,enemy_force_name in pairs(ENEMY_FORCES_NAMES) do
|
||||
local enemy_force = game.forces[enemy_force_name]
|
||||
|
||||
new_player_force.set_friend(enemy_force, false)
|
||||
new_player_force.set_cease_fire(enemy_force, false)
|
||||
|
||||
enemy_force.set_friend(new_player_force, false)
|
||||
enemy_force.set_cease_fire(new_player_force, false)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Keep the enemy evolution factor in check.
|
||||
---@return nil
|
||||
function RestrictEnemyEvolutionOnTick()
|
||||
local base_evo_factor = game.forces["enemy"].evolution_factor
|
||||
|
||||
-- Restrict the evolution factor of the enemy forces
|
||||
game.forces[ENEMY_FORCE_EASY].evolution_factor = math.min(base_evo_factor, global.ocfg.gameplay.modified_enemy_easy_evo)
|
||||
game.forces[ENEMY_FORCE_MEDIUM].evolution_factor = math.min(base_evo_factor, global.ocfg.gameplay.modified_enemy_medium_evo)
|
||||
end
|
||||
|
||||
---Downgrades worms based on distance from origin and near/far spawn distances.
|
||||
---This helps make sure worms aren't too overwhelming even at these further spawn distances.
|
||||
@ -104,13 +21,13 @@ function DowngradeWormsDistanceBasedOnChunkGenerate(event)
|
||||
local gameplay = global.ocfg.gameplay
|
||||
|
||||
if (util.distance({ x = 0, y = 0 }, event.area.left_top) < (gameplay.near_spawn_distance * CHUNK_SIZE)) then
|
||||
DowngradeWormsInArea(event.surface, event.area, 100, 100, 100)
|
||||
DowngradeWormsInArea(event.surface, event.area, 50, 100, 100) -- 50% small, 50% medium
|
||||
elseif (util.distance({ x = 0, y = 0 }, event.area.left_top) < (gameplay.far_spawn_distance * CHUNK_SIZE)) then
|
||||
DowngradeWormsInArea(event.surface, event.area, 50, 90, 100)
|
||||
elseif (util.distance({ x = 0, y = 0 }, event.area.left_top) < (gameplay.far_spawn_distance * CHUNK_SIZE * 2)) then
|
||||
DowngradeWormsInArea(event.surface, event.area, 20, 80, 97)
|
||||
DowngradeWormsInArea(event.surface, event.area, 25, 50, 95) -- 25% small, 25% medium, 45% big, 5% behemoth
|
||||
elseif (util.distance({ x = 0, y = 0 }, event.area.left_top) < (gameplay.far_spawn_distance * CHUNK_SIZE * 1.5)) then
|
||||
DowngradeWormsInArea(event.surface, event.area, 0, 40, 85) -- 40% medium, 45% big, 15% behemoth
|
||||
else
|
||||
DowngradeWormsInArea(event.surface, event.area, 0, 20, 90)
|
||||
DowngradeWormsInArea(event.surface, event.area, 0, 20, 50) -- 20% medium, 30% big, 50% behemoth
|
||||
end
|
||||
end
|
||||
|
||||
@ -141,7 +58,6 @@ function DowngradeAndReduceEnemiesOnChunkGenerate(event)
|
||||
-- TODO: Refactor this to reduce calls to find_entities_filtered!
|
||||
ReduceEnemiesInArea(surface, chunk_area, spawn_config.safe_area.warn_reduction)
|
||||
RemoveWormsInArea(surface, chunk_area, false, true, true, true) -- remove all non-small worms.
|
||||
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 * CHUNK_SIZE) then
|
||||
@ -149,7 +65,6 @@ function DowngradeAndReduceEnemiesOnChunkGenerate(event)
|
||||
-- TODO: Refactor this to reduce calls to find_entities_filtered!
|
||||
ReduceEnemiesInArea(surface, chunk_area, spawn_config.safe_area.danger_reduction)
|
||||
RemoveWormsInArea(surface, chunk_area, false, false, true, true) -- remove all huge/behemoth worms.
|
||||
ConvertEnemiesToOtherForceInArea(surface, chunk_area, ENEMY_FORCE_MEDIUM)
|
||||
end
|
||||
|
||||
end
|
||||
@ -180,9 +95,9 @@ end
|
||||
---Downgrades worms in an area based on chance. 100% small would mean all worms are changed to small.
|
||||
---@param surface LuaSurface
|
||||
---@param area BoundingBox
|
||||
---@param small_percent integer ---Chance to change to small worm
|
||||
---@param medium_percent integer
|
||||
---@param big_percent integer
|
||||
---@param small_percent integer ---Chance to force to small worm
|
||||
---@param medium_percent integer ---Chance to force to medium worm
|
||||
---@param big_percent integer ---Chance to force to big worm
|
||||
---@return nil
|
||||
function DowngradeWormsInArea(surface, area, small_percent, medium_percent, big_percent)
|
||||
-- Leave out "small-worm-turret" as it's the lowest.
|
||||
@ -193,26 +108,25 @@ function DowngradeWormsInArea(surface, area, small_percent, medium_percent, big_
|
||||
local rand_percent = math.random(0, 100)
|
||||
local worm_pos = entity.position
|
||||
local worm_name = entity.name
|
||||
local force = entity.force
|
||||
|
||||
-- If number is less than small percent, change to small
|
||||
if (rand_percent <= small_percent) then
|
||||
if (not (worm_name == "small-worm-turret")) then
|
||||
entity.destroy()
|
||||
surface.create_entity { name = "small-worm-turret", position = worm_pos, force = game.forces.enemy }
|
||||
end
|
||||
entity.destroy()
|
||||
surface.create_entity { name = "small-worm-turret", position = worm_pos, force = force }
|
||||
|
||||
-- ELSE If number is less than medium percent, change to small
|
||||
-- ELSE If number is less than medium percent, change to medium
|
||||
elseif (rand_percent <= medium_percent) then
|
||||
if (not (worm_name == "medium-worm-turret")) then
|
||||
entity.destroy()
|
||||
surface.create_entity { name = "medium-worm-turret", position = worm_pos, force = game.forces.enemy }
|
||||
surface.create_entity { name = "medium-worm-turret", position = worm_pos, force = force }
|
||||
end
|
||||
|
||||
-- ELSE If number is less than big percent, change to small
|
||||
-- ELSE If number is less than big percent, change to big
|
||||
elseif (rand_percent <= big_percent) then
|
||||
if (not (worm_name == "big-worm-turret")) then
|
||||
entity.destroy()
|
||||
surface.create_entity { name = "big-worm-turret", position = worm_pos, force = game.forces.enemy }
|
||||
surface.create_entity { name = "big-worm-turret", position = worm_pos, force = force }
|
||||
end
|
||||
|
||||
-- ELSE ignore it.
|
||||
@ -254,57 +168,6 @@ function RemoveWormsInArea(surface, area, small, medium, big, behemoth)
|
||||
end
|
||||
end
|
||||
|
||||
---Converts all enemies in the base enemy force in an area to easy force.
|
||||
---@param surface LuaSurface
|
||||
---@param area BoundingBox
|
||||
---@param force_name string
|
||||
---@return nil
|
||||
function ConvertEnemiesToOtherForceInArea(surface, area, force_name)
|
||||
for _, entity in pairs(surface.find_entities_filtered { area = area, force = "enemy" }) do
|
||||
entity.force = game.forces[force_name]
|
||||
end
|
||||
end
|
||||
|
||||
---Converts new enemy bases to different enemy forces if they are near to player bases.
|
||||
---@param event EventData.on_biter_base_built
|
||||
---@return nil
|
||||
function ChangeEnemySpawnersToOtherForceOnBuilt(event)
|
||||
if (not event.entity or not event.entity.position or not TableContains(ENEMY_FORCES_NAMES, event.entity.force.name)) then
|
||||
log("ChangeEnemySpawnersToOtherForceOnBuilt Unexpected entity or force?")
|
||||
return
|
||||
end
|
||||
|
||||
local enemy_pos = event.entity.position
|
||||
local surface = event.entity.surface
|
||||
local enemy_name = event.entity.name
|
||||
|
||||
if (not TableContains(ENEMY_BUILT_TYPES, enemy_name)) then
|
||||
log("ChangeEnemySpawnersToOtherForceOnBuilt Unexpected entity name? " .. enemy_name)
|
||||
return
|
||||
end
|
||||
|
||||
local closest_spawn = GetClosestUniqueSpawn(surface.name, enemy_pos)
|
||||
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 * 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 * 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 * 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).
|
||||
else
|
||||
event.entity.force = game.forces["enemy"]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- I wrote this to ensure everyone gets safer spawns regardless of evolution level.
|
||||
-- This is intended to downgrade any biters/spitters spawning near player bases.
|
||||
-- I'm not sure the performance impact of this but I'm hoping it's not bad.
|
||||
@ -334,19 +197,19 @@ function ModifyEnemySpawnsNearPlayerStartingAreas(event)
|
||||
-- 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 * CHUNK_SIZE) then
|
||||
if ((enemy_name == "biter-spawner") or (enemy_name == "spitter-spawner")) then
|
||||
event.entity.force = game.forces["enemy-easy"]
|
||||
|
||||
-- Do nothing.
|
||||
|
||||
elseif ((enemy_name == "big-biter") or (enemy_name == "behemoth-biter") or (enemy_name == "medium-biter")) then
|
||||
event.entity.destroy()
|
||||
surface.create_entity { name = "small-biter", position = enemy_pos, force = game.forces["enemy-easy"] }
|
||||
surface.create_entity { name = "small-biter", position = enemy_pos, force = game.forces.enemy }
|
||||
-- log("Downgraded biter close to spawn.")
|
||||
elseif ((enemy_name == "big-spitter") or (enemy_name == "behemoth-spitter") or (enemy_name == "medium-spitter")) then
|
||||
event.entity.destroy()
|
||||
surface.create_entity { name = "small-spitter", position = enemy_pos, force = game.forces["enemy-easy"] }
|
||||
surface.create_entity { name = "small-spitter", position = enemy_pos, force = game.forces.enemy }
|
||||
-- log("Downgraded spitter close to spawn.")
|
||||
elseif ((enemy_name == "big-worm-turret") or (enemy_name == "behemoth-worm-turret") or (enemy_name == "medium-worm-turret")) then
|
||||
event.entity.destroy()
|
||||
surface.create_entity { name = "small-worm-turret", position = enemy_pos, force = game.forces["enemy-easy"] }
|
||||
surface.create_entity { name = "small-worm-turret", position = enemy_pos, force = game.forces.enemy }
|
||||
-- log("Downgraded worm close to spawn.")
|
||||
end
|
||||
|
||||
|
@ -20,7 +20,7 @@ ABANDONED_FORCE_NAME = "_ABANDONED_"
|
||||
function InitSpawnGlobalsAndForces()
|
||||
|
||||
-- Contains a table of entries for each surface. This tracks which surfaces allow spawning?
|
||||
--[[@type table<string, boolean>]]
|
||||
--[[@type table<string, OarcSurfaceSpawnSetting>]]
|
||||
global.oarc_surfaces = {}
|
||||
for _, surface in pairs(game.surfaces) do
|
||||
SeparateSpawnsInitSurface(surface.name)
|
||||
@ -69,9 +69,6 @@ function InitSpawnGlobalsAndForces()
|
||||
game.create_force(ABANDONED_FORCE_NAME)
|
||||
-- game.create_force(DESTROYED_FORCE_NAME)
|
||||
|
||||
-- Special enemy forces for scaling down enemies near player bases.
|
||||
CreateEnemyForces()
|
||||
|
||||
-- Name a new force to be the default force.
|
||||
-- This is what any new player is assigned to when they join, even before they spawn.
|
||||
local main_force = CreatePlayerForce(global.ocfg.gameplay.main_force_name)
|
||||
@ -134,11 +131,14 @@ function SeparateSpawnsInitSurface(surface_name)
|
||||
|
||||
-- Add the surface to the list of surfaces that allow spawns with value from config.
|
||||
if global.ocfg.gameplay.default_allow_spawning_on_other_surfaces then
|
||||
global.oarc_surfaces[surface_name] = true
|
||||
global.oarc_surfaces[surface_name] = { primary = true, secondary = true }
|
||||
|
||||
-- Otherwise only allow the default surface (by default)
|
||||
else
|
||||
global.oarc_surfaces[surface_name] = (surface_name == global.ocfg.gameplay.default_surface)
|
||||
global.oarc_surfaces[surface_name] = {
|
||||
primary = (surface_name == global.ocfg.gameplay.default_surface),
|
||||
secondary = false
|
||||
}
|
||||
end
|
||||
|
||||
-- Make sure it has a surface configuration entry!
|
||||
@ -213,7 +213,7 @@ function SeparateSpawnsPlayerRespawned(event)
|
||||
if (player.surface.name == HOLDING_PEN_SURFACE_NAME) then return end
|
||||
|
||||
-- If the mod isn't active on this surface, then ignore it.
|
||||
if (not global.oarc_surfaces[surface_name]) then return end
|
||||
if (global.oarc_surfaces[surface_name] == nil) then return end
|
||||
|
||||
SendPlayerToSpawn(surface_name, player)
|
||||
GivePlayerRespawnItems(player)
|
||||
@ -239,16 +239,22 @@ function SeparateSpawnsPlayerChangedSurface(event)
|
||||
if (not global.ocfg.gameplay.enable_secondary_spawns) then return end
|
||||
|
||||
local player = game.players[event.player_index]
|
||||
local surface_name = player.surface.name
|
||||
|
||||
-- Check if player has been init'd yet. If not, then ignore it.
|
||||
if (global.player_respawns[player.name] == nil) then return end
|
||||
|
||||
-- If the mod isn't active on this surface, then ignore it.
|
||||
if (not global.oarc_surfaces[player.surface.name]) then return end
|
||||
if (global.oarc_surfaces[surface_name] == nil) then return end
|
||||
|
||||
-- If this is their first time on the planet, create a secondary spawn point for them.
|
||||
-- TODO: Check for buddy and shared spawn hosts?
|
||||
if (global.unique_spawns[player.surface.name] == nil) or (global.unique_spawns[player.surface.name][player.name] == nil) then
|
||||
-- If the mod is configured to not allow secondary spawns here, then ignore it.
|
||||
if (not global.oarc_surfaces[surface_name].secondary) then return end
|
||||
|
||||
-- Check if there is already a spawn for them on this surface
|
||||
-- Could be a buddy spawn, or a shared spawn, or a unique spawn.
|
||||
local player_spawn = FindPlayerSpawnOnSurface(player.name, surface_name)
|
||||
|
||||
if (player_spawn == nil) then
|
||||
log("WARNING - THIS IS NOT FULLY IMPLEMENTED YET!!")
|
||||
SecondarySpawn(player, player.surface)
|
||||
end
|
||||
@ -352,15 +358,16 @@ function PlaceResourcesInSemiCircle(surface, position, size_mod, amount_mod)
|
||||
local shuffled_list = FYShuffle(r_list)
|
||||
|
||||
-- This places resources in a semi-circle
|
||||
local angle_offset = global.ocfg.resource_placement.angle_offset
|
||||
local angle_offset_radians = math.rad(global.ocfg.resource_placement.angle_offset)
|
||||
local angle_final_radians = math.rad(global.ocfg.resource_placement.angle_final)
|
||||
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-1));
|
||||
local theta = ((angle_final_radians - angle_offset_radians) / (num_resources-1));
|
||||
local count = 0
|
||||
|
||||
local radius = global.ocfg.spawn_general.spawn_radius_tiles - global.ocfg.resource_placement.distance_to_edge
|
||||
|
||||
for _, r_name in pairs(shuffled_list) do
|
||||
local angle = (theta * count) + angle_offset;
|
||||
local angle = (theta * count) + angle_offset_radians;
|
||||
|
||||
local tx = (radius * math.cos(angle)) + position.x
|
||||
local ty = (radius * math.sin(angle)) + position.y
|
||||
@ -792,6 +799,19 @@ function FindPlayerHomeSpawn(player_name)
|
||||
end
|
||||
end
|
||||
|
||||
---Find the spawn of a player, if one exists, on a specific surface.
|
||||
---@param player_name string
|
||||
---@param surface_name string
|
||||
---@return OarcUniqueSpawn?
|
||||
function FindPlayerSpawnOnSurface(player_name, surface_name)
|
||||
if (global.unique_spawns[surface_name] == nil) then return nil end
|
||||
for _,spawn in pairs(global.unique_spawns[surface_name]) do
|
||||
if ((spawn.host_name == player_name) or TableContains(spawn.joiners, player_name)) then
|
||||
return spawn
|
||||
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!
|
||||
@ -864,12 +884,10 @@ function CleanupPlayerGlobals(player_name)
|
||||
for index, joiner in pairs(spawn.joiners) do
|
||||
if (player_name == joiner) then
|
||||
global.unique_spawns[surface_index][player_index].joiners[index] = nil
|
||||
goto LOOP_BREAK -- Nest loop break. Assumes only one entry per player is possible.
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
::LOOP_BREAK::
|
||||
|
||||
-- Clear their personal spawn point info
|
||||
if (global.player_respawns[player_name] ~= nil) then
|
||||
@ -1273,11 +1291,32 @@ function SecondarySpawn(player, surface)
|
||||
player.print({ "oarc-no-ungenerated-land-error" })
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
-- Add new spawn point for the new surface
|
||||
SetPlayerRespawn(player.name, surface.name, spawn_position, false) -- Do not reset cooldown
|
||||
QueuePlayerForDelayedSpawn(player.name, surface.name, spawn_position, spawn_choices.moat, false, nil)
|
||||
|
||||
-- TODO: Temporary for space age, buddies get a single spawn point for secondary spawns.
|
||||
if (spawn_choices.buddy) then
|
||||
SetPlayerRespawn(spawn_choices.buddy, surface.name, spawn_position, false)
|
||||
|
||||
-- Add buddy to the shared spawn?
|
||||
global.unique_spawns[surface.name][player.name].joiners = { spawn_choices.buddy }
|
||||
|
||||
log("Buddy secondary spawn NOT IMPLEMENTED YET!")
|
||||
end
|
||||
|
||||
-- -- If it is a buddy spawn, we need to setup both areas TOGETHER
|
||||
-- if spawn_choices.buddy then
|
||||
-- -- The x_offset must be big enough to ensure the spawns DO NOT overlap!
|
||||
-- local x_offset = (global.ocfg.spawn_general.spawn_radius_tiles * 2)
|
||||
-- if (spawn_choices.moat) then
|
||||
-- x_offset = x_offset + 10
|
||||
-- end
|
||||
-- local buddy_spawn_position = { x = spawn_position.x + x_offset, y = spawn_position.y }
|
||||
-- SetPlayerRespawn(spawn_choices.buddy, spawn_choices.surface, buddy_spawn_position, true)
|
||||
-- end
|
||||
|
||||
-- Send them to the holding pen
|
||||
SafeTeleport(player, game.surfaces[HOLDING_PEN_SURFACE_NAME], {x=0,y=0})
|
||||
|
||||
@ -1349,7 +1388,7 @@ end
|
||||
---@param player_name string
|
||||
---@return boolean
|
||||
function PlayerHasDelayedSpawn(player_name)
|
||||
for _,delayedSpawn in pairs(global.delayed_spawns --[[@as OarcDelayedSpawnsTable]]) do
|
||||
for _,delayedSpawn in pairs(global.delayed_spawns) do
|
||||
if (delayedSpawn.playerName == player_name) then
|
||||
return true
|
||||
end
|
||||
@ -1357,13 +1396,13 @@ function PlayerHasDelayedSpawn(player_name)
|
||||
return false
|
||||
end
|
||||
|
||||
---Get the list of surfaces that are allowed for spawning.
|
||||
---Get the list of surfaces that are allowed for primary spawning.
|
||||
---@return string[]
|
||||
function GetAllowedSurfaces()
|
||||
---@type string[]
|
||||
local surfaceList = {}
|
||||
for surfaceName,allowed in pairs(global.oarc_surfaces --[[@as table<string, boolean>]]) do
|
||||
if allowed then
|
||||
for surfaceName, allowed in pairs(global.oarc_surfaces) do
|
||||
if allowed.primary then
|
||||
table.insert(surfaceList, surfaceName)
|
||||
end
|
||||
end
|
||||
@ -1395,10 +1434,11 @@ function CreatePlayerForce(force_name)
|
||||
new_force = game.create_force(force_name)
|
||||
new_force.share_chart = global.ocfg.gameplay.enable_shared_team_vision
|
||||
new_force.friendly_fire = global.ocfg.gameplay.enable_friendly_fire
|
||||
new_force.research_queue_enabled = true
|
||||
-- SetCeaseFireBetweenAllPlayerForces()
|
||||
-- SetFriendlyBetweenAllPlayerForces()
|
||||
ConfigurePlayerForceRelationships(true, true)
|
||||
ConfigureEnemyForceRelationshipsForNewPlayerForce(new_force)
|
||||
-- ConfigureEnemyForceRelationshipsForNewPlayerForce(new_force)
|
||||
else
|
||||
log("TOO MANY FORCES!!! - CreatePlayerForce()")
|
||||
return game.forces[global.ocfg.gameplay.main_force_name]
|
||||
@ -1473,7 +1513,18 @@ SPAWN_TEAM_CHOICE = {
|
||||
---@alias OarcDelayedSpawnsTable table<string, OarcDelayedSpawn>
|
||||
|
||||
---This contains the spawn choices for a player in the spawn menu.
|
||||
---@alias OarcSpawnChoices { surface: string, team: SpawnTeamChoice, moat: boolean, buddy: string?, distance: integer, host: string?, buddy_team: boolean }
|
||||
---Class representing the spawn choices for a player in the spawn menu.
|
||||
---@class OarcSpawnChoices
|
||||
---@field surface string The surface on which the player wants to spawn.
|
||||
---@field team SpawnTeamChoice The team choice for the player. Main team or own team.
|
||||
---@field moat boolean Whether the player wants a moat around their spawn.
|
||||
---@field buddy string? The buddy player name if the player wants to spawn with a buddy.
|
||||
---@field distance integer The distance from the center of the map where the player wants to spawn.
|
||||
---@field host string? The host player name if the player wants to join a shared spawn.
|
||||
---@field buddy_team boolean Whether the player wants to join a buddy's team. This means both players will be on the same team.
|
||||
|
||||
---Table of [OarcSpawnChoices](lua://OarcSpawnChoices) indexed by player name.
|
||||
---@alias OarcSpawnChoicesTable table<string, OarcSpawnChoices>
|
||||
|
||||
---Primary means a player can spawn for the first time on this surface, secondary they can land here and also receive a custom spawn area.
|
||||
---@alias OarcSurfaceSpawnSetting { primary: boolean, secondary: boolean}
|
||||
|
@ -1122,7 +1122,10 @@ function AcceptBuddyRequest(player, requesting_buddy_name)
|
||||
local requesting_buddy = game.players[requesting_buddy_name]
|
||||
local surface = game.surfaces[spawn_choices.surface]
|
||||
|
||||
global.spawn_choices[player.name].host = nil -- N/A for buddy spawns so clear it.
|
||||
-- Copy the buddy's spawn choices to the accepting player
|
||||
spawn_choices.host = nil -- N/A for buddy spawns so clear it.
|
||||
global.spawn_choices[player.name] = table.deepcopy(spawn_choices)
|
||||
global.spawn_choices[player.name].buddy = requesting_buddy_name
|
||||
|
||||
-- Find coordinates of a good place to spawn
|
||||
local spawn_position = FindUngeneratedCoordinates(surface, spawn_choices.distance, 3)
|
||||
@ -1159,12 +1162,12 @@ function AcceptBuddyRequest(player, requesting_buddy_name)
|
||||
x_offset = x_offset + 10
|
||||
end
|
||||
buddySpawn = { x = spawn_position.x + x_offset, y = spawn_position.y }
|
||||
SetPlayerRespawn(player.name, spawn_choices.surface, spawn_position, true)
|
||||
SetPlayerRespawn(requesting_buddy_name, spawn_choices.surface, buddySpawn, true)
|
||||
SetPlayerRespawn(player.name, spawn_choices.surface, buddySpawn, true)
|
||||
SetPlayerRespawn(requesting_buddy_name, spawn_choices.surface, spawn_position, true)
|
||||
|
||||
-- Send the player there
|
||||
QueuePlayerForDelayedSpawn(player.name, spawn_choices.surface, spawn_position, spawn_choices.moat, true, requesting_buddy_name)
|
||||
QueuePlayerForDelayedSpawn(requesting_buddy_name, spawn_choices.surface, buddySpawn, spawn_choices.moat, true, player.name)
|
||||
QueuePlayerForDelayedSpawn(player.name, spawn_choices.surface, buddySpawn, spawn_choices.moat, true, requesting_buddy_name)
|
||||
QueuePlayerForDelayedSpawn(requesting_buddy_name, spawn_choices.surface, spawn_position, spawn_choices.moat, true, player.name)
|
||||
SendBroadcastMsg({"", {"oarc-buddies-are-joining", requesting_buddy_name, player.name, spawn_choices.surface}, " ", GetGPStext(spawn_choices.surface, spawn_position)})
|
||||
|
||||
-- Unlock spawn control gui tab
|
||||
|
@ -32,8 +32,8 @@ oarc-mod-enable-secondary-spawns=Enable secondary spawns
|
||||
|
||||
oarc-mod-scale-resources-around-spawns=Scale resources around spawns
|
||||
oarc-mod-modified-enemy-spawning=Scale enemies around spawns
|
||||
oarc-mod-modified-enemy-easy-evo=Easy enemy evolution
|
||||
oarc-mod-modified-enemy-medium-evo=Medium enemy evolution
|
||||
# oarc-mod-modified-enemy-easy-evo=Easy enemy evolution
|
||||
# oarc-mod-modified-enemy-medium-evo=Medium enemy evolution
|
||||
oarc-mod-minimum-online-time=Minimum online time
|
||||
oarc-mod-respawn-cooldown-min=Reset respawn point cooldown
|
||||
oarc-mod-enable-shared-power=Shared power
|
||||
@ -54,8 +54,8 @@ oarc-mod-spawn-general-shape=Spawn area shape
|
||||
|
||||
oarc-mod-resource-placement-enabled=Starting resource auto placement
|
||||
oarc-mod-resource-placement-distance-to-edge=Starting resource distance to edge
|
||||
oarc-mod-resource-placement-angle-offset=Starting resource angle offset
|
||||
oarc-mod-resource-placement-angle-final=Starting resource angle final
|
||||
oarc-mod-resource-placement-degrees-offset=Starting resource angle offset
|
||||
oarc-mod-resource-placement-degrees-final=Starting resource angle final
|
||||
oarc-mod-resource-placement-vertical-offset=Starting resource vertical offset
|
||||
oarc-mod-resource-placement-horizontal-offset=Starting resource horizontal offset
|
||||
oarc-mod-resource-placement-linear-spacing=Starting resource linear spacing
|
||||
@ -93,8 +93,8 @@ oarc-mod-enable-secondary-spawns=Enabling this will provide players with a secon
|
||||
|
||||
oarc-mod-scale-resources-around-spawns=This scales resources around every spawn area so far away spawns aren't immediately next to very rich deposits.
|
||||
oarc-mod-modified-enemy-spawning=This scales the enemy spawning globally based on the allowed spawn distances to avoid every spawn being surrounded by behemoth worms.
|
||||
oarc-mod-modified-enemy-easy-evo=This is the maximum evolution that the enemies in the warning zone (closest) to a spawn will be FIXED at to ensure all players have a safe start! The warning zone area can be configured via the custom scenario.
|
||||
oarc-mod-modified-enemy-medium-evo=This is the maximum evolution that the enemies in the danger zone (next closest) to a spawn will be allowed to reach. Helps ensure players have a safe start! The danger zone area can be configured via the custom scenario.
|
||||
# oarc-mod-modified-enemy-easy-evo=This is the maximum evolution that the enemies in the warning zone (closest) to a spawn will be FIXED at to ensure all players have a safe start! The warning zone area can be configured via the custom scenario.
|
||||
# oarc-mod-modified-enemy-medium-evo=This is the maximum evolution that the enemies in the danger zone (next closest) to a spawn will be allowed to reach. Helps ensure players have a safe start! The danger zone area can be configured via the custom scenario.
|
||||
oarc-mod-minimum-online-time=The minimum time a player must be online before they leave, otherwise their spawn area will be cleaned up.
|
||||
oarc-mod-respawn-cooldown-min=The minimum time a player must wait before they can change their spawn point to a new location.
|
||||
oarc-mod-enable-shared-power=This allows players to share their power network with other players. It will create a special power pole at their spawn point that connects to the shared power network wirelessly.
|
||||
@ -115,8 +115,8 @@ oarc-mod-spawn-general-shape=This is the shape of the spawn area.
|
||||
|
||||
oarc-mod-resource-placement-enabled=You should leave this enabled unless you are manually specifying resource placements in the custom scenario!
|
||||
oarc-mod-resource-placement-distance-to-edge=This is the distance from the edge of the spawn area that resources will be placed. Only applicable for circle/octagon shaped spawns.
|
||||
oarc-mod-resource-placement-angle-offset=This is the starting angle offset (in radians) for the resource placement. At what angle (in radians) do resources start. 0 = east. 3.14 = west. Resources are placed clockwise starting at this angle. Only applicable for circle/octagon shaped spawns.
|
||||
oarc-mod-resource-placement-angle-final=This is the final angle offset (in radians) for the resource placement. At what angle (in radians) do resources end. 0 = east. 3.14 = west. Resources are placed clockwise ending at this angle. Only applicable for circle/octagon shaped spawns.
|
||||
oarc-mod-resource-placement-degrees-offset=This is the starting angle offset (in degrees) for the resource placement. At what angle (in degrees) do resources start. 0 = east. 90 = south. Resources are placed clockwise starting at this angle. Only applicable for circle/octagon shaped spawns.
|
||||
oarc-mod-resource-placement-degrees-final=This is the final angle offset (in degrees) for the resource placement. At what angle (in degrees) do resources end. 0 = east. 90 = south. Resources are placed clockwise ending at this angle. Only applicable for circle/octagon shaped spawns.
|
||||
oarc-mod-resource-placement-vertical-offset=This is the vertical offset (in tiles) for the resource placement from the top-left of the spawn. Only applicable for square shaped spawns.
|
||||
oarc-mod-resource-placement-horizontal-offset=This is the horizontal offset (in tiles) for the resource placement from the top-left of the spawn. Only applicable for square shaped spawns.
|
||||
oarc-mod-resource-placement-linear-spacing=This is the linear spacing (in tiles) between resources. Only applicable for square shaped spawns.
|
||||
|
@ -221,9 +221,11 @@ oarc-settings-tab-description=This tab contains the same mod settings in the mod
|
||||
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.
|
||||
oarc-settings-tab-surface-secondary-checkbox-tooltip=Enabling this will give players custom secondary spawns when they first travel to this surface. [color=red]This feature is currently a work in progress.[/color]
|
||||
oarc-settings-tab-surface-regrowth-checkbox-tooltip=Enabling this will allow the regrowth and world eater features to work on this surface, if those are enabled.
|
||||
oarc-settings-tab-surface-column-header=Surface
|
||||
oarc-settings-tab-surface-spawning-enabled=Custom Spawns
|
||||
oarc-settings-tab-surface-spawning-enabled=Home
|
||||
oarc-settings-tab-surface-secondary-enabled=Secondary
|
||||
oarc-settings-tab-surface-regrowth-enabled=Regrowth
|
||||
oarc-settings-tab-title-export=Import/Export Settings
|
||||
oarc-settings-tab-export-button=Export
|
||||
|
7
migrations/oarc-mod_V2.0.4.lua
Normal file
7
migrations/oarc-mod_V2.0.4.lua
Normal file
@ -0,0 +1,7 @@
|
||||
-- If "enemy-easy" and "enemy-medium" forces still exist, merge them into "enemy" force.
|
||||
if game.forces["enemy-easy"] then
|
||||
game.merge_forces("enemy-easy", "enemy")
|
||||
end
|
||||
if game.forces["enemy-medium"] then
|
||||
game.merge_forces("enemy-medium", "enemy")
|
||||
end
|
52
settings.lua
52
settings.lua
@ -185,24 +185,24 @@ data:extend({
|
||||
default_value = true,
|
||||
order = "f2"
|
||||
},
|
||||
{
|
||||
type = "double-setting",
|
||||
name = "oarc-mod-modified-enemy-easy-evo",
|
||||
setting_type = "runtime-global",
|
||||
default_value = 0,
|
||||
minimum_value = 0,
|
||||
maximum_value = 1,
|
||||
order = "f21"
|
||||
},
|
||||
{
|
||||
type = "double-setting",
|
||||
name = "oarc-mod-modified-enemy-medium-evo",
|
||||
setting_type = "runtime-global",
|
||||
default_value = 0.3,
|
||||
minimum_value = 0,
|
||||
maximum_value = 1,
|
||||
order = "f22"
|
||||
},
|
||||
-- {
|
||||
-- type = "double-setting",
|
||||
-- name = "oarc-mod-modified-enemy-easy-evo",
|
||||
-- setting_type = "runtime-global",
|
||||
-- default_value = 0,
|
||||
-- minimum_value = 0,
|
||||
-- maximum_value = 1,
|
||||
-- order = "f21"
|
||||
-- },
|
||||
-- {
|
||||
-- type = "double-setting",
|
||||
-- name = "oarc-mod-modified-enemy-medium-evo",
|
||||
-- setting_type = "runtime-global",
|
||||
-- default_value = 0.3,
|
||||
-- minimum_value = 0,
|
||||
-- maximum_value = 1,
|
||||
-- order = "f22"
|
||||
-- },
|
||||
|
||||
{
|
||||
type = "int-setting",
|
||||
@ -345,21 +345,21 @@ data:extend({
|
||||
order = "i2"
|
||||
},
|
||||
{
|
||||
type = "double-setting",
|
||||
name = "oarc-mod-resource-placement-angle-offset",
|
||||
type = "int-setting",
|
||||
name = "oarc-mod-resource-placement-degrees-offset",
|
||||
setting_type = "runtime-global",
|
||||
default_value = 2.09,
|
||||
default_value = 120,
|
||||
minimum_value = 0,
|
||||
maximum_value = 6.28,
|
||||
maximum_value = 359,
|
||||
order = "i3"
|
||||
},
|
||||
{
|
||||
type = "double-setting",
|
||||
name = "oarc-mod-resource-placement-angle-final",
|
||||
type = "int-setting",
|
||||
name = "oarc-mod-resource-placement-degrees-final",
|
||||
setting_type = "runtime-global",
|
||||
default_value = 4.18,
|
||||
default_value = 240,
|
||||
minimum_value = 0,
|
||||
maximum_value = 6.28,
|
||||
maximum_value = 359,
|
||||
order = "i4"
|
||||
},
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user