diff --git a/DOCS.md b/DOCS.md index f78f517..05cb432 100644 --- a/DOCS.md +++ b/DOCS.md @@ -2,39 +2,40 @@ # Table of Contents * [1. Minigame configuration](#1-minigame-configuration) - * [1.1 Per server configuration](#11-per-server-configuration) - * [1.2 Privileges](#12-privileges) - * [1.3 Commands](#13-commands) - * [1.3.1 Admins only](#131-admins-only) - * [1.4 Callbacks](#14-callbacks) - * [1.5 Additional properties](#15-additional-properties) - * [1.5.1 Arena properties](#151-arena-properties) - * [1.5.1.1 Updating non temporary properties via code](#1511-updating-non-temporary-properties-via-code) - * [1.5.1.2 Updating properties for old arenas](#1512-updating-properties-for-old-arenas) - * [1.5.2 Player properties](#152-player-properties) - * [1.5.3 Team properties](#153-team-properties) - * [1.6 HUD](#16-hud) - * [1.7 Extendable editor](#17-extendable-editor) - * [1.8 Utils](#18-utils) - * [1.9 Getters](#19-getters) - * [1.10 Things you don't want to do with a light heart](#110-things-you-dont-want-to-do-with-a-light-heart) - * [1.11 Example file](#111-example-file) + * [1.1 Per server configuration](#11-per-server-configuration) + * [1.2 Privileges](#12-privileges) + * [1.3 Commands](#13-commands) + * [1.3.1 Admins only](#131-admins-only) + * [1.4 Callbacks](#14-callbacks) + * [1.5 Additional properties](#15-additional-properties) + * [1.5.1 Arena properties](#151-arena-properties) + * [1.5.1.1 Updating non temporary properties via code](#1511-updating-non-temporary-properties-via-code) + * [1.5.1.2 Updating properties for old arenas](#1512-updating-properties-for-old-arenas) + * [1.5.2 Player properties](#152-player-properties) + * [1.5.3 Team properties](#153-team-properties) + * [1.6 HUD](#16-hud) + * [1.7 Extendable editor](#17-extendable-editor) + * [1.8 Utils](#18-utils) + * [1.9 Getters](#19-getters) + * [1.10 Things you don't want to do with a light heart](#110-things-you-dont-want-to-do-with-a-light-heart) + * [1.11 Example file](#111-example-file) * [2. Arenas](#2-arenas) - * [2.1 Creating and removing arenas](#21-creating-and-removing-arenas) - * [2.1.1 Storing arenas](#211-storing-arenas) - * [2.1.2 Renaming an arena](#212-renaming-an-arena) - * [2.2 Setting up an arena](#22-setting-up-an-arena) - * [2.2.1 Editor](#221-editor) - * [2.2.2 CLI](#222-cli) - * [2.2.2.1 Players management](#2221-players-management) - * [2.2.2.2 Enabling/Disabling teams](#2222-enablingdisabling-teams) - * [2.2.2.3 Timers](#2223-timers) - * [2.2.2.4 Arena properties](#2224-arena-properties) - * [2.2.2.5 Spawners](#2225-spawners) - * [2.2.2.6 Signs](#2226-signs) - * [2.2.2.7 Music](#2227-music) - * [2.2.3 Enabling an arena](#223-enabling-an-arena) - * [2.3 Arena phases](#23-arena-phases) + * [2.1 Creating and removing arenas](#21-creating-and-removing-arenas) + * [2.1.1 Storing arenas](#211-storing-arenas) + * [2.1.2 Renaming an arena](#212-renaming-an-arena) + * [2.2 Setting up an arena](#22-setting-up-an-arena) + * [2.2.1 Editor](#221-editor) + * [2.2.2 CLI](#222-cli) + * [2.2.2.1 Players management](#2221-players-management) + * [2.2.2.2 Enabling/Disabling teams](#2222-enablingdisabling-teams) + * [2.2.2.3 Timers](#2223-timers) + * [2.2.2.4 Arena properties](#2224-arena-properties) + * [2.2.2.5 Spawners](#2225-spawners) + * [2.2.2.6 Signs](#2226-signs) + * [2.2.2.7 Music](#2227-music) + * [2.2.3 Enabling an arena](#223-enabling-an-arena) + * [2.3 Arena phases](#23-arena-phases) + * [2.4 Spectate mode](#24-spectate-mode) * [3. About the author(s)](#3-about-the-authors) ## 1. Minigame configuration @@ -53,8 +54,10 @@ The second field, on the contrary, is a table of optional parameters: they defin * `is_team_chat_default`: (bool) whether players messages in a game should be sent to their teammates only. It requires `teams`, default is false * `chat_all_prefix`: (string) prefix for every message sent in arena, team chat aside. Default is `[arena] ` (geolocalised) * `chat_team_prefix`: (string) prefix for every message sent in the team chat. Default is `[team] ` (geolocalised) +* `chat_spectate_prefix`: (string) prefix for every message sent in the spectate chat. Default is `[spectator]` (geolocalised) * `chat_all_color`: (string) color for every message sent in arena, team chat aside. Default is white (`"#ffffff"`) * `chat_team_color`: (string) color for every message sent in the team chat. Default is light sky blue (`"#ddfdff"`) +* `chat_spectate_color`: color for every message sent in the spectate chat. Default is gray (`"#dddddd"`) * `fov`: (int) changes the fov of every player * `camera_offset`: (table) changes the offset of the camera for evey player. It's structured as such: `{1st_person, 3rd_person}`, e.g. `{nil, {x=5, y=3, z=-4}}` * `hotbar`: (table) overrides the server hotbar while inside an arena. Its fields are: @@ -63,6 +66,7 @@ The second field, on the contrary, is a table of optional parameters: they defin * `selected_image =`: (string) the image to show when a slot is selected If a field is not declared, it'll keep the server defaults * `join_while_in_progress`: (bool) whether the minigame allows to join an ongoing match. Default is `false` +* `spectate_mode`: (bool) whether the minigame features the spectator mode. Default is `true` * `keep_inventory`: (bool) whether to keep players inventories when joining an arena. Default is `false`. Check out also `STORE_INVENTORY_MODE` in `SETTINGS.lua`, to choose whether and how to store players' inventory * `show_nametags`: (bool) whether to show the players nametags while in game. Default is `false` * `show_minimap`: (bool) whether to allow players to use the builtin minimap function. Default is `false` @@ -102,6 +106,7 @@ A couple more are available for players having the `arenalib_admin` privilege: * `/minigamesettings mod`: change `mod` settings * `/arenakick player_name`: kicks a player out of an ongoing game, no matter the mod * `/forceend mod arena_name`: forcibly ends an ongoing game +* `/flusharena mod arena_name`: restores a broken arena (when not in progress) Those aside, you need to connect a few functions with your mod in order to use them. The best way is with commands and I suggest you [ChatCmdBuilder](https://rubenwardy.com/minetest_modding_book/en/players/chat_complex.html) by rubenwardy. [This](https://gitlab.com/zughy-friends-minetest/block_league/-/blob/master/commands.lua) is what I came up with in my Block League minigame, which relies on arena_lib. As you can see, I declared a `local mod = "block_league"` at the beginning, because it's how I stored my mod inside the library. Also, I created the support for both the editor and the chat commands. @@ -109,19 +114,19 @@ Those aside, you need to connect a few functions with your mod in order to use t To customise your mod even more, there are a few empty callbacks you can use. They are: * `arena_lib.on_enable(mod, function(arena, p_name)`: use it to run more checks before enabling an arena. Must return true if all conditions are met * `arena_lib.on_disable(mod, function(arena, p_name)`: use it to run more checks before disabling an arena. Must return true if all conditions are met -* `arena_lib.on_load(mod, function(arena)` (we saw these 4 earlier) -* `arena_lib.on_start(mod, function(arena))` -* `arena_lib.on_celebration(mod, function(arena, winner_name)` -* `arena_lib.on_end(mod, function(arena, players, winner_name))` -* `arena_lib.on_join(mod, function(p_name, arena))`: called when a player joins an ongoing match +* `arena_lib.on_load(mod, function(arena)`: see [2.3 Arena phases](#23-arena-phases) +* `arena_lib.on_start(mod, function(arena))`: same as above +* `arena_lib.on_celebration(mod, function(arena, winner_name)`: same as above +* `arena_lib.on_end(mod, function(arena, players, winner_name, spectators))`: same as above. Players and spectators are given here because `end_arena` deleted them already - hence these are a copy +* `arena_lib.on_join(mod, function(p_name, arena, as_spectator))`: called when a player joins an ongoing match. If `as_spectator` is true, they'll be added as such * `arena_lib.on_death(mod, function(arena, p_name, reason))`: called when a player dies * `arena_lib.on_time_tick(mod, function(arena))`: called every second if `time_mode` is different from `"none"` * `arena_lib.on_timeout(mod, function(arena))`: called when the timer of an arena, if exists (`time_mode = "decremental"`), reaches 0. Not declaring it will make the server crash when time runs out * `arena_lib.on_eliminate(mod, function(arena, p_name))`: called when a player is eliminated (see `arena_lib.remove_player_from_arena(...)`) -* `arena_lib.on_kick(mod, function(arena, p_name))`: called when a player is kicked from a match (same as above) -* `arena_lib.on_quit(mod, function(arena, p_name, is_forced))`: called when a player quits from a match (same as above). `is_forced` is true when the match has been terminated via `force_arena_ending(...)` -* `arena_lib.on_prequit(mod, function(arena, p_name))`: called when a player tries to quit. If it returns false, quit is cancelled. Useful ie. to ask confirmation first, or simply impede a player to quit -* `arena_lib.on_disconnect(mod, function(arena, p_name))`: called when a player disconnects while in a match +* `arena_lib.on_kick(mod, function(arena, p_name, is_spectator))`: called when a player/spectator is kicked from a match (same as above) +* `arena_lib.on_quit(mod, function(arena, p_name, is_spectator, is_forced))`: called when a player/spectator quits from a match (same as above). `is_forced` is `true` when the match has been forcibly terminated +* `arena_lib.on_prequit(mod, function(arena, p_name))`: called when a player tries to quit. If it returns false, quit is cancelled. Useful to ask confirmation first, or simply to impede a player to quit +* `arena_lib.on_disconnect(mod, function(arena, p_name, is_spectator))`: called when a player/spectator disconnects while in a match > **BEWARE**: there is a default behaviour already for most of these situations: for instance when a player dies, their deaths increase by 1. These callbacks exist just in case you want to add some extra behaviour to arena_lib's. @@ -191,9 +196,9 @@ Same as above, but for teams. For instance, you could count how many rounds of a #### 1.6 HUD `arena_lib` also comes with a triple practical HUD: `title`, `broadcast` and `hotbar`. These HUDs only appear when a message is sent to them and they can be easily used via the following functions: -* `arena_lib.HUD_send_msg(HUD_type, p_name, msg, , , )`: send a message to the specified player in the specified HUD type (`"title"`, `"broadcast"` or `"hotbar"`). If no duration is declared, it won't disappear by itself. If a sound is declared, it'll be played at the very showing of the HUD. `color` must be in a hexadecimal format and, if not specified, it defaults to white (`0xFFFFFF`). -* `arena_lib.HUD_send_msg_all(HUD_type, arena, msg, , , )`: same as above, but for all the players inside the arena -* `arena_lib.HUD_hide(HUD_type, player_or_arena)`: it makes the specified HUD disappear; it can take both a player than a whole arena. Also, a special parameter `all` can be used in `HUD_type` to make both the HUDs disappear +* `arena_lib.HUD_send_msg(HUD_type, p_name, msg, , , )`: sends a message to the specified player/spectator in the specified HUD type (`"title"`, `"broadcast"` or `"hotbar"`). If no duration is declared, it won't disappear by itself. If a sound is declared, it'll be played at the very showing of the HUD. `color` must be in a hexadecimal format and, if not specified, it defaults to white (`0xFFFFFF`). +* `arena_lib.HUD_send_msg_all(HUD_type, arena, msg, , , )`: same as above, but for all the players and spectators inside the arena +* `arena_lib.HUD_hide(HUD_type, player_or_arena)`: makes the specified HUD disappear; it can take both a player/spectator and a whole arena. Also, a special parameter `all` can be used in `HUD_type` to make all the HUDs disappear #### 1.7 Extendable editor Since 4.0, every minigame can extend the editor with an additional custom section on the 5th slot. To do that, the function is @@ -225,8 +230,10 @@ There are also some other functions which might turn useful. They are: * `4`: forced by the mod. This should NOT be used but internally. Calls `on_quit` Default is 0 and these are mostly hardcoded in arena_lib already, so it's advised to not touch it and to use callbacks. The only exception is in case of manual elimination (ie. in a murder minigame, so reason = 1). Executioner can be passed to tell who removed the player. By default, this happens when someone uses /arenakick and /forceend, so that these commands can't be abused without consequences for the admin. -* `arena_lib.send_message_players_in_arena(arena, msg, , )`: send a message to all the players in that specific arena. If a teamID is specified, it'll be only sent to the players inside that very team. On the contrary, if except_teamID is true, it'll be sent to every player BUT the ones in the specified team -* `arena_lib.teleport_in_arena(sender, mod, arena_name)`: teleport the sender into the arena if at least one spawner has been set +* `arena_lib.send_message_in_arena(arena, channel, msg, , )`: sends a message to all the players/spectators in that specific arena, according to what `channel` is: `"players"`, `"spectators"` or `"both"`. If `teamID` is specified, it'll be only sent to the players inside that very team. On the contrary, if `except_teamID` is `true`, it'll be sent to every player BUT the ones in the specified team. These last two fields are pointless if `channel` is equal to `"spectators"` +* `arena_lib.teleport_in_arena(sender, mod, arena_name)`: teleports the sender into the arena if at least one spawner has been set +* `arena_lib.is_player_spectating(sp_name)`: returns whether a player is spectating a match, as a boolean +* `arena_lib.is_player_spectated(p_name)`: returns whether a player is being spectated * `arena_lib.is_arena_in_edit_mode(arena_name)`: returns whether the arena is in edit mode or not, as a boolean * `arena_lib.is_player_in_edit_mode(p_name)`: returns whether a player is editing an arena, as a boolean @@ -261,10 +268,16 @@ An arena is a table having as a key an ID and as a value its parameters. They ar * `author`: (string) the name of the one who built/designed the map. Default is `"???"`. It appears in the signs infobox (right-click an arena sign) * `sign`: (pos) the position of the sign associated with the arena * `players`: (table) where to store players information, such as their team ID (`teamID`) and `player_properties`. Format `{[p_name] = {stuff}, [p_name2] = {stuff}, ...}` +* `spectators`: (table) where to store spectators information. Format `{[sp_name] = true}` +* `players_and_spectators`: (table) where to store both players and spectators names. Format `{[psp_name] = true}` +* `past_present_players`: (table) keeps track of every player who took part to the match, even if they are spectators now or they left. Contrary to `players` and `players_and_spectators`, this is created when the arena loads, so it doesn't consider people who joined and left during the queue. Format `{[ppp_name] = true}` +* `past_present_players_inside`: (table) same as `past_present_players` but without keeping track of the ones who left * `teams`: (table) where to store teams information, such as their name (`name`) and `team_properties`. If there are no teams, it's `{-1}`. If there are, format is `{[teamID] = {stuff}, [teamID2] = {stuff}, ...}` * `teams_enabled`: (boolean) whether teams are enabled in the arena. Requires teams * `players_amount`: (int) separately stores how many players are inside the arena/queue -* `players_amount_per_team`: (table) separately stores how many players currently are in a given team. Format `{[teamID] = amount}` +* `players_amount_per_team`: (table) separately stores how many players currently are in a given team. Format `{[teamID] = amount}`. If teams are disabled, it's `nil` +* `spectators_amount`: (int) separately stores how many spectators are inside the arena +* `spectators_amount_per_team`: (table) like `players_amount_per_team`, but for spectators * `spawn_points`: (table) contains information about the spawn points. Format `{[spawnID] = {pos = coords, teamID = team ID}}`. If teams are disabled, `teamID` is `nil` * `max_players`: (string) default is 4. When this value is reached, queue time decreases to 5 if it's not lower already * `min_players`: (string) default is 2. When this value is reached, a queue starts @@ -316,7 +329,7 @@ Two things are needed to have an arena up to go: spawners and signs. There are t * `arena_lib.set_sign(sender, , )`: there must be one and only one sign per arena. Signs are the bridge between the arena and the rest of the world #### 2.2.1 Editor -Since 3.0, arena_lib comes with a fancy editor via hotbar so you don't have to configure and memorise a lot of commands (if you still want to go full CLI/chat though, skip this paragraph). +arena_lib comes with a fancy editor via hotbar so you don't have to configure and memorise a lot of commands (if you still want to go full CLI/chat though, skip this paragraph). In order to use the editor, no other players must be editing the same arena. When entering it, the arena is disabled automatically. The rest is pretty straightforward :D if you're not sure of what something does, just open the inventory and read its name. The function calling the editor is `arena_lib.enter_editor(sender, mod, arena_name)` @@ -402,9 +415,12 @@ The 4 functions, intertwined with the previously mentioned phases are: * `arena_lib.load_arena(mod, arena_ID)`: between the waiting and the loading phase. Called when the queue timer reaches 0, it teleports people inside. * `arena_lib.start_arena(mod_ref, arena)`: between the loading and the fighting phase. Called when the loading phase timer reaches 0. * `arena_lib.load_celebration(mod, arena, winner_name)`: between the fighting and the celebration phase. Called when the winning conditions are met. `winner_name` can be both a string and a table (in case of teams) -* `arena_lib.end_arena(mod_ref, mod, arena, winner_name)`: at the very end of the celebration phase. It teleports people outside the arena. `winner_name` is taken by `load_celebration(...)` +* `arena_lib.end_arena(mod_ref, mod, arena, winner_name, as_spectator)`: at the very end of the celebration phase. It teleports people outside the arena. `winner_name` is taken by `load_celebration(...)` -Overriding these functions is **not** recommended. Instead, there are 4 respective callbacks made specifically to customise the behaviour of the formers, sharing (almost) the same variables. They are called *after* the function they're associated with and by default they are empty, so feel free to override them. They are `on_load`, `on_start`, `on_celebration` and `on_end`. +Overriding these functions is **not** recommended. Instead, use the 4 respective callbacks made specifically to customise the behaviour of the formers, sharing (almost) the same variables. They are called *after* the function they're associated with and by default they are empty, so feel free to override them. They are `on_load`, `on_start`, `on_celebration` and `on_end`. + +### 2.4 Spectate mode +Every minigame has this mode enabled by default. As the name suggests, it allows people to spectate a match, and there are two ways to enter this mode: the first is by getting eliminated (`remove_player_from_arena` with `1` as a reason), while the other is through the very sign of the arena. In this last case, users just need to right-click the sign and press the "eye" button to be turned into spectators (a game must be in progress). While in this state, they can't interact in any way with the actual match: neither by hitting entities/blocks, nor by writing in chat. The latter, more precisely, is a separated chat that spectators and spectators only are able to read. Vice versa, they're not able to read the players one. ## 3. About the author(s) I'm Zughy (Marco), a professional Italian pixel artist who fights for FOSS and digital ethics. If this library spared you a lot of time and you want to support me somehow, please consider donating on [LiberaPay](https://liberapay.com/Zughy/). Also, this project wouldn't have been possible if it hadn't been for some friends who helped me testing through: `Giov4`, `SonoMichele`, `_Zaizen_` and `Xx_Crazyminer_xX` diff --git a/_editor/editor_icons.lua b/_editor/editor_icons.lua index 91a69c1..81f1c28 100755 --- a/_editor/editor_icons.lua +++ b/_editor/editor_icons.lua @@ -180,7 +180,7 @@ minetest.register_tool("arena_lib:editor_enable", { minetest.register_tool("arena_lib:editor_quit", { - description = S("Leave the editor"), + description = S("Leave"), inventory_image = "arenalib_editor_quit.png", groups = {not_in_creative_inventory = 1, oddly_breakable_by_hand = "2"}, on_place = function() end, diff --git a/_hud/hud_main.lua b/_hud/hud_main.lua index 1e23e4c..5f91a54 100755 --- a/_hud/hud_main.lua +++ b/_hud/hud_main.lua @@ -113,7 +113,7 @@ function arena_lib.HUD_send_msg_all(HUD_type, arena, msg, duration, sound, color color = color == nil and "0xFFFFFF" or color - for pl_name, _ in pairs(arena.players) do + for pl_name, _ in pairs(arena.players_and_spectators) do local pl = minetest.get_player_by_name(pl_name) local pl_HUD = player_huds[pl_name] @@ -186,7 +186,7 @@ function arena_lib.HUD_hide(HUD_type, player_or_arena) elseif type(player_or_arena) == "table" then - for pl_name, _ in pairs(player_or_arena.players) do + for pl_name, _ in pairs(player_or_arena.players_and_spectators) do local pl = minetest.get_player_by_name(pl_name) local pl_HUD = player_huds[pl_name] diff --git a/_spectate/spectate_hand.lua b/_spectate/spectate_hand.lua new file mode 100644 index 0000000..ce9b95d --- /dev/null +++ b/_spectate/spectate_hand.lua @@ -0,0 +1,6 @@ +minetest.register_node("arena_lib:spectate_hand", { + description = "Spectate hand", + wield_image = "arenalib_infobox_spectate.png", + range = 0, + groups = {not_in_creative_inventory = 1} +}) diff --git a/_spectate/spectate_main.lua b/_spectate/spectate_main.lua new file mode 100644 index 0000000..6f3202a --- /dev/null +++ b/_spectate/spectate_main.lua @@ -0,0 +1,311 @@ +local S = minetest.get_translator("arena_lib") + +local function override_hotbar() end +local function set_spectator() end + +local players_in_spectate_mode = {} -- KEY: player name, VALUE: {(string) minigame, (int) arenaID, (string) spectating} +local players_spectated = {} -- KEY: player name, VALUE: {(string) spectator(s) = true} +local spectate_temp_storage = {} -- KEY: player_name, VALUE: {(table) camera_offset} + + + +function arena_lib.enter_spectate_mode(p_name, arena) + + local mod = arena_lib.get_mod_by_player(p_name) + + -- se non supporta la spettatore + if not arena_lib.mods[mod].spectate_mode then + minetest.chat_send_player(p_name, minetest.colorize("#e6482e", S("[!] Spectate mode not supported!"))) + return end + + -- se l'arena non è abilitata + if not arena.enabled then + minetest.chat_send_player(p_name, minetest.colorize("#e6482e", S("[!] The arena is not enabled!"))) + return end + + -- se non è in corso + if not arena.in_game then + minetest.chat_send_player(p_name, minetest.colorize("#e6482e", S("[!] No ongoing game!"))) + return end + + local arena_ID = arena_lib.get_arenaID_by_player(p_name) + local team_ID = #arena.teams > 1 and 1 or nil + + players_in_spectate_mode[p_name] = { minigame = mod, arenaID = arena_ID, teamID = team_ID} + arena.spectators[p_name] = true + arena.players_and_spectators[p_name] = true + arena.spectators_amount = arena.spectators_amount + 1 + + local player = minetest.get_player_by_name(p_name) + + -- applico mano finta + player:get_inventory():set_size("hand", 1) + player:get_inventory():add_item("hand", "arena_lib:spectate_hand") + + -- se il giocatore non è mai entrato in partita, lo salvo nello spazio di archiviazione temporaneo + if not arena.past_present_players_inside[p_name] then + spectate_temp_storage[p_name] = {} + spectate_temp_storage[p_name].camera_offset = player:get_eye_offset() + end + + -- applicazione parametri vari + local current_properties = table.copy(player:get_properties()) + players_in_spectate_mode[p_name].properties = current_properties + + player:set_properties({ + visual_size = {x = 0, y = 0}, + makes_footstep_sound = false, + collisionbox = {0}, + pointable = false + }) + + player:set_eye_offset({x = 0, y = -2, z = -25}, {x=0, y=0, z=0}) + player:set_nametag_attributes({color = {a = 0}}) + + -- inizia a seguire + arena_lib.find_and_spectate_player(p_name) + + override_hotbar(player, mod, arena) + return true +end + + + +function arena_lib.leave_spectate_mode(p_name, to_join_match) + + local arena = arena_lib.get_arena_by_player(p_name) + + if to_join_match then + --TODO-TEMP: 5.4, aspettare o dà problemi con after + minetest.chat_send_player(p_name, "[!] SoonTM!") + return + + --TODO: questi check ha senso ridurli in un luogo unico per quando si prova a entrare, dato che appaiono pure sui cartelli + --[[ + -- se è piena + if arena.players_amount == arena.max_players * #arena.teams then + minetest.chat_send_player(p_name, minetest.colorize("#e6482e", S("[!] The arena is already full!"))) + return end + + -- se è in celebrazione + if arena.in_celebration then + minetest.chat_send_player(p_name, minetest.colorize("#e6482e", S("[!] The arena is loading, try again in a few seconds!"))) + return end + ]] + else + arena.players_and_spectators[p_name] = nil + end + + arena.spectators[p_name] = nil + arena.spectators_amount = arena.spectators_amount -1 + + local player = minetest.get_player_by_name(p_name) + + -- rimuovo mano finta + player:get_inventory():set_size("hand", 0) + + -- se il giocatore non è mai entrato in partita, riassegno le proprietà salvate qui + if not arena.past_present_players_inside[p_name] then + player:set_eye_offset(spectate_temp_storage[p_name].camera_offset[1], spectate_temp_storage[p_name].camera_offset[2]) + spectate_temp_storage[p_name] = nil + else + player:set_eye_offset({x=0, y=0, z=0}, {x=0, y=0, z=0}) + end + + player:set_detach() + player:get_meta():set_int("arenalib_watchID", 0) + + -- mantengo l'intangibilità per 0.1 secondi a causa del teletrasporto in 0.1 secondi di ritardo. Si veda https://github.com/minetest/minetest/pull/10235 + local properties = table.copy(players_in_spectate_mode[p_name].properties) + + minetest.after(0.1, function() + if not player then return end + player:set_properties(properties) + end) + --^---------------------------------------------------------------------------------------------^ + + arena_lib.HUD_hide("hotbar", p_name) + + local target = players_in_spectate_mode[p_name].spectating + + -- rimuovo dal database locale + players_spectated[target][p_name] = nil + players_in_spectate_mode[p_name] = nil +end + + + + + +---------------------------------------------- +--------------------UTILS--------------------- +---------------------------------------------- + +function arena_lib.is_player_spectating(sp_name) + return players_in_spectate_mode[sp_name] ~= nil +end + + + +function arena_lib.is_player_spectated(p_name) + return players_spectated[p_name] ~= nil +end + + + +function arena_lib.find_and_spectate_player(sp_name, change_team) + + local spectator = minetest.get_player_by_name(sp_name) + local arena = arena_lib.get_arena_by_player(sp_name) + + local prev_spectated = players_in_spectate_mode[sp_name].spectating + local watching_ID = spectator:get_meta():get_int("arenalib_watchID") + local team_ID = players_in_spectate_mode[sp_name].teamID + local players_amount + + -- se l'ultimo rimasto ha abbandonato (es. alt+f4), rispedisco subito fuori senza che cada all'infinito con rischio di crash + if arena.players_amount == 0 then + arena_lib.remove_player_from_arena(sp_name, 3) + return end + + -- se già seguiva qualcuno e c'è rimasto solo quel qualcuno, annullo + if prev_spectated and arena.players_amount == 1 then return end + + -- calcolo giocatori massimi tra cui ruotare + -- squadre: + if #arena.teams > 1 then + + -- se il giocatore seguito era l'ultimo membro della sua squadra, la imposto da cambiare + if arena.players_amount_per_team[team_ID] == 0 then + change_team = true + end + + -- eventuale cambio squadra sul quale eseguire il calcolo + if change_team and prev_spectated then + arena.spectators_amount_per_team[team_ID] = arena.spectators_amount_per_team[team_ID] - 1 + + local active_teams = arena_lib.get_active_teams(arena) + + if team_ID >= #active_teams then + team_ID = active_teams[1] + else + for i = team_ID + 1, #arena.teams do + if arena.players_amount_per_team[i] ~= 0 then + team_ID = i + break + end + end + end + players_in_spectate_mode[sp_name].teamID = team_ID + arena.spectators_amount_per_team[team_ID] = arena.spectators_amount_per_team[team_ID] + 1 + end + + players_amount = arena.players_amount_per_team[team_ID] + + -- no squadre: + else + players_amount = arena.players_amount + end + + local new_ID = players_amount <= watching_ID and 1 or watching_ID + 1 + local i = 0 + + -- trovo il giocatore da seguire + -- squadre: + if #arena.teams > 1 then + for _, pl_name in pairs(arena_lib.get_players_in_team(arena, team_ID)) do + i = i + 1 + + if i == new_ID then + set_spectator(spectator, pl_name, i, prev_spectated) + return true + end + end + + -- no squadre: + else + for pl_name, _ in pairs(arena.players) do + i = i + 1 + + if i == new_ID then + set_spectator(spectator, pl_name, i, prev_spectated) + return true + end + end + end +end + + + + + +---------------------------------------------- +-----------------GETTERS---------------------- +---------------------------------------------- + +function arena_lib.get_player_spectators(p_name) + return players_spectated[p_name] +end + + + + + +---------------------------------------------- +---------------FUNZIONI LOCALI---------------- +---------------------------------------------- + +function set_spectator(spectator, p_name, i, prev_spectated) + + local sp_name = spectator:get_player_name() + + -- se stava già seguendo qualcuno, lo rimuovo da questo + if prev_spectated then + players_spectated[prev_spectated][sp_name] = nil + + if not next(players_spectated[prev_spectated]) then + players_spectated[prev_spectated] = nil + end + end + + if not arena_lib.is_player_spectated(p_name) then + players_spectated[p_name] = {} + end + + players_spectated[p_name][sp_name] = true + players_in_spectate_mode[sp_name].spectating = p_name + + local target = minetest.get_player_by_name(p_name) + + spectator:set_attach(target, "", {x=0, y=-5, z=-20}, {x=0, y=0, z=0}) + spectator:get_meta():set_int("arenalib_watchID", i) + + arena_lib.HUD_send_msg("hotbar", sp_name, S("Currently spectating: @1", p_name)) +end + + + +function override_hotbar(player, mod, arena) + + player:get_inventory():set_list("main", {}) + player:get_inventory():set_list("craft",{}) + + local mod_ref = arena_lib.mods[mod] + local tools = { + "arena_lib:spectate_changeplayer", + "arena_lib:spectate_quit" + } + + if #arena.teams > 1 then + table.insert(tools, 2, "arena_lib:spectate_changeteam") + end + + if mod_ref.join_while_in_progress then + table.insert(tools, #tools, "arena_lib:spectate_join") + end + + minetest.after(0, function() + player:hud_set_hotbar_itemcount(#tools) + player:get_inventory():set_list("main", tools) + end) +end diff --git a/_spectate/spectate_tools.lua b/_spectate/spectate_tools.lua new file mode 100644 index 0000000..082f14f --- /dev/null +++ b/_spectate/spectate_tools.lua @@ -0,0 +1,86 @@ +local S = minetest.get_translator("arena_lib") + + + +minetest.register_tool("arena_lib:spectate_changeplayer", { + + description = S("Change player"), + inventory_image = "arenalib_spectate_changeplayer.png", + groups = {not_in_creative_inventory = 1, oddly_breakable_by_hand = "2"}, + on_place = function() end, + on_drop = function() end, + + on_use = function(itemstack, user) + + local p_name = user:get_player_name() + local arena = arena_lib.get_arena_by_player(p_name) + + -- non far cambiare se c'è rimasto solo un giocatore da seguire + if arena.players_amount == 1 then return end + + arena_lib.find_and_spectate_player(user:get_player_name()) + end + +}) + + + +minetest.register_tool("arena_lib:spectate_changeteam", { + + description = S("Change team"), + inventory_image = "arenalib_spectate_changeteam.png", + groups = {not_in_creative_inventory = 1, oddly_breakable_by_hand = "2"}, + on_place = function() end, + on_drop = function() end, + + on_use = function(itemstack, user) + + local p_name = user:get_player_name() + local arena = arena_lib.get_arena_by_player(p_name) + + -- non far cambiare se c'è rimasto solo una squadra da seguire + if arena_lib.get_active_teams(arena) == 1 then return end + + arena_lib.find_and_spectate_player(user:get_player_name(), true) + end + +}) + + + +minetest.register_tool("arena_lib:spectate_join", { + + description = S("Enter the match"), + inventory_image = "arenalib_editor_return.png", + groups = {not_in_creative_inventory = 1, oddly_breakable_by_hand = "2"}, + on_place = function() end, + on_drop = function() end, + + on_use = function(itemstack, user) + minetest.after(0, function() -- after sennò non rimuove quest'oggetto + local p_name = user:get_player_name() + local mod = arena_lib.get_mod_by_player(p_name) + local arena_ID = arena_lib.get_arenaID_by_player(p_name) + arena_lib.join_arena(mod, p_name, arena_ID) + end) + end + +}) + + + +minetest.register_tool("arena_lib:spectate_quit", { + + description = S("Leave"), + inventory_image = "arenalib_editor_quit.png", + groups = {not_in_creative_inventory = 1, oddly_breakable_by_hand = "2"}, + on_place = function() end, + on_drop = function() end, + + on_use = function(itemstack, user) + minetest.after(0, function() -- after sennò non rimuove quest'oggetto + arena_lib.remove_player_from_arena(user:get_player_name(), 3) + end) + end + +}) diff --git a/api.lua b/api.lua index 96c83ac..a808955 100755 --- a/api.lua +++ b/api.lua @@ -19,24 +19,32 @@ local function next_available_ID() end local function is_arena_name_allowed() end local function assign_team_spawner() end local function operations_before_entering_arena() end +local function operations_before_playing_arena() end local function operations_before_leaving_arena() end +local function handle_leaving_callbacks() end local function show_victory_particles() end local function time_start() end -local players_in_game = {} -- KEY: player name, VALUE: {(string) minigame, (int) arenaID} -local players_in_queue = {} -- KEY: player name, VALUE: {(string) minigame, (int) arenaID} -local players_temp_storage = {} -- KEY: player_name, VALUE: {(int) hotbar_slots, (string) hotbar_background_image, (string) hotbar_selected_image, - -- (int) bgm_handle, (int) fov, (table) camera_offset} +local players_in_game = {} -- KEY: player name, VALUE: {(string) minigame, (int) arenaID} +local players_in_queue = {} -- KEY: player name, VALUE: {(string) minigame, (int) arenaID} +local players_temp_storage = {} -- KEY: player_name, VALUE: {(int) hotbar_slots, (string) hotbar_background_image, (string) hotbar_selected_image, + -- (int) bgm_handle, (int) fov, (table) camera_offset} local arena_default = { name = "", author = "???", sign = {}, - players = {}, -- KEY: player name, VALUE: {kills, deaths, teamID, } + players = {}, -- KEY: player name, VALUE: {kills, deaths, teamID, } + spectators = {}, -- KEY: player name, VALUE: true + players_and_spectators = {}, -- KEY: pl/sp name, VALUE: true + past_present_players = {}, -- KEY: player_name, VALUE: true + past_present_players_inside = {}, -- KEY: player_name, VALUE: true teams = {-1}, teams_enabled = false, players_amount = 0, players_amount_per_team = nil, + spectators_amount = 0, + spectators_amount_per_team = nil, spawn_points = {}, -- KEY: ids, VALUE: {pos, teamID} max_players = 4, min_players = 2, @@ -96,12 +104,15 @@ function arena_lib.register_minigame(mod, def) mod_ref.is_team_chat_default = false mod_ref.chat_all_prefix = "[" .. S("arena") .. "] " mod_ref.chat_team_prefix = "[" .. S("team") .. "] " + mod_ref.chat_spectate_prefix = "[" .. S("spectator") .. "] " mod_ref.chat_all_color = "#ffffff" mod_ref.chat_team_color = "#ddfdff" + mod_ref.chat_spectate_color = "#dddddd" mod_ref.fov = nil mod_ref.camera_offset = nil mod_ref.hotbar = nil mod_ref.join_while_in_progress = false + mod_ref.spectate_mode = true mod_ref.keep_inventory = false mod_ref.show_nametags = false mod_ref.show_minimap = false @@ -147,6 +158,14 @@ function arena_lib.register_minigame(mod, def) mod_ref.chat_all_color = def.chat_all_color end + if def.chat_spectate_prefix then + mod_ref.chat_spectate_prefix = def.chat_spectate_prefix + end + + if def.chat_spectate_color then + mod_ref.chat_spectate_color = def.chat_spectate_color + end + if def.fov then mod_ref.fov = def.fov end @@ -166,16 +185,20 @@ function arena_lib.register_minigame(mod, def) mod_ref.join_while_in_progress = def.join_while_in_progress end + if def.spectate_mode == false then + mod_ref.spectate_mode = false + end + if def.keep_inventory == true then - mod_ref.keep_inventory = def.keep_inventory + mod_ref.keep_inventory = true end if def.show_nametags == true then - mod_ref.show_nametags = def.show_nametags + mod_ref.show_nametags = true end if def.show_minimap == true then - mod_ref.show_minimap = def.show_minimap + mod_ref.show_minimap = true end if def.time_mode then @@ -299,7 +322,7 @@ function arena_lib.create_arena(sender, mod, arena_name, min_players, max_player arena.max_players = max_players end - -- eventuali team + -- eventuali squadre if #mod_ref.teams > 1 then arena.teams = {} arena.teams_enabled = true @@ -309,6 +332,13 @@ function arena_lib.create_arena(sender, mod, arena_name, min_players, max_player arena.teams[k] = {name = t_name} arena.players_amount_per_team[k] = 0 end + + if mod_ref.spectate_mode then + arena.spectators_amount_per_team = {} + for k, t_name in pairs(mod_ref.teams) do + arena.spectators_amount_per_team[k] = 0 + end + end end -- eventuale tempo @@ -1083,21 +1113,47 @@ end --- per il player singolo a match iniziato -function arena_lib.join_arena(mod, p_name, arena_ID) +-- per il giocatore singolo a match iniziato +function arena_lib.join_arena(mod, p_name, arena_ID, as_spectator) local mod_ref = arena_lib.mods[mod] - local player = minetest.get_player_by_name(p_name) local arena = mod_ref.arenas[arena_ID] - operations_before_entering_arena(mod_ref, mod, arena, arena_ID, p_name) + -- se prova a entrare come spettatore + if as_spectator then + -- aggiungo temporaneamente, sennò non trova l'arena quando va a cercare lo spettatore + players_in_game[p_name] = {minigame = mod, arenaID = arena_ID} - -- teletrasporto - player:set_pos(arena_lib.get_random_spawner(arena, arena.players[p_name].teamID)) + -- se passa i controlli, lo inserisco e notifico i giocatori + if arena_lib.enter_spectate_mode(p_name, arena) then + operations_before_entering_arena(mod_ref, mod, arena, arena_ID, p_name, true) + arena_lib.send_message_in_arena(arena, "players", minetest.colorize("#cfc6b8", ">>> " .. p_name .. " (" .. S("spectator") .. ")")) + + -- sennò annullo + else + players_in_game[p_name] = nil + return + end + + -- se entra come giocatore + else + if arena_lib.is_player_spectating(p_name) then -- se lo fa mentre è spettatore, controllo che ci sia spazio ecc. + if not arena_lib.leave_spectate_mode(p_name, true) then return end + operations_before_playing_arena(mod_ref, arena, p_name) + else + operations_before_entering_arena(mod_ref, mod, arena, arena_ID, p_name) -- sennò entra normalmente + end + + local player = minetest.get_player_by_name(p_name) + + -- notifico e teletrasporto + arena_lib.send_message_in_arena(arena, "players", minetest.colorize("#c6f154", " >>> " .. p_name)) + player:set_pos(arena_lib.get_random_spawner(arena, arena.players[p_name].teamID)) + end -- eventuale codice aggiuntivo if mod_ref.on_join then - mod_ref.on_join(p_name, arena) + mod_ref.on_join(p_name, arena, as_spectator) end end @@ -1151,9 +1207,28 @@ end function arena_lib.end_arena(mod_ref, mod, arena, winner_name) - -- copia da passare a on_end + -- copie da passare a on_end + local spectators = {} local players = {} + -- rimozione spettatori + for sp_name, sp_stats in pairs(arena.spectators) do + + spectators[sp_name] = sp_stats + arena_lib.leave_spectate_mode(sp_name) + players_in_game[sp_name] = nil + + operations_before_leaving_arena(mod_ref, arena, sp_name) + + -- TEMP: 5.4, senza after non teletrasporta dove dovrebbe. Si veda https://github.com/minetest/minetest/pull/10235 + minetest.after(0.1, function() + local spectator = minetest.get_player_by_name(sp_name) + spectator:set_pos(mod_ref.settings.hub_spawn_point) + end) + --^-----------------------------------------------------^ + end + + -- rimozione giocatori for pl_name, stats in pairs(arena.players) do players[pl_name] = stats @@ -1163,29 +1238,18 @@ function arena_lib.end_arena(mod_ref, mod, arena, winner_name) operations_before_leaving_arena(mod_ref, arena, pl_name) end - -- effetto particellare - if type(winner_name) == "string" then - local winner = minetest.get_player_by_name(winner_name) + -- azzerramento giocatori e spettatori + arena.past_present_players = {} + arena.players_and_spectators = {} + arena.past_present_players_inside = {} - if winner then - show_victory_particles(winner:get_pos()) - end - - elseif type(winner_name) == "table" then - for _, pl_name in pairs(winner_name) do - local winner = minetest.get_player_by_name(pl_name) - - if winner then - show_victory_particles(winner:get_pos()) - end - end - end - - -- azzero il numero di giocatori arena.players_amount = 0 if arena.teams_enabled then for i = 1, #arena.teams do arena.players_amount_per_team[i] = 0 + if mod_ref.spectate_mode then + arena.spectators_amount_per_team[i] = 0 + end end end @@ -1206,9 +1270,27 @@ function arena_lib.end_arena(mod_ref, mod, arena, winner_name) end end + -- effetto particellare + if type(winner_name) == "string" then + local winner = minetest.get_player_by_name(winner_name) + + if winner then + show_victory_particles(winner:get_pos()) + end + + elseif type(winner_name) == "table" then + for _, pl_name in pairs(winner_name) do + local winner = minetest.get_player_by_name(pl_name) + + if winner then + show_victory_particles(winner:get_pos()) + end + end + end + -- eventuale codice aggiuntivo if mod_ref.on_end then - mod_ref.on_end(arena, players, winner_name) + mod_ref.on_end(arena, players, winner_name, spectators) end arena.in_loading = false -- nel caso venga forzata mentre sta caricando, sennò rimane a caricare all'infinito @@ -1311,9 +1393,9 @@ function arena_lib.force_arena_ending(mod, arena, sender) minetest.chat_send_player(sender, minetest.colorize("#e6482e", S("[!] No ongoing game!"))) return end - -- caccio tutti i giocatori - for pl_name, _ in pairs(arena.players) do - arena_lib.remove_player_from_arena(pl_name, 4, sender) + -- caccio tutti i giocatori e spettatori + for psp_name, _ in pairs(arena.players_and_spectators) do + arena_lib.remove_player_from_arena(psp_name, 4, sender) end arena_lib.end_arena(mod_ref, mod, arena) @@ -1323,6 +1405,9 @@ end function arena_lib.add_to_queue(p_name, mod, arena_ID) + local arena = arena_lib.mods[mod].arenas[arena_ID] + arena_lib.send_message_players_in_arena(arena, minetest.colorize("#c8d692", arena.name .. " > " .. p_name)) + players_in_queue[p_name] = {minigame = mod, arenaID = arena_ID} end @@ -1344,6 +1429,9 @@ function arena_lib.remove_player_from_queue(p_name) arena.players_amount_per_team[p_team_ID] = arena.players_amount_per_team[p_team_ID] - 1 end arena.players[p_name] = nil + arena.players_and_spectators[p_name] = nil + + arena_lib.HUD_hide("all", p_name) local players_required = arena_lib.get_players_to_start_queue(arena) @@ -1390,65 +1478,83 @@ function arena_lib.remove_player_from_arena(p_name, reason, executioner) local mod_ref = arena_lib.mods[mod] local arena = arena_lib.get_arena_by_player(p_name) - -- lo rimuovo - players_in_game[p_name] = nil - arena.players_amount = arena.players_amount - 1 - if arena.teams_enabled then - local p_team_ID = arena.players[p_name].teamID - arena.players_amount_per_team[p_team_ID] = arena.players_amount_per_team[p_team_ID] - 1 - end - arena.players[p_name] = nil + -- se il giocatore era in spettatore + if mod_ref.spectate_mode and arena_lib.is_player_spectating(p_name) then - -- se una ragione è specificata - if reason ~= 0 then + arena_lib.leave_spectate_mode(p_name) - operations_before_leaving_arena(mod_ref, arena, p_name) + -- TEMP: 5.4, senza after non teletrasporta dove dovrebbe. Si veda https://github.com/minetest/minetest/pull/10235 + -- Quando sistemato, basta eseguire solo operations_before_leaving_arena + if reason ~= 0 then + minetest.after(0.1, function() + operations_before_leaving_arena(mod_ref, arena, p_name) + arena.past_present_players_inside[p_name] = nil + end) + else + arena.past_present_players_inside[p_name] = nil + end - -- ripristino nomi - minetest.get_player_by_name(p_name):set_nametag_attributes({color = {a = 255, r = 255, g = 255, b = 255}}) + handle_leaving_callbacks(mod_ref, arena, p_name, reason, executioner, true) + players_in_game[p_name] = nil + arena_lib.send_message_in_arena(arena, "both", minetest.colorize("#cfc6b8", "<<< " .. S("@1 has quit the match", p_name) .. " (" .. S("spectator") .. ")")) + + -- sennò... + else + + -- rimuovo + arena.players_amount = arena.players_amount - 1 + if arena.teams_enabled then + local p_team_ID = arena.players[p_name].teamID + arena.players_amount_per_team[p_team_ID] = arena.players_amount_per_team[p_team_ID] - 1 + end + arena.players[p_name] = nil + + -- se ha abbandonato mentre aveva degli spettatori, li riassegno + if arena_lib.is_player_spectated(p_name) then + for sp_name, _ in pairs(arena_lib.get_player_spectators(p_name)) do + arena_lib.find_and_spectate_player(sp_name) + end + end + + -- se è stato eliminato, tratto il callback a parte perché è l'unico dove potrebbe venire mandato eventualmente in spettatore if reason == 1 then + + -- manda eventualmente in spettatore + if mod_ref.spectate_mode and arena.players_amount > 0 then + arena_lib.enter_spectate_mode(p_name, arena) + else + operations_before_leaving_arena(mod_ref, arena, p_name) + arena.players_and_spectators[p_name] = nil + arena.past_present_players_inside[p_name] = nil + players_in_game[p_name] = nil + end + if executioner then arena_lib.send_message_players_in_arena(arena, minetest.colorize("#f16a54", "<<< " .. S("@1 has been eliminated by @2", p_name, executioner))) else arena_lib.send_message_players_in_arena(arena, minetest.colorize("#f16a54", "<<< " .. S("@1 has been eliminated", p_name))) end + if mod_ref.on_eliminate then mod_ref.on_eliminate(arena, p_name) elseif mod_ref.on_quit then mod_ref.on_quit(arena, p_name) end - elseif reason == 2 then - if executioner then - arena_lib.send_message_players_in_arena(arena, minetest.colorize("#f16a54", "<<< " .. S("@1 has been kicked by @2", p_name, executioner))) - else - arena_lib.send_message_players_in_arena(arena, minetest.colorize("#f16a54", "<<< " .. S("@1 has been kicked", p_name))) - end - if mod_ref.on_kick then - mod_ref.on_kick(arena, p_name) - elseif mod_ref.on_quit then - mod_ref.on_quit(arena, p_name) - end - elseif reason == 3 then - arena_lib.send_message_players_in_arena(arena, minetest.colorize("#d69298", "<<< " .. S("@1 has quit the match", p_name))) - if mod_ref.on_quit then - mod_ref.on_quit(arena, p_name) - end - elseif reason == 4 then - if executioner then - minetest.chat_send_player(p_name, minetest.colorize("#d69298", S("The arena has been forcibly terminated by @1", executioner))) - else - minetest.chat_send_player(p_name, minetest.colorize("#d69298", S("The arena has been forcibly terminated"))) - end - if mod_ref.on_quit then - mod_ref.on_quit(arena, p_name, true) + + -- sennò procedo a rimuoverlo normalmente + else + if reason ~= 0 then -- non c'è bisogno di reimpostare i vari parametri se si è disconnesso + operations_before_leaving_arena(mod_ref, arena, p_name) end + + arena.players_and_spectators[p_name] = nil + arena.past_present_players_inside[p_name] = nil + players_in_game[p_name] = nil end - else - arena_lib.send_message_players_in_arena(arena, minetest.colorize("#f16a54", "<<< " .. p_name )) - if mod_ref.on_disconnect then - mod_ref.on_disconnect(arena, p_name) - end + + handle_leaving_callbacks(mod_ref, arena, p_name, reason, executioner) + end -- se il termine dell'arena è stato forzato o è già in celebrazione, non c'è bisogno di andare oltre @@ -1489,7 +1595,6 @@ end ---WARNING: internal use only function arena_lib.restore_inventory(p_name) if arena_lib.STORE_INVENTORY_MODE == "mod_db" and storage:get_string(p_name .. ".INVENTORY") ~= "" then @@ -1527,25 +1632,33 @@ end -function arena_lib.send_message_players_in_arena(arena, msg, teamID, except_teamID) +-- channel: "players", "spectators", "both" +function arena_lib.send_message_in_arena(arena, channel, msg, teamID, except_teamID) - if teamID then - if except_teamID then - for pl_name, pl_stats in pairs(arena.players) do - if pl_stats.teamID ~= teamID then - minetest.chat_send_player(pl_name, msg) + if channel == "players" then + if teamID then + if except_teamID then + for pl_name, pl_stats in pairs(arena.players) do + if pl_stats.teamID ~= teamID then + minetest.chat_send_player(pl_name, msg) + end + end + else + for pl_name, pl_stats in pairs(arena.players) do + if pl_stats.teamID == teamID then + minetest.chat_send_player(pl_name, msg) + end end end else - for pl_name, pl_stats in pairs(arena.players) do - if pl_stats.teamID == teamID then - minetest.chat_send_player(pl_name, msg) - end + for pl_name, _ in pairs(arena.players) do + minetest.chat_send_player(pl_name, msg) end end - else - for pl_name, _ in pairs(arena.players) do - minetest.chat_send_player(pl_name, msg) + + elseif channel == "spectators" then + for sp_name, _ in pairs(arena.spectators) do + minetest.chat_send_player(sp_name, msg) end end end @@ -1787,19 +1900,34 @@ function init_storage(mod, mod_ref) end --^------------------ LEGACY UPDATE, to remove in 6.0 -------------------^ - -- gestione team - if arena.teams_enabled and not (#mod_ref.teams > 1) then -- se avevo abilitato i team e ora li ho rimossi + --v------------------ LEGACY UPDATE, to remove in 7.0 -------------------v + if not arena.spectators then + arena.spectators = {} + arena.spectators_amount = 0 + arena.players_and_spectators = {} + arena.past_present_players = {} + arena.past_present_players_inside = {} + to_update = true + end + --^------------------ LEGACY UPDATE, to remove in 7.0 -------------------^ + + -- gestione squadre + if arena.teams_enabled and not (#mod_ref.teams > 1) then -- se avevo abilitato le squadre e ora le ho rimosse arena.players_amount_per_team = nil arena.teams = {-1} arena.teams_enabled = false - elseif #mod_ref.teams > 1 and arena.teams_enabled then -- sennò li genero per tutte le arena con teams_enabled + arena.spectators_amount_per_team = nil + elseif #mod_ref.teams > 1 and arena.teams_enabled then -- sennò le genero per tutte le arene con teams_enabled arena.players_amount_per_team = {} + arena.spectators_amount_per_team = {} arena.teams = {} for k, t_name in pairs(mod_ref.teams) do arena.players_amount_per_team[k] = 0 + arena.spectators_amount_per_team[k] = 0 arena.teams[k] = {name = t_name} end + end local arena_max_players = arena.max_players * #arena.teams @@ -2043,18 +2171,6 @@ function operations_before_entering_arena(mod_ref, mod, arena, arena_ID, p_name) local player = minetest.get_player_by_name(p_name) - -- applico eventuale fov - if mod_ref.fov then - players_temp_storage[p_name].fov = player:get_fov() - player:set_fov(mod_ref.fov) - end - - -- applico eventuale scostamento camera - if mod_ref.camera_offset then - players_temp_storage[p_name].camera_offset = player:get_eye_offset() - player:set_eye_offset(mod_ref.camera_offset[1], mod_ref.camera_offset[2]) - end - -- nascondo i nomi se l'opzione è abilitata if not mod_ref.show_nametags then player:set_nametag_attributes({color = {a = 0, r = 255, g = 255, b = 255}}) @@ -2065,48 +2181,6 @@ function operations_before_entering_arena(mod_ref, mod, arena, arena_ID, p_name) player:hud_set_flags({minimap = false}) end - -- cambio eventuale colore texture (richiede i team) - if arena.teams_enabled and mod_ref.teams_color_overlay then - player:set_properties({ - textures = {player:get_properties().textures[1] .. "^[colorize:" .. mod_ref.teams_color_overlay[arena.players[p_name].teamID] .. ":85"} - }) - end - - -- cambio l'eventuale hotbar - if mod_ref.hotbar then - - local hotbar = mod_ref.hotbar - - if hotbar.slots then - players_temp_storage[p_name].hotbar_slots = player:hud_get_hotbar_itemcount() - player:hud_set_hotbar_itemcount(hotbar.slots) - end - - if hotbar.background_image then - players_temp_storage[p_name].hotbar_background_image = player:hud_get_hotbar_image() - player:hud_set_hotbar_image(hotbar.background_image) - end - - if hotbar.selected_image then - players_temp_storage[p_name].hotbar_selected_image = player:hud_get_hotbar_selected_image() - player:hud_set_hotbar_selected_image(hotbar.selected_image) - end - end - - -- assegno eventuali proprietà giocatori - for k, v in pairs(mod_ref.player_properties) do - if type(v) == "table" then - arena.players[p_name][k] = copy_table(v) - else - arena.players[p_name][k] = v - end - end - - -- imposto eventuale fisica personalizzata - if mod_ref.in_game_physics then - player:set_physics_override(mod_ref.in_game_physics) - end - -- chiudo eventuali formspec minetest.close_formspec(p_name, "") @@ -2139,6 +2213,17 @@ function operations_before_entering_arena(mod_ref, mod, arena, arena_ID, p_name) -- li curo player:set_hp(minetest.PLAYER_MAX_HP_DEFAULT) + -- salvo la hotbar se c'è la spettatore o la hotbar personalizzata + if mod_ref.spectate_mode or mod_ref.hotbar then + players_temp_storage[p_name].hotbar_slots = player:hud_get_hotbar_itemcount() + players_temp_storage[p_name].hotbar_background_image = player:hud_get_hotbar_image() + players_temp_storage[p_name].hotbar_selected_image = player:hud_get_hotbar_selected_image() + end + + if not arena_lib.is_player_spectating(p_name) then + operations_before_playing_arena(mod_ref, arena, p_name) + end + -- registro giocatori nella tabella apposita players_in_queue[p_name] = nil players_in_game[p_name] = {minigame = mod, arenaID = arena_ID} @@ -2146,6 +2231,67 @@ end +function operations_before_playing_arena(mod_ref, arena, p_name) + + arena.past_present_players[p_name] = true + arena.past_present_players_inside[p_name] = true + + local player = minetest.get_player_by_name(p_name) + + -- applico eventuale fov + if mod_ref.fov then + players_temp_storage[p_name].fov = player:get_fov() + player:set_fov(mod_ref.fov) + end + + -- applico eventuale scostamento camera + if mod_ref.camera_offset then + players_temp_storage[p_name].camera_offset = player:get_eye_offset() + player:set_eye_offset(mod_ref.camera_offset[1], mod_ref.camera_offset[2]) + end + + -- cambio eventuale colore texture (richiede i team) + if arena.teams_enabled and mod_ref.teams_color_overlay then + player:set_properties({ + textures = {player:get_properties().textures[1] .. "^[colorize:" .. mod_ref.teams_color_overlay[arena.players[p_name].teamID] .. ":85"} + }) + end + + -- cambio l'eventuale hotbar + if mod_ref.hotbar then + + local hotbar = mod_ref.hotbar + + if hotbar.slots then + player:hud_set_hotbar_itemcount(hotbar.slots) + end + + if hotbar.background_image then + player:hud_set_hotbar_image(hotbar.background_image) + end + + if hotbar.selected_image then + player:hud_set_hotbar_selected_image(hotbar.selected_image) + end + end + + -- imposto eventuale fisica personalizzata + if mod_ref.in_game_physics then + player:set_physics_override(mod_ref.in_game_physics) + end + + -- assegno eventuali proprietà giocatori + for k, v in pairs(mod_ref.player_properties) do + if type(v) == "table" then + arena.players[p_name][k] = copy_table(v) + else + arena.players[p_name][k] = v + end + end +end + + + function operations_before_leaving_arena(mod_ref, arena, p_name) local player = minetest.get_player_by_name(p_name) @@ -2160,36 +2306,32 @@ function operations_before_leaving_arena(mod_ref, arena, p_name) end end - -- resetto eventuali texture - if arena.teams_enabled and mod_ref.teams_color_overlay then - player:set_properties({ - textures = {string.match(player:get_properties().textures[1], "(.*)^%[")} - }) + -- se c'è la spettatore o l'hotbar personalizzata, la ripristino + if mod_ref.spectate_mode or mod_ref.hotbar then + player:hud_set_hotbar_itemcount(players_temp_storage[p_name].hotbar_slots) + player:hud_set_hotbar_image(players_temp_storage[p_name].hotbar_background_image) + player:hud_set_hotbar_selected_image(players_temp_storage[p_name].hotbar_selected_image) end - -- reimposto eventuale hotbar - if mod_ref.hotbar then - local hotbar = mod_ref.hotbar + -- se ha partecipato come giocatore + if arena.past_present_players_inside[p_name] then - if hotbar.slots then - player:hud_set_hotbar_itemcount(players_temp_storage[p_name].hotbar_slots) + -- resetto eventuali texture + if arena.teams_enabled and mod_ref.teams_color_overlay then + player:set_properties({ + textures = {string.match(player:get_properties().textures[1], "(.*)^%[")} + }) end - if hotbar.background_image then - player:hud_set_hotbar_image(players_temp_storage[p_name].hotbar_background_image) - end - if hotbar.selected_image then - player:hud_set_hotbar_image(players_temp_storage[p_name].hotbar_selected_image) - end - end - -- reimposto eventuale fov - if mod_ref.fov then - player:set_fov(players_temp_storage[p_name].fov) - end + -- reimposto eventuale fov + if mod_ref.fov then + player:set_fov(players_temp_storage[p_name].fov) + end - -- ripristino eventuale camera - if mod_ref.camera_offset then - player:set_eye_offset(players_temp_storage[p_name].camera_offset[1], players_temp_storage[p_name].camera_offset[2]) + -- ripristino eventuale camera + if mod_ref.camera_offset then + player:set_eye_offset(players_temp_storage[p_name].camera_offset[1], players_temp_storage[p_name].camera_offset[2]) + end end -- ripristino gli HP @@ -2213,6 +2355,9 @@ function operations_before_leaving_arena(mod_ref, arena, p_name) -- riattivo la minimappa eventualmente disattivata player:hud_set_flags({minimap = true}) + -- ripristino nomi + player:set_nametag_attributes({color = {a = 255, r = 255, g = 255, b = 255}}) + -- disattivo eventuale musica di sottofondo if arena.bgm then minetest.sound_stop(players_temp_storage[p_name].bgm_handle) @@ -2224,6 +2369,62 @@ end +function handle_leaving_callbacks(mod_ref, arena, p_name, reason, executioner, is_spectator) + + local msg_color = reason < 3 and "#f16a54" or "#d69298" + local spect_str = "" + + if is_spectator then + msg_color = "#cfc6b8" + spect_str = " (" .. S("spectator") .. ")" + end + + -- se si è disconnesso + if reason == 0 then + arena_lib.send_message_players_in_arena(arena, minetest.colorize(msg_color, "<<< " .. p_name .. spect_str)) + + if mod_ref.on_disconnect then + mod_ref.on_disconnect(arena, p_name, is_spectator) + end + + -- se è stato cacciato + elseif reason == 2 then + if executioner then + arena_lib.send_message_players_in_arena(arena, minetest.colorize(msg_color, "<<< " .. S("@1 has been kicked by @2", p_name, executioner) .. spect_str)) + else + arena_lib.send_message_players_in_arena(arena, minetest.colorize(msg_color, "<<< " .. S("@1 has been kicked", p_name) .. spect_str)) + end + + if mod_ref.on_kick then + mod_ref.on_kick(arena, p_name, is_spectator) + elseif mod_ref.on_quit then + mod_ref.on_quit(arena, p_name, is_spectator) + end + + -- se ha abbandonato + elseif reason == 3 then + arena_lib.send_message_players_in_arena(arena, minetest.colorize(msg_color, "<<< " .. S("@1 has quit the match", p_name) .. spect_str)) + + if mod_ref.on_quit then + mod_ref.on_quit(arena, p_name, is_spectator) + end + + -- se la fine è stata forzata + elseif reason == 4 then + if executioner then + minetest.chat_send_player(p_name, minetest.colorize(msg_color, S("The arena has been forcibly terminated by @1", executioner) .. spect_str)) + else + minetest.chat_send_player(p_name, minetest.colorize(msg_color, S("The arena has been forcibly terminated") .. spect_str)) + end + + if mod_ref.on_quit then + mod_ref.on_quit(arena, p_name, is_spectator, true) + end + end +end + + + function show_victory_particles(p_pos) minetest.add_particlespawner({ amount = 50, @@ -2271,8 +2472,13 @@ end ------------------DEPRECATED------------------ ---------------------------------------------- --- to remove in 6.0 +-- to remove in 7.0 function arena_lib.remove_from_queue(p_name) minetest.log("warning", "[ARENA_LIB] remove_from_queue is deprecated. Please use remove_player_from_queue instead") arena_lib.remove_player_from_queue(p_name) end + +function arena_lib.send_message_players_in_arena(arena, msg, teamID, except_teamID) + minetest.log("warning", "[ARENA_LIB] send_message_players_in_arena is deprecated. Please use send_message_in_arena instead") + arena_lib.send_message_in_arena(arena, "players", msg, teamID, except_teamID) +end diff --git a/chat.lua b/chat.lua index 3861e81..bb6f9f6 100755 --- a/chat.lua +++ b/chat.lua @@ -5,17 +5,21 @@ minetest.register_on_chat_message(function(p_name, message) local mod_ref = arena_lib.mods[arena_lib.get_mod_by_player(p_name)] local arena = arena_lib.get_arena_by_player(p_name) - if arena.teams_enabled then - if mod_ref.is_team_chat_default then - arena_lib.send_message_players_in_arena(arena, minetest.colorize(mod_ref.chat_team_color, mod_ref.chat_team_prefix .. minetest.format_chat_message(p_name, message)), arena.players[p_name].teamID) - else - arena_lib.send_message_players_in_arena(arena, mod_ref.chat_all_prefix .. minetest.format_chat_message(p_name, message), arena.players[p_name].teamID) - arena_lib.send_message_players_in_arena(arena, minetest.colorize("#ffdddd", mod_ref.chat_all_prefix .. minetest.format_chat_message(p_name, message)), arena.players[p_name].teamID, true) - end + if arena_lib.is_player_spectating(p_name) then + arena_lib.send_message_in_arena(arena, "spectators", minetest.colorize(mod_ref.chat_spectate_color, mod_ref.chat_spectate_prefix .. minetest.format_chat_message(p_name, message))) else - arena_lib.send_message_players_in_arena(arena, minetest.colorize(mod_ref.chat_all_color, mod_ref.chat_all_prefix .. minetest.format_chat_message(p_name, message))) + if arena.teams_enabled then + if mod_ref.is_team_chat_default then + arena_lib.send_message_players_in_arena(arena, minetest.colorize(mod_ref.chat_team_color, mod_ref.chat_team_prefix .. minetest.format_chat_message(p_name, message)), arena.players[p_name].teamID) + else + arena_lib.send_message_players_in_arena(arena, mod_ref.chat_all_prefix .. minetest.format_chat_message(p_name, message), arena.players[p_name].teamID) + arena_lib.send_message_players_in_arena(arena, minetest.colorize("#ffdddd", mod_ref.chat_all_prefix .. minetest.format_chat_message(p_name, message)), arena.players[p_name].teamID, true) + end + else + arena_lib.send_message_players_in_arena(arena, minetest.colorize(mod_ref.chat_all_color, mod_ref.chat_all_prefix .. minetest.format_chat_message(p_name, message))) + end + return true end - return true else for _, pl_stats in pairs(minetest.get_connected_players()) do local pl_name = pl_stats:get_player_name() diff --git a/commands.lua b/commands.lua index 79fd4fd..b1c0737 100755 --- a/commands.lua +++ b/commands.lua @@ -85,6 +85,31 @@ minetest.register_chatcommand("forceend", { +minetest.register_chatcommand("flusharena", { + + params = "<" .. S("minigame") .. "> <" .. S("arena name") .. ">", + description = S("(temp) DEBUG ONLY: reset the properties of a bugged arena"), + privs = { + arenalib_admin = true, + }, + + func = function(sender, param) + local mod, arena_name = string.match(param, "^([%a%d_-]+) ([%a%d_-]+)$") + + -- se i parametri sono errati, annullo + if not mod or not arena_name then + minetest.chat_send_player(sender, minetest.colorize("#e6482e", S("[!] Parameters don't seem right!"))) + return end + + local id, arena = arena_lib.get_arena_by_name(mod, arena_name) + + arena_lib.flush_arena(mod, arena, sender) + end + +}) + + + ---------------------------------------------- diff --git a/debug_utilities.lua b/debug_utilities.lua index bcc537d..987ac44 100755 --- a/debug_utilities.lua +++ b/debug_utilities.lua @@ -34,6 +34,7 @@ function arena_lib.print_arena_info(sender, mod, arena_name) local min_players_per_team = "" local max_players_per_team = "" local players_inside_per_team = "" + local spectators_inside_per_team = "" -- concateno eventuali team if arena.teams_enabled then @@ -42,8 +43,14 @@ function arena_lib.print_arena_info(sender, mod, arena_name) for i = 1, #arena.teams do teams = teams .. "'" .. arena.teams[i].name .. "' " players_inside_per_team = players_inside_per_team .. "'" .. arena.teams[i].name .. "' : " .. arena.players_amount_per_team[i] .. " " + if mod_ref.spectate_mode then + spectators_inside_per_team = spectators_inside_per_team .. "'" .. arena.teams[i].name .. "' : " .. arena.spectators_amount_per_team[i] .. " " + end end players_inside_per_team = minetest.colorize("#eea160", S("Players inside per team: ")) .. minetest.colorize("#cfc6b8", players_inside_per_team) .. "\n" + if mod_ref.spectate_mode then + spectators_inside_per_team = minetest.colorize("#eea160", S("Spectators inside per team: ")) .. minetest.colorize("#cfc6b8", spectators_inside_per_team) .. "\n" + end else teams = "---" end @@ -59,9 +66,39 @@ function arena_lib.print_arena_info(sender, mod, arena_name) end -- concateno nomi giocatori - local names = "" + local p_names = "" for pl, stats in pairs(arena.players) do - names = names .. " " .. pl + p_names = p_names .. " " .. pl + end + + -- concateno nomi spettatori + local sp_names = "" + for sp_name, stats in pairs(arena.spectators) do + sp_names = sp_names .. " " .. sp_name + end + + -- concateno giocatori e spettatori (per verificare che campo sia giusto) + local psp_names = "" + local psp_amount = 0 + for psp_name, _ in pairs(arena.players_and_spectators) do + psp_names = psp_names .. " " .. psp_name + psp_amount = psp_amount + 1 + end + + -- concateno giocatori presenti e passati + local ppp_names = "" + local ppp_names_amount = 0 + for ppp_name, _ in pairs(arena.past_present_players) do + ppp_names = ppp_names .. " " .. ppp_name + ppp_names_amount = ppp_names_amount + 1 + end + + -- concateno giocatori presenti e passati + local ppp_names_inside = "" + local ppp_names_inside_amount = 0 + for ppp_name_inside, _ in pairs(arena.past_present_players_inside) do + ppp_names_inside = ppp_names_inside .. " " .. ppp_name_inside + ppp_names_inside_amount = ppp_names_inside_amount + 1 end -- calcolo stato arena @@ -165,8 +202,13 @@ function arena_lib.print_arena_info(sender, mod, arena_name) max_players_per_team .. minetest.colorize("#eea160", S("Players required: ")) .. minetest.colorize("#cfc6b8", arena_min_players) .. "\n" .. minetest.colorize("#eea160", S("Players supported: ")) .. minetest.colorize("#cfc6b8", arena_max_players) .. "\n" .. - minetest.colorize("#eea160", S("Players inside: ")) .. minetest.colorize("#cfc6b8", arena.players_amount .. " ( ".. names .. " )") .. "\n" .. + minetest.colorize("#eea160", S("Players inside: ")) .. minetest.colorize("#cfc6b8", arena.players_amount .. " ( ".. p_names .. " )") .. "\n" .. players_inside_per_team .. + minetest.colorize("#eea160", S("Spectators inside: ")) .. minetest.colorize("#cfc6b8", arena.spectators_amount .. " ( ".. sp_names .. " )") .. "\n" .. + spectators_inside_per_team .. + minetest.colorize("#eea160", S("Players and spectators inside: ")) .. minetest.colorize("#cfc6b8", psp_amount .. " ( ".. psp_names .. " )") .. "\n" .. + minetest.colorize("#eea160", S("Past and present players: ")) .. minetest.colorize("#cfc6b8", ppp_names_amount .. " ( " .. ppp_names .. " )") .."\n" .. + minetest.colorize("#eea160", S("Past and present players inside: ")) .. minetest.colorize("#cfc6b8", ppp_names_inside_amount .. " ( " .. ppp_names_inside .. " )") .."\n" .. minetest.colorize("#eea160", S("Enabled: ")) .. minetest.colorize("#cfc6b8", tostring(arena.enabled)) .. "\n" .. minetest.colorize("#eea160", S("Status: ")) .. minetest.colorize("#cfc6b8", status) .. "\n" .. minetest.colorize("#eea160", S("Sign: ")) .. minetest.colorize("#cfc6b8", sign_pos) .. "\n" .. @@ -212,6 +254,36 @@ end +function arena_lib.flush_arena(mod, arena, sender) + + if arena.in_queue or arena.in_game then + minetest.chat_send_player(sender, minetest.colorize("#e6482e", S("[!] You can't perform this action during an ongoing game!"))) + return end + + arena.players = {} + arena.spectators = {} + arena.players_and_spectators = {} + arena.past_present_players = {} + arena.past_present_players_inside = {} + arena.players_amount = 0 + + if arena.teams_enabled then + local mod_ref = arena_lib.mods[mod] + for i = 1, #arena.teams do + arena.players_amount_per_team[i] = 0 + if mod_ref.spectate_mode then + arena.spectators_amount_per_team[i] = 0 + end + end + end + + arena.current_time = nil + + minetest.chat_send_player(sender, "Sluuush!") +end + + + ---------------------------------------------- diff --git a/init.lua b/init.lua index 25de78f..a2ac2dc 100755 --- a/init.lua +++ b/init.lua @@ -21,6 +21,9 @@ dofile(modpath .. "/_editor/tools_sign.lua") dofile(modpath .. "/_editor/tools_spawner.lua") dofile(modpath .. "/_hud/hud_main.lua") dofile(modpath .. "/_hud/hud_waypoints.lua") +dofile(modpath .. "/_spectate/spectate_main.lua") +dofile(modpath .. "/_spectate/spectate_hand.lua") +dofile(modpath .. "/_spectate/spectate_tools.lua") dofile(modpath .. "/SETTINGS.lua") diff --git a/locale/arena_lib.es.tr b/locale/arena_lib.es.tr index 9917dfb..2e69989 100644 --- a/locale/arena_lib.es.tr +++ b/locale/arena_lib.es.tr @@ -165,7 +165,7 @@ Arena settings=Configuraciones de la arena Info=Información Go back=Regresar Enable and leave=Habilitar y salir -Leave the editor=Salir del editor +Leave=Salir # _editor/tools_bgm.lua Audio file=Archivo de audio diff --git a/locale/arena_lib.it.tr b/locale/arena_lib.it.tr index 503523a..3f08441 100755 --- a/locale/arena_lib.it.tr +++ b/locale/arena_lib.it.tr @@ -1,4 +1,4 @@ -# version 4.6.0 +# version 5.0.0-dev # author(s): Zughy # reviewer(s): # textdomain: arena_lib @@ -6,6 +6,7 @@ # api.lua arena=arena team=squadra +spectator=spettatore [!] You can't change type!=[!] Non puoi cambiare tipo! Parameter @1 successfully overwritten=Parametro @1 sovrascritto con successo [!] Parameters don't seem right!=[!] I parametri hanno qualcosa che non va! @@ -167,7 +168,7 @@ Arena settings=Impostazioni arena Info=Info Go back=Torna indietro Enable and leave=Abilita ed esci -Leave the editor=Esci dall'editor +Leave=Esci # _editor/tools_bgm.lua Audio file=File audio @@ -210,3 +211,12 @@ Remove sign=Rimuovi cartello # _hud/hud_waypoints.lua [!] Waypoints are not enabled!=[!] I waypoint non sono abilitati! + +# _spectate/spectate_main.lua +[!] Spectate mode not supported!=[!] Modalità spettatore non supportata! +Currently spectating: @1=Stai seguendo: @1 + +# _spectate/spectate_tools.lua +Change player=Cambia giocatore +Change team=Cambia squadra +Enter the match=Entra in partita diff --git a/locale/template.txt b/locale/template.txt index 3f42734..c038284 100755 --- a/locale/template.txt +++ b/locale/template.txt @@ -1,4 +1,4 @@ -# version 4.6.0 +# version 5.0.0-dev # author(s): # reviewer(s): # textdomain: arena_lib @@ -6,6 +6,7 @@ # api.lua arena= team= +spectator= [!] You can't change type!= Parameter @1 successfully overwritten= [!] Parameters don't seem right!= @@ -167,7 +168,7 @@ Arena settings= Info= Go back= Enable and leave= -Leave the editor= +Leave= # _editor/tools_bgm.lua Audio file= @@ -210,3 +211,12 @@ Remove sign= # _hud/hud_waypoints.lua [!] Waypoints are not enabled!= + +# _spectate/spectate_main.lua +[!] Spectate mode not supported!= +Currently spectating: @1= + +# _spectate/spectate_tools.lua +Change player= +Change team= +Enter the match= diff --git a/player_manager.lua b/player_manager.lua index 7230037..63dd3a0 100755 --- a/player_manager.lua +++ b/player_manager.lua @@ -6,11 +6,11 @@ minetest.register_on_joinplayer(function(player) arena_lib.restore_inventory(player:get_player_name()) end + local p_meta = player:get_meta() + -- nel caso qualcuno si fosse disconnesso da dentro all'editor o fosse crashato il server con qualcuno nell'editor if player:get_inventory():contains_item("main", "arena_lib:editor_quit") then - local p_meta = player:get_meta() - p_meta:set_string("arena_lib_editor.mod", "") p_meta:set_string("arena_lib_editor.arena", "") p_meta:set_int("arena_lib_editor.players_number", 0) @@ -21,8 +21,14 @@ minetest.register_on_joinplayer(function(player) player:get_inventory():set_list("main", {}) player:get_inventory():set_list("craft",{}) + + -- se invece era in spettatore + elseif player:get_inventory():get_list("hand") and player:get_inventory():contains_item("hand", "arena_lib:spectate_hand") then + player:get_inventory():set_size("hand", 0) end + p_meta:set_string("arenalib_infobox_mod", "") + p_meta:set_int("arenalib_infobox_arenaID", 0) end) @@ -52,6 +58,10 @@ minetest.register_on_punchplayer(function(player, hitter, time_from_last_punch, local p_name = hitter:get_player_name() local arena = arena_lib.get_arena_by_player(p_name) + if arena_lib.is_player_spectating(p_name) then + return true + end + if arena and arena.in_game and arena.players[p_name].teamID and arena.players[p_name].teamID == arena.players[target_name].teamID then return true end @@ -62,11 +72,17 @@ end) minetest.register_on_player_hpchange(function(player, hp_change, reason) - local mod = arena_lib.get_mod_by_player(player:get_player_name()) + local p_name = player:get_player_name() + local mod = arena_lib.get_mod_by_player(p_name) -- se non è in partita, annullo if not mod then return hp_change end + -- se è spettatore, annullo + if arena_lib.is_player_spectating(p_name) and reason.type ~= "respawn" then + return 0 + end + -- se un tipo di danno è disabilitato, annullo for _, disabled_damage in pairs(arena_lib.mods[mod].disabled_damage_types) do if reason.type == disabled_damage then @@ -108,5 +124,4 @@ minetest.register_on_respawnplayer(function(player) player:set_pos(arena_lib.get_random_spawner(arena, arena.players[p_name].teamID)) return true - end) diff --git a/signs.lua b/signs.lua index 83b4e83..dd0d897 100755 --- a/signs.lua +++ b/signs.lua @@ -43,9 +43,8 @@ signs_lib.register_sign("arena_lib:sign", { local mod = minetest.get_meta(pos):get_string("mod") local arenaID = minetest.get_meta(pos):get_int("arenaID") - local arena = arena_lib.mods[mod].arenas[arenaID] - minetest.show_formspec(clicker:get_player_name(), "arena_lib:infobox", get_infobox_formspec(arena)) + minetest.show_formspec(clicker:get_player_name(), "arena_lib:infobox", get_infobox_formspec(mod, arenaID, clicker)) end, @@ -142,13 +141,11 @@ signs_lib.register_sign("arena_lib:sign", { local party_members = parties.get_party_members(p_name) for _, pl_name in pairs(party_members) do - arena_lib.HUD_hide("all", pl_name) arena_lib.remove_player_from_queue(pl_name) end -- sennò rimuovo il singolo utente else - arena_lib.HUD_hide("all", p_name) arena_lib.remove_player_from_queue(p_name) end @@ -178,6 +175,7 @@ signs_lib.register_sign("arena_lib:sign", { -- aggiungo il giocatore for _, pl_name in pairs(players_to_add) do sign_arena.players[pl_name] = {kills = 0, deaths = 0, teamID = p_team_ID} + sign_arena.players_and_spectators[pl_name] = true end -- aumento il conteggio di giocatori in partita @@ -190,14 +188,12 @@ signs_lib.register_sign("arena_lib:sign", { if sign_arena.in_game then for _, pl_name in pairs(players_to_add) do arena_lib.join_arena(mod, pl_name, arenaID) - arena_lib.send_message_players_in_arena(sign_arena, minetest.colorize("#c6f154", " >>> " .. pl_name)) arena_lib.update_sign(sign_arena) end return else for _, pl_name in pairs(players_to_add) do arena_lib.add_to_queue(pl_name, mod, arenaID) - arena_lib.send_message_players_in_arena(sign_arena, minetest.colorize("#c8d692", sign_arena.name .. " > " .. pl_name)) end end @@ -377,8 +373,12 @@ end -function get_infobox_formspec(arena) +function get_infobox_formspec(mod, arenaID, player) + player:get_meta():set_string("arenalib_infobox_mod", mod) + player:get_meta():set_int("arenalib_infobox_arenaID", arenaID) + + local arena = arena_lib.mods[mod].arenas[arenaID] local bgm_info if arena.bgm then @@ -401,11 +401,11 @@ function get_infobox_formspec(arena) "image[1,0.7;1,1;arenalib_tool_settings_rename.png]", "image[1,1.7;1,1;arenalib_tool_settings_nameauthor.png]", "image[1,3.1;1,1;arenalib_editor_bgm.png]", + "image_button[6.1,2;1,1;arenalib_infobox_spectate.png;spectate;]", -- scritte "hypertext[2.4,1.1;4,1;name;]", "hypertext[2.4,2.15;4,1;name;]", "hypertext[2.4,3.15;4,1;name;]", - } return table.concat(formspec, "") @@ -423,5 +423,18 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) if fields.close then minetest.close_formspec(player:get_player_name(), formname) + player:get_meta():set_string("arenalib_infobox_mod", "") + player:get_meta():set_int("arenalib_infobox_arenaID", 0) + + elseif fields.spectate then + local mod = player:get_meta():get_string("arenalib_infobox_mod") + local arenaID = player:get_meta():get_int("arenalib_infobox_arenaID") + local p_name = player:get_player_name() + + if arena_lib.is_player_in_queue(p_name) then + arena_lib.remove_player_from_queue(p_name) + end + + arena_lib.join_arena(mod, p_name, arenaID, true) end end) diff --git a/textures/arenalib_infobox_spectate.png b/textures/arenalib_infobox_spectate.png new file mode 100644 index 0000000..3ad4e0a Binary files /dev/null and b/textures/arenalib_infobox_spectate.png differ diff --git a/textures/arenalib_spectate_changeplayer.png b/textures/arenalib_spectate_changeplayer.png new file mode 100644 index 0000000..e2f472f Binary files /dev/null and b/textures/arenalib_spectate_changeplayer.png differ diff --git a/textures/arenalib_spectate_changeteam.png b/textures/arenalib_spectate_changeteam.png new file mode 100644 index 0000000..a5ea911 Binary files /dev/null and b/textures/arenalib_spectate_changeteam.png differ