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

1285 lines
46 KiB
Lua

-- I made a separate file for all the GUI related functions. Yay me.
local SPAWN_GUI_MAX_WIDTH = 500
local SPAWN_GUI_MAX_HEIGHT = 1000
---A display gui message. Meant to be display the first time a player joins.
---@param player LuaPlayer
---@return boolean
function DisplayWelcomeTextGui(player)
if ((player.gui.screen["join_shared_spawn_wait_menu"] ~= nil) or
(player.gui.screen["buddy_wait_menu"] ~= nil) or
(player.gui.screen["buddy_request_menu"] ~= nil) or
(player.gui.screen["wait_for_spawn_dialog"] ~= nil)) then
log("DisplayWelcomeTextGui called while some other dialog is already displayed!")
return false
end
--Delete existing guis
if (player.gui.screen.welcome_msg ~= nil) then
player.gui.screen.welcome_msg.destroy()
end
if (player.gui.screen.spawn_opts ~= nil) then
player.gui.screen.spawn_opts.destroy()
end
if (player.gui.screen.self_reset_confirm ~= nil) then
player.gui.screen.self_reset_confirm.destroy()
end
local welcome_gui = player.gui.screen.add {
name = "welcome_msg",
type = "frame",
direction = "vertical",
caption = storage.ocfg.server_info.welcome_msg_title
}
welcome_gui.auto_center = true
welcome_gui.style.maximal_width = SPAWN_GUI_MAX_WIDTH
welcome_gui.style.maximal_height = SPAWN_GUI_MAX_HEIGHT
welcome_gui.style.padding = 5
local welcome_gui_if = welcome_gui.add {
type = "frame",
style = "inside_shallow_frame_with_padding",
direction = "vertical"
}
-- Informational message about the scenario
if (storage.ocfg.server_info.welcome_msg ~= " ") then
AddLabel(welcome_gui_if, nil, storage.ocfg.server_info.welcome_msg, my_label_style)
AddSpacer(welcome_gui_if)
end
-- Warnings about the scenario
AddLabel(welcome_gui_if, nil, { "oarc-scenario-info-warn-msg" }, my_note_style)
AddSpacer(welcome_gui_if)
AddLabel(welcome_gui_if, nil, { "oarc-experimental-warning" }, my_label_style)
-- Confirm button
local button_flow = welcome_gui.add {
type = "flow",
style = "dialog_buttons_horizontal_flow",
}
button_flow.style.horizontally_stretchable = true
local dragger = button_flow.add {
type = "empty-widget",
style = "draggable_space",
}
dragger.style.left_margin = 0
dragger.style.horizontally_stretchable = true
dragger.style.height = 30
local confirm_button = button_flow.add {
name = "welcome_okay_btn",
tags = { action = "oarc_spawn_options", setting = "welcome_okay" },
type = "button",
caption = { "oarc-i-understand" },
style = "confirm_button",
}
confirm_button.style.horizontal_align = "right"
return true
end
---Handle the gui click of the welcome msg
---@param event EventData.on_gui_click
---@return nil
function WelcomeTextGuiClick(event)
if not event.element.valid then return end
local player = game.players[event.player_index]
local buttonClicked = event.element.name
if (buttonClicked == "welcome_okay_btn") then
if (player.gui.screen.welcome_msg ~= nil) then
player.gui.screen.welcome_msg.destroy()
end
DisplaySpawnOptions(player)
end
end
---Creates the spawn menu gui frame to hold all the spawn options.
---@param player LuaPlayer
---@return LuaGuiElement
function CreateSpawnMenuGuiFrame(player)
local spawn_opts_frame = player.gui.screen.add {
name = "spawn_opts",
type = "frame",
direction = "vertical",
caption = { "oarc-spawn-options" }
}
spawn_opts_frame.style.maximal_width = SPAWN_GUI_MAX_WIDTH
spawn_opts_frame.style.maximal_height = SPAWN_GUI_MAX_HEIGHT
spawn_opts_frame.auto_center = true
spawn_opts_frame.style.padding = 5
local inside_frame = spawn_opts_frame.add {
name = "spawn_opts_if",
type = "frame",
style = "inside_shallow_frame",
direction = "vertical"
}
-- SUB HEADING w/ LABEL
local subhead = inside_frame.add{
type="frame",
name="sub_header",
style = "subheader_frame"
}
subhead.style.height = 46
subhead.style.horizontally_stretchable = true
AddLabel(subhead, "warning_lbl1", { "oarc-click-info-btn-help" }, my_note_style)
return inside_frame
end
---Show the surface select dropdown
---@param parent_flow LuaGuiElement
---@return nil
function CreateSurfaceSelectDropdown(parent_flow)
local surfacesHorizontalFlow = parent_flow.add {
name = "surfaces_horizontal_flow",
type = "flow",
direction = "horizontal"
}
local surface_list = GetAllowedSurfaces()
-- Get the index of the default surface if it exists
local default_surface_index = 1
for i,surface in ipairs(surface_list) do
if (surface == storage.ocfg.gameplay.default_surface) then
default_surface_index = i
break
end
end
AddLabel(surfacesHorizontalFlow, "surfacesHorizontalFlowLabel", "Select Surface: ", my_label_style)
surfacesHorizontalFlow.add {
name = "surface_select_dropdown",
tags = { action = "oarc_spawn_options", setting = "surface_select" },
type = "drop-down",
items = surface_list,
selected_index = default_surface_index,
tooltip = { "oarc-surface-select-tooltip" },
enabled = #surface_list > 1
}
end
---Display the team select radio buttons
---@param parent_flow LuaGuiElement
---@param enable_main_team boolean
---@param enable_separate_teams boolean
---@return nil
function DisplayTeamSelectRadioButtons(parent_flow, enable_main_team, enable_separate_teams)
if enable_main_team then
parent_flow.add {
name = "isolated_spawn_main_team_radio",
tags = { action = "oarc_spawn_options", setting = "team_select", value = SPAWN_TEAM_CHOICE.join_main_team },
type = "radiobutton",
caption = { "oarc-join-main-team-radio" },
tooltip = { "oarc-join-main-team-tooltip" },
-- If separate teams are not enabled, default to joining the main team, and disable the radio buttons.
state = true,
-- ignored_by_interaction = not enable_separate_teams,
-- enabled = enable_separate_teams
}
end
if (enable_separate_teams) then
parent_flow.add {
name = "isolated_spawn_new_team_radio",
tags = { action = "oarc_spawn_options", setting = "team_select", value = SPAWN_TEAM_CHOICE.join_own_team },
type = "radiobutton",
caption = { "oarc-create-own-team-radio" },
tooltip = { "oarc-create-own-team-tooltip" },
-- If main team is not enabled, default to joining the a separate team, and disable the radio buttons.
state = not enable_main_team,
-- ignored_by_interaction = not enable_main_team,
-- enabled = enable_main_team
}
end
end
---Create a distance select slider
---@param parent_flow LuaGuiElement
---@param minimum_distance number
---@param maximum_distance number
---@return nil
function CreateDistanceSelectSlider(parent_flow, minimum_distance, maximum_distance)
local slider_flow = parent_flow.add {
type = "flow",
direction = "horizontal",
style = "player_input_horizontal_flow"
}
slider_flow.style.horizontally_stretchable = true
local label = slider_flow.add {
type = "label",
caption = { "oarc-spawn-distance-slider-label" },
tooltip = { "oarc-spawn-distance-slider-tooltip" }
}
label.style.horizontal_align = "left"
local slider = slider_flow.add {
name = "spawn_distance_slider",
type = "slider",
tags = { action = "oarc_spawn_options", setting = "distance_select" },
minimum_value = minimum_distance,
maximum_value = maximum_distance,
value = minimum_distance,
discrete_slider = true,
value_step = 1,
tooltip = { "oarc-spawn-distance-slider-tooltip" }
}
slider.style.horizontally_stretchable = true
local text_value = slider_flow.add {
name = "spawn_distance_slider_value",
type = "textfield",
ignored_by_interaction = true,
caption = minimum_distance,
style = "slider_value_textfield",
text = tostring(minimum_distance)
}
text_value.style.horizontal_align = "right"
text_value.style.width = 50
end
---Create the spawn settings frame
---@param parent_flow LuaGuiElement
---@param gameplay OarcConfigGameplaySettings
---@return nil
function CreateSpawnSettingsFrame(parent_flow, gameplay)
local spawn_settings_frame = parent_flow.add {
name = "spawn_settings_frame",
type = "frame",
direction = "vertical",
style = "bordered_frame",
}
spawn_settings_frame.style.horizontally_stretchable = true
spawn_settings_frame.style.padding = 5
spawn_settings_frame.style.margin = 4
spawn_settings_frame.style.bottom_margin = 0
AddLabel(spawn_settings_frame, nil, { "oarc-spawn-menu-settings-header" }, my_label_header_style)
AddLabel(spawn_settings_frame, nil, { "oarc-spawn-menu-settings-info" }, my_label_style)
-- Pick surface
CreateSurfaceSelectDropdown(spawn_settings_frame)
-- Radio buttons to pick your team.
DisplayTeamSelectRadioButtons(spawn_settings_frame, gameplay.enable_main_team, gameplay.enable_separate_teams)
-- Allow players to spawn with a moat around their area.
if (gameplay.allow_moats_around_spawns) then
spawn_settings_frame.add {
name = "isolated_spawn_moat_option_checkbox",
tags = { action = "oarc_spawn_options", setting = "moat_option" },
type = "checkbox",
caption = { "oarc-moat-option" },
state = true, -- Default to true
tooltip = { "oarc-moat-option-tooltip" }
}
end
CreateDistanceSelectSlider(spawn_settings_frame, gameplay.near_spawn_distance, gameplay.far_spawn_distance)
end
---Create a frame and a confim button for player to request SOLO spawn creation
---@param parent_flow LuaGuiElement
---@param enable_shared_spawns boolean
---@param max_shared_players integer
---@return nil
function CreateSoloSpawnFrame(parent_flow, enable_shared_spawns, max_shared_players)
solo_spawn_frame = parent_flow.add {
name = "solo_spawn_frame",
type = "frame",
direction = "vertical",
style = "bordered_frame"
}
solo_spawn_frame.style.horizontally_stretchable = true
solo_spawn_frame.style.padding = 5
solo_spawn_frame.style.margin = 4
solo_spawn_frame.style.bottom_margin = 0
AddLabel(solo_spawn_frame, nil, { "oarc-spawn-menu-solo-header" }, my_label_header_style)
AddLabel(solo_spawn_frame, nil, { "oarc-starting-area-normal" }, my_label_style)
-- A note about sharing spawns
if enable_shared_spawns and (max_shared_players > 1) then
AddLabel(solo_spawn_frame, nil, { "oarc-max-players-shared-spawn", max_shared_players - 1 }, my_label_style)
end
local button_flow = solo_spawn_frame.add {
type = "flow",
direction = "horizontal"
}
button_flow.style.horizontal_align = "right"
button_flow.style.horizontally_stretchable = true
button_flow.add {
name = "spawn_request",
tags = { action = "oarc_spawn_options", setting = "spawn_request" },
type = "button",
caption = { "oarc-solo-spawn" },
tooltip = { "oarc-solo-spawn-tooltip" },
style = "green_button"
}
end
---Creates the shared spawn frame for joining another player's base
---@param parent_flow LuaGuiElement
---@param enable_shared_spawns boolean
---@return nil
function CreateSharedSpawnFrame(parent_flow, enable_shared_spawns)
local shared_spawn_frame = parent_flow.shared_spawn_frame
local prev_selected_host = nil ---@type string?
local prev_selected_spawn = nil ---@type OarcUniqueSpawn?
-- Create the shared spawn frame if it doesn't exist
if shared_spawn_frame == nil then
shared_spawn_frame = parent_flow.add {
name = "shared_spawn_frame",
type = "frame",
direction = "vertical",
style = "bordered_frame"
}
shared_spawn_frame.style.horizontally_stretchable = true
shared_spawn_frame.style.padding = 5
shared_spawn_frame.style.margin = 4
shared_spawn_frame.style.bottom_margin = 0
--- Let's us refresh the frame if it already exists instead of recreating it
else
-- Save the previous selected host so we can reselect it after the frame is recreated
if (shared_spawn_frame.shared_spawn_horizontal_flow ~= nil) then
local dropdown = shared_spawn_frame.shared_spawn_horizontal_flow.shared_spawn_select_dropdown
local index = dropdown.selected_index
if index > 0 then
prev_selected_host = dropdown.get_item(index) --[[@as string]]
end
end
for _,child in pairs(shared_spawn_frame.children) do
child.destroy()
end
end
AddLabel(shared_spawn_frame, nil, { "oarc-spawn-menu-shared-header" }, my_label_header_style)
if not enable_shared_spawns then
AddLabel(shared_spawn_frame, nil, { "oarc-shared-spawn-disabled" }, my_warning_style)
return
end
local avail_spawns = GetAvailableSharedSpawns()
local num_avail_spawns = #avail_spawns.hosts
if (num_avail_spawns > 0) then
AddLabel(shared_spawn_frame, nil, { "oarc-join-someone-info" }, my_label_style)
local new_selected_index = 0
if prev_selected_host then
for i,host in ipairs(avail_spawns.hosts) do
if host == prev_selected_host then
new_selected_index = i
break
end
end
end
local horizontal_flow = shared_spawn_frame.add {
name = "shared_spawn_horizontal_flow",
type = "flow",
direction = "horizontal",
style = "dialog_buttons_horizontal_flow"
}
horizontal_flow.style.horizontally_stretchable = true
local label = AddLabel(horizontal_flow, nil, { "oarc-join-someone-dropdown-label" }, my_label_style)
label.style.horizontal_align = "left"
local dropdown = horizontal_flow.add {
name = "shared_spawn_select_dropdown",
tags = { action = "oarc_spawn_options", setting = "shared_spawn_select" },
type = "drop-down",
items = avail_spawns.hosts,
selected_index = new_selected_index,
tooltip = { "oarc-join-someone-dropdown-tooltip" }
}
dropdown.style.horizontal_align = "left"
local dragger = horizontal_flow.add {
type = "empty-widget",
style = "draggable_space",
}
dragger.style.horizontally_stretchable = true
local button = horizontal_flow.add {
name = "join_other_spawn",
tags = { action = "oarc_spawn_options", setting = "join_other_spawn" },
type = "button",
tooltip = { "oarc-join-shared-button-tooltip" },
enabled = new_selected_index > 0
}
if new_selected_index == 0 then
button.caption = { "oarc-join-shared-button-disable" }
button.style = "red_button"
else
button.caption = { "oarc-join-shared-button-enable", prev_selected_host, avail_spawns.spawns[new_selected_index].surface_name }
button.style = "green_button"
end
button.style.horizontal_align = "right"
else
AddLabel(shared_spawn_frame, nil, { "oarc-no-shared-avail" }, my_label_style)
end
end
---Refresh the shared spawn frame if it exists
---@param player LuaPlayer
---@return nil
function RefreshSharedSpawnFrameIfExist(player)
local spawn_opts = player.gui.screen.spawn_opts
if spawn_opts == nil then return end
CreateSharedSpawnFrame(spawn_opts.spawn_opts_if, storage.ocfg.gameplay.enable_shared_spawns)
end
---Creates the buddy spawn frame for spawning with a buddy
---@param parent_flow LuaGuiElement
---@param player LuaPlayer
---@param enable_buddy_spawn boolean
---@param enable_separate_teams boolean
---@return nil
function CreateBuddySpawnFrame(parent_flow, player, enable_buddy_spawn, enable_separate_teams)
local buddy_spawn_frame = parent_flow.buddy_spawn_frame
local selected_buddy = nil ---@type string?
-- Create the buddy spawn frame if it doesn't exist
if buddy_spawn_frame == nil then
buddy_spawn_frame = parent_flow.add {
name = "buddy_spawn_frame",
type = "frame",
direction = "vertical",
style = "bordered_frame"
}
buddy_spawn_frame.style.horizontally_stretchable = true
buddy_spawn_frame.style.padding = 5
buddy_spawn_frame.style.margin = 4
--- Let's us refresh the frame if it already exists instead of recreating it
else
-- Save the previous selected buddy so we can reselect it after the frame is recreated
if buddy_spawn_frame.waiting_buddies_dropdown ~= nil then
local index = buddy_spawn_frame.waiting_buddies_dropdown.selected_index
if index > 0 then
selected_buddy = buddy_spawn_frame.waiting_buddies_dropdown.get_item(index) --[[@as string]]
--- Make sure the buddy is still valid?
if game.players[selected_buddy] and game.players[selected_buddy].gui.screen.spawn_opts == nil then
selected_buddy = nil
end
end
end
for _,child in pairs(buddy_spawn_frame.children) do
child.destroy()
end
end
AddLabel(buddy_spawn_frame, nil, { "oarc-spawn-menu-buddy-header" }, my_label_header_style)
if not enable_buddy_spawn then
AddLabel(buddy_spawn_frame, nil, { "oarc-buddy-spawn-disabled" }, my_warning_style)
return
end
-- Warnings and explanations...
AddLabel(buddy_spawn_frame, nil, { "oarc-buddy-spawn-instructions" }, my_label_style)
if (enable_separate_teams) then
buddy_spawn_frame.add {
tags = { action = "oarc_spawn_options", setting = "buddy_team_select" },
type = "checkbox",
caption = { "oarc-create-buddy-team" },
state = false,
tooltip = { "oarc-create-buddy-team-tooltip" }
}
end
---@type string[]
local avail_buddies = GetOtherPlayersInSpawnMenu(player)
log("Available buddies: " .. serpent.block(avail_buddies))
local previous_index = 0
if selected_buddy then
for i,host in ipairs(avail_buddies) do
if host == selected_buddy then
previous_index = i
break
end
end
end
local buddy_button_horizontal_flow = buddy_spawn_frame.add {
type = "flow",
direction = "horizontal",
style = "dialog_buttons_horizontal_flow"
}
buddy_button_horizontal_flow.style.horizontally_stretchable = true
local label = AddLabel(buddy_button_horizontal_flow, nil, { "oarc-buddy-select-label" }, my_label_style)
label.style.horizontal_align = "left"
local buddy_dropdown = buddy_button_horizontal_flow.add {
name = "waiting_buddies_dropdown",
tags = { action = "oarc_spawn_options", setting = "buddy_select" },
type = "drop-down",
items = avail_buddies,
selected_index = previous_index,
tooltip = { "oarc-buddy-select-tooltip" }
}
buddy_dropdown.style.horizontal_align = "left"
local empty = buddy_button_horizontal_flow.add {
type = "empty-widget",
style = "draggable_space",
}
empty.style.horizontally_stretchable = true
local button = buddy_button_horizontal_flow.add {
name = "buddy_spawn_request",
tags = { action = "oarc_spawn_options", setting = "buddy_spawn_request" },
type = "button",
caption = { "oarc-buddy-spawn" },
style = "green_button",
tooltip = { "oarc-buddy-spawn-tooltip" }
}
button.style.horizontal_align = "right"
end
---Refresh the buddy list without recreating any GUI elements
---@param player LuaPlayer
---@param dropdown LuaGuiElement The buddy dropdown element
---@return nil
function RefreshBuddyList(player, dropdown)
dropdown.items = GetOtherPlayersInSpawnMenu(player)
end
---Display the spawn options and explanation
---@param player LuaPlayer
---@return nil
function DisplaySpawnOptions(player)
if (player == nil) then
log("DisplaySpawnOptions with no valid player...")
return
end
if (player.gui.screen.spawn_opts ~= nil) then
log("Tried to display spawn options when it was already displayed!")
return
end
-- Get gameplay settings from config
---@type OarcConfigGameplaySettings
local gameplay = storage.ocfg.gameplay
-- Create the primary frame and a warning label
local sGui = CreateSpawnMenuGuiFrame(player)
-- Create the default settings entry for the OarcSpawnChoices table
local default_team = SPAWN_TEAM_CHOICE.join_main_team
if (not gameplay.enable_main_team and gameplay.enable_separate_teams) then
default_team = SPAWN_TEAM_CHOICE.join_own_team
end
---@type OarcSpawnChoices
local spawn_choices_entry = {
surface_name = gameplay.default_surface,
team = default_team,
moat = gameplay.allow_moats_around_spawns,
buddy = nil,
distance = gameplay.near_spawn_distance,
host = nil,
buddy_team = false
}
storage.spawn_choices[player.name] = spawn_choices_entry
CreateSpawnSettingsFrame(sGui, gameplay) -- The settings for configuring a spawn
CreateSoloSpawnFrame(sGui, gameplay.enable_shared_spawns, gameplay.number_of_players_per_shared_spawn) -- The primary method of spawning
CreateSharedSpawnFrame(sGui, gameplay.enable_shared_spawns) -- Spawn options to join another player's base.
CreateBuddySpawnFrame(sGui, player, gameplay.enable_buddy_spawn, gameplay.enable_separate_teams) -- Awesome buddy spawning system
script.raise_event("oarc-mod-on-spawn-choices-gui-displayed", { player_index = player.index, gui_element = sGui })
end
---This just updates the radio buttons/checkboxes when players click them.
---@param event EventData.on_gui_checked_state_changed
---@return nil
function SpawnOptsRadioSelect(event)
if not event.element.valid then return end
local elemName = event.element.name
local player = game.players[event.player_index]
local tags = event.element.tags
if (tags.action ~= "oarc_spawn_options") then
return
end
if (tags.setting == "team_select") then
storage.spawn_choices[player.name].team = tags.value --[[@as SpawnTeamChoice]]
-- Need to handle the radio button logic manually
if (elemName == "isolated_spawn_main_team_radio") then
if (event.element.parent.isolated_spawn_new_team_radio ~= nil) then
event.element.parent.isolated_spawn_new_team_radio.state = false
end
elseif (elemName == "isolated_spawn_new_team_radio") then
if (event.element.parent.isolated_spawn_main_team_radio ~= nil) then
event.element.parent.isolated_spawn_main_team_radio.state = false
end
end
elseif (tags.setting == "buddy_team_select") then
storage.spawn_choices[player.name].buddy_team = event.element.state
elseif (tags.setting == "moat_option") then
storage.spawn_choices[player.name].moat = event.element.state
end
end
---Handle the gui click of the spawn options
---@param event EventData.on_gui_click
---@return nil
function SpawnOptsGuiClick(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_spawn_options") then
return
end
if (tags.setting == "welcome_okay") then
if (player.gui.screen.welcome_msg ~= nil) then
player.gui.screen.welcome_msg.destroy()
end
DisplaySpawnOptions(player)
elseif (tags.setting == "spawn_request") then
PrimarySpawnRequest(player)
elseif (tags.setting == "join_other_spawn") then
RequestToJoinSharedSpawn(player)
elseif (tags.setting == "cancel_shared_spawn_wait_menu") then
CancelSharedSpawnRequest(player)
elseif (tags.setting == "buddy_select") then
RefreshBuddyList(player, event.element)
elseif (tags.setting == "buddy_spawn_request") then
RequestBuddySpawn(player)
elseif (tags.setting == "cancel_buddy_wait_menu") then
CancelBuddySpawnWaitMenu(player)
elseif (tags.setting == "accept_buddy_request") then
AcceptBuddyRequest(player, tags.requesting_buddy_name --[[@as string]])
elseif (tags.setting == "reject_buddy_request") then
RejectBuddyRequest(player, tags.requesting_buddy_name --[[@as string]])
elseif (tags.setting == "surface_select") then
event.element.items = GetAllowedSurfaces()
end
end
---Request a buddy spawn. Requires the buddy to accept or reject the request.
---@param player LuaPlayer
---@return nil
function RequestBuddySpawn(player)
local buddy_choice = storage.spawn_choices[player.name].buddy
if (buddy_choice == nil) then SendErrorMsg(player, { "oarc-invalid-buddy" }) return end
local buddy = game.players[buddy_choice]
if (buddy == nil) then SendErrorMsg(player, { "oarc-invalid-buddy" }) return end
-- Confirm the buddy is still in the spawn menu!
if (buddy.gui.screen.spawn_opts == nil) then SendErrorMsg(player, { "oarc-invalid-buddy", buddy.name }) return end
DisplayBuddySpawnWaitMenu(player)
DisplayBuddySpawnRequestMenu(buddy, player.name)
end
---Handle buddy spawn wait menu cancel (waiting for buddy to accept or decline proposal)
---@param player LuaPlayer
---@return nil
function CancelBuddySpawnWaitMenu(player)
---@type OarcSpawnChoices
local spawn_choices = storage.spawn_choices[player.name]
local buddy = game.players[spawn_choices.buddy]
player.gui.screen.buddy_wait_menu.destroy()
DisplaySpawnOptions(player)
-- Catch a case where the buddy has left the game early and no longer exists.
if (buddy == nil) then
return
end
if (buddy.gui.screen.buddy_request_menu ~= nil) then
buddy.gui.screen.buddy_request_menu.destroy()
DisplaySpawnOptions(buddy)
end
CompatSend(buddy, { "oarc-buddy-cancel-request", player.name })
end
---Request to join someone's shared spawn
---@param player LuaPlayer
---@return nil
function RequestToJoinSharedSpawn(player)
if (player.gui.screen.spawn_opts ~= nil) then
player.gui.screen.spawn_opts.destroy()
end
local host_name = storage.spawn_choices[player.name].host_name
if (host_name == nil) then SendErrorMsg(player, { "oarc-no-shared-spawn-selected" }) return end
-- Clear the spawn options gui
if (player.gui.screen.spawn_opts ~= nil) then
player.gui.screen.spawn_opts.destroy()
end
if ((game.players[host_name] ~= nil) and (game.players[host_name].connected)) then
local primary_spawn = FindPrimaryUniqueSpawn(host_name)
table.insert(storage.unique_spawns[primary_spawn.surface_name][host_name].join_queue, player.name)
-- Display wait menu with cancel button.
DisplaySharedSpawnJoinWaitMenu(player)
-- Tell other player they are requesting a response. Print it with color orange
CompatSend(game.players[host_name], { "oarc-player-requesting-join-you", player.name }, { color = { r = 1, g = 0.5, b = 0 }, sound_path = "utility/scenario_message" })
OarcGuiRefreshContent(game.players[host_name])
else
SendErrorMsg(player, { "oarc-invalid-host-shared-spawn" })
DisplaySpawnOptions(player)
end
end
---Cancel shared spawn request from the waiting menu
---@param player LuaPlayer
---@return nil
function CancelSharedSpawnRequest(player)
local host_name = storage.spawn_choices[player.name].host_name
if (host_name ~= nil) and (game.players[host_name] ~= nil) then
CompatSend(game.players[host_name], { "oarc-player-cancel-join-request", player.name })
end
--- Destroy the waiting menu and display the spawn options again.
player.gui.screen.join_shared_spawn_wait_menu.destroy()
DisplaySpawnOptions(player)
RemovePlayerFromJoinQueue(player.name)
log("ERROR! Failed to remove player from joinQueue?!")
end
---Handle slider value changes
---@param event EventData.on_gui_value_changed
---@return nil
function SpawnOptsValueChanged(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_spawn_options") then
return
end
if (tags.setting == "distance_select") then
local distance = event.element.slider_value
storage.spawn_choices[player.name].distance = distance
event.element.parent.spawn_distance_slider_value.text = tostring(distance)
-- log("GUI DEBUG Selected distance: " .. distance)
end
end
---Handle dropdown selection changes
---@param event EventData.on_gui_selection_state_changed
---@return nil
function SpawnOptsSelectionChanged(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_spawn_options") then
return
end
if (tags.setting == "surface_select") then
local index = event.element.selected_index
local surface_name = event.element.get_item(index) --[[@as string]]
storage.spawn_choices[player.name].surface_name = surface_name
log("GUI DEBUG Selected surface: " .. surface_name)
elseif (tags.setting == "shared_spawn_select") then
SharedSpawnSelect(event.element, player)
elseif (tags.setting == "buddy_select") then
local index = event.element.selected_index
if (index > 0) then
local buddyName = event.element.get_item(index) --[[@as string]]
storage.spawn_choices[player.name].buddy = buddyName
log("GUI DEBUG Selected buddy: " .. buddyName)
else
storage.spawn_choices[player.name].buddy = nil
end
end
end
---Handles the shared spawn host dropdown selection. Updates the join button and sets the host spawn choice entry.
---@param gui_element LuaGuiElement
---@param player LuaPlayer
---@return nil
function SharedSpawnSelect(gui_element, player)
local index = gui_element.selected_index
if (index > 0) then
local host_name = gui_element.get_item(index) --[[@as string]]
local button = gui_element.parent.join_other_spawn
local primary_spawn = FindPrimaryUniqueSpawn(host_name)
if (primary_spawn and
IsSharedSpawnOpen(primary_spawn.surface_name, host_name) and
not IsSharedSpawnFull(primary_spawn.surface_name, host_name)) then
storage.spawn_choices[player.name].host_name = host_name
button.enabled = true
button.caption = { "oarc-join-shared-button-enable", host_name, primary_spawn.surface_name }
button.style = "green_button"
else
SendErrorMsg(player, { "oarc-invalid-host-shared-spawn" })
storage.spawn_choices[player.name].host_name = nil
gui_element.selected_index = 0
button.enabled = false
button.caption = { "oarc-join-shared-button-disable" }
button.style = "red_button"
end
else
storage.spawn_choices[player.name].host_name = nil
end
end
---Requests the generation of a spawn point for the player (their first primary spawn)
---@param player LuaPlayer
---@return nil
function PrimarySpawnRequest(player)
-- Get the player's spawn choices
---@type OarcSpawnChoices
local spawn_choices = storage.spawn_choices[player.name]
if (spawn_choices == nil) then error("ERROR! No spawn choices found for player!") return end
-- N/A for solo spawns so clear these!
storage.spawn_choices[player.name].host_name = nil
storage.spawn_choices[player.name].buddy = nil
-- Find coordinates of a good place to spawn
local spawn_position = FindUngeneratedCoordinates(spawn_choices.surface_name, spawn_choices.distance, 3)
-- If that fails, just throw a warning and don't spawn them. They can try again.
if ((spawn_position.x == 0) and (spawn_position.y == 0)) then
SendErrorMsg(player, { "oarc-no-ungenerated-land-error" })
return
end
-- Create a new force for player if they choose that radio button
if spawn_choices.team ~= SPAWN_TEAM_CHOICE.join_main_team then
CreatePlayerCustomForce(player)
end
-- Queue spawn generation and the player.
local delayed_spawn = GenerateNewSpawn(player.name, spawn_choices.surface_name, spawn_position, spawn_choices, true)
QueuePlayerForSpawn(player.name, delayed_spawn)
SendBroadcastMsg({"", { "oarc-player-is-joining", player.name, spawn_choices.surface_name }, " ", GetGPStext(spawn_choices.surface_name, spawn_position)}, {color = player.color})
-- Unlock spawn control gui tab
SetOarcGuiTabEnabled(player, OARC_SPAWN_CTRL_TAB_NAME, true)
-- Destroy the spawn options gui
if (player.gui.screen.spawn_opts ~= nil) then
player.gui.screen.spawn_opts.destroy()
end
end
---Display shared spawn join wait menu to the requesting player
---@param player LuaPlayer
---@return nil
function DisplaySharedSpawnJoinWaitMenu(player)
if (player.gui.screen.spawn_opts ~= nil) then
player.gui.screen.spawn_opts.destroy()
end
local sGui = player.gui.screen.add {
name = "join_shared_spawn_wait_menu",
type = "frame",
direction = "vertical",
caption = { "oarc-waiting-for-spawn-owner" }
}
sGui.auto_center = true
sGui.style.maximal_width = SPAWN_GUI_MAX_WIDTH
sGui.style.maximal_height = SPAWN_GUI_MAX_HEIGHT
sGui.style.padding = 5
local sGui_if = sGui.add {
type = "frame",
direction = "vertical",
style = "inside_shallow_frame_with_padding"
}
-- Warnings and explanations...
AddLabel(sGui_if, "warning_lbl1", { "oarc-you-will-spawn-once-host" }, my_note_style)
local button_flow = sGui.add {
type = "flow",
direction = "horizontal",
style = "dialog_buttons_horizontal_flow"
}
button_flow.style.horizontally_stretchable = true
local cancel_button = button_flow.add {
name = "cancel_shared_spawn_wait_menu",
type = "button",
tags = { action = "oarc_spawn_options", setting = "cancel_shared_spawn_wait_menu" },
caption = { "oarc-cancel-button-caption" },
tooltip = { "oarc-return-to-previous-tooltip" },
style = "back_button"
}
local dragger = button_flow.add{type="empty-widget", style="draggable_space"}
dragger.style.right_margin = 0
dragger.style.horizontally_stretchable = true
dragger.style.height = 30
end
---Display the buddy spawn wait menu
---@param player LuaPlayer
---@return nil
function DisplayBuddySpawnWaitMenu(player)
if (player.gui.screen.spawn_opts ~= nil) then
player.gui.screen.spawn_opts.destroy()
end
local buddy_wait_menu = player.gui.screen.add {
name = "buddy_wait_menu",
type = "frame",
direction = "vertical",
caption = { "oarc-waiting-for-buddy" }
}
buddy_wait_menu.auto_center = true
buddy_wait_menu.style.maximal_width = SPAWN_GUI_MAX_WIDTH
buddy_wait_menu.style.maximal_height = SPAWN_GUI_MAX_HEIGHT
buddy_wait_menu.style.padding = 5
local buddy_wait_menu_if = buddy_wait_menu.add {
type = "frame",
direction = "vertical",
style = "inside_shallow_frame_with_padding"
}
-- Warnings and explanations...
AddLabel(buddy_wait_menu_if, nil, { "oarc-wait-buddy-select-yes" }, my_note_style)
AddSpacer(buddy_wait_menu_if)
local button_flow = buddy_wait_menu.add {
type = "flow",
direction = "horizontal",
style = "dialog_buttons_horizontal_flow"
}
button_flow.style.horizontally_stretchable = true
local cancel_button = button_flow.add {
name = "cancel_buddy_wait_menu",
tags = { action = "oarc_spawn_options", setting = "cancel_buddy_wait_menu" },
type = "button",
style = "back_button",
caption = { "oarc-cancel-button-caption" },
tooltip = { "oarc-return-to-previous-tooltip" },
}
local dragger = button_flow.add{type="empty-widget", style="draggable_space"}
dragger.style.right_margin = 0
dragger.style.horizontally_stretchable = true
dragger.style.height = 30
end
---Display the buddy spawn request menu
---@param player LuaPlayer The player that is receiving the buddy request
---@param requesting_buddy_name string The name of the player that is requesting to buddy spawn
---@return nil
function DisplayBuddySpawnRequestMenu(player, requesting_buddy_name)
if (player.gui.screen.spawn_opts ~= nil) then
player.gui.screen.spawn_opts.destroy()
end
local buddy_request_gui = player.gui.screen.add {
name = "buddy_request_menu",
type = "frame",
direction = "vertical",
caption = { "oarc-buddy-spawn-request-header" }
}
buddy_request_gui.auto_center = true
buddy_request_gui.style.maximal_width = SPAWN_GUI_MAX_WIDTH
buddy_request_gui.style.maximal_height = SPAWN_GUI_MAX_HEIGHT
buddy_request_gui.style.padding = 5
local buddy_request_gui_if = buddy_request_gui.add {
type = "frame",
direction = "vertical",
style = "inside_shallow_frame_with_padding"
}
-- Warnings and explanations...
AddLabel(buddy_request_gui_if, nil, { "oarc-buddy-requesting-from-you", requesting_buddy_name }, my_note_style)
AddSpacer(buddy_request_gui_if)
---@type OarcSpawnChoices
local spawn_choices = storage.spawn_choices[requesting_buddy_name]
---@type LocalisedString
local teamText = "error!"
if (spawn_choices.buddy_team) then
teamText = { "oarc-buddy-txt-buddy-team" }
elseif (spawn_choices.team == SPAWN_TEAM_CHOICE.join_main_team) then
teamText = { "oarc-buddy-txt-main-team" }
elseif (spawn_choices.team == SPAWN_TEAM_CHOICE.join_own_team) then
teamText = { "oarc-buddy-txt-new-teams" }
end
---@type LocalisedString
local moatText = " "
if (spawn_choices.moat) then
moatText = { "oarc-buddy-txt-moat" }
end
---@type LocalisedString
local surfaceText = { "oarc-buddy-txt-surface", spawn_choices.surface_name}
---@type LocalisedString
local distText = { "oarc-buddy-txt-distance", spawn_choices.distance}
---@type LocalisedString
local requestText = { "", requesting_buddy_name, { "oarc-buddy-txt-would-like" }, " ", teamText, { "oarc-buddy-txt-next-to-you" },
moatText, surfaceText, distText }
AddLabel(buddy_request_gui_if, nil, requestText, my_label_style)
local button_flow = buddy_request_gui.add {
type = "flow",
direction = "horizontal",
style = "dialog_buttons_horizontal_flow"
}
button_flow.style.horizontally_stretchable = true
local reject_button = button_flow.add {
name = "reject_buddy_request",
tags = { action = "oarc_spawn_options", setting = "reject_buddy_request", requesting_buddy_name = requesting_buddy_name },
type = "button",
style = "red_back_button",
caption = { "oarc-reject" }
}
reject_button.style.horizontal_align = "left"
local dragger = button_flow.add{type="empty-widget", style="draggable_space"}
dragger.style.horizontally_stretchable = true
dragger.style.height = 30
local accept_button = button_flow.add {
name = "accept_buddy_request",
tags = { action = "oarc_spawn_options", setting = "accept_buddy_request", requesting_buddy_name = requesting_buddy_name },
type = "button",
style = "confirm_button",
caption = { "oarc-accept" }
}
accept_button.style.horizontal_align = "right"
end
---Handles spawning the buddies together once the request has been accepted
---@param player LuaPlayer The player that is accepting the buddy request
---@param requesting_buddy_name string The name of the player that is requesting to buddy spawn
---@return nil
function AcceptBuddyRequest(player, requesting_buddy_name)
---@type OarcSpawnChoices
local spawn_choices = storage.spawn_choices[requesting_buddy_name]
local requesting_buddy = game.players[requesting_buddy_name]
local surface = game.surfaces[spawn_choices.surface_name]
-- Copy the buddy's spawn choices to the accepting player
spawn_choices.host_name = nil -- N/A for buddy spawns so clear it.
storage.spawn_choices[player.name] = table.deepcopy(spawn_choices)
storage.spawn_choices[player.name].buddy = requesting_buddy_name
local buddy_choices = storage.spawn_choices[player.name]
-- Find coordinates of a good place to spawn
local spawn_position = FindUngeneratedCoordinates(spawn_choices.surface_name, spawn_choices.distance, 3)
-- If that fails, just throw a warning and don't spawn them. They can try again.
if ((spawn_position.x == 0) and (spawn_position.y == 0)) then
SendErrorMsg(player, { "oarc-no-ungenerated-land-error" })
return
end
-- Create a new force for the combined players if they chose that option
if spawn_choices.buddy_team then
local buddyForce = CreatePlayerCustomForce(requesting_buddy)
player.force = buddyForce
-- Create a new force for each player if they chose that option
elseif spawn_choices.team == SPAWN_TEAM_CHOICE.join_own_team then
CreatePlayerCustomForce(player)
CreatePlayerCustomForce(requesting_buddy)
end
-- Destroy GUIs
if (requesting_buddy.gui.screen.buddy_wait_menu ~= nil) then
requesting_buddy.gui.screen.buddy_wait_menu.destroy()
end
if (player.gui.screen.buddy_request_menu ~= nil) then
player.gui.screen.buddy_request_menu.destroy()
end
-- Create that spawn in the global vars
local buddy_position = GetBuddySpawnPosition(spawn_position, spawn_choices.surface_name, spawn_choices.moat)
-- Queue spawn generation for the requesting buddy FIRST. (left)
local delayed_spawn = GenerateNewSpawn(requesting_buddy_name, spawn_choices.surface_name, spawn_position, spawn_choices, true)
QueuePlayerForSpawn(requesting_buddy_name, delayed_spawn)
-- ORDER MATTERS! Otherwise sometimes chunks don't generate properly!
-- Queue spawn generation for the accepting buddy SECOND. (right)
local delayed_spawn = GenerateNewSpawn(player.name, buddy_choices.surface_name, buddy_position, buddy_choices, true)
QueuePlayerForSpawn(player.name, delayed_spawn)
SendBroadcastMsg({"", {"oarc-buddies-are-joining", requesting_buddy_name, player.name, spawn_choices.surface_name}, " ", GetGPStext(spawn_choices.surface_name, spawn_position)})
-- Unlock spawn control gui tab
SetOarcGuiTabEnabled(player, OARC_SPAWN_CTRL_TAB_NAME, true)
SetOarcGuiTabEnabled(requesting_buddy, OARC_SPAWN_CTRL_TAB_NAME, true)
storage.buddy_pairs[player.name] = requesting_buddy_name
storage.buddy_pairs[requesting_buddy_name] = player.name
end
---Rejects a buddy spawn request proposal
---@param player LuaPlayer The player that is rejecting the buddy request
---@param requesting_buddy_name string The name of the player that is requesting to buddy spawn
---@return nil
function RejectBuddyRequest(player, requesting_buddy_name)
player.gui.screen.buddy_request_menu.destroy()
DisplaySpawnOptions(player)
local requester_buddy = game.players[requesting_buddy_name]
if (requester_buddy == nil) then
return
end
if (requester_buddy.gui.screen.buddy_wait_menu ~= nil) then
requester_buddy.gui.screen.buddy_wait_menu.destroy()
DisplaySpawnOptions(requester_buddy)
end
CompatSend(requester_buddy, { "oarc-buddy-declined", player.name })
end
---Display the please wait dialog
---@param player LuaPlayer
---@param delay_seconds integer
---@param surface LuaSurface
---@param position MapPosition
---@return nil
function DisplayPleaseWaitForSpawnDialog(player, delay_seconds, surface, position)
local pleaseWaitGui = player.gui.screen.add { name = "wait_for_spawn_dialog",
type = "frame",
direction = "vertical",
caption = { "oarc-spawn-wait" } }
pleaseWaitGui.auto_center = true
pleaseWaitGui.style.maximal_width = SPAWN_GUI_MAX_WIDTH
pleaseWaitGui.style.maximal_height = SPAWN_GUI_MAX_HEIGHT
-- Warnings and explanations...
local wait_warning_text = { "oarc-wait-text", delay_seconds }
AddLabel(pleaseWaitGui, "warning_lbl1", wait_warning_text, my_warning_style)
local pleaseWaitGui_if = pleaseWaitGui.add {
type = "frame",
direction = "vertical",
style = "inside_shallow_frame_with_padding"
}
pleaseWaitGui_if.add {
type = "minimap",
position = position,
surface_index = surface.index,
force = player.force.name
}
end
---Get a list of OTHER players currently in the spawn menu
---@param self_player LuaPlayer
---@return table
function GetOtherPlayersInSpawnMenu(self_player)
local other_players_in_spawn_menu = {}
for _, player in pairs(game.connected_players) do
if (player.gui.screen.spawn_opts ~= nil) and (self_player ~= player) then
table.insert(other_players_in_spawn_menu, player.name)
end
end
return other_players_in_spawn_menu
end
---Gui click event handlers
---@param event EventData.on_gui_click
---@return nil
function SeparateSpawnsGuiClick(event)
WelcomeTextGuiClick(event)
SpawnOptsGuiClick(event)
end
---Gui checked state changed event handlers
---@param event EventData.on_gui_checked_state_changed
---@return nil
function SeparateSpawnsGuiCheckedStateChanged(event)
SpawnOptsRadioSelect(event)
end
---Gui value changed event handlers
---@param event EventData.on_gui_value_changed
---@return nil
function SeparateSpawnsGuiValueChanged(event)
SpawnOptsValueChanged(event)
end
---Gui selection state changed event handlers
---@param event EventData.on_gui_selection_state_changed
---@return nil
function SeparateSpawnsGuiSelectionStateChanged(event)
SpawnOptsSelectionChanged(event)
end