From a735266eb3b6a68889eebe1b00722c948f70247a Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Wed, 28 Aug 2019 16:50:33 +0100 Subject: [PATCH] Add ctf_pvp_engine mods directly in the repo --- .gitmodules | 3 - mods/ctf_pvp_engine | 1 - mods/ctf_pvp_engine/.gitignore | 215 ++++++++ mods/ctf_pvp_engine/README.md | 23 + mods/ctf_pvp_engine/ctf/core.lua | 269 +++++++++ mods/ctf_pvp_engine/ctf/depends.txt | 2 + mods/ctf_pvp_engine/ctf/diplomacy.lua | 83 +++ mods/ctf_pvp_engine/ctf/gui.lua | 353 ++++++++++++ mods/ctf_pvp_engine/ctf/hud.lua | 50 ++ mods/ctf_pvp_engine/ctf/init.lua | 33 ++ mods/ctf_pvp_engine/ctf/teams.lua | 516 ++++++++++++++++++ .../ctf/textures/diplo_alliance.png | Bin 0 -> 6679 bytes .../ctf/textures/diplo_peace.png | Bin 0 -> 9263 bytes .../ctf_pvp_engine/ctf/textures/diplo_war.png | Bin 0 -> 9050 bytes mods/ctf_pvp_engine/ctf_chat/depends.txt | 4 + mods/ctf_pvp_engine/ctf_chat/init.lua | 411 ++++++++++++++ mods/ctf_pvp_engine/ctf_colors/depends.txt | 2 + mods/ctf_pvp_engine/ctf_colors/gui.lua | 60 ++ mods/ctf_pvp_engine/ctf_colors/hud.lua | 88 +++ mods/ctf_pvp_engine/ctf_colors/init.lua | 31 ++ .../textures/ctf_colors_hotbar_blue.png | Bin 0 -> 545 bytes .../textures/ctf_colors_hotbar_red.png | Bin 0 -> 542 bytes .../ctf_colors_hotbar_selected_blue.png | Bin 0 -> 3545 bytes .../ctf_colors_hotbar_selected_red.png | Bin 0 -> 2905 bytes .../textures/ctf_colors_skin_black.png | Bin 0 -> 2629 bytes .../textures/ctf_colors_skin_blue.png | Bin 0 -> 3250 bytes .../textures/ctf_colors_skin_cyan.png | Bin 0 -> 2917 bytes .../textures/ctf_colors_skin_gold.png | Bin 0 -> 3087 bytes .../textures/ctf_colors_skin_gray.png | Bin 0 -> 3023 bytes .../textures/ctf_colors_skin_green.png | Bin 0 -> 2754 bytes .../textures/ctf_colors_skin_orange.png | Bin 0 -> 3050 bytes .../textures/ctf_colors_skin_pink.png | Bin 0 -> 3158 bytes .../textures/ctf_colors_skin_purple.png | Bin 0 -> 3164 bytes .../textures/ctf_colors_skin_red.png | Bin 0 -> 3170 bytes .../textures/ctf_colors_skin_silver.png | Bin 0 -> 3033 bytes .../textures/ctf_colors_skin_yellow.png | Bin 0 -> 3027 bytes mods/ctf_pvp_engine/ctf_flag/api.lua | 242 ++++++++ mods/ctf_pvp_engine/ctf_flag/depends.txt | 3 + mods/ctf_pvp_engine/ctf_flag/flag_func.lua | 253 +++++++++ mods/ctf_pvp_engine/ctf_flag/flags.lua | 90 +++ mods/ctf_pvp_engine/ctf_flag/gui.lua | 184 +++++++ mods/ctf_pvp_engine/ctf_flag/hud.lua | 102 ++++ mods/ctf_pvp_engine/ctf_flag/init.lua | 146 +++++ .../ctf_flag/sounds/trumpet_lose.ogg | Bin 0 -> 39347 bytes .../ctf_flag/sounds/trumpet_win.ogg | Bin 0 -> 42011 bytes .../ctf_flag/textures/flag_black.png | Bin 0 -> 601 bytes .../ctf_flag/textures/flag_black2.png | Bin 0 -> 598 bytes .../ctf_flag/textures/flag_blue.png | Bin 0 -> 776 bytes .../ctf_flag/textures/flag_blue2.png | Bin 0 -> 732 bytes .../ctf_flag/textures/flag_cyan.png | Bin 0 -> 666 bytes .../ctf_flag/textures/flag_cyan2.png | Bin 0 -> 676 bytes .../ctf_flag/textures/flag_gold.png | Bin 0 -> 618 bytes .../ctf_flag/textures/flag_gold2.png | Bin 0 -> 625 bytes .../ctf_flag/textures/flag_gray.png | Bin 0 -> 549 bytes .../ctf_flag/textures/flag_gray2.png | Bin 0 -> 571 bytes .../ctf_flag/textures/flag_green.png | Bin 0 -> 826 bytes .../ctf_flag/textures/flag_green2.png | Bin 0 -> 775 bytes .../ctf_flag/textures/flag_orange.png | Bin 0 -> 632 bytes .../ctf_flag/textures/flag_orange2.png | Bin 0 -> 635 bytes .../ctf_flag/textures/flag_pink.png | Bin 0 -> 690 bytes .../ctf_flag/textures/flag_pink2.png | Bin 0 -> 688 bytes .../ctf_flag/textures/flag_purple.png | Bin 0 -> 622 bytes .../ctf_flag/textures/flag_purple2.png | Bin 0 -> 616 bytes .../ctf_flag/textures/flag_red.png | Bin 0 -> 812 bytes .../ctf_flag/textures/flag_red2.png | Bin 0 -> 874 bytes .../ctf_flag/textures/flag_silver.png | Bin 0 -> 544 bytes .../ctf_flag/textures/flag_silver2.png | Bin 0 -> 567 bytes .../ctf_flag/textures/flag_yellow.png | Bin 0 -> 657 bytes .../ctf_flag/textures/flag_yellow2.png | Bin 0 -> 671 bytes mods/ctf_pvp_engine/ctf_protect/depends.txt | 1 + mods/ctf_pvp_engine/ctf_protect/init.lua | 28 + mods/ctf_pvp_engine/doc_data.md | 80 +++ mods/ctf_pvp_engine/doc_project_overview.md | 70 +++ mods/ctf_pvp_engine/doc_settings.md | 60 ++ mods/ctf_pvp_engine/hudkit/init.lua | 67 +++ .../lib_chatcmdbuilder/.gitignore | 80 +++ .../ctf_pvp_engine/lib_chatcmdbuilder/LICENSE | 19 + .../lib_chatcmdbuilder/README.md | 89 +++ .../lib_chatcmdbuilder/description.txt | 1 + .../lib_chatcmdbuilder/init.lua | 230 ++++++++ .../lib_chatcmdbuilder/mod.conf | 7 + mods/ctf_pvp_engine/modpack.txt | 0 82 files changed, 3892 insertions(+), 4 deletions(-) delete mode 160000 mods/ctf_pvp_engine create mode 100644 mods/ctf_pvp_engine/.gitignore create mode 100644 mods/ctf_pvp_engine/README.md create mode 100644 mods/ctf_pvp_engine/ctf/core.lua create mode 100644 mods/ctf_pvp_engine/ctf/depends.txt create mode 100644 mods/ctf_pvp_engine/ctf/diplomacy.lua create mode 100644 mods/ctf_pvp_engine/ctf/gui.lua create mode 100644 mods/ctf_pvp_engine/ctf/hud.lua create mode 100644 mods/ctf_pvp_engine/ctf/init.lua create mode 100644 mods/ctf_pvp_engine/ctf/teams.lua create mode 100644 mods/ctf_pvp_engine/ctf/textures/diplo_alliance.png create mode 100644 mods/ctf_pvp_engine/ctf/textures/diplo_peace.png create mode 100644 mods/ctf_pvp_engine/ctf/textures/diplo_war.png create mode 100644 mods/ctf_pvp_engine/ctf_chat/depends.txt create mode 100644 mods/ctf_pvp_engine/ctf_chat/init.lua create mode 100644 mods/ctf_pvp_engine/ctf_colors/depends.txt create mode 100644 mods/ctf_pvp_engine/ctf_colors/gui.lua create mode 100644 mods/ctf_pvp_engine/ctf_colors/hud.lua create mode 100644 mods/ctf_pvp_engine/ctf_colors/init.lua create mode 100644 mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_hotbar_blue.png create mode 100644 mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_hotbar_red.png create mode 100644 mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_hotbar_selected_blue.png create mode 100644 mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_hotbar_selected_red.png create mode 100644 mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_skin_black.png create mode 100644 mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_skin_blue.png create mode 100644 mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_skin_cyan.png create mode 100644 mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_skin_gold.png create mode 100644 mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_skin_gray.png create mode 100644 mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_skin_green.png create mode 100644 mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_skin_orange.png create mode 100644 mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_skin_pink.png create mode 100644 mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_skin_purple.png create mode 100644 mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_skin_red.png create mode 100644 mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_skin_silver.png create mode 100644 mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_skin_yellow.png create mode 100644 mods/ctf_pvp_engine/ctf_flag/api.lua create mode 100644 mods/ctf_pvp_engine/ctf_flag/depends.txt create mode 100644 mods/ctf_pvp_engine/ctf_flag/flag_func.lua create mode 100644 mods/ctf_pvp_engine/ctf_flag/flags.lua create mode 100644 mods/ctf_pvp_engine/ctf_flag/gui.lua create mode 100644 mods/ctf_pvp_engine/ctf_flag/hud.lua create mode 100644 mods/ctf_pvp_engine/ctf_flag/init.lua create mode 100644 mods/ctf_pvp_engine/ctf_flag/sounds/trumpet_lose.ogg create mode 100644 mods/ctf_pvp_engine/ctf_flag/sounds/trumpet_win.ogg create mode 100644 mods/ctf_pvp_engine/ctf_flag/textures/flag_black.png create mode 100644 mods/ctf_pvp_engine/ctf_flag/textures/flag_black2.png create mode 100644 mods/ctf_pvp_engine/ctf_flag/textures/flag_blue.png create mode 100644 mods/ctf_pvp_engine/ctf_flag/textures/flag_blue2.png create mode 100644 mods/ctf_pvp_engine/ctf_flag/textures/flag_cyan.png create mode 100644 mods/ctf_pvp_engine/ctf_flag/textures/flag_cyan2.png create mode 100644 mods/ctf_pvp_engine/ctf_flag/textures/flag_gold.png create mode 100644 mods/ctf_pvp_engine/ctf_flag/textures/flag_gold2.png create mode 100644 mods/ctf_pvp_engine/ctf_flag/textures/flag_gray.png create mode 100644 mods/ctf_pvp_engine/ctf_flag/textures/flag_gray2.png create mode 100644 mods/ctf_pvp_engine/ctf_flag/textures/flag_green.png create mode 100644 mods/ctf_pvp_engine/ctf_flag/textures/flag_green2.png create mode 100644 mods/ctf_pvp_engine/ctf_flag/textures/flag_orange.png create mode 100644 mods/ctf_pvp_engine/ctf_flag/textures/flag_orange2.png create mode 100644 mods/ctf_pvp_engine/ctf_flag/textures/flag_pink.png create mode 100644 mods/ctf_pvp_engine/ctf_flag/textures/flag_pink2.png create mode 100644 mods/ctf_pvp_engine/ctf_flag/textures/flag_purple.png create mode 100644 mods/ctf_pvp_engine/ctf_flag/textures/flag_purple2.png create mode 100644 mods/ctf_pvp_engine/ctf_flag/textures/flag_red.png create mode 100644 mods/ctf_pvp_engine/ctf_flag/textures/flag_red2.png create mode 100644 mods/ctf_pvp_engine/ctf_flag/textures/flag_silver.png create mode 100644 mods/ctf_pvp_engine/ctf_flag/textures/flag_silver2.png create mode 100644 mods/ctf_pvp_engine/ctf_flag/textures/flag_yellow.png create mode 100644 mods/ctf_pvp_engine/ctf_flag/textures/flag_yellow2.png create mode 100644 mods/ctf_pvp_engine/ctf_protect/depends.txt create mode 100644 mods/ctf_pvp_engine/ctf_protect/init.lua create mode 100644 mods/ctf_pvp_engine/doc_data.md create mode 100644 mods/ctf_pvp_engine/doc_project_overview.md create mode 100644 mods/ctf_pvp_engine/doc_settings.md create mode 100644 mods/ctf_pvp_engine/hudkit/init.lua create mode 100644 mods/ctf_pvp_engine/lib_chatcmdbuilder/.gitignore create mode 100644 mods/ctf_pvp_engine/lib_chatcmdbuilder/LICENSE create mode 100644 mods/ctf_pvp_engine/lib_chatcmdbuilder/README.md create mode 100644 mods/ctf_pvp_engine/lib_chatcmdbuilder/description.txt create mode 100644 mods/ctf_pvp_engine/lib_chatcmdbuilder/init.lua create mode 100644 mods/ctf_pvp_engine/lib_chatcmdbuilder/mod.conf create mode 100644 mods/ctf_pvp_engine/modpack.txt diff --git a/.gitmodules b/.gitmodules index 184272e..034060a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "mods/ctf_pvp_engine"] - path = mods/ctf_pvp_engine - url = https://github.com/rubenwardy/ctf_pvp_engine [submodule "mods/crafting"] path = mods/crafting url = https://github.com/rubenwardy/crafting diff --git a/mods/ctf_pvp_engine b/mods/ctf_pvp_engine deleted file mode 160000 index ef6e358..0000000 --- a/mods/ctf_pvp_engine +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ef6e358de558617181c5acf52299212c8fcb9bce diff --git a/mods/ctf_pvp_engine/.gitignore b/mods/ctf_pvp_engine/.gitignore new file mode 100644 index 0000000..b9d6bd9 --- /dev/null +++ b/mods/ctf_pvp_engine/.gitignore @@ -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 diff --git a/mods/ctf_pvp_engine/README.md b/mods/ctf_pvp_engine/README.md new file mode 100644 index 0000000..40684c1 --- /dev/null +++ b/mods/ctf_pvp_engine/README.md @@ -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 diff --git a/mods/ctf_pvp_engine/ctf/core.lua b/mods/ctf_pvp_engine/ctf/core.lua new file mode 100644 index 0000000..ebc67e4 --- /dev/null +++ b/mods/ctf_pvp_engine/ctf/core.lua @@ -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 diff --git a/mods/ctf_pvp_engine/ctf/depends.txt b/mods/ctf_pvp_engine/ctf/depends.txt new file mode 100644 index 0000000..5588f85 --- /dev/null +++ b/mods/ctf_pvp_engine/ctf/depends.txt @@ -0,0 +1,2 @@ +chatplus? +hudkit diff --git a/mods/ctf_pvp_engine/ctf/diplomacy.lua b/mods/ctf_pvp_engine/ctf/diplomacy.lua new file mode 100644 index 0000000..a753c8c --- /dev/null +++ b/mods/ctf_pvp_engine/ctf/diplomacy.lua @@ -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 diff --git a/mods/ctf_pvp_engine/ctf/gui.lua b/mods/ctf_pvp_engine/ctf/gui.lua new file mode 100644 index 0000000..58a5964 --- /dev/null +++ b/mods/ctf_pvp_engine/ctf/gui.lua @@ -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) diff --git a/mods/ctf_pvp_engine/ctf/hud.lua b/mods/ctf_pvp_engine/ctf/hud.lua new file mode 100644 index 0000000..32da7f9 --- /dev/null +++ b/mods/ctf_pvp_engine/ctf/hud.lua @@ -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) diff --git a/mods/ctf_pvp_engine/ctf/init.lua b/mods/ctf_pvp_engine/ctf/init.lua new file mode 100644 index 0000000..61d3f8f --- /dev/null +++ b/mods/ctf_pvp_engine/ctf/init.lua @@ -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() diff --git a/mods/ctf_pvp_engine/ctf/teams.lua b/mods/ctf_pvp_engine/ctf/teams.lua new file mode 100644 index 0000000..362fd6f --- /dev/null +++ b/mods/ctf_pvp_engine/ctf/teams.lua @@ -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) diff --git a/mods/ctf_pvp_engine/ctf/textures/diplo_alliance.png b/mods/ctf_pvp_engine/ctf/textures/diplo_alliance.png new file mode 100644 index 0000000000000000000000000000000000000000..be56c425c1b0f4d2fe1a0a0de87293a3c2db1257 GIT binary patch literal 6679 zcmV+y8tCPTP)Savxbzckf-r%i`i<;n!b(rT@c+nSJ#u01N^XGZ@4Spq@pl zq-v_B;@^<@FNg^KbOJz#ZDZUb-;%g(87aUh?xtspUUj=skyMsyygeDp@lvF{L9a8tekK&`tP_( zT|+#?o)}SBw7+FrIFN4n5sO_ZHc*w{8&;Q{Ou<%3C-W z<$g9-*5!}!GXgQ*8k$woQ8n5oWMpxx4Bw0yq{3D*Mf&YV{5~V&c*H$+)?2Ho#Yy!R3po%$`<*2eyaP0xF?lK zMp)r>MdXNuITnG8JV1I**-D}$kt$-DjEO+ZAST~pRPNx2kOtqMcqMsm*?T`lw&*rf z_qyx49*@W4@$qjC<(1qvOKp-=L+kMNx3+%D0ZK_>t zqKGs;qS}SdnPz4pvPGWq+~u&9^~Nn59Yv8D}%*| zk)~v7q-5qfjUz1ain}Ksv6YDM&;RmIj80Kj{!pPleiv?#b%%dN>gPgPNC$2lY^QK*F?@!!Bd#U{|W z=D4na9<@l0D%{25MPp^$Xf>cyqDa4jv|m&?U_`3AMYsFC=#Dy#2(%b$t?S~)wXSPj z0nr>$keXSRWTs>BXON-(wtjq|3@S`k87HA`z2Gn3zQJaJg5I{wzsQnoWl0dLZ-IZj zO4>T>Sk0xf>D1mI;r-%DY#3&E*M2uS!Z{xMH z+5rydUl~Rq!N}OmRi|W;>Oo({bC9S+EoqU8C__wq`XXCxTR&17e?;Lkz~r}WRdM{hvZCgJjMS}5P1OHKEWx~ii5M`itc^hMRH|ZOkKz#~ z@j8Q-dELd!784&4VPauq`b-k69YrJ8n>gcUqqHP~S;UgCsR}J4j`6h?uFgKN?@<>W zoVzTGpj9D~&SJ&EUE0mLZ@5u503X|W)Ne=nIR#)w~b@?TkUYTb3DE&{EU9&m<8oKU848I=`h{XgyCVmqs3PvLnz zg+VMm$A~nzy9D)Ve_<#0y-9wB)Bqv{pa3%)uLU!(jjBrtY7k>l6J4UD$_L>S#GxlO zfUtO7yzZudGX6w3djY0(gE8B-YI%3qh_sH z6uVA_fFcRk*4mwviM8NlyY3ZMa^6N>x)xj`>&(>^VpST67P`J7waIRVfktvvfWgY_ ze0fyu4z~<2W&)8FEpae*IGrT0Tv1tMhzQ_7qnSCgd!#$DFeU)w_PP1jGtoMbhng9| z%g}pWeaU`4MUnyY%;bpBzj&?X_fr5w$+KFZIcJGY+>SJUXT$6~Yb0^mq}blgeOYfy z1d0OSL3?8{^VI~D?+cl!WGZrm>+rbP>a41wvx8ewIn4s6tDpnYqP2`32cpRu#?DyM zl%57|v#wVvPR_Pb!@0wO?FcprG_1pz+m$6decjZI*ZAtvoYZ4%xj8CoxR&T*{_KSHW6==tPn;Abr(mqJ#L2+o&8m zn69YElhP>+`a-Xd8h2vlX79r%0ydb4(+-*IoEc<9=V;`pTM)XJ5!J1-qqp?hrNv>C zO^*b-rz$F2#JO7WK4h@l(KEidd&bS-G#7WfFdNQd-Crn>R;FD5<%P4ab?Zo2bi7ej z$*NMVl(XSc$P>L();TGIeRrkvU3k2UsSW0T#QWNA1|2Nr2v4_+WCm-TiE*3RK(3Ah zZZ+E%>XScx`}0}8pzpjJ;sx-t+_7#VfAPYEV3h1uN=;}wnq#QVuf~{m06+Z&1CZoj z*AprZR+5NJaZ;v}S^<7vm+&2#LJ|A^F)_Ild_nWAjZd3O7ha#cWWggxx)NJkV ztvl?M3`EY3Tq`iMw~edgl+mb!A4k-^L8uY^iY^Q@uq1+u19xg7ehOune>1>hAgYXj z)4~#zcbkt+eAirc$(q*abflPpFxHfoh4ZMtx1i-^amW@#!7;a>Nw_^u5+jf13Y!%H#V!yn+O%GS~nq3k#m8X;gsJUiM zEhAxDWe}VxqIg_CtRJzKi>N#zGYBvaG&QMTkP<7PVCV7tM%lW& z{I+pCYN^{_41yuETHd*+NGgrgM4iUK60+dI8Y zF}FtWJJtfw8qji!>@81`?Mg5r0yD2`U61v6T#t|S_*mCt@d_;LFa4Kq-)a>*)s;9c z?|DF{!s+gX0Eid!LNu(SvlkFKB^^ZC3GtN;_s}3JA$xqf{&A~pRrUAr&eavjWbs}@l+NJXOYVA$9I3%)mVz+|<`)x%+Pc1MrTyM(@Yhjh)Q}o7_ zM#oQ0MfLM3`@i+6vPE}_HYlt zEsOL4T42T%(&hqr`QmCOnmB>TCTlXvgCPd+Z7gxI9oW0fDCD6SH7cFN6b+4O^o_oj zXp`AZvN+N5b|Ozzf$RyNDi)E0xJ^ z4w7O<8QhF(C8}>an~7WzsyT*`&|GU)xM$OgFdFA11-dQhPi;!J`kQXbeAJ?|VhaMT z#btIr++0O}`&<`4u-qnGRkNcL*j##Roi1Lhf>h*30%PI9xY%y}^|JxX^RjH)=iWrm zz#JXUacW-U8LpkE$H}9rjh_7g4OI~FAo1UJ{;G;8eNJn}D%*@Iwfpw1AgG>sL^<_# zjG0Kx9h@4syLM`HXy+y1)$DCHf79WH)H4}ty|6K z0zsoZ1u3&Y6|9D+_ixRQK$S#95aV1iuOv7H#i>Sh$Pm_@HIItvR*}p-mZGHx;e!_U z_(TkMxMpOFeLmCc$3|BitI8*uUGyrH3`z!n`@9O?^IV5JHqlniw%L-UobU(%(cmnr z^3t4tsJQ}rA;ZqX3OHGVc$c13X^|IuFIfY$Q;xT2D{53z_dZLQ7uD@c1aJO}nzuLr zL%o4knzT32_U7a9P9lA-N zXM8H1xamb_wxo)EFOi9rhMETUswWJP9xG$h^|`nHk0Tmw@lf|HYi6jVSJus0r?AEBro;Hs;oY2P zgLC%5&JKTNm5?g@ZV~oAir}kwbd;5(nHE{`tbej&Q@!};OuB( zDs@v#H`|GZ00cQTS+e60zUP;s3O_sal_~Pg@KniBoYE}aK8Dzv+vQ7OszY8GSs**W z6L+7WtOvg6AZd~l7$|STJ9L(?Zt~^lK`?*8Hw~@U+gx<(z2B3;dn39N_ZUr~6lr-@ zj#5RP|H%R}oHOX)FcM`MG>{c^o1LL@cOI3UBXQ)k+M>uu>B{e>0&W^jxCt#>8 z$D-yCVp=6r24jm{E1qk`Vn$FyhYzATR(aP~zwqI{eETrF2$RdzJ8M1GhqF4K9?gnT z+VUw|MsTy6(!#|$KKB|FATwVGEN{yZb|}QfGSf~e*iqafCpybtrwZSh1W{7$jzzae zKquMl&t+<9Ri+Iy-9o0jkVZ*DnpVflmJTW93BC@-Zg#ip8^W8Rv3F|0HzQ5mWj&n? z1mL=rK-gbH+MdMU)qJXt4pKo1d}SWMvl%k8#IC z<%*%rYejTLU@I}3o5d~Ur&`dM2w1k*mSxXzgrEUAa@4Oo_OmLG5vAu4u!(Km`zbgz zLStNe77*6tUo~JU(pC?FuI~C<&H55`5!!L)kejE%lEieIP>r+JRQ*&v&+1|}FmNDQ zlN?RVX~#iF=aA8%SmZ>ZG&%(*YMokzv0l9{HXE9B&Wy|{`x7_z9T-K(9#WX5=}OBs zqv!E;D2bbL=^Yj$lHI`Vm3;gHTU{k9sG`_F#lfgqO}Vu*R;|KkRz&`52Rl;(R~}VC z1n|NF8!hB0ZkPQ{4WgsT+0-eYH&xv(yFA*wb^{-$7H5p&9IyrkVQ<9>O74bWW;i2For}&a##0E96nHdL}9y)>| z2dR9cIG`zpu?{=4!Il>nX=}Zm>BXDgWuz#bwd!}9yiGL^%H*~GG>Ld6Ytm#mbX5Rc zq9|X%8Z_>n!dOXnl^C$3`_Op}UPlo17-$M(rs^`z$pR{Me2E*Fdqj&5%?T-iKuWTq zY)=wEj`IP=kruZTEm{vl%qdaQ4BiDufSMtT!g=bNrjTOPk1u1~{YT#66OqB99w&Wx zf2fZ$ftlHsZO3+1Cu|C3bNBd=qUQyusBV#`s&091-7zecZD!`R_+rQItm%9!{N=aY;f!~hmKLdD&Y=F$^ePtv_@CQaam0laC}4`YkQK%3=t z8kk|k*YK0?pD<*D7HMJeoUQ-(;fI=?nk7I_6wFC9(0!1lyPfVK-IDhR2vwMns*=K~ zS4)}niH!%mWg_)K@Lc*44sSuPer9AV@}n>A%IWGUiR>OibQ6QOnaenbrvSrp1ed6lYQJI59LDxB`<{EA6qOSc%J1E@f9whmmz3&?7t&@g$&Y^DI0UQm7M-9zK$1qa8;hfB;(TeRd375)zfYUFueH8=NYYJo_ea&;m0* zuB(U9M%d8tXhW!;k`0{G6thygsbWXjFz0kAMeXBcf$1tg{qWP*dfKCgF4j~Drk)^V zHq0p{B6DcCd?L>(7$du=W<1DVG9l)So(%?7>kPQagmG5N_0fG^KF>0 zW#GE14PxVXh_nX43?eoLfcwCblObryUKCiJH_yjBH)X)aL0P3JE9E#XyR1Vr(6c$m zX(QL){q*-MfJvgJL($Vl3Cn}XXxc}~PMY(c+#m*B*#llFb(n{K9of**SI}Cbk`nv5 zTHYLu5OwjDN8fm{(5DhbQ&v>`XrYRY_wxfN@uQYt?rcypHSl42Gptj|BL);8y6f!JBG%Zl{Xt zx3s1&3xia_-zPD|yvjFw+bEj5HPd#tZmjm6hv2*=3B?l^T{(*9S8>=J8@Xv9VE9g3 zbe=}gm)xf$I1R8eo|R58W+|J1d=+NVIFT-LncSvg651_x-O13JWq7xF%32JphPmWn zCOHDa9HP>7oZ*q&jTdcDYmFzRp&umukD+8>os4><6y5jPOf%LNa{QtjE>V`i2AUjRq{I=U5FZF zHoKo$^oG>`a#+Xym@W?Ic_N2|akygl-%oD(O*#77(wo86t#L;!EHzs;BqY~0v@?Ea zmW%E4d}r~R$WJbKtn1^t9@q77e3&`P4&3rqfptF>z+MiIDQVWqQ7s-_L(TI9$Hel` zf9i28P@T>y)+aqEjpKsq+`2j_c^-<#i%a%hwVR|&N91eaDf!Gs>_?$x|+Nod_@who%&NW>;?@iJ(Ff77hfeX5uG09ZZZL zKNlSi?w(fBA_DC*qA?G&aEX?G9~CNqw~Or z(2(7uODN)MG2w&uTMSSAlDxJ)#8fFJY5JJ-Qkprs3#mEQqBYKcNQK#&12&bBZ1#%k h!($44T<;9$e*q$X`?sveTw(wK002ovPDHLkV1iNV^RoZ| literal 0 HcmV?d00001 diff --git a/mods/ctf_pvp_engine/ctf/textures/diplo_peace.png b/mods/ctf_pvp_engine/ctf/textures/diplo_peace.png new file mode 100644 index 0000000000000000000000000000000000000000..6f0da5bb1590fe1d06f6a7a67ea11996a5875e41 GIT binary patch literal 9263 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 - show details about team 'name'") + minetest.chat_send_player(name, "/team - get which team 'player' is in") + minetest.chat_send_player(name, "/team player - 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 - add a team called name (ctf_admin only)") + minetest.chat_send_player(name, "/team remove - 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 - closes a team to new players (ctf_team_mgr only)") + minetest.chat_send_player(name, "/team unlock - opens a team to new players (ctf_team_mgr only)") + minetest.chat_send_player(name, "/team bjoin - Command is * for all players, playername for one, !playername to remove (ctf_team_mgr only)") + minetest.chat_send_player(name, "/team join - add 'player' to team 'team' (ctf_team_mgr only)") + minetest.chat_send_player(name, "/team removeply - 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 diff --git a/mods/ctf_pvp_engine/ctf_colors/depends.txt b/mods/ctf_pvp_engine/ctf_colors/depends.txt new file mode 100644 index 0000000..10733cb --- /dev/null +++ b/mods/ctf_pvp_engine/ctf_colors/depends.txt @@ -0,0 +1,2 @@ +ctf +3d_armor? diff --git a/mods/ctf_pvp_engine/ctf_colors/gui.lua b/mods/ctf_pvp_engine/ctf_colors/gui.lua new file mode 100644 index 0000000..6a0beed --- /dev/null +++ b/mods/ctf_pvp_engine/ctf_colors/gui.lua @@ -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) diff --git a/mods/ctf_pvp_engine/ctf_colors/hud.lua b/mods/ctf_pvp_engine/ctf_colors/hud.lua new file mode 100644 index 0000000..40d6f47 --- /dev/null +++ b/mods/ctf_pvp_engine/ctf_colors/hud.lua @@ -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) diff --git a/mods/ctf_pvp_engine/ctf_colors/init.lua b/mods/ctf_pvp_engine/ctf_colors/init.lua new file mode 100644 index 0000000..ac6e445 --- /dev/null +++ b/mods/ctf_pvp_engine/ctf_colors/init.lua @@ -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") diff --git a/mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_hotbar_blue.png b/mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_hotbar_blue.png new file mode 100644 index 0000000000000000000000000000000000000000..b0e7b202813cb9afb130812eefcf6fe07c7a592b GIT binary patch literal 545 zcmeAS@N?(olHy`uVBq!ia0y~yUlDE4H!+#K5uy^@npa^Gy zM`SSr1Gg{;GcwGYBLNg-FY)wsWq-)U!^g#3aVc;K0|VnaPZ!6Kid%25ZR9;RfbaXBHBDviDM1Y0*cZi6HQx-E?dB(kX_X`#;t` zTbH{&oBvIUy7kSIer1~`&)qfkBHP}Xhl9Mb9z#VylA+D7A{QKfS#VqGH zltOjg?DS=5c+a85z~IQrps<30fkl*|VF43^LjWTKlBfXC>rGs6QK%xQD7~}-T?ld- zl3S1*3=&0hFwlj_4hCvHpgt?S-CHwx(m@-~Nh`B&uZ9Id*`jU1!PS2y*5Ch;QT(lDE4H!+#K5uy^@npa^Gy zM`SSr1Gg{;GcwGYBLNg-FY)wsWq-)U!^b7tbT{WX0|Vn~PZ!6Kid%25ZR9;d*`*_Np(+u``iv{ zTgqOxY4Y4RDeBfYPx_@MZfU-Erf!|-+K*5XkmM}KuR0e_f2@f9ob|l$&fb-UAf*sp z%X5yXF&rpo5oKstz{DT`^j#AdgF^r#gTe|11|(4qEd~ZhR+uPE5mb~;T0z!;oQC8U zBnN{;ksJ*429kq8S|14CU40l7aLtvIRIcZ0Hp2p~Y|+->>s5ax*2n+M2!34*4;yuB dMo5s?Kjd3qa^+UORB|jxz|+;wWt~$(699Uxpg#Zr literal 0 HcmV?d00001 diff --git a/mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_hotbar_selected_blue.png b/mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_hotbar_selected_blue.png new file mode 100644 index 0000000000000000000000000000000000000000..760257c3c77162d479333ca80c9c08f90f58797b GIT binary patch literal 3545 zcmV;~4JPu5P)WFKHDrBbR`gAf`fqpmR>-wgHPb{7K%`WBIIJoz?3IPrX)is z5Zr+~GiU4ST5`q6%IvJA&z-TAzjJ1~sf zzW3cb0Dw{s#<+CVpd4yR!8wJL6pRv7)%ix1qhK~YJHs{;pP1X9rS7XbRfq91Hnj}dT2V5|pc1cP!o^%lcup_Bzk zK<0kOz!-tj4r(IAe#amM1tCbk<*?r|99x6OCxypnjgJo_o{k2`lYuo3yB!B-B(1|3 zfd|raMnrzPAJLvPjwb_32o8HbBLe3Hm87`67WnEr4SxR9@8N&m|1FF@{*nigIzdzl zfX8rpAGl}t4uC*Cac0+PSUbR)9!9qygh8uU=!aXpeO2N1MgpywaZCP?krRz^7*rsJ zHWtP>SUsS38Z8w#s67}ZD{cV*v?i@PDu>6Xz`0UV>>3WV2G2*0ufG|fm4UMg&S`jW zq1812;Oa2vwg?YT3SDQCBx)1Pi7_tSe<>A*9fuSYRYgImnCnny72KpRjc%b;gVxq~ z%8(fKs#PtVQy9B%z^fbd?JfRP-GMT>;+p)W7^|l>hNR=-U+*mlVNhd(_INj2gE9$1 zgQ`hL?z`DKvbqBkHLM<>#)qT>bNxPa2XJu!7kdcFA$TC1HXdDjztX#mH|S3c56@R< zY6fk5Vt}gI0RY~;tx;u!;qj%t(;!p;AV~qM@d0Xlgc+aUoB}{VsemyjSbc(X8f<)k zsqQe!PjjU`Au} zc@0WAtR10;hjc753S|;W(Y=oPjevxV*w2 zcR#`p|89?~1INnBpI=IV@diQ$5uv&TWpa9PpI+Qi1vm}wG>i$zjcgy!bxtS5Q!1cG zfKU?fkNyie5KEawUlduttH}KvCVompmU?&1|r=US* z$V&vaY9?uPVlv7k2%+gb@9oOSZXz_Y`fewn<*P(}qg&|l5#Bp6UccyDM?;962Jxao?XRiH5reQ$B9&6-0A zlgVL{vT)`_WM+DFP!38v2!6rJ=k9>D#a&wK(7=Crv2mGgDy;aWZs?+Ndofb-R;7}0 z(V>_8P%}ejtlDaG&NgjwsFZZ37Yf86X{sc6_w8;ISvajFj=z9ZXH(WGmOyfQvtq*- zLKS#9o_Ee9KXj!-y$cn_0-xnOCGpqBheizTQi8cC$}hC=dA7+I7qW6$l6jRJv8LZ_ zb14wAO)=xdfYf-Ai#?5@XQDuX7jAt=YEAyHR~P$=3Ry}k&pdH-dh|yi?%apT3xW1 zBQ!Ogg-&hGLrdh%wb-`u&gq$9TGpIPl%(N+&?uLO>PwL?@R>e66|km*cRDr99d;ab z#pX4oWTB{-#q`UX^Z$3COv0Kjv}sOmQqzR?^3)NSBQr!+EOqu4k)H$S`^+ej4G$oz zdTyOmU@7tpqPm51I<%GZHNG;NpSR$64CA^hq6A8AlIGAKT1$$)w@`Y~&KUjBm_-4A zx(aQHmceFjnDe$8gy3SwkH?8>KT9 z2rY=bY?Mg)D2(%9xc3cTM@q@l&se#*OjtSZv1J}O_cp16+ww!_zGy^d>A?GT$QvEj z4Yyh7S-O|!z<3SsKC?eG7yF1zZCq)1STLK1eG#cI%5&+!dr6;~6e;E2(5izu*P$is z+;;9?s=e{0A4(}lEkjzrSosAmnFPst?Ca?}r7*T*;Wp*5JTDIYwi{~I>~r)e@_Dw2 z<^g|%7^48SlOpC>xy|l7AY`KnWpK{m*xBSZ6+OBP zJzw^QX1z^CA7c{xe4v^T5n zyEsEBb}|>;c^R3`s#(W(-oKFYp+=u(<8`7@DloV5`M=|!#m!cne3sJOdKILeqt0`O z2)u`P0XK}1b=5N;OckwsEI<`lb7*ig*J^P!bHq6fJsxpvO)BCSnDpl~&o36~nmW{! zRv*vCv<+J4qykO}~4{dys;^<1zO~uP3gb1mrGIL~(NIQ8$ z(aN`-nR!FWZMrn^rNBDu^iHR^esenGd5FtYq>~(VMT>L4CF-#iCu2at>C2yYCufzB z63;KJ{EQo?5PdTrnsg|W5Ijuy-Z!O}Y-Z&%FX&RkZH6(DbVRI`lqQF7cblTfuMnE+ z&^+4&0RQ+e|ND!N4R?(`?drp!{KqO2Sm}H?Obo=5>uqp2w|^qfPfX>^R=NtuZQxkDrFLdBL04HQv0E zkb**KhvPJ|Js-7xoW`_U=do*ey2tZTfQZST2+O(o*H;48dDN2P}*SmxSa)nMCHJ#04Bu8S@gA}X)ctMbmHn7$5!Lm*~IO;9Zy`7b5$k7 zt`65%A`SC@d{W`NOXH{BRvy-C#mcpLo`{mKX`MfBaWOsH-HimJ1cTZ`jm^2cJ^$a=JszGE?jKbekRP)xT?YusVW#aNfH3>1?F_n3 zpFin?Oh^Ml88-G;#_OW>41~0(my25djcMx z6xwNbg<4YdgFWxsFsUwWd>BJt-NJjDikY%`1Hr@A8pF64KtL!<9TCp-xc~SE{M)~W zuf(G09y^P!y@wj^XI&F<^bR2m$Lbc0H)()>+@T>3;Ey}ObnSVr$39TQbmNY zG|!$=j;_=A`p*NDc4;RHo`;-q2ZZ7D9^dE|-=t$KY+0dOX!V>FbMD|$Dxg*f9n$>y z+}&Wj0lhFRCL$%4Wa8j{I;|TGmNWfCu+uquS-ODJJX9_YP~&5|7MKsTqP&3`A3-Tk z!&zobGd)xO9^T)h1@NE${at+L-A!7b0O#$0n*HC*ULtl$#Hy$@J@ojPE}jJf)h(FV z&+7ZmshJ|rJqkhvxY#3pf1dB;5X%fc+gyOJ*4CTr=basK_XaKw>D$bBJZA(D5<0gt zg3WTE6LHrSPXgJz!EpK-k01U6?|=83^VsW%)ck?w)Ml$P$H%_b-Qh3$?!h^8AM1 T!ng*H00000NkvXXu0mjfTRO(< literal 0 HcmV?d00001 diff --git a/mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_hotbar_selected_red.png b/mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_hotbar_selected_red.png new file mode 100644 index 0000000000000000000000000000000000000000..0d39112e76b5c7df7e84e7b7e0b66b6327c2fdda GIT binary patch literal 2905 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 literal 0 HcmV?d00001 diff --git a/mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_skin_blue.png b/mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_skin_blue.png new file mode 100644 index 0000000000000000000000000000000000000000..6b711ed1b58a50b8160288c50faf3f07a9a1da48 GIT binary patch literal 3250 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 literal 0 HcmV?d00001 diff --git a/mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_skin_cyan.png b/mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_skin_cyan.png new file mode 100644 index 0000000000000000000000000000000000000000..2acfdb8a074d8ef60994caa6414a3302a9555949 GIT binary patch literal 2917 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 literal 0 HcmV?d00001 diff --git a/mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_skin_gray.png b/mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_skin_gray.png new file mode 100644 index 0000000000000000000000000000000000000000..8966626a8d4548700973d6fcf5d8643b3ede5124 GIT binary patch literal 3023 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 RBh3H+002ovPDHLkV1nQ9>dXKD literal 0 HcmV?d00001 diff --git a/mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_skin_green.png b/mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_skin_green.png new file mode 100644 index 0000000000000000000000000000000000000000..05021781e03fbb71cb309d5ac233c6c90f332fda GIT binary patch literal 2754 zcmV;z3O)6SP)2uX*6~`Is8-X;*&E0SAO>&cvke%!y3xOmA1X`so?O2^U+G=e9kx~mtSV|M1>{}P0 z6tr}4X|WZxTJ;a`g>NiW*4CH)5y$g6w}*Qt8RupuDVh1s@A;i`o`-wBXM29k$@kY+ zT_PRjMY1?qD2r2t(os7`&f#QNIw0MOPIVqq za1wcI%A7}z-ffUgi-4)m>>tkJ+(1o8hIKux4lh^d-C3N67}m(qk7R9TP5{mkNiv~d zqu^LlRbo!w{QYUUaruZ`d*=oD_|4t&@ugjc>z5D9tv|jpV*+rFNRkO(X-VA0Xj9Cb zobyHh5q%uR>B%7djez%$$@O;+$kjK;4FCS^e!20xqjKvHFVFY~lKMD`)045HF?J6K zNKGZ6SEEk`7y(yKYqXCz0UG_E%fH^(i3|c}+5^adDFm!+ipywY?A{PSjkhas7RPx) z2v7P08F1}a6Y|NgC*<1Od*$j|d$b((%FXwWPbq=?r7-nxpvGz)XGwdW5WqL3Lz+Iy@oQ-#Y|IeJz25a`o-;Ss@^&$$6t=63`cDj9)wfbbbf`IGhV6 z!KWXcmm43PG~E2)6d?5tw`YL>IzI&9EMz!%Up5|8p&r+nY%QSVVXk@g+{SR+G+4YurIFK2KT?|wGG)(K$ka%!BY*pKbXLnPL4Qb7otX5P%H$yXk0hev9LF=aJ?@LzHoDq#@hw zeG(u;hYhx#8JxjcoS8WrLJUrTznjkHJfNw4`j_)^`%h;)JxOtXD0S`xIFO;k#_ie5 z8JxwL4$lrR70)tsM9h$TZT0lz^1~1Pf8~n~8@G2kXZg7}=}px82Ww>Lyvv&d|9tl( zPUJeQmz)#AUuWlkUB(&_x?DooYiD@Pdv@`Js2sSC6!w~&wT|vJ&)J>{5S0sLMgY<9 zruErhU!M|foV@erv;OWthHgq$aE3Qk?+KKMmyQD&xuKnY>R&;7gnd5ncjJ1dl#zF9DzH{McEY z>72>g{uRkY@aVHfW(4@BEjK!!-`P3-Do>5HkwC-&Yh<}Lx!^22le0DebBk|y#H@USFm zzT=Sx)5?JMT`{6fLsVE(g$T=vt``#0_;lFTJ$sVUH<6P5{TX!ZNU$&O`H;9&NgI8= zN`_CT-Fbh`oPcQMH*nrah*v)%!C;WcTLzSrl;r&fM$T4B{gWZmpYHA+ zo5NE3eZ6!&6OsBKXXJHCM(@59WnuC~PXu`yNiACw1eucyG+2 zT=}q$zim-<{1{-33>`Muvd8xJ9!;8j;8dA(?ADv#p;GDDr#M(DUC$?^=fzTK*rZPH z(DTQXz}-=Mb{|mUp3R)oxH%+?$0E|YJu1CNN`*eexq~Mw@)JN57X%tI&X<49(!aF! zyEH)00waLlkXZO_D~-(hQOL0<)cznSjXw$5vteV1Xqyww>mz_QGIY7N>?NXF|8a}l zfYx{SZgu{JQp$t~T4ZY;4_JnD?o3z-4V_9;LY4_Nj|HUesi4&TFl5uAWph;e52Q@L z@?3R(0*I!kH<5P&a1?SNg=i;g9z||8j7l0Lqq60>s71A7Ph3XMr=?k=OEjA|M1_tG zSR+G+4YurIFA<|T4!&Gw5nFMwQhIf~^&L-|UFUe52;2VEmln!HSL$W$TeY(MOa&3Q z3~1AoA3B$ofulMaPo!)_uKi67cKHb)jd2tQW7_oM8fBcrQOJP+);JF!3VYTNK>g9Z zd!AQE#uBpV$r9?4PED5wtdXI^23z*9*W_*6tvPDVCv6(^?9J#~l^`;u{^<~7)FQh4 zSehq#GKmZzGZ?4Ht+(_}B;9U01gs&YamYc0y~h1Qp+cnCRqlh=JO`#!CUPc8vOpD05N8Niy+=z5$-?zctinwd`2h%QlW z-4gX^0oKUSVS_Du*vpNOu5FpJ;!LGQa`_3nP0}N{a&!H*i|t-m zcubb;FLk@D{pm7UyfupRQ@%*~;KD(lGF-HB3BYmmpZ)FPGzdsRK(@wXmFooryr^G;Xx0dc=tuvz%!14p<{Y=NZ8s_FBqz?n=1apWGL)C)xm~Cx3b) z=Z?WuJC^`H$^AV)0VV2WFuBs4b`%s8$btn6jDV8Fu-*;UxK7vf1tqOQvmmw7qB!(g z+M{Rn+&Q5WL7nCuuttW?Bg`K5c9H@6repva!~MZ;lamQhCoib(q<_WUmEXGZ5})PM zCx8!~qM{L|M~~n6 z0Wt`19Wt_lHDJx&CGq>DKQUJZllRF;`In?OK37^pbEPXfS3WtM0IZRr!vFxgk;~+!m1FZS~2?+7e@Vo^44=zgvTvY;>uJFMN)(^mI%O?~9|y*EC5I5%%) zcK24*(5(SUULxbhi8$wf{^uW&!X@qJU*1h+9q=K6fU^c8Kmsv>0F{r#n6OsX-W7y@ zy>%iN&HonN^l;xVFmK=a#ZNq&=9R~Xh#)v;Fd~o=#^|uhM?#1=XJBPE5#gWy^ZteQ z&1DX#L&OI)mOG0TAti_@;X|bIk(6}vMg(UJ&gdc&iL12bhF3SfY?Y6AA2A{nnZ+6* zr9?=H+Q-)U8W?p@<_yMM&Az+T0Y2iap~ws(=j#hm9q`v*`4f^a@#TydJR~)m^#<6Y z$BUo-U7n;>bs%$=PHrEvAE|xhyYE~a;U{)LN+80TaiEDr(`O7e%SfRF0bBGCF|9K` zO7tH_K_dMK`-WtB;&!P6c#MBzTmhFrigCqZk$OHO-^{PHfXg^Q9`b>gQEi_6dhZHf!V2oAhToD0p(M^xz09mo$a34NsT85bZ9}s{N zUhN|Y4Y`32NgdEs1e4|9K#V1NPc@rRY!y$uKoRAest!%Isr}kV!V2>K&m7>aq4t58 z5`qv`pl1YJ(QQ*;30K$c;JS~^o{!uzYbax5U!;wCN{YfKcJR-=w40WtF2;y4(h`ml zC^AbOVq5x_X(7@qt`01bm{KCfgfRwZh0Gf2pw96jw&P5rCjfT(T`C{&Ariv6mM})> zyfwvlZnO%Vw5kI0Sh5DHkHGc&-e!6G3kW4Gi`noHA0C*^6JfRe&b63 z{MzsQ0nqS3Ok_?FjFP00mK^JlSkA5Kb6!$}bBD*M$jP%tWHqClGtIl;tmPm6<4^#8 z>6Png=>!{+Iz~{jWCUj{Atk(z#Hc|~2g#a3T?Ryc=Et|vGS~m^jV}^|=U0F4bwHgm zp4WWq&%cDR>vR!==a+xu^YptJl~PIzYV}=UjH<2zkQ82zc!)h4_k*s?>QMy z8{tArT1JFUzOGv4l@gn2r1fgWysEnUQH~MOD2vdNXSY}n;B?{D0b0d$o}H~tQc90R z`}TLf!J9YFOWK<^zs>FMldOWXM-LTXW&YDNH%KnR=O}ZgLjAeZ9?^kqwVm6yyxX|>qYiV?H#L*|dLOYKL z+u#3vwl`q3fbmN9^b45Pu#rJ0gW``a)=%k;8>u?EMc&(iQ1brAzs}<1kk0l2i<3iM z{*~9|iIkxCB`)i>Ig{skBak$YX0zGF$Jfabxl@I>JRT=tdjrUNu$W>E><@D=1sDrw zrvNPKtLXBYywiul4Vc}3`cl}syd^PXGpefk$S1FPr7UDz4kV(l87*MIlxYRM97qw8 zhu|4(cB#ra)A97*LNGSh%{C~AF*MIX(LqE+NHMUH!QQ4Wppz;4E<7B(^O}~+#f*b@USr|) zw@#*G_coRI@=lLl0XLrM)5+laPR@QGu5UZ4a?W7GF{>3hoyJxtgM)!?c4q@JqniiF zi!MfBf2YUx&d%j2P;5TWeEdU5iH)tFz`6qKa)K|3F|_$_vkuO62%*M`;9QrwnotMz zcJ9uY!=!>{heM z!2zQW4*~e$hl+@WSLci-a|WATM&l`ikL>}+7ds%ONN4i}jIoHZYb6g7B8mj942I-O z)p#O^STYOygY*0!J-E&OPM>?nWA+cO190!~E-&o&8I8y6Kl?n@>Ft*FcelFW=g>QV zP_{Wf$e>zG+1`K;r?5Nh!{z~WcA=gy*oEoIAusMKhi*bcsa)PErinx3OP~r-)DRbl7oX%lZkJ-OARHg>@uMN3(bV}u6 zFuqN0;baD7fZ55MegRoHBrm#D)8qE{+`+w(LY;aHVHZw+0NExd4-P3C^u4%?BBvN~ z_s%V@@8mE&glzM2!L%%QA_i+SjIo4zPV_2+msMdM8MNweBZKX|1Fi9c8w|U!zqbR$ z5cXctPcdYFZ%6ZVa09X)+j|FWWU2>xxuS>e$H<@gI21#+w|Zn&qyKOBa&XO zN6&P8+S*<{Z@9ce44zPz&At4(P|LcTgsNQQvVQp{iC@EsF zuD}?F_X~~c>ASkITC2j{qcPWuW6eVVTX6SiOvkA+D28zQF0)#5cz;llH@Usc-6aBA zgs$w-<^>WgG5AK4q(`pOvu^KdBpAb#lqy8dJv{l~AN87Q>s4Zy5$Xu3k!h(WhDZuR z45cEVchE+BcYbs}`|xG`9>2%+t(Crd0=)w^7KiNjoBUnrr=BqA_nA(|O~rZKd0$U* zV%-`7L<}(mthw6XM$S*ZrxA9p<$$6nh%pi--@_O~1VVEd3L)qbwt1e_&>0<)+oFy5 zdnfRmJ65P`>CnDyS{AQHhspsZ81eLG|H7aH%`u(-Ia84%)-=2nz}MJ@2M{n>L5iWN zSbFm#&JlfuaoHo6AdhuGUDxD!&U`+nD2f%9KmT5isq4D!*W263Xa3Y;C1K4SGAqm~ z!@P#fmi)!*$^pNA^N>0ix*2Tbr_|wu5Dg*eb)1i?5`}ZL#{|y6U;ddO#vsP%W}_i` suR5gZ57k2doieI}Q%Y3R(UlzVze%)oYpVmZ_y7O^07*qoM6N<$f+FGNH2?qr literal 0 HcmV?d00001 diff --git a/mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_skin_pink.png b/mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_skin_pink.png new file mode 100644 index 0000000000000000000000000000000000000000..a13562c3c186d2eeea9e9b09b88137df6dd42c8b GIT binary patch literal 3158 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 wVb!5AW*ySqPt79#I^{fcgO6r<@gA3+YKj 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 IM6N<$g6_`~E&u=k literal 0 HcmV?d00001 diff --git a/mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_skin_silver.png b/mods/ctf_pvp_engine/ctf_colors/textures/ctf_colors_skin_silver.png new file mode 100644 index 0000000000000000000000000000000000000000..291e8f644e60b9eea832dab36ea28a5e510e3116 GIT binary patch literal 3033 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 biNgN^91SM}cl$_900000NkvXXu0mjf<0AR&kp zDJ>ZE010Mf;6bEBN??#60pbybo6ovLctaHP_?weNndf35YefA1^o^IUrIXez3J4+#XEH5dUBh!F%ReI&+&wK6eQ z5WaJBL-v~gEjsDZwqIw^jQNdUd_E0Ij}H++aL!;vASH~^Zl#Ze5OL1H*lZ%gf3Do! zbA7WP09A%AB}=U`WfpRlI5wNeF5MJ_{KOETmmV^F~B0#pigV2b)XIG#{u$$4aAs8De)|x zeE~2c#5&xK2r(r>Ojx6zNf64RddajPMT{{RV>NUR7y)n5Nl)Sc8C$>RK77!$3^Dyb zL;y;7m5(6Qqp3Xn`F)R9Bvm6LO6ly1xLUq95P z)R|gZioiy<$J|uTSHJYx#^9p6$*GTif>&R?&zqOO4#01}@iD&r?FHWas~z&$!wn$O zdp5egTDZ`VmJy-dnp7==Qi;vf(t5mN9#q}^EMP>`%OW)7*~upz*c*BUK&zO}Ge0*& zO6f6g-}=UvdGqpaNqh71*SYnLVp1>EW9H|w2?--LlRv8DGHZx2Xd3WrM9rfh7!jf* zTG`|4w=%=9j2L4CYn6DS_oNsJRY@odLRmmc_+p43_PKj|mAkiB@xwm87(z--zfhH= z7>VBV&tLqE9*&yRT4>bUh|Ndrtm~TzCV$%Jn^*3NwWe9WC?8+HD0O`&0+Qe#zxWwF z%uJwYFjEX{v` zW*!mN?tO~IdDz;9jqYy$BeT%$!R$1&+wjtBd+Vq4ebPHu;rJ5x3cUS$h8t_Nj~r*X zvBrxZe?y*133^}PvQCpTtyZfRNSa6eet*viZ@ve4OBLeic$|Rw!*FOC9&SON!RhB= zda54tuD*xE{Wm-q!m$NdI1Pil@Sy*UrLc9ahQy4`D9iGFpS=15I4Xj1Eg%tn#a8Y0 zejlc$AVwI}Jo?-sY;|FKXaBjkx?nA^rKvRT{q~XLPY-~ILBxYsm&qS6pg0bB;}uEB-n&*R#*l=@WZ%iW7ts`s(qd7ms2yNRb$-#&}XBMFk|{5fM@h^#Vm)R~$DBU{_a4BpqfmNq26lGf=wVpB1B>%&+{o)o zM__$J|KHfsJpjzjYMPus3+)5k$+&#xG=uI9NQtSr^H`T-U5nrgVhm0G+pLXqZ9=H9 zA~@HfDt9PL_1<&`Zr1OqjuPJO_16Jq?5U3S>kYKe!%di(f-66U6H9RCzDC1#PmN#S z(Ddl7Ls6*b&z#UrFQ3$L09S8n1sRqK>U-<({1U9)fe*iQ002^ov}azx7>gJ?QSu-m zVj;!4G8mFCRO5*tV(H|tP@{k8L-3s+z(>x(m22=z^#=a?`|y#M;rgof`OytEwl`3t zdIMNKuB3SWjOO^6=M|*+!x|YE&cfmnumEhr#TQ}ahW7UXKKKIc)bu00B)>m>b!GR^ZWWL^h^ETd$_*|c<5dOhVZ====C%LoK@o&=i%)YO`Yyt z?RS7Gpyf`)VCzc?U-pS!W$>sfOhyI`I8$x)#zq&Gmo)#*ovx$eSUu+)MqWIjY3e%D zY?WA*81;Ry2}=vmX~C&iwBO=MeV;Ai*4l149zSomR+|_+p(^To z`AMOcb!G@H5mHrWKt&9Z6oeQGrrL07%;Sse8sY%6=W3C% z3THl``8j(|J$&l8lGW0JYJ*Yx>@j_xIiV4t73WFkeLcyEbrTUFVu&GN&B1;)a(?H# zij;E=2jqE9jFGVOU5qhAAk>GU5P}|I>(Bk6GWaia)O*%wsJC-yZH|86)V8xHwa@HK z&H8l(aR%1^4d&;dD0dG?i$_7Msd*`Yudps_gsRDMQVey)(rPBo5q*hq*})>9sw!Ho z7K6cnJkQ5ie)m1Ob{dLM%s4qUs-u8h2_+JQ{ VVZVU!mLmWF002ovPDHLkV1ig>&#?di literal 0 HcmV?d00001 diff --git a/mods/ctf_pvp_engine/ctf_flag/api.lua b/mods/ctf_pvp_engine/ctf_flag/api.lua new file mode 100644 index 0000000..478a5c1 --- /dev/null +++ b/mods/ctf_pvp_engine/ctf_flag/api.lua @@ -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 diff --git a/mods/ctf_pvp_engine/ctf_flag/depends.txt b/mods/ctf_pvp_engine/ctf_flag/depends.txt new file mode 100644 index 0000000..ff56a63 --- /dev/null +++ b/mods/ctf_pvp_engine/ctf_flag/depends.txt @@ -0,0 +1,3 @@ +ctf +ctf_colors +chatplus? diff --git a/mods/ctf_pvp_engine/ctf_flag/flag_func.lua b/mods/ctf_pvp_engine/ctf_flag/flag_func.lua new file mode 100644 index 0000000..b6542c4 --- /dev/null +++ b/mods/ctf_pvp_engine/ctf_flag/flag_func.lua @@ -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 +} diff --git a/mods/ctf_pvp_engine/ctf_flag/flags.lua b/mods/ctf_pvp_engine/ctf_flag/flags.lua new file mode 100644 index 0000000..a7a9d8d --- /dev/null +++ b/mods/ctf_pvp_engine/ctf_flag/flags.lua @@ -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 diff --git a/mods/ctf_pvp_engine/ctf_flag/gui.lua b/mods/ctf_pvp_engine/ctf_flag/gui.lua new file mode 100644 index 0000000..71d497f --- /dev/null +++ b/mods/ctf_pvp_engine/ctf_flag/gui.lua @@ -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) diff --git a/mods/ctf_pvp_engine/ctf_flag/hud.lua b/mods/ctf_pvp_engine/ctf_flag/hud.lua new file mode 100644 index 0000000..2eee563 --- /dev/null +++ b/mods/ctf_pvp_engine/ctf_flag/hud.lua @@ -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) diff --git a/mods/ctf_pvp_engine/ctf_flag/init.lua b/mods/ctf_pvp_engine/ctf_flag/init.lua new file mode 100644 index 0000000..1022d5e --- /dev/null +++ b/mods/ctf_pvp_engine/ctf_flag/init.lua @@ -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) diff --git a/mods/ctf_pvp_engine/ctf_flag/sounds/trumpet_lose.ogg b/mods/ctf_pvp_engine/ctf_flag/sounds/trumpet_lose.ogg new file mode 100644 index 0000000000000000000000000000000000000000..0d8bcdb4ac8f4e7c00e76007cc4df8ba52d4f6ef GIT binary patch literal 39347 zcmeFYby!tT+bBFcfK6|@agzdq0@4zj?hZ+%C6y2rB$h~*G$@?{(j^GuRw?O{5+w{! zL=hB31kOT#&vTyd{od=G@4C+Q{db1hi|JW&$J{Zq3@=?W1xWBu*yOxIa-4JtovlM~ zBCcKWb9DDVEGhUJ2i5!5re^x)a0swFf6Zm)TENV;5UcD+o3;?Ewn&@VNW0xg2j-}M;?I!9 z-Q)OY={N)^k%9 zHd6d)0uXSM<51{+ul)ZNU9B=*{`V2@)-MF`Ff99g+53EX)lGQ&{DevVK)4^kIK`U? z_xMR@`b+fr%fnIgs@jWmz2WbA)c-=lG1>vZi?R3kvcG_#VInf=CwbmqVa8v71_s43 zME=*|)*pI-7s8e2n&|;y&L%tj0Sg2V%Upy}rT9A&+#sgt9q!)Pz3pLHs(bGXE9rA4 zpT?TyoNTYm&iR8uuMw8FFfIiL?)DVk9lPg0qD^tH>~AWNHix99asn=6SW_Y=VWDKmOo4qmF+hmcpv79|3us-?_OqW&vk{hMQ4v{Z%G(#ON`U08%yY$GT4nX1WYozm`d4AO1ey% zyS%*SGS}*2^DxA!^&f`$%QlNM0sp}{$1K7voAg#I75zUsr%)hiMJ93Q zAMbx~4nd4v73QW!Vg2pDYg8DA0eV%P=|2$w03n$|_s>3JY$Ec~MDnGHh`E`<|C(dq z+LsdglM*msGXP)#;9Y6?R1g)dW3p_lvKvObo4v%CUCGd0#csljJ+5fXAJduPk8jk-A15Y$=h8UOlXD)aa!dZ2^1T0DZ&%=K+m$4+m z@IU6lOX%ax^#9Li`oAOmUkLnfApny&f(rgIsuXdZLP9bK5d9XPiqqbdfSjy0`)l z5}N7~$obz7^Z&i{zf6dL1OSLGwq*2SA4DcOS>aQK$?dIxKI#T5o*r@B7rSerxWGzKEVl%lq{&|mEGHe^hSx0+SL}|2 z#S08}qGPa2x#ET@%-i_^;?tO?o9-*qLmK<(5DWMOyoFOW9yH{RW z1xphApN203?3iMX%c@}Mf;?_yZa0(R0=dKe9@Pq?%>L$C3ahFym6e)^>xh(PnhX$2RA_g{9kCtc^Qb(n*9Mc10XCNqbvU;3~uez^1%F)u-@vCTKc!-p8V)P&=1W2 z2gZJOFgA_>#W#42qspo(GB`I011j=Z->s@zf7W^o|Cs=XSI59#e?)-c9^k^#b}YVy z$pJ9*0YIqUAs`~HxPbr*I^?muj1a>J!a5f*S1DFSOE5C||4IG`2<`)6|FKY{5*+?X z`I7yo)$%{>m;XOgv(}z9Ef#=zBoc+Uiby<%Ipu587|`35-Y|0sZfU1OBiP? zPXDSXibypXfO{NQz|--SP5td5aI6XIRQ|i7EIr-iPhZR*6&W@~k2O_Vn0C`m;C_+t z9Dj9Z7$9Wu9DgcwV3UBaT%e|Q$Q z(6A&8&C}Ko68r|VwQE#0SU(h&&T5lzwOH!9;FJfnb@IY7g2a`P2|{JrX_+d6*(MZ@ zr^K!%h-u2cH&f|jhPUi+08ljIbQpU4ty>g82CtQj1Z3nC9M9G9_|se^ghC%ZnUi@^ z04T={VPj)@aAHNQ0z%~|sD~&j81#?LYIU?hr%|?JJ$5827!_=|B|9ELDkHNTb@I(- z1Y2&Qso5tyJP!bzU3(6B}U`W%N#0CssO6e&MfvsF%(H*Dl%7O&m0i@E2L;7x%p zL_LaXtP7i@ob1jV-~RrjD&(qwU-4u9D^0s7Kwy!%@x5l$+W&!w%jpd&l5g-!P8zJ0 zJkBgwUCls?7M!y^{qU@7Wx#9nsrdNqgQ{k_=qwEVkS|ky=k_qu@G)h|9<1IBRRJteJ zKeJ-@4$Gd9PxW4%E!460Fc%xk6ve*cPeL#!sJW3N^lq_rBQ1$kaO--Vlt3aI>*W(e zJOBOr+blHXqI$B(8u!tXSGqNr!f?54ma(qR3w7nN6rDfD-yS&)Ety_nqdxr?ih?{3cM4a5de$=-!VwFR7!v!jhLs!hd=XS zc17XT3E>DSsf3VUD^#v@j3o^YpSH6^jWFlV{yMfsyh3jjM7DUBWURz;pkok#5Rjbg z{H{HfG?1S;y;gKixBgx7M(^CuyT19aPu~`u(@=Rj=m!| zgz4rU;yJP1^eV)72B%B90#~UZ3kq+irVFP6!yZ>Ff65Tv zzm{Rfik`M?On5}0Kck!(5g&>!Yq_sW#tYe#n>`~!;_U7D3Udd0gXX?}+`UFVEM)mK zRXP4vGNOgZ8eva_?WKr+%8cqSba>aD8igZ=gR8vA)ep%1f=aclt7F%m7LOoJ36d)L(z3urLf_ly; zOOr#-ek5$--(`HBXfV)W(FL`L+%o}dT|5&zdYfmP%B_X=OVv2$qIy3PUhiCy zrg)khVHAN&nj=4lBNGQ#iL@JmnasPiC+z2WtK$xh7I2U`@Z-=L_>o$cr#R9J}&`3xyw;_!8LVU8Tg6OB`tS?r@0`tqP{`BDOLh1K!BZkNL+m3QeGXaW-$ zGE!=pwL9abU0H4H1Q~%I5&8MiXwsX@ddAAHJr=`r_$cpF*JBXJvwsBBrG+L zIZc_-40nuNbRBDHu^WNxS+_NPLs_yaby5`vnLZUFnu=|4_o=w1y+kL`j|aY@OG)qN zJ3TDx13jR$q^-svghwU|lm#>Dx$;j~TB)(Ceb12V^TZD<~4&gmGj1ZptfMw*_j^~CN}yWcUBxNt1(w>`ECq);5hDK4i*?( z#FGXn&g6y_q)6`PiT+}{vwZ}-5hdWphQ4U<_Q}3##fZN5nir_eU%g^Vb6!kKDqH+@8|1 zu(tLG;H9nXsn>7J!({EwPQ8)Wi4IxzIbr{dZx#s9*x!}TT2l@T@b756_#nfvwZ-`Q zr~5+TF&C`DspJS3E6fTT74~p2R|# zDhVRY>TDX!DriDLz=jC|2rGtYN@A0h+S?cSS~fq-5p!K^e?0rTVD!|%H*CBgYH9mP zYM)15Eu;T*Ew^oJc^idJm31kZs)@3vG5?0jxV0Q_OuhG9VNZiPdlWZ_m4uHAu3q?Y zCBL^P`&`m#o|Fp~@fVRhQrWtpTF!ZC7eg9@a%1`WDe>|*``>3~d+fv!)!JCSY@CF!e~ytjfq^c<74^^Cm0AFHsS1T0Cp>%;tT`jq`3m&*De)YniOE zRvc@N!E1Nsys=TIQ(FA1UDw!)Q=TXDfT*(+%b%nghcz^9CSL0N=!qGr9Le+RZ1ua7^kPxYay^e5qp{a}1Tmi`tbKN?F)4>norPmQ87OYAeDj%R zBDEwXc!>-=x}5)};Fsl8DD*h5gSTb} zyc+TOC|jwc**WeSmjcdK&%HCd7hZYKTuf_!c25Be38hK4sEQP)tXhXLy=q%|*vN7q|WiSt9FbKm5LSG+tD)eWjItJHv<4G^RQnB!(0D5R;QVM{Y%J)w>%B0W3y~ICk$9V>hqmt{?*+a{~$3(V3#^u+3SJQHJiap(HGx1 zAhy#h!&_%GV$bG#c;6*X5jG|7XgX7ZMN2#%v|6PCOm^g8WZ*_ri${41&YXOt(XgX~ zOl+nCHL~=zVeToB90m^?orSG@YP=H=O)#^r6TP6gS6{?fF z0m^#o8ek3|{g|&uw8n*3nG>mF)y!L5kYJh{fN5`03zhCiry}s$F=LFa&&`CG*#Jb8 z{frx}MaLssPQaP>BzcR1R&YzL0^iS-S`pd6^b|=>45=fzf@zu{MoUekWst>FOy)~m zlMd93N%+iwc1!Wj36Sh32KI=Ah6Y~ur`5fd5RudkBIMG{i*6Wt95dV3(_;6O zY=4c@iL%6{RZI~($=qL40& zqdU8llm|wV?O2!^@%1li%=3!m*K2XU;QDB8BEH#mRbutg)zs-?*8=gc_n(m=oJZ6^ zMK=r4EjD5dRN5W|lG)`Ik)XahN|_7%4hnh=eP(Ggz1-Zf*yMX;z4h=pGW`~#C+5og zZU>Yxk;ID#+_K{+D~a{hCuj1f`)w{%6yMAM&FQRsS@bdQUqyClS0ao{h7ie9)^1ZO z2&*(CvrjlbFO!3F`F?#2pTU>pP1HjYE5uD6ky5>MD6Wv64?sUENR#=K_{=R1M z2?SlGRx8E;vrjKK7F&oDo%RnQ!h}yYN)Zb|z>3t&so791b_fLA(;`cR|ua@0g|NNdc zrXeT3X?0HO>$dE=ot$kJ-((UEod!JZPwSC`ah7X-{Slj;=R&W$|7^L?5nAiXF3UU_ zT3Y$=?W1?1&C4HuE5in7fV6j^*B*0NnDU?#lMF@guS1(wC`XQ?amTi~4NdI-84Xd9 z({`kPqu^=Q;7A^0Rm+GXfWXt_s-+bY$*+mEc7lmy;{49_zUpgzB&51X|A1z#{Q?Ot zupJQSL3WS1JvN{Sn9~TLtq@xlMzi*0WO@$1`MgsqFFVjSMM!X;uSm3>&&DBwOIRhA zLO6#oT|su*U9uXZMGTO{oWZ=xwJyff1Tq{JARY*VO%TJ_Y(ZH+&VavxAP^o2l}S=` zRdQo^az@3@k~(I09V248L7}v_u`RS;MTvn|OVk z^5xAyE~R?t#~}R?3^GsCwXciE?QPF0>gpNZi&3bFl8j4$Jk^mwzRtahyp$OTR6&}xVdH7U|ze3gm4 znv>PCP87Vf=bwG^MDKDz*1bs0++Z2$Dr+8oqOXSytsGF*u*$|;>4jU5K{cSSIC|L& zrJQ)`SYN^`F4b5s!br{{fb0O%s&=zaaG>|o5suymo=zew`k!}VwK-#KqOn!FA>243IH ze(4Fi4fLxoZ-4ObE{E*0h5m+QU!w0?#fhaw!0rmPXxM!F{w(&Q0uCfSaW8)N!})G3 ztEW!WbW8Wm6H}Ib&5J()^xOg9s144RzR)|Zjd%=uBhS5U)9k!hmv&_^$nW*cS z!$q}tJmuAg`l@=1xlUx@=tj;#RnAEcW$3C5DK2%oa4mn}CM)%mEUyZ$&!pmx%;tw8 ziMJvd#cC@WX=5wQHotZ3BM-J88A2sKc-MnPV4-FP21mitHBw^V=`7ZY&p!{_5s-0m zz!__EPcwWGw^1qs3agHdG8HSw2sFf{YlbvOPH;02t�eDSqKXpwC-)5IqArAW20~ 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 z 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 literal 0 HcmV?d00001 diff --git a/mods/ctf_pvp_engine/ctf_flag/sounds/trumpet_win.ogg b/mods/ctf_pvp_engine/ctf_flag/sounds/trumpet_win.ogg new file mode 100644 index 0000000000000000000000000000000000000000..e0d61b4aecd3c9c69ae5cb2e7a5996ce37e249d2 GIT binary patch literal 42011 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 literal 0 HcmV?d00001 diff --git a/mods/ctf_pvp_engine/ctf_flag/textures/flag_black2.png b/mods/ctf_pvp_engine/ctf_flag/textures/flag_black2.png new file mode 100644 index 0000000000000000000000000000000000000000..72bacfc35b3626747269dd0d8cf9d446fddcc5dc GIT binary patch literal 598 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 A6#xJL literal 0 HcmV?d00001 diff --git a/mods/ctf_pvp_engine/ctf_flag/textures/flag_cyan2.png b/mods/ctf_pvp_engine/ctf_flag/textures/flag_cyan2.png new file mode 100644 index 0000000000000000000000000000000000000000..4ee3a29d92e53fa66b8196cc39adbaf2cdd7ec9a GIT binary patch literal 676 zcmV;V0$crwP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!TN2bZe?^JG%heMHD!e|WdHyI0!c(cR5(vn zlFLh!VHCzsMTOnTZqS7;6qH66=pGd%SQG@&E`oN^qE-Jw3#C=y#+{p4>B`ZWg*%l& z2EhwnP#I7M(NShp)X|v{pMK{X`BFH%JnwnWInO!IdF|W#>d-1AYim0o9R+lD+S;0x zN|oU5Zrj>ABwYd5{dg3bjgDG2YtiUK9#&ULs0I`YmQFvGPJ!Fot@Y2R(7a@QJ6;XD3xq+(RzBQ^}i)@Y^>BG`9yi-#&Ru%lvlU(QL_j0CE52Sx=_P^kiLy8~(0000< KMNUMnLSTXk$1l79 literal 0 HcmV?d00001 diff --git a/mods/ctf_pvp_engine/ctf_flag/textures/flag_gold.png b/mods/ctf_pvp_engine/ctf_flag/textures/flag_gold.png new file mode 100644 index 0000000000000000000000000000000000000000..0969ba768ef05c5a8a0accd178df11c00674d90c GIT binary patch literal 618 zcmV-w0+s!VP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!TN2bZe?^JG%heMHD!e|WdHyH$Vo&&R5(vn zlE+GeQ4od4-n%G@qFceeFCe(c&aE4_u6>Ds2$qGt_Zp2oMPrMJz4wlyDEbV3!wtU# zhRaRx=m-F%WuGH(rL}!DXJ7W(g#oZ>=woP)R^TlF&ngr(FxjNT&_Epy}a$JROPGs)`a*+VCAL^mPl0F#rzVcpwHEKtmKJ ziHX?72}&q>5Y0usHfThA3Nw8%7e_d_M9XnsnVSyWaAKQ6hrgw&vgK$lV_-2U<%^DOxNtvfPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!TN2bZe?^JG%heMHD!e|WdHyH&q+ixJ6pouM8N)JAcr%xayl2zl~k4&80%##ZMI&h z*cp_gnGmX7!}8|La103l3ypHWnkeS#5@T_FyApq~5OuvA3kek)-QpRQV{<5Me3x59 z>MIJ3^<+;0c-JJGy>hu2P5;4E(A83mK3QxMetsoU%@;=MZ~{CeNCyIv!|7mqk2-`$ zN{jOJ!ZdqZy8?S$Po`p}O%m3tRJ**{hel{QR<;_tI z{-#f6!??3FC_J#KMj@upEs>P+Z>sW(l?MEA4KQD>;lVTm(VVCS`lco+Cq_ov1%nm{VK&R%U2Sg>wAYq~;cw>ztN{!7HF#x^0GPzZf z{7s_=b^_%gkP_jpb|`G>AcXmMaA4~OPLaekuz>-#Iv=uTrVJ4_#h@c%AqEXpe0s7` zztpT6V4JQ{LBo&KKhq4GxA5W4!XijK=2u5L?s?eeIwgq{YpeVK~8GJe^MQLGE@tdl>d?4waX9MrwsZK>+H_W|KXvC`O}^ z6KW1Q(R#PkXfzg!#d^K&_xt>O{SIn|FaVIUzP~<&8Swz441mmr0w>*W*BWFWETPHg z^O;OW2S51Sm=$*_mEw)(htLS*wgSlKestT2Zh&Fb7XsWeXbr6HL=u4c@6b)~cs!!h z_iyQNIPCX(xwYnUx$Sl~4Q-l-OjVPwSS<>V-6xcv^xm-{< zu4^P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!TN2bZe?^JG%heMHD!e|WdHyHnMp)JR5(vn z(n*RNK@bH{UB}?PFVN5n?|SP^Yb9FqtkOKs1G0o95ITiV=vSnuMoE%nMg0HcM`Wcw zfB#JOWHPB%tF2b+bUO8Vz2$OQtJMS-i-mZ<-@n~%snyr7WOuvW5QT2H+dlL8ywm6X zes_*Wqtw6cN3!LF#^Z6ZSR4!nv)N3f(P${*bf=Q~TYF1(CX<0apU=+2;ZQD@o6Y8Q zI^FO0D&u25EkQehhQnblmxEIQ6Hd+5YPDXkWn;ph_a9+vx>Bj=632q(;qiFvcDv1H zqX7~szgVI-pU8Yy{19Ivvpf zWd>8kaMJ1Y@p#nCw z5l(2x1wEzw9Y#zd{2hWXO>?3%syc=U17*pACU?19o)rQi_=Yx6HcXQrjd~N+n8b`L zgb~?7hG=;~IfU!=YW!vzgiQRqX^+h@KcWyWw4p(Rkk2TJ{sUYZZEv3PIFA4T002ov JPDHLkV1gli^Fsgt literal 0 HcmV?d00001 diff --git a/mods/ctf_pvp_engine/ctf_flag/textures/flag_green.png b/mods/ctf_pvp_engine/ctf_flag/textures/flag_green.png new file mode 100644 index 0000000000000000000000000000000000000000..7800464668d0e43face8f2cb039e3614710e4cc5 GIT binary patch literal 826 zcmV-A1I7G_P)pF2XskIMF-sm6cHU0RC#o~0008nNkl_6P}?srNONb z;zo?w!x67eYIw&oN3Dwqfa|2Ip?e0@3vRyG7FZ0$xv#`9gAVMqG51yG#W0c%QgcMl zG63jV20hC_WN5ROx`dS&%D)=bHG$@m4#x&uj)4&c!e z-@Fe3hm&4py3yr9dY#8VJ)-Q=t`Cn| z7nCXu#FNAj{^`Oy7xZm*#3~fqa5o0?5>eH~Yrl%|Uj=5Sf$_C|;dTmvKOZ{Wn}bRh zS{B$2;Rmbe?Kb>zNGg|wd>HAOik%Iz@pK!H=)N}jA4a7`QTKTYs{jB107*qoM6N<$ Ef}x6e2mk;8 literal 0 HcmV?d00001 diff --git a/mods/ctf_pvp_engine/ctf_flag/textures/flag_green2.png b/mods/ctf_pvp_engine/ctf_flag/textures/flag_green2.png new file mode 100644 index 0000000000000000000000000000000000000000..d4ce6effc03d43ca81320e9f2cd39a311562badf GIT binary patch literal 775 zcmV+i1Ni)jP)pF2XskIMF-sm6cHT-psm5}0007}NklIF z5dnv@b0We5ecZf2Ym2Ph?q2(o4{va_8An*oP#xPq!gY$Ycl$lSus~MJ$P9#CxZY9q zjtT&JN2RNMT7n4S(F!%lH?61ut1nYxzXisCSjpUOctde8;sl?gE;k`cQ(WJ@!SdNsQP6>B z0LNfzjF^83r&X1S$)s!DlR6}_WmIiKXc7rfDgrHtXsh)u>IjOCbEiSkTuP}fWtBD?JlV$06fx-~BH_5YrZ1|uhx-(U>H z1UZHQGRTIh+h@X`g(FNfel`T-Q5esib0xlY4V>b_%cn24qxU3Lky*UqF~RSQS4kLJ z*5f$xQL!T6=_GsoGyuS;{r$nt52og0ybz&*vcZw~4VA$dz@DH9N+r}ob`pt7KmVt3 zcqZselip&_N;3qfs~-!n67;Mlje{!mhcIG;)ez*Ge*uG;Mb%uapkDw0002ovPDHLk FV1gHIWmEtF literal 0 HcmV?d00001 diff --git a/mods/ctf_pvp_engine/ctf_flag/textures/flag_orange.png b/mods/ctf_pvp_engine/ctf_flag/textures/flag_orange.png new file mode 100644 index 0000000000000000000000000000000000000000..9d99418607702271472940e57937bc5e12e9db3c GIT binary patch literal 632 zcmV-;0*C#HP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!TN2bZe?^JG%heMHD!e|WdHyH)=5M`R5(vn zQd>_GQ4pSDp_U@6%QcWl2;sqmcmIHi34I^};Z{^c?!^kOh%4I)q$DK`A^a8o5IhjT zfO3&2$YuDxJ)1R6CMPFzX1>eJk;5K2e=VAhTu;c|wCH9#?te$+&pT1i#$CTwk0d+7 z5S@|RDbdA8tibb!2mi)JXUlFTc{1t53)9cTkl$~F6`3edbw0+^iEFNwY@YpdK+w5e z><&ZrU-{IoWq35-WPqXbpj$Lsb2TPg&WkWa=PHQ0T+fDdg6*N)Qva&Sbp)=@A*6Ei!*-eTrBw&WjXm%|Cm6)c{Vyk&DBZd-81`V{4t$|6@83Xiy!D9~KMUV|K zA43L{WP^C7Wh8S4&|4!yGYyr6cJk?nxTaExeEHh@0jr?{=E*v#exIV$&P#5X0)+jEP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!TN2bZe?^JG%heMHD!e|WdHyH*-1n}R5(vn zl1WcfVHAc>nTn;%5+aZomL#&~2S~z#L=y)_kwHOe87!0{gBT(ZNLQ}>AN(2?>~J9< zpbSochv#j{ZJK_OOZE^NGn}Iy^W_?J81;opq67AgCZ=J`w-hr^H{)CR)*58yV;xA``^sGcAd(jv z--H7o(=E9z8nBePx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!TN2bZe?^JG%heMHD!e|WdHyI5J^NqR5(vn zQp-!!VH7>K@PTDep-5EHVlkGYd7O-$ zA4WY-@OpvEDex>iP^mHIQ#0JRLKkPz!AoR}NAE#=zH{S@Q(!tb@Tnd5d6ElwJ%l>$ zUme49O>SX=Dg1_)4JB8dLJe-a(KUi+6KLX|#aDRr1#NatVFhk!zlfFH%ykM(BQJ6i z%}fe1ivxJtin}~byJm6~{VV7b6SeM8O7I)r-X&&`(Qa+i3$w{oO8qa4Fk(tt3-gmh z1@2zp(XBrimj>%j3I^~&rfH|JWy41_gi}fkFlufTjp9~^;_rCJ^W4N3^QZwPQ}V(@ z+v{ZtQpA?gt0na)X~VrfRCCufG3muy^@Rj%Ye)yXW~k&U+FBGDM4b04+U3B^?09EoL2&L2otSbML?p(LDakOz#KcgA(TdxDpMI1YTemzj5~}~0Uub?@Dy6)p0aB~d1yf0wqXf9JouZ3)_H-3eTs~nokOSmF>U%d@jeCoPjrFn YFL$=9c;H)7Y5)KL07*qoM6N<$g4~iNlmGw# literal 0 HcmV?d00001 diff --git a/mods/ctf_pvp_engine/ctf_flag/textures/flag_pink2.png b/mods/ctf_pvp_engine/ctf_flag/textures/flag_pink2.png new file mode 100644 index 0000000000000000000000000000000000000000..b5899cd9f6096a689e0d50c2017a528e473e0e7b GIT binary patch literal 688 zcmV;h0#E&kP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!TN2bZe?^JG%heMHD!e|WdHyI4oO5oR5(vn zl3QpFU>L{$b7+K&%poCDnA9+BE%ow%LKrm>qxqZ(HBjS5I$mJ*WTS(WwLLBhjLgVSON(4waTEpV=M$+|`B0 z$}LtN3Ed?2>0)MjB$(FBWx7mF0T{!?9(idgc8gN*C*BqUe+ zeV}X>{BpI2kxHk^I1IDeiEA9xdoN)dI=(-7B20Qn^~~usuu9!F`fj}`R8F6&OLXn8 zYW-nKQw$_)Vsfm)3DPlo*1%>;b;H{nW*9>{%-Ddn3Y}?~dY;bwP&r#MdoO9#i>RzY zrPUDM%I>hib@eJVdQW9~3z=aRGrmV5xrc@Q?BJ!YTT@O34s|LBqHU*FRV*E3ZdZ)c z!2t`ucp!+F&~2y2^obN2^~`byL$e5wB1>3HK$X~MLu&Pib+qXf$0*X(9#xuvC60AE zHCW?>XV>U{ziLdBm17*`sjjdwcrv+RvLS_hoevk4g5d?Hcf!1$9A%kwxFsK->n0>} zcx1DRE$BmCvbAQ?peKQt-AEb_src3?X`9U!ST@87d_onv<@2@`frbT*YqUh8YWNRR Wle8(4apkxG0000Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!TN2bZe?^JG%heMHD!e|WdHyH%t=H+R5(vv zlF2WhQ51$>Ls4pKO;uqnvF|@1v9Rm58(ZuC6B3CKVnNhgbB(2?rKZ-<3Ok8N+E@^@ zW>wSi+}vM2`AKfRoP77Z=RD^?8Yh|S`gQFH5tmTC&k@_LZI(7C|dz5I`G5x#sZ>aoL za6|^#z4|{IZB6<^#?Kcvpqnn8qx_m=!x2{XrZ70J1HYQ}mq{A6|0Tgt4#{cepERaWPjwh9Obd zo9Id2=>*xgG(*s;Lke-ihQ=JSm$F=?l^lnTKfq83PGShj^8>gU@W&(d>$**I0mip2%x z9TFR$FfsrDYts>iqQnDY*dA`WQk$%X0ni`-40G@elG#*S%ycrJ8tOoIh2HV71$-@Bjb+07*qo IM6N<$g79|{P5=M^ literal 0 HcmV?d00001 diff --git a/mods/ctf_pvp_engine/ctf_flag/textures/flag_purple2.png b/mods/ctf_pvp_engine/ctf_flag/textures/flag_purple2.png new file mode 100644 index 0000000000000000000000000000000000000000..6930dde5736b0fb9e163e1bad53433028751970f GIT binary patch literal 616 zcmV-u0+;=XP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!TN2bZe?^JG%heMHD!e|WdHyH#z{m$R5(vv zlF3e0Q4mEp(nzn;6C#XFnEL}J4xBkwdtr+r+vB_O16x-Jj?#7+o>!&6|Q<6M7REa)^(Tu^d~s(Wc+;S zzQwtbR6EBq+!x)by5dC;U<@W%Drb$>sC=skU}Qu4Bi=P>=N^e{&gpDppdZ{zMRunp zWi)Eg)=Z8Gs_f-6ADHGF0Xkp+)M_0A7^5sK6Zw_yVvuM+D$W=bRAiurEDGGQ&gFm8 z^x%XWDKemVGd000McNliru-3Am987P4sSeyU=0=Y>< zK~yNuO^{1Z69E*3?=aIh)0RT(l(tk#TN(-!5t5QvNyMs=U_dq|x?|zaAK}J@3*(N& zg$qnvYE&KujWIR^2qFm9RO6V6i|CO?t+s;pY#tvGHa34AXnwcf`Ch#%Qa<5hPa(-Q8bTT ziT({zqn&LUY2%SP$iEK_^@n3Xli(j)3xzn8_2rU3D&TeuB)F_OmVW!__GtnJ2A$QS zhm8#ZXSevGlO=Hyg~>^Gm!t6XvAK>ObrI1Fb!K3^F3sYrBMg37JWls;OE?e)=zhJF zgC(uD8A>G`S*h{6`&BxU{?*fbX@avjvnCigKu<`Otou z7mh=ax>5}c1%SWDD%V?tG}mq{9spoqmC+ei#q&i}IrqT(AA|csJ++%kvKL4Dn6+<4 zv9N~;cE*L&_XRwf$7fOMBrV#!&&LGnUbmB zg%UMl2}Y7+o+k4&X2i>!$}F`x(#s!ey*8x8iZ7nAZjW(gP2?1)p2;0B!AMe}#E&bb zgtiVO5njntQxjFK+P<|=6Ck?1J2=#n*x2Do6>;lwXg>~6K7n>xKgs0YpJ9a{&$L37 zFrW&Bc`ID;dATpEn170k?#zv!Z>TE_jl?i8cG&nxMCl%^Yt)g@J)_H?;|{E0vZj?JvTE9Y)rte2tLk4aG^EiC6^taC4rO2p zCDfk-;Q}77{y~?Y!;Nbif-o)yz~@&17;G)4eR^yrr0%iP*%E$#24-jXq+mAVGd000McNliru-3Am98Uk^M!ifL?0{2No zK~y-)Rg+I_QU@HzKaV~h@ZeDjrG?Q-`qJ3eR;g9HnP}8S+Bw~2yY8^d5^p>0x_Do9 z@?@4}7n7ZuE?v}Q-JiIb%&erHNo!aIs{9e8qqIDEk5?YBL!*IT@;&~(`R0@FSNQDu zOoy)f>Ru9qKI}s#wvHcfa}Q-7obzYM)$5EJFL{!w5yQ!SpB-`Xn7jL*s%$a$;YVm2 ze_{DY@vSuxTyRj^!O=Q$viT%j^q9roZ?S($6^m<3qHdMxq&JR%sw=yF^FN zLe`IH=o$bnS;5dX{JSOyEfkN3$~U(O0BIUbh@eiNBT?2kzWoIMgJtxE_vsrBcyE=f zCWW#_RkOJKGK*<}>$Mwu&_!DvW~i)l_*%qMFJn(73EsF$HI>1!RYaVh>1ELQ`8nQ} zj^@x&WG8GMfvfZn5#HVApSH;2@?!=7WC`0?$V<1{7tT?Y+F-Y#kIur>3@Onf+}uYu z;LN3y6ll|l1Yr6!wx^i_Zv>D_Ch^HKg~iKkixT;r1mmB6PSqwOAB)nF0(3k+UKA?i z))I&=S$P}(X{LZ&F4E_R{-t#WBm-?KM*Hr!2$qMI-_I=;UqU_3L_{>;F8I3G^-oL#!jz*)=X0sWO$BV^cyWO_i zZ72PH|8zPz{P|mN*-W~l<#O3-wdV7=LN1p(91g?b5N*9)lc0tF`Tm&hm~gpVrqk(U zGJ)%KI`w*;mCxC1=7e1F>m}Wh0oLA7aE9r2%`v%l4k`+0?tdR zR3ZfT9|@w-Xb2iB6^pbXg!C*F3gpo4Zd!)a-8;Tu3@x+Lx@$@0ld%dQl}Jo12tNoi z3HLzjuIcObD!}o0bWOA+K?Y)rz{D5>G;vS}AZSf93|)!DiWdJsfQ}sun~A}P$y!bb iGjsw?(FG?>XEVQpY6$d-D#ix@0000Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!TN2bZe?^JG%heMHD!e|WdHyHl}SWFR5(vv z(l>4!K@fEYPt6G;3;L0Z9kLEqtWo$?RIOmTJ-t;l&tM`I~)!jG@H##Ce!ct*X#B9 zd~Ubf^?H3ipDUuwub;_UtyZAK2vhC#dL)6W)he=jI-N$pKc17dTrPLJ-Jh=q!7&a) z5(vyElgTX-w0GFzez{zrN28HiaXOvU93+fhe>{Yx>G60>jJ9JjkU5CttpWS^-y#_c zjY_35o6T_Y`8=8;l}eF^_W6I)rBaCxNz;^`i7{v_w4}-VSP|R+Cm+z5q)#2Z!wdIe1_9k{X9JqOq5u^pUIRpX$y-EdPJ_@-J6xnPR!5FR%VMLZK zZGh@>zu(Ii@)#OzS^)Q^T{b0yvnzVIa=AHhw&OI8eF9t%niJ1@4B&^b1tMmo(IA(QkG-tG9aF zn_^+XA;Vf04oh%U#_#biJDjq$clP%OZ$6N)P+muCeJm_AYG-rCCt1fQshAj8P7_!l zlRYVtN?7_|QYeqyY{F7WLM0*v%L7K~JOOHnMAqhqRZgh8Km(ae{f3irtX^d+;qKmG z=Yt93d7LC{Ul*u1(fiD>kQ{hxgrT<8*P8F|Nj9m8gQf$BmI@olpFvtEWA*6nXpsH4wQuTY#iSL?$JJuo0?g7DT#{3;FerXhIbZI6sXqaNC7|M zoR+Wt3|2daP`$gyXv@S?+Z@({c=|4(CCTyVXqs>hKt1Wna5D4HO+4b(a4K=o$^~;Z z$8f&VOR2IYoG&)qL9`sW?UY`0pg|gxH&G;Tt-sgYsE{X0c@nr|&IMMhgR0LelqQTX riQ2RpsMCojVdRN+hX!9jH@eON3{kJP)OzC300000NkvXXu0mjfrD-Am literal 0 HcmV?d00001 diff --git a/mods/ctf_pvp_engine/ctf_flag/textures/flag_yellow2.png b/mods/ctf_pvp_engine/ctf_flag/textures/flag_yellow2.png new file mode 100644 index 0000000000000000000000000000000000000000..642f5273007d8f0d12a5cb965718d62fa2717c79 GIT binary patch literal 671 zcmV;Q0$}}#P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!TN2bZe?^JG%heMHD!e|WdHyH{YgYYR5(vn zk;_X|0Tjl+CYp>M))+~FX=;ULL?Hz&BWU5SZJV~P{U?G}?iEA`NkmhW>`7&5nwgJ$ z+dO=Y-X_OKI*yKhea9=;1`e0GbI&>7cYYrpmz8+Mh0_cj4Zi{>eEPwqbG^?n{RXSw zKqs&}3hrFSlkz)WF`ga$07i~2e8l1>aOU{TTQGK($xDB~gS@L1XnFe3EBzIG2?qTG z^@x43#k22hx3+8zVf!aWnt;lR`(AP5BG+@)deKn@GvEZwWFl9ZRavxe;rZZcndgtLV{z>;QT4hoy4ODMQ+*A(M+yhAaCeaTc60abHf?3$Hg=Db^K7e zrEmtf3pfT&9wu+OuR9}lfKE^JLzQ-PcP+$+zq9)4#N?V*cGcMFD?QfwrxTy6ae!3= zsbR#wni*|1OS)CQdC!eSI!?!blr*Eyvb)}AOg9HG`ogw~y<{g_O()cjw4{@R;8wo) z%)N&~)R+mzvjs{|Wv3|2k|#3NhREwZGb~odl47dR64=ZmOGSL+NBYrHkbCRnRr0#g zbOPoo=wIrBG8No?1$V#<2xY2pxKT4j+`Xb$3_#FN-ZtV+z!Y3WDshxi=?bMd+VF(W zbkw?XUWnxC;An-dcabq^mg+vmH`E}hlK$t$N|cj>_y=h@v8R5EYTp0=002ovPDHLk FV1m_8DbN4_ literal 0 HcmV?d00001 diff --git a/mods/ctf_pvp_engine/ctf_protect/depends.txt b/mods/ctf_pvp_engine/ctf_protect/depends.txt new file mode 100644 index 0000000..a7176f2 --- /dev/null +++ b/mods/ctf_pvp_engine/ctf_protect/depends.txt @@ -0,0 +1 @@ +ctf \ No newline at end of file diff --git a/mods/ctf_pvp_engine/ctf_protect/init.lua b/mods/ctf_pvp_engine/ctf_protect/init.lua new file mode 100644 index 0000000..25c3809 --- /dev/null +++ b/mods/ctf_pvp_engine/ctf_protect/init.lua @@ -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 diff --git a/mods/ctf_pvp_engine/doc_data.md b/mods/ctf_pvp_engine/doc_data.md new file mode 100644 index 0000000..eca89bd --- /dev/null +++ b/mods/ctf_pvp_engine/doc_data.md @@ -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" +} +``` diff --git a/mods/ctf_pvp_engine/doc_project_overview.md b/mods/ctf_pvp_engine/doc_project_overview.md new file mode 100644 index 0000000..99902b8 --- /dev/null +++ b/mods/ctf_pvp_engine/doc_project_overview.md @@ -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 diff --git a/mods/ctf_pvp_engine/doc_settings.md b/mods/ctf_pvp_engine/doc_settings.md new file mode 100644 index 0000000..68e999a --- /dev/null +++ b/mods/ctf_pvp_engine/doc_settings.md @@ -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 | diff --git a/mods/ctf_pvp_engine/hudkit/init.lua b/mods/ctf_pvp_engine/hudkit/init.lua new file mode 100644 index 0000000..c4633c1 --- /dev/null +++ b/mods/ctf_pvp_engine/hudkit/init.lua @@ -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 diff --git a/mods/ctf_pvp_engine/lib_chatcmdbuilder/.gitignore b/mods/ctf_pvp_engine/lib_chatcmdbuilder/.gitignore new file mode 100644 index 0000000..9cac31c --- /dev/null +++ b/mods/ctf_pvp_engine/lib_chatcmdbuilder/.gitignore @@ -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 diff --git a/mods/ctf_pvp_engine/lib_chatcmdbuilder/LICENSE b/mods/ctf_pvp_engine/lib_chatcmdbuilder/LICENSE new file mode 100644 index 0000000..588da15 --- /dev/null +++ b/mods/ctf_pvp_engine/lib_chatcmdbuilder/LICENSE @@ -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. diff --git a/mods/ctf_pvp_engine/lib_chatcmdbuilder/README.md b/mods/ctf_pvp_engine/lib_chatcmdbuilder/README.md new file mode 100644 index 0000000..7d52585 --- /dev/null +++ b/mods/ctf_pvp_engine/lib_chatcmdbuilder/README.md @@ -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 +``` diff --git a/mods/ctf_pvp_engine/lib_chatcmdbuilder/description.txt b/mods/ctf_pvp_engine/lib_chatcmdbuilder/description.txt new file mode 100644 index 0000000..6e5d637 --- /dev/null +++ b/mods/ctf_pvp_engine/lib_chatcmdbuilder/description.txt @@ -0,0 +1 @@ +A library to make registering chat commands easier diff --git a/mods/ctf_pvp_engine/lib_chatcmdbuilder/init.lua b/mods/ctf_pvp_engine/lib_chatcmdbuilder/init.lua new file mode 100644 index 0000000..400d250 --- /dev/null +++ b/mods/ctf_pvp_engine/lib_chatcmdbuilder/init.lua @@ -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 diff --git a/mods/ctf_pvp_engine/lib_chatcmdbuilder/mod.conf b/mods/ctf_pvp_engine/lib_chatcmdbuilder/mod.conf new file mode 100644 index 0000000..1172971 --- /dev/null +++ b/mods/ctf_pvp_engine/lib_chatcmdbuilder/mod.conf @@ -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 diff --git a/mods/ctf_pvp_engine/modpack.txt b/mods/ctf_pvp_engine/modpack.txt new file mode 100644 index 0000000..e69de29