From 57434f4b68a8a092d96cf2991f9064d00ded3226 Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Wed, 31 Mar 2021 00:43:57 +0100 Subject: [PATCH 1/4] Added spectate control --- modules/control/spectate.lua | 146 +++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 modules/control/spectate.lua diff --git a/modules/control/spectate.lua b/modules/control/spectate.lua new file mode 100644 index 00000000..25354854 --- /dev/null +++ b/modules/control/spectate.lua @@ -0,0 +1,146 @@ + +local Event = require 'utils.event' --- @dep utils.event +local Global = require 'utils.global' --- @dep utils.global +local Gui = require 'expcore.gui' --- @dep expcore.gui + +----- Locals ----- +local follow_label -- Gui constructor +local following = {} +local spectating = {} +local Public = {} + +----- Global data ----- +Global.register({ + following = following, + spectating = spectating +}, function(tbl) + following = tbl.following + spectating = tbl.spectating +end) + +----- Public Functions ----- + +--- Puts a player into spectator while maintaining an association to their character +-- @tparam LuaPlayer player The player that will be placed into spectator +-- @treturn boolean Returns false if the player was already in spectator +function Public.start_spectate(player) + assert(player and player.valid, 'Invalid player given to follower') + if spectating[player.index] or not player.character then return false end + local character = player.character + player.set_controller{ type = defines.controllers.spectator } + player.associate_character(character) + spectating[player.index] = character + return true +end + +--- Return a player from spectator back to their character, if the character was killed then respawn the character +-- @tparam LuaPlayer player The player that will leave spectator +function Public.stop_spectate(player) + assert(player and player.valid, 'Invalid player given to follower') + local character = spectating[player.index] + spectating[player.index] = nil + if character and character.valid then + player.teleport(character.position, character.surface) + player.set_controller{ type = defines.controllers.character, character = character } + else + player.ticks_to_respawn = 300 + end +end + +--- Puts a player into spectator and follows an entity as it moves +-- @tparam LuaPlayer player The player that will follow the entity +-- @tparam ?LuaPlayer|LuaEntity entity The player or entity that will be followed +function Public.start_follow(player, entity) + assert(player and player.valid, 'Invalid player given to follower') + assert(entity and entity.valid, 'Invalid entity given to follower') + local spectate = Public.start_spectate(player) + + player.close_map() + follow_label(player.gui.screen, entity) + player.teleport(entity.position, entity.surface) + following[player.index] = { player, entity, entity.position, spectate } +end + +--- Stops a player from following their current entity, can also return a player from spectate +-- @tparam LuaPlayer player The player that you want to stop following their entity +function Public.stop_follow(player) + assert(player and player.valid, 'Invalid player given to follower') + if following[player.index] and following[player.index][4] then Public.stop_spectate(player) end + + Gui.destroy_if_valid(player.gui.screen[follow_label.name]) + following[player.index] = nil +end + +--- Used to stop all players following an entity or player +function Public.stop_all() + for key, data in pairs(following) do + Public.stop_follow(data[1]) + end +end + +----- Gui ----- + +--- Label used to show that the player is following, also used to allow esc to stop following +-- @element follow_label +follow_label = +Gui.element(function(event_trigger, parent, target) + Gui.destroy_if_valid(parent[event_trigger]) + + local label = parent.add{ + name = event_trigger, + type = 'label', + style = 'heading_1_label', + caption = 'Following '..target.name..'.\nClick here or press esc to stop following.' + } + + local player = Gui.get_player_from_element(parent) + local res = player.display_resolution + label.location = {0, res.height-150} + label.style.width = res.width + label.style.horizontal_align = 'center' + player.opened = label + + return label +end) +:on_close(Public.stop_follow) +:on_click(Public.stop_follow) + +----- Events ----- + +--- Updates the location of the player as well as doing some sanity checks +-- @tparam LuaPlayer player The player to update the position of +-- @tparam ?LuaPlayer|LuaEntity entity The player or entity being followed +local function update_player_location(player, entity, old_position) + if player.character or not entity.valid then + Public.stop_follow(player) + elseif player.position.x ~= old_position.x or player.position.y ~= old_position.y then + Public.stop_follow(player) + else + player.teleport(entity.position, entity.surface) + end +end + +--- Updates the locations of all players currently following something +local function update_all() + for _, data in pairs(following) do + update_player_location(data[1], data[2], data[3]) + data[3] = data[1].position + end +end + +-- Update the location of all players each tick +Event.add(defines.events.on_tick, update_all) + +-- Check for player leaving +Event.add(defines.events.on_player_left_game, function(event) + local player = game.players[event.player_index] + Public.stop_follow(player) + for _, data in pairs(following) do + if data[2] == player then + Public.stop_follow(data[1]) + end + end +end) + +----- Module Return ----- +return Public \ No newline at end of file From f56eb4c145e73c07162165cfd16fb09d976a9b78 Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Wed, 31 Mar 2021 01:06:57 +0100 Subject: [PATCH 2/4] Added spectate commands --- config/_file_loader.lua | 1 + config/expcore/roles.lua | 4 ++++ locale/en/commands.cfg | 5 ++++- modules/commands/spectate.lua | 33 +++++++++++++++++++++++++++++++++ modules/control/spectate.lua | 16 ++++++++++++++++ 5 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 modules/commands/spectate.lua diff --git a/config/_file_loader.lua b/config/_file_loader.lua index 16a135af..f42a33d7 100644 --- a/config/_file_loader.lua +++ b/config/_file_loader.lua @@ -29,6 +29,7 @@ return { 'modules.commands.home', 'modules.commands.connect', 'modules.commands.last-location', + 'modules.commands.spectate', --- Addons 'modules.addons.chat-popups', diff --git a/config/expcore/roles.lua b/config/expcore/roles.lua index 9b826202..62932b07 100644 --- a/config/expcore/roles.lua +++ b/config/expcore/roles.lua @@ -102,6 +102,8 @@ Roles.new_role('Trainee','TrMod') 'command/unjail', 'command/kick', 'command/ban', + 'command/spectate', + 'command/follow', } --- Trusted Roles @@ -115,6 +117,8 @@ Roles.new_role('Board Member','Board') :allow{ 'command/goto', 'command/repair', + 'command/spectate', + 'command/follow', } Roles.new_role('Senior Backer','Backer') diff --git a/locale/en/commands.cfg b/locale/en/commands.cfg index dea7b935..c2f91450 100644 --- a/locale/en/commands.cfg +++ b/locale/en/commands.cfg @@ -82,4 +82,7 @@ offline=You cannot connect as the server is currently offline: __1__ none-matching=No servers were found with that name, if you used an address please append true to the end of your command. [expcom-lastlocation] -response=Last location of __1__ was [gps=__2__,__3__] \ No newline at end of file +response=Last location of __1__ was [gps=__2__,__3__] + +[expcom-spectate] +follow-self=You can not follow your self \ No newline at end of file diff --git a/modules/commands/spectate.lua b/modules/commands/spectate.lua new file mode 100644 index 00000000..51f28bd3 --- /dev/null +++ b/modules/commands/spectate.lua @@ -0,0 +1,33 @@ +--[[-- Commands Module - Spectate + - Adds a commands relating to spectate and follow + @commands Spectate +]] + +local Spectate = require 'modules.control.spectate' --- @dep modules.control.spectate +local Commands = require 'expcore.commands' --- @dep expcore.commands +require 'config.expcore.command_general_parse' + +--- Toggles spectator mode for the caller +-- @command spectate +Commands.new_command('spectate', 'Toggles spectator mode') +:register(function(player) + if Spectate.is_spectating(player) then + Spectate.stop_spectate(player) + else + Spectate.start_spectate(player) + end +end) + +--- Enters follow mode for the caller, following the given player. +-- @command follow +-- @tparam LuaPlayer player The player that will be followed +Commands.new_command('follow', 'Start following a player in spectator') +:add_alias('f') +:add_param('player', false, 'player-online') +:register(function(player, action_player) + if player == action_player then + return Commands.error{'expcom-spectate.follow-self'} + else + Spectate.start_follow(player, action_player) + end +end) \ No newline at end of file diff --git a/modules/control/spectate.lua b/modules/control/spectate.lua index 25354854..d9320ae7 100644 --- a/modules/control/spectate.lua +++ b/modules/control/spectate.lua @@ -20,6 +20,14 @@ end) ----- Public Functions ----- +--- Test if a player is in spectator mode +-- @tparam LuaPlayer player The player to test the controller type of +-- @treturn boolean True if the player is in spectator mode +function Public.is_spectating(player) + assert(player and player.valid, 'Invalid player given to follower') + return player.controller_type == defines.controllers.spectator +end + --- Puts a player into spectator while maintaining an association to their character -- @tparam LuaPlayer player The player that will be placed into spectator -- @treturn boolean Returns false if the player was already in spectator @@ -47,6 +55,14 @@ function Public.stop_spectate(player) end end +--- Test if a player is in follow mode +-- @tparam LuaPlayer player The player to test the follow mode of +-- @treturn boolean True if the player is in follow mode +function Public.is_following(player) + assert(player and player.valid, 'Invalid player given to follower') + return following[player.index] ~= nil +end + --- Puts a player into spectator and follows an entity as it moves -- @tparam LuaPlayer player The player that will follow the entity -- @tparam ?LuaPlayer|LuaEntity entity The player or entity that will be followed From d485e2148061369278c040fc3dbb29a4d37ca73c Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Fri, 16 Apr 2021 18:13:41 +0100 Subject: [PATCH 3/4] Fixed calling set_controller during on_close --- modules/control/spectate.lua | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/modules/control/spectate.lua b/modules/control/spectate.lua index d9320ae7..5fec23af 100644 --- a/modules/control/spectate.lua +++ b/modules/control/spectate.lua @@ -48,8 +48,10 @@ function Public.stop_spectate(player) local character = spectating[player.index] spectating[player.index] = nil if character and character.valid then + local opened = player.opened player.teleport(character.position, character.surface) player.set_controller{ type = defines.controllers.character, character = character } + if opened then player.opened = opened end -- Maintain opened after controller change else player.ticks_to_respawn = 300 end @@ -118,8 +120,12 @@ Gui.element(function(event_trigger, parent, target) return label end) -:on_close(Public.stop_follow) :on_click(Public.stop_follow) +:on_close(function(player) + -- Dont call set_controller during on_close as it invalidates the controller + -- Setting an invalid position (as to not equal their current) will call stop_follow on the next tick + following[player.index][3] = {} +end) ----- Events ----- @@ -148,7 +154,7 @@ end Event.add(defines.events.on_tick, update_all) -- Check for player leaving -Event.add(defines.events.on_player_left_game, function(event) +Event.add(defines.events.on_pre_player_left_game, function(event) local player = game.players[event.player_index] Public.stop_follow(player) for _, data in pairs(following) do From ca25dc7cb7ddccb10b904fa72bbfeb90026f4c9f Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Sun, 25 Apr 2021 23:52:19 +0100 Subject: [PATCH 4/4] Implemented requested changes --- locale/en/commands.cfg | 2 +- modules/commands/spectate.lua | 2 +- modules/control/spectate.lua | 24 ++++++++++++------------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/locale/en/commands.cfg b/locale/en/commands.cfg index c2f91450..e3f622ce 100644 --- a/locale/en/commands.cfg +++ b/locale/en/commands.cfg @@ -85,4 +85,4 @@ none-matching=No servers were found with that name, if you used an address pleas response=Last location of __1__ was [gps=__2__,__3__] [expcom-spectate] -follow-self=You can not follow your self \ No newline at end of file +follow-self=You can not follow yourself \ No newline at end of file diff --git a/modules/commands/spectate.lua b/modules/commands/spectate.lua index 51f28bd3..aa74d591 100644 --- a/modules/commands/spectate.lua +++ b/modules/commands/spectate.lua @@ -1,5 +1,5 @@ --[[-- Commands Module - Spectate - - Adds a commands relating to spectate and follow + - Adds commands relating to spectate and follow @commands Spectate ]] diff --git a/modules/control/spectate.lua b/modules/control/spectate.lua index 5fec23af..6f9944f8 100644 --- a/modules/control/spectate.lua +++ b/modules/control/spectate.lua @@ -22,15 +22,15 @@ end) --- Test if a player is in spectator mode -- @tparam LuaPlayer player The player to test the controller type of --- @treturn boolean True if the player is in spectator mode +-- @treturn boolean Returns true if the player is in spectator mode function Public.is_spectating(player) assert(player and player.valid, 'Invalid player given to follower') return player.controller_type == defines.controllers.spectator end ---- Puts a player into spectator while maintaining an association to their character --- @tparam LuaPlayer player The player that will be placed into spectator --- @treturn boolean Returns false if the player was already in spectator +--- Puts a player into spectator mode while maintaining an association to their character +-- @tparam LuaPlayer player The player that will be placed into spectator mode +-- @treturn boolean Returns false if the player was already in spectator mode function Public.start_spectate(player) assert(player and player.valid, 'Invalid player given to follower') if spectating[player.index] or not player.character then return false end @@ -41,8 +41,8 @@ function Public.start_spectate(player) return true end ---- Return a player from spectator back to their character, if the character was killed then respawn the character --- @tparam LuaPlayer player The player that will leave spectator +--- Return a player from spectator mode back to their character, if their character was killed then respawn them +-- @tparam LuaPlayer player The player that will leave spectator mode function Public.stop_spectate(player) assert(player and player.valid, 'Invalid player given to follower') local character = spectating[player.index] @@ -59,13 +59,13 @@ end --- Test if a player is in follow mode -- @tparam LuaPlayer player The player to test the follow mode of --- @treturn boolean True if the player is in follow mode +-- @treturn boolean Returns true if the player is in follow mode function Public.is_following(player) assert(player and player.valid, 'Invalid player given to follower') return following[player.index] ~= nil end ---- Puts a player into spectator and follows an entity as it moves +--- Puts a player into spectator mode and follows an entity as it moves -- @tparam LuaPlayer player The player that will follow the entity -- @tparam ?LuaPlayer|LuaEntity entity The player or entity that will be followed function Public.start_follow(player, entity) @@ -79,8 +79,8 @@ function Public.start_follow(player, entity) following[player.index] = { player, entity, entity.position, spectate } end ---- Stops a player from following their current entity, can also return a player from spectate --- @tparam LuaPlayer player The player that you want to stop following their entity +--- Returns camera control to the player, will return a player to their character if start_follow placed them into spectator mode +-- @tparam LuaPlayer player The player that will regain control of their camera function Public.stop_follow(player) assert(player and player.valid, 'Invalid player given to follower') if following[player.index] and following[player.index][4] then Public.stop_spectate(player) end @@ -89,7 +89,7 @@ function Public.stop_follow(player) following[player.index] = nil end ---- Used to stop all players following an entity or player +--- Returns camera control to all players, will return a player to their character if start_follow placed them into spectator mode function Public.stop_all() for key, data in pairs(following) do Public.stop_follow(data[1]) @@ -122,7 +122,7 @@ Gui.element(function(event_trigger, parent, target) end) :on_click(Public.stop_follow) :on_close(function(player) - -- Dont call set_controller during on_close as it invalidates the controller + -- Don't call set_controller during on_close as it invalidates the controller -- Setting an invalid position (as to not equal their current) will call stop_follow on the next tick following[player.index][3] = {} end)