Add ctf_pvp_engine mods directly in the repo

This commit is contained in:
rubenwardy 2019-08-28 16:50:33 +01:00
parent 1032a78a6c
commit a735266eb3
82 changed files with 3892 additions and 4 deletions

3
.gitmodules vendored
View file

@ -1,6 +1,3 @@
[submodule "mods/ctf_pvp_engine"]
path = mods/ctf_pvp_engine
url = https://github.com/rubenwardy/ctf_pvp_engine
[submodule "mods/crafting"]
path = mods/crafting
url = https://github.com/rubenwardy/crafting

@ -1 +0,0 @@
Subproject commit ef6e358de558617181c5acf52299212c8fcb9bce

215
mods/ctf_pvp_engine/.gitignore vendored Normal file
View file

@ -0,0 +1,215 @@
#################
## Eclipse
#################
*.pydevproject
.project
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.classpath
.settings/
.loadpath
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# CDT-specific
.cproject
# PDT-specific
.buildpath
#################
## Visual Studio
#################
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.sln.docstates
# Build results
[Dd]ebug/
[Rr]elease/
x64/
build/
[Bb]in/
[Oo]bj/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
*_i.c
*_p.c
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.log
*.scc
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
*.ncrunch*
.*crunch*.local.xml
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.Publish.xml
*.pubxml
# NuGet Packages Directory
## TODO: If you have NuGet Package Restore enabled, uncomment the next line
#packages/
# Windows Azure Build Output
csx
*.build.csdef
# Windows Store app package directory
AppPackages/
# Others
sql/
*.Cache
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.[Pp]ublish.xml
*.pfx
*.publishsettings
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file to a newer
# Visual Studio version. Backup files are not needed, because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
App_Data/*.mdf
App_Data/*.ldf
#############
## Windows detritus
#############
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Mac crap
.DS_Store
#############
## Python
#############
*.py[co]
# Packages
*.egg
*.egg-info
dist/
build/
eggs/
parts/
var/
sdist/
develop-eggs/
.installed.cfg
# Installer logs
pip-log.txt
# Unit test / coverage reports
.coverage
.tox
#Translations
*.mo
#Mr Developer
.mr.developer.cfg

View file

@ -0,0 +1,23 @@
CTF PvP Engine
==============
A highly modular framework for the Minetest game engine, in order to allow
the development of Capture the Flag / City vs City games. Good for any
sort of game where players can join teams - flags are optional, everything
is highly configurable.
Licenses
========
Created by: [rubenwardy](http://rubenwardy.com/).
Copyright (c) 2013 - 2015
**Code:** LGPL 2.1 or later.
**Textures:** CC-BY-SA 3.0
ctf_flag/sounds/trumpet* by tobyk, license: CC-BY 3.0
from: http://freesound.org/people/tobyk/sounds/26198/
Documentation
=============
See the doc_* files, starting with doc_project_overview.md

View 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

View file

@ -0,0 +1,2 @@
chatplus?
hudkit

View 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

View 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)

View 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)

View 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()

View 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)

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

View file

@ -0,0 +1,4 @@
ctf
ctf_colors
chatplus?
irc?

View file

@ -0,0 +1,411 @@
ctf.register_on_init(function()
ctf.log("chat", "Initialising...")
-- Settings: Chat
ctf._set("chat.team_channel", true)
ctf._set("chat.global_channel", true)
ctf._set("chat.default", "global")
end)
function minetest.is_player_name_valid(name)
return name:match("^[%a%d_-]+$")
end
local function team_console_help(name)
minetest.chat_send_player(name, "Try:")
minetest.chat_send_player(name, "/team - show team panel")
minetest.chat_send_player(name, "/team all - list all teams")
minetest.chat_send_player(name, "/team <team> - show details about team 'name'")
minetest.chat_send_player(name, "/team <name> - get which team 'player' is in")
minetest.chat_send_player(name, "/team player <name> - get which team 'player' is in")
local privs = minetest.get_player_privs(name)
if privs and privs.ctf_admin == true then
minetest.chat_send_player(name, "/team add <team> - add a team called name (ctf_admin only)")
minetest.chat_send_player(name, "/team remove <team> - add a team called name (ctf_admin only)")
end
if privs and privs.ctf_team_mgr == true then
minetest.chat_send_player(name, "/team lock <team> - closes a team to new players (ctf_team_mgr only)")
minetest.chat_send_player(name, "/team unlock <team> - opens a team to new players (ctf_team_mgr only)")
minetest.chat_send_player(name, "/team bjoin <team> <commands> - Command is * for all players, playername for one, !playername to remove (ctf_team_mgr only)")
minetest.chat_send_player(name, "/team join <name> <team> - add 'player' to team 'team' (ctf_team_mgr only)")
minetest.chat_send_player(name, "/team removeply <name> - add 'player' to team 'team' (ctf_team_mgr only)")
end
end
minetest.register_chatcommand("team", {
description = "Open the team console, or run team command (see /team help)",
func = function(name, param)
local test = string.match(param, "^player ([%a%d_-]+)")
local create = string.match(param, "^add ([%a%d_-]+)")
local remove = string.match(param, "^remove ([%a%d_-]+)")
local lock = string.match(param, "^lock ([%a%d_-]+)")
local unlock = string.match(param, "^unlock ([%a%d_-]+)")
local j_name, j_tname = string.match(param, "^join ([%a%d_-]+) ([%a%d_]+)")
local b_tname, b_pattern = string.match(param, "^bjoin ([%a%d_-]+) ([%a%d_-%*%! ]+)")
local l_name = string.match(param, "^removeplr ([%a%d_-]+)")
if create then
local privs = minetest.get_player_privs(name)
if privs and privs.ctf_admin then
if (
string.match(create, "([%a%b_]-)")
and create ~= ""
and create ~= nil
and ctf.team({name=create, add_team=true})
) then
return true, "Added team '"..create.."'"
else
return false, "Error adding team '"..create.."'"
end
else
return false, "You are not a ctf_admin!"
end
elseif remove then
local privs = minetest.get_player_privs(name)
if privs and privs.ctf_admin then
if ctf.remove_team(remove) then
return true, "Removed team '" .. remove .. "'"
else
return false, "Error removing team '" .. remove .. "'"
end
else
return false, "You are not a ctf_admin!"
end
elseif lock then
local privs = minetest.get_player_privs(name)
if privs and privs.ctf_team_mgr then
local team = ctf.team(lock)
if team then
team.data.allow_joins = false
return true, "Locked team to new members"
else
return false, "Unable to find that team!"
end
else
return false, "You are not a ctf_team_mgr!"
end
elseif unlock then
local privs = minetest.get_player_privs(name)
if privs and privs.ctf_team_mgr then
local team = ctf.team(unlock)
if team then
team.data.allow_joins = true
return true, "Unlocked team to new members"
else
return false, "Unable to find that team!"
end
else
return false, "You are not a ctf_team_mgr!"
end
elseif param == "all" then
ctf.list_teams(name)
elseif ctf.team(param) then
minetest.chat_send_player(name, "Team "..param..":")
local count = 0
for _, value in pairs(ctf.team(param).players) do
count = count + 1
if value.auth then
minetest.chat_send_player(name, count .. ">> " .. value.name
.. " (team owner)")
else
minetest.chat_send_player(name, count .. ">> " .. value.name)
end
end
elseif ctf.player_or_nil(param) or test then
if not test then
test = param
end
if ctf.player(test).team then
if ctf.player(test).auth then
return true, test ..
" is in team " .. ctf.player(test).team.." (team owner)"
else
return true, test ..
" is in team " .. ctf.player(test).team
end
else
return true, test.." is not in a team"
end
elseif j_name and j_tname then
local privs = minetest.get_player_privs(name)
if privs and privs.ctf_team_mgr then
if ctf.join(j_name, j_tname, true, name) then
return true, "Successfully added " .. j_name .. " to " .. j_tname
else
return false, "Failed to add " .. j_name .. " to " .. j_tname
end
else
return true, "You are not a ctf_team_mgr!"
end
elseif b_pattern and b_tname then
local privs = minetest.get_player_privs(name)
if privs and privs.ctf_team_mgr then
local tokens = string.split(b_pattern, " ")
local players = {}
for _, token in pairs(tokens) do
print(token)
if token == "*" then
for _, player in pairs(minetest.get_connected_players()) do
players[player:get_player_name()] = true
end
elseif token:sub(1, 1) == "!" then
players[token:sub(2, #token)] = nil
elseif minetest.is_player_name_valid(token) then
players[token] = true
else
return false, "Invalid token: " .. token .. "\nExpecting *, playername, or !playername."
end
end
for pname, _ in pairs(players) do
ctf.join(pname, b_tname, true, name)
end
return true, "Success!"
else
return false, "You are not a ctf_team_mgr!"
end
elseif l_name then
local privs = minetest.get_player_privs(name)
if privs and privs.ctf_team_mgr then
if ctf.remove_player(l_name) then
return true, "Removed player " .. l_name
else
return false, "Failed to remove player."
end
else
return false, "You are not a ctf_team_mgr!"
end
elseif param=="help" then
team_console_help(name)
else
if param ~= "" and param ~= nil then
minetest.chat_send_player(name, "'"..param.."' is an invalid parameter to /team")
team_console_help(name)
end
if ctf.setting("gui") then
if (ctf and
ctf.players and
ctf.players[name] and
ctf.players[name].team) then
print("showing")
ctf.gui.show(name)
return true, "Showing the team window"
else
return false, "You're not part of a team!"
end
else
return false, "GUI is disabled!"
end
end
return false, "Nothing could be done"
end
})
minetest.register_chatcommand("join", {
params = "team name",
description = "Add to team",
func = function(name, param)
if ctf.join(name, param, false, name) then
return true, "Joined team " .. param .. "!"
else
return false, "Failed to join team!"
end
end
})
minetest.register_chatcommand("ctf_clean", {
description = "Do admin cleaning stuff",
privs = {ctf_admin=true},
func = function(name, param)
ctf.log("chat", "Cleaning CTF...")
ctf.clean_player_lists()
if ctf_flag and ctf_flag.assert_flags then
ctf_flag.assert_flags()
end
return true, "CTF cleaned!"
end
})
minetest.register_chatcommand("ctf_reset", {
description = "Delete all CTF saved states and start again.",
privs = {ctf_admin=true},
func = function(name, param)
minetest.chat_send_all("The CTF core was reset by the admin. All team memberships," ..
"flags, land ownerships etc have been deleted.")
ctf.reset()
return true, "Reset CTF core."
end,
})
minetest.register_chatcommand("ctf_reload", {
description = "reload the ctf main frame and get settings",
privs = {ctf_admin=true},
func = function(name, param)
ctf.needs_save = true
ctf.init()
return true, "CTF core reloaded!"
end
})
minetest.register_chatcommand("ctf_ls", {
description = "ctf: list settings",
privs = {ctf_admin=true},
func = function(name, param)
minetest.chat_send_player(name, "Settings:")
for set, def in orderedPairs(ctf._defsettings) do
minetest.chat_send_player(name, " - " .. set .. ": " .. dump(ctf.setting(set)))
print("\"" .. set .. "\" " .. dump(ctf.setting(set)))
end
return true
end
})
minetest.register_chatcommand("team_owner", {
params = "player name",
description = "Make player team owner",
privs = {ctf_admin=true},
func = function(name, param)
if ctf and ctf.players and ctf.player(param) and ctf.player(param).team and ctf.team(ctf.player(param).team) then
if ctf.player(param).auth == true then
ctf.player(param).auth = false
return true, param.." was downgraded from team admin status"
else
ctf.player(param).auth = true
return true, param.." was upgraded to an admin of "..ctf.player(name).team
end
ctf.needs_save = true
else
return false, "Unable to do that :/ "..param.." does not exist, or is not part of a valid team."
end
end
})
minetest.register_chatcommand("post", {
params = "message",
description = "Post a message on your team's message board",
func = function(name, param)
if ctf and ctf.players and ctf.players[name] and ctf.players[name].team and ctf.teams[ctf.players[name].team] then
if not ctf.player(name).auth then
minetest.chat_send_player(name, "You do not own that team")
end
if not ctf.teams[ctf.players[name].team].log then
ctf.teams[ctf.players[name].team].log = {}
end
table.insert(ctf.teams[ctf.players[name].team].log,{msg=param})
minetest.chat_send_player(name, "Posted: "..param)
else
minetest.chat_send_player(name, "Could not post message")
end
end,
})
minetest.register_chatcommand("all", {
params = "msg",
description = "Send a message on the global channel",
func = function(name, param)
if not ctf.setting("chat.global_channel") then
minetest.chat_send_player(name, "The global channel is disabled")
return
end
if ctf.player(name).team then
local tosend = ctf.player(name).team ..
" <" .. name .. "> " .. param
minetest.chat_send_all(tosend)
if minetest.global_exists("chatplus") then
chatplus.log(tosend)
end
else
minetest.chat_send_all("<"..name.."> "..param)
end
end
})
minetest.register_chatcommand("t", {
params = "msg",
description = "Send a message on the team channel",
func = function(name, param)
if not ctf.setting("chat.team_channel") then
minetest.chat_send_player(name, "The team channel is disabled.")
return
end
local tname = ctf.player(name).team
local team = ctf.team(tname)
if team then
minetest.log("action", tname .. "<" .. name .. "> ** ".. param .. " **")
if minetest.global_exists("chatplus") then
chatplus.log("<" .. name .. "> ** ".. param .. " **")
end
tcolor = ctf_colors.get_color(ctf.player(name))
for username, to in pairs(team.players) do
minetest.chat_send_player(username,
minetest.colorize(tcolor.css, "<" .. name .. "> ** " .. param .. " **"))
end
if minetest.global_exists("irc") and irc.feature_mod_channel then
irc:say(irc.config.channel, tname .. "<" .. name .. "> ** " .. param .. " **", true)
end
else
minetest.chat_send_player(name,
"You're not in a team, so you have no team to talk to.")
end
end
})
if minetest.global_exists("irc") then
function irc.playerMessage(name, message)
local tname = ctf.player(name).team
local color = ctf_colors.get_irc_color(ctf.player(name))
local clear = "\x0F"
if color then
color = "\x03" .. color
else
color = ""
clear = ""
end
local abrace = color .. "<" .. clear
local bbrace = color .. ">" .. clear
return ("%s%s%s %s"):format(abrace, name, bbrace, message)
end
end
local handler
handler = function(name, message)
if ctf.player(name).team then
for i = 1, #minetest.registered_on_chat_messages do
local func = minetest.registered_on_chat_messages[i]
if func ~= handler and func(name, message) then
return true
end
end
if not minetest.check_player_privs(name, {shout = true}) then
minetest.chat_send_player(name, "-!- You don't have permission to shout.")
return true
end
local tcolor = ctf_colors.get_color(ctf.player(name))
minetest.chat_send_all(minetest.colorize(tcolor.css,
"<" .. name .. "> ") .. message)
return true
else
return nil
end
end
table.insert(minetest.registered_on_chat_messages, 1, handler)
minetest.registered_chatcommands["me"].func = function(name, param)
if ctf.player(name).team then
local tcolor = ctf_colors.get_color(ctf.player(name))
name = minetest.colorize(tcolor.css, "* " .. name)
else
name = "* ".. name
end
minetest.chat_send_all(name .. " " .. param)
end

View file

@ -0,0 +1,2 @@
ctf
3d_armor?

View file

@ -0,0 +1,60 @@
ctf.gui.register_tab("settings", "Settings", function(name, team)
local color = ""
if ctf.team(team).data.color then
color = ctf.team(team).data.color
end
local result = "field[3,2;4,1;color;Team Color;" .. color .. "]" ..
"button[4,6;2,1;save;Save]"
if not ctf.can_mod(name,team) then
result = "label[0.5,1;You do not own this team!"
end
minetest.show_formspec(name, "ctf:settings",
"size[10,7]" ..
ctf.gui.get_tabs(name, team) ..
result
)
end)
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= "ctf:settings" then
return false
end
-- Settings page
if fields.save then
local name = player:get_player_name()
local pdata = ctf.player(name)
local team = ctf.team(pdata.team)
ctf.gui.show(name, "settings")
if team and ctf.can_mod(name, pdata.team) then
if ctf.flag_colors[fields.color] then
team.data.color = fields.color
ctf.needs_save = true
minetest.chat_send_player(name, "Team color set to " .. fields.color)
else
local colors = ""
for color, code in pairs(ctf.flag_colors) do
if colors ~= "" then
colors = colors .. ", "
end
colors = colors .. color
end
minetest.chat_send_player(name, "Color " .. fields.color ..
" does not exist! Available: " .. colors)
end
elseif team then
minetest.chat_send_player(name, "You don't have the rights to change settings.")
else
minetest.chat_send_player(name, "You don't appear to be in a team")
end
return true
end
end)

View file

@ -0,0 +1,88 @@
function ctf_colors.get_color(tplayer)
local team = ctf.team(tplayer.team)
local tcolor_text = nil
if team then
tcolor_text = team.data.color
end
local tcolor_hex = ctf.flag_colors[tcolor_text]
if not tcolor_hex then
tcolor_hex = "0x000000"
end
local tcolor_css = "#" .. tcolor_hex:sub(3, 8)
return {
text = tcolor_text,
hex = tcolor_hex,
css = tcolor_css
}
end
function ctf_colors.get_irc_color(tplayer)
local team = ctf.team(tplayer.team)
local tcolor_text = nil
if team then
tcolor_text = team.data.color
end
return ctf_colors.irc_colors[tcolor_text]
end
function ctf_colors.get_nametag_color(name, tplayer, tcolor_text, tcolor_hex)
if ctf.setting("colors.nametag.tcolor") then
return "0xFF" .. string.sub(tcolor_hex, 3)
else
return "0xFFFFFFFF"
end
end
function ctf_colors.set_skin(player, color)
if minetest.global_exists("armor") then
-- TODO: how should support for skin mods be done?
armor.textures[name].skin = "ctf_colors_skin_" .. color .. ".png"
armor:update_player_visuals(player)
else
player:set_properties({
textures = {"ctf_colors_skin_" .. color .. ".png"}
})
end
end
function ctf_colors.update(player, name, tplayer)
if not player then
player = minetest.get_player_by_name(name)
end
local tcolor = ctf_colors.get_color(tplayer)
if ctf.setting("colors.hudtint") then
if tcolor.text == "red" or tcolor.text == "blue" then
player:hud_set_hotbar_image("ctf_colors_hotbar_" .. tcolor.text .. ".png")
player:hud_set_hotbar_selected_image("ctf_colors_hotbar_selected_" .. tcolor.text .. ".png")
else
ctf.error("ctf_colors", "Hint color not supported for " .. tcolor.text)
end
end
if ctf.setting("colors.skins") and tcolor.text then
ctf_colors.set_skin(player, tcolor.text)
end
if ctf.setting("hud.teamname") then
if not ctf.hud:exists(player, "ctf:hud_team") then
ctf.hud:add(player, "ctf:hud_team", {
hud_elem_type = "text",
position = {x = 1, y = 0},
scale = {x = 100, y = 100},
text = "Team " .. tplayer.team,
number = tcolor.hex,
offset = {x = -20, y = 20},
alignment = {x = -1, y = 0}
})
else
ctf.hud:change(player, "ctf:hud_team", "text", "Team " .. tplayer.team)
ctf.hud:change(player, "ctf:hud_team", "number", tcolor.hex)
end
end
end
ctf.hud.register_part(ctf_colors.update)

View file

@ -0,0 +1,31 @@
-- Supported colors
ctf_colors = {}
ctf_colors.colors = {
red = "0xFF4444",
cyan = "0x00FFFF",
blue = "0x4466FF",
purple = "0x800080",
yellow = "0xFFFF00",
green = "0x00FF00",
pink = "0xFF00FF",
silver = "0xC0C0C0",
gray = "0x808080",
black = "0x000000",
orange = "0xFFA500",
gold = "0x808000"
}
ctf_colors.irc_colors = {
red = "4",
blue = "2",
}
ctf.flag_colors = ctf_colors.colors
ctf.register_on_init(function()
ctf.log("colors", "Initialising...")
ctf._set("colors.skins", false)
ctf._set("colors.hudtint", true)
ctf._set("hud.teamname", false)
end)
dofile(minetest.get_modpath("ctf_colors") .. "/hud.lua")
dofile(minetest.get_modpath("ctf_colors") .. "/gui.lua")

Binary file not shown.

After

Width:  |  Height:  |  Size: 545 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 542 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

View file

@ -0,0 +1,242 @@
ctf_flag.registered_on_capture = {}
function ctf_flag.register_on_capture(func)
if ctf._mt_loaded then
error("You can't register callbacks at game time!")
end
table.insert(ctf_flag.registered_on_capture, func)
end
ctf_flag.registered_on_pick_up = {}
function ctf_flag.register_on_pick_up(func)
if ctf._mt_loaded then
error("You can't register callbacks at game time!")
end
table.insert(ctf_flag.registered_on_pick_up, func)
end
ctf_flag.registered_on_drop = {}
function ctf_flag.register_on_drop(func)
if ctf._mt_loaded then
error("You can't register callbacks at game time!")
end
table.insert(ctf_flag.registered_on_drop, func)
end
ctf_flag.registered_on_precapture = {}
function ctf_flag.register_on_precapture(func)
if ctf._mt_loaded then
error("You can't register callbacks at game time!")
end
table.insert(ctf_flag.registered_on_precapture, func)
end
ctf_flag.registered_on_prepick_up = {}
function ctf_flag.register_on_prepick_up(func)
if ctf._mt_loaded then
error("You can't register callbacks at game time!")
end
table.insert(ctf_flag.registered_on_prepick_up, func)
end
function ctf_flag.collect_claimed()
local claimed = {}
for _, team in pairs(ctf.teams) do
for i = 1, #team.flags do
if team.flags[i].claimed then
table.insert(claimed, team.flags[i])
end
end
end
return claimed
end
function ctf_flag.get_claimed_by_player(name)
local claimed = ctf_flag.collect_claimed()
for _, flag in pairs(claimed) do
if flag.claimed.player == name then
return name
end
end
end
function ctf_flag.player_drop_flag(name)
if not name then
return
end
local claimed = ctf_flag.collect_claimed()
for i = 1, #claimed do
local flag = claimed[i]
if flag.claimed.player == name then
flag.claimed = nil
local flag_name = ""
if flag.name then
flag_name = flag.name .. " "
end
flag_name = flag.team .. "'s " .. flag_name .. "flag"
ctf.hud.updateAll()
ctf.action("flag", name .. " dropped " .. flag_name)
minetest.chat_send_all(flag_name.." has returned.")
for i = 1, #ctf_flag.registered_on_drop do
ctf_flag.registered_on_drop[i](name, flag)
end
end
end
end
-- add a flag to a team
function ctf_flag.add(team, pos)
if not team or team == "" then
return
end
ctf.log("flag", "Adding flag to " .. team .. " at (" .. pos.x ..
", " .. pos.y .. ", " .. pos.z .. ")")
if not ctf.team(team).flags then
ctf.team(team).flags = {}
end
pos.team = team
table.insert(ctf.team(team).flags,pos)
ctf.needs_save = true
end
function ctf_flag.update(pos)
if minetest.get_node(pos).name ~= "ctf_flag:flag" then
return
end
local top = {x=pos.x,y=pos.y+1,z=pos.z}
local flagmeta = minetest.get_meta(pos)
if not flagmeta then
return
end
local flag_team_data = ctf_flag.get(pos)
if not flag_team_data or not ctf.team(flag_team_data.team)then
ctf.log("flag", "Flag does not exist! Deleting nodes. "..dump(pos))
minetest.set_node(pos,{name="air"})
minetest.set_node(top,{name="air"})
return
end
local topmeta = minetest.get_meta(top)
local flag_name = flag_team_data.name
if flag_name and flag_name ~= "" then
flagmeta:set_string("infotext", flag_name.." - "..flag_team_data.team)
else
flagmeta:set_string("infotext", flag_team_data.team.."'s flag")
end
if not ctf.team(flag_team_data.team).data.color then
ctf.team(flag_team_data.team).data.color = "red"
ctf.needs_save = true
end
if flag_team_data.claimed then
minetest.set_node(top,{name="ctf_flag:flag_captured_top"})
else
minetest.set_node(top,{name="ctf_flag:flag_top_"..ctf.team(flag_team_data.team).data.color})
end
topmeta = minetest.get_meta(top)
if flag_name and flag_name ~= "" then
topmeta:set_string("infotext", flag_name.." - "..flag_team_data.team)
else
topmeta:set_string("infotext", flag_team_data.team.."'s flag")
end
end
function ctf_flag.flag_tick(pos)
ctf_flag.update(pos)
minetest.get_node_timer(pos):start(5)
end
-- get a flag from a team
function ctf_flag.get(pos)
if not pos then
return
end
local result = nil
for _, team in pairs(ctf.teams) do
for i = 1, #team.flags do
if (
team.flags[i].x == pos.x and
team.flags[i].y == pos.y and
team.flags[i].z == pos.z
) then
if result then
minetest.chat_send_all("[CTF ERROR] Multiple teams have same flag. Please report this to the server operator / admin")
print("CTF ERROR DATA")
print("Multiple teams have same flag.")
print("This is a sign of ctf.txt corruption.")
print("----------------")
print(dump(result))
print(dump(team.flags[i]))
print("----------------")
else
result = team.flags[i]
end
end
end
end
return result
end
-- delete a flag from a team
function ctf_flag.delete(team, pos)
if not team or team == "" then
return
end
ctf.log("flag", "Deleting flag from " .. team .. " at (" .. pos.x ..
", " .. pos.y .. ", " .. pos.z .. ")")
for i = 1, #ctf.team(team).flags do
if (
ctf.team(team).flags[i].x == pos.x and
ctf.team(team).flags[i].y == pos.y and
ctf.team(team).flags[i].z == pos.z
) then
table.remove(ctf.team(team).flags,i)
return
end
end
end
function ctf_flag.assert_flag(flag)
minetest.get_voxel_manip(flag, { x = flag.x + 1, y = flag.y + 1, z = flag.z + 1})
local nodename = minetest.get_node(flag).name
if nodename ~= "ctf_flag:flag" then
ctf.log("flag", flag.team .. " has wrong node at flag position, " .. nodename .. ", correcting...")
minetest.set_node(flag, { name = "ctf_flag:flag"})
ctf_flag.update(flag)
end
end
function ctf_flag.assert_flags()
for tname, team in pairs(ctf.teams) do
ctf_flag.assert_flags_team(tname)
end
end
function ctf_flag.assert_flags_team(tname)
local team = ctf.team(tname)
if not tname or not team then
return false
end
if not team.flags then
team.flags = {}
end
for i=1, #team.flags do
ctf_flag.assert_flag(team.flags[i])
end
end

View file

@ -0,0 +1,3 @@
ctf
ctf_colors
chatplus?

View file

@ -0,0 +1,253 @@
local function do_capture(attname, flag, returned)
local team = flag.team
local attacker = ctf.player(attname)
local flag_name = ""
if flag.name then
flag_name = flag.name .. " "
end
flag_name = team .. "'s " .. flag_name .. "flag"
if ctf.setting("flag.capture_take") and not returned then
for i = 1, #ctf_flag.registered_on_prepick_up do
if not ctf_flag.registered_on_prepick_up[i](attname, flag) then
return
end
end
minetest.chat_send_all(flag_name.." has been picked up by "..
attname.." (team "..attacker.team..")")
ctf.action("flag", attname .. " picked up " .. flag_name)
-- Post to flag owner's board
ctf.post(team, {
msg = flag_name .. " has been taken by " .. attname .. " of ".. attacker.team,
icon="flag_red" })
-- Post to attacker's board
ctf.post(attacker.team, {
msg = attname .. " snatched '" .. flag_name .. "' from " .. team,
icon="flag_green"})
-- Add to claimed list
flag.claimed = {
team = attacker.team,
player = attname
}
ctf.hud.updateAll()
ctf_flag.update(flag)
for i = 1, #ctf_flag.registered_on_pick_up do
ctf_flag.registered_on_pick_up[i](attname, flag)
end
else
for i = 1, #ctf_flag.registered_on_precapture do
if not ctf_flag.registered_on_precapture[i](attname, flag) then
return
end
end
minetest.chat_send_all(flag_name.." has been captured "..
" by "..attname.." (team "..attacker.team..")")
ctf.action("flag", attname .. " captured " .. flag_name)
-- Post to flag owner's board
ctf.post(team, {
msg = flag_name .. " has been captured by " .. attacker.team,
icon="flag_red"})
-- Post to attacker's board
ctf.post(attacker.team, {
msg = attname .. " captured '" .. flag_name .. "' from " .. team,
icon="flag_green"})
-- Take flag
if ctf.setting("flag.allow_multiple") then
ctf_flag.delete(team, vector.new(flag))
ctf_flag.add(attacker.team, vector.new(flag))
else
minetest.set_node(pos,{name="air"})
ctf_flag.delete(team,pos)
end
for i = 1, #ctf_flag.registered_on_capture do
ctf_flag.registered_on_capture[i](attname, flag)
end
end
ctf.needs_save = true
end
local function player_drop_flag(player)
return ctf_flag.player_drop_flag(player:get_player_name())
end
minetest.register_on_dieplayer(player_drop_flag)
minetest.register_on_leaveplayer(player_drop_flag)
ctf_flag = {
on_punch_top = function(pos, node, puncher)
pos.y = pos.y - 1
ctf_flag.on_punch(pos, node, puncher)
end,
on_rightclick_top = function(pos, node, clicker)
pos.y = pos.y - 1
ctf_flag.on_rightclick(pos, node, clicker)
end,
on_rightclick = function(pos, node, clicker)
local name = clicker:get_player_name()
local flag = ctf_flag.get(pos)
if not flag then
return
end
if flag.claimed then
if ctf.setting("flag.capture_take") then
minetest.chat_send_player(name, "This flag has been taken by "..flag.claimed.player)
minetest.chat_send_player(name, "who is a member of team "..flag.claimed.team)
return
else
minetest.chat_send_player(name, "Oops! This flag should not be captured. Reverting...")
flag.claimed = nil
end
end
ctf.gui.flag_board(name, pos)
end,
on_punch = function(pos, node, puncher)
local name = puncher:get_player_name()
if not puncher or not name then
return
end
local flag = ctf_flag.get(pos)
if not flag then
return
end
if flag.claimed then
if ctf.setting("flag.capture_take") then
minetest.chat_send_player(name, "This flag has been taken by " .. flag.claimed.player)
minetest.chat_send_player(name, "who is a member of team " .. flag.claimed.team)
return
else
minetest.chat_send_player(name, "Oops! This flag should not be captured. Reverting.")
flag.claimed = nil
end
end
local team = flag.team
if not team then
return
end
if ctf.team(team) and ctf.player(name).team then
if ctf.player(name).team == team then
-- Clicking on their team's flag
if ctf.setting("flag.capture_take") then
ctf_flag._flagret(name)
end
else
-- Clicked on another team's flag
local diplo = ctf.diplo.get(team, ctf.player(name).team) or
ctf.setting("default_diplo_state")
if diplo ~= "war" then
minetest.chat_send_player(name, "You are at peace with this team!")
return
end
do_capture(name, flag)
end
else
minetest.chat_send_player(name, "You are not part of a team!")
end
end,
_flagret = function(name)
local claimed = ctf_flag.collect_claimed()
for i = 1, #claimed do
local flag = claimed[i]
if flag.claimed.player == name then
do_capture(name, flag, true)
end
end
end,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", "Unowned flag")
minetest.get_node_timer(pos):start(5)
end,
after_place_node = function(pos, placer)
local name = placer:get_player_name()
if not pos or not name then
minetest.set_node(pos, {name="air"})
return
end
local meta = minetest.get_meta(pos)
if not meta then
minetest.set_node(pos, {name="air"})
return
end
local tplayer = ctf.player_or_nil(name)
if tplayer and ctf.team(tplayer.team) then
if not minetest.check_player_privs(name, {ctf_place_flag=true}) then
minetest.chat_send_player(name, "You're not allowed to place flags! Reported to admin for investigation.")
minetest.set_node(pos, {name="air"})
if minetest.global_exists("chatplus") then
chatplus.send_mail("*SERVER*", minetest.settings:get("name"),
"player " .. name .. " attempted to place flag!")
end
return
end
local tname = tplayer.team
local team = ctf.team(tplayer.team)
meta:set_string("infotext", tname.."'s flag")
-- add flag
ctf_flag.add(tname, pos)
-- TODO: fix this hackiness
if team.spawn and not ctf.setting("flag.allow_multiple") and
minetest.get_node(team.spawn).name == "ctf_flag:flag" then
-- send message
minetest.chat_send_all(tname .. "'s flag has been moved")
minetest.set_node(team.spawn, {name="air"})
minetest.set_node({
x = team.spawn.x,
y = team.spawn.y+1,
z = team.spawn.z
}, {name="air"})
team.spawn = pos
end
ctf.needs_save = true
local pos2 = {
x = pos.x,
y = pos.y + 1,
z = pos.z
}
if not team.data.color then
team.data.color = "red"
ctf.needs_save = true
end
minetest.set_node(pos2, {name="ctf_flag:flag_top_"..team.data.color})
local meta2 = minetest.get_meta(pos2)
meta2:set_string("infotext", tname.."'s flag")
else
minetest.chat_send_player(name, "You are not part of a team!")
minetest.set_node(pos, {name="air"})
end
end
}

View file

@ -0,0 +1,90 @@
-- The flag
minetest.register_node("ctf_flag:flag", {
description = "Flag",
drawtype="nodebox",
paramtype = "light",
walkable = false,
inventory_image = "flag_silver2.png",
tiles = {
"default_wood.png",
"default_wood.png",
"default_wood.png",
"default_wood.png",
"default_wood.png",
"default_wood.png"
},
node_box = {
type = "fixed",
fixed = {
{0.250000,-0.500000,0.000000,0.312500,0.500000,0.062500}
}
},
groups = {immortal=1,is_flag=1,flag_bottom=1},
on_punch = ctf_flag.on_punch,
on_rightclick = ctf_flag.on_rightclick,
on_construct = ctf_flag.on_construct,
after_place_node = ctf_flag.after_place_node,
on_timer = ctf_flag.flag_tick
})
for color, _ in pairs(ctf.flag_colors) do
minetest.register_node("ctf_flag:flag_top_"..color,{
description = "You are not meant to have this! - flag top",
drawtype="nodebox",
paramtype = "light",
walkable = false,
tiles = {
"default_wood.png",
"default_wood.png",
"default_wood.png",
"default_wood.png",
"flag_"..color.."2.png",
"flag_"..color..".png"
},
node_box = {
type = "fixed",
fixed = {
{0.250000,-0.500000,0.000000,0.312500,0.500000,0.062500},
{-0.5,0,0.000000,0.250000,0.500000,0.062500}
}
},
groups = {immortal=1,is_flag=1,flag_top=1,not_in_creative_inventory=1},
on_punch = ctf_flag.on_punch_top,
on_rightclick = ctf_flag.on_rightclick_top
})
end
minetest.register_node("ctf_flag:flag_captured_top",{
description = "You are not meant to have this! - flag captured",
drawtype = "nodebox",
paramtype = "light",
walkable = false,
tiles = {
"default_wood.png",
"default_wood.png",
"default_wood.png",
"default_wood.png",
"default_wood.png",
"default_wood.png"
},
node_box = {
type = "fixed",
fixed = {
{0.250000,-0.500000,0.000000,0.312500,0.500000,0.062500}
}
},
groups = {immortal=1,is_flag=1,flag_top=1,not_in_creative_inventory=1},
on_punch = ctf_flag.on_punch_top,
on_rightclick = ctf_flag.on_rightclick_top
})
if ctf.setting("flag.crafting") then
minetest.register_craft({
output = "ctf_flag:flag",
recipe = {
{"default:stick", "group:wool"},
{"default:stick", "",},
{"default:stick", ""}
}
})
end

View file

@ -0,0 +1,184 @@
-- Team interface
ctf.gui.register_tab("flags", "Flags", function(name, team)
local result = ""
local t = ctf.team(team)
if not t then
return
end
local x = 1
local y = 2
result = result .. "label[1,1;Click a flag button to go there]"
if ctf.setting("gui.team.teleport_to_spawn") and minetest.get_setting("static_spawnpoint") then
local x,y,z = string.match(minetest.get_setting("static_spawnpoint"), "(%d+),(%d+),(%d+)")
result = result ..
"button[" .. x .. "," .. y .. ";2,1;goto_"
..f.x.."_"..f.y.."_"..f.z..";"
result = result .. "Spawn]"
x = x + 2
end
for i=1, #t.flags do
local f = t.flags[i]
if x > 8 then
x = 1
y = y + 1
end
if y > 6 then
break
end
result = result ..
"button[" .. x .. "," .. y .. ";2,1;goto_"
..f.x.."_"..f.y.."_"..f.z..";"
if f.name then
result = result .. f.name .. "]"
else
result = result .. "("..f.x..","..f.y..","..f.z..")]"
end
x = x + 2
end
minetest.show_formspec(name, "ctf:flags",
"size[10,7]"..
ctf.gui.get_tabs(name,team)..
result)
end)
minetest.register_on_player_receive_fields(function(player, formname, fields)
-- Todo: fix security issue here
-- local name = player:get_player_name()
-- if formname == "ctf:flags" then
-- for key, field in pairs(fields) do
-- local x,y,z = string.match(key, "goto_([%d-]+)_([%d-]+)_([%d-]+)")
-- if x and y and z then
-- player:setpos({ x=tonumber(x), y=tonumber(y), z=tonumber(z) })
-- return true
-- end
-- end
-- end
end)
-- Flag interface
function ctf.gui.flag_board(name, pos)
local flag = ctf_flag.get(pos)
if not flag then
return
end
local team = flag.team
if not team then
return
end
if not ctf.can_mod(name, team) then
if ctf.player(name).team and ctf.player(name).team == team then
ctf.gui.show(name)
end
return
end
ctf.log("gui", name .. " views flag board")
local flag_name = flag.name
if not ctf.setting("flag.names") then
flag.name = nil
return
end
if not ctf.setting("gui") then
return
end
if not flag_name then
flag_name = ""
end
if not ctf.gui.flag_data then
ctf.gui.flag_data = {}
end
ctf.gui.flag_data[name] = {pos=pos}
minetest.show_formspec(name, "ctf:flag_board",
"size[6,3]"..
"field[1,1;4,1;flag_name;Flag Name;"..flag_name.."]"..
"button_exit[1,2;2,1;save;Save]"..
"button_exit[3,2;2,1;delete;Delete]"
)
end
minetest.register_on_player_receive_fields(function(player, formname, fields)
local name = player:get_player_name()
if not formname=="ctf:flag_board" then
return false
end
if fields.save and fields.flag_name then
local flag = ctf_flag.get(ctf.gui.flag_data[name].pos)
if not flag then
return false
end
local team = flag.team
if not team then
return false
end
if ctf.can_mod(name,team) == false then
return false
end
local flag_name = flag.name
if not flag_name then
flag_name = ""
end
flag.name = fields.flag_name
local msg = flag_name.." was renamed to "..fields.flag_name
if flag_name=="" then
msg = "A flag was named "..fields.flag_name.." at ("..ctf.gui.flag_data[name].pos.x..","..ctf.gui.flag_data[name].pos.z..")"
end
ctf.post(team,{msg=msg,icon="flag_info"})
return true
elseif fields.delete then
local pos = ctf.gui.flag_data[name].pos
local flag = ctf_flag.get(ctf.gui.flag_data[name].pos)
if not flag then
return
end
local team = flag.team
if not team then
return
end
if ctf.can_mod(name,team) == false then
return false
end
ctf_flag.delete(team,pos)
minetest.set_node(pos,{name="air"})
pos.y=pos.y+1
minetest.set_node(pos,{name="air"})
player:get_inventory():add_item("main", "ctf_flag:flag")
return true
end
end)

View file

@ -0,0 +1,102 @@
-- TODO: delete flags if they are removed (ctf.next, or captured)
ctf.hud.register_part(function(player, name, tplayer)
if ctf.setting("flag.waypoints") then
for tname, team in pairs(ctf.teams) do
for _, flag in pairs(team.flags) do
local hud = "ctf:hud_" .. tname
local flag_name = flag.name or tname .. "'s base"
local color = ctf.flag_colors[team.data.color]
if not color then
color = "0x000000"
end
if ctf.hud:exists(player, hud) then
ctf.hud:change(player, hud, "world_pos", {
x = flag.x,
y = flag.y,
z = flag.z
})
else
ctf.hud:add(player, hud, {
hud_elem_type = "waypoint",
name = flag_name,
number = color,
world_pos = {
x = flag.x,
y = flag.y,
z = flag.z
}
})
end
end
end
end
end)
ctf.hud.register_part(function(player, name, tplayer)
-- Check all flags
local alert = nil
local color = "0xFFFFFF"
if ctf.setting("flag.alerts") then
if ctf.setting("flag.alerts.neutral_alert") then
alert = "Punch the enemy flag! Protect your flag!"
end
local claimed = ctf_flag.collect_claimed()
local enemyHolder = nil
local teamHolder = nil
for _, flag in pairs(claimed) do
if flag.team == tplayer.team then
enemyHolder = flag.claimed.player
else
teamHolder = flag.claimed.player
end
end
if teamHolder == name then
if enemyHolder then
alert = "You can't capture the flag until " .. enemyHolder .. " is killed!"
color = "0xFF0000"
else
alert = "You've got the flag! Run back and punch your flag!"
color = "0xFF0000"
end
elseif teamHolder then
if enemyHolder then
alert = "Kill " .. enemyHolder .. " to allow " .. teamHolder .. " to capture the flag!"
color = "0xFF0000"
else
alert = "Protect " .. teamHolder .. ", they've got the enemy flag!"
color = "0xFF0000"
end
elseif enemyHolder then
alert = "Kill " .. enemyHolder .. ", they've got your flag!"
color = "0xFF0000"
end
end
-- Display alert
if alert then
if ctf.hud:exists(player, "ctf:hud_team_alert") then
ctf.hud:change(player, "ctf:hud_team_alert", "text", alert)
ctf.hud:change(player, "ctf:hud_team_alert", "number", color)
else
local y
if ctf.setting("hud.teamname") then
y = 50
else
y = 20
end
ctf.hud:add(player, "ctf:hud_team_alert", {
hud_elem_type = "text",
position = {x = 1, y = 0},
scale = {x = 100, y = 100},
text = alert,
number = color,
offset = {x = -10, y = y},
alignment = {x = -1, y = 0}
})
end
else
ctf.hud:remove(player, "ctf:hud_team_alert")
end
end)

View file

@ -0,0 +1,146 @@
-- Initialise
ctf.register_on_init(function()
ctf.log("flag", "Initialising...")
ctf._set("flag.allow_multiple", true)
ctf._set("flag.capture_take", false)
ctf._set("flag.names", true)
ctf._set("flag.waypoints", true)
ctf._set("flag.protect_distance", 25)
ctf._set("flag.nobuild_radius", 3)
ctf._set("flag.drop_time", 7*60)
ctf._set("flag.drop_warn_time", 60)
ctf._set("flag.crafting", false)
ctf._set("flag.alerts", true)
ctf._set("flag.alerts.neutral_alert", true)
ctf._set("gui.team.teleport_to_flag", true)
ctf._set("gui.team.teleport_to_spawn", false)
end)
minetest.register_privilege("ctf_place_flag", {
description = "can place flag"
})
dofile(minetest.get_modpath("ctf_flag") .. "/hud.lua")
dofile(minetest.get_modpath("ctf_flag") .. "/gui.lua")
dofile(minetest.get_modpath("ctf_flag") .. "/flag_func.lua")
dofile(minetest.get_modpath("ctf_flag") .. "/api.lua")
dofile(minetest.get_modpath("ctf_flag") .. "/flags.lua")
ctf.register_on_new_team(function(team)
team.flags = {}
end)
function ctf_flag.get_nearest(pos)
local closest = nil
local closest_distSQ = 1000000
local pd = ctf.setting("flag.protect_distance")
local pdSQ = pd * pd
for tname, team in pairs(ctf.teams) do
for i = 1, #team.flags do
local distSQ = vector.distanceSQ(pos, team.flags[i])
if distSQ < pdSQ and distSQ < closest_distSQ then
closest = team.flags[i]
closest_distSQ = distSQ
end
end
end
return closest, closest_distSQ
end
function ctf_flag.get_nearest_team_dist(pos)
local flag, distSQ = ctf_flag.get_nearest(pos)
if flag then
return flag.team, distSQ
end
end
ctf.register_on_territory_query(ctf_flag.get_nearest_team_dist)
function ctf.get_spawn(team)
if not ctf.team(team) then
return nil
end
if ctf.team(team).spawn then
return ctf.team(team).spawn
end
-- Get spawn from first flag
ctf_flag.assert_flags(team)
if #ctf.team(team).flags > 0 then
return ctf.team(team).flags[1]
else
return nil
end
end
-- Add minimum build range
local old_is_protected = minetest.is_protected
local r = ctf.setting("flag.nobuild_radius")
local rs = r * r
function minetest.is_protected(pos, name)
if r <= 0 or rs == 0 then
return old_is_protected(pos, name)
end
local flag, distSQ = ctf_flag.get_nearest(pos)
if flag and pos.y >= flag.y - 1 and distSQ < rs then
minetest.chat_send_player(name,
"Too close to the flag to build! Leave at least " .. r .. " blocks around the flag.")
return true
else
return old_is_protected(pos, name)
end
end
-- Play sound
ctf_flag.register_on_pick_up(function(attname, flag)
local vteam = ctf.team(flag.team)
for name, player in pairs(vteam.players) do
minetest.sound_play({name="trumpet_lose"}, {
to_player = name,
gain = 1.0, -- default
})
end
local ateam = ctf.team(ctf.player(attname).team)
for name, player in pairs(ateam.players) do
minetest.sound_play({name="trumpet_win"}, {
to_player = name,
gain = 1.0, -- default
})
end
end)
-- Drop after time
local pickup_times = {}
ctf_flag.register_on_pick_up(function(attname, flag)
pickup_times[attname] = minetest.get_gametime()
end)
ctf_flag.register_on_drop(function(attname, flag)
pickup_times[attname] = nil
end)
ctf_flag.register_on_capture(function(attname, flag)
pickup_times[attname] = nil
end)
ctf.register_on_new_game(function()
pickup_times = {}
end)
local function update_flag_drops()
local time = minetest.get_gametime()
local drop_time = ctf.setting("flag.drop_time")
for name, start in pairs(pickup_times) do
local remaining = drop_time - time + start
if remaining < 0 then
ctf_flag.player_drop_flag(name)
minetest.chat_send_player(name, "You took too long to capture the flag, so it returned!")
elseif remaining < ctf.setting("flag.drop_warn_time") then
minetest.chat_send_player(name, "You have " .. remaining ..
" seconds to capture the flag before it returns.")
end
end
minetest.after(5, update_flag_drops)
end
minetest.after(5, update_flag_drops)

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 601 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 598 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 776 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 732 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 666 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 676 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 618 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 625 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 549 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 571 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 826 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 775 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 632 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 635 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 690 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 688 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 616 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 812 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 874 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 567 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 657 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 671 B

View file

@ -0,0 +1 @@
ctf

View file

@ -0,0 +1,28 @@
-- This mod is used to protect nodes in the capture the flag game
ctf.register_on_init(function()
ctf.log("chat", "Initialising...")
-- Settings: Chat
ctf._set("node_ownership", true)
end)
local old_is_protected = minetest.is_protected
function minetest.is_protected(pos, name)
if not ctf.setting("node_ownership") then
return old_is_protected(pos, name)
end
local team = ctf.get_territory_owner(pos)
if not team or not ctf.team(team) then
return old_is_protected(pos, name)
end
if ctf.player(name).team == team then
return old_is_protected(pos, name)
else
minetest.chat_send_player(name, "You cannot dig on team "..team.."'s land")
return true
end
end

View file

@ -0,0 +1,80 @@
# Data Formats
This file documents the contents of ctf.txt
Values are added to the file using ctf.register_on_save and ctf.register_on_load.
Here are the default values:
```lua
{
players = ctf.players,
teams = ctf.teams,
diplo = ctf.diplo.diplo
}
```
## Players
Commonly called tplayer (may be called data or player in old code).
Player name is commonly called name (but may be called other things in older code).
```lua
ctf.players = {
username = (player_table)
}
(player_table) = {
name = "username",
team = "teamname",
auth = false
-- true if the player is a team admin. Team admins can change team settings.
-- See ctf.can_mod()
-- Note that priv:ctf_admin can also change team settings
}
```
## Teams
Commonly called team.
Team name is commonly called tname (but may be called team in old code).
```lua
ctf.teams = {
teamname = (team_table)
}
(team_table) = {
data = {
name = "teamname",
color = "teamcolor" -- see ctf_colors
},
flags = {
(flag_table), (flag_table)
},
players = {
username1 = (player_table),
username2 = (player_table)
},
spawn = { x=0, y=0, z=0 }
-- fallback team spawn. Read by ctf.get_spawn() and overriding functions
-- Don't use directly, instead call ctf.get_spawn("teamname")
}
(flag_table) = {
x=0, y=0, z=0,
flag_name = "Capital" -- human readable name
}
```
## Diplomacy
```lua
ctf.diplo.diplo = {
(diplo_table), (diplo_table)
}
(diplo_table) = {
one = "teamname1",
two = "teamname2",
state = "war" / "peace" / "alliance"
}
```

View file

@ -0,0 +1,70 @@
# Welcome
The aim of CTF_PvP_Engine is to provide a base to any subgame which uses the
concepts of teams. Flags are a plugin mod, so it isn't CTF as such.
# Modules in CTF_PvP_Engine
## hudkit
A support library to make the HUD API nicer.
WTFPL.
## ctf
Requires hudkit. Support for chatplus.
Core framework, players, teams, diplomacy, hud and gui.
* core - adds saving, loading and settings. All modules depend on this.
* teams - add the concepts of teams and players. All modules except core depend on this.
* diplomacy - adds inter team states of war, peace and alliances.
* gui - adds the team gui on /team. Allows tabs to be registered.
* hud - adds the name of the team in the TR of the screen, and sets the color
## ctf_chat
Requires ctf. Support for chatplus.
Chat commands and chat channels.
## ctf_colors
Requires ctf. Support for 3d_armor.
Adds player colors.
* gui - settings form
* hud - team name color, player skin color, nametag color
* init - table of colors
## ctf_flag
Requires ctf and ctf_colors. Support for chatplus.
Adds flags and flag taking.
* api - flag callbacks, flag management (adding, capturing, updating), flag checking (asserts)
* flag_func - functions for flag node definitions.
* flags - flag node definitions.
* gui - flag naming GUI, flag teleport GUI.
* hud - waypoints, alerts ("Punch the enemy flag!" etc in top right)
* init - get nearest flag, overrides ctf.get_spawn(), minimum build range, pick up sound, flag capture timeout.
## ctf_protect
Adds node ownership / protection to teams.
Requires ctf_flag.
# Past/Other Mods
Please look
## ctf_turret
Adds auto-firing turrets that fire on enemies.
See git history.
## Capture the flag
more mods available in [capture the flag](http://github.com/rubenwardy/capturetheflag/).
* ctf_match - adds the concept of winning, match build time,
and reseting the map / setting up a new game.
Requires ctf_flag

View file

@ -0,0 +1,60 @@
# ctf
| name | default value | description |
| -------------------------- | ------------- | ---------------------------------------------------------------- |
| allocate_mode | 0 | 0=off, 1=firstnonfullteam, 2=RandomOfSmallestTwo, 3=SmallestTeam |
| autoalloc_on_joinplayer | true | Trigger auto alloc on join player |
| default_diplo_state | "war" | "war", "alliance" or "peace" |
| diplomacy | true | Is diplomacy enabled |
| friendly_fire | true | True if players can't hit other players on their team |
| maximum_in_team | -1 | Player cap |
| players_can_change_team | true | |
| hud | true | Enable HUD |
| gui | true | Enable GUI |
| gui.team | true | Whether to show team gui (/team) |
| gui.team.initial | "news" | Initial tab |
| gui.tab.diplo | true | Show diplo tab |
| gui.tab.news | true | Show news tab |
| gui.tab.settings | true | Show settings tab |
| spawn_offset | {x=0, y=0, z=0} | Offset of static spawn-point from team-base |
# ctf_chat
| name | default value | description |
| -------------------------- | ------------- | ---------------------------------------------------------------- |
| chat.default | "global" | "global" or "team" |
| chat.global_channel | true | |
| chat.team_channel | true | |
# ctf_colors
| name | default value | description |
| -------------------------- | ------------- | ---------------------------------------------------------------- |
| colors.nametag | true | Whether to colour the name tagColour nametag |
| colors.nametag.tcolor | false | Base nametag colour on team colour |
| colors.skins | false | Team skins are coloured |
# ctf_flag
| name | default value | description |
| -------------------------- | ------------- | ---------------------------------------------------------------- |
| flag.alerts | true | Prompts like "X has captured your flag" |
| flag.alerts.neutral_alert | true | Show prompt in neutral state, ie: "attack and defend!" |
| flag.allow_multiple | true | Teams can have multiple flags |
| flag.capture_take | false | Whether a player needs to return flag to base to capture |
| flag.drop_time | 420 | Time in seconds before a player drops the flag they're holding |
| flag.drop_warn_time | 60 | Warning time before drop |
| flag.nobuild_radius | 3 | Area around flag where you can't build |
| flag.names | true | Enable naming flags |
| flag.protect_distance | 25 | Area protection distance |
| flag.waypoints | true | Enable waypoints to flags |
| flag.crafting | false | Enable the crafting of flags |
| gui.tab.flags | true | Show flags tab |
| gui.team.teleport_to_flag | true | Enable teleport to flag button in flags tab |
| gui.team.teleport_to_spawn | false | Enable teleport to spawn button in flags tab |
# ctf_protect
| name | default value | description |
| -------------------------- | ------------- | ---------------------------------------------------------------- |
| node_ownership | true | Whether node protection per team is enabled |

View file

@ -0,0 +1,67 @@
function hudkit()
return {
players = {},
add = function(self, player, id, def)
local name = player:get_player_name()
local elements = self.players[name]
if not elements then
self.players[name] = {}
elements = self.players[name]
end
elements[id] = {
id = player:hud_add(def),
def = def
}
return true
end,
exists = function(self, player, id)
if not player then
return false
end
local name = player:get_player_name()
local elements = self.players[name]
if not elements or not elements[id] then
return false
end
return true
end,
change = function(self, player, id, stat, value)
if not player then
return false
end
local name = player:get_player_name()
local elements = self.players[name]
if not elements or not elements[id] or not elements[id].id then
return false
end
if elements[id].def[stat] ~= value then
elements[id].def[stat] = value
player:hud_change(elements[id].id, stat, value)
end
return true
end,
remove = function(self, player, id)
local name = player:get_player_name()
local elements = self.players[name]
if not elements or not elements[id] or not elements[id].id then
return false
end
player:hud_remove(elements[id].id)
elements[id] = nil
return true
end
}
end

View file

@ -0,0 +1,80 @@
# Created by https://www.gitignore.io/api/lin,linux,windows,lua
#!! ERROR: lin is undefined. Use list command to see defined gitignore types !!#
### Linux ###
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
### Windows ###
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk
### Lua ###
# Compiled Lua sources
luac.out
# luarocks build files
*.src.rock
*.zip
*.tar.gz
# Object files
*.o
*.os
*.ko
*.obj
*.elf
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
*.def
*.exp
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex

View file

@ -0,0 +1,19 @@
Copyright (c) 2016-17 rubenwardy
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,89 @@
# ChatCmdBuilder
Easily create complex chat commands with no regex.
Created by rubenwardy
License: MIT
# Usage
## Registering Chat Commands
`ChatCmdBuilder.new(name, setup)` registers a new chat command called `name`.
Setup is called immediately after calling `new` to initialise subcommands.
You can set values in the chat command definition by using def:
`ChatCmdBuilder.new(name, setup, def)`.
Here is an example:
```Lua
ChatCmdBuilder.new("admin", function(cmd)
cmd:sub("kill :target", function(name, target)
local player = minetest.get_player_by_name(target)
if player then
player:set_hp(0)
return true, "Killed " .. target
else
return false, "Unable to find " .. target
end
end)
cmd:sub("move :target to :pos:pos", function(name, target, pos)
local player = minetest.get_player_by_name(target)
if player then
player:setpos(pos)
return true, "Moved " .. target .. " to " .. minetest.pos_to_string(pos)
else
return false, "Unable to find " .. target
end
end)
end, {
description = "Admin tools",
privs = {
kick = true,
ban = true
}
})
```
A player could then do `/admin kill player1` to kill player1,
or `/admin move player1 to 0,0,0` to teleport a user.
## Introduction to Routing
A route is a string. Let's look at `move :target to :pos:pos`:
* `move` and `to` are constants. They need to be there in order to match.
* `:target` and `:pos:pos` are parameters. They're passed to the function.
* The second `pos` in `:pos:pos` after `:` is the param type. `:target` has an implicit
type of `word`.
## Param Types
* `word` - default. Any string without spaces.
* `number` - Any number, including decimals
* `int` - Any integer, no decimals
* `text` - Any string
* `pos` - 1,2,3 or 1.1,2,3.4567 or (1,2,3) or 1.2, 2 ,3.2
## Build chat command function
If you don't want to register the chatcommand at this point, you can just generate
a function using `ChatCmdBuilder.build`.
For example, this is the full definition of ChatCmdBuilder.new:
```Lua
function ChatCmdBuilder.new(name, func, def)
def = def or {}
def.func = ChatCmdBuilder.build(name, func)
minetest.register_chatcommand(name, def)
end
```
## Run tests
```Bash
sudo apt-get install luajit
luajit init.lua
```

View file

@ -0,0 +1 @@
A library to make registering chat commands easier

View file

@ -0,0 +1,230 @@
ChatCmdBuilder = {}
function ChatCmdBuilder.new(name, func, def)
def = def or {}
local cmd = ChatCmdBuilder.build(func)
cmd.def = def
def.func = cmd.run
minetest.register_chatcommand(name, def)
return cmd
end
local STATE_READY = 1
local STATE_PARAM = 2
local STATE_PARAM_TYPE = 3
local bad_chars = {}
bad_chars["("] = true
bad_chars[")"] = true
bad_chars["."] = true
bad_chars["%"] = true
bad_chars["+"] = true
bad_chars["-"] = true
bad_chars["*"] = true
bad_chars["?"] = true
bad_chars["["] = true
bad_chars["^"] = true
bad_chars["$"] = true
local function escape(char)
if bad_chars[char] then
return "%" .. char
else
return char
end
end
function ChatCmdBuilder.build(func)
local cmd = {
_subs = {}
}
function cmd:sub(route, func, def)
print("Parsing " .. route)
def = def or {}
if string.trim then
route = string.trim(route)
end
local sub = {
pattern = "^",
params = {},
func = func
}
-- End of param reached: add it to the pattern
local param = ""
local param_type = ""
local should_be_eos = false
local function finishParam()
if param ~= "" and param_type ~= "" then
print(" - Found param " .. param .. " type " .. param_type)
if param_type == "pos" then
sub.pattern = sub.pattern .. "%(? *(%-?[%d.]+) *, *(%-?[%d.]+) *, *(%-?[%d.]+) *%)?"
elseif param_type == "text" then
sub.pattern = sub.pattern .. "(*+)"
should_be_eos = true
elseif param_type == "number" then
sub.pattern = sub.pattern .. "([%d.]+)"
elseif param_type == "int" then
sub.pattern = sub.pattern .. "([%d]+)"
else
if param_type ~= "word" then
print("Unrecognised param_type=" .. param_type .. ", using 'word' type instead")
param_type = "word"
end
sub.pattern = sub.pattern .. "([^ ]+)"
end
table.insert(sub.params, param_type)
param = ""
param_type = ""
end
end
-- Iterate through the route to find params
local state = STATE_READY
for i = 1, #route do
local c = route:sub(i, i)
if should_be_eos then
error("Should be end of string. Nothing is allowed after a param of type text.")
end
if state == STATE_READY then
if c == ":" then
print(" - Found :, entering param")
state = STATE_PARAM
param_type = "word"
else
sub.pattern = sub.pattern .. escape(c)
end
elseif state == STATE_PARAM then
if c == ":" then
print(" - Found :, entering param type")
state = STATE_PARAM_TYPE
param_type = ""
elseif c:match("%W") then
print(" - Found nonalphanum, leaving param")
state = STATE_READY
finishParam()
sub.pattern = sub.pattern .. escape(c)
else
param = param .. c
end
elseif state == STATE_PARAM_TYPE then
if c:match("%W") then
print(" - Found nonalphanum, leaving param type")
state = STATE_READY
finishParam()
sub.pattern = sub.pattern .. escape(c)
else
param_type = param_type .. c
end
end
end
print(" - End of route")
finishParam()
sub.pattern = sub.pattern .. "$"
print("Pattern: " .. sub.pattern)
table.insert(self._subs, sub)
end
if func then
func(cmd)
end
cmd.run = function(name, param)
for i = 1, #cmd._subs do
local sub = cmd._subs[i]
local res = { string.match(param, sub.pattern) }
if #res > 0 then
local pointer = 1
local params = { name }
for j = 1, #sub.params do
local param = sub.params[j]
if param == "pos" then
local pos = {
x = tonumber(res[pointer]),
y = tonumber(res[pointer + 1]),
z = tonumber(res[pointer + 2])
}
table.insert(params, pos)
pointer = pointer + 3
elseif param == "number" or param == "int" then
table.insert(params, tonumber(res[pointer]))
pointer = pointer + 1
else
table.insert(params, res[pointer])
pointer = pointer + 1
end
end
return sub.func(unpack(params))
end
end
print("No matches")
end
return cmd
end
local function run_tests()
if not (ChatCmdBuilder.build(function(cmd)
cmd:sub("bar :one and :two:word", function(name, one, two)
if name == "singleplayer" and one == "abc" and two == "def" then
return true
end
end)
end))("singleplayer", "bar abc and def") then
error("Test 1 failed")
end
local move = ChatCmdBuilder.build(function(cmd)
cmd:sub("move :target to :pos:pos", function(name, target, pos)
if name == "singleplayer" and target == "player1" and
pos.x == 0 and pos.y == 1 and pos.z == 2 then
return true
end
end)
end)
if not move("singleplayer", "move player1 to 0,1,2") then
error("Test 2 failed")
end
if not move("singleplayer", "move player1 to (0,1,2)") then
error("Test 3 failed")
end
if not move("singleplayer", "move player1 to 0, 1,2") then
error("Test 4 failed")
end
if not move("singleplayer", "move player1 to 0 ,1, 2") then
error("Test 5 failed")
end
if not move("singleplayer", "move player1 to 0, 1, 2") then
error("Test 6 failed")
end
if not move("singleplayer", "move player1 to 0 ,1 ,2") then
error("Test 7 failed")
end
if not move("singleplayer", "move player1 to ( 0 ,1 ,2)") then
error("Test 7 failed")
end
if move("singleplayer", "move player1 to abc,def,sdosd") then
error("Test 8 failed")
end
if move("singleplayer", "move player1 to abc def sdosd") then
error("Test 8 failed")
end
if not (ChatCmdBuilder.build(function(cmd)
cmd:sub("does :one:int plus :two:int equal :three:int", function(name, one, two, three)
if name == "singleplayer" and one + two == three then
return true
end
end)
end))("singleplayer", "does 1 plus 2 equal 3") then
error("Test 9 failed")
end
end
if not minetest then
run_tests()
end

View file

@ -0,0 +1,7 @@
name = lib_chatcmdbuilder
title = Chat Command Builder
author = rubenwardy
description = A library to make registering chat commands easier
license = MIT
forum = https://forum.minetest.net/viewtopic.php?t=14899
version = 0.1.0

View file