1
0
mirror of https://github.com/SmallJoker/bitchange.git synced 2024-11-16 07:40:17 +01:00
bitchange/shop.lua
Foz 6f9cc0081a Prevent shops from trading tools deceptively
This limits the tools a shop will accept or give to those whose wear is less
than or equal to the tool shown in the Owner needs or Owner gives lists. This
protects both owners and customers from unfair trades. Owners are protected
from receiving worn out tools when a new tool is placed in the needs list.
Customers are protected from receiving worn out tools when a new one is placed
in the give list.
2017-01-31 19:51:35 +01:00

414 lines
12 KiB
Lua

--Created by Krock for the BitChange mod
--Parts of codes, images and ideas from Dan Duncombe's exchange shop
-- https://forum.minetest.net/viewtopic.php?id=7002
--License: WTFPL
local exchange_shop = {}
-- Tool wear aware replacement for contains_item.
local function list_contains_item(inv, listname, stack)
local list = inv:get_list(listname)
for i, list_stack in pairs(list) do
if list_stack:get_name() == stack:get_name() and
list_stack:get_count() >= stack:get_count() and
list_stack:get_wear() <= stack:get_wear() then
return i
end
end
end
-- Tool wear aware replacement for remove_item.
local function list_remove_item(inv, listname, stack)
local index = list_contains_item(inv, listname, stack)
if index then
local list_stack = inv:get_stack(listname, index)
local removed_stack = list_stack:take_item(stack:get_count())
inv:set_stack(listname, index, list_stack)
return removed_stack
end
end
local function get_exchange_shop_formspec(number,pos,title)
local formspec = ""
local name = "nodemeta:"..pos.x..","..pos.y..","..pos.z
if number == 1 then
-- customer
formspec = ("size[8,9;]"..
"label[0,0;Exchange shop]"..
"label[1,0.5;Owner needs:]"..
"list["..name..";cust_ow;1,1;2,2;]"..
"button[3,2.4;2,1;exchange;Exchange]"..
"label[5,0.5;Owner gives:]"..
"list["..name..";cust_og;5,1;2,2;]"..
"label[0.7,3.5;Ejected items:]"..
"label[0.7,3.8;(Remove me!)]"..
"list["..name..";cust_ej;3,3.5;4,1;]"..
"list[current_player;main;0,5;8,4;]"..
"listring["..name..";custm_ej]"..
"listring[current_player;main]")
elseif number == 2 or number == 3 then
-- owner
formspec = ("size[11,10;]"..
"label[0.3,0.1;Title:]"..
"field[1.5,0.5;3,0.5;title;;"..title.."]"..
"button[4.1,0.24;1,0.5;set_title;Set]"..
"label[0,0.7;You need:]"..
"list["..name..";cust_ow;0,1.2;2,2;]"..
"label[3,0.7;You give:]"..
"list["..name..";cust_og;3,1.2;2,2;]"..
"label[0.3,3.5;Ejected items: (Remove me!)]"..
"list["..name..";custm_ej;0,4;4,1;]"..
"label[6,0;You are viewing:]"..
"label[6,0.3;(Click to switch)]"..
"listring["..name..";custm_ej]"..
"listring[current_player;main]")
if number == 2 then
formspec = (formspec..
"button[8.5,0.2;2.5,0.5;vstock;Customers stock]"..
"list["..name..";custm;6,1;5,4;]"..
"listring["..name..";custm]"..
"listring[current_player;main]")
else
formspec = (formspec..
"button[8.5,0.2;2.5,0.5;vcustm;Your stock]"..
"list["..name..";stock;6,1;5,4;]"..
"listring["..name..";stock]"..
"listring[current_player;main]")
end
formspec = (formspec..
"label[1,5;Use (E) + (Right click) for customer interface]"..
"list[current_player;main;1,6;8,4;]")
end
return formspec
end
local function get_exchange_shop_tube_config(mode)
if bitchange.exchangeshop_pipeworks then
if mode == 1 then
return {choppy=2, oddly_breakable_by_hand=2, tubedevice=1, tubedevice_receiver=1}
else
return {
insert_object = function(pos, node, stack, direction)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return inv:add_item("stock",stack)
end,
can_insert = function(pos, node, stack, direction)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return inv:room_for_item("stock",stack)
end,
input_inventory="custm",
connect_sides = {left=1, right=1, back=1, top=1, bottom=1}
}
end
else
if mode == 1 then
return {choppy=2,oddly_breakable_by_hand=2}
else
return {
insert_object = function(pos, node, stack, direction)
return false
end,
can_insert = function(pos, node, stack, direction)
return false
end,
connect_sides = {}
}
end
end
end
minetest.register_on_player_receive_fields(function(sender, formname, fields)
if formname ~= "bitchange:shop_formspec" then
return
end
local player_name = sender:get_player_name()
if not exchange_shop[player_name] then
return
end
local pos = exchange_shop[player_name]
local meta = minetest.get_meta(pos)
local title = meta:get_string("title") or ""
local shop_owner = meta:get_string("owner")
if fields.quit then
exchange_shop[player_name] = nil
return
end
if fields.set_title then
if fields.title and title ~= fields.title then
if fields.title ~= "" then
meta:set_string("infotext", "'"..fields.title.."' (owned by "..shop_owner..")")
else
meta:set_string("infotext", "Exchange shop (owned by "..shop_owner..")")
end
meta:set_string("title", minetest.formspec_escape(fields.title))
end
end
if fields.exchange then
local shop_inv = meta:get_inventory()
if shop_inv:is_empty("cust_ow")
and shop_inv:is_empty("cust_og") then
return
end
if not shop_inv:is_empty("cust_ej")
or not shop_inv:is_empty("custm_ej") then
minetest.chat_send_player(player_name,
"One or multiple ejection fields are filled. "..
"Please empty them or contact the shop owner.")
return
end
local player_inv = sender:get_inventory()
local err_msg = ""
local cust_ow = shop_inv:get_list("cust_ow")
local cust_og = shop_inv:get_list("cust_og")
-- Check validness of stack "owner wants"
local cust_ow_ok = true
for i1, item1 in pairs(cust_ow) do
local name1 = item1:get_name()
for i2, item2 in pairs(cust_ow) do
if name1 == "" then
break
end
if i1 ~= i2 and name1 == item2:get_name() then
cust_ow_ok = false
break
end
end
if not cust_ow_ok then
err_msg = "The field 'Owner needs' can not contain multiple "..
"times the same items. Please contact the shop owner."
break
end
end
-- Check validness of stack "owner gives"
if err_msg == "" then
local cust_og_ok = true
for i1, item1 in pairs(cust_og) do
local name1 = item1:get_name()
for i2, item2 in pairs(cust_og) do
if name1 == "" then
break
end
if i1 ~= i2 and name1 == item2:get_name() then
cust_og_ok = false
break
end
end
if not cust_og_ok then
err_msg = "The field 'Owner gives' can not contain multiple "..
"times the same items. Please contact the shop owner."
break
end
end
end
-- Check for space in the shop
if err_msg == "" then
for i, item in pairs(cust_ow) do
if not shop_inv:room_for_item("custm", item) then
err_msg = "The stock in this shop is full. "..
"Please contact the shop owner."
break
end
end
end
-- Check availability of the shop's items
if err_msg == "" then
for i, item in pairs(cust_og) do
if not list_contains_item(shop_inv, "stock", item) then
err_msg = "This shop is sold out."
break
end
end
end
-- Check for space in the player's inventory
if err_msg == "" then
for i, item in pairs(cust_og) do
if not player_inv:room_for_item("main", item) then
err_msg = "You do not have enough space in your inventory."
break
end
end
end
-- Check availability of the player's items
if err_msg == "" then
for i, item in pairs(cust_ow) do
if not list_contains_item(player_inv, "main", item) then
err_msg = "You do not have the required items."
break
end
end
end
-- Do the exchange!
if err_msg == "" then
local fully_exchanged = true
for i, item in pairs(cust_ow) do
local stack = list_remove_item(player_inv, "main", item)
if shop_inv:room_for_item("custm", stack) then
shop_inv:add_item("custm", stack)
else
-- Move to ejection field
shop_inv:add_item("custm_ej", stack)
fully_exchanged = false
end
end
for i, item in pairs(cust_og) do
local stack = list_remove_item(shop_inv, "stock", item)
if player_inv:room_for_item("main", stack) then
player_inv:add_item("main", stack)
else
-- Move to ejection field
shop_inv:add_item("cust_ej", stack)
fully_exchanged = false
end
end
if not fully_exchanged then
err_msg = "Warning! Stacks are overflowing somewhere!"
end
end
-- Throw error message
if err_msg ~= "" then
minetest.chat_send_player(player_name, "Exchange shop: "..err_msg)
end
elseif bitchange.has_access(shop_owner, player_name) then
local num = 0
if fields.vcustm then
num = 2
elseif fields.vstock then
num = 3
else
return
end
minetest.show_formspec(player_name, "bitchange:shop_formspec", get_exchange_shop_formspec(num, pos, title))
end
end)
minetest.register_node("bitchange:shop", {
description = "Shop",
tiles = {"bitchange_shop_top.png", "bitchange_shop_top.png",
"bitchange_shop_side.png", "bitchange_shop_side.png",
"bitchange_shop_side.png", "bitchange_shop_front.png"},
paramtype2 = "facedir",
groups = get_exchange_shop_tube_config(1),
tube = get_exchange_shop_tube_config(2),
sounds = default.node_sound_wood_defaults(),
after_place_node = function(pos, placer)
local meta = minetest.get_meta(pos)
meta:set_string("owner", placer:get_player_name())
meta:set_string("infotext", "Exchange shop (owned by "..
meta:get_string("owner")..")")
end,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", "Exchange shop (constructing)")
meta:set_string("formspec", "")
meta:set_string("owner", "")
local inv = meta:get_inventory()
inv:set_size("stock", 5*4) -- needed stock for exchanges
inv:set_size("custm", 5*4) -- stock of the customers exchanges
inv:set_size("custm_ej", 4) -- ejected items if shop has no inventory room
inv:set_size("cust_ow", 2*2) -- owner wants
inv:set_size("cust_og", 2*2) -- owner gives
inv:set_size("cust_ej", 4) -- ejected items if player has no inventory room
end,
can_dig = function(pos,player)
local meta = minetest.get_meta(pos);
local inv = meta:get_inventory()
if inv:is_empty("stock") and inv:is_empty("custm")
and inv:is_empty("cust_ow") and inv:is_empty("custm_ej")
and inv:is_empty("cust_og") and inv:is_empty("cust_ej") then
return true
end
minetest.chat_send_player(player:get_player_name(), "Cannot dig exchange shop: one or multiple stocks are in use.")
return false
end,
on_rightclick = function(pos, node, clicker, itemstack)
local meta = minetest.get_meta(pos)
local player_name = clicker:get_player_name()
local view = 0
exchange_shop[player_name] = pos
if bitchange.has_access(meta:get_string("owner"), player_name) then
if clicker:get_player_control().aux1 then
view = 1
else
view = 2
end
else
view = 1
end
minetest.show_formspec(player_name, "bitchange:shop_formspec", get_exchange_shop_formspec(view, pos, meta:get_string("title")))
end,
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
local meta = minetest.get_meta(pos)
if bitchange.has_access(meta:get_string("owner"), player:get_player_name()) then
return count
end
return 0
end,
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
if player:get_player_name() == ":pipeworks" then
return stack:get_count()
end
if listname == "custm" then
minetest.chat_send_player(player:get_player_name(), "Exchange shop: Please press 'Customers stock' and insert your items there.")
return 0
end
local meta = minetest.get_meta(pos)
if bitchange.has_access(meta:get_string("owner"), player:get_player_name())
and listname ~= "cust_ej"
and listname ~= "custm_ej" then
return stack:get_count()
end
return 0
end,
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
if player:get_player_name() == ":pipeworks" then
return stack:get_count()
end
local meta = minetest.get_meta(pos)
if bitchange.has_access(meta:get_string("owner"), player:get_player_name())
or listname == "cust_ej" then
return stack:get_count()
end
return 0
end,
})
minetest.register_craft({
output = "bitchange:shop",
recipe = {
{"default:sign_wall"},
{"default:chest_locked"},
}
})
minetest.register_on_dieplayer(function(player)
local player_name = player:get_player_name()
exchange_shop[player_name] = nil
end)
if minetest.get_modpath("wrench") and wrench then
local STRING = wrench.META_TYPE_STRING
wrench:register_node("bitchange:shop", {
lists = {"stock", "custm", "custm_ej", "cust_ow", "cust_og", "cust_ej"},
metas = {
owner = STRING,
infotext = STRING,
title = STRING,
},
owned = true
})
end