emeraldbank/fancyshop.lua
2024-04-01 19:44:56 +02:00

1782 lines
68 KiB
Lua

-- Copyright (C) 2021, 2024 Sandro del Toro
-- This file is part of Emeraldbank Minetest Mod.
-- Emeraldbank is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Affero General Public License as
-- published by the Free Software Foundation, either version 3 of the
-- License, or (at your option) any later version.
-- Emeraldbank is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU Affero General Public License for more details.
-- You should have received a copy of the GNU Affero General Public License
-- along with Emeraldbank. If not, see <https://www.gnu.org/licenses/>.
-- This code was forked from fancy_vend mod, adapted to MineClone2.
local S = core.get_translator(core.get_current_modname())
local settings = minetest.settings
local shopcraft = core.settings:get_bool("emeraldbank.shop_craft") or true
local formspec_prefix = "emeraldbank:shop_formspec"
local shop_timer = emeraldbank.shop_timer
local display_node = (settings:get("fancy_vend.display_node") or "mcl_core:glass")
local max_logs = (tonumber(settings:get("fancy_vend.log_max")) or 40)
local autorotate_speed = (tonumber(settings:get("fancy_vend.autorotate_speed")) or 1)
local no_alerts = settings:get_bool("fancy_vend.no_alerts")
local drop_vendor = "fancy_vend:player_vendor"
-- Register a copy of the display node with no drops to make players separating the obsidian glass with something like a piston a non-issue.
local display_node_def = table.copy(minetest.registered_nodes[display_node])
display_node_def.drop = ""
display_node_def.pointable = false
display_node_def.groups.not_in_creative_inventory = 1
display_node_def.description = S("Fancy Vendor Display Node (you hacker you!)")
display_node_def.on_blast = function() return nil end
if pipeworks then
display_node_def.digiline = {
wire = {
rules = pipeworks.digilines_rules
}
}
end
minetest.register_node(":fancy_vend:display_node", display_node_def)
-- Craftitem to display when vendor is inactive (Use just image for this???)
minetest.register_craftitem(":fancy_vend:inactive",{inventory_image = "inactive.png",})
minetest.register_privilege("disable_vendor", S("Enables the user to set all vendors to inactive."))
local function bts(bool)
if bool == false then
return "false"
elseif bool == true then
return "true"
else
return bool
end
end
local function stb(str)
if str == "false" then
return false
elseif str == "true" then
return true
else
return str
end
end
local modstorage = minetest.get_mod_storage()
if modstorage:get_string("all_inactive_force") == "" then
modstorage:set_string("all_inactive_force", "false")
end
local all_inactive_force = stb(modstorage:get_string("all_inactive_force"))
minetest.register_chatcommand("disable_all_vendors", {
description = S("Toggle vendor inactivity."),
privs = {disable_vendor=true},
func = function(name, param)
if all_inactive_force then
all_inactive_force = false
modstorage:set_string("all_inactive_force", "false")
else
all_inactive_force = true
modstorage:set_string("all_inactive_force", "true")
end
end,
})
table.length = function(table)
local length
for i in pairs(table) do
length = length + 1
end
return length
end
local function send_message(pos, channel, msg)
if channel and channel ~= "" then
digilines.receptor_send(pos, digilines.rules.default, channel, msg)
end
end
-- Awards
if minetest.get_modpath("awards") then
awards.register_achievement("fancy_vend:getting_fancy",{
title = S("Getting Fancy"),
description = S("Craft a fancy vendor."),
trigger = {
type = "craft",
item = drop_vendor,
target = 1,
},
icon = "player_vend.png^mcl_core_emerald.png",
})
-- awards.register_achievement("fancy_vend:wizard",{
-- title = "You're a Wizard",
-- description = S("Craft a copy tool."),
-- trigger = {
-- type = "craft",
-- item = "fancy_vend:copy_tool",
-- target = 1,
-- },
-- icon = "copier.png",
-- })
awards.register_achievement("fancy_vend:trader",{
title = "Trader",
description = "Configure a depositor.",
icon = "player_depo.png^mcl_core_emerald.png",
})
awards.register_achievement("fancy_vend:seller",{
title = "Seller",
description = "Configure a vendor.",
icon = "player_vend.png^mcl_core_emerald.png",
})
awards.register_achievement("fancy_vend:shop_keeper",{
title = "Shop Keeper",
description = "Configure 10 vendors or depositors.",
icon = "player_vend.png^mcl_core_emerald.png",
secret = true,
})
awards.register_achievement("fancy_vend:merchant",{
title = "Merchant",
description = "Configure 25 vendors or depositors.",
icon = "player_vend.png^mcl_core_emerald.png",
secret = true,
})
awards.register_achievement("fancy_vend:super_merchant",{
title = "Super Merchant",
description = "Configure 100 vendors or depositors. How do you even have this much stuff to sell?",
icon = "player_vend.png^mcl_core_emerald.png",
secret = true,
})
awards.register_achievement("fancy_vend:god_merchant",{
title = "God Merchant",
description = "Configure 9001 vendors or depositors. Ok wot.",
icon = "player_vend.png^mcl_core_emerald.png",
secret = true, -- Oi. Cheater.
})
end
local tmp = {}
minetest.register_entity(":fancy_vend:display_item",{
hp_max = 1,
visual = "wielditem",
visual_size = {x = 0.33, y = 0.33},
collisionbox = {0, 0, 0, 0, 0, 0},
physical = false,
textures = {"air"},
on_activate = function(self, staticdata)
if tmp.nodename ~= nil and tmp.texture ~= nil then
self.nodename = tmp.nodename
tmp.nodename = nil
self.texture = tmp.texture
tmp.texture = nil
else
if staticdata ~= nil and staticdata ~= "" then
local data = staticdata:split(';')
if data and data[1] and data[2] then
self.nodename = data[1]
self.texture = data[2]
end
end
end
if self.texture ~= nil then
self.object:set_properties({textures = {self.texture}})
end
self.object:set_properties({automatic_rotate = autorotate_speed})
end,
get_staticdata = function(self)
if self.nodename ~= nil and self.texture ~= nil then
return self.nodename .. ';' .. self.texture
end
return ""
end,
})
local function remove_item(pos)
local objs = nil
objs = minetest.get_objects_inside_radius(pos, .5)
if objs then
for _, obj in ipairs(objs) do
if obj and obj:get_luaentity() and obj:get_luaentity().name == "fancy_vend:display_item" then
obj:remove()
end
end
end
end
local function update_item(pos, node)
pos.y = pos.y + 1
remove_item(pos)
if minetest.get_node(pos).name ~= "fancy_vend:display_node" then
-- minetest.log("warning", "[Emeraldbank]: Placing display item inside "..minetest.get_node(pos).name.." at "..minetest.pos_to_string(pos).." is not permitted, aborting")
-- return
minetest.set_node(pos, {name = "fancy_vend:display_node"})
end
pos.y = pos.y - 1
local meta = minetest.get_meta(pos)
if meta:get_string("item") ~= "" then
pos.y = pos.y + (12 / 16 + 0.11)
tmp.nodename = node.name
tmp.texture = ItemStack(meta:get_string("item")):get_name()
local e = minetest.add_entity(pos,"fancy_vend:display_item")
pos.y = pos.y - (12 / 16 + 0.11)
end
end
-- LBM to refresh entities after clearobjects
minetest.register_lbm({
label = "Refresh vendor display",
name = ":fancy_vend:display_refresh",
nodenames = {"fancy_vend:display_node"},
run_at_every_load = true,
action = function(pos, node)
if not next(minetest.get_objects_inside_radius(pos, 0.5)) then
pos.y = pos.y - 1
update_item(pos, node)
end
end
})
local function set_vendor_settings(pos, SettingsDef)
local meta = minetest.get_meta(pos)
meta:set_string("settings", minetest.serialize(SettingsDef))
end
emeraldbank.set_vendor_settings = set_vendor_settings
local function reset_vendor_settings(pos)
local settings_default = {
input_item = "mcl_core:emerald", -- Don't change this unless you plan on setting this up to add this item to the inventories
output_item = "", -- Don't change this unless you plan on setting this up to add this item to the inventories
input_item_qty = 1,
output_item_qty = 1,
admin_vendor = false,
depositor = false,
currency_eject = false,
accept_output_only = false,
split_incoming_stacks = false,
inactive_force = false,
accept_worn_input = true,
accept_worn_output = true,
digiline_channel = "",
co_sellers = "",
banned_buyers = "",
auto_sort = false,
}
set_vendor_settings(pos, settings_default)
return settings_default
end
emeraldbank.reset_vendor_settings = reset_vendor_settings
local function get_vendor_settings(pos)
local meta = minetest.get_meta(pos)
local settings = minetest.deserialize(meta:get_string("settings"))
if not settings then
return reset_vendor_settings(pos)
else
-- If settings added by newer versions of fancy_vend are nil then send defaults
if settings.auto_sort == nil then
settings.auto_sort = false
end
-- Sanitatize number values (backwards compat)
settings.input_item_qty = (type(settings.input_item_qty) == "number" and math.abs(settings.input_item_qty) or 1)
settings.output_item_qty = (type(settings.output_item_qty) == "number" and math.abs(settings.output_item_qty) or 1)
return settings
end
end
emeraldbank.get_vendor_settings = get_vendor_settings
local function can_buy_from_vendor(pos, player)
local settings = get_vendor_settings(pos)
local banned_buyers = string.split((settings.banned_buyers or ""),",")
for i in pairs(banned_buyers) do
if banned_buyers[i] == player:get_player_name() then
return false
end
end
return true
end
local function can_modify_vendor(pos, player)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local is_owner = false
if meta:get_string("owner") == player:get_player_name() or minetest.check_player_privs(player, {protection_bypass=true}) then
is_owner = true
end
return is_owner
end
local function can_dig_vendor(pos, player)
local meta = minetest.get_meta(pos);
local inv = meta:get_inventory()
return inv:is_empty("main") and can_modify_vendor(pos, player)
end
local function can_access_vendor_inv(player, pos)
local meta = minetest.get_meta(pos)
if minetest.check_player_privs(player, {protection_bypass=true}) or meta:get_string("owner") == player:get_player_name() then return true end
local settings = get_vendor_settings(pos)
local co_sellers = string.split(settings.co_sellers,",")
for i in pairs(co_sellers) do
if co_sellers[i] == player:get_player_name() then
return true
end
end
return false
end
-- Inventory helpers:
-- Function to sort inventory (Taken from technic_chests)
local function sort_inventory(inv)
local inlist = inv:get_list("main")
local typecnt = {}
local typekeys = {}
for _, st in ipairs(inlist) do
if not st:is_empty() then
local n = st:get_name()
local w = st:get_wear()
local m = st:get_metadata()
local k = string.format("%s %05d %s", n, w, m)
if not typecnt[k] then
typecnt[k] = {
name = n,
wear = w,
metadata = m,
stack_max = st:get_stack_max(),
count = 0,
}
table.insert(typekeys, k)
end
typecnt[k].count = typecnt[k].count + st:get_count()
end
end
table.sort(typekeys)
local outlist = {}
for _, k in ipairs(typekeys) do
local tc = typecnt[k]
while tc.count > 0 do
local c = math.min(tc.count, tc.stack_max)
table.insert(outlist, ItemStack({
name = tc.name,
wear = tc.wear,
metadata = tc.metadata,
count = c,
}))
tc.count = tc.count - c
end
end
if #outlist > #inlist then return end
while #outlist < #inlist do
table.insert(outlist, ItemStack(nil))
end
inv:set_list("main", outlist)
end
local function free_slots(inv, listname, itemname, quantity)
local size = inv:get_size(listname)
local free = 0
for i=1,size do
local stack = inv:get_stack(listname, i)
if stack:is_empty() or stack:get_free_space() > 0 then
if stack:is_empty() then
free = free + ItemStack(itemname):get_stack_max()
elseif stack:get_name() == itemname then
free = free + stack:get_free_space()
end
end
end
if free < quantity then
return false
else
return true
end
end
local function inv_insert(inv, listname, itemstack, quantity, from_table, pos, input_eject)
local stackmax = itemstack:get_stack_max()
local name = itemstack:get_name()
local stacks = {}
local remaining_quantity = quantity
-- Add the full stacks to the list
while remaining_quantity > stackmax do
table.insert(stacks, {name = name, count = stackmax})
remaining_quantity = remaining_quantity - stackmax
end
-- Add the remaining stack to the list
table.insert(stacks, {name = name, count = remaining_quantity})
-- If tool add wears ignores if from_table = nil (eg, due to vendor beig admin vendor)
if minetest.registered_tools[name] and from_table then
for i in pairs(stacks) do
local from_item_table = from_table[i].item:to_table()
stacks[i].wear = from_item_table.wear
end
end
-- if has metadata add metadata
if from_table then
for i in pairs(stacks) do
if not i then return end -- crash
local from_item_table = from_table[i].item:to_table()
if from_item_table.name == name then
if from_item_table.metadata then
stacks[i].metadata = from_item_table.metadata -- Apparently some mods *cough* digtron *cough* do use deprecated metadata strings
end
if from_item_table.meta then
stacks[i].meta = from_item_table.meta -- Most mods use metadata tables which is the correct method but ok
end
end
end
end
-- Add to inventory or eject to pipeworks/hoppers (whichever is applicable)
local output_tube_connected = false
local output_hopper_connected = false
if input_eject and pos then
local pos_under = vector.new(pos)
pos_under.y = pos_under.y - 1
local node_under = minetest.get_node(pos_under)
if minetest.get_item_group(node_under.name, "tubedevice") > 0 then
output_tube_connected = true
end
if node_under.name == "hopper:hopper" or node_under.name == "hopper:hopper_side" then
output_hopper_connected = true
end
end
for i in pairs(stacks) do
if output_tube_connected then
pipeworks.tube_inject_item(pos, pos, vector.new(0, -1, 0), stacks[i], minetest.get_meta(pos):get_string("owner"))
else
local leftovers = ItemStack(stacks[i])
if output_hopper_connected then
local pos_under = {x = pos.x, y = pos.y-1, z = pos.z}
local hopper_inv = minetest.get_meta(pos_under):get_inventory()
leftovers = hopper_inv:add_item("main", leftovers)
end
if not leftovers:is_empty() then
inv:add_item(listname, leftovers)
end
end
end
end
local function inv_remove(inv, listname, remove_table, itemstring, quantity)
local count = 0
for i in pairs(remove_table) do
count = count + remove_table[i].item:get_count()
inv:set_stack(listname, remove_table[i].id, nil)
end
-- Add back items if too many were taken
if count > quantity then
inv:add_item(listname, ItemStack({name = itemstring, count = count - quantity}))
end
end
local function inv_contains_items(inv, listname, itemstring, quantity, ignore_wear)
local minimum = quantity
local get_items = {}
local count = 0
for i=1,inv:get_size(listname) do
local stack = inv:get_stack(listname, i)
if stack:get_name() == itemstring then
if ignore_wear or (not minetest.registered_tools[itemstring] or stack:get_wear() == 0) then
count = count + stack:get_count()
table.insert(get_items, {id=i, item=stack})
if count >= minimum then
return true, get_items
end
end
end
end
return false
end
local function get_vendor_status(pos)
local settings = get_vendor_settings(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
if all_inactive_force then
return false, "all_inactive_force"
elseif settings.input_item == "" or settings.output_item == "" then
return false, "unconfigured"
elseif settings.inactive_force then
return false, "inactive_force"
elseif not minetest.check_player_privs(meta:get_string("owner"), {admin_shop=true}) and settings.admin_vendor == true then
return false, "no_privs"
elseif not inv_contains_items(inv, "main", settings.output_item, settings.output_item_qty, settings.accept_worn_output) and not settings.admin_vendor then
return false, "no_output"
elseif not free_slots(inv, "main", settings.input_item, settings.input_item_qty) and not settings.admin_vendor then
return false, "no_room"
else
return true
end
end
local function make_inactive_string(errorcode)
local status_str = ""
if errorcode == "unconfigured" then
status_str = status_str.." (unconfigured)"
elseif errorcode == "inactive_force" then
status_str = status_str.." (forced)"
elseif errorcode == "no_output" then
status_str = status_str.." (out of stock)"
elseif errorcode == "no_room" then
status_str = status_str.." (no room)"
elseif errorcode == "no_privs" then
status_str = status_str.." (seller has insufficient privilages)"
elseif errorcode == "all_inactive_force" then
status_str = status_str.." (all vendors disabled temporarily by admin)"
end
return status_str
end
-- Various email and tell mod support
-- This function takes the position of a vendor and alerts the owner if it has just been emptied
local email_loaded, tell_loaded, mail_loaded = minetest.get_modpath("email"), minetest.get_modpath("tell"), minetest.get_modpath("mail")
local function alert_owner_if_empty(pos)
if no_alerts then return end
local meta = minetest.get_meta(pos)
local settings = get_vendor_settings(pos)
local owner = meta:get_string("owner")
local alerted = stb(meta:get_string("alerted") or "false") -- check
local status, errorcode = get_vendor_status(pos)
-- Message to send
local stock_msg = "Your vendor trading "..settings.input_item_qty.." "..minetest.registered_items[settings.input_item].description.." for "..settings.output_item_qty.." "..minetest.registered_items[settings.output_item].description.." at position "..minetest.pos_to_string(pos, 0).." has just run out of stock."
if not alerted and not status and errorcode == "no_output" then
if mail_loaded and mail.send then
local success, error = mail.send({
from = "Emerald Shop",
to = owner,
--cc = "carbon, copy",
--bcc = "blind, carbon, copy",
subject = S("Out of Stock!"),
body = stock_msg
})
-- if "success" is false the error parameter will contain a message
meta:set_string("alerted", "true")
return
-- Rubenwardy's Email Mod: https://github.com/rubenwardy/email
elseif email_loaded then
email.send_mail("Fancy Vend", owner, stock_msg)
meta:set_string("alerted", "true")
return
elseif tell_loaded then
-- octacians tell mod https://github.com/octacian/tell
tell.add(owner, "Fancy Vend", stock_msg)
meta:set_string("alerted", "true")
return
end
end
end
local function run_inv_checks(pos, player, lots)
local settings = get_vendor_settings(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local player_inv = player:get_inventory()
local player_emeralds = emeraldbank.get_emeralds(player:get_player_name())
local ct = {}
-- Get input and output quantities after multiplying by lot count
local output_qty = settings.output_item_qty * lots
local input_qty = settings.input_item_qty * lots
-- Perform inventory checks
ct.player_has, ct.player_item_table = inv_contains_items(player_inv, "main", settings.input_item, input_qty, settings.accept_worn_input)
if settings.input_item == "mcl_core:emerald" then
ct.player_has = player_emeralds >= input_qty
end
ct.vendor_has, ct.vendor_item_table = inv_contains_items(inv, "main", settings.output_item, output_qty, settings.accept_worn_output)
ct.player_fits = free_slots(player_inv, "main", settings.output_item, output_qty)
ct.vendor_fits = free_slots(inv, "main", settings.input_item, input_qty)
if ct.player_has and ct.vendor_has and ct.player_fits and ct.vendor_fits then
ct.overall = true
else
ct.overall = false
end
return ct
end
local function get_max_lots(pos, player)
local max = 0
while run_inv_checks(pos, player, max).overall do
max = max + 1
end
return max
end
local function make_purchase(pos, player, lots)
if not can_buy_from_vendor(pos, player) then
return false, "You cannot purchase from this vendor"
end
local settings = get_vendor_settings(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local player_inv = player:get_inventory()
local name = player:get_player_name()
local player_emeralds = emeraldbank.get_emeralds(name)
local status, errorcode = get_vendor_status(pos)
-- Double check settings, vendors which were incorrectly set up before this bug fix won't matter anymore
settings.input_item_qty = math.abs(settings.input_item_qty)
settings.output_item_qty = math.abs(settings.output_item_qty)
if status then
-- Get input and output quantities after multiplying by lot count
local output_qty = settings.output_item_qty * lots
local input_qty = settings.input_item_qty * lots
-- Perform inventory checks
local ct = run_inv_checks(pos, player, lots)
if ct.player_has then
if ct.player_fits then
if settings.admin_vendor then
minetest.log("action", name.." trades "..settings.input_item_qty.." "..settings.input_item.." for "..settings.output_item_qty.." "..settings.output_item.." using vendor at "..minetest.pos_to_string(pos))
if settings.input_item == "mcl_core:emerald" then
emeraldbank.add_emeralds(player, - input_qty)
else
inv_remove(player_inv, "main", ct.player_item_table, settings.input_item, input_qty)
end
inv_insert(player_inv, "main", ItemStack(settings.output_item), output_qty, nil)
if minetest.get_modpath("digilines") then
send_message(pos, settings.digiline_channel, msg)
end
return true, "Trade successful"
elseif ct.vendor_has then
if ct.vendor_fits then
minetest.log("action", name.." trades "..settings.input_item_qty.." "..settings.input_item.." for "..settings.output_item_qty.." "..settings.output_item.." using vendor at "..minetest.pos_to_string(pos))
inv_remove(inv, "main", ct.vendor_item_table, settings.output_item, output_qty)
if settings.input_item == "mcl_core:emerald" then
emeraldbank.add_emeralds(player, - input_qty)
else
inv_remove(player_inv, "main", ct.player_item_table, settings.input_item, input_qty)
end
inv_insert(player_inv, "main", ItemStack(settings.output_item), output_qty, ct.vendor_item_table)
inv_insert(inv, "main", ItemStack(settings.input_item), input_qty, ct.player_item_table, pos, (minetest.get_modpath("pipeworks") and settings.currency_eject))
-- Run mail mod checks
alert_owner_if_empty(pos)
return true, S("Trade successful")
else
return false, S("Vendor has insufficient space")
end
else
return false, S("Vendor has insufficient resources")
end
else
return false, S("You have insufficient space")
end
else
return false, S("You have insufficient funds")
end
else
return false, S("Vendor is inactive")..make_inactive_string(errorcode)
end
end
local function get_vendor_buyer_fs(pos, player, lots)
local base = "size[9,9]"..
"label[0,0;"..S("Owner wants:").."]"..
"label[0,1.25;"..S("for:").."]"..
"button[0,2.7;2,1;buy;"..S("Buy").."]"..
"label[2.8,2.9;"..S("lots.").."]"..
"button[0,3.6;2,1;lot_fill;"..S("Fill lots to max.").."]"..
"list[current_player;main;0,5.05;9,3;9]"..
mcl_formspec.get_itemslot_bg(0,5.05,9,3)..
"list[current_player;main;0,8.19;9,1;]"..
mcl_formspec.get_itemslot_bg(0,8.19,9,1)..
"listring[current_player;main]"..
"field_close_on_enter[lot_count;false]"
-- Add dynamic elements
local settings = get_vendor_settings(pos)
local meta = minetest.get_meta(pos)
local status, errorcode = get_vendor_status(pos)
local itemstuff =
"item_image_button[0,0.4;1,1;"..settings.input_item..";ignore;]"..
"label[0.9,0.6;"..settings.input_item_qty.." "..minetest.registered_items[settings.input_item].description.."]"..
"item_image_button[0,1.7;1,1;"..settings.output_item..";ignore;]"..
"label[0.9,1.9;"..settings.output_item_qty.." "..minetest.registered_items[settings.output_item].description.."]"
local status_str
if status then
status_str = S("active")
else
status_str = S("inactive")..make_inactive_string(errorcode)
end
local status_fs =
"label[4,0.4;"..S("Vendor status: ")..status_str.."]"..
"label[4,0.8;"..S("Message: ")..meta:get_string("message").."]"..
"label[4,0;"..S("Vendor owned by: ")..meta:get_string("owner").."]"
local setting_specific = ""
if not settings.accept_worn_input then
setting_specific = setting_specific.."label[4,1.6;"..S("Vendor will not accept worn tools.").."]"
end
if not settings.accept_worn_output then
setting_specific = setting_specific.."label[4,1.2;"..S("Vendor will not sell worn tools.").."]"
end
local fields = "field[2.2,3.2;1,0.6;lot_count;;"..(lots or 1).."]"
local fs = base..itemstuff..status_fs..setting_specific..fields
return fs
end
local function get_vendor_settings_fs(pos)
local base = "size[9,10]"..
"label[2.8,0.5;"..S("Input item").."]"..
"label[6.8,0.5;"..S("Output item").."]"..
"image[0,1.3;1,1;debug_btn.png]"..
"item_image_button[0,2.3;1,1;mcl_books:book;button_log;]"..
"item_image_button[0,3.3;1,1;mcl_core:gold_ingot;button_buy;]"..
"list[current_player;main;0,5;9,3;9]"..
mcl_formspec.get_itemslot_bg(0,5,9,3)..
"list[current_player;main;0,8.24;9,1;]"..
mcl_formspec.get_itemslot_bg(0,8.24,9,1)..
"listring[current_player;main]"..
"button_exit[0,9.2;1,1;btn_exit;"..S("Done").."]"
-- Add dynamic elements
local pos_str = pos.x..","..pos.y..","..pos.z
local settings = get_vendor_settings(pos)
if settings.admin_vendor then
base = base.."item_image[0,0.3;1,1;mcl_chests:chest]"
else
base = base.."item_image_button[0,0.3;1,1;mcl_chests:chest;button_inv;]"
end
local inv =
"list[nodemeta:"..pos_str..";wanted_item;1,0.3;1,1;]"..
mcl_formspec.get_itemslot_bg(1,0.3,1,1)..
"list[nodemeta:"..pos_str..";given_item;5,0.3;1,1;]"..
mcl_formspec.get_itemslot_bg(5,0.3,1,1)..
"listring[nodemeta:"..pos_str..";wanted_item]"..
"listring[nodemeta:"..pos_str..";given_item]"
local fields =
"field[2.2,0.8;1,0.6;input_item_qty;;"..settings.input_item_qty.."]"..
"field[6.2,0.8;1,0.6;output_item_qty;;"..settings.output_item_qty.."]"..
"field[1.3,4.1;2.66,1;co_sellers;"..S("Co-Sellers:")..";"..settings.co_sellers.."]"..
"field[3.86,4.1;2.66,1;banned_buyers;"..S("Banned Buyers:")..";"..settings.banned_buyers.."]"..
"field_close_on_enter[input_item_qty;false]"..
"field_close_on_enter[output_item_qty;false]"..
"field_close_on_enter[co_sellers;false]"..
"field_close_on_enter[banned_buyers;false]"
local checkboxes =
"checkbox[1,2.2;inactive_force;"..S("Force vendor into an inactive state.")..";"..bts(settings.inactive_force).."]"..
"checkbox[1,2.6;depositor;"..S("Set this vendor to a Depositor.")..";"..bts(settings.depositor).."]"..
"checkbox[1,3.0;accept_worn_output;"..S("Sell worn tools.")..";"..bts(settings.accept_worn_output).."]"..
"checkbox[5,3.0;accept_worn_input;"..S("Buy worn tools.")..";"..bts(settings.accept_worn_input).."]"..
"checkbox[5,2.6;auto_sort;"..S("Automatically sort inventory.")..";"..bts(settings.auto_sort).."]"
-- Admin vendor checkbox only if owner is admin
local meta = minetest.get_meta(pos)
if minetest.check_player_privs(meta:get_string("owner"), {admin_shop=true}) or settings.admin_vendor then
checkboxes = checkboxes..
"checkbox[5,2.2;admin_vendor;"..S("Set vendor to an admin vendor.")..";"..bts(settings.admin_vendor).."]"
end
-- Optional dependancy specific elements
if minetest.get_modpath("pipeworks") or minetest.get_modpath("hopper") then
checkboxes = checkboxes..
"checkbox[1,1.7;currency_eject;"..S("Eject incoming currency.")..";"..bts(settings.currency_eject).."]"
if minetest.get_modpath("pipeworks") then
checkboxes = checkboxes..
"checkbox[5,1.3;accept_output_only;"..S("Accept for-sale item only.")..";"..bts(settings.accept_output_only).."]"..
"checkbox[1,1.3;split_incoming_stacks;"..S("Split incoming stacks.")..";"..bts(settings.split_incoming_stacks).."]"
end
end
if minetest.get_modpath("digilines") then
fields = fields..
"field[6.41,4.1;2.66,1;digiline_channel;"..S("Digiline Channel:")..";"..settings.digiline_channel.."]"..
"field_close_on_enter[digiline_channel;false]"
end
local fs = base..inv..fields..checkboxes
return fs
end
local function get_vendor_default_fs(pos, player)
local base = "size[16,11]"..
"item_image[0,0.3;1,1;mcl_chests:chest]"..
"list[current_player;main;4,6.6;9,3;9]"..
mcl_formspec.get_itemslot_bg(4,6.6,9,3)..
"list[current_player;main;4,9.75;9,1;]"..
mcl_formspec.get_itemslot_bg(4,9.75,9,1)..
"listring[current_player;main]"..
"button[1,6.85;3,1;inv_tovendor;"..S("All To Vendor").."]"..
"button[13,6.85;3,1;inv_fromvendor;"..S("All From Vendor").."]"..
"button[1,8.08;3,1;inv_output_tovendor;"..S("Output To Vendor").."]"..
"button[13,8.08;3,1;inv_input_fromvendor;"..S("Input From Vendor").."]"..
"button[1,9.31;3,1;sort;"..S("Sort Inventory").."]"..
"button_exit[0,10;1,1;btn_exit;"..S("Done").."]"
-- Add dynamic elements
local pos_str = pos.x..","..pos.y..","..pos.z
local inv_lists =
"list[nodemeta:"..pos_str..";main;1,0.3;15,6;]"..
mcl_formspec.get_itemslot_bg(1,0.3,15,6)..
"listring[nodemeta:"..pos_str..";main]"
local settings_btn = ""
if can_modify_vendor(pos, player) then
settings_btn =
"image_button[0,1.3;1,1;debug_btn.png;button_settings;]"..
"item_image_button[0,2.3;1,1;mcl_books:book;button_log;]"..
"item_image_button[0,3.3;1,1;mcl_core:gold_ingot;button_buy;]"
else
settings_btn =
"image[0,1.3;1,1;debug_btn.png]"..
"item_image[0,2.3;1,1;mcl_books:book]"..
"item_image[0,3.3;1,1;mcl_core:gold_ingot;button_buy;]"
end
local fs = base..inv_lists..settings_btn
return fs
end
local function get_vendor_log_fs(pos)
local base = "size[9,9]"..
"image_button[0,1.3;1,1;debug_btn.png;button_settings;]"..
"item_image[0,2.3;1,1;mcl_books:book]"..
"item_image_button[0,3.3;1,1;mcl_core:gold_ingot;button_buy;]"..
"button_exit[0,8;1,1;btn_exit;"..S("Done").."]"
-- Add dynamic elements
local meta = minetest.get_meta(pos)
local logs = minetest.deserialize(meta:get_string("log"))
local settings = get_vendor_settings(pos)
if settings.admin_vendor then
base = base.."item_image[0,0.3;1,1;mcl_chests:chest]"
else
base = base.."item_image_button[0,0.3;1,1;mcl_chests:chest;button_inv;]"
end
if not logs then logs = {S("Error loading logs"),} end
local logs_tl =
"textlist[1,0.5;7.8,8.6;logs;"..table.concat(logs, ",").."]"..
"label[1,0;Showing (up to "..max_logs..") recent log entries:]"
local fs = base..logs_tl
return fs
end
local function show_buyer_formspec(player, pos)
minetest.show_formspec(player:get_player_name(), "fancy_vend:buyer;"..minetest.pos_to_string(pos), get_vendor_buyer_fs(pos, player, nil))
end
local function show_vendor_formspec(player, pos)
local settings = get_vendor_settings(pos)
if can_access_vendor_inv(player, pos) then
local status, errorcode = get_vendor_status(pos)
if ((not status and errorcode == "unconfigured") and can_modify_vendor(pos, player)) or settings.admin_vendor then
minetest.show_formspec(player:get_player_name(), "fancy_vend:settings;"..minetest.pos_to_string(pos), get_vendor_settings_fs(pos))
else
minetest.show_formspec(player:get_player_name(), "fancy_vend:default;"..minetest.pos_to_string(pos), get_vendor_default_fs(pos, player))
end
else
show_buyer_formspec(player, pos)
end
end
local function swap_vendor(pos, vendor_type)
local node = minetest.get_node(pos)
node.name = vendor_type
minetest.swap_node(pos, node)
end
local function get_correct_vendor(settings)
if settings.admin_vendor then
if settings.depositor then
return "fancy_vend:admin_depo"
else
return "fancy_vend:admin_vendor"
end
else
if settings.depositor then
return "fancy_vend:player_depo"
else
return "fancy_vend:player_vendor"
end
end
end
local function is_vendor(name)
local vendor_names = {
"fancy_vend:player_vendor",
"fancy_vend:player_depo",
"fancy_vend:admin_vendor",
"fancy_vend:admin_depo",
}
for i,n in ipairs(vendor_names) do
if name == n then
return true
end
end
return false
end
local function refresh_vendor(pos)
local node = minetest.get_node(pos)
if node.name:split(":")[1] ~= "fancy_vend" then
return false, "not a vendor"
end
local settings = get_vendor_settings(pos)
local meta = minetest.get_meta(pos)
local status, errorcode = get_vendor_status(pos)
local correct_vendor = get_correct_vendor(settings)
if status or errorcode ~= "no_output" then
meta:set_string("alerted", "false")
end
if status then
meta:set_string("infotext", (settings.admin_vendor and "Admin" or "Player").." Vendor trading "..settings.input_item_qty.." "..minetest.registered_items[settings.input_item].description.." for "..settings.output_item_qty.." "..minetest.registered_items[settings.output_item].description.." (owned by " .. meta:get_string("owner") .. ")")
if meta:get_string("configured") == "" then
meta:set_string("configured", "true")
if minetest.get_modpath("awards") then
local name = meta:get_string("owner")
local data = awards.player(name)
-- Ensure fancy_vend_configure table is in data
if not data.fancy_vend_configure then
data.fancy_vend_configure = {}
end
awards.increment_item_counter(data, "fancy_vend_configure", correct_vendor)
total_item_count = 0
-- for k, v in pairs(data.fancy_vend_configure) do
-- total_item_count = total_item_count + v
-- end
if awards.get_item_count(data, "fancy_vend_configure", "fancy_vend:player_vendor") >= 1 then
awards.unlock(name, "fancy_vend:seller")
end
if awards.get_item_count(data, "fancy_vend_configure", "fancy_vend:player_depo") >= 1 then
awards.unlock(name, "fancy_vend:trader")
end
if total_item_count >= 10 then
awards.unlock(name, "fancy_vend:shop_keeper")
end
if total_item_count >= 25 then
awards.unlock(name, "fancy_vend:merchant")
end
if total_item_count >= 100 then
awards.unlock(name, "fancy_vend:super_merchant")
end
if total_item_count >= 9001 then
awards.unlock(name, "fancy_vend:god_merchant")
end
end
end
if settings.depositor then
if meta:get_string("item") ~= settings.input_item then
meta:set_string("item", settings.input_item)
update_item(pos, node)
end
else
if meta:get_string("item") ~= settings.output_item then
meta:set_string("item", settings.output_item)
update_item(pos, node)
end
end
else
meta:set_string("infotext", "Inactive "..(settings.admin_vendor and "Admin" or "Player").." Vendor"..make_inactive_string(errorcode).." (owned by " .. meta:get_string("owner") .. ")")
if meta:get_string("item") ~= "fancy_vend:inactive" then
meta:set_string("item", "fancy_vend:inactive")
update_item(pos, node)
end
if not alerted and not status and errorcode == "no_room" then
minetest.chat_send_player(meta:get_string("owner"), "[Fancy_Vend]: Error with vendor at "..minetest.pos_to_string(pos, 0)..": does not have room for payment.")
meta:set_string("alerted", "true")
end
end
if correct_vendor ~= node.name then
swap_vendor(pos, correct_vendor)
end
end
emeraldbank.refresh_vendor = refresh_vendor
local function move_inv(frominv, toinv, filter)
for i, v in ipairs(frominv:get_list("main") or {}) do
if v:get_name() == filter or not filter then
if toinv:room_for_item("main", v) then
local leftover = toinv:add_item("main", v)
frominv:remove_item("main", v)
if leftover
and not leftover:is_empty() then
frominv:add_item("main", v)
end
end
end
end
end
minetest.register_on_player_receive_fields(function(player, formname, fields)
local name = formname:split(":")[1]
if name ~= "fancy_vend" then return end
local formtype = formname:split(":")[2]
formtype = formtype:split(";")[1]
local pos = minetest.string_to_pos(formname:split(";")[2])
if not pos then return end
local node = minetest.get_node(pos)
if not is_vendor(node.name) then return end
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local player_inv = player:get_inventory()
local settings = get_vendor_settings(pos)
-- Handle settings changes
if can_modify_vendor(pos, player) then
for i in pairs(fields) do
if stb(fields[i]) ~= settings[i] then
settings[i] = stb(fields[i])
end
end
-- Check number-only fields contain only numbers
if not tonumber(settings.input_item_qty) then
settings.input_item_qty = 1
else
settings.input_item_qty = math.floor(math.abs(tonumber(settings.input_item_qty)))
end
if not tonumber(settings.output_item_qty) then
settings.output_item_qty = 1
else
settings.output_item_qty = math.floor(math.abs(tonumber(settings.output_item_qty)))
end
-- Check item quantities aren't too high (which could lead to additional processing for no reason), if so, set it to the maximum the player inventory can handle
if ItemStack(settings.output_item):get_stack_max() * player_inv:get_size("main") < settings.output_item_qty then
settings.output_item_qty = ItemStack(settings.output_item):get_stack_max() * player_inv:get_size("main")
end
if ItemStack(settings.input_item):get_stack_max() * player_inv:get_size("main") < settings.input_item_qty then
settings.input_item_qty = ItemStack(settings.input_item):get_stack_max() * player_inv:get_size("main")
end
-- Admin vendor priv check
if not minetest.check_player_privs(meta:get_string("owner"), {admin_shop=true}) and fields.admin_vendor == "true" then
settings.admin_vendor = false
end
set_vendor_settings(pos, settings)
refresh_vendor(pos)
end
if fields.quit then
if can_access_vendor_inv(player, pos) and settings.auto_sort then
sort_inventory(inv)
end
return true
end
if fields.sort and can_access_vendor_inv(player, pos) then
sort_inventory(inv)
end
if fields.buy then
local lots = math.floor(tonumber(fields.lot_count) or 1)
-- prevent negative numbers
lots = math.max(lots, 1)
local success, message = make_purchase(pos, player, lots)
if success then
-- Add to vendor logs
local logs = minetest.deserialize(meta:get_string("log"))
for i in pairs(logs) do
if i >= max_logs then
table.remove(logs, 1)
end
end
table.insert(logs, "Player "..player:get_player_name().." purchased "..lots.." lots from this vendor.")
meta:set_string("log", minetest.serialize(logs))
-- Send digiline message if applicable
if minetest.get_modpath("digilines") then
local msg = {
buyer = player:get_player_name(),
lots = lots,
settings = settings,
}
send_message(pos, settings.digiline_channel, msg)
end
end
-- Set message and refresh vendor
if message then
meta:set_string("message", message)
end
refresh_vendor(pos)
elseif fields.lot_fill then
minetest.show_formspec(player:get_player_name(), "fancy_vend:buyer;"..minetest.pos_to_string(pos), get_vendor_buyer_fs(pos, player, get_max_lots(pos, player)))
return true
end
if can_access_vendor_inv(player, pos) then
if fields.inv_tovendor then
minetest.log("action", player:get_player_name().." moves inventory contents to vendor at "..minetest.pos_to_string(pos))
move_inv(player_inv, inv, nil)
refresh_vendor(pos)
elseif fields.inv_output_tovendor then
minetest.log("action", player:get_player_name().." moves output items in inventory to vendor at "..minetest.pos_to_string(pos))
move_inv(player_inv, inv, settings.output_item)
refresh_vendor(pos)
elseif fields.inv_fromvendor then
minetest.log("action", player:get_player_name().." moves inventory contents from vendor at "..minetest.pos_to_string(pos))
move_inv(inv, player_inv, nil)
refresh_vendor(pos)
elseif fields.inv_input_fromvendor then
minetest.log("action", player:get_player_name().." moves input items from vendor at "..minetest.pos_to_string(pos))
move_inv(inv, player_inv, settings.input_item)
refresh_vendor(pos)
end
end
-- Handle page changes
if fields.button_log then
minetest.show_formspec(player:get_player_name(), "fancy_vend:log;"..minetest.pos_to_string(pos), get_vendor_log_fs(pos))
return
elseif fields.button_settings then
minetest.show_formspec(player:get_player_name(), "fancy_vend:settings;"..minetest.pos_to_string(pos), get_vendor_settings_fs(pos))
return
elseif fields.button_inv then
minetest.show_formspec(player:get_player_name(), "fancy_vend:default;"..minetest.pos_to_string(pos), get_vendor_default_fs(pos, player))
return
elseif fields.button_buy then
minetest.show_formspec(player:get_player_name(), "fancy_vend:buyer;"..minetest.pos_to_string(pos), get_vendor_buyer_fs(pos, player, (tonumber(fields.lot_count) or 1)))
return
end
-- Update formspec
if formtype == "log" then
minetest.show_formspec(player:get_player_name(), "fancy_vend:log;"..minetest.pos_to_string(pos), get_vendor_log_fs(pos, player))
elseif formtype == "settings" then
minetest.show_formspec(player:get_player_name(), "fancy_vend:settings;"..minetest.pos_to_string(pos), get_vendor_settings_fs(pos, player))
elseif formtype == "default" then
minetest.show_formspec(player:get_player_name(), "fancy_vend:default;"..minetest.pos_to_string(pos), get_vendor_default_fs(pos, player))
elseif formtype == "buyer" then
minetest.show_formspec(player:get_player_name(), "fancy_vend:buyer;"..minetest.pos_to_string(pos), get_vendor_buyer_fs(pos, player, (tonumber(fields.lot_count) or 1)))
end
end)
local vendor_template = {
description = S("Vending Machine"),
legacy_facedir_simple = true,
--paramtype2 = "facedir",
groups = {choppy=2, oddly_breakable_by_hand=2, tubedevice=1, tubedevice_receiver=1, axey=1, handy=1},
_mcl_blast_resistance = 5,
_mcl_hardness = 0.8,
selection_box = {
type = "fixed",
fixed = {-0.5, -0.5, -0.5, 0.5, 1.5, 0.5},
},
is_ground_content = false,
light_source = 8,
sounds = mcl_sounds.node_sound_wood_defaults(),
drop = drop_vendor,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("Unconfigured Player Vendor"))
meta:set_string("message", S("Vendor initialized"))
meta:set_string("owner", "")
local inv = meta:get_inventory()
inv:set_size("main", 15*6)
inv:set_size("wanted_item", 1*1)
inv:add_item("wanted_item", {name = "mcl_core:emerald"})
inv:set_size("given_item", 1*1)
reset_vendor_settings(pos)
meta:set_string("log", "")
end,
can_dig = can_dig_vendor,
on_place = function(itemstack, placer, pointed_thing)
if pointed_thing.type ~= "node" then return end
local pointed_node_pos = minetest.get_pointed_thing_position(pointed_thing, false)
local pointed_node = minetest.get_node(pointed_node_pos)
if minetest.registered_nodes[pointed_node.name].buildable_to then
pointed_thing.above = pointed_node_pos
end
-- Set variables for access later (for various checks, etc.)
local name = placer:get_player_name()
local above_node_pos = table.copy(pointed_thing.above)
above_node_pos.y = above_node_pos.y + 1
local above_node = minetest.get_node(above_node_pos).name
-- If node above is air or the display node, and it is not protected, attempt to place the vendor. If vendor sucessfully places, place display node above, otherwise alert the user
if (minetest.registered_nodes[above_node].buildable_to or above_node == "fancy_vend:display_node") and not minetest.is_protected(above_node_pos, name) then
local itemstack, success = minetest.item_place(itemstack, placer, pointed_thing, nil)
if above_node ~= "fancy_vend:display_node" and success then
minetest.set_node(above_node_pos, minetest.registered_nodes["fancy_vend:display_node"])
end
-- Set owner
local meta = minetest.get_meta(pointed_thing.above)
meta:set_string("owner", placer:get_player_name() or "")
-- Set default meta
meta:set_string("log", minetest.serialize({"Vendor placed by "..placer:get_player_name(),}))
reset_vendor_settings(pointed_thing.above)
refresh_vendor(pointed_thing.above)
else
minetest.chat_send_player(name, "Vendors require 2 nodes of space.")
end
if minetest.get_modpath("pipeworks") then
pipeworks.after_place(pointed_thing.above)
end
return itemstack
end,
after_place_node = function(pos, placer, itemstack)
core.get_node_timer(pos):start(shop_timer)
end,
on_timer = function(pos, elapsed)
core.get_node_timer(pos):start(shop_timer)
emeraldbank.get_stonks(pos)
end,
on_dig = function(pos, node, digger)
-- Set variables for access later (for various checks, etc.)
local name = digger:get_player_name()
local above_node_pos = table.copy(pos)
above_node_pos.y = above_node_pos.y + 1
-- abandon if player shouldn't be able to dig node
local can_dig = can_dig_vendor(pos, digger)
if not can_dig then return end
-- Try remove display node, if the whole node is able to be removed by the player, remove the display node and continue to remove vendor, if it doesn't exist and vendor can be dug continue to remove vendor.
local success
if minetest.get_node(above_node_pos).name == "fancy_vend:display_node" then
if not minetest.is_protected(above_node_pos, name) and not minetest.is_protected(pos, name) then
minetest.remove_node(above_node_pos)
remove_item(above_node_pos)
success = true
else
success = false
end
else
if not minetest.is_protected(pos, name) then
success = true
else
success = false
end
end
-- If failed to remove display node, don't remove vendor. since protection for whole vendor was checked at display removal, protection need not be re-checked
if success then
minetest.remove_node(pos)
--minetest.handle_node_drops(pos, {drop_vendor}, digger)
minetest.add_item(pos, drop_vendor)
if minetest.get_modpath("pipeworks") then
pipeworks.after_dig(pos)
end
end
end,
tube = {
input_inventory = "main",
connect_sides = {left = 1, right = 1, back = 1, bottom = 1},
insert_object = function(pos, node, stack, direction)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local remaining = inv:add_item("main", stack)
refresh_vendor(pos)
return remaining
end,
can_insert = function(pos, node, stack, direction)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local settings = get_vendor_settings(pos)
if settings.split_stacks then
stack = stack:peek_item(1)
end
if settings.accept_output_only then
if stack:get_name() ~= settings.output_item then
return false
end
end
return inv:room_for_item("main", stack)
end,
},
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
if (not can_access_vendor_inv(player, pos)) or to_list == "wanted_item" or to_list == "given_item" then
return 0
end
return count
end,
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
if not can_access_vendor_inv(player, pos) then
return 0
end
if listname == "wanted_item" or listname == "given_item" then
local inv = minetest.get_meta(pos):get_inventory()
inv:set_stack(listname, index, ItemStack(stack:get_name()))
local settings = get_vendor_settings(pos)
if listname == "wanted_item" then
settings.input_item = stack:get_name()
elseif listname == "given_item" then
settings.output_item = stack:get_name()
end
set_vendor_settings(pos, settings)
return 0
end
return stack:get_count()
end,
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
if not can_access_vendor_inv(player, pos) then
return 0
end
if listname == "wanted_item" or listname == "given_item" then
local inv = minetest.get_meta(pos):get_inventory()
local fake_stack = inv:get_stack(listname, index)
fake_stack:take_item(stack:get_count())
inv:set_stack(listname, index, fake_stack)
local settings = get_vendor_settings(pos)
if listname == "wanted_item" then
settings.input_item = ""
elseif listname == "given_item" then
settings.output_item = ""
end
set_vendor_settings(pos, settings)
return 0
end
return stack:get_count()
end,
on_rightclick = function(pos, node, clicker)
node = minetest.get_node(pos)
if node.name == "fancy_vend:display_node" then
pos.y = pos.y - 1
end
show_vendor_formspec(clicker, pos)
core.get_node_timer(pos):start(shop_timer)
end,
on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
minetest.log("action", player:get_player_name().." moves stuff in vendor at "..minetest.pos_to_string(pos))
refresh_vendor(pos)
end,
on_metadata_inventory_put = function(pos, listname, index, stack, player)
minetest.log("action", player:get_player_name().." moves "..stack:get_name().." to vendor at "..minetest.pos_to_string(pos))
refresh_vendor(pos)
end,
on_metadata_inventory_take = function(pos, listname, index, stack, player)
minetest.log("action", player:get_player_name().." takes "..stack:get_name().." from vendor at "..minetest.pos_to_string(pos))
refresh_vendor(pos)
end,
on_blast = function()
-- TNT immunity
return nil
end,
}
if pipeworks then
vendor_template.digiline = {
receptor = {},
effector = {
action = function() end
},
wire = {
rules = pipeworks.digilines_rules
},
}
end
local player_vendor = table.copy(vendor_template)
player_vendor.tiles = {
"player_vend.png", "player_vend.png",
"player_vend.png^mcl_core_emerald.png",
}
local player_depo = table.copy(vendor_template)
player_depo.tiles = {
"player_depo.png", "player_depo.png",
"player_depo.png^mcl_core_emerald.png",
}
player_depo.on_timer = function(pos, elapsed)
core.get_node_timer(pos):start(shop_timer)
end
player_depo.groups.not_in_creative_inventory = 1
local admin_vendor = table.copy(vendor_template)
admin_vendor.tiles = {
"admin_vend.png", "admin_vend.png",
"admin_vend.png^mcl_core_emerald.png",
}
admin_vendor.groups.not_in_creative_inventory = 1
local admin_depo = table.copy(vendor_template)
admin_depo.tiles = {
"admin_depo.png", "admin_depo.png",
"admin_depo.png^mcl_core_emerald.png",
}
admin_depo.groups.not_in_creative_inventory = 1
minetest.register_node(":fancy_vend:player_vendor", player_vendor)
minetest.register_node(":fancy_vend:player_depo", player_depo)
minetest.register_node(":fancy_vend:admin_vendor", admin_vendor)
minetest.register_node(":fancy_vend:admin_depo", admin_depo)
if shopcraft then
minetest.register_craft({
output = "fancy_vend:player_vendor",
recipe = {
{ "", display_node, ""},
{ "mesecons:redstone", "mcl_core:emerald", "mesecons:redstone"},
{ "","mcl_chests:chest",""},
}
})
end
-- Hopper support
if minetest.get_modpath("hopper") then
hopper:add_container({
{"side", "fancy_vend:player_vendor", "main"}
})
hopper:add_container({
{"side", "fancy_vend:player_depo", "main"}
})
end
---------------
-- Copy Tool --
---------------
local function get_vendor_pos_and_settings(pointed_thing)
if pointed_thing.type ~= "node" then return false end
local pos = minetest.get_pointed_thing_position(pointed_thing, false)
local node = minetest.get_node(pos)
if node.name == "fancy_vend:display_node" then
pos.y = pos.y - 1
node = minetest.get_node(pos)
end
if not is_vendor(node.name) then return false end
local settings = get_vendor_settings(pos)
return pos, settings
end
--minetest.register_alias("fancy_vend:copy_tool", "mcl_core:emerald")
minetest.register_tool(":fancy_vend:copy_tool",{
inventory_image = "copier.png",
description = S("Geminio Wand (For copying vendor settings, right click to save settings, left click to set settings.)"),
stack_max = 1,
on_place = function(itemstack, placer, pointed_thing)
local pos, settings = get_vendor_pos_and_settings(pointed_thing)
if not pos then return end
local meta = itemstack:get_meta()
meta:set_string("settings", minetest.serialize(settings))
minetest.chat_send_player(placer:get_player_name(), "Settings saved.")
return itemstack
end,
on_use = function(itemstack, user, pointed_thing)
local pos, current_settings = get_vendor_pos_and_settings(pointed_thing)
if not pos then return end
local meta = itemstack:get_meta()
local node_meta = minetest.get_meta(pos)
local new_settings = minetest.deserialize(meta:get_string("settings"))
if not new_settings then
minetest.chat_send_player(user:get_player_name(), "No settings to set with. Right-click first on the vendor you want to copy settings from.")
return
end
if can_modify_vendor(pos, user) then
-- Admin vendor priv check
if not minetest.check_player_privs(node_meta:get_string("owner"), {admin_shop=true}) and new_settings.admin_vendor == true then
settings.admin_vendor = false
end
new_settings.input_item = current_settings.input_item
new_settings.input_item_qty = current_settings.input_item_qty
new_settings.output_item = current_settings.output_item
new_settings.output_item_qty = current_settings.output_item_qty
-- Admin vendor priv check
if not minetest.check_player_privs(node_meta:get_string("owner"), {admin_shop=true}) and new_settings.admin_vendor then
new_settings.admin_vendor = current_settings.admin_vendor
end
set_vendor_settings(pos, new_settings)
refresh_vendor(pos)
minetest.chat_send_player(user:get_player_name(), "Settings set.")
else
minetest.chat_send_player(user:get_player_name(), "You cannot modify this vendor.")
end
end,
})
minetest.register_craft({
output = "fancy_vend:copy_tool",
recipe = {
{"mcl_core:stick","", "" },
{"", "mcl_core:obsidian","" },
{"", "", "mcl_core:emerald"},
}
})
minetest.register_craft({
output = "fancy_vend:copy_tool",
recipe = {
{"", "", "mcl_core:stick"},
{"", "mcl_core:obsidian","" },
{"mcl_core:emerald", "", "" },
}
})
---------------------------
-- Vendor Upgrade System --
---------------------------
local old_vendor_mods = string.split((minetest.settings:get("fancy_vend_old_vendor_mods") or "emeraldbank"), ",")
local old_vendor_mods_table = {}
for i in pairs(old_vendor_mods) do
old_vendor_mods_table[old_vendor_mods[i]] = true
end
local base_upgrade_template = {
description = S("Shop Upgrade (Try and place to upgrade)"),
legacy_facedir_simple = true,
--paramtype2 = "facedir",
groups = {choppy=2, oddly_breakable_by_hand=2, not_in_creative_inventory=1, axey=1, handy=1},
_mcl_blast_resistance = 5,
_mcl_hardness = 0.8,
is_ground_content = false,
light_source = 8,
sounds = mcl_sounds.node_sound_wood_defaults(),
drop = drop_vendor,
tiles = {
"player_vend.png^mcl_core_emerald.png",
"player_vend.png^mcl_core_emerald.png",
"upgrade_front.png",
},
on_place = function(itemstack, placer, pointed_thing)
return ItemStack(drop_vendor.." "..itemstack:get_count())
end,
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
local meta = minetest.get_meta(pos)
if player:get_player_name() ~= meta:get_string("owner") then return 0 end
return count
end,
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
local meta = minetest.get_meta(pos)
if player:get_player_name() ~= meta:get_string("owner") then return 0 end
return stack:get_count()
end,
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
local meta = minetest.get_meta(pos)
if player:get_player_name() ~= meta:get_string("owner") then return 0 end
return stack:get_count()
end,
on_blast = function()
return nil
end,
}
local clear_craft_vendors = {}
if old_vendor_mods_table["emeraldbank"] then
local emeraldbank_template = table.copy(base_upgrade_template)
emeraldbank_template.can_dig = function(pos, player)
local pname = player:get_player_name()
local is_admin = core.check_player_privs(pname, {admin_shop=true})
local meta = core.get_meta(pos)
local owner = meta:get_string("owner")
local inv = meta:get_inventory()
return inv:is_empty("stock") and (pname == owner or is_admin)
end
emeraldbank_template.on_rightclick = function(pos, node, clicker, itemstack)
local meta = core.get_meta(pos)
local nodename = core.get_node(pos).name
local owner = meta:get_string("owner")
local pname = clicker:get_player_name()
core.get_node_timer(pos):start(shop_timer)
emeraldbank.get_stonks(pos)
core.show_formspec(pname, formspec_prefix..core.pos_to_string(pos), emeraldbank.get_shop_fs(pos, clicker))
end
emeraldbank_template.on_timer = function(pos, elapsed)
core.get_node_timer(pos):start(shop_timer)
emeraldbank.get_stonks(pos)
end
emeraldbank_template.on_blast = function()
return nil
end
minetest.override_item("emeraldbank:shop", emeraldbank_template)
table.insert(clear_craft_vendors, "emeraldbank:shop")
minetest.override_item("emeraldbank:shop_empty", emeraldbank_template)
table.insert(clear_craft_vendors, "emeraldbank:shop_empty")
end
if old_vendor_mods_table["currency"] then
local currency_template = table.copy(base_upgrade_template)
currency_template.can_dig = function(pos, player)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return inv:is_empty("stock") and inv:is_empty("customers_gave") and inv:is_empty("owner_wants") and inv:is_empty("owner_gives") and (meta:get_string("owner") == player:get_player_name() or minetest.check_player_privs(player:get_player_name(), {protection_bypass=true}))
end
currency_template.on_rightclick = function(pos, node, clicker, itemstack)
local meta = minetest.get_meta(pos)
local list_name = "nodemeta:"..pos.x..','..pos.y..','..pos.z
if clicker:get_player_name() == meta:get_string("owner") then
minetest.show_formspec(clicker:get_player_name(),"fancy_vend:currency_shop_formspec",
"size[8,9.5]"..
"label[0,0;" .. "Customers gave:" .. "]"..
"list["..list_name..";customers_gave;0,0.5;3,2;]"..
"label[0,2.5;" .. "Your stock:" .. "]"..
"list["..list_name..";stock;0,3;3,2;]"..
"label[5,0;" .. "You want:" .. "]"..
"list["..list_name..";owner_wants;5,0.5;3,2;]"..
"label[5,2.5;" .. "In exchange, you give:" .. "]"..
"list["..list_name..";owner_gives;5,3;3,2;]"..
"list[current_player;main;0,5.5;8,4;]"
)
end
end
currency_template.on_blast = function()
return nil
end
minetest.register_node(":currency:shop", currency_template)
table.insert(clear_craft_vendors, "currency:shop")
end
if old_vendor_mods_table["easyvend"] then
local nodes = {"easyvend:vendor", "easyvend:vendor_on", "easyvend:depositor", "easyvend:depositor_on"}
for i in pairs(nodes) do
minetest.register_node(":"..nodes[i], base_upgrade_template)
table.insert(clear_craft_vendors, nodes[i])
end
end
if old_vendor_mods_table["vendor"] then
local nodes = {"vendor:vendor", "vendor:depositor"}
for i in pairs(nodes) do
minetest.register_node(":"..nodes[i], base_upgrade_template)
table.insert(clear_craft_vendors, nodes[i])
end
end
if old_vendor_mods_table["money"] then
local money_template = table.copy(base_upgrade_template)
money_template.can_dig = function(pos, player)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return inv:is_empty("main") and (meta:get_string("owner") == player:get_player_name() or minetest.check_player_privs(player:get_player_name(), {protection_bypass=true}))
end
money_template.on_rightclick = function(pos, node, clicker, itemstack)
local meta = minetest.get_meta(pos)
local list_name = "nodemeta:"..pos.x..','..pos.y..','..pos.z
if clicker:get_player_name() == meta:get_string("owner") then
minetest.show_formspec(clicker:get_player_name(),"fancy_vend:money_shop_formspec",
"size[8,10;]"..
"list["..listname..";main;0,0;8,4;]"..
"list[current_player;main;0,6;8,4;]"
)
end
end
money_template.on_blast = function()
return nil
end
local nodes = {"money:barter_shop", "money:shop", "money:admin_shop", "money:admin_barter_shop"}
for i in pairs(nodes) do
minetest.register_node(":"..nodes[i], money_template)
table.insert(clear_craft_vendors, nodes[i])
end
end
for i_n in pairs(clear_craft_vendors) do
local currency_crafts = minetest.get_all_craft_recipes(i_n)
if currency_crafts then
for i in pairs(currency_crafts) do
minetest.clear_craft(currency_crafts[i])
end
end
end