123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600 |
- -- mod: [bounty]
- -- description: adds a death certificate, to get a bounty rewards.
- -- notes: the idea is to get a death certificate, so you can get a reward from
- -- an automated machine.
- -- author: boxface
- -- license: MIT
- -- version: 1.0
- -- date: 2019-10-20
- --
- -- version: 1.1
- -- date: 2022-08-20
- --[[
- *DONE: add bounty mark item
- use item, show dialog, enter username, and place bounty on player
- if more than x players place bounty on player then the bounty becomes
- active and the player bones will drop an item proving the player was
- killed and someone took his bones. (captured bones)
- * do not give player head (bounty reward) to same player
- * show a chat message like: "Server: Someone put a price on <player>'s head."
- * may be interesting to add a list to the 'dead proof' of the items
- captured in the bones.
- * may be bounty price needs to expire at some point.
- * trusted players may place a higher bounty level.
- * new (just registered) players may not place bounty.
- ** Add reason for capture.
- * must not place bounty on yourself.
- ** TODO: if owner take items from his own bones, give no reward for trash bones.
- ** Require at least 2 players to activate bounty. (?)
- --]]
- if not minetest.global_exists("bounty") then
- bounty = {}
- bounty.wanted = {test = true}
- bounty.author = "boxface"
- bounty.version = 1.1
- bounty.date = "2022-08-20"
- bounty.modname = minetest.get_current_modname()
- bounty.modpath = minetest.get_modpath(bounty.modname)
- bounty.storage = minetest.get_mod_storage()
- -- load register file only the first time
- dofile(bounty.modpath .. "/register.lua")
- else
- if not bounty.author == "boxface" then
- local err = "[bounty] another mod registered my global variable."
- minetest.log("error", err)
- return false
- end
- end
- -- code
- bounty.reward = {}
- bounty.bones = {}
- function bounty.chat_send_player(pname, msg)
- local s = minetest.colorize("goldenrod", msg)
- minetest.chat_send_player(pname, s)
- end
- function bounty.command_list(pname, param)
- local msg = ""
- nwanted = 0
- for k, v in pairs(bounty.wanted) do
- local count = bounty.get_count(k)
- msg = "Wanted: %s, by %d players."
- msg = string.format(msg, k, count)
- bounty.chat_send_player(pname, msg)
- nwanted = nwanted + 1
- end
- if nwanted == 0 then
- bounty.chat_send_player(pname, "Bounty list is empty.")
- else
- bounty.chat_send_player(pname, "End of list.")
- end
- return true
- end
- function bounty.get_bounty_data(pos)
- local meta = minetest.get_meta(pos)
- local data = {}
- data.pos = {x = pos.x, y = pos.y, z = pos.z}
- data.owner = meta:get_string("owner")
- data.bounty = meta:get_string("bounty")
- data.date = meta:get_string("date")
- return data
- end
- function bounty.give_reward(data, player)
- assert(type(data)=="table")
- local pname = player:get_player_name()
- -- do not give bounty proof to same player
- if data.bounty == "" or data.bounty == pname then
- minetest.log("action", "[bounty] no bounty to the same player.")
- return false
- end
- -- check if player is wanted
- if (bounty.check_bounty(data.bounty) == 0) then
- -- no bounty on player, no reward
- -- avoid reward to any bones
- return false
- end
- -- check if bones removed and empty inventory
- local meta = minetest.get_meta(data.pos)
- local owner = meta:get_string("owner")
- local inv = meta:get_inventory("main")
- if owner ~= "" or not inv:is_empty("main") then
- -- inventory must be empty
- -- owner removed
- -- and bones node removed
- return false
- end
- -- create bounty reward stack
- local stack = ItemStack("bounty:reward")
- local smeta = stack:get_meta()
- local player_inv = player:get_inventory()
- smeta:set_string("bounty", data.bounty)
- smeta:set_string("date", data.date)
- smeta:set_string("capturedby", pname)
- smeta:set_string("tombstone", "")
- smeta:set_string("infotext", data.bounty .. "\n dead proof")
- smeta:set_string("description", data.bounty .. " dead proof")
- if player_inv:room_for_item("main", stack) then
- player_inv:add_item("main", stack)
- bounty.chat_send_player(pname, "[bounty] Reward added to your inventory")
- else
- minetest.add_item(data.pos, stack)
- bounty.chat_send_player(pname, "[bounty] Your inventory is full, reward dropped.")
- end
- return true
- end
- function bounty.mark_use(itemstack, user, pointed_thing)
- -- mark a player as wanted
- -- put price on a player's head
- local pname = user:get_player_name()
- local formspec = bounty.get_mark_formspec()
- minetest.show_formspec(pname, "bounty:mark", formspec)
- end
- function bounty.place_bounty(target, placer)
- -- set bounty on player
- -- record must contain:
- --
- -- playername: who set bounty.
- -- targetname: wanted player name.
- -- dateadded: date the bounty was placed.
- -- expiry time?
- local pname = placer:get_player_name()
- local has_bounty = bounty.wanted[target] ~= nil
- local result = false
- if not has_bounty then
- -- place first bounty
- local data = {}
- data.placers = {}
- data.placers[pname] = {
- date = os.date("%Y-%m-%d %H:%M")
- }
- bounty.wanted[target] = data
- result = true
- else
- -- check if already placed
- if bounty.wanted[target] and bounty.wanted[target].placers and
- not bounty.wanted[target].placers[pname] then
- -- place bounty if not paced on this target
- bounty.wanted[target].placers[pname] = {
- date = os.date("%Y-%m-%d %H:%H")
- }
- result = true
- end
- end
- if result then
- local msg = "You have placed bounty on <%s>."
- msg = string.format(msg, target)
- bounty.chat_send_player(pname, msg)
- end
- return result
- end
- function bounty.get_count(target)
- local count = 0
- if bounty.wanted[target] and bounty.wanted[target].placers then
- for k, v in pairs(bounty.wanted[target].placers) do
- count = count + 1
- end
- end
- return count
- end
- function bounty.remove_bounty(target, placer)
- local pname = placer:get_player_name()
- local result = false
- if bounty.wanted[target] then
- if bounty.wanted[target].placers and
- bounty.wanted[target].placers[pname] then
- bounty.wanted[target].placers[pname] = nil
- local msg = "You removed bounty from <%s>."
- msg = string.format(msg, target)
- bounty.chat_send_player(pname, msg)
- result = true
- -- if list is empty, clear record
- if bounty.get_count(target) == 0 then
- bounty.wanted[target] = nil
- local msg = "Bounty on %s has been cleared."
- msg = string.format(msg, target)
- bounty.chat_send_player(pname, msg)
- end
- end
- end
- return result
- end
- function bounty.check_bounty(target)
- local data = bounty.wanted[target]
- print("Dump " .. target .. ": " .. dump(data))
- local count = 0
- if data and data.placers then
- for k, v in pairs(data.placers) do
- count = count + 1
- end
- end
- print("Count: " .. count)
- return count
- end
- -- test
- function bounty.do_dieplayer(objref, reason)
- local deadplayer = objref:get_player_name()
- local wanted_count = bounty.check_bounty(deadplayer)
- local msg = "Player <%s> died, wanted level %d."
- msg = string.format(msg, deadplayer, wanted_count)
- msg = minetest.colorize("cyan", msg)
- minetest.chat_send_all(msg)
- if wanted_count > 0 then
- local msg = "Player <%s> is wanted by %d players."
- msg = string.format(msg, deadplayer, wanted_count)
- msg = minetest.colorize("cyan", msg)
- minetest.chat_send_all(msg)
- end
- if reason and reason.object and reason.object:is_player() then
- if wanted_count > 0 then
- local pname = reason.object:get_player_name()
- local msg = "Server: Player <%s> has a price on his head."
- msg = string.format(msg, deadplayer)
- msg = minetest.colorize("darkorange", msg)
- minetest.chat_send_player(pname, msg)
- end
- end
- end
- function bounty.get_mark_formspec()
- local formspec = {
- "formspec_version[2]",
- "size[8,4]",
- "box[0.25,0.25;7.5,1;#80C0C0]",
- "item_image[0.4,0.4;0.8,0.8;bounty:mark]",
- "label[1.5,0.5;Bounty marker]",
- "label[1.5,1.0;Put a price on a player's head.]",
- -- "label[1.5,1.5;Get their bones and get a proof.]",
- "field[0.5,2;7,0.8;bountytarget;Player name:;]",
- "field_close_on_enter[bountytarget;false]",
- "style[place;bgcolor=#E00000]",
- "style[remove;bgcolor=#00E000]",
- "tooltip[place;Place bounty.]",
- "tooltip[remove;Remove bounty.]",
- "button_exit[0.5,3;1.75,0.8;place;Place]",
- "button_exit[2.5,3;1.75,0.8;remove;Remove]",
- "button_exit[4.5,3;1.75,0.8;cancel;Cancel]",
- -- debug
- --"style[dump;bgcolor=#0000F0]",
- --"button_exit[6.5,3;1,0.8;dump;Test]"
- }
- local str = table.concat(formspec)
- -- str = string.gsub(str, "${variable}", variable)
- return str
- end
- function bounty.get_tombstone_formspec(data, readonly)
- if data == nil then
- data = {}
- end
- local bountyname = data.bounty or "unknown"
- local capturedby = data.capturedby or "unknown"
- local captureddate = data.date or ""
- local tombstone = data.tombstone or ""
- local formspec = {
- "formspec_version[2]",
- "size[6,5]",
- "box[0.25,0.25;5.5,1;#80C0C0]",
- "item_image[0.4,0.4;0.8,0.8;bounty:reward]",
- "label[1.5,0.5;Captured bones]",
- "label[1.5,1.0;${bountyname}]",
- "label[0.25,1.5;Captured by: ${capturedby}]",
- "label[0.25,2.0;Date: ${date}]",
- "field[0.25,3.0;5.5,0.8;tombstone;Tombstone text;${tombstone}]",
- "button_exit[1.5,4;3,0.8;write;Write]"
- }
-
- if readonly then
- formspec = {
- "formspec_version[2]",
- "size[6,5]",
- "box[0.25,0.25;5.5,1;#80C0C0]",
- "item_image[0.4,0.4;0.8,0.8;bounty:reward]",
- "label[1.5,0.5;Captured bones]",
- "label[1.5,1.0;${bountyname}]",
- "label[0.25,1.5;Captured by: ${capturedby}]",
- "label[0.25,2.0;Date: ${date}]",
- "label[0.25,3.0;${tombstone}]",
- "button_exit[1.5,4;3,0.8;close;Close]"
- }
- end
- str = table.concat(formspec)
- str = string.gsub(str, "${bountyname}", bountyname)
- str = string.gsub(str, "${date}", captureddate)
- str = string.gsub(str, "${capturedby}", capturedby)
- str = string.gsub(str, "${tombstone}", tombstone)
- return str
- end
- function bounty.reward.do_after_place_node(pos, placer, itemstack, pointed_thing)
- -- bounty reward
- local meta = minetest.get_meta(pos)
- local imeta = itemstack:get_meta()
- meta:set_string("bounty", imeta:get_string("bounty"))
- meta:set_string("date", imeta:get_string("date"))
- meta:set_string("capturedby", imeta:get_string("capturedby"))
- meta:set_string("tombstone", imeta:get_string("tombstone"))
- meta:set_string("infotext", imeta:get_string("bounty") ..
- "\nDate: " .. imeta:get_string("date") ..
- "\nCaptured by: " .. imeta:get_string("capturedby") ..
- "\n" .. imeta:get_string("tombstone"))
- meta:set_string("description", imeta:get_string("description"))
- return false
- end
- function bounty.reward.do_preserve_metadata(pos, oldnode, oldmeta, drops)
- -- bounty reward
- -- debug
- --minetest.chat_send_all(
- -- minetest.colorize("darkgrey", "Debug: do_preserve_metadata"))
- print("preserve_metadata:")
- print(dump(drops))
- print("oldmeta:")
- print(dump(oldmeta))
- stack = drops[1]
- smeta = stack:get_meta()
- smeta:set_string("bounty", oldmeta["bounty"])
- smeta:set_string("date", oldmeta["date"])
- smeta:set_string("capturedby", oldmeta["capturedby"])
- smeta:set_string("tombstone", oldmeta["tombstone"])
- smeta:set_string("infotext", oldmeta["infotext"])
- smeta:set_string("description", oldmeta["description"])
- end
- function bounty.reward.do_use(itemstack, user, pointed_thing)
- -- bounty reward
- local pname = user:get_player_name()
- local imeta = itemstack:get_meta()
- --local bountyname = imeta:get_string("bounty")
- --local tombstone = imeta:get_string("tombstone")
- -- minetest.chat_send_player(pname, "USING THIS THING!")
- local data = {
- bounty = imeta:get_string("bounty"),
- tombstone = imeta:get_string("tombstone"),
- date = imeta:get_string("date"),
- capturedby = imeta:get_string("capturedby")
- }
- local formspec = bounty.get_tombstone_formspec(data)
- minetest.show_formspec(pname, "bounty:tombstone", formspec)
- -- minetest.show_formspec(pname, "bounty:tombstone",
- -- bounty.get_tombstone_formspec(bountyname, tombstone))
- end
- function bounty.reward.do_rightclick(pos, node, clicker, pointed_thing)
- local pname = clicker:get_player_name()
- local meta = minetest.get_meta(pos)
- local data = {
- bounty = meta:get_string("bounty"),
- tombstone = meta:get_string("tombstone"),
- date = meta:get_string("date"),
- capturedby = meta:get_string("capturedby")
- }
- local formspec = bounty.get_tombstone_formspec(data, true)
- minetest.show_formspec(pname, "bounty:tombstonerw", formspec)
- end
- function bounty.do_player_receive_fields(player, formname, fields)
- if formname == "bounty:mark" then
- -- debug
- --local dbg = "formspec[bounty:mark]\nFields: " .. dump(fields)
- --dbg = minetest.colorize("magenta", dbg)
- --minetest.chat_send_player(player:get_player_name(), dbg)
- --
- local bountylevel = 0
- if fields.key_enter_field == "bountytarget" then
- if fields.bountytarget ~= "" then
- -- bounty.check_bounty(fields.bountytarget)
- end
- end
- -- check buttons
- if fields.dump then
- minetest.chat_send_player(player:get_player_name(),
- "Wanted list: " .. dump(bounty.wanted))
- elseif fields.place then
- local pname = player:get_player_name()
- local target = fields.bountytarget
-
- -- player exists?
- --if bountytarget and minetest.builtin_auth_handler.get_auth(bountytarget) ~= nil then
- -- minetest.chat_send_player(pname, "Player found!")
- --else
- -- minetest.chat_send_player(pname, "That player is not found.")
- --end
-
- if target ~= "" then
- -- place bounty
- if bounty.place_bounty(target, player) then
- -- use bounty tag
- wield = player:get_wielded_item()
- if wield:get_name() == "bounty:mark" then
- wield:take_item()
- player:set_wielded_item(wield)
- end
- end
- -- bounty.storage.
- end
- elseif fields.remove then
- local pname = player:get_player_name()
- local target = fields.bountytarget
- if target ~= "" then
- if bounty.remove_bounty(target, player) then
- -- DONE: use bounty tag
- wield = player:get_wielded_item()
- if wield:get_name() == "bounty:mark" then
- wield:take_item()
- player:set_wielded_item(wield)
- end
- end
- end
- end
- elseif formname == "bounty:tombstone" then
- if fields.tombstone then
- -- update tombstone text
- local pname = player:get_player_name()
- local wielditem = player:get_wielded_item()
- if not wielditem then
- return
- end
- -- debug
- print("Setting wielded item " .. fields.tombstone)
- local wieldmeta = wielditem:get_meta()
- wieldmeta:set_string("tombstone", fields.tombstone)
- player:set_wielded_item(wielditem)
- end
- end
- end
- function bounty.do_shutdown()
- bounty.store_save()
- end
- function bounty.store_load()
- bounty.wanted = minetest.deserialize(bounty.storage:get_string("wanted")) or {}
- end
- function bounty.store_save()
- bounty.storage:set_string("wanted", minetest.serialize(bounty.wanted))
- end
- -- ========== BONES OVERRIDE FUNCTIONS ==========
- function bounty.override_bones()
- local bones = minetest.registered_nodes["bones:bones"]
- bounty.oldbones_metadata_inventory_take = bones.on_metadata_inventory_take
- bones.on_metadata_inventory_take = function(...)
- return bounty.bones.do_metadata_inventory_take(...)
- end
- bounty.oldbones_on_punch = bones.on_punch
- bones.on_punch = function(...)
- return bounty.bones.do_punch(...)
- end
- bounty.oldbones_on_timer = bones.on_timer
- bones.on_timer = function(...)
- return bounty.bones.do_timer(...)
- end
- end
- function bounty.bones.do_metadata_inventory_take(pos, listname, index, stack, player)
- -- get bounty data
- local bountydata = bounty.get_bounty_data(pos)
- -- original bones callback
- local result = bounty.oldbones_metadata_inventory_take(pos, listname, index, stack, player)
- -- check if node was removed
- -- give bounty reward if applicable
- bounty.give_reward(bountydata, player)
- return result
- end
- function bounty.bones.do_punch(pos, node, player)
- -- get bounty data
- local bountydata = bounty.get_bounty_data(pos)
- -- original callback
- local result = bounty.oldbones_on_punch(pos, node, player)
- -- check if node was removed
- -- give bounty reward if applicable
- bounty.give_reward(bountydata, player)
- return result
- end
- function bounty.bones.do_timer(pos, elapsed)
- local meta = minetest.get_meta(pos)
-
- local owner = meta:get_string("owner")
- if owner ~= "" and bounty.get_count(owner)>0 then
- -- add "bounty" name to bones
- meta:set_string("bounty", meta:get_string("owner"))
- -- add "deathdate" to bones
- local deathdate = os.date("%Y-%m-%d %H:%M")
- meta:set_string("date", deathdate)
- end
- -- bones callback function
- local result = bounty.oldbones_on_timer(pos, elapsed)
-
- -- debug
- --local time = meta:get_int("time") + elapsed
- --if result then
- -- minetest.chat_send_all(minetest.colorize("orange",
- -- "Server: player bones still warm. (time: ".. time .. ")."))
- --else
- -- minetest.chat_send_all(minetest.colorize("cyan",
- -- "Server: player bones got cold!"))
- --end
- return result
- end
- -- ---------- END BONES OVERRIDE FUNCTIONS ----------
- -- initialization
- if not bounty.runonce then
- -- debug
- local share_bones_time = tonumber(minetest.settings:get("share_bones_time")) or 1200
- print("Debug: Share bones time: " .. share_bones_time)
- minetest.register_chatcommand("bounty", {
- params = "",
- description = "Shows bounty list.",
- privs = {},
- func = function(...)
- return bounty.command_list(...)
- end
- })
- minetest.register_on_dieplayer(function(...)
- bounty.do_dieplayer(...)
- end)
- minetest.register_on_player_receive_fields(function(...)
- bounty.do_player_receive_fields(...)
- end)
- minetest.register_on_shutdown(function(...)
- return bounty.do_shutdown(...)
- end)
- bounty.override_bones()
- bounty.store_load()
- if minetest.get_modpath("reload") then
- reload.register_file("bounty:init", bounty.modpath .. "/init.lua", false)
- end
- bounty.runonce = true
- end
- --<eof>
|