Add ctf_pvp_engine mods directly in the repo
This commit is contained in:
parent
1032a78a6c
commit
a735266eb3
82 changed files with 3892 additions and 4 deletions
269
mods/ctf_pvp_engine/ctf/core.lua
Normal file
269
mods/ctf_pvp_engine/ctf/core.lua
Normal file
|
@ -0,0 +1,269 @@
|
|||
-- Awaiting core support.
|
||||
local function __genOrderedIndex( t )
|
||||
local orderedIndex = {}
|
||||
for key in pairs(t) do
|
||||
table.insert( orderedIndex, key )
|
||||
end
|
||||
table.sort( orderedIndex )
|
||||
return orderedIndex
|
||||
end
|
||||
|
||||
local function orderedNext(t, state)
|
||||
-- Equivalent of the next function, but returns the keys in the alphabetic
|
||||
-- order. We use a temporary ordered key table that is stored in the
|
||||
-- table being iterated.
|
||||
|
||||
local key = nil
|
||||
if state == nil then
|
||||
t.__orderedIndex = __genOrderedIndex( t )
|
||||
key = t.__orderedIndex[1]
|
||||
else
|
||||
for i = 1,table.getn(t.__orderedIndex) do
|
||||
if t.__orderedIndex[i] == state then
|
||||
key = t.__orderedIndex[i+1]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if key then
|
||||
return key, t[key]
|
||||
end
|
||||
|
||||
-- no more value to return, cleanup
|
||||
t.__orderedIndex = nil
|
||||
return
|
||||
end
|
||||
|
||||
function orderedPairs(t)
|
||||
-- Equivalent of the pairs() function on tables. Allows to iterate
|
||||
-- in order
|
||||
return orderedNext, t, nil
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- Registered
|
||||
ctf.registered_on_load = {}
|
||||
function ctf.register_on_load(func)
|
||||
if ctf._mt_loaded then
|
||||
error("You can't register callbacks at game time!")
|
||||
end
|
||||
table.insert(ctf.registered_on_load, func)
|
||||
if ctf._loaddata then
|
||||
func(ctf._loaddata)
|
||||
end
|
||||
end
|
||||
ctf.registered_on_save = {}
|
||||
function ctf.register_on_save(func)
|
||||
if ctf._mt_loaded then
|
||||
error("You can't register callbacks at game time!")
|
||||
end
|
||||
table.insert(ctf.registered_on_save, func)
|
||||
end
|
||||
ctf.registered_on_init = {}
|
||||
function ctf.register_on_init(func)
|
||||
if ctf._mt_loaded then
|
||||
error("You can't register callbacks at game time!")
|
||||
end
|
||||
table.insert(ctf.registered_on_init, func)
|
||||
if ctf._inited then
|
||||
func()
|
||||
end
|
||||
end
|
||||
ctf.registered_on_new_team = {}
|
||||
function ctf.register_on_new_team(func)
|
||||
if ctf._mt_loaded then
|
||||
error("You can't register callbacks at game time!")
|
||||
end
|
||||
table.insert(ctf.registered_on_new_team, func)
|
||||
end
|
||||
ctf.registered_on_territory_query = {}
|
||||
function ctf.register_on_territory_query(func)
|
||||
if ctf._mt_loaded then
|
||||
error("You can't register callbacks at game time!")
|
||||
end
|
||||
table.insert(ctf.registered_on_territory_query, func)
|
||||
end
|
||||
ctf.registered_on_new_game = {}
|
||||
function ctf.register_on_new_game(func)
|
||||
if ctf._mt_loaded then
|
||||
error("You can't register callbacks at game time!")
|
||||
end
|
||||
table.insert(ctf.registered_on_new_game, func)
|
||||
if ctf._new_game then
|
||||
func()
|
||||
end
|
||||
end
|
||||
|
||||
function vector.distanceSQ(p1, p2)
|
||||
local x = p1.x - p2.x
|
||||
local y = p1.y - p2.y
|
||||
local z = p1.z - p2.z
|
||||
return x*x + y*y + z*z
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- Debug helpers
|
||||
function ctf.error(area, msg)
|
||||
minetest.log("error", "CTF::" .. area .. " - " ..msg)
|
||||
end
|
||||
function ctf.log(area, msg)
|
||||
if area and area ~= "" then
|
||||
print("[CaptureTheFlag] (" .. area .. ") " .. msg)
|
||||
else
|
||||
print("[CaptureTheFlag] " .. msg)
|
||||
end
|
||||
end
|
||||
function ctf.action(area, msg)
|
||||
if area and area ~= "" then
|
||||
minetest.log("action", "[CaptureTheFlag] (" .. area .. ") " .. msg)
|
||||
else
|
||||
minetest.log("action", "[CaptureTheFlag] " .. msg)
|
||||
end
|
||||
end
|
||||
function ctf.warning(area, msg)
|
||||
print("WARNING: [CaptureTheFlag] (" .. area .. ") " .. msg)
|
||||
end
|
||||
|
||||
function ctf.init()
|
||||
ctf._inited = true
|
||||
ctf.log("init", "Initialising!")
|
||||
|
||||
-- Set up structures
|
||||
ctf._defsettings = {}
|
||||
ctf.teams = {}
|
||||
ctf.players = {}
|
||||
|
||||
-- See minetest.conf.example in the root of this subgame
|
||||
|
||||
ctf.log("init", "Creating Default Settings")
|
||||
ctf._set("diplomacy", true)
|
||||
ctf._set("players_can_change_team", true)
|
||||
ctf._set("allocate_mode", 0)
|
||||
ctf._set("maximum_in_team", -1)
|
||||
ctf._set("default_diplo_state", "war")
|
||||
ctf._set("hud", true)
|
||||
ctf._set("autoalloc_on_joinplayer", true)
|
||||
ctf._set("friendly_fire", true)
|
||||
ctf._set("spawn_offset", "0,0,0")
|
||||
|
||||
|
||||
for i = 1, #ctf.registered_on_init do
|
||||
ctf.registered_on_init[i]()
|
||||
end
|
||||
|
||||
ctf.load()
|
||||
end
|
||||
|
||||
function ctf.reset()
|
||||
ctf.log("io", "Deleting CTF save data...")
|
||||
os.remove(minetest.get_worldpath().."/ctf.txt")
|
||||
ctf.player_last_team = {}
|
||||
ctf.init()
|
||||
end
|
||||
|
||||
-- Set default setting value
|
||||
function ctf._set(setting, default)
|
||||
if ctf._defsettings[setting] then
|
||||
ctf.warning("settings", "Setting " .. dump(setting) .. " redeclared!")
|
||||
ctf.warning("settings", debug.traceback())
|
||||
end
|
||||
ctf._defsettings[setting] = default
|
||||
|
||||
if minetest.settings:get("ctf."..setting) then
|
||||
ctf.log("settings", "- " .. setting .. ": " .. minetest.settings:get("ctf."..setting))
|
||||
elseif minetest.settings:get("ctf_"..setting) then
|
||||
ctf.log("settings", "- " .. setting .. ": " .. minetest.settings:get("ctf_"..setting))
|
||||
ctf.warning("settings", "deprecated setting ctf_"..setting..
|
||||
" used, use ctf."..setting.." instead.")
|
||||
end
|
||||
end
|
||||
|
||||
function ctf.setting(name)
|
||||
local set = minetest.settings:get("ctf."..name) or
|
||||
minetest.settings:get("ctf_"..name)
|
||||
local dset = ctf._defsettings[name]
|
||||
if dset == nil then
|
||||
ctf.error("setting", "No such setting - " .. name)
|
||||
return nil
|
||||
end
|
||||
|
||||
if set ~= nil then
|
||||
if type(dset) == "number" then
|
||||
return tonumber(set)
|
||||
elseif type(dset) == "boolean" then
|
||||
return minetest.is_yes(set)
|
||||
else
|
||||
return set
|
||||
end
|
||||
else
|
||||
return dset
|
||||
end
|
||||
end
|
||||
|
||||
function ctf.load()
|
||||
ctf.log("io", "Loading CTF state")
|
||||
local file = io.open(minetest.get_worldpath().."/ctf.txt", "r")
|
||||
if file then
|
||||
local table = minetest.deserialize(file:read("*all"))
|
||||
if type(table) == "table" then
|
||||
ctf.teams = table.teams
|
||||
ctf.players = table.players
|
||||
|
||||
for i = 1, #ctf.registered_on_load do
|
||||
ctf.registered_on_load[i](table)
|
||||
end
|
||||
return
|
||||
end
|
||||
ctf._loaddata = table
|
||||
else
|
||||
ctf.log("io", "ctf.txt is not present in the world folder")
|
||||
ctf._new_game = true
|
||||
for i = 1, #ctf.registered_on_new_game do
|
||||
ctf.registered_on_new_game[i]()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
minetest.after(0, function()
|
||||
ctf._loaddata = nil
|
||||
ctf._mt_loaded = true
|
||||
end)
|
||||
|
||||
function ctf.check_save()
|
||||
if ctf_flag and ctf_flag.assert_flags then
|
||||
ctf_flag.assert_flags()
|
||||
end
|
||||
if ctf.needs_save then
|
||||
ctf.save()
|
||||
end
|
||||
minetest.after(10, ctf.check_save)
|
||||
end
|
||||
minetest.after(10, ctf.check_save)
|
||||
|
||||
function ctf.save()
|
||||
local file = io.open(minetest.get_worldpath().."/ctf.txt", "w")
|
||||
if file then
|
||||
local out = {
|
||||
teams = ctf.teams,
|
||||
players = ctf.players
|
||||
}
|
||||
|
||||
for i = 1, #ctf.registered_on_save do
|
||||
local res = ctf.registered_on_save[i]()
|
||||
|
||||
if res then
|
||||
for key, value in pairs(res) do
|
||||
out[key] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
file:write(minetest.serialize(out))
|
||||
file:close()
|
||||
ctf.needs_save = false
|
||||
else
|
||||
ctf.error("io", "CTF file failed to save!")
|
||||
end
|
||||
end
|
2
mods/ctf_pvp_engine/ctf/depends.txt
Normal file
2
mods/ctf_pvp_engine/ctf/depends.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
chatplus?
|
||||
hudkit
|
83
mods/ctf_pvp_engine/ctf/diplomacy.lua
Normal file
83
mods/ctf_pvp_engine/ctf/diplomacy.lua
Normal file
|
@ -0,0 +1,83 @@
|
|||
-- diplo states: war, peace, alliance
|
||||
ctf.diplo = {
|
||||
diplo = {}
|
||||
}
|
||||
|
||||
ctf.register_on_load(function(table)
|
||||
ctf.diplo.diplo = table.diplo
|
||||
end)
|
||||
|
||||
ctf.register_on_save(function()
|
||||
return { diplo = ctf.diplo.diplo }
|
||||
end)
|
||||
|
||||
function ctf.diplo.get(one,two)
|
||||
if not ctf.diplo.diplo then
|
||||
return ctf.setting("default_diplo_state")
|
||||
end
|
||||
|
||||
for i = 1, #ctf.diplo.diplo do
|
||||
local dip = ctf.diplo.diplo[i]
|
||||
if (dip.one == one and dip.two == two) or
|
||||
(dip.one == two and dip.two == one) then
|
||||
return dip.state
|
||||
end
|
||||
end
|
||||
|
||||
return ctf.setting("default_diplo_state")
|
||||
end
|
||||
|
||||
function ctf.diplo.set(one, two, state)
|
||||
if ctf.diplo.diplo then
|
||||
-- Check the table for an existing diplo state
|
||||
for i = 1, #ctf.diplo.diplo do
|
||||
local dip = ctf.diplo.diplo[i]
|
||||
if (dip.one == one and dip.two == two) or
|
||||
(dip.one == two and dip.two == one) then
|
||||
dip.state = state
|
||||
return
|
||||
end
|
||||
end
|
||||
else
|
||||
ctf.diplo.diplo = {}
|
||||
end
|
||||
|
||||
table.insert(ctf.diplo.diplo,{one=one,two=two,state=state})
|
||||
end
|
||||
|
||||
function ctf.diplo.check_requests(one, two)
|
||||
local team = ctf.team(two)
|
||||
|
||||
if not team.log then
|
||||
return nil
|
||||
end
|
||||
|
||||
for i=1,#team.log do
|
||||
if team.log[i].team == one and
|
||||
team.log[i].type == "request" and
|
||||
team.log[i].mode == "diplo" then
|
||||
return team.log[i].msg
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
function ctf.diplo.cancel_requests(one, two)
|
||||
local team = ctf.team(two)
|
||||
|
||||
if not team.log then
|
||||
return
|
||||
end
|
||||
|
||||
for i=1,#team.log do
|
||||
if team.log[i].team == one and
|
||||
team.log[i].type == "request" and
|
||||
team.log[i].mode == "diplo" then
|
||||
table.remove(team.log,i)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
return
|
||||
end
|
353
mods/ctf_pvp_engine/ctf/gui.lua
Normal file
353
mods/ctf_pvp_engine/ctf/gui.lua
Normal file
|
@ -0,0 +1,353 @@
|
|||
ctf.gui = {
|
||||
tabs = {}
|
||||
}
|
||||
|
||||
ctf.register_on_init(function()
|
||||
ctf._set("gui", true)
|
||||
ctf._set("gui.team", true)
|
||||
ctf._set("gui.team.initial", "news")
|
||||
|
||||
for name, tab in pairs(ctf.gui.tabs) do
|
||||
ctf._set("gui.tab." .. name, true)
|
||||
end
|
||||
end)
|
||||
|
||||
function ctf.gui.register_tab(name, title, func)
|
||||
ctf.gui.tabs[name] = {
|
||||
name = name,
|
||||
title = title,
|
||||
func = func
|
||||
}
|
||||
|
||||
if ctf._defsettings and ctf._defsettings["gui.tab." .. name] == nil then
|
||||
ctf._set("gui.tab." .. name, true)
|
||||
end
|
||||
end
|
||||
|
||||
function ctf.gui.show(name, tab, tname)
|
||||
if not tab then
|
||||
tab = ctf.setting("gui.team.initial") or "news"
|
||||
end
|
||||
|
||||
if not tab or not ctf.gui.tabs[tab] or not name or name == "" then
|
||||
ctf.log("gui", "Invalid tab or name given to ctf.gui.show")
|
||||
return
|
||||
end
|
||||
|
||||
if not ctf.setting("gui.team") or not ctf.setting("gui") then
|
||||
return
|
||||
end
|
||||
|
||||
if not ctf.team(tname) then
|
||||
tname = ctf.player(name).team
|
||||
end
|
||||
|
||||
if ctf.team(tname) then
|
||||
ctf.action("gui", name .. " views " .. tname .. "'s " .. tab .. " page")
|
||||
ctf.gui.tabs[tab].func(name, tname)
|
||||
else
|
||||
ctf.log("gui", "Invalid team given to ctf.gui.show")
|
||||
end
|
||||
end
|
||||
|
||||
-- Get tab buttons
|
||||
function ctf.gui.get_tabs(name, tname)
|
||||
local result = ""
|
||||
local id = 1
|
||||
local function addtab(name,text)
|
||||
result = result .. "button[" .. (id*2-1) .. ",0;2,1;" .. name .. ";" .. text .. "]"
|
||||
id = id + 1
|
||||
end
|
||||
|
||||
for name, tab in pairs(ctf.gui.tabs) do
|
||||
if ctf.setting("gui.tab." .. name) then
|
||||
addtab(name, tab.title)
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
-- Team interface
|
||||
ctf.gui.register_tab("news", "News", function(name, tname)
|
||||
local result = ""
|
||||
local team = ctf.team(tname).log
|
||||
|
||||
if not team then
|
||||
team = {}
|
||||
end
|
||||
|
||||
local amount = 0
|
||||
|
||||
for i = 1, #team do
|
||||
if team[i].type == "request" then
|
||||
if ctf.can_mod(name, tname) then
|
||||
amount = amount + 2
|
||||
local height = (amount*0.5) + 0.5
|
||||
amount = amount + 1
|
||||
|
||||
if team[i].mode == "diplo" then
|
||||
result = result .. "background[0.5," .. height .. ";8.3,1;diplo_" .. team[i].msg .. ".png]"
|
||||
if team[i].msg == "alliance" then
|
||||
result = result .. "label[1," .. height .. ";" ..
|
||||
team[i].team .. " offers an " ..
|
||||
minetest.formspec_escape(team[i].msg) .. " treaty]"
|
||||
else
|
||||
result = result .. "label[1," .. height .. ";" ..
|
||||
team[i].team .. " offers a " ..
|
||||
minetest.formspec_escape(team[i].msg) .. " treaty]"
|
||||
end
|
||||
result = result .. "button[6," .. height .. ";1,1;btn_y" .. i .. ";Yes]"
|
||||
result = result .. "button[7," .. height .. ";1,1;btn_n" .. i .. ";No]"
|
||||
else
|
||||
result = result .. "label[0.5," .. height .. ";RANDOM REQUEST TYPE]"
|
||||
end
|
||||
end
|
||||
else
|
||||
amount = amount + 1
|
||||
local height = (amount*0.5) + 0.5
|
||||
|
||||
if height > 5 then
|
||||
break
|
||||
end
|
||||
|
||||
result = result .. "label[0.5," .. height .. ";" ..
|
||||
minetest.formspec_escape(team[i].msg) .. "]"
|
||||
end
|
||||
end
|
||||
|
||||
if ctf.can_mod(name, tname) then
|
||||
result = result .. "button[4,6;2,1;clear;Clear all]"
|
||||
end
|
||||
|
||||
if amount == 0 then
|
||||
result = "label[0.5,1;Welcome to the news panel]" ..
|
||||
"label[0.5,1.5;News such as attacks will appear here]"
|
||||
end
|
||||
|
||||
minetest.show_formspec(name, "ctf:news",
|
||||
"size[10,7]" ..
|
||||
ctf.gui.get_tabs(name, tname) ..
|
||||
result)
|
||||
end)
|
||||
|
||||
-- Team interface
|
||||
ctf.gui.register_tab("diplo", "Diplomacy", function(name, tname)
|
||||
local result = ""
|
||||
local data = {}
|
||||
|
||||
local amount = 0
|
||||
|
||||
for key, value in pairs(ctf.teams) do
|
||||
if key ~= tname then
|
||||
table.insert(data,{
|
||||
team = key,
|
||||
state = ctf.diplo.get(tname, key),
|
||||
to = ctf.diplo.check_requests(tname, key),
|
||||
from = ctf.diplo.check_requests(key, tname)
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
result = result .. "label[1,1;Diplomacy from the perspective of " .. tname .. "]"
|
||||
|
||||
for i = 1, #data do
|
||||
amount = i
|
||||
local height = (i*1)+0.5
|
||||
|
||||
if height > 5 then
|
||||
break
|
||||
end
|
||||
|
||||
result = result .. "background[1," .. height .. ";8.2,1;diplo_" ..
|
||||
data[i].state .. ".png]"
|
||||
result = result .. "button[1.25," .. height .. ";2,1;team_" ..
|
||||
data[i].team .. ";" .. data[i].team .. "]"
|
||||
result = result .. "label[3.75," .. height .. ";" .. data[i].state
|
||||
.. "]"
|
||||
|
||||
if ctf.can_mod(name, tname) and ctf.player(name).team == tname then
|
||||
if not data[i].from and not data[i].to then
|
||||
if data[i].state == "war" then
|
||||
result = result .. "button[7.5," .. height ..
|
||||
";1.5,1;peace_" .. data[i].team .. ";Peace]"
|
||||
elseif data[i].state == "peace" then
|
||||
result = result .. "button[6," .. height ..
|
||||
";1.5,1;war_" .. data[i].team .. ";War]"
|
||||
result = result .. "button[7.5," .. height ..
|
||||
";1.5,1;alli_" .. data[i].team .. ";Alliance]"
|
||||
elseif data[i].state == "alliance" then
|
||||
result = result .. "button[6," .. height ..
|
||||
";1.5,1;peace_" .. data[i].team .. ";Peace]"
|
||||
end
|
||||
elseif data[i].from ~= nil then
|
||||
result = result .. "label[6," .. height ..
|
||||
";request recieved]"
|
||||
elseif data[i].to ~= nil then
|
||||
result = result .. "label[5.5," .. height ..
|
||||
";request sent]"
|
||||
result = result .. "button[7.5," .. height ..
|
||||
";1.5,1;cancel_" .. data[i].team .. ";Cancel]"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
minetest.show_formspec(name, "ctf:diplo",
|
||||
"size[10,7]" ..
|
||||
ctf.gui.get_tabs(name, tname) ..
|
||||
result
|
||||
)
|
||||
end)
|
||||
|
||||
local function formspec_is_ctf_tab(fsname)
|
||||
for name, tab in pairs(ctf.gui.tabs) do
|
||||
if fsname == "ctf:" .. name then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if not formspec_is_ctf_tab(formname) then
|
||||
return false
|
||||
end
|
||||
|
||||
local name = player:get_player_name()
|
||||
local tplayer = ctf.player(name)
|
||||
local tname = tplayer.team
|
||||
local team = ctf.team(tname)
|
||||
|
||||
if not team then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Do navigation
|
||||
for tabname, tab in pairs(ctf.gui.tabs) do
|
||||
if fields[tabname] then
|
||||
ctf.gui.show(name, tabname)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- Todo: move callbacks
|
||||
-- News page
|
||||
if fields.clear then
|
||||
team.log = {}
|
||||
ctf.needs_save = true
|
||||
ctf.gui.show(name, "news")
|
||||
return true
|
||||
end
|
||||
end)
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
local name = player:get_player_name()
|
||||
local tplayer = ctf.player(name)
|
||||
local tname = tplayer.team
|
||||
local team = ctf.team(tname)
|
||||
|
||||
if not team then
|
||||
return false
|
||||
end
|
||||
|
||||
if formname == "ctf:news" then
|
||||
for key, field in pairs(fields) do
|
||||
local ok, id = string.match(key, "btn_([yn])([0123456789]+)")
|
||||
if ok and id then
|
||||
if ok == "y" then
|
||||
ctf.diplo.set(tname, team.log[tonumber(id)].team, team.log[tonumber(id)].msg)
|
||||
|
||||
-- Post to acceptor's log
|
||||
ctf.post(tname, {
|
||||
msg = "You have accepted the " ..
|
||||
team.log[tonumber(id)].msg .. " request from " ..
|
||||
team.log[tonumber(id)].team })
|
||||
|
||||
-- Post to request's log
|
||||
ctf.post(team.log[tonumber(id)].team, {
|
||||
msg = tname .. " has accepted your " ..
|
||||
team.log[tonumber(id)].msg .. " request" })
|
||||
|
||||
id = id + 1
|
||||
end
|
||||
|
||||
table.remove(team.log, id)
|
||||
ctf.needs_save = true
|
||||
ctf.gui.show(name, "news")
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
local name = player:get_player_name()
|
||||
local tplayer = ctf.player(name)
|
||||
local tname = tplayer.team
|
||||
local team = ctf.team(tname)
|
||||
|
||||
if not team or formname ~= "ctf:diplo" then
|
||||
return false
|
||||
end
|
||||
|
||||
for key, field in pairs(fields) do
|
||||
local tname2 = string.match(key, "team_(.+)")
|
||||
if tname2 and ctf.team(tname2) then
|
||||
ctf.gui.show(name, "diplo", tname2)
|
||||
return true
|
||||
end
|
||||
|
||||
if ctf.can_mod(name, tname) then
|
||||
tname2 = string.match(key, "peace_(.+)")
|
||||
if tname2 then
|
||||
if ctf.diplo.get(tname, tname2) == "war" then
|
||||
ctf.post(tname2, {
|
||||
type = "request",
|
||||
msg = "peace",
|
||||
team = tname,
|
||||
mode = "diplo" })
|
||||
else
|
||||
ctf.diplo.set(tname, tname2, "peace")
|
||||
ctf.post(tname, {
|
||||
msg = "You have cancelled the alliance treaty with " .. tname2 })
|
||||
ctf.post(tname2, {
|
||||
msg = tname .. " has cancelled the alliance treaty" })
|
||||
end
|
||||
|
||||
ctf.gui.show(name, "diplo")
|
||||
return true
|
||||
end
|
||||
|
||||
tname2 = string.match(key, "war_(.+)")
|
||||
if tname2 then
|
||||
ctf.diplo.set(tname, tname2, "war")
|
||||
ctf.post(tname, {
|
||||
msg = "You have declared war on " .. tname2 })
|
||||
ctf.post(tname2, {
|
||||
msg = tname .. " has declared war on you" })
|
||||
|
||||
ctf.gui.show(name, "diplo")
|
||||
return true
|
||||
end
|
||||
|
||||
tname2 = string.match(key, "alli_(.+)")
|
||||
if tname2 then
|
||||
ctf.post(tname2, {
|
||||
type = "request",
|
||||
msg = "alliance",
|
||||
team = tname,
|
||||
mode = "diplo" })
|
||||
|
||||
ctf.gui.show(name, "diplo")
|
||||
return true
|
||||
end
|
||||
|
||||
tname2 = string.match(key, "cancel_(.+)")
|
||||
if tname2 then
|
||||
ctf.diplo.cancel_requests(tname, tname2)
|
||||
ctf.gui.show(name, "diplo")
|
||||
return true
|
||||
end
|
||||
end -- end if can mod
|
||||
end -- end for each field
|
||||
end)
|
50
mods/ctf_pvp_engine/ctf/hud.lua
Normal file
50
mods/ctf_pvp_engine/ctf/hud.lua
Normal file
|
@ -0,0 +1,50 @@
|
|||
ctf.hud = hudkit()
|
||||
ctf.hud.parts = {}
|
||||
function ctf.hud.register_part(func)
|
||||
table.insert(ctf.hud.parts, func)
|
||||
end
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
ctf.hud.players[player:get_player_name()] = nil
|
||||
end)
|
||||
|
||||
ctf.register_on_join_team(function(name, tname)
|
||||
if ctf.setting("hud") then
|
||||
ctf.hud.update(minetest.get_player_by_name(name))
|
||||
end
|
||||
end)
|
||||
|
||||
function ctf.hud.update(player)
|
||||
if not player then
|
||||
return
|
||||
end
|
||||
|
||||
local name = player:get_player_name()
|
||||
local tplayer = ctf.player(name)
|
||||
|
||||
if not tplayer or not tplayer.team or not ctf.team(tplayer.team) then
|
||||
return
|
||||
end
|
||||
|
||||
-- Team Identifier
|
||||
for i = 1, #ctf.hud.parts do
|
||||
ctf.hud.parts[i](player, name, tplayer)
|
||||
end
|
||||
end
|
||||
|
||||
function ctf.hud.updateAll()
|
||||
if not ctf.setting("hud") then
|
||||
return
|
||||
end
|
||||
|
||||
local players = minetest.get_connected_players()
|
||||
for i = 1, #players do
|
||||
ctf.hud.update(players[i])
|
||||
end
|
||||
end
|
||||
|
||||
local function tick()
|
||||
ctf.hud.updateAll()
|
||||
minetest.after(10, tick)
|
||||
end
|
||||
minetest.after(1, tick)
|
33
mods/ctf_pvp_engine/ctf/init.lua
Normal file
33
mods/ctf_pvp_engine/ctf/init.lua
Normal file
|
@ -0,0 +1,33 @@
|
|||
-- CAPTURE THE FLAG
|
||||
-- by Andrew "rubenwardy" Ward
|
||||
-----------------------------------------
|
||||
|
||||
ctf = {}
|
||||
|
||||
-- Fix for https://github.com/minetest/minetest/issues/2383
|
||||
local csa = minetest.chat_send_all
|
||||
function minetest.chat_send_all(msg)
|
||||
minetest.after(0, function()
|
||||
csa(msg)
|
||||
end)
|
||||
end
|
||||
|
||||
-- Privs
|
||||
minetest.register_privilege("ctf_team_mgr", {
|
||||
description = "Team manager",
|
||||
})
|
||||
|
||||
minetest.register_privilege("ctf_admin", {
|
||||
description = "Can create teams, manage players, assign team owners.",
|
||||
})
|
||||
|
||||
-- Modules
|
||||
dofile(minetest.get_modpath("ctf") .. "/core.lua")
|
||||
dofile(minetest.get_modpath("ctf") .. "/teams.lua")
|
||||
dofile(minetest.get_modpath("ctf") .. "/diplomacy.lua")
|
||||
dofile(minetest.get_modpath("ctf") .. "/gui.lua")
|
||||
dofile(minetest.get_modpath("ctf") .. "/hud.lua")
|
||||
|
||||
-- Init
|
||||
ctf.init()
|
||||
ctf.clean_player_lists()
|
516
mods/ctf_pvp_engine/ctf/teams.lua
Normal file
516
mods/ctf_pvp_engine/ctf/teams.lua
Normal file
|
@ -0,0 +1,516 @@
|
|||
-- 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)
|
BIN
mods/ctf_pvp_engine/ctf/textures/diplo_alliance.png
Normal file
BIN
mods/ctf_pvp_engine/ctf/textures/diplo_alliance.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.5 KiB |
BIN
mods/ctf_pvp_engine/ctf/textures/diplo_peace.png
Normal file
BIN
mods/ctf_pvp_engine/ctf/textures/diplo_peace.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9 KiB |
BIN
mods/ctf_pvp_engine/ctf/textures/diplo_war.png
Normal file
BIN
mods/ctf_pvp_engine/ctf/textures/diplo_war.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.8 KiB |
Loading…
Add table
Add a link
Reference in a new issue