Add team assignment callback also for people joining an ongoing match (and rename the one when queuing)

This commit is contained in:
marco_a 2024-03-05 00:06:33 +01:00
parent f1b7c025b1
commit c058b3a544
4 changed files with 35 additions and 12 deletions

13
DOCS.md
View File

@ -203,7 +203,6 @@ All callbacks are called after the normal operations, unless explicitly stated (
* `arena_lib.on_prejoin_queue(mod, function(arena, p_name)`: run more checks when entering a queue. Must return `true` or the player won't be added
* `arena_lib.on_join_queue(mod, function(arena, p_name)`: additional actions to perform after a player has successfully joined a queue
* `arena_lib.on_leave_queue(mod, function(arena, p_name)`: same as above, but for when they leave
* `arena_lib.on_assign_teams(mod, function(arena, max_current_amount)`: customise team assignment (e.g. to balance teams). See [2.4.1 Teams assignment](#241-teams-assignment) to understand how it works. It runs right before the loading phase
* `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, winners)`: same as above. `winners` can be either a string, an integer or a table of string/integers. If you want to have a single winner, return their name (string). If you want to have a whole team, return the team ID (integer). If you want to have more single winners, a table of strings, and for more teams, a table of integers.
@ -216,6 +215,8 @@ All callbacks are called after the normal operations, unless explicitly stated (
* Due to current MT limitations, calling `set_pos()` on a player is not possible, unless it's done after at least 0.2 seconds (see [here](https://github.com/minetest/minetest/issues/12092))
* `as_spectator` returns true if they join as a spectator. If entering as a spectator, `on_join` is executed before entering in spectate mode (the order is join -> spectate). This means that the player as a spectator still doesn't exist
* `was_spectator` returns true if the user was spectating `arena` when joining as an actual player.
* `arena_lib.on_generate_teams(mod, function(arena, max_current_amount)`: customise team assignment right before the loading of an arena (e.g. to balance teams). See [2.4.1 Teams assignment](#241-teams-assignment) to understand how it works
* `arena_lib.on_assign_team(mod, function(arena, p_name)`: customise team assignment for `p_name` and their potential party when entering an ongoing match. See [2.4.1 Teams assignment](#241-teams-assignment) to understand how it works
* `arena_lib.on_death(mod, function(arena, p_name, reason))`: called when a player dies
* `arena_lib.on_respawn(mod, function(arena, p_name))`: called when a player respawns
* `arena_lib.on_change_spectated_target(mod, function(arena, sp_name, t_type, t_name, prev_type, prev_spectated, is_forced))`: called when a spectator (`sp_name`) changes who or what they're spectating, including when they get assigned someone to spectate at entering the arena. `is_forced` returns true when the change is a result of `spectate_target(..)`
@ -691,19 +692,21 @@ By default, teams are not enabled. They can be activated globally by declaring t
It's possible to retrieve the team a player's in by accessing the built-in `teamID` player property. Teams are determined right before launching the loading phase, meaning that retrieving `teamID` on a player that is queuing will return `nil`. The only exception is people in parties (it requires `parties` mod), since arena_lib needs to know what slots of what teams are already taken. This is done both to check if there's enough space left for other parties and to know how to equally distribute solo-queuing players.
#### 2.4.1 Teams assignment
Even if players distribution when joining an ongoing game is pretty straightforward (fill the team with the least players), queues are more elaborated.
Default players distribution when joining an ongoing game is pretty straightforward (fill the team with the least players), but queues are more elaborated.
In queues, teams are assigned with the following logic:
* when joining the queue (parties only): starting from `teamID` 1, put the party in the team with the least amount of players. In case of a game with 3+ teams, where all teams are already featuring a party, check if the joining party has more players of the team with the second least players. If it does, merge the players of the two teams with the least players into the same team and put the joining party into a team on its own. E.g. 2, 3, 5 ..4.. => 4, 5 (2+3), 5 (and not 6 (2+4), 3, 5)
* right before the loading phase: if there are no parties queuing, evenly distribute players in teams. If there are parties, distribute players until every team has the same amount; then, evenly distribute the remaining players, if any
* right before the loading phase: if there are no parties queuing, evenly distribute players in teams. If there are, distribute players until every team has the same amount; then, evenly distribute the remaining players, if any
If you're not happy of how people are split into teams right before the loading phase (e.g. a minigame with an ELO system), you can override the distribution by writing your own assigning system. This is done through the `on_assign_teams` callback. Its structure is `on_assign_teams(arena, max_curr_amount)`, where `max_curr_amount` is the amount of players of the team that currently features the most (so, people in parties).
If you're not happy of how people are split into teams (e.g. a minigame with an ELO system), you can override the distribution by writing your own assigning system. This is done through the `on_generate_teams` (for queues) and `on_assign_team` (for ongoing games) callbacks.
The former is `on_generate_teams(arena, max_curr_amount)`, where `max_curr_amount` is the amount of players of the team that currently features the most (so, people in parties).
In order to work, the following conditions must be satisfied at the end of the callback:
* no empty teams
* every player has a `teamID` assigned (`arena.players[p_name].teamID`)
* `players_amount_per_team` is equivalent to the amount of players of each team (you could increase it every time you assign a team)
* (not mandatory, yet nice) avoid splitting people in parties into different teams, as it'd make parties basically useless
Also, it's nice not to split people in parties into different teams, as it makes the party feature basically useless.
On the contrary, `on_assign_team(arena, p_name)` is only applied to `p_name` (and their potential party) and it must return the team ID that will be assigned to them. Contrary to `on_generate_teams`, modders mustn't touch anything: no internal `teamID` assignment nor `players_amount_per_team` increment; just return the desired teamID and arena_lib will do the rest.
### 2.5 Spectate mode

View File

@ -346,8 +346,8 @@ function arena_lib.queue_to_arena(mod, arena)
local max_curr_amount = get_max_curr_amount(arena.players_amount_per_team)
-- eventuale distribuzione squadre personalizzata
if mod_ref.on_assign_teams then
mod_ref.on_assign_teams(arena)
if mod_ref.on_generate_teams then
mod_ref.on_generate_teams(arena, max_curr_amount)
-- sennò quella normale
else

View File

@ -96,8 +96,8 @@ end
function arena_lib.on_assign_teams(mod, func)
arena_lib.mods[mod].on_assign_teams = func
function arena_lib.on_generate_teams(mod, func)
arena_lib.mods[mod].on_generate_teams = func
end
@ -120,6 +120,14 @@ function arena_lib.on_prejoin(mod, func)
arena_lib.mods[mod].on_prejoin = func
end
function arena_lib.on_assign_team(mod, func)
arena_lib.mods[mod].on_assign_team = func
end
function arena_lib.on_join(mod, func)
arena_lib.mods[mod].on_join = func
end

View File

@ -107,6 +107,17 @@ end
----------------------------------------------
function assign_team(arena, p_name)
-- se ha assegnazione on_join personalizzata
if arena.in_game then
local mod = arena_lib.get_mod_by_matchID(arena.matchID)
local mod_ref = arena_lib.mods[mod]
if mod_ref.on_assign_team then
return mod_ref.on_assign_team(arena, p_name)
end
end
-- sennò normale
local sorted_teams = {} -- {ID, amount}
for teamID, amount in pairs(arena.players_amount_per_team) do
@ -122,9 +133,10 @@ function assign_team(arena, p_name)
end
end)
-- se è un gruppo e ci stanno già gruppi in tutte le squadre, li distribuisco il
-- più equamente possibile: se la 2° squadra ha meno gente della squadra entrante,
-- somma 1° con 2° e metti la entrante come 1° (es. 2, 3, 5 | 4 => 4, 2 + 3, 3)
-- se non è in partita, è un gruppo e ci stanno già gruppi in tutte le squadre,
-- li distribuisco il più equamente possibile: se la 2° squadra ha meno gente
-- della squadra entrante, somma 1° con 2° e metti la entrante come 1°
-- (es. 2, 3, 5 | 4 => 4, 2 + 3, 3)
if not arena.in_game and sorted_teams[1].amount > 0 then
if sorted_teams[2].amount < #parties.get_party_members(p_name, true) then
for pl_name, pl_data in pairs(arena.players) do