ctf_pvp_engine/ctf/teams.lua
ANAND 769b278494
Pass tool_capabilities to on_killedplayer callbacks (#38)
Provides a solution to the regression introduced by 1c373b66, which removed passing of hard-coded kill types to on_killedplayer callbacks, and passed the wielditem instead.

The problem with passing wielditem is that throwables can't be detected, as the wielditem would be nil. Grenades used to be detected before the aforementioned commit due to the special `grenade` damage group in the grenade's toolcaps.
2019-07-06 21:27:15 +05:30

517 lines
12 KiB
Lua

-- Get or add a team
function ctf.team(name)
if name == nil then
return nil
end
if type(name) == "table" then
if not name.add_team then
ctf.error("team", "Invalid table given to ctf.team")
return
end
return ctf.create_team(name.name, name)
else
local team = ctf.teams[name]
if team then
if not team.data or not team.players then
ctf.warning("team", "Assertion failed, data{} or players{} not " ..
"found in team{}")
end
return team
else
if name and name:trim() ~= "" then
ctf.warning("team", dump(name) .. " does not exist!")
end
return nil
end
end
end
function ctf.create_team(name, data)
ctf.log("team", "Creating team " .. name)
ctf.teams[name] = {
data = data,
spawn = nil,
players = {}
}
for i = 1, #ctf.registered_on_new_team do
ctf.registered_on_new_team[i](ctf.teams[name])
end
ctf.needs_save = true
return ctf.teams[name]
end
function ctf.remove_team(name)
local team = ctf.team(name)
if team then
for username, player in pairs(team.players) do
player.team = nil
end
for i = 1, #team.flags do
team.flags[i].team = nil
end
ctf.teams[name] = nil
ctf.needs_save = true
return true
else
return false
end
end
function ctf.list_teams(name)
minetest.chat_send_player(name, "Teams:")
for tname, team in pairs(ctf.teams) do
if team and team.players then
local details = ""
local numPlayers = ctf.count_players_in_team(tname)
details = numPlayers .. " members"
if team.flags then
local numFlags = 0
for flagid, flag in pairs(team.flags) do
numFlags = numFlags + 1
end
details = details .. ", " .. numFlags .. " flags"
end
minetest.chat_send_player(name, ">> " .. tname ..
" (" .. details .. ")")
end
end
end
-- Count number of players in a team
function ctf.count_players_in_team(team)
local count = 0
for name, player in pairs(ctf.team(team).players) do
count = count + 1
end
return count
end
function ctf.new_player(name)
if name then
ctf.players[name] = {
name = name
}
ctf.needs_save = true
else
ctf.error("team", "Can't create a blank player")
ctf.log("team", debug.traceback())
end
end
-- get a player
function ctf.player(name)
if not ctf.players[name] then
ctf.new_player(name)
end
return ctf.players[name]
end
function ctf.player_or_nil(name)
return ctf.players[name]
end
function ctf.chat_send_team(team, msg)
if type(team) == "string" then
team = ctf.team(team)
end
for pname, _ in pairs(team.players) do
minetest.chat_send_player(pname, msg)
end
end
function ctf.remove_player(name)
ctf.log("team", "Removing player ".. dump(name))
local player = ctf.players[name]
if player then
local team = ctf.team(player.team)
if team then
team.players[name] = nil
end
ctf.players[name] = nil
ctf.needs_save = true
return true
else
return false
end
end
ctf.registered_on_join_team = {}
function ctf.register_on_join_team(func)
if ctf._mt_loaded then
error("You can't register callbacks at game time!")
end
table.insert(ctf.registered_on_join_team, func)
end
ctf.player_last_team = {}
-- Player joins team
-- Called by /join, /team join or auto allocate.
function ctf.join(name, team, force, by)
if not name or name == "" or not team or team == "" then
ctf.log("team", "Missing parameters to ctf.join")
return false
end
local player = ctf.player(name)
if not force and not ctf.setting("players_can_change_team")
and player.team and ctf.team(player.team) then
if by then
if by == name then
ctf.action("teams", name .. " attempted to change to " .. team)
minetest.chat_send_player(by, "You are not allowed to switch teams, traitor!")
else
ctf.action("teams", by .. " attempted to change " .. name .. " to " .. team)
minetest.chat_send_player(by, "Failed to add " .. name .. " to " .. team ..
" as players_can_change_team = false")
end
else
ctf.log("teams", "failed to add " .. name .. " to " .. team ..
" as players_can_change_team = false")
end
return false
end
local team_data = ctf.team(team)
if not team_data then
if by then
minetest.chat_send_player(by, "No such team.")
ctf.list_teams(by)
if by == name then
minetest.log("action", by .. " tried to move " .. name .. " to " .. team .. ", which doesn't exist")
else
minetest.log("action", name .. " attempted to join " .. team .. ", which doesn't exist")
end
else
ctf.log("teams", "failed to add " .. name .. " to " .. team ..
" as team does not exist")
end
return false
end
if player.team then
local oldteam = ctf.team(player.team)
if oldteam then
oldteam.players[player.name] = nil
end
end
player.team = team
team_data.players[player.name] = player
ctf.player_last_team[name] = team
ctf.needs_save = true
minetest.log("action", name .. " joined team " .. team)
minetest.chat_send_all(name.." has joined team "..team)
for i = 1, #ctf.registered_on_join_team do
ctf.registered_on_join_team[i](name, team)
end
return true
end
-- Cleans up the player lists
function ctf.clean_player_lists()
ctf.log("utils", "Cleaning player lists")
for _, str in pairs(ctf.players) do
if str and str.team and ctf.teams[str.team] then
ctf.log("utils", " - Adding player "..str.name.." to team "..str.team)
ctf.teams[str.team].players[str.name] = str
else
ctf.log("utils", " - Skipping player "..str.name)
end
end
ctf.needs_save = true
end
-- Sees if the player can change stuff in a team
function ctf.can_mod(player,team)
local privs = minetest.get_player_privs(player)
if privs then
if privs.ctf_admin == true then
return true
end
end
if player and ctf.teams[team] and ctf.teams[team].players and ctf.teams[team].players[player] then
if ctf.teams[team].players[player].auth == true then
return true
end
end
return false
end
-- post a message to a team board
function ctf.post(team, msg)
if not ctf.team(team) then
return false
end
if not ctf.team(team).log then
ctf.team(team).log = {}
end
ctf.log("team", "message posted to team board")
table.insert(ctf.team(team).log, 1, msg)
ctf.needs_save = true
return true
end
-- Automatic Allocation
function ctf.autoalloc(name, alloc_mode)
alloc_mode = alloc_mode or ctf.setting("allocate_mode")
if alloc_mode == 0 then
return
end
local last_team = ctf.player_last_team[name]
if last_team then
return last_team
end
local max_players = ctf.setting("maximum_in_team")
local mtot = false -- more than one team
for key, team in pairs(ctf.teams) do
mtot = true
break
end
if not mtot then
ctf.error("autoalloc", "No teams to allocate " .. name .. " to!")
return
end
if alloc_mode == 1 then
local index = {}
for key, team in pairs(ctf.teams) do
if team.data.allow_joins ~= false and (max_players == -1 or
ctf.count_players_in_team(key) < max_players) then
table.insert(index, key)
end
end
if #index == 0 then
ctf.error("autoalloc", "No teams to join!")
else
return index[math.random(1, #index)]
end
elseif alloc_mode == 2 then
local one = nil
local one_count = -1
local two = nil
local two_count = -1
for key, team in pairs(ctf.teams) do
local count = ctf.count_players_in_team(key)
if team.data.allow_joins ~= false and
(max_players == -1 or count < max_players) then
if count > one_count then
two = one
two_count = one_count
one = key
one_count = count
end
if count > two_count then
two = key
two_count = count
end
end
end
if not one and not two then
ctf.error("autoalloc", "No teams to join!")
elseif one and two then
if math.random() > 0.5 then
return one
else
return two
end
else
if one then
return one
else
return two
end
end
elseif alloc_mode == 3 then
local smallest = nil
local smallest_count = 1000
for key, team in pairs(ctf.teams) do
local count = ctf.count_players_in_team(key)
if team.data.allow_joins ~= false and
(not smallest or count < smallest_count) then
smallest = key
smallest_count = count
end
end
if not smallest then
ctf.error("autoalloc", "No teams to join!")
else
return smallest
end
elseif alloc_mode == 4 then
return ctf.custom_alloc(name)
else
ctf.error("autoalloc",
"Unknown allocation mode: " .. alloc_mode)
end
end
-- Custom team allocation function. Throws error
-- if unimplemented, and autoalloc mode 4 is selected
function ctf.custom_alloc()
error("Allocation mode set to custom while " ..
"ctf.custom_alloc hasn't been overridden!")
end
-- updates the spawn position for a team
function ctf.get_spawn(team)
if ctf.team(team) then
local spawn = ctf.team(team).spawn
if not spawn then
return nil
end
return vector.add(spawn, minetest.string_to_pos(ctf.setting("spawn_offset")))
else
return nil
end
end
function ctf.move_to_spawn(name)
local player = minetest.get_player_by_name(name)
local tplayer = ctf.player(name)
if ctf.team(tplayer.team) then
local spawn = ctf.get_spawn(tplayer.team)
if spawn then
player:move_to(spawn, false)
return true
end
end
return false
end
minetest.register_on_respawnplayer(function(player)
if not player then
return false
end
return ctf.move_to_spawn(player:get_player_name())
end)
function ctf.get_territory_owner(pos)
local largest = nil
local largest_weight = 0
for i = 1, #ctf.registered_on_territory_query do
local team, weight = ctf.registered_on_territory_query[i](pos)
if team and weight then
if weight == -1 then
return team
end
if weight > largest_weight then
largest = team
largest_weight = weight
end
end
end
return largest
end
minetest.register_on_newplayer(function(player)
local name = player:get_player_name()
local team = ctf.autoalloc(name)
if team then
ctf.log("autoalloc", name .. " was allocated to " .. team)
ctf.join(name, team)
ctf.move_to_spawn(player:get_player_name())
end
end)
minetest.register_on_joinplayer(function(player)
if not ctf.setting("autoalloc_on_joinplayer") then
return
end
local name = player:get_player_name()
if ctf.team(ctf.player(name).team) then
return
end
local team = ctf.autoalloc(name)
if team then
ctf.log("autoalloc", name .. " was allocated to " .. team)
ctf.join(name, team)
ctf.move_to_spawn(player:get_player_name())
end
end)
-- Disable friendly fire.
ctf.registered_on_killedplayer = {}
function ctf.register_on_killedplayer(func)
if ctf._mt_loaded then
error("You can't register callbacks at game time!")
end
table.insert(ctf.registered_on_killedplayer, func)
end
local dead_players = {}
minetest.register_on_respawnplayer(function(player)
dead_players[player:get_player_name()] = nil
end)
minetest.register_on_joinplayer(function(player)
dead_players[player:get_player_name()] = nil
end)
minetest.register_on_punchplayer(function(player, hitter,
time_from_last_punch, tool_capabilities, dir, damage)
if player and hitter then
local pname = player:get_player_name()
local hname = hitter:get_player_name()
local to = ctf.player(pname)
local from = ctf.player(hname)
if dead_players[pname] then
return
end
if to.team == from.team and to.team ~= "" and
to.team ~= nil and to.name ~= from.name then
minetest.chat_send_player(hname, pname .. " is on your team!")
if not ctf.setting("friendly_fire") then
return true
end
end
local hp = player:get_hp()
if hp == 0 then
return false
end
if hp - damage <= 0 then
dead_players[pname] = true
local wielded = hitter:get_wielded_item()
for i = 1, #ctf.registered_on_killedplayer do
ctf.registered_on_killedplayer[i](pname, hname,
wielded, tool_capabilities)
end
return false
end
end
end)