From 37e7ee967f025166b47074efd2592a5306c63d70 Mon Sep 17 00:00:00 2001 From: chmodsayshello Date: Mon, 3 Oct 2022 10:30:47 +0000 Subject: [PATCH] add mod itself --- chatcmdbuilder.lua | 306 +++++++++++++++++++++++++++++++++++++++++++++ init.lua | 118 +++++++++++++++++ mod.conf | 2 + setup.lua | 58 +++++++++ the_bridge.lua | 90 +++++++++++++ 5 files changed, 574 insertions(+) create mode 100644 chatcmdbuilder.lua create mode 100644 init.lua create mode 100644 mod.conf create mode 100644 setup.lua create mode 100644 the_bridge.lua diff --git a/chatcmdbuilder.lua b/chatcmdbuilder.lua new file mode 100644 index 0000000..0f8080a --- /dev/null +++ b/chatcmdbuilder.lua @@ -0,0 +1,306 @@ +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 + +local dprint = function() end + +ChatCmdBuilder.types = { + pos = "%(? *(%-?[%d.]+) *, *(%-?[%d.]+) *, *(%-?[%d.]+) *%)?", + text = "(.+)", + number = "(%-?[%d.]+)", + int = "(%-?[%d]+)", + word = "([^ ]+)", + alpha = "([A-Za-z]+)", + modname = "([a-z0-9_]+)", + alphascore = "([A-Za-z_]+)", + alphanumeric = "([A-Za-z0-9]+)", + username = "([A-Za-z0-9-_]+)", +} + +function ChatCmdBuilder.build(func) + local cmd = { + _subs = {} + } + function cmd:sub(route, func, def) + dprint("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 + dprint(" - Found param " .. param .. " type " .. param_type) + + local pattern = ChatCmdBuilder.types[param_type] + if not pattern then + error("Unrecognised param_type=" .. param_type) + end + + sub.pattern = sub.pattern .. pattern + + table.insert(sub.params, param_type) + + param = "" + param_type = "" + end + end + + -- Iterate through the route to find params + local state = STATE_READY + local catching_space = false + local match_space = " " -- change to "%s" to also catch tabs and newlines + local catch_space = match_space.."+" + 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 + dprint(" - Found :, entering param") + state = STATE_PARAM + param_type = "word" + catching_space = false + elseif c:match(match_space) then + print(" - Found space") + if not catching_space then + catching_space = true + sub.pattern = sub.pattern .. catch_space + end + else + catching_space = false + sub.pattern = sub.pattern .. escape(c) + end + elseif state == STATE_PARAM then + if c == ":" then + dprint(" - Found :, entering param type") + state = STATE_PARAM_TYPE + param_type = "" + elseif c:match(match_space) then + print(" - Found whitespace, leaving param") + state = STATE_READY + finishParam() + catching_space = true + sub.pattern = sub.pattern .. catch_space + elseif c:match("%W") then + dprint(" - 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(match_space) then + print(" - Found space, leaving param type") + state = STATE_READY + finishParam() + catching_space = true + sub.pattern = sub.pattern .. catch_space + elseif c:match("%W") then + dprint(" - Found nonalphanum, leaving param type") + state = STATE_READY + finishParam() + sub.pattern = sub.pattern .. escape(c) + else + param_type = param_type .. c + end + end + end + dprint(" - End of route") + finishParam() + sub.pattern = sub.pattern .. "$" + dprint("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 + if table.unpack then + -- lua 5.2 or later + return sub.func(table.unpack(params)) + else + -- lua 5.1 or earlier + return sub.func(unpack(params)) + end + end + end + return false, "Invalid command" + 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)).run("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).run + 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 8 failed") + end + if move("singleplayer", "move player1 to abc,def,sdosd") then + error("Test 9 failed") + end + if move("singleplayer", "move player1 to abc def sdosd") then + error("Test 10 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)).run("singleplayer", "does 1 plus 2 equal 3") then + error("Test 11 failed") + end + + local checknegint = ChatCmdBuilder.build(function(cmd) + cmd:sub("checknegint :x:int", function(name, x) + return x + end) + end).run + if checknegint("checker","checknegint -2") ~= -2 then + error("Test 12 failed") + end + + local checknegnumber = ChatCmdBuilder.build(function(cmd) + cmd:sub("checknegnumber :x:number", function(name, x) + return x + end) + end).run + if checknegnumber("checker","checknegnumber -3.3") ~= -3.3 then + error("Test 13 failed") + end + + local checknegpos = ChatCmdBuilder.build(function(cmd) + cmd:sub("checknegpos :pos:pos", function(name, pos) + return pos + end) + end).run + local negpos = checknegpos("checker","checknegpos (-13.3,-4.6,-1234.5)") + if negpos.x ~= -13.3 or negpos.y ~= -4.6 or negpos.z ~= -1234.5 then + error("Test 14 failed") + end + + local checktypes = ChatCmdBuilder.build(function(cmd) + cmd:sub("checktypes :int:int :number:number :pos:pos :word:word :text:text", function(name, int, number, pos, word, text) + return int, number, pos.x, pos.y, pos.z, word, text + end) + end).run + local int, number, posx, posy, posz, word, text + int, number, posx, posy, posz, word, text = checktypes("checker","checktypes -1 -2.4 (-3,-5.3,6.12) some text to finish off with") + --dprint(int, number, posx, posy, posz, word, text) + if int ~= -1 or number ~= -2.4 or posx ~= -3 or posy ~= -5.3 or posz ~= 6.12 or word ~= "some" or text ~= "text to finish off with" then + error("Test 15 failed") + end + dprint("All tests passed") + +end +if not minetest then + run_tests() +end diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..7bbc289 --- /dev/null +++ b/init.lua @@ -0,0 +1,118 @@ +--20221002_1a + +local S = minetest.get_translator("the_bridge") + +team_id_red = 1 +team_id_blue = 2 + + + +local modpath = minetest.get_modpath("the_bridge") + +minetest.register_privilege("the_bridge_admin", S("Is required in order to manage the bride games!")) + +arena_lib.register_minigame("the_bridge", { + name = "The Bridge", + --icon = "magiccompass_the_bridge.png", + teams = { S("red"), S("blue") }, + teams_color_overlay = { "crimson", "blue"}, + celebration_time = 3, + load_time = 6, + prefix = "[TB] ", + queue_waiting_time = 20, + show_minimap = true, + join_while_in_progress=false, + properties = { + the_bridge_area_pos_1 = {x = 0, y = 0, z = 0}, + the_bridge_area_pos_2 = {x = 0, y = 0, z = 0}, + mapdata = nil, + }, + in_game_physics = { + speed = player_speed, + jump = player_jump, + }, + + disabled_damage_types = {}, + hotbar = { + slots = 8, + }, + + team_properties = { + goals = 0 + }, + +}) + +if not minetest.get_modpath("lib_chatcmdbuilder") then + dofile(minetest.get_modpath("the_bridge") .. "/chatcmdbuilder.lua") +end + +dofile(minetest.get_modpath("the_bridge") .. "/setup.lua") +dofile(minetest.get_modpath("the_bridge") .. "/the_bridge.lua") + + + +ChatCmdBuilder.new("the_bridge", function(cmd) + + -- create arena + cmd:sub("create :arena", function(name, arena_name) + arena_lib.create_arena(name, "the_bridge", arena_name) + end) + + cmd:sub("create :arena :minplayers:int :maxplayers:int", function(name, arena_name, min_players, max_players) + arena_lib.create_arena(name, "the_bridge", arena_name, min_players, max_players) + end) + + -- remove arena + cmd:sub("remove :arena", function(name, arena_name) + arena_lib.remove_arena(name, "the_bridge", arena_name) + end) + + -- list of the arenas + cmd:sub("list", function(name) + arena_lib.print_arenas(name, "the_bridge") + end) + + -- enter editor mode + cmd:sub("edit :arena", function(sender, arena) + arena_lib.enter_editor(sender, "the_bridge", arena) + end) + + -- enable and disable arenas + cmd:sub("enable :arena", function(name, arena) + arena_lib.enable_arena(name, "the_bridge", arena) + end) + + cmd:sub("disable :arena", function(name, arena) + arena_lib.disable_arena(name, "the_bridge", arena) + end) + + end, { + description = [[ + + (/help the_bridge) + + Use this to configure your arena: + - create [min players] [max players] + - edit + - enable + + Other commands: + - remove + - disable + ]], + privs = { + the_bridge_admin = true + }, + }) + + + arena_lib.on_enable("the_bridge", function(arena, p_name) + if #arena.the_bridge_area_pos_1 ~= #arena.the_bridge_area_pos_2 then + minetest.chat_send_player(p_name,"Missing params in the positions") + return false + end + return true + end) + + \ No newline at end of file diff --git a/mod.conf b/mod.conf new file mode 100644 index 0000000..b4f538d --- /dev/null +++ b/mod.conf @@ -0,0 +1,2 @@ +author = chmodsayshello +depends = arena_lib, walkover diff --git a/setup.lua b/setup.lua new file mode 100644 index 0000000..48a41bc --- /dev/null +++ b/setup.lua @@ -0,0 +1,58 @@ +local S = minetest.get_translator("the_bridge") + +local function send_message(arena,num_str) + arena_lib.HUD_send_msg_all("title", arena, num_str, 1,nil,0xFF0000) + --arena_lib.HUD_send_msg_all(HUD_type, arena, msg, , , ) +end + + +function savemap(pos_min,pos_max,arena) + local manip = VoxelManip(pos_min,pos_max) + local emin, emax = manip:read_from_map(pos_min, pos_max) + local data = manip:get_data() + arena.mapdata = data + local a = VoxelArea:new{ + MinEdge = emin, + MaxEdge = emax + } + for x = emin.x,emax.x do + for y = emin.y,emax.y do + for z = emin.z, emax.z do + local idx = a:index(x, y, z) + arena.mapdata[idx]=data[idx] + end + end + end +end + +function restore_map(arena) + local manip = VoxelManip(arena.the_bridge_area_pos_1,arena.the_bridge_area_pos_2) + manip:set_data(arena.mapdata) + manip:write_to_map(true) +end + + +arena_lib.on_load("the_bridge", function(arena) + savemap(arena.the_bridge_area_pos_1,arena.the_bridge_area_pos_2,arena)--backup map + if not arena.teams_enabled then + send_message(arena,"cancelled!") + arena_lib.force_arena_ending("the_bridge", arena) + minetest.log("warning","The Bridge is not supposed to run without any teams!") + end --this game is not supposed to be used without teams! + send_message(arena,S("GO!")) +end) + + +arena_lib.on_enable("the_bridge", function(arena, p_name) + savemap(arena.the_bridge_area_pos_1,arena.the_bridge_area_pos_2,arena) + return true +end) + +arena_lib.on_end("the_bridge", function(arena, players, winners, spectators, is_forced) + restore_map(arena) +end) + +arena_lib.on_disable("the_bridge", function(arena, p_name) + restore_map(arena) + return true +end) \ No newline at end of file diff --git a/the_bridge.lua b/the_bridge.lua new file mode 100644 index 0000000..78394e0 --- /dev/null +++ b/the_bridge.lua @@ -0,0 +1,90 @@ +local S = minetest.get_translator("the_bridge") + +local function after_goal(arena) + local players_team_red = arena_lib.get_players_in_team(arena, team_id_red, true) + local players_team_blue = arena_lib.get_players_in_team(arena, team_id_blue, true) + + for player_index in ipairs(players_team_red) do + local player = players_team_red[player_index] + player:set_hp(20) + player:set_pos(arena_lib.get_random_spawner(arena, team_id_red)) + end + + for player_index in ipairs(players_team_blue) do + local player = players_team_blue[player_index] + player:set_hp(20) + player:set_pos(arena_lib.get_random_spawner(arena, team_id_blue)) + end +end + +local goalfunc = function(pos, node, player) + minetest.log("check!") + if not player then return end + arena = arena_lib.get_arena_by_player(player:get_player_name()) + if not arena then return end + + minetest.log("check passed!") + nodename = node.name + if nodename == "the_bridge:goal_area_red" then + arena.teams[team_id_red].goals = arena.teams[team_id_red].goals +1 + if arena.teams[team_id_red].goals >= 5 and arena.in_game then + arena_lib.load_celebration("the_bridge", arena, team_id_red) + return + end + after_goal(arena) + + elseif nodename == "the_bridge:goal_area_blue" then + arena.teams[team_id_blue].goals = arena.teams[team_id_blue].goals +1 + if arena.teams[team_id_blue].goals >= 5 and arena.in_game then + arena_lib.load_celebration("the_bridge", arena, team_id_blue) + return + end + after_goal(arena) + end +end + + +-- __________________ +--__________________/Node Registration\____________ +minetest.register_node("the_bridge:goal_area_red",{ + description = S("Red Goal"), + drawtype = "glasslike", + tiles = {"^[colorize:#d9405e"}, + paramtype = "light", + sunlight_propagates = true, + + walkable = true, + pointable = true, + diggable = false, + buildable_to = false, + drop = "", + on_walk_over=goalfunc, +}) + +minetest.register_node("the_bridge:goal_area_blue",{ + description = S("Blue Goal"), + drawtype = "glasslike", + tiles = {"^[colorize:#3548da"}, + paramtype = "light", + sunlight_propagates = true, + + walkable = true, + pointable = true, + diggable = false, + buildable_to = false, + drop = "", + on_walk_over=goalfunc, +}) + +minetest.register_node("the_bridge:void", { + description = S("The Bridge void"), + drawtype = "airlike", + paramtype = "light", + sunlight_propagates = true, + walkable = true, + pointable = true, + diggable = false, + buildable_to = false, + drop = "", + damage_per_second = 40, +})