324 lines
9.8 KiB
Lua
324 lines
9.8 KiB
Lua
--Variables
|
|
local modname = minetest.get_current_modname()
|
|
local S = minetest.get_translator(modname)
|
|
|
|
--Settings
|
|
pickp = {}
|
|
local modpath = minetest.get_modpath(modname)
|
|
assert(loadfile(modpath.. "/settings.lua"))(modpath)
|
|
|
|
local _contexts = {}
|
|
|
|
--Aunction for copy a table in deep (children too)
|
|
local function deepCopy(original)
|
|
local copy = {}
|
|
for k, v in pairs(original) do
|
|
if type(v) == "table" then
|
|
v = deepCopy(v)
|
|
end
|
|
copy[k] = v
|
|
end
|
|
return copy
|
|
end
|
|
|
|
function pickp.make_sound(dest_type, dest, soundfile, max_hear_distance)
|
|
if dest_type == "object" then
|
|
minetest.sound_play(soundfile, {object = dest, gain = 0.5, max_hear_distance = max_hear_distance or 10,})
|
|
elseif dest_type == "player" then
|
|
local player_name = dest:get_player_name()
|
|
minetest.sound_play(soundfile, {to_player = player_name, gain = 0.5, max_hear_distance = max_hear_distance or 10,})
|
|
elseif dest_type == "pos" then
|
|
minetest.sound_play(soundfile, {pos = dest, gain = 0.5, max_hear_distance = 10 or max_hear_distance,})
|
|
end
|
|
end
|
|
|
|
local function enable_steal(clicker, clicked, rob_table, angle_2d)
|
|
local steal_table = {}
|
|
steal_table["clicked"] = clicked:get_player_name()
|
|
steal_table["angle_2d"] = angle_2d
|
|
steal_table["items"] = deepCopy(rob_table)
|
|
_contexts[clicker:get_player_name()] = steal_table
|
|
end
|
|
|
|
local function disable_stealth(player, close_form, msg)
|
|
local player_name = player:get_player_name()
|
|
_contexts[player_name] = nil
|
|
if msg then
|
|
minetest.chat_send_player(player_name, msg)
|
|
end
|
|
if close_form then
|
|
minetest.close_formspec(player_name, "pickp:form")
|
|
end
|
|
end
|
|
|
|
local function is_stealing(player) --it returns the rob table of items too
|
|
if _contexts[player:get_player_name()] then
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
local function get_steal_table(player)
|
|
return _contexts[player:get_player_name()] or nil
|
|
end
|
|
|
|
minetest.register_on_leaveplayer(function(player)
|
|
disable_stealth(player, false, false)
|
|
end)
|
|
|
|
minetest.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool_capabilities, dir, damage)
|
|
if is_stealing(player) then
|
|
disable_stealth(player, true, S("Aborted robbery!"))
|
|
end
|
|
end)
|
|
|
|
local function get_rob_list(clicked)
|
|
local inv = clicked:get_inventory()
|
|
local list_inv = inv:get_list("main")
|
|
local list_hotbar = {}
|
|
for i=1,pickp.settings["hotbar_size"],1 do
|
|
if list_inv[i]:get_count() > 0 then
|
|
if math.random(0,1) < pickp.settings["hotbar_ratio"] then
|
|
list_hotbar[#list_hotbar+1] = {itemstack = list_inv[i], type = "hotbar"}
|
|
end
|
|
end
|
|
end
|
|
local list_main = {}
|
|
for i=pickp.settings["hotbar_size"]+1,#list_inv,1 do
|
|
if list_inv[i]:get_count() > 0 then
|
|
if math.random(0,1) < pickp.settings["main_ratio"] then
|
|
list_main[#list_main+1] = {itemstack = list_inv[i], type = "main"}
|
|
end
|
|
end
|
|
end
|
|
local rob_list = ""
|
|
if #list_hotbar == 0 and #list_main == 0 then
|
|
return rob_list, nil
|
|
end
|
|
--minetest.chat_send_all("hotbar_before="..tostring(#list_hotbar))
|
|
--minetest.chat_send_all("main="..tostring(#list_main))
|
|
--merge the tables
|
|
if #list_main > 0 then
|
|
for key,value in pairs(list_main) do
|
|
table.insert(list_hotbar, value)
|
|
end
|
|
end
|
|
--minetest.chat_send_all("hotbar_after="..tostring(#list_hotbar))
|
|
local i = 1
|
|
local item_stack, item_name, columns
|
|
for y= 1, 4 do
|
|
if i > #list_hotbar then
|
|
break
|
|
end
|
|
columns = y
|
|
for x= 0,3 do
|
|
if list_hotbar[i] then
|
|
item_stack = list_hotbar[i].itemstack
|
|
item_name = item_stack:get_name()
|
|
rob_list = rob_list .. " item_image_button [".. tostring(x)..".3"..",".. tostring(y)..".6"..";1,1;"
|
|
.. item_name .. ";item_name;"..tostring(i).."]"
|
|
i = i + 1
|
|
else
|
|
i = i + 1
|
|
end
|
|
if i > #list_hotbar then
|
|
break
|
|
end
|
|
end
|
|
end
|
|
--minetest.chat_send_all("rob_list="..rob_list)
|
|
return rob_list, list_hotbar, columns
|
|
end
|
|
|
|
local function get_formspec(clicker, clicked, rob_list,columns)
|
|
columns = columns + 1
|
|
local formspec =
|
|
"formspec_version[4]" ..
|
|
"size[4.6,"..tostring(columns)..".9]" ..
|
|
"image[0.30,0.30;1,1;pickp_thief_face.png]" ..
|
|
"label[1.5,0.65;"..S("Pick up an item").."]" ..
|
|
"label[1.5,1;"..S("quickly!").."]" ..
|
|
rob_list
|
|
return formspec
|
|
end
|
|
|
|
local function get_angle(clicker, clicked)
|
|
local clicker_look_view = clicker:get_look_dir()
|
|
local x1 = clicker_look_view.x
|
|
local z1 = clicker_look_view.z
|
|
local clicked_look_view = clicked:get_look_dir()
|
|
local x2 = clicked_look_view.x
|
|
local z2 = clicked_look_view.z
|
|
return math.deg(math.acos((x1 * x2 + z1 * z2) / (math.sqrt(x1^2 + z1^2) * math.sqrt(x2^2 + z2^2))))
|
|
end
|
|
|
|
local function get_stealth_ratio(angle_2d)
|
|
local stealth_ratio
|
|
if angle_2d <= pickp.settings["zone_1_2_limit"] then
|
|
stealth_ratio = pickp.settings["zone1_stealth_ratio"]
|
|
elseif angle_2d > pickp.settings["zone_1_2_limit"] and angle_2d < pickp.settings["zone_2_3_limit"] then
|
|
stealth_ratio = pickp.settings["zone2_stealth_ratio"]
|
|
else
|
|
stealth_ratio = pickp.settings["zone3_stealth_ratio"]
|
|
end
|
|
return stealth_ratio
|
|
end
|
|
|
|
local function detection_warning(clicker, clicked, alarm_type)
|
|
local clicker_name = clicker:get_player_name()
|
|
local clicked_name = clicked:get_player_name()
|
|
local msg
|
|
if math.random(0,1) <= pickp.settings["warning_failed_thief_ratio"] then
|
|
if alarm_type == "try" then
|
|
msg = S("Someone has tried to steal from you!")
|
|
elseif alarm_type == "failed" then
|
|
msg = S("Someone has stolen from you!")
|
|
else
|
|
return
|
|
end
|
|
else
|
|
msg = clicker_name .. " "
|
|
if alarm_type == "try" then
|
|
msg = msg .. S("tried to steal from you!")
|
|
elseif alarm_type == "failed" then
|
|
msg = msg .. S("has stolen from you!")
|
|
else
|
|
return
|
|
end
|
|
end
|
|
minetest.chat_send_player(clicked_name, msg)
|
|
if pickp.settings["sound_alarm"] then
|
|
pickp.make_sound("player", clicked, "pickp_alarm", 10)
|
|
end
|
|
end
|
|
|
|
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
|
if formname ~= "pickp:form" then
|
|
return
|
|
end
|
|
if not player then
|
|
return
|
|
end
|
|
if not(fields.item_name) then
|
|
return
|
|
end
|
|
local steal_table = get_steal_table(player)
|
|
local steal_itemstack = steal_table.items[tonumber(fields.item_name)].itemstack
|
|
local clicked = minetest.get_player_by_name(steal_table.clicked)
|
|
local inv_clicker = player:get_inventory()
|
|
local inv_clicked = clicked:get_inventory()
|
|
--Search the item to rob in the clicked player
|
|
if inv_clicked:contains_item("main", steal_itemstack) then
|
|
|
|
local steal_itemstack_name = steal_itemstack:get_name()
|
|
local steal_amount = math.random(1, steal_itemstack:get_count())
|
|
local final_steal_itemstack = ItemStack(steal_itemstack_name .. " " .. tostring(steal_amount))
|
|
|
|
inv_clicked:remove_item("main", final_steal_itemstack)
|
|
|
|
if inv_clicker:room_for_item("main", final_steal_itemstack) then
|
|
inv_clicker:add_item("main", final_steal_itemstack)
|
|
else
|
|
local clicker_pos = player:get_pos()
|
|
minetest.item_drop(final_steal_itemstack, player, clicker_pos)
|
|
end
|
|
|
|
disable_stealth(player, true, false)
|
|
|
|
--DETECTION WARNING
|
|
|
|
--Check the angle/ratio
|
|
local angle_2d = get_angle(player, clicked)
|
|
local stealth_ratio = get_stealth_ratio(angle_2d)
|
|
local type_item_reduction_factor
|
|
if steal_itemstack.type == "main" then
|
|
type_item_reduction_factor = pickp.settings["warning_main_item"]
|
|
else
|
|
type_item_reduction_factor = pickp.settings["warning_hotbar_item"]
|
|
end
|
|
stealth_ratio = stealth_ratio + type_item_reduction_factor
|
|
|
|
if math.random(0,1) >= stealth_ratio then
|
|
--NOT detected
|
|
minetest.chat_send_player(player:get_player_name(), S("Successful robbery!"))
|
|
else
|
|
--DETECTION WARNING
|
|
detection_warning(player, clicked, "failed")
|
|
end
|
|
else
|
|
disable_stealth(player, true, "The player has moved his items!")
|
|
end
|
|
return true
|
|
end)
|
|
|
|
local function check_distance(clicker, clicked)
|
|
local pos1 = clicker:get_pos()
|
|
local pos2 = clicked:get_pos()
|
|
local distance = vector.distance(pos1, pos2)
|
|
if distance > pickp.settings["stealing_distance"] then
|
|
return false
|
|
else
|
|
return true
|
|
end
|
|
end
|
|
|
|
local function stealth(clicker, clicked)
|
|
local clicker_name = clicker:get_player_name()
|
|
local clicked_name = clicked:get_player_name()
|
|
if not(clicker_name) or not(clicked_name) or not(is_stealing(clicker)) then
|
|
return
|
|
end
|
|
--1) CHECK THE DISTANCE
|
|
if not(check_distance(clicker, clicked)) then
|
|
disable_stealth(clicker, true, "The victim has walked away!")
|
|
return
|
|
end
|
|
--2) CHECK THE ANGLE
|
|
local angle_2d = get_angle(clicker, clicked)
|
|
--minetest.chat_send_all(tostring(angle_2d))
|
|
local stealth_ratio = get_stealth_ratio(angle_2d)
|
|
--Detect the zone & get the ratio accordingly-->
|
|
if math.random(0,1) >= stealth_ratio then --NOT detected
|
|
minetest.after(pickp.settings["stealth_timing"], stealth, clicker, clicked)
|
|
return
|
|
end
|
|
--DETECTED!
|
|
disable_stealth(clicker, true, S("Failed robbery!"))
|
|
if pickp.settings["sound_fail"] then
|
|
pickp.make_sound("player", clicker, "pickp_fail", 10)
|
|
end
|
|
--DETECTION WARNING
|
|
detection_warning(clicker, clicked, "try")
|
|
end
|
|
|
|
local function pickpocketing(clicker, clicked)
|
|
local clicker_name = clicker:get_player_name()
|
|
local rob_list, rob_table, columns = get_rob_list(clicked)
|
|
if not(rob_list == "") then
|
|
minetest.show_formspec(clicker_name,
|
|
"pickp:form", get_formspec(clicker, clicked, rob_list, columns))
|
|
local angle_2d = get_angle(clicker, clicked)
|
|
enable_steal(clicker, clicked, rob_table, angle_2d) --mark as stealing
|
|
minetest.after(pickp.settings["stealth_timing"], stealth, clicker, clicked)
|
|
else
|
|
minetest.chat_send_player(clicker_name, S("Failed robbery!"))
|
|
if pickp.settings["sound_fail"] then
|
|
pickp.make_sound("player", clicker, "pickp_fail", 10)
|
|
end
|
|
--DETECTION WARNING
|
|
detection_warning(clicker, clicked, "try")
|
|
end
|
|
end
|
|
|
|
minetest.register_on_rightclickplayer(function(player, clicker)
|
|
local controls = clicker:get_player_control()
|
|
if controls.sneak and not(is_stealing(clicker)) then
|
|
if not(check_distance(clicker, player)) then
|
|
minetest.chat_send_player(clicker:get_player_name(), S("Too far to steal!"))
|
|
return
|
|
end
|
|
pickpocketing(clicker, player)
|
|
end
|
|
end)
|