diff --git a/mods/ctf/ctf_map/maps b/mods/ctf/ctf_map/maps index e831d074cf..3dad69c045 160000 --- a/mods/ctf/ctf_map/maps +++ b/mods/ctf/ctf_map/maps @@ -1 +1 @@ -Subproject commit e831d074cfd388d06f961227706c00023e8ba2ff +Subproject commit 3dad69c045fdadf59e38a09d8217f78cc7790118 diff --git a/mods/ctf/ctf_modebase/bounties.lua b/mods/ctf/ctf_modebase/bounties.lua index 1ebae4f4ec..1b7b6e4c79 100644 --- a/mods/ctf/ctf_modebase/bounties.lua +++ b/mods/ctf/ctf_modebase/bounties.lua @@ -3,6 +3,37 @@ local timer = nil local bounties = {} ctf_modebase.bounties = {} +-- ^ This is for game's own bounties +ctf_modebase.contributed_bounties = { +--[[ + ["player_name"] = { + total = score, + contributors = {["player1"] = amount, ["player2"] = amount, ...} + }, a total bounty of `total` score is on `player_name` player contributed + by those in the list(`player1`, `player2`, ...) + ... +--]] +} +-- ^ This is for player contributed bounties + +local function get_contributors(name) + local bounty = ctf_modebase.contributed_bounties[name] + if not bounty then + return "" + else + local list = "" + local first = true + for contributor, score in pairs(bounty.contributors) do + if first then + list = list .. contributor + first = false + else + list = "," .. list .. contributor + end + end + return list + end +end local function get_reward_str(rewards) local ret = "" @@ -21,7 +52,7 @@ local function set(pname, pteam, rewards) -- -- bounty_kills(int) which is usually 1 -- -- score(int) which is the amount of score given to the one -- -- -- who claims the bounty - local bounty_message = minetest.colorize(CHAT_COLOR, string.format( + local bounty_message = core.colorize(CHAT_COLOR, string.format( "[Bounty] %s. Rewards: %s", pname, get_reward_str(rewards) )) @@ -36,23 +67,52 @@ local function set(pname, pteam, rewards) end local function remove(pname, pteam) - minetest.chat_send_all(minetest.colorize(CHAT_COLOR, string.format("[Bounty] %s is no longer bountied", pname))) + core.chat_send_all(core.colorize(CHAT_COLOR, string.format("[Bounty] %s is no longer bountied", pname))) bounties[pteam] = nil end function ctf_modebase.bounties.claim(player, killer) local pteam = ctf_teams.get(player) - - if not (pteam and bounties[pteam] and bounties[pteam].name == player) then + local is_bounty = not (pteam and bounties[pteam] and bounties[pteam].name == player) + if is_bounty and not ctf_modebase.contributed_bounties[player] then + -- checking if there is bounty on this player return end local rewards = bounties[pteam].rewards local bounty_kill_text = string.format("[Bounty] %s killed %s and got %s", killer, player, get_reward_str(rewards)) - minetest.chat_send_all(minetest.colorize(CHAT_COLOR, bounty_kill_text)) + core.chat_send_all(core.colorize(CHAT_COLOR, bounty_kill_text)) ctf_modebase.announce(bounty_kill_text) + local rewards = { bounty_kills = 0, score = 0 } + if bounties[pteam] and bounties[pteam].rewards then + rewards = bounties[pteam].rewards + local bounty_kill_text = + string.format( + "[Bounty] %s killed %s and got %s from the game!", + killer, + player, + get_reward_str(rewards) + ) + core.chat_send_all(core.colorize(CHAT_COLOR, bounty_kill_text)) + ctf_modebase.announce(bounty_kill_text) - bounties[pteam] = nil + bounties[pteam] = nil + end + if ctf_modebase.contributed_bounties[player] then + local score = ctf_modebase.contributed_bounties[player].total + rewards.score = rewards.score + score + local bounty_kill_text = + string.format( + "[Player bounty] %s killed %s and got %d from %s!", + killer, + player, + score, + get_contributors(player) + ) + core.chat_send_all(core.colorize(CHAT_COLOR, bounty_kill_text)) + ctf_modebase.announce(bounty_kill_text) + ctf_modebase.contributed_bounties[player] = nil + end return rewards end @@ -94,7 +154,7 @@ function ctf_modebase.bounties.reassign() end function ctf_modebase.bounties.reassign_timer() - timer = minetest.after(math.random(180, 360), function() + timer = core.after(math.random(180, 360), function() ctf_modebase.bounties.reassign() ctf_modebase.bounties.reassign_timer() end) @@ -134,7 +194,7 @@ ctf_teams.register_on_allocplayer(function(player, new_team, old_team) end if #output > 0 then - minetest.chat_send_player(pname, table.concat(output, "\n")) + core.chat_send_player(pname, table.concat(output, "\n")) end end) @@ -145,14 +205,14 @@ ctf_core.register_chatcommand_alias("list_bounties", "lb", { local output = {} local x = 0 for tname, bounty in pairs(bounties) do - local player = minetest.get_player_by_name(bounty.name) + local player = core.get_player_by_name(bounty.name) if player and pteam ~= tname then local label = string.format( "label[%d,0.1;%s: %s score]", x, bounty.name, - minetest.colorize("cyan", bounty.rewards.score) + core.colorize("cyan", bounty.rewards.score) ) table.insert(output, label) @@ -166,19 +226,40 @@ ctf_core.register_chatcommand_alias("list_bounties", "lb", { x = x + 4.5 end end + for pname, bounty in pairs(ctf_modebase.contributed_bounties) do + local player = core.get_player_by_name(pname) + if player then + local label = string.format( + "label[%d,0.1;%s: %s score]", + x, + pname, + core.colorize("cyan", bounty.total) + ) + table.insert(output, label) + + local model = "model[%d,1;4,6;player;character.b3d;%s;{0,160};;;]" + model = string.format( + model, + x, + player:get_properties().textures[1] + ) + table.insert(output, model) + x = x + 4.5 + end + end if #output <= 0 then return false, "There are no bounties you can claim" end x = x - 1.5 local formspec = "size[" .. x .. ",6]\n" .. table.concat(output, "\n") - minetest.show_formspec(name, "ctf_modebase:lb", formspec) + core.show_formspec(name, "ctf_modebase:lb", formspec) return true, "" - end + end, }) -ctf_core.register_chatcommand_alias("put_bounty", "pb", { - description = "Put bounty on some player", +ctf_core.register_chatcommand_alias("place_bounty", "pb", { + description = "Place bounty on some player", params = " ", privs = { ctf_admin = true }, func = function(name, param) @@ -202,9 +283,86 @@ ctf_core.register_chatcommand_alias("put_bounty", "pb", { pteam, { bounty_kills=1, score=amount } ) - return true, "Successfully placed a bounty of " .. amount .. " on " .. minetest.colorize(team_colour, player) .. "!" + return true, "Successfully placed a bounty of " .. amount .. " on " .. core.colorize(team_colour, player) .. "!" else return false, "Invalid Amount" end end, }) + + + +ctf_core.register_chatcommand_alias("bounty", "b", { + description = "Place a bounty on someone using your match score.\n" .. + "The score is returned to you if the match ends and nobody kills.\n" .. + "Use negative score to revoke a bounty", + params = " ", + func = function(name, params) + local bname, amount = string.match(params, "([^%s]*) ([^%s]*)") + if not (amount and bname) then + return false, "Missing argument(s)" + end + amount = math.floor(amount) + local bteam = ctf_teams.get(bname) + if not bteam then + return false, "This player isn't online or isn't in a team" + end + if bteam == ctf_teams.get(name) then + return false, "You cannot place a bounty on your teammate!" + end + local current_mode = ctf_modebase:get_current_mode() + if amount <= 0 then + local contributors = ctf_modebase.contribued_bounties[bname] + local contributed_amount = contributors[bname] or 0 + if math.abs(amount) > contributed_amount then + contributed_amount = math.abs(amount) + end + ctf_modebase.contributed_bounties[bname][name] = ctf_modebase.contributed_bounties[bname][name] - contributed_amount + current_mode.recent_rankings.add(name, { score = contributed_amount }, true) + return true, tostring(contributed_amount) .. " points returned to you." + end + + if amount < 5 then + return false, "Your bounty needs to be of at least 5 points" + end + if amount > 100 then + return false, "Your bounty cannot be of more than 100 points" + end + + if not current_mode or not ctf_modebase.match_started then + return false, "Match has not started yet." + end + local cur_score = current_mode.recent_rankings.get(name).score or 0 + if amount > cur_score then + return false, "You haven't got enough points" + end + current_mode.recent_rankings.add(name, {score=-amount}, true) + if not ctf_modebase.contributed_bounties[bname] then + local contributors = {} + contributors[name] = amount + ctf_modebase.contributed_bounties[bname] = { total = amount, contributors = contributors } + else + if not ctf_modebase.contributed_bounties[bname].contributors[name] then + ctf_modebase.contributed_bounties[bname].contributors[name] = amount + else + ctf_modebase.contributed_bounties[bname].contributors[name] = + ctf_modebase.contributed_bounties[bname].contributors[name] + amount + end + ctf_modebase.contributed_bounties[bname].total = ctf_modebase.contributed_bounties[bname].total + amount + end + local total = ctf_modebase.contributed_bounties[bname].total + core.chat_send_all( + core.colorize( + CHAT_COLOR, + string.format("%s placed %d bounty on %s!", get_contributors(bname), total, bname))) + end, +}) + +ctf_api.register_on_match_end(function() + local current_mode = ctf_modebase:get_current_mode() + for pname, bounty in pairs(ctf_modebase.contributed_bounties) do + for contributor, amount in pairs(bounty.contributors) do + current_mode.recent_rankings.add(contributor, {score=amount}, true) + end + end +end) diff --git a/mods/ctf/ctf_modebase/recent_rankings.lua b/mods/ctf/ctf_modebase/recent_rankings.lua index 42ea7fb07c..63da948fd0 100644 --- a/mods/ctf/ctf_modebase/recent_rankings.lua +++ b/mods/ctf/ctf_modebase/recent_rankings.lua @@ -74,6 +74,12 @@ return { on_match_end = function() rankings_players = {} rankings_teams = {} + local current_mode = ctf_modebase:get_current_mode() + for _, bounties in pairs(ctf_modebase.contributed_bounties) do + for bounty_donator, bounty_amount in pairs(bounties["contributors"]) do + current_mode.recent_rankings.add(bounty_donator, {score=bounty_amount}, true) + end + end end, players = function() return rankings_players end, teams = function() diff --git a/mods/ctf/ctf_rankings/default.lua b/mods/ctf/ctf_rankings/default.lua index e4ebb6eed0..bacd7e7f06 100644 --- a/mods/ctf/ctf_rankings/default.lua +++ b/mods/ctf/ctf_rankings/default.lua @@ -1,57 +1,57 @@ return function(top) - local modstorage = assert(core.get_mod_storage(), "Can only init rankings at runtime!") +local modstorage = assert(core.get_mod_storage(), "Can only init rankings at runtime!") - for k, v in pairs(modstorage:to_table()["fields"]) do - local rank = core.parse_json(v) - if rank ~= nil and rank.score then - top:set(k, rank.score) - end +for k, v in pairs(modstorage:to_table()["fields"]) do + local rank = core.parse_json(v) + if rank ~= nil and rank.score then + top:set(k, rank.score) end +end - return { - backend = "default", - top = top, - modstorage = modstorage, - - get = function(self, pname) - pname = PlayerName(pname) +return { + backend = "default", + top = top, + modstorage = modstorage, - local rank_str = self.modstorage:get_string(pname) + get = function(self, pname) + pname = PlayerName(pname) - if not rank_str or rank_str == "" then - return false - end + local rank_str = self.modstorage:get_string(pname) - return core.parse_json(rank_str) - end, - set = function(self, pname, newrankings, erase_unset) - pname = PlayerName(pname) + if not rank_str or rank_str == "" then + return false + end - if not erase_unset then - local rank = self:get(pname) - if rank then - for k, v in pairs(newrankings) do - rank[k] = v - end + return core.parse_json(rank_str) + end, + set = function(self, pname, newrankings, erase_unset) + pname = PlayerName(pname) - newrankings = rank + if not erase_unset then + local rank = self:get(pname) + if rank then + for k, v in pairs(newrankings) do + rank[k] = v end - end - self.top:set(pname, newrankings.score or 0) - self.modstorage:set_string(pname, core.write_json(newrankings)) - end, - add = function(self, pname, amounts) - pname = PlayerName(pname) + newrankings = rank + end + end - local newrankings = self:get(pname) or {} + self.top:set(pname, newrankings.score or 0) + self.modstorage:set_string(pname, core.write_json(newrankings)) + end, + add = function(self, pname, amounts) + pname = PlayerName(pname) - for k, v in pairs(amounts) do - newrankings[k] = (newrankings[k] or 0) + v - end + local newrankings = self:get(pname) or {} - self.top:set(pname, newrankings.score or 0) - self.modstorage:set_string(pname, core.write_json(newrankings)) + for k, v in pairs(amounts) do + newrankings[k] = (newrankings[k] or 0) + v end - } + + self.top:set(pname, newrankings.score or 0) + self.modstorage:set_string(pname, core.write_json(newrankings)) + end +} end