PlanetPvPGreen/observer.lua
2026-09-30 21:13:44 -05:00

571 lines
13 KiB
Lua

local mod_gui = require("__core__.lualib.mod-gui")
local observer = {}
local PLANETS = {
"nauvis",
"vulcanus",
"fulgora",
"gleba",
"aquilo"
}
local function get_data(player)
storage.observers = storage.observers or {}
if not storage.observers[player.index] then
storage.observers[player.index] = {
following = nil,
perspective = "observer",
gui_open = false
}
end
return storage.observers[player.index]
end
local function is_observer(player)
return player.controller_type == defines.controllers.spectator
end
local function create_observer_force()
local force = game.forces["observer"]
if not force then
force = game.create_force("observer")
end
return force
end
observer.create_force = create_observer_force
function observer.create_button(player)
if not is_observer(player) then
return
end
local flow = mod_gui.get_button_flow(player)
if flow.observer_button then
return
end
flow.add{
type = "sprite-button",
name = "observer_button",
sprite = "utility/search_icon",
tooltip = "Observer"
}
end
function observer.destroy_button(player)
local flow = mod_gui.get_button_flow(player)
if flow.observer_button then
flow.observer_button.destroy()
end
if player.gui.screen.observer_window then
player.gui.screen.observer_window.destroy()
end
end
local function build_window(player)
local data = get_data(player)
local selected_force_name = nil
if data.perspective
and data.perspective ~= "observer"
and storage.planet_teams then
selected_force_name = storage.planet_teams[data.perspective]
end
if player.gui.screen.observer_window then
player.gui.screen.observer_window.destroy()
end
local frame = player.gui.screen.add{
type = "frame",
name = "observer_window",
direction = "vertical",
caption = "Observer"
}
frame.auto_center = true
player.opened = frame
local perspective_header = frame.add{
type = "flow",
direction = "horizontal"
}
perspective_header.add{
type = "sprite",
sprite = "utility/search_icon"
}
perspective_header.add{
type = "label",
caption = "Perspective"
}
local perspective_flow = frame.add{
type = "flow",
direction = "horizontal"
}
for _, planet in ipairs(PLANETS) do
perspective_flow.add{
type = "sprite-button",
name = "observer_perspective_" .. planet,
sprite = "space-location/" .. planet,
tooltip = planet:gsub("^%l", string.upper),
style = "slot_button",
toggled = data.perspective == planet
}
end
frame.add{
type = "line"
}
local perspective_caption = "None selected"
if selected_force_name then
perspective_caption = selected_force_name
end
frame.add{
type = "label",
caption = "Viewing: " .. perspective_caption
}
frame.add{
type = "line"
}
local platform_header = frame.add{
type = "flow",
direction = "horizontal"
}
platform_header.add{
type = "sprite",
sprite = "item/space-platform-foundation"
}
platform_header.add{
type = "label",
caption = "Team Platforms"
}
local platform_scroll = frame.add{
type = "scroll-pane",
name = "observer_platforms"
}
platform_scroll.style.maximal_height = 220
local platforms_found = 0
if selected_force_name then
for _, surface in pairs(game.surfaces) do
local platform = surface.platform
if platform
and platform.valid
and platform.force
and platform.force.name == selected_force_name then
local caption =
"[img=item/space-platform-foundation] "
.. platform.name
if platform.space_location then
caption = caption
.. " (" .. platform.space_location.name .. ")"
elseif platform.space_connection then
caption = caption .. " (traveling)"
end
platform_scroll.add{
type = "button",
name = "observer_platform_" .. platform.index,
caption = caption
}
platforms_found = platforms_found + 1
end
end
end
if not selected_force_name then
platform_scroll.add{
type = "label",
caption = "Select a team perspective."
}
elseif platforms_found == 0 then
platform_scroll.add{
type = "label",
caption = "No platforms owned by this team."
}
end
frame.add{
type = "line"
}
local player_header = frame.add{
type = "flow",
direction = "horizontal"
}
player_header.add{
type = "sprite",
sprite = "entity/character"
}
player_header.add{
type = "label",
caption = "Team Players"
}
local scroll = frame.add{
type = "scroll-pane",
name = "observer_players"
}
scroll.style.maximal_height = 320
local players_found = 0
for _, target in pairs(game.connected_players) do
local target_is_observer =
storage.observers
and storage.observers[target.index] ~= nil
local belongs_to_selected_team =
selected_force_name
and target.force
and target.force.name == selected_force_name
if belongs_to_selected_team and not target_is_observer then
local caption = "[img=entity/character] " .. target.name
if not target.character then
caption = caption .. " (dead)"
elseif target.surface and target.surface.platform then
caption = caption .. " (platform)"
elseif target.surface then
caption = caption .. " (" .. target.surface.name .. ")"
end
scroll.add{
type = "button",
name = "observer_follow_" .. target.index,
caption = caption
}
players_found = players_found + 1
end
end
if not selected_force_name then
scroll.add{
type = "label",
caption = "Select a team perspective."
}
elseif players_found == 0 then
scroll.add{
type = "label",
caption = "No connected non-observer players on this team."
}
end
frame.add{
type = "line"
}
frame.add{
type = "button",
name = "observer_stop_follow",
caption = "[img=utility/close] Stop Following"
}
data.gui_open = true
end
function observer.toggle_window(player)
if not player or not player.valid then
return
end
if not is_observer(player) then
return
end
local data = get_data(player)
local window = player.gui.screen.observer_window
if window and window.valid then
if player.opened == window then
player.opened = nil
end
window.destroy()
data.gui_open = false
else
build_window(player)
end
end
function observer.on_gui_click(event)
local player = game.get_player(event.player_index)
if not player then
return false
end
if not is_observer(player) then
return false
end
local element = event.element
if not (element and element.valid) then
return false
end
local data = get_data(player)
if element.name == "observer_button" then
if player.gui.screen.observer_window then
player.gui.screen.observer_window.destroy()
data.gui_open = false
else
build_window(player)
end
return true
end
if element.name:find("^observer_perspective_") then
local perspective =
element.name:gsub("^observer_perspective_", "")
local force_name =
storage.planet_teams
and storage.planet_teams[perspective]
local force =
force_name
and game.forces[force_name]
if force then
player.force = force
data.perspective = perspective
data.following = nil
local surface = game.surfaces[perspective]
if surface then
player.teleport({0, 0}, surface)
end
player.print("Perspective: " .. force.name)
build_window(player)
else
player.print(
"Force not found for perspective: "
.. tostring(perspective)
)
end
return true
end
if element.name:find("^observer_follow_") then
local id = tonumber(element.name:match("%d+"))
if id then
local target = game.get_player(id)
if target and target.valid and target.character and target.character.valid then
data.following = id
player.centered_on = target.character
player.print("Now following " .. target.name)
else
player.print("That player is not currently available to follow.")
end
end
return true
end
if element.name:find("^observer_platform_") then
local platform_index =
tonumber(element.name:match("%d+"))
local selected_platform = nil
if platform_index then
for _, surface in pairs(game.surfaces) do
local platform = surface.platform
if platform
and platform.valid
and platform.index == platform_index then
selected_platform = platform
break
end
end
end
if selected_platform
and selected_platform.surface
and selected_platform.surface.valid then
data.following = nil
player.teleport(
{0, 0},
selected_platform.surface
)
player.print(
"Viewing platform: "
.. selected_platform.name
)
else
player.print("Platform is no longer available.")
build_window(player)
end
return true
end
if element.name == "observer_stop_follow" then
data.following = nil
player.print("Stopped following.")
return true
end
end
function observer.on_tick(event)
if not storage.observers then
return
end
for player_index, data in pairs(storage.observers) do
local observer_player = game.get_player(player_index)
if observer_player
and observer_player.valid
and observer_player.controller_type == defines.controllers.spectator
and data.following then
local target = game.get_player(data.following)
if target
and target.valid
and target.connected
and target.character then
if observer_player.surface ~= target.surface then
observer_player.teleport(
target.position,
target.surface
)
else
local dx = observer_player.position.x - target.position.x
local dy = observer_player.position.y - target.position.y
if math.abs(dx) > 2
or math.abs(dy) > 2 then
observer_player.teleport(
target.position,
target.surface
)
end
end
else
data.following = nil
observer_player.print("Target unavailable.")
end
end
end
end
function observer.enable(player)
local force = create_observer_force()
player.force = force
player.set_controller{
type = defines.controllers.spectator
}
player.print("Perspective: " .. force.name)
player.show_on_map = false
player.set_controller{
type = defines.controllers.spectator
}
get_data(player)
observer.create_button(player)
end
function observer.disable(player)
observer.destroy_button(player)
if storage.observers then
storage.observers[player.index] = nil
end
end
--------------------------------------------------------------------------------
return observer