diff --git a/.luacheckrc b/.luacheckrc index 4ab1475..6f666c3 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -18,7 +18,7 @@ globals = { "ctf_match", "ctf_stats", "ctf_treasure", "ctf_playertag", "chatplus", "irc", "armor", "vote", "give_initial_stuff", "hud_score", "physics", "tsm_chests", "armor", "shooter", "grenades", "ctf_classes", "ctf_bandages", "ctf_respawn_immunity", - "ctf_marker", + "ctf_marker", "kill_assist" } read_globals = { diff --git a/game.conf b/game.conf index 21aeb2a..3819993 100644 --- a/game.conf +++ b/game.conf @@ -1,3 +1,4 @@ name = Capture the Flag description = Use swords, guns, and grenades to combat the enemy and capture their flag before they capture yours. +allowed_mapgens = singlenode min_minetest_version = 5.3 diff --git a/mods/ctf/ctf/core.lua b/mods/ctf/ctf/core.lua index 905e78b..e03e81c 100644 --- a/mods/ctf/ctf/core.lua +++ b/mods/ctf/ctf/core.lua @@ -103,9 +103,9 @@ function ctf.error(area, msg) end function ctf.log(area, msg) if area and area ~= "" then - minetest.log("[CTF | " .. area .. "] " .. msg) + minetest.log("info", "[CTF | " .. area .. "] " .. msg) else - minetest.log("[CTF]" .. msg) + minetest.log("info", "[CTF]" .. msg) end end function ctf.action(area, msg) diff --git a/mods/ctf/ctf/teams.lua b/mods/ctf/ctf/teams.lua index b13d7ea..115020a 100644 --- a/mods/ctf/ctf/teams.lua +++ b/mods/ctf/ctf/teams.lua @@ -445,6 +445,19 @@ function ctf.register_on_killedplayer(func) end table.insert(ctf.registered_on_killedplayer, func) end + +function ctf.can_attack(player, hitter, time_from_last_punch, tool_capabilities, dir, damage, ...) + return true +end + +ctf.registered_on_attack = {} +function ctf.register_on_attack(func) + if ctf._mt_loaded then + error("You can't register callbacks at game time!") + end + table.insert(ctf.registered_on_attack, func) +end + local dead_players = {} minetest.register_on_respawnplayer(function(player) dead_players[player:get_player_name()] = nil @@ -453,7 +466,7 @@ 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) + 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() @@ -473,6 +486,12 @@ minetest.register_on_punchplayer(function(player, hitter, end end + if ctf.can_attack(player, hitter, time_from_last_punch, tool_capabilities, + dir, damage, ...) == false + then + return true + end + local hp = player:get_hp() if hp == 0 then return false @@ -487,5 +506,12 @@ minetest.register_on_punchplayer(function(player, hitter, end return false end + + for i = 1, #ctf.registered_on_attack do + ctf.registered_on_attack[i]( + player, hitter, time_from_last_punch, + tool_capabilities, dir, damage, ... + ) + end end end) diff --git a/mods/ctf/ctf_bandages/init.lua b/mods/ctf/ctf_bandages/init.lua index a6a4bec..d8f9c5c 100644 --- a/mods/ctf/ctf_bandages/init.lua +++ b/mods/ctf/ctf_bandages/init.lua @@ -1,7 +1,7 @@ --Inspired from Andrey's bandages mod ctf_bandages = {} -ctf_bandages.heal_percent = 0.75 --Percentage of total HP to be healed +ctf_bandages.heal_percent = 0.75 -- Percentage of total HP to be healed minetest.register_craftitem("ctf_bandages:bandage", { description = "Bandage\n\n" .. @@ -10,32 +10,53 @@ minetest.register_craftitem("ctf_bandages:bandage", { inventory_image = "ctf_bandages_bandage.png", stack_max = 1, on_use = function(itemstack, player, pointed_thing) - if pointed_thing.type ~= "object" then - return - end + if pointed_thing.type ~= "object" then return end + local object = pointed_thing.ref - if not object:is_player() then - return - end + if not object:is_player() then return end + local pname = object:get_player_name() local name = player:get_player_name() + if ctf.player(pname).team == ctf.player(name).team then local hp = object:get_hp() - local limit = ctf_bandages.heal_percent * - object:get_properties().hp_max - if hp > 0 and hp < limit then - hp = hp + math.random(3,4) + local limit = ctf_bandages.heal_percent * object:get_properties().hp_max + + if hp <= 0 then + hud_event.new(name, { + name = "ctf_bandages:dead", + color = "warning", + value = pname .. " is dead!", + }) + elseif hp >= limit then + hud_event.new(name, { + name = "ctf_bandages:limit", + color = "warning", + value = pname .. " already has " .. limit .. " HP!", + }) + else + local hp_add = math.random(3,4) + + kill_assist.add_heal_assist(pname, hp_add) + hp = hp + hp_add + if hp > limit then hp = limit end + object:set_hp(hp) - minetest.chat_send_player(pname, minetest.colorize("#C1FF44", name .. " has healed you!")) - return itemstack - else - minetest.chat_send_player(name, pname .. " has " .. hp .. " HP. You can't heal them.") + hud_event.new(pname, { + name = "ctf_bandages:heal", + color = 0xC1FF44, + value = name .. " healed you!", + }) end else - minetest.chat_send_player(name, pname.." isn't in your team!") + hud_event.new(name, { + name = "ctf_bandages:team", + color = "warning", + value = pname .. " isn't in your team!", + }) end end, }) diff --git a/mods/ctf/ctf_chat/init.lua b/mods/ctf/ctf_chat/init.lua index d730c74..df69b01 100644 --- a/mods/ctf/ctf_chat/init.lua +++ b/mods/ctf/ctf_chat/init.lua @@ -183,6 +183,7 @@ minetest.register_chatcommand("team", { minetest.register_chatcommand("join", { params = "team name", description = "Add to team", + privs = {ctf_team_mgr = true}, func = function(name, param) if ctf.join(name, param, false, name) then return true, "Joined team " .. param .. "!" @@ -271,6 +272,8 @@ minetest.register_chatcommand("t", { end }) +local function me_func() end + if minetest.global_exists("irc") then function irc.playerMessage(name, message) local color = ctf_colors.get_irc_color(ctf.player(name)) @@ -285,6 +288,14 @@ if minetest.global_exists("irc") then local bbrace = color .. ">" .. clear return ("%s%s%s %s"):format(abrace, name, bbrace, message) end + + me_func = function(...) + local message = irc.playerMessage(...) + + message = "*" .. message:sub(message:find(" ")) + + irc.say(message) + end end local handler @@ -312,6 +323,8 @@ end table.insert(minetest.registered_on_chat_messages, 1, handler) minetest.registered_chatcommands["me"].func = function(name, param) + me_func(name, param) + if ctf.player(name).team then local tcolor = ctf_colors.get_color(ctf.player(name)) name = minetest.colorize(tcolor.css, "* " .. name) @@ -319,5 +332,7 @@ minetest.registered_chatcommands["me"].func = function(name, param) name = "* ".. name end + minetest.log("action", "[CHAT] "..name.." "..param) + minetest.chat_send_all(name .. " " .. param) end diff --git a/mods/ctf/ctf_classes/api.lua b/mods/ctf/ctf_classes/api.lua index e474ddf..e8fe5f3 100644 --- a/mods/ctf/ctf_classes/api.lua +++ b/mods/ctf/ctf_classes/api.lua @@ -73,16 +73,18 @@ function ctf_classes.set(player, new_name) local meta = player:get_meta() local old_name = meta:get("ctf_classes:class") + if old_name == new_name then + return + end + meta:set_string("ctf_classes:class", new_name) ctf_classes.update(player) ctf_classes.set_cooldown(player:get_player_name()) - if old_name == nil or old_name ~= new_name then - local old = old_name and ctf_classes.__classes[old_name] - for i=1, #registered_on_changed do - registered_on_changed[i](player, old, new) - end + local old = old_name and ctf_classes.__classes[old_name] + for i=1, #registered_on_changed do + registered_on_changed[i](player, old, new) end end diff --git a/mods/ctf/ctf_classes/medic.lua b/mods/ctf/ctf_classes/medic.lua index 49a771b..fae1b36 100644 --- a/mods/ctf/ctf_classes/medic.lua +++ b/mods/ctf/ctf_classes/medic.lua @@ -99,6 +99,7 @@ minetest.override_item("ctf_bandages:bandage", { if ctf.player(pname).team == ctf.player(name).team then local nodename = minetest.get_node(object:get_pos()).name if ctf_classes.dont_heal[pname] or nodename:find("lava") or nodename:find("water") or nodename:find("trap") then + minetest.chat_send_player(name, "You can't heal player in lava, water or spikes!") return -- Can't heal players in lava/water/spikes end @@ -132,7 +133,7 @@ minetest.override_item("ctf_bandages:bandage", { }) local diggers = {} -local DIG_COOLDOWN = 45 +local DIG_COOLDOWN = 30 local DIG_DIST_LIMIT = 30 local DIG_SPEED = 0.1 @@ -147,17 +148,21 @@ local function isdiggable(name) ) end +local function paxel_stop(pname, reason) + hud_event.new(pname, { + name = "ctf_classes:paxel_stop", + color = "success", + value = table.concat({"Pillar digging stopped", reason, "- wait " .. DIG_COOLDOWN .. "s"}, " "), + }) + diggers[pname] = minetest.after(DIG_COOLDOWN, function() diggers[pname] = nil end) +end + local function remove_pillar(pos, pname) local name = minetest.get_node(pos).name if name:find("default") and isdiggable(name) then local player = minetest.get_player_by_name(pname) - if minetest.get_modpath("antisabotage") then - -- Fix paxel being capable of mining blocks under teammates - if antisabotage.is_sabotage(pos, minetest.get_node(pos), player) then return end - end - minetest.dig_node(pos) if player and diggers[pname] and type(diggers[pname]) ~= "table" then @@ -165,13 +170,11 @@ local function remove_pillar(pos, pname) pos.y = pos.y + 1 minetest.after(DIG_SPEED, remove_pillar, pos, pname) else - minetest.chat_send_player(pname, "Pillar digging stopped, too far away from digging pos. Can activate again in "..DIG_COOLDOWN.." seconds") - diggers[pname] = minetest.after(DIG_COOLDOWN, function() diggers[pname] = nil end) + paxel_stop(pname, "at too far away node") end end else - minetest.chat_send_player(pname, "Pillar digging stopped at undiggable node. Can activate again in "..DIG_COOLDOWN.." seconds") - diggers[pname] = minetest.after(DIG_COOLDOWN, function() diggers[pname] = nil end) + paxel_stop(pname, "at undiggable node") end end @@ -195,20 +198,39 @@ minetest.register_tool("ctf_classes:paxel_bronze", { if pointed_thing.type == "node" then local pname = placer:get_player_name() - if not isdiggable(minetest.get_node(pointed_thing.under).name) or ctf_match.is_in_build_time() then - minetest.chat_send_player(pname, "Can't dig node or build time active") + if not isdiggable(minetest.get_node(pointed_thing.under).name) then + hud_event.new(pname, { + name = "ctf_classes:paxel_undiggable", + color = "warning", + value = "Can't paxel node!", + }) + return minetest.item_place(itemstack, placer, pointed_thing) + end + if ctf_match.is_in_build_time() then + hud_event.new(pname, { + name = "ctf_classes:paxel_build_time", + color = "warning", + value = "Build time active!", + }) return minetest.item_place(itemstack, placer, pointed_thing) end if not diggers[pname] then - minetest.chat_send_player(pname, "Pillar digging started") + hud_event.new(pname, { + name = "ctf_classes:paxel_start", + color = "primary", + value = "Pillar digging started", + }) diggers[pname] = true remove_pillar(pointed_thing.under, pname) elseif type(diggers[pname]) ~= "table" then - minetest.chat_send_player(pname, "Pillar digging stopped. Can activate again in "..DIG_COOLDOWN.." seconds") - diggers[pname] = minetest.after(DIG_COOLDOWN, function() diggers[pname] = nil end) + paxel_stop(pname) else - minetest.chat_send_player(pname, "You can't activate yet") + hud_event.new(pname, { + name = "ctf_classes:paxel_timer", + color = "warning", + value = "You can't activate yet!", + }) end end end, @@ -220,8 +242,7 @@ minetest.register_tool("ctf_classes:paxel_bronze", { minetest.after(2, function() if user and user:get_player_control().RMB then if diggers[pname] and type(diggers[pname]) ~= "table" then - minetest.chat_send_player(pname, "Pillar digging stopped. Can activate again in "..DIG_COOLDOWN.." seconds") - diggers[pname] = minetest.after(DIG_COOLDOWN, function() diggers[pname] = nil end) + paxel_stop(pname) end end end) diff --git a/mods/ctf/ctf_classes/melee.lua b/mods/ctf/ctf_classes/melee.lua index 1395bdd..2b33700 100644 --- a/mods/ctf/ctf_classes/melee.lua +++ b/mods/ctf/ctf_classes/melee.lua @@ -29,12 +29,12 @@ end, true) local sword_special_timer = {} -local SWORD_SPECIAL_COOLDOWN = 40 +local SWORD_SPECIAL_COOLDOWN = 20 local function sword_special_timer_func(pname, timeleft) sword_special_timer[pname] = timeleft - if timeleft - 10 >= 0 then - minetest.after(10, sword_special_timer_func, pname, timeleft - 10) + if timeleft - 2 >= 0 then + minetest.after(2, sword_special_timer_func, pname, timeleft - 2) else sword_special_timer[pname] = nil end @@ -57,8 +57,8 @@ minetest.register_tool("ctf_classes:sword_bronze", { local pname = placer:get_player_name() if not pointed_thing then return end - if sword_special_timer[pname] then - minetest.chat_send_player(pname, "You can't place a marker yet (>"..sword_special_timer[pname].."s left)") + if sword_special_timer[pname] and placer:get_player_control().sneak then + minetest.chat_send_player(pname, "You have to wait "..sword_special_timer[pname].."s to place marker again") if pointed_thing.type == "node" then return minetest.item_place(itemstack, placer, pointed_thing) @@ -89,7 +89,7 @@ minetest.register_tool("ctf_classes:sword_bronze", { if #enemies > 0 then ctf_marker.remove_marker(pteam) - ctf_marker.add_marker(pname, pteam, pos, ("[Enemies Found!: <%s>]"):format(table.concat(enemies, ", "))) + ctf_marker.add_marker(pname, pteam, pos, (" found enemies: <%s>]"):format(table.concat(enemies, ", "))) end return @@ -102,10 +102,10 @@ minetest.register_tool("ctf_classes:sword_bronze", { -- Check if player is sneaking before placing marker if not placer:get_player_control().sneak then return end - sword_special_timer[pname] = 20 - sword_special_timer_func(pname, 20) + sword_special_timer[pname] = 4 + sword_special_timer_func(pname, 4) - minetest.registered_chatcommands["m"].func(pname, "Marked with "..pname.."'s sword") + minetest.registered_chatcommands["m"].func(pname, "placed with sword") end, on_secondary_use = function(itemstack, user, pointed_thing) if pointed_thing then diff --git a/mods/ctf/ctf_classes/ranged.lua b/mods/ctf/ctf_classes/ranged.lua index 87ed5f7..81702d8 100644 --- a/mods/ctf/ctf_classes/ranged.lua +++ b/mods/ctf/ctf_classes/ranged.lua @@ -29,7 +29,7 @@ shooter.get_weapon_spec = function(user, weapon_name) if table.indexof(class.properties.allowed_guns or {}, weapon_name) == -1 then minetest.chat_send_player(user:get_player_name(), - "Your class can't use that weapon! Change your class at spawn") + "Your class can't use that weapon! Change your class at base") return nil end @@ -49,7 +49,7 @@ local function check_grapple(itemname) on_use = function(itemstack, user, ...) if not ctf_classes.get(user).properties.allow_grapples then minetest.chat_send_player(user:get_player_name(), - "Your class can't use that weapon! Change your class at spawn") + "Your class can't use that weapon! Change your class at base") return itemstack end @@ -73,6 +73,12 @@ check_grapple("shooter_hook:grapple_hook") -- Override grappling hook entity to check if player has flag before teleporting local old_grapple_step = minetest.registered_entities["shooter_hook:hook"].on_step minetest.registered_entities["shooter_hook:hook"].on_step = function(self, dtime, ...) + -- User left the game. Life is no longer worth living for this poor hook + if not self.user or not minetest.get_player_by_name(self.user) then + self.object:remove() + return + end + -- Remove entity if player has flag -- This is to prevent players from firing the hook, and then punching the flag if ctf_flag.has_flag(self.user) then diff --git a/mods/ctf/ctf_crafting/init.lua b/mods/ctf/ctf_crafting/init.lua index 9dc605b..a819871 100644 --- a/mods/ctf/ctf_crafting/init.lua +++ b/mods/ctf/ctf_crafting/init.lua @@ -52,15 +52,6 @@ for ore, ore_item in pairs(full_ores) do }) end --- Bronze ingot <== Steel + Coal + wood -crafting.register_recipe({ - type = "inv", - output = "default:bronze_ingot", - items = { "default:steel_ingot", "default:coal_lump", "group:wood"}, - always_known = true, - level = 1, -}) - -- Mese crystal x9 <== Mese block crafting.register_recipe({ type = "inv", @@ -110,7 +101,7 @@ crafting.register_recipe({ crafting.register_recipe({ type = "inv", output = "default:stick 4", - items = { "default:wood" }, + items = { "group:wood" }, always_known = true, level = 1, }) @@ -142,20 +133,11 @@ crafting.register_recipe({ level = 1, }) --- Arrow x5 -crafting.register_recipe({ - type = "inv", - output = "shooter:arrow_white 5", - items = { "default:stick 5", "default:cobble" }, - always_known = true, - level = 1, -}) - -- 7.62mm sniper rifle (unloaded) crafting.register_recipe({ type = "inv", output = "sniper_rifles:rifle_762", - items = { "default:steel_ingot 10", "default:mese_crystal", "default:wood 2" }, + items = { "default:steel_ingot 10", "default:mese_crystal", "group:wood 2" }, always_known = false, level = 1 }) @@ -164,7 +146,7 @@ crafting.register_recipe({ crafting.register_recipe({ type = "inv", output = "sniper_rifles:rifle_magnum", - items = { "default:steel_ingot 5", "default:bronze_ingot 5", "default:diamond", "default:wood 3" }, + items = { "default:steel_ingot 10", "default:coal_lump 5", "default:diamond", "group:wood 2" }, always_known = false, level = 1, }) @@ -198,7 +180,7 @@ crafting.register_recipe({ crafting.register_recipe({ type = "inv", - output = "ctf_traps:dirt 1", + output = "ctf_traps:dirt 5", items = { "default:dirt 5", "default:coal_lump" }, always_known = true, level = 1, @@ -206,7 +188,7 @@ crafting.register_recipe({ crafting.register_recipe({ type = "inv", - output = "ctf_traps:cobble 1", + output = "ctf_traps:cobble 4", items = { "default:cobble 4", "default:coal_lump" }, always_known = true, level = 1, @@ -267,7 +249,7 @@ end crafting.register_recipe({ type = "inv", output = "grenades:frag 1", - items = { "default:steel_ingot 5", "default:iron_lump" }, + items = { "default:steel_ingot 5", "default:iron_lump", "group:wood", "default:coal_lump",}, always_known = true, level = 1, }) @@ -283,7 +265,7 @@ crafting.register_recipe({ crafting.register_recipe({ type = "inv", output = "grenades:smoke 1", - items = { "default:steel_ingot 5", "default:coal_lump 4" }, + items = { "default:steel_ingot 5", "default:coal_lump 5", "group:wood" }, always_known = true, level = 1, }) diff --git a/mods/ctf/ctf_flag/init.lua b/mods/ctf/ctf_flag/init.lua index f459541..13df89b 100644 --- a/mods/ctf/ctf_flag/init.lua +++ b/mods/ctf/ctf_flag/init.lua @@ -84,8 +84,11 @@ function minetest.is_protected(pos, name, ...) 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.") + hud_event.new(name, { + name = "ctf_bandages:team", + color = "warning", + value = "Too close to the flag to build (< " .. r .. " nodes) !", + }) return true else return old_is_protected(pos, name, ...) diff --git a/mods/ctf/ctf_inventory/init.lua b/mods/ctf/ctf_inventory/init.lua index 546f8c8..785d269 100644 --- a/mods/ctf/ctf_inventory/init.lua +++ b/mods/ctf/ctf_inventory/init.lua @@ -1,4 +1,4 @@ -local color = "#66A0FF" + color = "#66A0FF" local items = { "", color .. "Game Play", @@ -10,8 +10,8 @@ local items = { "* Use medkits to replenish health gradually.", "* Gain more score by killing more than you die, or by capturing the flag.", "* Players are immune for 5 seconds after they respawn.", - "* Access the pro section of the chest by achieving a 2k+ score and", - " killing 2 people for every death.", + "* Access the pro section of the chest by achieving 2k+ score,", + " killing 3 people for every 2 deaths, and capturing the flag at least 10 times", "", color .. "Team Co-op", @@ -30,7 +30,12 @@ local items = { color .. "Contact Moderators", "", - "* Report people who sabotage using /report." + "* Report people using /report or the #reports channel in Discord", + "", + + color .. "Other", + "", + "* Capture The Flag Discord: https://discord.gg/vcZTRPX", } for i = 1, #items do items[i] = minetest.formspec_escape(items[i]) diff --git a/mods/ctf/ctf_map/ctf_map_core/base.lua b/mods/ctf/ctf_map/ctf_map_core/base.lua index b431168..dce97e8 100644 --- a/mods/ctf/ctf_map/ctf_map_core/base.lua +++ b/mods/ctf/ctf_map/ctf_map_core/base.lua @@ -34,12 +34,13 @@ function ctf_map.place_base(color, pos) minetest.set_node(pos3, chest) local inv = minetest.get_meta(pos3):get_inventory() inv:add_item("main", ItemStack("default:cobble 99")) - inv:add_item("main", ItemStack("default:cobble 99")) - inv:add_item("main", ItemStack("default:cobble 99")) + inv:add_item("main", ItemStack("ctf_map:reinforced_cobble 50")) + inv:add_item("main", ItemStack("ctf_traps:damage_cobble 40")) inv:add_item("main", ItemStack("default:wood 99")) inv:add_item("main", ItemStack("default:stick 30")) inv:add_item("main", ItemStack("default:glass 5")) inv:add_item("main", ItemStack("default:torch 10")) + inv:add_item("main", ItemStack("doors:door_steel 2")) end -- Override ctf.get_spawn to implement random spawns diff --git a/mods/ctf/ctf_map/ctf_map_core/chest.lua b/mods/ctf/ctf_map/ctf_map_core/chest.lua index 7d878c3..01ebbc2 100644 --- a/mods/ctf/ctf_map/ctf_map_core/chest.lua +++ b/mods/ctf/ctf_map/ctf_map_core/chest.lua @@ -102,7 +102,7 @@ for _, chest_color in pairs(colors) do else formspec = formspec .. "label[4.75,3;" .. minetest.formspec_escape("You need at least 10000" .. - "\nscore and 1.5+ KD to\naccess the pro section") .. "]" + "\nscore, 1.5+ KD, and 10+\ncaptures to access the\npro section") .. "]" end formspec = formspec .. diff --git a/mods/ctf/ctf_map/ctf_map_core/maps b/mods/ctf/ctf_map/ctf_map_core/maps index 9dd765d..bbf6007 160000 --- a/mods/ctf/ctf_map/ctf_map_core/maps +++ b/mods/ctf/ctf_map/ctf_map_core/maps @@ -1 +1 @@ -Subproject commit 9dd765d328e44d5a1ba2b2a444df4ce7e90b35be +Subproject commit bbf60077855ee90b95ca68f1b9bc853f7d53ecf2 diff --git a/mods/ctf/ctf_map/ctf_map_core/nodes.lua b/mods/ctf/ctf_map/ctf_map_core/nodes.lua index 601cadc..c446752 100644 --- a/mods/ctf/ctf_map/ctf_map_core/nodes.lua +++ b/mods/ctf/ctf_map/ctf_map_core/nodes.lua @@ -424,6 +424,25 @@ do sounds = default.node_sound_leaves_defaults() }) + minetest.register_node(":ctf_map:papyrus", { + description = "Indestructible Papyrus", + drawtype = "plantlike", + tiles = {"default_papyrus.png"}, + inventory_image = "default_papyrus.png", + wield_image = "default_papyrus.png", + paramtype = "light", + sunlight_propagates = true, + walkable = false, + selection_box = { + type = "fixed", + fixed = {-6 / 16, -0.5, -6 / 16, 6 / 16, 0.5, 6 / 16}, + }, + groups = {immortal = 1}, + sounds = default.node_sound_leaves_defaults(), + after_dig_node = function(pos, node, metadata, digger) + default.dig_up(pos, node, digger) + end, + }) minetest.register_node(":ctf_map:jungletree", { description = "Indestructible Jungle Tree", diff --git a/mods/ctf/ctf_map/map_maker/README.md b/mods/ctf/ctf_map/map_maker/README.md index 6dc8630..12b0b64 100644 --- a/mods/ctf/ctf_map/map_maker/README.md +++ b/mods/ctf/ctf_map/map_maker/README.md @@ -35,9 +35,9 @@ There are multiple ways do this, this is the simplest in most cases. ### 4. Place barriers -* Set the middle barrier direction. The barrier is a plane defined by a co-ordinate = 0. - If the barrier is X=0, then it will placed with every node of the barrier having X=0. - If the barrier is Z=0, then it will placed with every node of the barrier having Z=0. +* The barrier is a plane defined by co-ordinate (=0). +* If you choose `X=0` the barrier will be placed having the X co-ordinate as 0. But from a few months, the `X=0` co-ordinate creates bugs and errors. It's better if you choose `Z=0` for creating your map. +* If you choose `Z=0` The barrier will be placed having the Z co-ordinate as 0. * Click "place barrier". Note that this command does not have an undo. * After placing barriers you should place 2 flags where you want bases to be. You get flags in `/gui` --> `Giveme flags` @@ -48,7 +48,7 @@ There are multiple ways do this, this is the simplest in most cases. ### 6. Export * Click export, and wait until completion. -* Copy the resultant folder from `worlddir/schems/` into `ctf_map/ctf_map_core/maps/`. +* Copy the resultant folder from `worlddir/schems/` into `games/capturetheflag/mods/ctf/ctf_map/ctf_map_core/maps/`. * Profit! @@ -56,7 +56,7 @@ There are multiple ways do this, this is the simplest in most cases. ### Map meta -Each map's metadata is stored in an accompanying .conf file containing the following data: +Each map's metadata is stored in an accompanying `map.conf` file containing the following data: * `name`: Name of map. * `author`: Author of the map. @@ -73,7 +73,6 @@ zone `i`, relative to the center of the schem. * `license`: Name of the license of the map. * `other`: [Optional] Misc. information about the map. This is displayed in the maps catalog. * `base_node`: [Optional] Technical name of node to be used for the team base. -* `schematic`: Name of the map's schematic. * `initial_stuff`: [Optional] Comma-separated list of itemstacks to be given to the player on join and on respawn. * `treasures`: [Optional] List of treasures to be registered for the map, in a serialized @@ -86,16 +85,16 @@ format. Refer to the `treasures` sub-section for more details. #### `license` -* Every map must have its own license. Once you've chosen your license, simply add the following line to the map's `.conf` file: +* Every map must have its own license. Once you've chosen your license, simply add the following line to the `map.conf` file: - ```lua + ```properties license = ``` * If attribution is required (for example if you modify other's map and you have to tell who is author of the original map), that has to be appended to the `license` field. If you want to tell more infomation, you can use: - ```lua + ```properties others = ``` @@ -110,12 +109,21 @@ If you want to tell more infomation, you can use: An example `treasures` value that registers steel pick, shotgun, and grenade: -```lua +```properties treasures = default:pick_steel,0.5,5,1,10;shooter:shotgun,0.04,2,1;shooter:grenade,0.08,2,1 ``` (See [here](../../../other/treasurer/README.md) to understand the magic numbers) +#### `initial_stuff` +`initial_stuff` are the items given to players at their (re)spawn. The `initial_stuff` field is located in the `map.conf` file. At least a pickaxe and some torches should be given in the map's `initial_stuff`. + +An example of `initial_stuff` value that registers a stone pickaxe, 30 cobblestones, 5 torches and a pistol is given below. + +```properties +initial_stuff = default:pick_stone,default:cobble 30,default:torch 5,shooter:pistol +``` + ### `screenshot` Every map must have its own screenshot in map's folder. It should have an aspect ratio of 3:2 (screenshot 600x400px is suggested). @@ -133,18 +141,18 @@ Six images which should be in map's folder. * `skybox_5.png` - south * `skybox_6.png` - north -You have to include skybox license in `license` in `.conf` file. We can only accept Free Software licenses, e.g. `CC0`, `CC BY 3.0`, `CC BY 4.0`, `CC BY-SA 3.0`, `CC BY-SA 4.0`. - -You can find some good skyboxes with suitable licenses at [https://opengameart.org](https://opengameart.org/art-search-advanced?field_art_tags_tid=skybox). +You have to include skybox license in `license` in `map.conf` file. We can only accept Free Software licenses, e.g. `CC0`, `CC BY 3.0`, `CC BY 4.0`, `CC BY-SA 3.0`, `CC BY-SA 4.0`. +Before you test your skybox images in local CTF game, run the `update.sh` file in the `games/capturetheflag/` folder. +You can find some good skyboxes with suitable licenses at [opengameart.org](https://opengameart.org/art-search-advanced?field_art_tags_tid=skybox) or [www.humus.name](https://www.humus.name/index.php?page=Textures). ## Editing exported map The easiest way to edit exported maps is the following: * Create a world using `singlenode` mapgen. Enable `WorldEdit` and `ctf_map` mod, -* Go in the world's folder, create a folder named `schems`, and place the `.mts` file inside, +* Go in the world's folder, create a folder named `schems`, and place the `map.mts` file inside, * Start the game, `/grantme all` and enable `fly` (there is no ground in singlenode mapgen), * Do `//1` to set the position where you will generate the map, -* Do `//mtschemplace yourschematic` (where `yourschematic` is the name of the mts file without `.mts`). +* Do `//mtschemplace map` (where `map` is the name of the mts file without `.mts`). When you finish: diff --git a/mods/ctf/ctf_map/map_maker/gui.lua b/mods/ctf/ctf_map/map_maker/gui.lua index 5084b49..c4b45ab 100644 --- a/mods/ctf/ctf_map/map_maker/gui.lua +++ b/mods/ctf/ctf_map/map_maker/gui.lua @@ -5,6 +5,8 @@ function map_maker.show_gui(name) local formspec = { "size[9,9.5]", "bgcolor[#080808BB;true]", + default.gui_bg, + default.gui_bg_img, "label[0,0;1. Select Area]", "field[0.4,1;1,1;posx;X;", context.center.x, "]", @@ -55,7 +57,8 @@ end function map_maker.show_progress_formspec(name, text) minetest.show_formspec(name, "ctf_map:progress", "size[6,1]bgcolor[#080808BB;true]" .. - "label[0,0;" .. + default.gui_bg .. + default.gui_bg_img .. "label[0,0;" .. minetest.formspec_escape(text) .. "]") end diff --git a/mods/ctf/ctf_marker/init.lua b/mods/ctf/ctf_marker/init.lua index ca2600a..9c9d348 100644 --- a/mods/ctf/ctf_marker/init.lua +++ b/mods/ctf/ctf_marker/init.lua @@ -47,7 +47,7 @@ function ctf_marker.add_marker(name, tname, pos, str) if tplayer then teams[tname].players[pname] = tplayer:hud_add({ hud_elem_type = "waypoint", - name = str, + name = "[" .. name .. "'s marker" .. str, number = tonumber(ctf.flag_colors[team.data.color]), world_pos = pos }) @@ -99,7 +99,7 @@ minetest.register_chatcommand("m", { local tname = ctf.player(name).team -- Handle waypoint string - local str = (param and param:trim() ~= "") and param or name .. "'s marker" + local str = (param and param:trim() ~= "") and ": " .. param or "" if pointed.type == "object" then local concat local obj = pointed.ref @@ -126,7 +126,7 @@ minetest.register_chatcommand("m", { end str = concat and str .. " <" .. concat .. ">" end - str = "[" .. str .. "]" + str = str .. "]" -- Remove existing marker if it exists ctf_marker.remove_marker(tname) @@ -134,3 +134,46 @@ minetest.register_chatcommand("m", { ctf_marker.add_marker(name, tname, minetest.get_pointed_thing_position(pointed), str) end }) + + +local function mr_command(name) + local tname = ctf.player(name).team + local player = minetest.get_player_by_name(name) + local mmsg = "" + local args = "" + + local function hud_check() + if teams[tname].players[name] then + mmsg = player:hud_get(teams[tname].players[name]).name + args = mmsg:split("'") + end + end + + if pcall(hud_check) then + if args[1] == "[" .. name then + ctf_marker.remove_marker(tname) + local team = ctf.team(tname) + for pname, _ in pairs(team.players) do + minetest.chat_send_player(pname, msg("* " .. name .. " removed their marker!")) + end + elseif args[1] == "" or nil then + minetest.chat_send_player(name, msg("No marker to remove")) + else + minetest.chat_send_player(name, msg("Not your marker!")) + end + else + minetest.chat_send_player(name, msg("No marker to remove")) + end +end + +minetest.register_chatcommand("m_remove", { + description = "Remove your own marker (/mr)", + privs = {interact = true}, + func = mr_command +}) + +minetest.register_chatcommand("mr", { + description = "Remove your own marker", + privs = {interact = true}, + func = mr_command +}) diff --git a/mods/ctf/ctf_match/buildtime.lua b/mods/ctf/ctf_match/buildtime.lua index 5caead3..24ba042 100644 --- a/mods/ctf/ctf_match/buildtime.lua +++ b/mods/ctf/ctf_match/buildtime.lua @@ -62,14 +62,21 @@ minetest.register_globalstep(function(delta) end end) -minetest.register_on_punchplayer(function(_, hitter) +local old_can_attack = ctf.can_attack +function ctf.can_attack(player, hitter, ...) if ctf_match.is_in_build_time() then if hitter:is_player() then - minetest.chat_send_player(hitter:get_player_name(), "Match hasn't started yet!") + hud_event.new(hitter:get_player_name(), { + name = "ctf_match:buildtime_hit", + color = "warning", + value = "Match hasn't started yet!", + }) end - return true + return false end -end) + + return old_can_attack(player, hitter, ...) +end ctf_match.register_on_build_time_start(function() minetest.chat_send_all(minetest.colorize("#fcca05", ("Prepare your base! Match starts in " .. diff --git a/mods/ctf/ctf_match/chat.lua b/mods/ctf/ctf_match/chat.lua index 85d328a..009ab51 100644 --- a/mods/ctf/ctf_match/chat.lua +++ b/mods/ctf/ctf_match/chat.lua @@ -52,7 +52,7 @@ minetest.register_chatcommand("ctf_respawn", { local restart_on_next_match = false local restart_on_next_match_by = nil -minetest.register_chatcommand("ctf_queue_restart", { +minetest.register_chatcommand("restart", { description = "Queue server restart", privs = { server = true @@ -65,7 +65,7 @@ minetest.register_chatcommand("ctf_queue_restart", { end }) -minetest.register_chatcommand("ctf_unqueue_restart", { +minetest.register_chatcommand("unqueue_restart", { description = "Unqueue server restart", privs = { server = true diff --git a/mods/ctf/ctf_match/vote.lua b/mods/ctf/ctf_match/vote.lua index 743ba8d..eef2c6e 100644 --- a/mods/ctf/ctf_match/vote.lua +++ b/mods/ctf/ctf_match/vote.lua @@ -6,6 +6,16 @@ function ctf_match.register_on_skip_map(func) table.insert(ctf_match.registered_on_skip_map, func) end +function skip() + for i = 1, #ctf_match.registered_on_skip_map do + ctf_match.registered_on_skip_map[i]() + end + ctf_match.next() +end + +local can_vote_skip = false +local voted_skip = false +local flags_hold = 0 function ctf_match.vote_next(name) local tcolor = ctf_colors.get_color(ctf.player(name)).css or "#FFFFFFFF" minetest.chat_send_all(minetest.colorize("#FFAA11", "Vote started by ") .. @@ -22,10 +32,12 @@ function ctf_match.vote_next(name) if result == "yes" then minetest.chat_send_all("Vote to skip match passed, " .. #results.yes .. " to " .. #results.no) - for i = 1, #ctf_match.registered_on_skip_map do - ctf_match.registered_on_skip_map[i]() + + can_vote_skip = false + voted_skip = true + if flags_hold <= 0 then + skip() end - ctf_match.next() else minetest.chat_send_all("Vote to skip match failed, " .. #results.no .. " to " .. #results.yes) @@ -53,9 +65,8 @@ minetest.register_chatcommand("vote", { local matchskip_time local matchskip_timer = 0 -local can_skip = false minetest.register_globalstep(function(dtime) - if not can_skip then return end + if not can_vote_skip then return end matchskip_timer = matchskip_timer + dtime @@ -68,19 +79,26 @@ minetest.register_globalstep(function(dtime) end end) -local function prevent_autoskip() - can_skip = false -end - -ctf.register_on_new_game(prevent_autoskip) -ctf_flag.register_on_pick_up(prevent_autoskip) +ctf.register_on_new_game(function() + can_vote_skip = false + voted_skip = false + flags_hold = 0 +end) +ctf_flag.register_on_pick_up(function() + flags_hold = flags_hold + 1 +end) ctf_flag.register_on_drop(function() - can_skip = true + flags_hold = flags_hold - 1 + if voted_skip and flags_hold <= 0 then + minetest.after(5, function() + skip() + end) + end end) ctf_match.register_on_build_time_end(function() - can_skip = true + can_vote_skip = true matchskip_timer = 0 -- Set to initial vote time - matchskip_time = tonumber(minetest.settings:get("ctf_match.auto_skip_delay")) or 60 * 60 + matchskip_time = tonumber(minetest.settings:get("ctf_match.auto_skip_delay")) or 50 * 60 end) diff --git a/mods/ctf/ctf_metrics/init.lua b/mods/ctf/ctf_metrics/init.lua index 36ebc02..6210ea3 100644 --- a/mods/ctf/ctf_metrics/init.lua +++ b/mods/ctf/ctf_metrics/init.lua @@ -1,44 +1,77 @@ -if not minetest.global_exists("prometheus") then +if not minetest.create_metric then return end -local kill_counter = 0 + +local function counter(...) + return minetest.create_metric("counter", ...) +end +local function gauge(...) + return minetest.create_metric("gauge", ...) +end + + +-- +-- Kills +-- +local kill_counter = counter("ctf_kills", "Total kills") ctf.register_on_killedplayer(function(victim, killer, type) - kill_counter = kill_counter + 1 + kill_counter:increment() end) -local function step() - prometheus.post("minetest_kills", kill_counter) - kill_counter = 0 - local sum = 0 - local avg = 0 - local bins = { r050=0, r200=0, r5000=0, rest=0 } - if #minetest.get_connected_players() > 0 then - for _, player in pairs(minetest.get_connected_players()) do - local total, _ = ctf_stats.player(player:get_player_name()) - sum = sum + total.score +-- +-- Damage +-- +local punch_counter = counter("ctf_punches", "Total punches") +local damage_counter = counter("ctf_damage_given", "Total damage given") +ctf.register_on_attack(function(_, _, _, _, _, damage) + punch_counter:increment() + damage_counter:increment(damage) +end) - if total.score > 174000 then - bins.r050 = bins.r050 + 1 - elseif total.score > 10000 then - bins.r200 = bins.r200 + 1 - elseif total.score > 1000 then - bins.r5000 = bins.r5000 + 1 - else - bins.rest = bins.rest + 1 - end - end - avg = sum / #minetest.get_connected_players() - end +-- +-- Digs / places +-- +local dig_counter = counter("ctf_digs", "Total digs") +local place_counter = counter("ctf_places", "Total digs") +minetest.register_on_dignode(function() + dig_counter:increment() +end) +minetest.register_on_placenode(function() + place_counter:increment() +end) - for key, value in pairs(bins) do - prometheus.post("minetest_ctf_score_bins{rank=\"" .. key .. "\"}", value) - end - prometheus.post("minetest_ctf_score_total", sum) - prometheus.post("minetest_ctf_score_avg", avg) +-- +-- Gauges +-- - minetest.after(15, step) +local class_gauges = {} +for _, class in ipairs(ctf_classes.__classes_ordered) do + class_gauges[class.name] = gauge("ctf_class_players", "Player count for class", { + class = class.name + }) end -minetest.after(15, step) + +local online_score = gauge("ctf_online_score", "Total score of online players") +local match_time = gauge("ctf_match_play_time", "Current time in match") + +minetest.register_globalstep(function() + local sum = 0 + local class_counts = {} + for _, player in pairs(minetest.get_connected_players()) do + local total, _ = ctf_stats.player(player:get_player_name()) + sum = sum + total.score + + local class = ctf_classes.get(player) + class_counts[class.name] = (class_counts[class.name] or 0) + 1 + end + + online_score:set(sum) + match_time:set(ctf_match.get_match_duration() or 0) + + for _, class in ipairs(ctf_classes.__classes_ordered) do + class_gauges[class.name]:set(class_counts[class.name] or 0) + end +end) diff --git a/mods/ctf/ctf_metrics/mod.conf b/mods/ctf/ctf_metrics/mod.conf index 2a0f1f2..eac4cbd 100644 --- a/mods/ctf/ctf_metrics/mod.conf +++ b/mods/ctf/ctf_metrics/mod.conf @@ -1,3 +1,2 @@ name = ctf_metrics -depends = ctf, ctf_stats -optional_depends = prometheus +depends = ctf, ctf_stats, ctf_classes diff --git a/mods/ctf/ctf_respawn_immunity/init.lua b/mods/ctf/ctf_respawn_immunity/init.lua index ae64984..3f763c7 100644 --- a/mods/ctf/ctf_respawn_immunity/init.lua +++ b/mods/ctf/ctf_respawn_immunity/init.lua @@ -34,32 +34,36 @@ function ctf_respawn_immunity.update_effects(player) -- end end -minetest.register_on_punchplayer(function(player, hitter, - time_from_last_punch, tool_capabilities, dir, damage) +local old_can_attack = ctf.can_attack +function ctf.can_attack(player, hitter, ...) if not player or not hitter then - return false + return end local pname = player:get_player_name() local hname = hitter:get_player_name() - local pteam = ctf.player(pname).team - local hteam = ctf.player(hname).team - if pteam ~= hteam then - if player and ctf_respawn_immunity.is_immune(player) then - minetest.chat_send_player(hname, minetest.colorize("#EE8822", pname .. - " just respawned or joined," .. " and is immune to attacks!")) - return true - end - - if hitter and ctf_respawn_immunity.is_immune(hitter) then - minetest.chat_send_player(hname, minetest.colorize("#FF8C00", - "Your immunity has ended because you attacked a player")) - immune_players[hname] = nil - ctf_respawn_immunity.update_effects(hitter) - end + if ctf_respawn_immunity.is_immune(player) then + hud_event.new(hname, { + name = "ctf_respawn_immunity:hit", + color = 0xEE8822, + value = pname .. " has respawn immunity!", + }) + return false end -end) + + if ctf_respawn_immunity.is_immune(hitter) then + hud_event.new(hname, { + name = "ctf_respawn_immunity:end", + color = 0xFF8C00, + value = "Your immunity has ended!", + }) + immune_players[hname] = nil + ctf_respawn_immunity.update_effects(hitter) + end + + return old_can_attack(player, hitter, ...) +end minetest.register_on_joinplayer(ctf_respawn_immunity.set_immune) minetest.register_on_respawnplayer(ctf_respawn_immunity.set_immune) diff --git a/mods/ctf/ctf_stats/chat.lua b/mods/ctf/ctf_stats/chat.lua index a3f3aa5..0d5e921 100644 --- a/mods/ctf/ctf_stats/chat.lua +++ b/mods/ctf/ctf_stats/chat.lua @@ -240,6 +240,11 @@ minetest.register_chatcommand("makepro", { modified = true end + if stats.captures < 10 then + stats.captures = 10 + modified = true + end + if modified then ctf_stats.request_save() return true, "Made " .. param .. " a pro!" diff --git a/mods/ctf/ctf_stats/init.lua b/mods/ctf/ctf_stats/init.lua index 19c4a07..8531dcd 100644 --- a/mods/ctf/ctf_stats/init.lua +++ b/mods/ctf/ctf_stats/init.lua @@ -207,7 +207,7 @@ end function ctf_stats.is_pro(name) local stats = ctf_stats.player(name) local kd = stats.kills / (stats.deaths == 0 and 1 or stats.deaths) - return stats.score >= 10000 and kd >= 1.5 + return stats.score >= 10000 and kd >= 1.5 and stats.captures >= 10 end ctf.register_on_join_team(function(name, tname, oldteam) @@ -373,7 +373,7 @@ local function invHasGoodWeapons(inv) return false end -local function calculateKillReward(victim, killer, toolcaps) +function ctf_stats.calculateKillReward(victim, killer, toolcaps) local vmain, victim_match = ctf_stats.player(victim) if not vmain or not victim_match then return 5 end @@ -417,33 +417,23 @@ local function calculateKillReward(victim, killer, toolcaps) return reward end -ctf.register_on_killedplayer(function(victim, killer, _, toolcaps) +ctf.register_on_killedplayer(function(victim, killer) -- Suicide is not encouraged here at CTF if victim == killer then return end local main, match = ctf_stats.player(killer) if main and match then - local reward = calculateKillReward(victim, killer, toolcaps) main.kills = main.kills + 1 - main.score = main.score + reward match.kills = match.kills + 1 - match.score = match.score + reward match.kills_since_death = match.kills_since_death + 1 _needs_save = true - - reward = math.floor(reward * 100) / 100 - - hud_score.new(killer, { - name = "ctf_stats:kill_score", - color = "0x00FF00", - value = reward - }) end end) minetest.register_on_dieplayer(function(player) local main, match = ctf_stats.player(player:get_player_name()) + if main and match then main.deaths = main.deaths + 1 match.deaths = match.deaths + 1 diff --git a/mods/mtg/default/nodes.lua b/mods/mtg/default/nodes.lua index 20db166..e2aa4ad 100644 --- a/mods/mtg/default/nodes.lua +++ b/mods/mtg/default/nodes.lua @@ -52,6 +52,8 @@ default:dirt_with_dry_grass default:dirt_with_snow default:dirt_with_rainforest_litter default:dirt_with_coniferous_litter +default:dry_dirt +default:dry_dirt_with_dry_grass default:permafrost default:permafrost_with_stones @@ -497,6 +499,25 @@ minetest.register_node("default:dirt_with_coniferous_litter", { }), }) +minetest.register_node("default:dry_dirt", { + description = "Savanna Dirt", + tiles = {"default_dry_dirt.png"}, + groups = {crumbly = 3, soil = 1}, + sounds = default.node_sound_dirt_defaults(), +}) + +minetest.register_node("default:dry_dirt_with_dry_grass", { + description = "Savanna Dirt with Savanna Grass", + tiles = {"default_dry_grass.png", "default_dry_dirt.png", + {name = "default_dry_dirt.png^default_dry_grass_side.png", + tileable_vertical = false}}, + groups = {crumbly = 3, soil = 1}, + drop = "default:dry_dirt", + sounds = default.node_sound_dirt_defaults({ + footstep = {name = "default_grass_footstep", gain = 0.4}, + }), +}) + minetest.register_node("default:permafrost", { description = "Permafrost", tiles = {"default_permafrost.png"}, diff --git a/mods/mtg/default/textures/default_dry_dirt.png b/mods/mtg/default/textures/default_dry_dirt.png new file mode 100644 index 0000000..8ee5398 Binary files /dev/null and b/mods/mtg/default/textures/default_dry_dirt.png differ diff --git a/mods/mtg/doors/init.lua b/mods/mtg/doors/init.lua index b606e80..240134d 100644 --- a/mods/mtg/doors/init.lua +++ b/mods/mtg/doors/init.lua @@ -188,27 +188,6 @@ function _doors.door_toggle(pos, node, clicker) end -local function on_place_node(place_to, newnode, - placer, oldnode, itemstack, pointed_thing) - -- Run script hook - for _, callback in ipairs(minetest.registered_on_placenodes) do - -- Deepcopy pos, node and pointed_thing because callback can modify them - local place_to_copy = {x = place_to.x, y = place_to.y, z = place_to.z} - local newnode_copy = - {name = newnode.name, param1 = newnode.param1, param2 = newnode.param2} - local oldnode_copy = - {name = oldnode.name, param1 = oldnode.param1, param2 = oldnode.param2} - local pointed_thing_copy = { - type = pointed_thing.type, - above = vector.new(pointed_thing.above), - under = vector.new(pointed_thing.under), - ref = pointed_thing.ref, - } - callback(place_to_copy, newnode_copy, placer, - oldnode_copy, itemstack, pointed_thing_copy) - end -end - local function can_dig_door(pos, digger) replace_old_owner_information(pos) if default.can_interact_with_node(digger, pos) then @@ -362,15 +341,19 @@ function doors.register(name, def) meta:set_string("owner_team", tname) end + local copy = table.copy + local newnode = minetest.get_node(pos) + for _, on_placenode in ipairs(minetest.registered_on_placenodes) do + if on_placenode(copy(pos), copy(newnode), placer, copy(node), ItemStack(itemstack), copy(pointed_thing)) then + return itemstack + end + end if not (creative and creative.is_enabled_for and creative.is_enabled_for(pn)) then itemstack:take_item() end minetest.sound_play(def.sounds.place, {pos = pos}) - on_place_node(pos, minetest.get_node(pos), - placer, node, itemstack, pointed_thing) - return itemstack end }) diff --git a/mods/mtg/wool/init.lua b/mods/mtg/wool/init.lua index fa6b569..a969e0c 100644 --- a/mods/mtg/wool/init.lua +++ b/mods/mtg/wool/init.lua @@ -29,7 +29,7 @@ for i = 1, #dyes do is_ground_content = false, groups = {snappy = 2, choppy = 2, oddly_breakable_by_hand = 3, flammable = 3, wool = 1}, - sounds = default.node_sound_defaults(), + sounds = default.node_sound_dirt_defaults(), }) end diff --git a/mods/other/hud_events/README.md b/mods/other/hud_events/README.md new file mode 100644 index 0000000..dae8219 --- /dev/null +++ b/mods/other/hud_events/README.md @@ -0,0 +1,57 @@ +# `hud_events` + +Forked and edited from `hud_score` by ANAND (ClobberXD), licensed under the LGPLv2.1+ license. + +`hud_events` provides an API to display HUD event elements which can be used to +display various hints and messages. + +## Methods + +- `hud_event.new(name, event_def)`: Adds a new HUD event element to player `name`. + - `name` [string]: Player name + - `event_def` [table]: HUD event element definition. See below. + +## HUD event element definition + +HUD event element definition table, passed to `hud_event.new`. + +Example definition: + +```lua +{ + name = "ctf_bandages:healing", -- Can be any arbitrary string + color = "0x00FF00", -- Should be compatible with Minetest's HUD def + value = "x has healed y", -- The actual event to be displayed + -- Field `time` is automatically added by `hud_event.new` + -- to keep track of element expiry +} +``` + +## `players` table + +This is a table of tables, indexed by player names. This table holds the HUD +data of all online players. Each sub-table is a list of HUD event elements, +which are added by `hud_event.new`. + +```lua +local players = { + ["name"] = { + [1] = , + [2] = , + [3] = + ... + }, + ["name2"] = { + ... + }, + ... +} +``` + +## Changes + +Changes that have been made compared to the original `hud_score` mod. Lines mentioned underneath refer to the lines in the `hud_events`' init.lua file. +- replaced all occurences of `score` with `event` (10th March 2021) +- changed variables and arguments in the lines 5, 6 and 36 (10th march 2021) +- edited and added arguments in line 39 and 40 (10th march 2021) +- deleted an `if` statement after line 28 (10th march 2021) diff --git a/mods/other/hud_events/init.lua b/mods/other/hud_events/init.lua new file mode 100644 index 0000000..84124f7 --- /dev/null +++ b/mods/other/hud_events/init.lua @@ -0,0 +1,139 @@ +hud_event = {} +local hud = hudkit() + +local players = {} +local duration = 7 +local max = 3 +local next_check = 10000000 +-- Bootstrap 5 palette +local colors = { + primary = 0x0D6EFD, + secondary = 0x6C757D, + success = 0x198754, + info = 0x0DCAF0, + warning = 0xFFC107, + danger = 0xDC3545, + light = 0xF8F9FA, + dark = 0x212529, +} +hud_event.colors = colors + +local function update(name) + local player = minetest.get_player_by_name(name) + if not player then + return + end + + -- Handle all elements marked for deletion + -- and rebuild table + local temp = {} + for _, def in ipairs(players[name]) do + if def.delete then + if hud:exists(player, def.name) then + hud:remove(player, def.name) + end + else + table.insert(temp, def) + end + end + + for i, def in ipairs(temp) do + local text = tostring(def.value) + if hud:exists(player, def.name) then + hud:change(player, def.name, "text", text) + hud:change(player, def.name, "offset", {x = 0, y = i * 20}) + else + hud:add(player, def.name, { + hud_elem_type = "text", + alignment = {x = 0, y = 0}, + position = {x = 0.5, y = 0.7}, + offset = {x = 0, y = i * 20}, + number = tonumber(def.color), + text = text, + z_index = -200 + }) + end + end + players[name] = temp +end + +function hud_event.new(name, def) + -- Verify HUD event element def + if not name or not def or type(def) ~= "table" or + not def.name or not def.value or not def.color then + error("hud_event: Invalid HUD event element definition", 2) + end + + def.color = colors[def.color] or def.color + + local player = minetest.get_player_by_name(name) + if not player then + return + end + + -- Store element expiration time in def.time + -- and append event element def to players[name] + def.time = os.time() + duration + if next_check > duration then + next_check = duration + end + + -- If a HUD event element with the same name exists already, + -- reuse it instead of creating a new element + local is_new = true + for i, hud_event_spec in ipairs(players[name]) do + if hud_event_spec.name == def.name then + is_new = false + players[name][i] = def + break + end + end + + if is_new then + table.insert(players[name], def) + end + + -- If more than `max` active elements, mark oldest element for deletion + if #players[name] > max then + players[name][1].delete = true + end + + update(name) +end + +minetest.register_globalstep(function(dtime) + next_check = next_check - dtime + if next_check > 0 then + return + end + + next_check = 10000000 + + -- Loop through HUD score elements of all players + -- and remove them if they've expired + for name, hudset in pairs(players) do + local modified = false + for i, def in pairs(hudset) do + local rem = def.time - os.time() + if rem <= 0 then + def.delete = true + modified = true + elseif rem < next_check then + next_check = rem + end + end + + -- If a player's hudset was modified, update player's HUD + if modified then + update(name) + end + end +end) + +minetest.register_on_joinplayer(function(player) + players[player:get_player_name()] = {} +end) + +minetest.register_on_leaveplayer(function(player) + players[player:get_player_name()] = nil +end) diff --git a/mods/other/hud_events/mod.conf b/mods/other/hud_events/mod.conf new file mode 100644 index 0000000..e91b4e4 --- /dev/null +++ b/mods/other/hud_events/mod.conf @@ -0,0 +1,3 @@ +name = hud_events +description = API for displaying events on HUD +depends = hudkit diff --git a/mods/other/random_messages/init.lua b/mods/other/random_messages/init.lua index 9048dd9..26c3fd0 100644 --- a/mods/other/random_messages/init.lua +++ b/mods/other/random_messages/init.lua @@ -56,11 +56,11 @@ function random_messages.read_messages() "You gain more score the better the opponent you defeat.", "Find weapons in chests or mine and use furnaces to make stronger swords.", "Players are immune to attack for 5 seconds after they respawn.", - "Access the pro section of the chest by achieving a 10k+ score and killing 3 people for every 2 deaths.", + "Access the pro section of the chest by achieving a 10k+ score, killing 3 people for every 2 deaths and capture the flag 10 times.", "Use team doors (steel) to stop the enemy walking into your base.", "Craft 6 cobbles and 1 steel ingot together to make reinforced cobble.", "Sprint by pressing the fast key (E) when you have stamina.", - "Like CTF? Give feedback using /report, and consider donating at rubenwardy.com/donate", + "Like CTF? Give feedback using /report, and consider joining the Discord", "Want to submit your own map? Visit ctf.rubenwardy.com to get involved.", "Using limited resources for building structures that don't strengthen your base's defences is discouraged.", "To report misbehaving players to moderators, please use /report ", @@ -72,13 +72,14 @@ function random_messages.read_messages() "Use /r or /rn to check the rankings of the player in the given rank.", "Use bandages on team-mates to heal them by 3-4 HP if their health is below 15 HP.", "Use /m to add a team marker at pointed location, that's visible only to team-mates.", - "Use /summary to check scores of the current match and the previous match.", + "Use /mr to remove your marker.", + "Use /summary or /s to check scores of the current match and the previous match.", "Use /maps to view the maps catalog. It also contains license info and attribution details.", "Change your class in your base by right clicking the home flag or typing /class.", "Medics cause troops within 10 metres to regenerate health faster.", "Hitting your enemy does more damage than not hitting them.", "Press right mouse button or double-tap the screen to activate scope while wielding a sniper rifle.", - "The 'Updates' tab in your inventory will show some of the latest updates to CTF", + "Medics can dig pillars by right clicking the base of one with their paxel.", } end diff --git a/mods/pvp/anticoward/init.lua b/mods/pvp/anticoward/init.lua index 1dc70ec..bba05b5 100644 --- a/mods/pvp/anticoward/init.lua +++ b/mods/pvp/anticoward/init.lua @@ -3,29 +3,18 @@ potential_cowards = {} local TIMER_UPDATE_INTERVAL = 2 local COMBAT_TIMEOUT_TIME = 20 +local COMBATLOG_SCORE_PENALTY = 10 -- --- Make suicides and combat logs award last puncher with kill -- -minetest.register_on_punchplayer(function(player, hitter, -time_from_last_punch, tool_capabilities, dir, damage) +ctf.register_on_attack(function(player, hitter, + time_from_last_punch, tool_capabilities, dir, damage) if player and hitter then - if ctf_respawn_immunity.is_immune(player) or ctf_match.is_in_build_time() then - return - end - local pname = player:get_player_name() local hname = hitter:get_player_name() - local to = ctf.player(pname) - local from = ctf.player(hname) - - if to.team == from.team and to.team ~= "" and - to.team ~= nil and to.name ~= from.name then - return - end - local hp = player:get_hp() - damage if hp <= 0 then if potential_cowards[pname] then @@ -38,7 +27,7 @@ time_from_last_punch, tool_capabilities, dir, damage) potential_cowards[hname] = nil end - return false + return end if not potential_cowards[pname] then @@ -94,11 +83,6 @@ minetest.register_on_dieplayer(function(player, reason) last_attacker:hud_remove(potential_cowards[hname].hud or 0) potential_cowards[hname] = nil end - - if potential_cowards[pname] then - player:hud_remove(potential_cowards[pname].hud or 0) - potential_cowards[pname] = nil - end else for victim in pairs(potential_cowards) do if potential_cowards[victim].puncher == pname then @@ -114,6 +98,11 @@ minetest.register_on_dieplayer(function(player, reason) end end end + + if potential_cowards[pname] then + player:hud_remove(potential_cowards[pname].hud or 0) + potential_cowards[pname] = nil + end end) minetest.register_on_leaveplayer(function(player, timeout) @@ -136,6 +125,17 @@ minetest.register_on_leaveplayer(function(player, timeout) ) end + local main, match = ctf_stats.player(pname) + + if main and match then + main.deaths = main.deaths + 1 + match.deaths = match.deaths + 1 + main.score = main.score - COMBATLOG_SCORE_PENALTY + match.score = match.score - COMBATLOG_SCORE_PENALTY + match.kills_since_death = 0 + ctf_stats.request_save() + end + potential_cowards[pname] = nil end end) @@ -156,6 +156,7 @@ minetest.register_globalstep(function(dtime) end potential_cowards[k] = nil + kill_assist.clear_assists(k) end end @@ -164,9 +165,23 @@ minetest.register_globalstep(function(dtime) end) ctf_match.register_on_new_match(function() + for coward, info in pairs(potential_cowards) do + coward = minetest.get_player_by_name(coward) + + if coward and info.hud then + coward:hud_remove(info.hud) + end + end potential_cowards = {} end) ctf.register_on_new_game(function() + for coward, info in pairs(potential_cowards) do + coward = minetest.get_player_by_name(coward) + + if coward and info.hud then + coward:hud_remove(info.hud) + end + end potential_cowards = {} end) diff --git a/mods/pvp/anticoward/mod.conf b/mods/pvp/anticoward/mod.conf index a8245cc..743a1ed 100644 --- a/mods/pvp/anticoward/mod.conf +++ b/mods/pvp/anticoward/mod.conf @@ -1,2 +1,2 @@ name = anticoward -depends = ctf, ctf_classes, ctf_match +depends = ctf, ctf_classes, ctf_match, kill_assist diff --git a/mods/pvp/antisabotage/init.lua b/mods/pvp/antisabotage/init.lua deleted file mode 100644 index cd8f2f1..0000000 --- a/mods/pvp/antisabotage/init.lua +++ /dev/null @@ -1,32 +0,0 @@ --- Code by Apelta. Mutelated by Lone_Wolf. Mutelated again by Apelta. -antisabotage = {} - -function antisabotage.is_sabotage(pos, oldnode, digger) -- used for paxel - local dname = digger:get_player_name() - - for _, player in pairs(minetest.get_connected_players()) do - local name = player:get_player_name() - - if name ~= dname and ctf.players[name].team == ctf.players[dname].team then - local player_pos = player:get_pos() - - if math.floor(player_pos.y) == pos.y and vector.distance(player_pos, pos) <= 1.5 then - minetest.set_node(pos, oldnode) - - -- Remove all node drops - for _, item in pairs(minetest.get_node_drops(oldnode)) do - digger:get_inventory():remove_item("main", ItemStack(item)) - end - - minetest.chat_send_player(dname, "You can't mine blocks under your teammates!") - return true - end - end - end -end - -minetest.register_on_dignode(function(pos, oldnode, digger) - if not digger:is_player() then return end - - antisabotage.is_sabotage(pos, oldnode, digger) -end) diff --git a/mods/pvp/antisabotage/license.txt b/mods/pvp/antisabotage/license.txt deleted file mode 100644 index 9026f4b..0000000 --- a/mods/pvp/antisabotage/license.txt +++ /dev/null @@ -1,7 +0,0 @@ -Copyright 2020 Apelta - -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/pvp/antisabotage/mod.conf b/mods/pvp/antisabotage/mod.conf deleted file mode 100644 index b810098..0000000 --- a/mods/pvp/antisabotage/mod.conf +++ /dev/null @@ -1,2 +0,0 @@ -name = antisabotage -depends = ctf diff --git a/mods/pvp/antisabotage/readme.md b/mods/pvp/antisabotage/readme.md deleted file mode 100644 index 7fc21cc..0000000 --- a/mods/pvp/antisabotage/readme.md +++ /dev/null @@ -1,3 +0,0 @@ -# Anti Sabotage - -A simple mod that prevents players from sabotaging their teammates by digging blocks out from underneath them diff --git a/mods/pvp/hpregen/init.lua b/mods/pvp/hpregen/init.lua index 3b83e7e..6d2c48a 100644 --- a/mods/pvp/hpregen/init.lua +++ b/mods/pvp/hpregen/init.lua @@ -16,8 +16,10 @@ local function regen_all() local newhp = oldhp + hpregen.amount if newhp > player:get_properties().hp_max then newhp = player:get_properties().hp_max + kill_assist.clear_assists(player:get_player_name()) end if oldhp ~= newhp then + kill_assist.add_heal_assist(player:get_player_name(), hpregen.amount) player:set_hp(newhp) end end diff --git a/mods/pvp/hpregen/mod.conf b/mods/pvp/hpregen/mod.conf index 7fb7317..5ff8c6f 100644 --- a/mods/pvp/hpregen/mod.conf +++ b/mods/pvp/hpregen/mod.conf @@ -1 +1,2 @@ name = hpregen +depends = kill_assist diff --git a/mods/pvp/kill_assist/init.lua b/mods/pvp/kill_assist/init.lua new file mode 100644 index 0000000..b813500 --- /dev/null +++ b/mods/pvp/kill_assist/init.lua @@ -0,0 +1,90 @@ +kill_assist = {} + +local kill_assists = {} + +function kill_assist.clear_assists(player) + if type(player) == "string" then + kill_assists[player] = nil + else + kill_assists = {} + end +end + +function kill_assist.add_assist(victim, attacker, damage) + if victim == attacker then return end + + if not kill_assists[victim] then + kill_assists[victim] = { + players = {}, + hp_offset = 0 + } + end + + kill_assists[victim].players[attacker] = (kill_assists[victim].players[attacker] or 0) + damage +end + +function kill_assist.add_heal_assist(victim, healed_hp) + if not kill_assists[victim] then return end + + kill_assists[victim].hp_offset = kill_assists[victim].hp_offset + healed_hp +end + +function kill_assist.reward_assists(victim, killer, reward) + local max_hp = minetest.get_player_by_name(victim):get_properties().max_hp or 20 + + if not kill_assists[victim] then + if victim ~= killer then + kill_assist.add_assist(victim, killer, max_hp) + else + return + end + end + + for name, damage in pairs(kill_assists[victim].players) do + if minetest.get_player_by_name(name) then + local help_percent = damage / (max_hp + kill_assists[victim].hp_offset) + local main, match = ctf_stats.player(name) + local color = "0x00FFFF" + + if name == killer or help_percent >= 0.33 then + reward = math.max(math.floor((reward * help_percent)*100)/100, 1) + end + + match.score = match.score + reward + main.score = main.score + reward + + if name == killer then + color = "0x00FF00" + end + + hud_score.new(name, { + name = "kill_assist:score", + color = color, + value = reward + }) + end + end + + ctf_stats.request_save() + kill_assist.clear_assists(victim) +end + +ctf.register_on_killedplayer(function(victim, killer, _, toolcaps) + local reward = ctf_stats.calculateKillReward(victim, killer, toolcaps) + reward = math.floor(reward * 100) / 100 + kill_assist.reward_assists(victim, killer, reward) +end) + +ctf.register_on_attack(function(player, hitter, _, _, _, damage) + kill_assist.add_assist(player:get_player_name(), hitter:get_player_name(), damage) +end) + +ctf_match.register_on_new_match(function() + kill_assist.clear_assists() +end) +ctf.register_on_new_game(function() + kill_assist.clear_assists() +end) +minetest.register_on_leaveplayer(function(player) + kill_assist.clear_assists(player) +end) diff --git a/mods/pvp/kill_assist/mod.conf b/mods/pvp/kill_assist/mod.conf new file mode 100644 index 0000000..f6f4bda --- /dev/null +++ b/mods/pvp/kill_assist/mod.conf @@ -0,0 +1,2 @@ +name = kill_assist +depends = ctf, ctf_match, ctf_stats, hud_score diff --git a/mods/pvp/medkits/init.lua b/mods/pvp/medkits/init.lua index a9f6c11..710ccb9 100644 --- a/mods/pvp/medkits/init.lua +++ b/mods/pvp/medkits/init.lua @@ -71,8 +71,11 @@ local function stop_healing(player, interrupted) players[name] = nil if interrupted then - minetest.chat_send_player(name, minetest.colorize("#FF4444", - "Your healing was interrupted"..reason_handler(interrupted))) + hud_event.new(name, { + name = "medkits:interrupt", + color = 0xFF4444, + value = "Your healing was interrupted" .. reason_handler(interrupted), + }) player:set_hp(info.hp) player:get_inventory():add_item("main", ItemStack("medkits:medkit 1")) end @@ -117,6 +120,7 @@ minetest.register_globalstep(function(dtime) if pstat then local hp = player:get_hp() if hp < pstat.regen_max then + kill_assist.add_heal_assist(name, regen_step) player:set_hp(math.min(hp + regen_step, pstat.regen_max)) else stop_healing(player) diff --git a/mods/pvp/place_limit/License.txt b/mods/pvp/place_limit/License.txt new file mode 100644 index 0000000..2e7c235 --- /dev/null +++ b/mods/pvp/place_limit/License.txt @@ -0,0 +1,19 @@ +Copyright (c) 2021 appgurueu + +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/pvp/place_limit/Readme.md b/mods/pvp/place_limit/Readme.md new file mode 100644 index 0000000..8c201a7 --- /dev/null +++ b/mods/pvp/place_limit/Readme.md @@ -0,0 +1,8 @@ +# `place_limit` + +Fixes two things related to node placement: + +* Ratelimits node placement +* Disallows building to non-pointable nodes (anticheat + race condition fix) + +Licensed under the MIT license, written by appgurueu. \ No newline at end of file diff --git a/mods/pvp/place_limit/init.lua b/mods/pvp/place_limit/init.lua new file mode 100644 index 0000000..49c64e1 --- /dev/null +++ b/mods/pvp/place_limit/init.lua @@ -0,0 +1,49 @@ +-- Licensed under the MIT license, written by appgurueu. +local players = {} +local blocks_per_second = 5 +local resend_notification_seconds = 10 + +minetest.register_on_joinplayer(function(player) + -- player has to wait after join before they can place a node + players[player:get_player_name()] = { + last_notification_sent = -math.huge + } +end) + +minetest.register_on_leaveplayer(function(player) + players[player:get_player_name()] = nil +end) + +minetest.register_on_placenode(function(pos, _newnode, placer, oldnode, _itemstack, pointed_thing) + local name = placer:get_player_name() + if not ItemStack(minetest.get_node(pointed_thing.under).name):get_definition().pointable then + -- This should happen rarely + hud_event.new(name, { + name = "place_limit:unpointable", + color = "warning", + value = "Block not pointable (dug/replaced)!", + }) + minetest.set_node(pos, oldnode) + return true + end + local time = minetest.get_us_time() + local placements = players[name] + for i = #placements, 1, -1 do + if time - placements[i] > 1e6 then + placements[i] = nil + end + end + if #placements >= blocks_per_second then + if (time - placements.last_notification_sent) / 1e6 >= resend_notification_seconds then + hud_event.new(name, { + name = "place_limit:speed", + color = "warning", + value = "Placing too fast!", + }) + placements.last_notification_sent = time + end + minetest.set_node(pos, oldnode) + return true + end + table.insert(placements, 1, time) +end) diff --git a/mods/pvp/place_limit/mod.conf b/mods/pvp/place_limit/mod.conf new file mode 100644 index 0000000..e29d69c --- /dev/null +++ b/mods/pvp/place_limit/mod.conf @@ -0,0 +1,3 @@ +name = place_limit +description = Limits block placement +depends = hud_events diff --git a/mods/pvp/shooter b/mods/pvp/shooter index e856150..6939ab0 160000 --- a/mods/pvp/shooter +++ b/mods/pvp/shooter @@ -1 +1 @@ -Subproject commit e856150c13972825626bdadd2f80165a853408c1 +Subproject commit 6939ab0b6d22387fcede92d152cc3cd5c6a0d151 diff --git a/mods/pvp/sniper_rifles/init.lua b/mods/pvp/sniper_rifles/init.lua index a0ba73b..17b8182 100644 --- a/mods/pvp/sniper_rifles/init.lua +++ b/mods/pvp/sniper_rifles/init.lua @@ -163,8 +163,11 @@ function minetest.is_protected(pos, name, info, ...) 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, - "You can't shoot blocks within "..r.." nodes of a flag!") + hud_event.new(name, { + name = "sniper_rifles:hit_base", + color = "warning", + value = "You can't shoot blocks within " .. r .. " nodes of a flag!", + }) return true else return old_is_protected(pos, name, info, ...)