Spectate mode | /flusharena | send_message_players_in_arena -> send_message_in_arena

This commit is contained in:
Zughy 2021-02-15 22:53:32 +00:00
parent e4025ac7a2
commit 68e725d74b
19 changed files with 1036 additions and 259 deletions

52
DOCS.md
View File

@ -35,6 +35,7 @@
* [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, <duration>, <sound>, <color>)`: 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, <duration>, <sound>, <color>)`: 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, <duration>, <sound>, <color>)`: 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, <duration>, <sound>, <color>)`: 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, <teamID>, <except_teamID>)`: 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, <teamID>, <except_teamID>)`: 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, <pos, remove>, <mod, arena_name>)`: 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`

View File

@ -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,

View File

@ -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]

View File

@ -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}
})

311
_spectate/spectate_main.lua Normal file
View File

@ -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

View File

@ -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
})

494
api.lua
View File

@ -19,7 +19,9 @@ 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
@ -33,10 +35,16 @@ local arena_default = {
author = "???",
sign = {},
players = {}, -- KEY: player name, VALUE: {kills, deaths, teamID, <player_properties>}
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
-- 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,8 +1478,31 @@ 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
-- se il giocatore era in spettatore
if mod_ref.spectate_mode and arena_lib.is_player_spectating(p_name) then
arena_lib.leave_spectate_mode(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
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
@ -1399,56 +1510,51 @@ function arena_lib.remove_player_from_arena(p_name, reason, executioner)
end
arena.players[p_name] = nil
-- se una ragione è specificata
if reason ~= 0 then
operations_before_leaving_arena(mod_ref, arena, p_name)
-- ripristino nomi
minetest.get_player_by_name(p_name):set_nametag_attributes({color = {a = 255, r = 255, g = 255, b = 255}})
-- 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)))
-- sennò procedo a rimuoverlo normalmente
else
arena_lib.send_message_players_in_arena(arena, minetest.colorize("#f16a54", "<<< " .. S("@1 has been kicked", p_name)))
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
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)
end
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)
arena.players_and_spectators[p_name] = nil
arena.past_present_players_inside[p_name] = nil
players_in_game[p_name] = nil
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,8 +1632,10 @@ 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 channel == "players" then
if teamID then
if except_teamID then
for pl_name, pl_stats in pairs(arena.players) do
@ -1548,6 +1655,12 @@ function arena_lib.send_message_players_in_arena(arena, msg, teamID, except_team
minetest.chat_send_player(pl_name, msg)
end
end
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,6 +2306,16 @@ function operations_before_leaving_arena(mod_ref, arena, p_name)
end
end
-- 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
-- se ha partecipato come giocatore
if arena.past_present_players_inside[p_name] then
-- resetto eventuali texture
if arena.teams_enabled and mod_ref.teams_color_overlay then
player:set_properties({
@ -2167,21 +2323,6 @@ function operations_before_leaving_arena(mod_ref, arena, p_name)
})
end
-- reimposto eventuale hotbar
if mod_ref.hotbar then
local hotbar = mod_ref.hotbar
if hotbar.slots then
player:hud_set_hotbar_itemcount(players_temp_storage[p_name].hotbar_slots)
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)
@ -2191,6 +2332,7 @@ function operations_before_leaving_arena(mod_ref, arena, p_name)
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
player:set_hp(minetest.PLAYER_MAX_HP_DEFAULT)
@ -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

View File

@ -5,6 +5,9 @@ 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_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
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)
@ -16,6 +19,7 @@ minetest.register_on_chat_message(function(p_name, message)
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
else
for _, pl_stats in pairs(minetest.get_connected_players()) do
local pl_name = pl_stats:get_player_name()

View File

@ -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
})
----------------------------------------------

View File

@ -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
----------------------------------------------

View File

@ -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")

View File

@ -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

View File

@ -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

View File

@ -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=

View File

@ -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)

View File

@ -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;<style size=20 font=mono color=#5a5353>" .. FS(arena.name) .. "</style>]",
"hypertext[2.4,2.15;4,1;name;<style size=20 font=mono color=#5a5353>" .. FS(arena.author) .. "</style>]",
"hypertext[2.4,3.15;4,1;name;<global valign=middle><style size=20 font=mono color=#5a5353>" .. FS(bgm_info) .. "</style>]",
}
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)

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 B