Add ctf_pvp_engine mods directly in the repo
3
.gitmodules
vendored
|
@ -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"]
|
[submodule "mods/crafting"]
|
||||||
path = mods/crafting
|
path = mods/crafting
|
||||||
url = https://github.com/rubenwardy/crafting
|
url = https://github.com/rubenwardy/crafting
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit ef6e358de558617181c5acf52299212c8fcb9bce
|
|
215
mods/ctf_pvp_engine/.gitignore
vendored
Normal 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
|
23
mods/ctf_pvp_engine/README.md
Normal 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
|
269
mods/ctf_pvp_engine/ctf/core.lua
Normal file
|
@ -0,0 +1,269 @@
|
||||||
|
-- Awaiting core support.
|
||||||
|
local function __genOrderedIndex( t )
|
||||||
|
local orderedIndex = {}
|
||||||
|
for key in pairs(t) do
|
||||||
|
table.insert( orderedIndex, key )
|
||||||
|
end
|
||||||
|
table.sort( orderedIndex )
|
||||||
|
return orderedIndex
|
||||||
|
end
|
||||||
|
|
||||||
|
local function orderedNext(t, state)
|
||||||
|
-- Equivalent of the next function, but returns the keys in the alphabetic
|
||||||
|
-- order. We use a temporary ordered key table that is stored in the
|
||||||
|
-- table being iterated.
|
||||||
|
|
||||||
|
local key = nil
|
||||||
|
if state == nil then
|
||||||
|
t.__orderedIndex = __genOrderedIndex( t )
|
||||||
|
key = t.__orderedIndex[1]
|
||||||
|
else
|
||||||
|
for i = 1,table.getn(t.__orderedIndex) do
|
||||||
|
if t.__orderedIndex[i] == state then
|
||||||
|
key = t.__orderedIndex[i+1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if key then
|
||||||
|
return key, t[key]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- no more value to return, cleanup
|
||||||
|
t.__orderedIndex = nil
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
function orderedPairs(t)
|
||||||
|
-- Equivalent of the pairs() function on tables. Allows to iterate
|
||||||
|
-- in order
|
||||||
|
return orderedNext, t, nil
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- Registered
|
||||||
|
ctf.registered_on_load = {}
|
||||||
|
function ctf.register_on_load(func)
|
||||||
|
if ctf._mt_loaded then
|
||||||
|
error("You can't register callbacks at game time!")
|
||||||
|
end
|
||||||
|
table.insert(ctf.registered_on_load, func)
|
||||||
|
if ctf._loaddata then
|
||||||
|
func(ctf._loaddata)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
ctf.registered_on_save = {}
|
||||||
|
function ctf.register_on_save(func)
|
||||||
|
if ctf._mt_loaded then
|
||||||
|
error("You can't register callbacks at game time!")
|
||||||
|
end
|
||||||
|
table.insert(ctf.registered_on_save, func)
|
||||||
|
end
|
||||||
|
ctf.registered_on_init = {}
|
||||||
|
function ctf.register_on_init(func)
|
||||||
|
if ctf._mt_loaded then
|
||||||
|
error("You can't register callbacks at game time!")
|
||||||
|
end
|
||||||
|
table.insert(ctf.registered_on_init, func)
|
||||||
|
if ctf._inited then
|
||||||
|
func()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
ctf.registered_on_new_team = {}
|
||||||
|
function ctf.register_on_new_team(func)
|
||||||
|
if ctf._mt_loaded then
|
||||||
|
error("You can't register callbacks at game time!")
|
||||||
|
end
|
||||||
|
table.insert(ctf.registered_on_new_team, func)
|
||||||
|
end
|
||||||
|
ctf.registered_on_territory_query = {}
|
||||||
|
function ctf.register_on_territory_query(func)
|
||||||
|
if ctf._mt_loaded then
|
||||||
|
error("You can't register callbacks at game time!")
|
||||||
|
end
|
||||||
|
table.insert(ctf.registered_on_territory_query, func)
|
||||||
|
end
|
||||||
|
ctf.registered_on_new_game = {}
|
||||||
|
function ctf.register_on_new_game(func)
|
||||||
|
if ctf._mt_loaded then
|
||||||
|
error("You can't register callbacks at game time!")
|
||||||
|
end
|
||||||
|
table.insert(ctf.registered_on_new_game, func)
|
||||||
|
if ctf._new_game then
|
||||||
|
func()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function vector.distanceSQ(p1, p2)
|
||||||
|
local x = p1.x - p2.x
|
||||||
|
local y = p1.y - p2.y
|
||||||
|
local z = p1.z - p2.z
|
||||||
|
return x*x + y*y + z*z
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- Debug helpers
|
||||||
|
function ctf.error(area, msg)
|
||||||
|
minetest.log("error", "CTF::" .. area .. " - " ..msg)
|
||||||
|
end
|
||||||
|
function ctf.log(area, msg)
|
||||||
|
if area and area ~= "" then
|
||||||
|
print("[CaptureTheFlag] (" .. area .. ") " .. msg)
|
||||||
|
else
|
||||||
|
print("[CaptureTheFlag] " .. msg)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function ctf.action(area, msg)
|
||||||
|
if area and area ~= "" then
|
||||||
|
minetest.log("action", "[CaptureTheFlag] (" .. area .. ") " .. msg)
|
||||||
|
else
|
||||||
|
minetest.log("action", "[CaptureTheFlag] " .. msg)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function ctf.warning(area, msg)
|
||||||
|
print("WARNING: [CaptureTheFlag] (" .. area .. ") " .. msg)
|
||||||
|
end
|
||||||
|
|
||||||
|
function ctf.init()
|
||||||
|
ctf._inited = true
|
||||||
|
ctf.log("init", "Initialising!")
|
||||||
|
|
||||||
|
-- Set up structures
|
||||||
|
ctf._defsettings = {}
|
||||||
|
ctf.teams = {}
|
||||||
|
ctf.players = {}
|
||||||
|
|
||||||
|
-- See minetest.conf.example in the root of this subgame
|
||||||
|
|
||||||
|
ctf.log("init", "Creating Default Settings")
|
||||||
|
ctf._set("diplomacy", true)
|
||||||
|
ctf._set("players_can_change_team", true)
|
||||||
|
ctf._set("allocate_mode", 0)
|
||||||
|
ctf._set("maximum_in_team", -1)
|
||||||
|
ctf._set("default_diplo_state", "war")
|
||||||
|
ctf._set("hud", true)
|
||||||
|
ctf._set("autoalloc_on_joinplayer", true)
|
||||||
|
ctf._set("friendly_fire", true)
|
||||||
|
ctf._set("spawn_offset", "0,0,0")
|
||||||
|
|
||||||
|
|
||||||
|
for i = 1, #ctf.registered_on_init do
|
||||||
|
ctf.registered_on_init[i]()
|
||||||
|
end
|
||||||
|
|
||||||
|
ctf.load()
|
||||||
|
end
|
||||||
|
|
||||||
|
function ctf.reset()
|
||||||
|
ctf.log("io", "Deleting CTF save data...")
|
||||||
|
os.remove(minetest.get_worldpath().."/ctf.txt")
|
||||||
|
ctf.player_last_team = {}
|
||||||
|
ctf.init()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set default setting value
|
||||||
|
function ctf._set(setting, default)
|
||||||
|
if ctf._defsettings[setting] then
|
||||||
|
ctf.warning("settings", "Setting " .. dump(setting) .. " redeclared!")
|
||||||
|
ctf.warning("settings", debug.traceback())
|
||||||
|
end
|
||||||
|
ctf._defsettings[setting] = default
|
||||||
|
|
||||||
|
if minetest.settings:get("ctf."..setting) then
|
||||||
|
ctf.log("settings", "- " .. setting .. ": " .. minetest.settings:get("ctf."..setting))
|
||||||
|
elseif minetest.settings:get("ctf_"..setting) then
|
||||||
|
ctf.log("settings", "- " .. setting .. ": " .. minetest.settings:get("ctf_"..setting))
|
||||||
|
ctf.warning("settings", "deprecated setting ctf_"..setting..
|
||||||
|
" used, use ctf."..setting.." instead.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function ctf.setting(name)
|
||||||
|
local set = minetest.settings:get("ctf."..name) or
|
||||||
|
minetest.settings:get("ctf_"..name)
|
||||||
|
local dset = ctf._defsettings[name]
|
||||||
|
if dset == nil then
|
||||||
|
ctf.error("setting", "No such setting - " .. name)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if set ~= nil then
|
||||||
|
if type(dset) == "number" then
|
||||||
|
return tonumber(set)
|
||||||
|
elseif type(dset) == "boolean" then
|
||||||
|
return minetest.is_yes(set)
|
||||||
|
else
|
||||||
|
return set
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return dset
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function ctf.load()
|
||||||
|
ctf.log("io", "Loading CTF state")
|
||||||
|
local file = io.open(minetest.get_worldpath().."/ctf.txt", "r")
|
||||||
|
if file then
|
||||||
|
local table = minetest.deserialize(file:read("*all"))
|
||||||
|
if type(table) == "table" then
|
||||||
|
ctf.teams = table.teams
|
||||||
|
ctf.players = table.players
|
||||||
|
|
||||||
|
for i = 1, #ctf.registered_on_load do
|
||||||
|
ctf.registered_on_load[i](table)
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
ctf._loaddata = table
|
||||||
|
else
|
||||||
|
ctf.log("io", "ctf.txt is not present in the world folder")
|
||||||
|
ctf._new_game = true
|
||||||
|
for i = 1, #ctf.registered_on_new_game do
|
||||||
|
ctf.registered_on_new_game[i]()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.after(0, function()
|
||||||
|
ctf._loaddata = nil
|
||||||
|
ctf._mt_loaded = true
|
||||||
|
end)
|
||||||
|
|
||||||
|
function ctf.check_save()
|
||||||
|
if ctf_flag and ctf_flag.assert_flags then
|
||||||
|
ctf_flag.assert_flags()
|
||||||
|
end
|
||||||
|
if ctf.needs_save then
|
||||||
|
ctf.save()
|
||||||
|
end
|
||||||
|
minetest.after(10, ctf.check_save)
|
||||||
|
end
|
||||||
|
minetest.after(10, ctf.check_save)
|
||||||
|
|
||||||
|
function ctf.save()
|
||||||
|
local file = io.open(minetest.get_worldpath().."/ctf.txt", "w")
|
||||||
|
if file then
|
||||||
|
local out = {
|
||||||
|
teams = ctf.teams,
|
||||||
|
players = ctf.players
|
||||||
|
}
|
||||||
|
|
||||||
|
for i = 1, #ctf.registered_on_save do
|
||||||
|
local res = ctf.registered_on_save[i]()
|
||||||
|
|
||||||
|
if res then
|
||||||
|
for key, value in pairs(res) do
|
||||||
|
out[key] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
file:write(minetest.serialize(out))
|
||||||
|
file:close()
|
||||||
|
ctf.needs_save = false
|
||||||
|
else
|
||||||
|
ctf.error("io", "CTF file failed to save!")
|
||||||
|
end
|
||||||
|
end
|
2
mods/ctf_pvp_engine/ctf/depends.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
chatplus?
|
||||||
|
hudkit
|
83
mods/ctf_pvp_engine/ctf/diplomacy.lua
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
-- diplo states: war, peace, alliance
|
||||||
|
ctf.diplo = {
|
||||||
|
diplo = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctf.register_on_load(function(table)
|
||||||
|
ctf.diplo.diplo = table.diplo
|
||||||
|
end)
|
||||||
|
|
||||||
|
ctf.register_on_save(function()
|
||||||
|
return { diplo = ctf.diplo.diplo }
|
||||||
|
end)
|
||||||
|
|
||||||
|
function ctf.diplo.get(one,two)
|
||||||
|
if not ctf.diplo.diplo then
|
||||||
|
return ctf.setting("default_diplo_state")
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = 1, #ctf.diplo.diplo do
|
||||||
|
local dip = ctf.diplo.diplo[i]
|
||||||
|
if (dip.one == one and dip.two == two) or
|
||||||
|
(dip.one == two and dip.two == one) then
|
||||||
|
return dip.state
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return ctf.setting("default_diplo_state")
|
||||||
|
end
|
||||||
|
|
||||||
|
function ctf.diplo.set(one, two, state)
|
||||||
|
if ctf.diplo.diplo then
|
||||||
|
-- Check the table for an existing diplo state
|
||||||
|
for i = 1, #ctf.diplo.diplo do
|
||||||
|
local dip = ctf.diplo.diplo[i]
|
||||||
|
if (dip.one == one and dip.two == two) or
|
||||||
|
(dip.one == two and dip.two == one) then
|
||||||
|
dip.state = state
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
ctf.diplo.diplo = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(ctf.diplo.diplo,{one=one,two=two,state=state})
|
||||||
|
end
|
||||||
|
|
||||||
|
function ctf.diplo.check_requests(one, two)
|
||||||
|
local team = ctf.team(two)
|
||||||
|
|
||||||
|
if not team.log then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
for i=1,#team.log do
|
||||||
|
if team.log[i].team == one and
|
||||||
|
team.log[i].type == "request" and
|
||||||
|
team.log[i].mode == "diplo" then
|
||||||
|
return team.log[i].msg
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function ctf.diplo.cancel_requests(one, two)
|
||||||
|
local team = ctf.team(two)
|
||||||
|
|
||||||
|
if not team.log then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
for i=1,#team.log do
|
||||||
|
if team.log[i].team == one and
|
||||||
|
team.log[i].type == "request" and
|
||||||
|
team.log[i].mode == "diplo" then
|
||||||
|
table.remove(team.log,i)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return
|
||||||
|
end
|
353
mods/ctf_pvp_engine/ctf/gui.lua
Normal file
|
@ -0,0 +1,353 @@
|
||||||
|
ctf.gui = {
|
||||||
|
tabs = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctf.register_on_init(function()
|
||||||
|
ctf._set("gui", true)
|
||||||
|
ctf._set("gui.team", true)
|
||||||
|
ctf._set("gui.team.initial", "news")
|
||||||
|
|
||||||
|
for name, tab in pairs(ctf.gui.tabs) do
|
||||||
|
ctf._set("gui.tab." .. name, true)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
function ctf.gui.register_tab(name, title, func)
|
||||||
|
ctf.gui.tabs[name] = {
|
||||||
|
name = name,
|
||||||
|
title = title,
|
||||||
|
func = func
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctf._defsettings and ctf._defsettings["gui.tab." .. name] == nil then
|
||||||
|
ctf._set("gui.tab." .. name, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function ctf.gui.show(name, tab, tname)
|
||||||
|
if not tab then
|
||||||
|
tab = ctf.setting("gui.team.initial") or "news"
|
||||||
|
end
|
||||||
|
|
||||||
|
if not tab or not ctf.gui.tabs[tab] or not name or name == "" then
|
||||||
|
ctf.log("gui", "Invalid tab or name given to ctf.gui.show")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if not ctf.setting("gui.team") or not ctf.setting("gui") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if not ctf.team(tname) then
|
||||||
|
tname = ctf.player(name).team
|
||||||
|
end
|
||||||
|
|
||||||
|
if ctf.team(tname) then
|
||||||
|
ctf.action("gui", name .. " views " .. tname .. "'s " .. tab .. " page")
|
||||||
|
ctf.gui.tabs[tab].func(name, tname)
|
||||||
|
else
|
||||||
|
ctf.log("gui", "Invalid team given to ctf.gui.show")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get tab buttons
|
||||||
|
function ctf.gui.get_tabs(name, tname)
|
||||||
|
local result = ""
|
||||||
|
local id = 1
|
||||||
|
local function addtab(name,text)
|
||||||
|
result = result .. "button[" .. (id*2-1) .. ",0;2,1;" .. name .. ";" .. text .. "]"
|
||||||
|
id = id + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
for name, tab in pairs(ctf.gui.tabs) do
|
||||||
|
if ctf.setting("gui.tab." .. name) then
|
||||||
|
addtab(name, tab.title)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Team interface
|
||||||
|
ctf.gui.register_tab("news", "News", function(name, tname)
|
||||||
|
local result = ""
|
||||||
|
local team = ctf.team(tname).log
|
||||||
|
|
||||||
|
if not team then
|
||||||
|
team = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
local amount = 0
|
||||||
|
|
||||||
|
for i = 1, #team do
|
||||||
|
if team[i].type == "request" then
|
||||||
|
if ctf.can_mod(name, tname) then
|
||||||
|
amount = amount + 2
|
||||||
|
local height = (amount*0.5) + 0.5
|
||||||
|
amount = amount + 1
|
||||||
|
|
||||||
|
if team[i].mode == "diplo" then
|
||||||
|
result = result .. "background[0.5," .. height .. ";8.3,1;diplo_" .. team[i].msg .. ".png]"
|
||||||
|
if team[i].msg == "alliance" then
|
||||||
|
result = result .. "label[1," .. height .. ";" ..
|
||||||
|
team[i].team .. " offers an " ..
|
||||||
|
minetest.formspec_escape(team[i].msg) .. " treaty]"
|
||||||
|
else
|
||||||
|
result = result .. "label[1," .. height .. ";" ..
|
||||||
|
team[i].team .. " offers a " ..
|
||||||
|
minetest.formspec_escape(team[i].msg) .. " treaty]"
|
||||||
|
end
|
||||||
|
result = result .. "button[6," .. height .. ";1,1;btn_y" .. i .. ";Yes]"
|
||||||
|
result = result .. "button[7," .. height .. ";1,1;btn_n" .. i .. ";No]"
|
||||||
|
else
|
||||||
|
result = result .. "label[0.5," .. height .. ";RANDOM REQUEST TYPE]"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
amount = amount + 1
|
||||||
|
local height = (amount*0.5) + 0.5
|
||||||
|
|
||||||
|
if height > 5 then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
result = result .. "label[0.5," .. height .. ";" ..
|
||||||
|
minetest.formspec_escape(team[i].msg) .. "]"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if ctf.can_mod(name, tname) then
|
||||||
|
result = result .. "button[4,6;2,1;clear;Clear all]"
|
||||||
|
end
|
||||||
|
|
||||||
|
if amount == 0 then
|
||||||
|
result = "label[0.5,1;Welcome to the news panel]" ..
|
||||||
|
"label[0.5,1.5;News such as attacks will appear here]"
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.show_formspec(name, "ctf:news",
|
||||||
|
"size[10,7]" ..
|
||||||
|
ctf.gui.get_tabs(name, tname) ..
|
||||||
|
result)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Team interface
|
||||||
|
ctf.gui.register_tab("diplo", "Diplomacy", function(name, tname)
|
||||||
|
local result = ""
|
||||||
|
local data = {}
|
||||||
|
|
||||||
|
local amount = 0
|
||||||
|
|
||||||
|
for key, value in pairs(ctf.teams) do
|
||||||
|
if key ~= tname then
|
||||||
|
table.insert(data,{
|
||||||
|
team = key,
|
||||||
|
state = ctf.diplo.get(tname, key),
|
||||||
|
to = ctf.diplo.check_requests(tname, key),
|
||||||
|
from = ctf.diplo.check_requests(key, tname)
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
result = result .. "label[1,1;Diplomacy from the perspective of " .. tname .. "]"
|
||||||
|
|
||||||
|
for i = 1, #data do
|
||||||
|
amount = i
|
||||||
|
local height = (i*1)+0.5
|
||||||
|
|
||||||
|
if height > 5 then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
result = result .. "background[1," .. height .. ";8.2,1;diplo_" ..
|
||||||
|
data[i].state .. ".png]"
|
||||||
|
result = result .. "button[1.25," .. height .. ";2,1;team_" ..
|
||||||
|
data[i].team .. ";" .. data[i].team .. "]"
|
||||||
|
result = result .. "label[3.75," .. height .. ";" .. data[i].state
|
||||||
|
.. "]"
|
||||||
|
|
||||||
|
if ctf.can_mod(name, tname) and ctf.player(name).team == tname then
|
||||||
|
if not data[i].from and not data[i].to then
|
||||||
|
if data[i].state == "war" then
|
||||||
|
result = result .. "button[7.5," .. height ..
|
||||||
|
";1.5,1;peace_" .. data[i].team .. ";Peace]"
|
||||||
|
elseif data[i].state == "peace" then
|
||||||
|
result = result .. "button[6," .. height ..
|
||||||
|
";1.5,1;war_" .. data[i].team .. ";War]"
|
||||||
|
result = result .. "button[7.5," .. height ..
|
||||||
|
";1.5,1;alli_" .. data[i].team .. ";Alliance]"
|
||||||
|
elseif data[i].state == "alliance" then
|
||||||
|
result = result .. "button[6," .. height ..
|
||||||
|
";1.5,1;peace_" .. data[i].team .. ";Peace]"
|
||||||
|
end
|
||||||
|
elseif data[i].from ~= nil then
|
||||||
|
result = result .. "label[6," .. height ..
|
||||||
|
";request recieved]"
|
||||||
|
elseif data[i].to ~= nil then
|
||||||
|
result = result .. "label[5.5," .. height ..
|
||||||
|
";request sent]"
|
||||||
|
result = result .. "button[7.5," .. height ..
|
||||||
|
";1.5,1;cancel_" .. data[i].team .. ";Cancel]"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.show_formspec(name, "ctf:diplo",
|
||||||
|
"size[10,7]" ..
|
||||||
|
ctf.gui.get_tabs(name, tname) ..
|
||||||
|
result
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
|
||||||
|
local function formspec_is_ctf_tab(fsname)
|
||||||
|
for name, tab in pairs(ctf.gui.tabs) do
|
||||||
|
if fsname == "ctf:" .. name then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||||
|
if not formspec_is_ctf_tab(formname) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local name = player:get_player_name()
|
||||||
|
local tplayer = ctf.player(name)
|
||||||
|
local tname = tplayer.team
|
||||||
|
local team = ctf.team(tname)
|
||||||
|
|
||||||
|
if not team then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Do navigation
|
||||||
|
for tabname, tab in pairs(ctf.gui.tabs) do
|
||||||
|
if fields[tabname] then
|
||||||
|
ctf.gui.show(name, tabname)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Todo: move callbacks
|
||||||
|
-- News page
|
||||||
|
if fields.clear then
|
||||||
|
team.log = {}
|
||||||
|
ctf.needs_save = true
|
||||||
|
ctf.gui.show(name, "news")
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||||
|
local name = player:get_player_name()
|
||||||
|
local tplayer = ctf.player(name)
|
||||||
|
local tname = tplayer.team
|
||||||
|
local team = ctf.team(tname)
|
||||||
|
|
||||||
|
if not team then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if formname == "ctf:news" then
|
||||||
|
for key, field in pairs(fields) do
|
||||||
|
local ok, id = string.match(key, "btn_([yn])([0123456789]+)")
|
||||||
|
if ok and id then
|
||||||
|
if ok == "y" then
|
||||||
|
ctf.diplo.set(tname, team.log[tonumber(id)].team, team.log[tonumber(id)].msg)
|
||||||
|
|
||||||
|
-- Post to acceptor's log
|
||||||
|
ctf.post(tname, {
|
||||||
|
msg = "You have accepted the " ..
|
||||||
|
team.log[tonumber(id)].msg .. " request from " ..
|
||||||
|
team.log[tonumber(id)].team })
|
||||||
|
|
||||||
|
-- Post to request's log
|
||||||
|
ctf.post(team.log[tonumber(id)].team, {
|
||||||
|
msg = tname .. " has accepted your " ..
|
||||||
|
team.log[tonumber(id)].msg .. " request" })
|
||||||
|
|
||||||
|
id = id + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
table.remove(team.log, id)
|
||||||
|
ctf.needs_save = true
|
||||||
|
ctf.gui.show(name, "news")
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||||
|
local name = player:get_player_name()
|
||||||
|
local tplayer = ctf.player(name)
|
||||||
|
local tname = tplayer.team
|
||||||
|
local team = ctf.team(tname)
|
||||||
|
|
||||||
|
if not team or formname ~= "ctf:diplo" then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
for key, field in pairs(fields) do
|
||||||
|
local tname2 = string.match(key, "team_(.+)")
|
||||||
|
if tname2 and ctf.team(tname2) then
|
||||||
|
ctf.gui.show(name, "diplo", tname2)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
if ctf.can_mod(name, tname) then
|
||||||
|
tname2 = string.match(key, "peace_(.+)")
|
||||||
|
if tname2 then
|
||||||
|
if ctf.diplo.get(tname, tname2) == "war" then
|
||||||
|
ctf.post(tname2, {
|
||||||
|
type = "request",
|
||||||
|
msg = "peace",
|
||||||
|
team = tname,
|
||||||
|
mode = "diplo" })
|
||||||
|
else
|
||||||
|
ctf.diplo.set(tname, tname2, "peace")
|
||||||
|
ctf.post(tname, {
|
||||||
|
msg = "You have cancelled the alliance treaty with " .. tname2 })
|
||||||
|
ctf.post(tname2, {
|
||||||
|
msg = tname .. " has cancelled the alliance treaty" })
|
||||||
|
end
|
||||||
|
|
||||||
|
ctf.gui.show(name, "diplo")
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
tname2 = string.match(key, "war_(.+)")
|
||||||
|
if tname2 then
|
||||||
|
ctf.diplo.set(tname, tname2, "war")
|
||||||
|
ctf.post(tname, {
|
||||||
|
msg = "You have declared war on " .. tname2 })
|
||||||
|
ctf.post(tname2, {
|
||||||
|
msg = tname .. " has declared war on you" })
|
||||||
|
|
||||||
|
ctf.gui.show(name, "diplo")
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
tname2 = string.match(key, "alli_(.+)")
|
||||||
|
if tname2 then
|
||||||
|
ctf.post(tname2, {
|
||||||
|
type = "request",
|
||||||
|
msg = "alliance",
|
||||||
|
team = tname,
|
||||||
|
mode = "diplo" })
|
||||||
|
|
||||||
|
ctf.gui.show(name, "diplo")
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
tname2 = string.match(key, "cancel_(.+)")
|
||||||
|
if tname2 then
|
||||||
|
ctf.diplo.cancel_requests(tname, tname2)
|
||||||
|
ctf.gui.show(name, "diplo")
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end -- end if can mod
|
||||||
|
end -- end for each field
|
||||||
|
end)
|
50
mods/ctf_pvp_engine/ctf/hud.lua
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
ctf.hud = hudkit()
|
||||||
|
ctf.hud.parts = {}
|
||||||
|
function ctf.hud.register_part(func)
|
||||||
|
table.insert(ctf.hud.parts, func)
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.register_on_leaveplayer(function(player)
|
||||||
|
ctf.hud.players[player:get_player_name()] = nil
|
||||||
|
end)
|
||||||
|
|
||||||
|
ctf.register_on_join_team(function(name, tname)
|
||||||
|
if ctf.setting("hud") then
|
||||||
|
ctf.hud.update(minetest.get_player_by_name(name))
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
function ctf.hud.update(player)
|
||||||
|
if not player then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local name = player:get_player_name()
|
||||||
|
local tplayer = ctf.player(name)
|
||||||
|
|
||||||
|
if not tplayer or not tplayer.team or not ctf.team(tplayer.team) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Team Identifier
|
||||||
|
for i = 1, #ctf.hud.parts do
|
||||||
|
ctf.hud.parts[i](player, name, tplayer)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function ctf.hud.updateAll()
|
||||||
|
if not ctf.setting("hud") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local players = minetest.get_connected_players()
|
||||||
|
for i = 1, #players do
|
||||||
|
ctf.hud.update(players[i])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function tick()
|
||||||
|
ctf.hud.updateAll()
|
||||||
|
minetest.after(10, tick)
|
||||||
|
end
|
||||||
|
minetest.after(1, tick)
|
33
mods/ctf_pvp_engine/ctf/init.lua
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
-- CAPTURE THE FLAG
|
||||||
|
-- by Andrew "rubenwardy" Ward
|
||||||
|
-----------------------------------------
|
||||||
|
|
||||||
|
ctf = {}
|
||||||
|
|
||||||
|
-- Fix for https://github.com/minetest/minetest/issues/2383
|
||||||
|
local csa = minetest.chat_send_all
|
||||||
|
function minetest.chat_send_all(msg)
|
||||||
|
minetest.after(0, function()
|
||||||
|
csa(msg)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Privs
|
||||||
|
minetest.register_privilege("ctf_team_mgr", {
|
||||||
|
description = "Team manager",
|
||||||
|
})
|
||||||
|
|
||||||
|
minetest.register_privilege("ctf_admin", {
|
||||||
|
description = "Can create teams, manage players, assign team owners.",
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Modules
|
||||||
|
dofile(minetest.get_modpath("ctf") .. "/core.lua")
|
||||||
|
dofile(minetest.get_modpath("ctf") .. "/teams.lua")
|
||||||
|
dofile(minetest.get_modpath("ctf") .. "/diplomacy.lua")
|
||||||
|
dofile(minetest.get_modpath("ctf") .. "/gui.lua")
|
||||||
|
dofile(minetest.get_modpath("ctf") .. "/hud.lua")
|
||||||
|
|
||||||
|
-- Init
|
||||||
|
ctf.init()
|
||||||
|
ctf.clean_player_lists()
|
516
mods/ctf_pvp_engine/ctf/teams.lua
Normal file
|
@ -0,0 +1,516 @@
|
||||||
|
-- Get or add a team
|
||||||
|
function ctf.team(name)
|
||||||
|
if name == nil then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
if type(name) == "table" then
|
||||||
|
if not name.add_team then
|
||||||
|
ctf.error("team", "Invalid table given to ctf.team")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
return ctf.create_team(name.name, name)
|
||||||
|
else
|
||||||
|
local team = ctf.teams[name]
|
||||||
|
if team then
|
||||||
|
if not team.data or not team.players then
|
||||||
|
ctf.warning("team", "Assertion failed, data{} or players{} not " ..
|
||||||
|
"found in team{}")
|
||||||
|
end
|
||||||
|
return team
|
||||||
|
else
|
||||||
|
if name and name:trim() ~= "" then
|
||||||
|
ctf.warning("team", dump(name) .. " does not exist!")
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function ctf.create_team(name, data)
|
||||||
|
ctf.log("team", "Creating team " .. name)
|
||||||
|
|
||||||
|
ctf.teams[name] = {
|
||||||
|
data = data,
|
||||||
|
spawn = nil,
|
||||||
|
players = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i = 1, #ctf.registered_on_new_team do
|
||||||
|
ctf.registered_on_new_team[i](ctf.teams[name])
|
||||||
|
end
|
||||||
|
|
||||||
|
ctf.needs_save = true
|
||||||
|
|
||||||
|
return ctf.teams[name]
|
||||||
|
end
|
||||||
|
|
||||||
|
function ctf.remove_team(name)
|
||||||
|
local team = ctf.team(name)
|
||||||
|
if team then
|
||||||
|
for username, player in pairs(team.players) do
|
||||||
|
player.team = nil
|
||||||
|
end
|
||||||
|
for i = 1, #team.flags do
|
||||||
|
team.flags[i].team = nil
|
||||||
|
end
|
||||||
|
ctf.teams[name] = nil
|
||||||
|
ctf.needs_save = true
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function ctf.list_teams(name)
|
||||||
|
minetest.chat_send_player(name, "Teams:")
|
||||||
|
for tname, team in pairs(ctf.teams) do
|
||||||
|
if team and team.players then
|
||||||
|
local details = ""
|
||||||
|
|
||||||
|
local numPlayers = ctf.count_players_in_team(tname)
|
||||||
|
details = numPlayers .. " members"
|
||||||
|
|
||||||
|
if team.flags then
|
||||||
|
local numFlags = 0
|
||||||
|
for flagid, flag in pairs(team.flags) do
|
||||||
|
numFlags = numFlags + 1
|
||||||
|
end
|
||||||
|
details = details .. ", " .. numFlags .. " flags"
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.chat_send_player(name, ">> " .. tname ..
|
||||||
|
" (" .. details .. ")")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Count number of players in a team
|
||||||
|
function ctf.count_players_in_team(team)
|
||||||
|
local count = 0
|
||||||
|
for name, player in pairs(ctf.team(team).players) do
|
||||||
|
count = count + 1
|
||||||
|
end
|
||||||
|
return count
|
||||||
|
end
|
||||||
|
|
||||||
|
function ctf.new_player(name)
|
||||||
|
if name then
|
||||||
|
ctf.players[name] = {
|
||||||
|
name = name
|
||||||
|
}
|
||||||
|
ctf.needs_save = true
|
||||||
|
else
|
||||||
|
ctf.error("team", "Can't create a blank player")
|
||||||
|
ctf.log("team", debug.traceback())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get a player
|
||||||
|
function ctf.player(name)
|
||||||
|
if not ctf.players[name] then
|
||||||
|
ctf.new_player(name)
|
||||||
|
end
|
||||||
|
return ctf.players[name]
|
||||||
|
end
|
||||||
|
|
||||||
|
function ctf.player_or_nil(name)
|
||||||
|
return ctf.players[name]
|
||||||
|
end
|
||||||
|
|
||||||
|
function ctf.chat_send_team(team, msg)
|
||||||
|
if type(team) == "string" then
|
||||||
|
team = ctf.team(team)
|
||||||
|
end
|
||||||
|
|
||||||
|
for pname, _ in pairs(team.players) do
|
||||||
|
minetest.chat_send_player(pname, msg)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function ctf.remove_player(name)
|
||||||
|
ctf.log("team", "Removing player ".. dump(name))
|
||||||
|
local player = ctf.players[name]
|
||||||
|
if player then
|
||||||
|
local team = ctf.team(player.team)
|
||||||
|
if team then
|
||||||
|
team.players[name] = nil
|
||||||
|
end
|
||||||
|
ctf.players[name] = nil
|
||||||
|
ctf.needs_save = true
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ctf.registered_on_join_team = {}
|
||||||
|
function ctf.register_on_join_team(func)
|
||||||
|
if ctf._mt_loaded then
|
||||||
|
error("You can't register callbacks at game time!")
|
||||||
|
end
|
||||||
|
table.insert(ctf.registered_on_join_team, func)
|
||||||
|
end
|
||||||
|
|
||||||
|
ctf.player_last_team = {}
|
||||||
|
|
||||||
|
-- Player joins team
|
||||||
|
-- Called by /join, /team join or auto allocate.
|
||||||
|
function ctf.join(name, team, force, by)
|
||||||
|
if not name or name == "" or not team or team == "" then
|
||||||
|
ctf.log("team", "Missing parameters to ctf.join")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local player = ctf.player(name)
|
||||||
|
|
||||||
|
if not force and not ctf.setting("players_can_change_team")
|
||||||
|
and player.team and ctf.team(player.team) then
|
||||||
|
if by then
|
||||||
|
if by == name then
|
||||||
|
ctf.action("teams", name .. " attempted to change to " .. team)
|
||||||
|
minetest.chat_send_player(by, "You are not allowed to switch teams, traitor!")
|
||||||
|
else
|
||||||
|
ctf.action("teams", by .. " attempted to change " .. name .. " to " .. team)
|
||||||
|
minetest.chat_send_player(by, "Failed to add " .. name .. " to " .. team ..
|
||||||
|
" as players_can_change_team = false")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
ctf.log("teams", "failed to add " .. name .. " to " .. team ..
|
||||||
|
" as players_can_change_team = false")
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local team_data = ctf.team(team)
|
||||||
|
if not team_data then
|
||||||
|
if by then
|
||||||
|
minetest.chat_send_player(by, "No such team.")
|
||||||
|
ctf.list_teams(by)
|
||||||
|
if by == name then
|
||||||
|
minetest.log("action", by .. " tried to move " .. name .. " to " .. team .. ", which doesn't exist")
|
||||||
|
else
|
||||||
|
minetest.log("action", name .. " attempted to join " .. team .. ", which doesn't exist")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
ctf.log("teams", "failed to add " .. name .. " to " .. team ..
|
||||||
|
" as team does not exist")
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if player.team then
|
||||||
|
local oldteam = ctf.team(player.team)
|
||||||
|
if oldteam then
|
||||||
|
oldteam.players[player.name] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
player.team = team
|
||||||
|
team_data.players[player.name] = player
|
||||||
|
ctf.player_last_team[name] = team
|
||||||
|
|
||||||
|
ctf.needs_save = true
|
||||||
|
|
||||||
|
minetest.log("action", name .. " joined team " .. team)
|
||||||
|
minetest.chat_send_all(name.." has joined team "..team)
|
||||||
|
|
||||||
|
for i = 1, #ctf.registered_on_join_team do
|
||||||
|
ctf.registered_on_join_team[i](name, team)
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Cleans up the player lists
|
||||||
|
function ctf.clean_player_lists()
|
||||||
|
ctf.log("utils", "Cleaning player lists")
|
||||||
|
for _, str in pairs(ctf.players) do
|
||||||
|
if str and str.team and ctf.teams[str.team] then
|
||||||
|
ctf.log("utils", " - Adding player "..str.name.." to team "..str.team)
|
||||||
|
ctf.teams[str.team].players[str.name] = str
|
||||||
|
else
|
||||||
|
ctf.log("utils", " - Skipping player "..str.name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ctf.needs_save = true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Sees if the player can change stuff in a team
|
||||||
|
function ctf.can_mod(player,team)
|
||||||
|
local privs = minetest.get_player_privs(player)
|
||||||
|
|
||||||
|
if privs then
|
||||||
|
if privs.ctf_admin == true then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if player and ctf.teams[team] and ctf.teams[team].players and ctf.teams[team].players[player] then
|
||||||
|
if ctf.teams[team].players[player].auth == true then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- post a message to a team board
|
||||||
|
function ctf.post(team, msg)
|
||||||
|
if not ctf.team(team) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if not ctf.team(team).log then
|
||||||
|
ctf.team(team).log = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
ctf.log("team", "message posted to team board")
|
||||||
|
|
||||||
|
table.insert(ctf.team(team).log, 1, msg)
|
||||||
|
ctf.needs_save = true
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Automatic Allocation
|
||||||
|
function ctf.autoalloc(name, alloc_mode)
|
||||||
|
alloc_mode = alloc_mode or ctf.setting("allocate_mode")
|
||||||
|
if alloc_mode == 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local last_team = ctf.player_last_team[name]
|
||||||
|
if last_team then
|
||||||
|
return last_team
|
||||||
|
end
|
||||||
|
|
||||||
|
local max_players = ctf.setting("maximum_in_team")
|
||||||
|
|
||||||
|
local mtot = false -- more than one team
|
||||||
|
for key, team in pairs(ctf.teams) do
|
||||||
|
mtot = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
if not mtot then
|
||||||
|
ctf.error("autoalloc", "No teams to allocate " .. name .. " to!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if alloc_mode == 1 then
|
||||||
|
local index = {}
|
||||||
|
|
||||||
|
for key, team in pairs(ctf.teams) do
|
||||||
|
if team.data.allow_joins ~= false and (max_players == -1 or
|
||||||
|
ctf.count_players_in_team(key) < max_players) then
|
||||||
|
table.insert(index, key)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if #index == 0 then
|
||||||
|
ctf.error("autoalloc", "No teams to join!")
|
||||||
|
else
|
||||||
|
return index[math.random(1, #index)]
|
||||||
|
end
|
||||||
|
elseif alloc_mode == 2 then
|
||||||
|
local one = nil
|
||||||
|
local one_count = -1
|
||||||
|
local two = nil
|
||||||
|
local two_count = -1
|
||||||
|
for key, team in pairs(ctf.teams) do
|
||||||
|
local count = ctf.count_players_in_team(key)
|
||||||
|
if team.data.allow_joins ~= false and
|
||||||
|
(max_players == -1 or count < max_players) then
|
||||||
|
if count > one_count then
|
||||||
|
two = one
|
||||||
|
two_count = one_count
|
||||||
|
one = key
|
||||||
|
one_count = count
|
||||||
|
end
|
||||||
|
|
||||||
|
if count > two_count then
|
||||||
|
two = key
|
||||||
|
two_count = count
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not one and not two then
|
||||||
|
ctf.error("autoalloc", "No teams to join!")
|
||||||
|
elseif one and two then
|
||||||
|
if math.random() > 0.5 then
|
||||||
|
return one
|
||||||
|
else
|
||||||
|
return two
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if one then
|
||||||
|
return one
|
||||||
|
else
|
||||||
|
return two
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif alloc_mode == 3 then
|
||||||
|
local smallest = nil
|
||||||
|
local smallest_count = 1000
|
||||||
|
for key, team in pairs(ctf.teams) do
|
||||||
|
local count = ctf.count_players_in_team(key)
|
||||||
|
if team.data.allow_joins ~= false and
|
||||||
|
(not smallest or count < smallest_count) then
|
||||||
|
smallest = key
|
||||||
|
smallest_count = count
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not smallest then
|
||||||
|
ctf.error("autoalloc", "No teams to join!")
|
||||||
|
else
|
||||||
|
return smallest
|
||||||
|
end
|
||||||
|
elseif alloc_mode == 4 then
|
||||||
|
return ctf.custom_alloc(name)
|
||||||
|
else
|
||||||
|
ctf.error("autoalloc",
|
||||||
|
"Unknown allocation mode: " .. alloc_mode)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Custom team allocation function. Throws error
|
||||||
|
-- if unimplemented, and autoalloc mode 4 is selected
|
||||||
|
function ctf.custom_alloc()
|
||||||
|
error("Allocation mode set to custom while " ..
|
||||||
|
"ctf.custom_alloc hasn't been overridden!")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- updates the spawn position for a team
|
||||||
|
function ctf.get_spawn(team)
|
||||||
|
if ctf.team(team) then
|
||||||
|
local spawn = ctf.team(team).spawn
|
||||||
|
if not spawn then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
return vector.add(spawn, minetest.string_to_pos(ctf.setting("spawn_offset")))
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function ctf.move_to_spawn(name)
|
||||||
|
local player = minetest.get_player_by_name(name)
|
||||||
|
local tplayer = ctf.player(name)
|
||||||
|
if ctf.team(tplayer.team) then
|
||||||
|
local spawn = ctf.get_spawn(tplayer.team)
|
||||||
|
if spawn then
|
||||||
|
player:move_to(spawn, false)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.register_on_respawnplayer(function(player)
|
||||||
|
if not player then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
return ctf.move_to_spawn(player:get_player_name())
|
||||||
|
end)
|
||||||
|
|
||||||
|
function ctf.get_territory_owner(pos)
|
||||||
|
local largest = nil
|
||||||
|
local largest_weight = 0
|
||||||
|
for i = 1, #ctf.registered_on_territory_query do
|
||||||
|
local team, weight = ctf.registered_on_territory_query[i](pos)
|
||||||
|
if team and weight then
|
||||||
|
if weight == -1 then
|
||||||
|
return team
|
||||||
|
end
|
||||||
|
if weight > largest_weight then
|
||||||
|
largest = team
|
||||||
|
largest_weight = weight
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return largest
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.register_on_newplayer(function(player)
|
||||||
|
local name = player:get_player_name()
|
||||||
|
local team = ctf.autoalloc(name)
|
||||||
|
if team then
|
||||||
|
ctf.log("autoalloc", name .. " was allocated to " .. team)
|
||||||
|
ctf.join(name, team)
|
||||||
|
ctf.move_to_spawn(player:get_player_name())
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
minetest.register_on_joinplayer(function(player)
|
||||||
|
if not ctf.setting("autoalloc_on_joinplayer") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local name = player:get_player_name()
|
||||||
|
if ctf.team(ctf.player(name).team) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local team = ctf.autoalloc(name)
|
||||||
|
if team then
|
||||||
|
ctf.log("autoalloc", name .. " was allocated to " .. team)
|
||||||
|
ctf.join(name, team)
|
||||||
|
ctf.move_to_spawn(player:get_player_name())
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Disable friendly fire.
|
||||||
|
ctf.registered_on_killedplayer = {}
|
||||||
|
function ctf.register_on_killedplayer(func)
|
||||||
|
if ctf._mt_loaded then
|
||||||
|
error("You can't register callbacks at game time!")
|
||||||
|
end
|
||||||
|
table.insert(ctf.registered_on_killedplayer, func)
|
||||||
|
end
|
||||||
|
local dead_players = {}
|
||||||
|
minetest.register_on_respawnplayer(function(player)
|
||||||
|
dead_players[player:get_player_name()] = nil
|
||||||
|
end)
|
||||||
|
minetest.register_on_joinplayer(function(player)
|
||||||
|
dead_players[player:get_player_name()] = nil
|
||||||
|
end)
|
||||||
|
minetest.register_on_punchplayer(function(player, hitter,
|
||||||
|
time_from_last_punch, tool_capabilities, dir, damage)
|
||||||
|
if player and hitter then
|
||||||
|
local pname = player:get_player_name()
|
||||||
|
local hname = hitter:get_player_name()
|
||||||
|
|
||||||
|
local to = ctf.player(pname)
|
||||||
|
local from = ctf.player(hname)
|
||||||
|
|
||||||
|
if dead_players[pname] then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if to.team == from.team and to.team ~= "" and
|
||||||
|
to.team ~= nil and to.name ~= from.name then
|
||||||
|
minetest.chat_send_player(hname, pname .. " is on your team!")
|
||||||
|
if not ctf.setting("friendly_fire") then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local hp = player:get_hp()
|
||||||
|
if hp == 0 then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if hp - damage <= 0 then
|
||||||
|
dead_players[pname] = true
|
||||||
|
local wielded = hitter:get_wielded_item()
|
||||||
|
for i = 1, #ctf.registered_on_killedplayer do
|
||||||
|
ctf.registered_on_killedplayer[i](pname, hname,
|
||||||
|
wielded, tool_capabilities)
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
BIN
mods/ctf_pvp_engine/ctf/textures/diplo_alliance.png
Normal file
After Width: | Height: | Size: 6.5 KiB |
BIN
mods/ctf_pvp_engine/ctf/textures/diplo_peace.png
Normal file
After Width: | Height: | Size: 9 KiB |
BIN
mods/ctf_pvp_engine/ctf/textures/diplo_war.png
Normal file
After Width: | Height: | Size: 8.8 KiB |
4
mods/ctf_pvp_engine/ctf_chat/depends.txt
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
ctf
|
||||||
|
ctf_colors
|
||||||
|
chatplus?
|
||||||
|
irc?
|
411
mods/ctf_pvp_engine/ctf_chat/init.lua
Normal 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
|
2
mods/ctf_pvp_engine/ctf_colors/depends.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
ctf
|
||||||
|
3d_armor?
|
60
mods/ctf_pvp_engine/ctf_colors/gui.lua
Normal 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)
|
88
mods/ctf_pvp_engine/ctf_colors/hud.lua
Normal 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)
|
31
mods/ctf_pvp_engine/ctf_colors/init.lua
Normal 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")
|
After Width: | Height: | Size: 545 B |
After Width: | Height: | Size: 542 B |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 2.6 KiB |
BIN
mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_skin_blue.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_skin_cyan.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_skin_gold.png
Normal file
After Width: | Height: | Size: 3 KiB |
BIN
mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_skin_gray.png
Normal file
After Width: | Height: | Size: 3 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 3 KiB |
BIN
mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_skin_pink.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 3.1 KiB |
BIN
mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_skin_red.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 3 KiB |
After Width: | Height: | Size: 3 KiB |
242
mods/ctf_pvp_engine/ctf_flag/api.lua
Normal 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
|
3
mods/ctf_pvp_engine/ctf_flag/depends.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
ctf
|
||||||
|
ctf_colors
|
||||||
|
chatplus?
|
253
mods/ctf_pvp_engine/ctf_flag/flag_func.lua
Normal 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
|
||||||
|
}
|
90
mods/ctf_pvp_engine/ctf_flag/flags.lua
Normal 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
|
184
mods/ctf_pvp_engine/ctf_flag/gui.lua
Normal 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)
|
102
mods/ctf_pvp_engine/ctf_flag/hud.lua
Normal 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)
|
146
mods/ctf_pvp_engine/ctf_flag/init.lua
Normal 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)
|
BIN
mods/ctf_pvp_engine/ctf_flag/sounds/trumpet_lose.ogg
Normal file
BIN
mods/ctf_pvp_engine/ctf_flag/sounds/trumpet_win.ogg
Normal file
BIN
mods/ctf_pvp_engine/ctf_flag/textures/flag_black.png
Normal file
After Width: | Height: | Size: 601 B |
BIN
mods/ctf_pvp_engine/ctf_flag/textures/flag_black2.png
Normal file
After Width: | Height: | Size: 598 B |
BIN
mods/ctf_pvp_engine/ctf_flag/textures/flag_blue.png
Normal file
After Width: | Height: | Size: 776 B |
BIN
mods/ctf_pvp_engine/ctf_flag/textures/flag_blue2.png
Normal file
After Width: | Height: | Size: 732 B |
BIN
mods/ctf_pvp_engine/ctf_flag/textures/flag_cyan.png
Normal file
After Width: | Height: | Size: 666 B |
BIN
mods/ctf_pvp_engine/ctf_flag/textures/flag_cyan2.png
Normal file
After Width: | Height: | Size: 676 B |
BIN
mods/ctf_pvp_engine/ctf_flag/textures/flag_gold.png
Normal file
After Width: | Height: | Size: 618 B |
BIN
mods/ctf_pvp_engine/ctf_flag/textures/flag_gold2.png
Normal file
After Width: | Height: | Size: 625 B |
BIN
mods/ctf_pvp_engine/ctf_flag/textures/flag_gray.png
Normal file
After Width: | Height: | Size: 549 B |
BIN
mods/ctf_pvp_engine/ctf_flag/textures/flag_gray2.png
Normal file
After Width: | Height: | Size: 571 B |
BIN
mods/ctf_pvp_engine/ctf_flag/textures/flag_green.png
Normal file
After Width: | Height: | Size: 826 B |
BIN
mods/ctf_pvp_engine/ctf_flag/textures/flag_green2.png
Normal file
After Width: | Height: | Size: 775 B |
BIN
mods/ctf_pvp_engine/ctf_flag/textures/flag_orange.png
Normal file
After Width: | Height: | Size: 632 B |
BIN
mods/ctf_pvp_engine/ctf_flag/textures/flag_orange2.png
Normal file
After Width: | Height: | Size: 635 B |
BIN
mods/ctf_pvp_engine/ctf_flag/textures/flag_pink.png
Normal file
After Width: | Height: | Size: 690 B |
BIN
mods/ctf_pvp_engine/ctf_flag/textures/flag_pink2.png
Normal file
After Width: | Height: | Size: 688 B |
BIN
mods/ctf_pvp_engine/ctf_flag/textures/flag_purple.png
Normal file
After Width: | Height: | Size: 622 B |
BIN
mods/ctf_pvp_engine/ctf_flag/textures/flag_purple2.png
Normal file
After Width: | Height: | Size: 616 B |
BIN
mods/ctf_pvp_engine/ctf_flag/textures/flag_red.png
Normal file
After Width: | Height: | Size: 812 B |
BIN
mods/ctf_pvp_engine/ctf_flag/textures/flag_red2.png
Normal file
After Width: | Height: | Size: 874 B |
BIN
mods/ctf_pvp_engine/ctf_flag/textures/flag_silver.png
Normal file
After Width: | Height: | Size: 544 B |
BIN
mods/ctf_pvp_engine/ctf_flag/textures/flag_silver2.png
Normal file
After Width: | Height: | Size: 567 B |
BIN
mods/ctf_pvp_engine/ctf_flag/textures/flag_yellow.png
Normal file
After Width: | Height: | Size: 657 B |
BIN
mods/ctf_pvp_engine/ctf_flag/textures/flag_yellow2.png
Normal file
After Width: | Height: | Size: 671 B |
1
mods/ctf_pvp_engine/ctf_protect/depends.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
ctf
|
28
mods/ctf_pvp_engine/ctf_protect/init.lua
Normal 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
|
80
mods/ctf_pvp_engine/doc_data.md
Normal 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"
|
||||||
|
}
|
||||||
|
```
|
70
mods/ctf_pvp_engine/doc_project_overview.md
Normal 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
|
60
mods/ctf_pvp_engine/doc_settings.md
Normal 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 |
|
67
mods/ctf_pvp_engine/hudkit/init.lua
Normal 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
|
80
mods/ctf_pvp_engine/lib_chatcmdbuilder/.gitignore
vendored
Normal 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
|
19
mods/ctf_pvp_engine/lib_chatcmdbuilder/LICENSE
Normal 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.
|
89
mods/ctf_pvp_engine/lib_chatcmdbuilder/README.md
Normal 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
|
||||||
|
```
|
1
mods/ctf_pvp_engine/lib_chatcmdbuilder/description.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
A library to make registering chat commands easier
|
230
mods/ctf_pvp_engine/lib_chatcmdbuilder/init.lua
Normal 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
|
7
mods/ctf_pvp_engine/lib_chatcmdbuilder/mod.conf
Normal 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
|