* Spectate: add "blind" mode

* Spectate: add item to rotate between party members
* Follow party members first when getting eliminated and joining spectate mode
* Add utils `get_party_members_playing(..)` and `get_party_members_spectating(..)`
This commit is contained in:
Zughy 2024-07-19 17:20:03 +00:00
parent a1394e7499
commit 2291ec8e3b
16 changed files with 339 additions and 89 deletions

17
DOCS.md
View File

@ -132,7 +132,7 @@ The second field, on the contrary, is a table of optional parameters: they defin
* `end_when_too_few`: (bool) whether the minigame should end its matches when only one player/team is left. Default il `true`
* `eliminate_on_death`: (bool) whether players are automatically eliminated when dying (both `on_death` and `on_eliminate` callbacks are called, in this order). Default is `false`
* `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`
* `spectate_mode`: (string/bool) whether the minigame features the spectator mode. It can be `"all"`, `"blind"` or `false`. Default is `"all"`. See [2.5 Spectate mode](#25-spectate-mode) for further information
* `regenerate_map`: (bool) whether to regenerate the maps of the minigame between a game and another. Default is `false`. Be sure to check [Map regeneration](#2231-map-regeneration) first
* `can_build`: (bool) whether players are allowed to place or dig nodes during a match. Default is `false`
* `can_drop`: (bool) whether players can drop items during a match. Default is `true`
@ -435,6 +435,10 @@ There are also some other functions which might turn useful. They are:
* `arena_lib.get_spectatable_areas(mod, arena_name)`: same as in `get_spectatable_entities(..)` but for areas. Entities returned in the table are the dummy ObjectRef entities put at the area coordinates
* `arena_lib.get_player_in_edit_mode(arena_name)`: returns the name of the player who's editing `arena_name`, if any
Requiring `parties`:
* `arena_lib.get_party_members_playing(p_name, arena)`: returns a table containing as value the names of all the party members of `p_name` currently playing in `arena`
* `arena_lib.get_party_members_spectating(p_name, arena)`: returns a table containing as value the names of all the party members of `p_name` currently spectating in `arena`
### 1.10 Endless minigames
As the name suggests, endless minigames have got no end. When the server starts, all the enabled arenas of an endless minigame are automatically loaded and the only way to stop them is to disable them (e.g. by entering the editor). Calling the end of an arena will also try to disable the arena (i.e. through `/arenas forceend` or the respective function `force_end`); in case the disabling process should fail, the arena will be automatically launched again.
@ -697,7 +701,7 @@ An arena comes in 4 phases:
* `celebration phase`: the after-match. By default people stroll around for the arena knowing who won, waiting to be teleported. Relevant function: `arena_lib.load_celebration(..)`. Relevant callbacks: `on_celebration`, `on_end`.
### 2.3.1 Handling player properties (saving and restoring)
When a player joins a match, they're automatically detached from any entity they're attached to and their life fulled. This is also true when they leave a match. Additionally, when they leave their physics is set to the setting `arena_lib.SERVER_PHYSICS`, unless `hub` is installed.
When a player joins a match, they're automatically detached from any entity they're attached to and their life fulled. This is also true when they leave a match. Additionally, when they leave their physics is set to the setting `arena_lib.SERVER_PHYSICS`, unless `hub` is installed.
arena_lib stores several player's values before teleporting them inside the arena, that are automatically restored when the player leaves it. Some of these values are saved only if the arena explicitly forces a property (e.g. celestial vault, lighting, aspect), where as some others are always saved. The values that are always saved (and restored) are:
* nametag
@ -727,8 +731,13 @@ On the contrary, `on_assign_team(arena, p_name)` is only applied to `p_name` (an
### 2.5 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), whereas the other is through the very entrance of the arena (if implemented). 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.
By default, spectate mode allows to follow players, but it also allows modders to expand it to entities and areas. To do that, have a look at `arena_lib.add_spectate_entity(..)` and `arena_lib.add_spectate_area(..)`
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), whereas the other is through the very entrance of the arena (if implemented). While in this state, spectators are invisible and 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, players can only read players' chat.
Spectate mode comes in two variants: `"all"` and `"blind"`.
* `"all"` allows to rotate between all the players in a match, and it also allows modders to expand the spectatable targets to entities and areas. To do that, have a look at `arena_lib.add_spectate_entity(..)` and `arena_lib.add_spectate_area(..)`. In this variant, people can join from outside.
* `"blind"`, on the contrary, it doesn't support custom targets and it allows players to rotate only between their teams (if teams are supported) or their party members (if they're in a party). If they're in neither, they'll be stuck with the player assigned once eliminated. In this variant, people cannot join from outside.
No matter the variant, the priority to determine whom to automatically follow when entering spectate mode or when your target disappears is team/party -> executor -> random.
<br>

View File

@ -140,6 +140,7 @@ There is no entrance to remove assigned to @1!=Es gibt keinen an @1 zugewiesenen
##[ src/api/in_game.lua ]##
No ongoing game!=Kein laufendes Spiel!
Spectate mode not supported!=Zuschauermodus nicht unterstützt!
This minigame doesn't allow spectators to join from outside!=
There is nothing to spectate!=Es gibt nichts zum Zuschauen!
The arena is loading, try again in a few seconds!=Die Arena lädt, bitte versuchen Sie es in ein paar Sekunden erneut!
This minigame doesn't allow to join while in progress!=Dieses Minispiel erlaubt keinen Beitritt, während es läuft!

View File

@ -140,6 +140,7 @@ There is no entrance to remove assigned to @1!=
##[ src/api/in_game.lua ]##
No ongoing game!=¡Ninguna partida en curso!
Spectate mode not supported!=¡Modo espectador no admitido!
This minigame doesn't allow spectators to join from outside!=
There is nothing to spectate!=¡No hay nada que asistir!
The arena is loading, try again in a few seconds!=¡La arena está cargando, reinténtalo en unos pocos segundos!
This minigame doesn't allow to join while in progress!=¡Este minijuego no permite entrar cuando la partida ha comenzado!

View File

@ -140,6 +140,7 @@ There is no entrance to remove assigned to @1!=
##[ src/api/in_game.lua ]##
No ongoing game!=Nincs folyamatban kévő menet!
Spectate mode not supported!=Nézői mód nem támogatott!
This minigame doesn't allow spectators to join from outside!=
There is nothing to spectate!=Nincs mit nézni!
The arena is loading, try again in a few seconds!=Az aréna tölt próbáld ujra pár másodperc mulva!
This minigame doesn't allow to join while in progress!=Ez a minigame nem engedi a csatlakozást menet közben!

View File

@ -140,6 +140,7 @@ There is no entrance to remove assigned to @1!=Non c'è nessun'entrata da rimuov
##[ src/api/in_game.lua ]##
No ongoing game!=Nessuna partita in corso!
Spectate mode not supported!=Modalità assisti non supportata!
This minigame doesn't allow spectators to join from outside!=Questo minigioco non permette allɜ spettatorɜ di entrare da fuori!
There is nothing to spectate!=Non c'è niente a cui assistere!
The arena is loading, try again in a few seconds!=L'arena è in caricamento, riprova tra qualche secondo!
This minigame doesn't allow to join while in progress!=Questo minigioco non permette di entrare a partita iniziata!

View File

@ -140,6 +140,7 @@ There is no entrance to remove assigned to @1!=Nie ma żadnego przypisanego do @
##[ src/api/in_game.lua ]##
No ongoing game!=Brak trwającej gry!
Spectate mode not supported!=Tryb obserwatora nie jest wspierany!
This minigame doesn't allow spectators to join from outside!=
There is nothing to spectate!=Nie ma niczego do obserwowania!
The arena is loading, try again in a few seconds!=Arena się ładuje, spróbuj ponownie po kilku sekundach!
This minigame doesn't allow to join while in progress!=Nie można dołączyć do tej minigry, kiedy jest w toku!

View File

@ -140,6 +140,7 @@ There is no entrance to remove assigned to @1!=
##[ src/api/in_game.lua ]##
No ongoing game!=Игра не запущена!
Spectate mode not supported!=Режим наблюдения не поддерживается!
This minigame doesn't allow spectators to join from outside!=
There is nothing to spectate!=Тут незачем наблюдать!
The arena is loading, try again in a few seconds!=Арена загружается, попробуйте через несколько секунд!
This minigame doesn't allow to join while in progress!=Эта мини-игра не позволяет входить во время игры!

View File

@ -140,6 +140,7 @@ There is no entrance to remove assigned to @1!=
##[ src/api/in_game.lua ]##
No ongoing game!=
Spectate mode not supported!=
This minigame doesn't allow spectators to join from outside!=
There is nothing to spectate!=
The arena is loading, try again in a few seconds!=
This minigame doesn't allow to join while in progress!=

View File

@ -138,6 +138,11 @@ function arena_lib.register_minigame(mod, def)
def.chat_settings.color_spectate = def.chat_spectate_color
minetest.log("warning", "[ARENA_LIB] (" .. mod .. ") chat_spectate_color is deprecated. Use chat_settings = {color_spectate = \"some color\"} instead")
end
if def.spectate_mode == true then
def.spectate_mode = "all"
minetest.log("warning", "[ARENA_LIB] (" .. mod .. ") spectate_mode = true is deprecated. Use spectate_mode = \"all\" instead")
end
--^------------------ LEGACY UPDATE, to remove in 9.0 -------------------^
arena_lib.mods[mod] = {}
@ -197,7 +202,7 @@ function arena_lib.register_minigame(mod, def)
mod_ref.end_when_too_few = true
mod_ref.eliminate_on_death = false
mod_ref.join_while_in_progress = false
mod_ref.spectate_mode = true
mod_ref.spectate_mode = "all"
mod_ref.regenerate_map = false
mod_ref.can_build = false
mod_ref.can_drop = true
@ -365,8 +370,16 @@ function arena_lib.register_minigame(mod, def)
mod_ref.join_while_in_progress = true
end
if def.spectate_mode == false then
mod_ref.spectate_mode = false
if def.spectate_mode then
local spectate_mode = def.spectate_mode
if spectate_mode == "blind" or spectate_mode == false then
if spectate_mode == "blind" then
assert(not mod_ref.endless, "[ARENA_LIB] Endless mode can't support \"blind\" spectate mode!")
assert(not mod_ref.join_while_in_progress, "[ARENA_LIB] Join while in progress can't be `true` in \"blind\" spectate mode!")
end
mod_ref.spectate_mode = spectate_mode
end
end
if def.regenerate_map == true then

View File

@ -20,6 +20,8 @@ local players_temp_storage = {} -- KEY: p_name, VALUE: {(int) hotbar_sl
-- (table) player_aspect, (int) fov, (table) camera_offset, (table) armor_groups, (string) inventory_fs,
-- (table) attachments, (table) nametag, (table) celvault_sky, (table) celvault_sun, (table) celvault_moon,
-- (table) celvault_stars, (table) celvault_clouds, (int) weather_ID}
local is_parties_enabled = minetest.get_modpath("parties")
function arena_lib.load_arena(mod, arena_ID)
@ -165,10 +167,14 @@ function arena_lib.join_arena(mod, p_name, arena_ID, as_spectator)
-- se prova a entrare come spettante
if as_spectator then
-- se non supporta la spettatore
if not arena_lib.mods[mod].spectate_mode then
if mod_ref.spectate_mode == false then
arena_lib.print_error(p_name, S("Spectate mode not supported!"))
return end
if mod_ref.spectate_mode == "blind" then
arena_lib.print_error(p_name, S("This minigame doesn't allow spectators to join from outside!"))
return end
-- se l'arena non è abilitata
if not arena.enabled then
arena_lib.print_error(p_name, S("The arena is not enabled!"))
@ -490,9 +496,9 @@ function arena_lib.remove_player_from_arena(p_name, reason, xc_name, elim_msg)
-- reason 1 = has been eliminated
-- reason 2 = has been kicked
-- reason 3 = has quit the arena
assert(reason, "[ARENA_LIB] 'remove_player_from_arena': A reason must be specified!")
assert(reason, "[ARENA_LIB] 'remove_player_from_arena': a reason must be specified!")
-- se lə giocatorə non è in partita, annullo
-- se lə giocante non è in partita, annullo
if not arena_lib.is_player_in_arena(p_name) then return end
local mod = arena_lib.get_mod_by_player(p_name)
@ -500,7 +506,7 @@ function arena_lib.remove_player_from_arena(p_name, reason, xc_name, elim_msg)
local arena = arena_lib.get_arena_by_player(p_name)
local p_properties -- copia da passare ai richiami
-- se lə giocatorə era in spettatore
-- se lə giocante era in spettatore
if mod_ref.spectate_mode and arena_lib.is_player_spectating(p_name) then
p_properties = table.copy(arena.spectators[p_name])
arena_lib.leave_spectate_mode(p_name)
@ -518,21 +524,73 @@ function arena_lib.remove_player_from_arena(p_name, reason, xc_name, elim_msg)
p_properties = table.copy(arena.players[p_name])
local p_team_ID = arena.players[p_name].teamID
-- rimuovo
arena.players_amount = arena.players_amount - 1
if arena.teams_enabled then
local p_team_ID = arena.players[p_name].teamID
if p_team_ID then
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
-- se ha abbandonato mentre aveva dellɜ spettanti, lɜ riassegno
if arena_lib.is_player_spectated(p_name) then
for sp_name, _ in pairs(arena_lib.get_player_spectators(p_name)) do
if reason == 1 and xc_name and minetest.get_player_by_name(xc_name) and sp_name ~= xc_name then -- sp_name ~= xc_name in caso si uccidano più o meno nello stesso istante
arena_lib.spectate_target(mod, arena, sp_name, "player", xc_name)
else
arena_lib.find_and_spectate_player(sp_name)
-- se è "blind", ci son squadre e la sua squadra == 0, prendi tutta la gente e mettila su altra squadra
if mod_ref.spectate_mode == "blind" and arena.teams_enabled and arena.players_amount_per_team[p_team_ID] == 0 then
local target_name = xc_name
-- se è mortə da solə, cerca una squadra a caso
if not target_name or reason ~= 1 then
for team_id, amnt in ipairs(arena.players_amount_per_team) do
if amnt > 0 then
for pl_name, pl_data in pairs(arena.players) do
if pl_data.teamID == team_id then
target_name = pl_name
break
end
end
break
end
end
end
for sp_name, _ in pairs(arena_lib.get_player_spectators(p_name)) do
arena_lib.spectate_target(mod, arena, sp_name, "player", target_name)
end
-- sennò riassegna normalmente
else
-- aggiornamenti vari barra delle azioni per spettanti che non vengono spostatɜ
-- TODO: togli ruota giocante se a squadre e squadra p_name ora ha solo 1 membro rimasto
-- se era in gruppo, controlla se va aggiornata la barra delle azioni
if is_parties_enabled and parties.is_player_in_party(p_name) then
for _, spp_name in ipairs(arena_lib.get_party_members_spectating(p_name, arena)) do
if spp_name ~= p_name then
arena_lib.update_spec_hotbar_party(mod, arena, spp_name)
end
end
end
-- calcolo se spettanti p_name erano in gruppo e se c'è un membro del gruppo da seguire
for sp_name, _ in pairs(arena_lib.get_player_spectators(p_name)) do
local party_member_in_game
if is_parties_enabled and parties.is_player_in_party(sp_name) then
local pt_members = arena_lib.get_party_members_playing(sp_name, arena)
if next(pt_members) then
party_member_in_game = pt_members[1]
end
end
-- priorità: membro gruppo -> esecutre -> casuale
if party_member_in_game then
arena_lib.spectate_target(mod, arena, sp_name, "player", party_member_in_game)
elseif reason == 1 and xc_name and arena_lib.is_player_playing(xc_name, mod) and sp_name ~= xc_name then -- sp_name ~= xc_name in caso si uccidano nello stesso istante
arena_lib.spectate_target(mod, arena, sp_name, "player", xc_name)
else
arena_lib.find_and_spectate_player(sp_name)
end
end
end
end
@ -540,7 +598,7 @@ function arena_lib.remove_player_from_arena(p_name, reason, xc_name, elim_msg)
-- se è stato eliminato e c'è la spettatore, non va rimosso, bensì solo spostato in spettatore
if reason == 1 and mod_ref.spectate_mode and arena.players_amount > 0 then
eliminate_player(mod, arena, p_name, xc_name, p_properties, elim_msg)
arena_lib.enter_spectate_mode(p_name, arena, xc_name)
arena_lib.enter_spectate_mode(p_name, arena, p_team_ID, xc_name)
-- sennò procedo a rimuoverlo normalmente
else

View File

@ -44,3 +44,29 @@ parties.register_on_pre_party_join(function(party_leader, p_name)
return true
end)
function arena_lib.get_party_members_playing(p_name, arena)
local pt_members = {}
for _, pl_name in ipairs(parties.get_party_members(p_name, true)) do
if arena.players[pl_name] then
pt_members[#pt_members +1] = pl_name
end
end
return pt_members
end
function arena_lib.get_party_members_spectating(p_name, arena)
local pt_members = {}
for _, pl_name in ipairs(parties.get_party_members(p_name, true)) do
if arena.spectators[pl_name] then
pt_members[#pt_members +1] = pl_name
end
end
return pt_members
end

View File

@ -335,7 +335,7 @@ function get_infobox_formspec(mod, arenaID, player)
play_tip = NS("Play (you can also left-click the sign)")
end
-- tasto "assisti"
if not mod_ref.spectate_mode then
if mod_ref.spectate_mode ~= "all" then
spec_btn = "arenalib_infobox_spectate_off.png"
spec_tip = NS("Spectate mode not supported")
elseif arena.in_game then

View File

@ -1,5 +1,8 @@
local S = minetest.get_translator("arena_lib")
local function follow_executioner_or_random() end
local function calc_p_amount_teams() end
local function calc_spec_id() end
local function override_hotbar() end
local function set_spectator() end
@ -10,6 +13,7 @@ local entities_spectated = {} -- KEY: [mod][arena_name][entity nam
local areas_spectated = {} -- KEY: [mod][arena_name][area_name], VALUE: {(string) spectator(s) = true}
local entities_storage = {} -- KEY: [mod][arena_name][entity_name], VALUE: entity
local areas_storage = {} -- KEY: [mod][arena_name][area_name], VALUE: dummy entity
local is_parties_enabled = minetest.get_modpath("parties")
@ -74,20 +78,21 @@ end
-- entering / leaving
----------------------------
function arena_lib.enter_spectate_mode(p_name, arena, xc_name)
function arena_lib.enter_spectate_mode(p_name, arena, p_team_ID, xc_name)
local mod = arena_lib.get_mod_by_player(p_name)
local arena_ID = arena_lib.get_arenaID_by_player(p_name)
local team_ID = #arena.teams > 1 and 1 or nil
local player = minetest.get_player_by_name(p_name)
local hand = player:get_inventory():get_list("hand")
players_in_spectate_mode[p_name] = { minigame = mod, arenaID = arena_ID, teamID = team_ID, hand = hand}
players_in_spectate_mode[p_name] = { minigame = mod, arenaID = arena_ID, teamID = p_team_ID, hand = hand}
arena.spectators[p_name] = {}
arena.players_and_spectators[p_name] = true
arena.spectators_amount = arena.spectators_amount + 1
local mod_ref = arena_lib.mods[mod]
-- eventuali proprietà aggiuntive
for k, v in pairs(arena_lib.mods[mod].spectator_properties) do
for k, v in pairs(mod_ref.spectator_properties) do
if type(v) == "table" then
arena.spectators[p_name][k] = table.copy(v)
else
@ -140,10 +145,30 @@ function arena_lib.enter_spectate_mode(p_name, arena, xc_name)
minetest.chat_send_player(p_name, minetest.colorize("#cfc6b8", S("Spectators inside: @1", curr_spectators)))
-- inizia a seguire
if xc_name and p_name ~= xc_name and minetest.get_player_by_name(xc_name) then
arena_lib.spectate_target(mod, arena, p_name, "player", xc_name)
if mod_ref.spectate_mode == "blind" then
local found = false
if p_team_ID and arena.players_amount_per_team[p_team_ID] > 0 then
for _, pl_name in pairs(arena_lib.get_players_in_team(arena, p_team_ID)) do
arena_lib.spectate_target(mod, arena, p_name, "player", pl_name)
found = true
break
end
elseif is_parties_enabled and parties.is_player_in_party(p_name) then
local pt_members = arena_lib.get_party_members_playing(p_name, arena)
if #pt_members > 0 then
arena_lib.spectate_target(mod, arena, p_name, "player", pt_members[1])
found = true
end
end
if not found then
follow_executioner_or_random(mod, arena, p_name, "player", xc_name)
end
else
arena_lib.find_and_spectate_player(p_name)
follow_executioner_or_random(mod, arena, p_name, "player", xc_name)
end
override_hotbar(player, mod, arena)
@ -209,21 +234,28 @@ end
-- find next spectatable target
----------------------------
function arena_lib.find_and_spectate_player(sp_name, change_team, go_counterwise)
function arena_lib.find_and_spectate_player(sp_name, change_team, go_counterwise, rotate_in_party)
local arena = arena_lib.get_arena_by_player(sp_name)
-- se l'ultimo rimasto ha abbandonato (es. alt+f4), rispedisco subito fuori senza che cada all'infinito con rischio di crash
-- se l'ultimə rimastə 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
local prev_spectated = players_in_spectate_mode[sp_name].spectating
-- se c'è rimasto solo un giocatore e già lo si seguiva, annullo
-- se c'è rimastə solo unə giocante e già lə si seguiva, annullo
if arena.players_amount == 1 and prev_spectated and arena.players[prev_spectated] then return end
local mod = arena_lib.get_mod_by_player(sp_name)
local spectator = minetest.get_player_by_name(sp_name)
-- aggiornamenti vari della barra delle azioni
-- TODO: aggiornare icona persona singola quando squadre e 1vN
if is_parties_enabled and parties.is_player_in_party(sp_name) then
arena_lib.update_spec_hotbar_party(mod, arena, sp_name)
end
if players_in_spectate_mode[sp_name].type ~= "player" then
spectator:get_meta():set_int("arenalib_watchID", 0)
end
@ -231,57 +263,23 @@ function arena_lib.find_and_spectate_player(sp_name, change_team, go_counterwise
local team_ID = players_in_spectate_mode[sp_name].teamID
local players_amount
-- calcolo giocatori massimi tra cui ruotare
-- calcolo giocanti massimɜ tra cui ruotare
-- squadre:
if #arena.teams > 1 then
-- se è l'unico rimasto nella squadra e già lo si seguiva, annullo
if arena.players_amount_per_team[team_ID] == 1 and not change_team and prev_spectated and arena.players[prev_spectated] then return end
-- 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 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[#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]
if arena.teams_enabled and not rotate_in_party then
players_amount = calc_p_amount_teams(arena, sp_name, team_ID, change_team, prev_spectated)
elseif rotate_in_party then
players_amount = #arena_lib.get_party_members_playing(sp_name, arena)
-- no squadre:
else
players_amount = arena.players_amount
end
local mod = arena_lib.get_mod_by_player(sp_name)
local current_ID = spectator:get_meta():get_int("arenalib_watchID")
local new_ID
local curr_ID = spectator:get_meta():get_int("arenalib_watchID")
local new_ID = calc_spec_id(curr_ID, players_amount, go_counterwise)
if go_counterwise then
new_ID = current_ID == 1 and players_amount or current_ID - 1
else
new_ID = players_amount <= current_ID and 1 or current_ID + 1
end
-- trovo il giocatore da seguire
-- trovo giocante da seguire
-- squadre:
if #arena.teams > 1 then
if arena.teams_enabled then
local players_team = arena_lib.get_players_in_team(arena, team_ID)
for i = 1, #players_team do
@ -291,11 +289,39 @@ function arena_lib.find_and_spectate_player(sp_name, change_team, go_counterwise
end
end
-- no squadre:
-- gruppo:
elseif rotate_in_party then
local pt_members = arena_lib.get_party_members_playing(sp_name, arena)
for i, pl_name in ipairs(pt_members) do
if i == new_ID then
-- evita che alternando tra singolo e gruppo riselezioni la stessa persona
if prev_spectated and prev_spectated == pl_name then
i = calc_spec_id(new_ID, players_amount, go_counterwise)
pl_name = pt_members[i]
end
set_spectator(mod, arena.name, spectator, "player", pl_name, i)
return true
end
end
-- singolo:
else
local i = 1
for pl_name, _ in pairs(arena.players) do
if i == new_ID then
-- evita che alternando tra singolo e gruppo riselezioni la stessa persona
if prev_spectated and prev_spectated == pl_name then
new_ID = calc_spec_id(new_ID, players_amount, go_counterwise)
i = 1
for pla_name, _ in pairs(arena.players) do
if i == new_ID then
set_spectator(mod, arena.name, spectator, "player", pla_name, i)
return true
end
i = i +1
end
end
set_spectator(mod, arena.name, spectator, "player", pl_name, i)
return true
end
@ -513,7 +539,7 @@ function arena_lib.spectate_target(mod, arena, sp_name, type, t_name)
end
-- sì, potrei richiedere direttamente 'spectator', ma per coesione con il resto dell'API e con il fatto che
-- arena_lib salva lɜ spettatorɜ indicizzandolɜ per nome, tanto vale una conversione in più qui
-- arena_lib salva lɜ spettanti indicizzandolɜ per nome, tanto vale una conversione in più qui
local spectator = minetest.get_player_by_name(sp_name)
local i = spectator:get_meta():get_int("arenalib_watchID") -- non c'è bisogno di calcolare l'ID, riapplico quello che già ha
set_spectator(mod, arena.name, spectator, type, t_name, i, true)
@ -521,6 +547,20 @@ end
function arena_lib.update_spec_hotbar_party(mod, arena, sp_name)
local pt_members_amnt = #arena_lib.get_party_members_playing(sp_name, arena)
local spectator = minetest.get_player_by_name(sp_name)
local spectate_mode = arena_lib.mods[mod].spectate_mode
if ((spectate_mode == "blind" and pt_members_amnt <= 1) or
(spectate_mode ~= "blind" and pt_members_amnt == 0)) and
spectator:get_inventory():contains_item("main", "arena_lib:spectate_changeplayerparty") then
override_hotbar(spectator, mod, arena)
end
end
----------------------------------------------
@ -600,6 +640,62 @@ end
---------------FUNZIONI LOCALI----------------
----------------------------------------------
function follow_executioner_or_random(mod, arena, p_name, xc_name)
if xc_name and arena.players[xc_name] then
arena_lib.spectate_target(mod, arena, p_name, "player", xc_name)
else
arena_lib.find_and_spectate_player(p_name)
end
end
-- viene chiamata solo una volta ma a tenerla nella funzione principale rendeva il
-- codice pressoché illeggibile. Da qui la funzione locale a parte
function calc_p_amount_teams(arena, sp_name, team_ID, change_team, prev_spectated)
-- se è l'unico rimasto nella squadra e già lo si seguiva, annullo
if arena.players_amount_per_team[team_ID] == 1 and not change_team and prev_spectated and arena.players[prev_spectated] then return end
-- 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 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[#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
return arena.players_amount_per_team[team_ID]
end
function calc_spec_id(curr_ID, players_amount, go_counterwise)
if go_counterwise then
return curr_ID == 1 and players_amount or curr_ID - 1
else
return players_amount <= curr_ID and 1 or curr_ID + 1
end
end
function set_spectator(mod, arena_name, spectator, type, name, i, is_forced)
local sp_name = spectator:get_player_name()
local prev_spectated = players_in_spectate_mode[sp_name].spectating
@ -622,7 +718,7 @@ function set_spectator(mod, arena_name, spectator, type, name, i, is_forced)
players_spectated[name][sp_name] = true
target = minetest.get_player_by_name(name)
spectator:set_attach(target, "", {x=0, y=-5, z=-20}, {x=0, y=0, z=0})
spectator:set_attach(target, "", {x=0, y=-5, z=-20}, {x=0, y=0, z=0}) -- TODO: si può portare fuori
spectator:set_hp(target:get_hp() > 0 and target:get_hp() or 1)
elseif type == "entity" then
@ -661,28 +757,47 @@ function override_hotbar(player, mod, arena)
player:get_inventory():set_list("main", {})
player:get_inventory():set_list("craft",{})
local sp_name = player:get_player_name()
local mod_ref = arena_lib.mods[mod]
local tools = {
"arena_lib:spectate_changeplayer", -- TODO: 6.0, with endless arenas I could have a situation where I have entities/areas to spectate but no players. Still hardcoded?
"arena_lib:spectate_quit"
}
local spectate_mode = mod_ref.spectate_mode
local tools = {}
local pt_members_playing_amnt = 0
if #arena.teams > 1 then
table.insert(tools, 2, "arena_lib:spectate_changeteam")
if is_parties_enabled and parties.is_player_in_party(sp_name) then
pt_members_playing_amnt = #arena_lib.get_party_members_playing(sp_name, arena)
end
if arena.spectate_entities_amount > 0 then
table.insert(tools, #tools, "arena_lib:spectate_changeentity")
if spectate_mode ~= "blind" or arena.teams_enabled then
-- TODO: se è a squadre e c'è solo una persona nella tal squadra, non metterlo
-- Poi ridallo però se nell'altra ce n'è più di una
tools[1] = "arena_lib:spectate_changeplayer" -- TODO: with endless arenas I could have a situation where I have entities/areas to spectate but no players. Dehardcode it?
end
if arena.spectate_areas_amount > 0 then
table.insert(tools, #tools, "arena_lib:spectate_changearea")
if (spectate_mode == "blind" and pt_members_playing_amnt > 1) or
(spectate_mode ~= "blind" and pt_members_playing_amnt > 0) then
tools[#tools +1] = "arena_lib:spectate_changeplayerparty"
end
if mod_ref.join_while_in_progress then
table.insert(tools, #tools, "arena_lib:spectate_join")
if spectate_mode == "all" then
if #arena.teams > 1 then
tools[#tools +1] = "arena_lib:spectate_changeteam"
end
if arena.spectate_entities_amount > 0 then
tools[#tools +1] = "arena_lib:spectate_changeentity"
end
if arena.spectate_areas_amount > 0 then
tools[#tools +1] = "arena_lib:spectate_changearea"
end
if mod_ref.join_while_in_progress then
tools[#tools +1] = "arena_lib:spectate_join"
end
end
tools[#tools +1] = "arena_lib:spectate_quit"
player:hud_set_hotbar_image("arenalib_gui_hotbar" .. #tools .. ".png")
player:hud_set_hotbar_itemcount(#tools)
player:get_inventory():set_list("main", tools)

View File

@ -24,6 +24,28 @@ minetest.register_tool("arena_lib:spectate_changeplayer", {
minetest.register_tool("arena_lib:spectate_changeplayerparty", {
description = S("Change player"),
inventory_image = "arenalib_spectate_changeplayerparty.png",
groups = {not_in_creative_inventory = 1},
on_drop = function() end,
on_use = function(itemstack, user)
arena_lib.find_and_spectate_player(user:get_player_name(), false, false, true)
end,
on_secondary_use = function(itemstack, user)
arena_lib.find_and_spectate_player(user:get_player_name(), false, true, true)
end,
on_place = function(itemstack, user)
arena_lib.find_and_spectate_player(user:get_player_name(), false, true, true)
end
})
minetest.register_tool("arena_lib:spectate_changeteam", {
description = S("Change team"),

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 B