diff --git a/DOCS.md b/DOCS.md index 047bb9d..01968c1 100644 --- a/DOCS.md +++ b/DOCS.md @@ -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.
diff --git a/locale/arena_lib.de.tr b/locale/arena_lib.de.tr index 1b39141..35210fc 100644 --- a/locale/arena_lib.de.tr +++ b/locale/arena_lib.de.tr @@ -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! diff --git a/locale/arena_lib.es.tr b/locale/arena_lib.es.tr index 1b34f10..14bb290 100644 --- a/locale/arena_lib.es.tr +++ b/locale/arena_lib.es.tr @@ -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! diff --git a/locale/arena_lib.hu.tr b/locale/arena_lib.hu.tr index 52f0c50..f74bbae 100644 --- a/locale/arena_lib.hu.tr +++ b/locale/arena_lib.hu.tr @@ -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! diff --git a/locale/arena_lib.it.tr b/locale/arena_lib.it.tr index a013249..248185d 100644 --- a/locale/arena_lib.it.tr +++ b/locale/arena_lib.it.tr @@ -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! diff --git a/locale/arena_lib.pl.tr b/locale/arena_lib.pl.tr index a8fe880..a94ff7f 100644 --- a/locale/arena_lib.pl.tr +++ b/locale/arena_lib.pl.tr @@ -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! diff --git a/locale/arena_lib.ru.tr b/locale/arena_lib.ru.tr index a83cb6f..2b5f9d3 100644 --- a/locale/arena_lib.ru.tr +++ b/locale/arena_lib.ru.tr @@ -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!=Эта мини-игра не позволяет входить во время игры! diff --git a/locale/template.txt b/locale/template.txt index afab5d0..4b49614 100644 --- a/locale/template.txt +++ b/locale/template.txt @@ -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!= diff --git a/src/api/core.lua b/src/api/core.lua index b7f6a8e..fe90a29 100644 --- a/src/api/core.lua +++ b/src/api/core.lua @@ -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 diff --git a/src/api/in_game.lua b/src/api/in_game.lua index 704c7d0..104b5b0 100644 --- a/src/api/in_game.lua +++ b/src/api/in_game.lua @@ -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 diff --git a/src/deps/parties.lua b/src/deps/parties.lua index 024c286..8d99ae8 100644 --- a/src/deps/parties.lua +++ b/src/deps/parties.lua @@ -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 \ No newline at end of file diff --git a/src/signs/signs.lua b/src/signs/signs.lua index 9288142..0d4fb70 100644 --- a/src/signs/signs.lua +++ b/src/signs/signs.lua @@ -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 diff --git a/src/spectate/spectate_main.lua b/src/spectate/spectate_main.lua index a774f19..6b6dedf 100644 --- a/src/spectate/spectate_main.lua +++ b/src/spectate/spectate_main.lua @@ -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) diff --git a/src/spectate/spectate_tools.lua b/src/spectate/spectate_tools.lua index 187bc20..3b2a8d0 100644 --- a/src/spectate/spectate_tools.lua +++ b/src/spectate/spectate_tools.lua @@ -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"), diff --git a/textures/arenalib_gui_hotbar1.png b/textures/arenalib_gui_hotbar1.png new file mode 100644 index 0000000..ab18f78 Binary files /dev/null and b/textures/arenalib_gui_hotbar1.png differ diff --git a/textures/arenalib_spectate_changeplayerparty.png b/textures/arenalib_spectate_changeplayerparty.png new file mode 100644 index 0000000..be059ef Binary files /dev/null and b/textures/arenalib_spectate_changeplayerparty.png differ