From 07295f6a1754b9600c31a8a4e35a2554a0e27edf Mon Sep 17 00:00:00 2001 From: Oarcinae Date: Thu, 5 Dec 2024 21:01:09 -0500 Subject: [PATCH] More moddifng functionality. Some reduced debug logs. --- TEMPLATE_SCENARIO/OARC/control.lua | 124 +++++++++++++++++++++++++---- changelog.txt | 5 ++ control.lua | 46 ++++++----- data.lua | 6 ++ lib/oarc_gui_tabs.lua | 49 ++++++++++-- lib/separate_spawns.lua | 5 +- 6 files changed, 190 insertions(+), 45 deletions(-) diff --git a/TEMPLATE_SCENARIO/OARC/control.lua b/TEMPLATE_SCENARIO/OARC/control.lua index cdaa926..20f2609 100644 --- a/TEMPLATE_SCENARIO/OARC/control.lua +++ b/TEMPLATE_SCENARIO/OARC/control.lua @@ -1,4 +1,5 @@ -- To edit this scenario, you must make a copy of it and place it in your own scenarios folder first! +-- Alternatively, you can make a dependent mod based on this as well. -- I provide this empty scenario to avoid the freeplay scenario extra baggage and as a template for how to modify the -- settings on init. You can use the freeplay scenario too just fine if you want. @@ -13,19 +14,14 @@ -- Alternatively, you can use the in-game "export" button to get a string of all settings that you can then format -- and edit however you want to use here. --- ANY CONFIG HERE WILL OVERWRITE MOD SETTINGS! This is the whole point. - --- Check if the OARC mod is loaded. Other than that, it's an empty scenario! -script.on_init(function(event) - if not script.active_mods["oarc-mod"] then - error("OARC mod not found! This scenario is intended to be run with the OARC mod!") - end - - storage.ocfg_copy = remote.call("oarc_mod", "get_mod_settings") - log("server info test: ") -end) - - +---------------------------------------------------------------------------------------------------------------- +-- In order to modify the settings of the OARC mod, you need to implement the following section. +-- I need to insert any settings overrides before I run my mod's other init functions so this is why I do it +-- this way! If I let you overwrite config at a later time, it may have no effect or might cause other issues. +-- You should only be modifying the section as described below. Don't modify the rest of this interface. +-- +-- You can implement this in a scenario OR a mod (despite the name). +---------------------------------------------------------------------------------------------------------------- local oarc_scenario_interface = { get_scenario_settings = function() @@ -35,6 +31,7 @@ local oarc_scenario_interface = -- Overwrite whatever settings you want here: -- If you provide an invalid value for a mod setting, it will error and not load the scenario. + -- ANY CONFIG HERE WILL OVERWRITE MOD SETTINGS! This is the whole point. ---------------------------------------------------------------------------------------------------------------- modified_settings.server_info.welcome_msg_title = "THIS IS A TEMPLATE SCENARIO" modified_settings.server_info.welcome_msg = "This is a template scenario. You can modify the settings in the control.lua file. If you are seeing this message, you did not modify the scenario correctly." @@ -51,19 +48,34 @@ local oarc_scenario_interface = } remote.add_interface("oarc_scenario", oarc_scenario_interface) +---------------------------------------------------------------------------------------------------------------- +-- End of settings override section. +---------------------------------------------------------------------------------------------------------------- +-- Check if the OARC mod is loaded. Other than that, it's an empty scenario! You can modify it however you want. +script.on_init(function(event) + if not script.active_mods["oarc-mod"] then + error("OARC mod not found! This scenario is intended to be run with the OARC mod!") + end + + -- Please note: a scenario can NOT call this during on_init because you can't set load order for a scenario! + -- You just get nil back. If you are making a mod, you can use this to get the config during on_init. + -- storage.ocfg_copy = remote.call("oarc_mod", "get_mod_settings") + -- See the oarc-mod-on-config-changed event for how to keep the config up to date. +end) ---------------------------------------------------------------------------------------------------------------- --- Everything below here is an example of how to use the custom events provided by the OARC mod. +-- This section shows an example of how to customize the spawn gui options presented to the player, and how to +-- use that to customize the spawn area generation process. ---------------------------------------------------------------------------------------------------------------- -local util = require("util") +local util = require("util") -- Only needed for the distance function in this example. -- You can require files to use them if you want like this. (Ignore the diagnostic warning.) ---@diagnostic disable-next-line: different-requires -require("__oarc-mod__/lib/oarc_gui_utils") +require("__oarc-mod__/lib/oarc_gui_utils") -- I have a helper function in here for creating GUI elements. -- This will keep a local copy of the mod's config so you can use it in your custom events. script.on_event("oarc-mod-on-config-changed", function(event) @@ -133,6 +145,10 @@ script.on_event("oarc-mod-on-chunk-generated-near-spawn", function(event) custom_event.surface.set_tiles(tiles) end) +---------------------------------------------------------------------------------------------------------------- +-- This section just supports the above events. +---------------------------------------------------------------------------------------------------------------- + ---A helper function to create a dropdown for selecting tiles ---@param parent_flow LuaGuiElement ---@return nil @@ -214,4 +230,80 @@ script.on_event(defines.events.on_gui_selection_state_changed, function(event) if not event.element.valid then return end CustomSpawnOptsSelectionChanged(event) +end) + +---------------------------------------------------------------------------------------------------------------- +-- This section shows how to add a custom tab to the Oarc Mod GUI (the top left button). +---------------------------------------------------------------------------------------------------------------- + +local CUSTOM_TAB_NAME = "Test Mod" + +script.on_event("oarc-mod-on-mod-top-left-gui-created", function(event --[[@as OarcModOnModTopLeftGuiCreatedEvent]]) + + -- Create a custom empty tab: + remote.call("oarc_mod", "create_custom_gui_tab", game.players[event.player_index], CUSTOM_TAB_NAME) + + -- Get the tab content element + local tab_content = remote.call("oarc_mod", "get_custom_gui_tab_content_element", game.players[event.player_index], CUSTOM_TAB_NAME) + + -- Save a reference to that tab content for later use, if it becomes nil you can ask for it again. + -- Or you can just always ask for it when you need it, depends on how often you plan to use it. + if storage.custom_tabs_table == nil then + storage.custom_tabs_table = {} + end + storage.custom_tabs_table[event.player_index] = {content = tab_content, example_data = 0} + + -- Use the element to add content directly to it. + CreateExampleCustomTabContent(tab_content, tostring(0)) +end) + +---Creates the test tab content, just adds a couple of text labels and a button as an example. +---@param tab_container LuaGuiElement +---@param example_data string +---@return nil +function CreateExampleCustomTabContent(tab_container, example_data) + tab_container.clear() + tab_container.add { + type = "label", + caption = "This is a test tab to demonstrate how to add tabs to the Oarc Mod GUI.", + style = "caption_label" + } + tab_container.add { + type = "label", + caption = "This tab has been clicked " .. example_data .. " times.", + style = "caption_label" + } + tab_container.add { + type = "button", + caption = "Click Me!", + style = "button", + name = "test_mod_button", + tags = { test_mod_button = true } -- Learn to use tags, this is great for identifying where GUI events came from. + } +end + +-- This is the event that is triggered when a player selects a tab (on ANY tabbed pane) +script.on_event(defines.events.on_gui_selected_tab_changed, function (event) + if (event.element.name ~= "oarc_tabs") then return end -- This is what I have named my tabbed pane in my mod. + + -- Check if your custom tab was selected + local tab_name = event.element.tabs[event.element.selected_tab_index].tab.name + if (tab_name ~= "Test Mod") then return end + + log("Selected Test Mod tab") + + -- Proof of concept that shows you can update/change the content of your custom tab when it is selected. + local entry = storage.custom_tabs_table[event.player_index] + entry.example_data = entry.example_data + 1 + CreateExampleCustomTabContent(entry.content, tostring(entry.example_data)) +end) + +-- This is the event that is triggered when a player clicks a button in the GUI (on ANY GUI) +script.on_event(defines.events.on_gui_click, function (event) + + -- Check if the button clicked was yours, how you can identify it is up to you, but tags are a good way. + if event.element.tags and event.element.tags.test_mod_button then + log("Test Mod button clicked!") + game.players[event.player_index].print("Test Mod button clicked!") + end end) \ No newline at end of file diff --git a/changelog.txt b/changelog.txt index 9827727..693e245 100644 --- a/changelog.txt +++ b/changelog.txt @@ -3,6 +3,11 @@ Version: 2.1.17 Date: ???? Bugfixes: - Fix incorrect names printed for infinite technology research. + Info: + - Add more info to the template scenario to help with understanding how to override settings. + - Disable some of the extra debug logging. + Modding: + - Add remote interfaces to add custom tabs to the top left GUI. Updated template scenario to show how to use the new features. --------------------------------------------------------------------------------------------------- Version: 2.1.16 Date: 2024-12-03 diff --git a/control.lua b/control.lua index dd620d0..46c1148 100644 --- a/control.lua +++ b/control.lua @@ -116,10 +116,10 @@ end) -- SeparateSpawnsPlayerChangedSurface(event) -- end) -script.on_event(defines.events.on_rocket_launched, function(event) - log("Rocket launched!") - log(serpent.block(event)) -end) +-- script.on_event(defines.events.on_rocket_launched, function(event) +-- log("Rocket launched!") +-- log(serpent.block(event)) +-- end) script.on_event(defines.events.on_player_driving_changed_state, function (event) local entity = event.entity @@ -130,19 +130,11 @@ script.on_event(defines.events.on_player_driving_changed_state, function (event) RegrowthMarkAreaSafeGivenTilePos(entity.surface.name, entity.position, 1, false) end - log("Player driving changed state") - log(serpent.block(event)) - -- Track the surfaces whenever driving state changes for a cargo-pod ONLY. -- This triggers events for surface changes when using the standard space travel method. if (entity ~= nil) and (entity.name == "cargo-pod") then local player = game.players[event.player_index] - -- Check if driving flag is set - if (player.driving) then - log("Player is driving a cargo-pod") - end - SeparateSpawnsUpdatePlayerSurface(player, entity.surface.name) end end) @@ -184,10 +176,10 @@ end) ---@class OarcModOnSpawnChoicesGuiDisplayedEvent: OarcCustomEventBase ---@field player_index integer ---@field gui_element LuaGuiElement -script.on_event("oarc-mod-on-spawn-choices-gui-displayed", function(event) - log("EVENT - oarc-mod-on-spawn-choices-gui-displayed:" .. serpent.block(event --[[@as OarcModOnSpawnChoicesGuiDisplayedEvent]])) - -- The 4 main sub sections are called: spawn_settings_frame, solo_spawn_frame, shared_spawn_frame, and buddy_spawn_frame -end) +-- script.on_event("oarc-mod-on-spawn-choices-gui-displayed", function(event) +-- log("EVENT - oarc-mod-on-spawn-choices-gui-displayed:" .. serpent.block(event --[[@as OarcModOnSpawnChoicesGuiDisplayedEvent]])) +-- -- The 4 main sub sections are called: spawn_settings_frame, solo_spawn_frame, shared_spawn_frame, and buddy_spawn_frame +-- end) ---@class OarcModOnSpawnCreatedEvent: OarcCustomEventBase @@ -221,8 +213,6 @@ end) ---@field old_surface_name string ---@field new_surface_name string script.on_event("oarc-mod-character-surface-changed", function(event) - log("EVENT - oarc-mod-character-surface-changed:" .. serpent.block(event --[[@as OarcModCharacterSurfaceChangedEvent]])) - --This is just here so I don't get lua warnings about unused variables. ---@type OarcModCharacterSurfaceChangedEvent local custom_event = event --[[@as OarcModCharacterSurfaceChangedEvent]] @@ -231,22 +221,28 @@ script.on_event("oarc-mod-character-surface-changed", function(event) SeparateSpawnsPlayerChangedSurface(player, custom_event.old_surface_name, custom_event.new_surface_name) end) +-- I wouldn't recommend any logging inside this event as it is called for every chunk generated near a spawn. ---@class OarcModOnChunkGeneratedNearSpawnEvent: OarcCustomEventBase ---@field surface LuaSurface ---@field chunk_area BoundingBox ---@field spawn_data OarcUniqueSpawn -- script.on_event("oarc-mod-on-chunk-generated-near-spawn", function(event) --- I wouldn't recommend any logging inside this event as it is called for every chunk generated near a spawn. -- log("EVENT - oarc-mod-on-chunk-generated-near-spawn:" .. serpent.block(event --[[@as OarcModOnChunkGeneratedNearSpawnEvent]])) -- end) +-- This can get called quite a lot during init or importing of settings. ---@class OarcModOnConfigChangedEvent: OarcCustomEventBase -- script.on_event("oarc-mod-on-config-changed", function(event) --- This can get called quite a lot during init or importing of settings. -- log("EVENT - oarc-mod-on-config-changed:" .. serpent.block(event --[[@as OarcModOnConfigChangedEvent]])) -- end) --- I raise this event whenever teleporting the player! +---@class OarcModOnModTopLeftGuiCreatedEvent: OarcCustomEventBase +---@field player_index integer +-- script.on_event("oarc-mod-on-mod-top-left-gui-created", function(event) +-- log("EVENT - oarc-mod-on-mod-top-left-gui-created:" .. serpent.block(event --[[@as OarcModOnModTopLeftGuiCreatedEvent]])) +-- end) + +-- I raise this event whenever teleporting the player, and it is used to update the player's surface. script.on_event(defines.events.script_raised_teleported, function(event) local entity = event.entity if entity.type == "character" and entity.player then @@ -507,6 +503,14 @@ local oarc_mod_interface = get_player_primary_spawn = function(player_name) return FindPrimaryUniqueSpawn(player_name) end, + + create_custom_gui_tab = function(player, tab_name) + AddCustomOarcGuiTab(player, tab_name) + end, + + get_custom_gui_tab_content_element = function(player, tab_name) + return OarcGuiGetTabContentElement(player, tab_name) + end, } remote.add_interface("oarc_mod", oarc_mod_interface) diff --git a/data.lua b/data.lua index 44d4910..88a7396 100644 --- a/data.lua +++ b/data.lua @@ -74,6 +74,12 @@ data:extend({ type = "custom-event", name = "oarc-mod-on-config-changed", }, + + -- A player's custom GUI panel (top right special button) was created + { + type = "custom-event", + name = "oarc-mod-on-mod-top-left-gui-created", + }, }) diff --git a/lib/oarc_gui_tabs.lua b/lib/oarc_gui_tabs.lua index bce7a2f..9a1d158 100644 --- a/lib/oarc_gui_tabs.lua +++ b/lib/oarc_gui_tabs.lua @@ -101,6 +101,9 @@ function InitOarcGuiTabs(player) SetOarcGuiTabEnabled(player, OARC_SURFACE_CONFIG_TAB_NAME, true) end + -- Let other mods know the top left GUI was created, this is a good time to add buttons to it. + script.raise_event("oarc-mod-on-mod-top-left-gui-created", {player_index = player.index}) + HideOarcGui(player) end @@ -201,13 +204,31 @@ function OarcGuiCreateContentOfTab(player) -- log("OarcGuiCreateContentOfTab: " .. tab_name) for _,t in ipairs(otabs.tabs) do - t.content.clear() - if (t.tab.name == tab_name) then - OARC_GUI_TAB_CONTENT[tab_name].create_tab_function(t.content, player) + if (OARC_GUI_TAB_CONTENT[t.tab.name] ~= nil) then -- Only clear my own tabs. + t.content.clear() + if (t.tab.name == tab_name) then + OARC_GUI_TAB_CONTENT[tab_name].create_tab_function(t.content, player) + end end end end +---Gets the content element of the named tab. +---@param player LuaPlayer +---@param tab_name string +---@return LuaGuiElement? +function OarcGuiGetTabContentElement(player, tab_name) + local otabs = GetOarcGuiTabsPane(player) + if (otabs == nil) then return nil end + + for _,t in ipairs(otabs.tabs) do + if (t.tab.name == tab_name) then + return t.content + end + end + return nil +end + ---Just an alias for OarcGuiCreateContentOfTab ---@param player LuaPlayer ---@return nil @@ -270,7 +291,8 @@ end -- It adds whatever it wants to the provided scroll-pane. ---@param player LuaPlayer ---@param tab_name string -function AddOarcGuiTab(player, tab_name) +---@param localized_name LocalisedString +function AddOarcGuiTabWrapper(player, tab_name, localized_name) if (not DoesOarcGuiExist(player)) then CreateOarcGuiTabsPane(player) end @@ -284,7 +306,7 @@ function AddOarcGuiTab(player, tab_name) local new_tab = otabs.add{ type="tab", name=tab_name, - caption=OARC_GUI_TAB_CONTENT[tab_name].localized_name,} + caption=localized_name} -- Create inside frame for content local tab_inside_frame = otabs.add{ @@ -314,6 +336,13 @@ function AddOarcGuiTab(player, tab_name) end end +-- Uses AddOarcGuiTabWrapper to add my own tabs using OARC_GUI_TAB_CONTENT for the localized name. +---@param player LuaPlayer +---@param tab_name string +function AddOarcGuiTab(player, tab_name) + AddOarcGuiTabWrapper(player, tab_name, OARC_GUI_TAB_CONTENT[tab_name].localized_name) +end + -- https://forums.factorio.com/viewtopic.php?f=7&t=115901 -- ---Removes a tab from the GUI. -- ---@param player LuaPlayer @@ -428,6 +457,16 @@ function AddRemoveOarcGuiTabForAllPlayers(tab_name, add, enable) end end + +-- Lets other mods add their own tabs to the OARC GUI. +---@param player LuaPlayer +---@param tab_name string +function AddCustomOarcGuiTab(player, tab_name) + AddOarcGuiTabWrapper(player, tab_name, tab_name) + SetOarcGuiTabEnabled(player, tab_name, true) +end + + --[[ _____ _____ _ _ _____ _ _ _ _ _ ___ _ ___ ___ ___ | __\ \ / / __| \| |_ _| | || | /_\ | \| | \| | | __| _ \/ __| diff --git a/lib/separate_spawns.lua b/lib/separate_spawns.lua index b89ee1a..9b5b5d9 100644 --- a/lib/separate_spawns.lua +++ b/lib/separate_spawns.lua @@ -28,10 +28,9 @@ function InitSpawnGlobalsAndForces() end SeparateSpawnsInitPlanets() - -- This contains each player's respawn point. Literally where they will respawn on death + -- This contains each player's respawn point for each surface. Literally where they will respawn on death -- There is a way in game to change this under one of the little menu features I added. This allows players to -- change their respawn point to something other than their home base. - -- TODO: Space Age will potentially affect this, as I may need to allow for multiple respawn points on different surfaces. --[[@type OarcPlayerRespawnsTable]] storage.player_respawns = {} @@ -52,7 +51,7 @@ function InitSpawnGlobalsAndForces() storage.delayed_spawns = {} -- This stores the spawn choices that a player makes from the GUI interactions. - -- Intended to be re-used for secondary spawns! (TODO SPACE AGE) + -- Intended to be re-used for secondary spawns! --[[@type OarcSpawnChoicesTable]] storage.spawn_choices = {}