local S = minetest.get_translator(minetest.get_current_modname()) local mcl_formspec_itemslots local inventory_row_size = 8 if minetest.get_modpath("mcl_formspec") then mcl_formspec_itemslots = mcl_formspec.get_itemslot_bg end if minetest.get_modpath("mcl_core") then inventory_row_size = 9 -- Mineclone2/a has a different inventory size than default end local truncate_item_names_to = 30 -- Large textures can screw with the formspecs. -- See https://github.com/minetest/minetest/issues/9300 for a feature request that would simplify and improve icon generation, if supported. -- In the meantime, here's some methods for overriding item icons to manually work around this: local override_item_icon = {} commoditymarket.override_item_icon = function(item_name, new_icon_texture) override_item_icon[item_name] = new_icon_texture end local override_image_icon = {} commoditymarket.override_image_icon = function(old_icon_texture, new_icon_texture) override_image_icon[old_icon_texture] = new_icon_texture end -- And a setting for disabling icons entirely: local global_enable_item_icons = minetest.settings:get_bool("commoditymarket_enable_item_icons", true) --[inventorycube{{{ --Escaping does not apply here and `^` is replaced by `&` in texture names instead. --Example: -- [inventorycube{grass.png{dirt.png&grass_side.png{dirt.png&grass_side.png --Creates an inventorycube with `grass.png`, `dirt.png^grass_side.png` and `dirt.png^grass_side.png` textures local process_inventory_cube = function(texture_string) if not texture_string:sub(1,14) == "[inventorycube" then return texture_string end local split = texture_string:split("{") local left = split[3] -- the "front" of the cube we're seeing in the inventory list if left == nil then -- in case something weird happens, don't crash. return texture_string end left = left:gsub("&", "^") return left end local get_icon = function(item) local def = minetest.registered_items[item] local returnstring = "unknown_item.png" if def == nil then return returnstring end local override = override_item_icon[item] if override then return override end local inventory_image = def.inventory_image if inventory_image and inventory_image ~= "" then returnstring = inventory_image else local tiles = def.tiles if tiles then local tilecount = #tiles -- Textures of node; +Y, -Y, +X, -X, +Z, -Z local selected_tile = tiles[math.min(5,tilecount)] if type(selected_tile) == "string" then returnstring = selected_tile else local tile_name = selected_tile.name if tile_name then returnstring = tile_name end end end end returnstring = process_inventory_cube(returnstring) -- Formspec tables can't handle image compositing and modifiers local found_caret = returnstring:find("%^") if found_caret then returnstring = returnstring:sub(1, found_caret-1) end override = override_image_icon[returnstring] if override then return override end return minetest.formspec_escape(returnstring) end -- Exposed so that the purge_unknowns command can use it. commoditymarket.get_icon = get_icon local truncate_string = function(target, length) local strip_target = minetest.strip_colors(target) if strip_target:len() > length then return string.sub(strip_target,1,length-2).."..." end return strip_target end local get_translated_string = minetest.get_translated_string local lang_code = nil -- it's ugly using this to pass the language code into this function, but it's efficient and works well for the sort functions. local get_item_description = function(item) local description = S("Unknown Item") local def = minetest.registered_items[item] if def then description = def.description if get_translated_string then description = get_translated_string(lang_code, description) end end -- We keep the first line of the description, which is the game name of an item (ex: "Dirt" for the item "default:dirt") description = description:split("\n", false, 2)[1] return truncate_string(minetest.formspec_escape(description),truncate_item_names_to) end -- Inventory formspec ------------------------------------------------------------------------------------- local inventory_item_comp = function(invitem1, invitem2) return invitem1.item < invitem2.item end local inventory_desc_comp = function(invitem1, invitem2) return invitem1.description < invitem2.description end local get_account_formspec = function(market, account) local show_itemnames = account.show_itemnames == "true" local show_descriptions = account.show_descriptions == "true" local show_icons = global_enable_item_icons and ((account.show_icons or "true") == "true") local market_def = market.def if account.inventory_item_selected and not account.inventory[account.inventory_item_selected] then -- This can happen if a player has put an item up for sale that he had previously selected in this screen, -- thus emptying his inventory of it. account.inventory_item_selected = nil end local inventory = {} local inventory_count = 0 for item, quantity in pairs(account.inventory) do local icon if show_icons then icon = get_icon(item) end table.insert(inventory, {item=item, quantity=quantity, icon=icon, description=get_item_description(item)}) inventory_count = inventory_count + quantity end if show_itemnames then table.sort(inventory, inventory_item_comp) elseif show_descriptions then table.sort(inventory, inventory_desc_comp) end local formspec = { "size[10,10]" .."tabheader[0,0;tabs;"..market_def.description..","..S("Your Inventory")..","..S("Market Orders")..";2;false;true]" } formspec[#formspec+1] = "tablecolumns[" if show_icons then formspec[#formspec+1] = "image" for i=1, #inventory, 2 do formspec[#formspec+1] = ","..i.."="..inventory[i].icon end formspec[#formspec+1] = ";" end if show_itemnames then formspec[#formspec+1] = "text;" end if show_descriptions then formspec[#formspec+1] = "text;" end formspec[#formspec+1] = "text,align=center" if show_icons then formspec[#formspec+1] = ";image" for i=2, #inventory, 2 do formspec[#formspec+1] = ","..i.."="..inventory[i].icon end end if show_itemnames then formspec[#formspec+1] = ";text" end if show_descriptions then formspec[#formspec+1] = ";text" end formspec[#formspec+1] = ";text,align=center]" .."tooltip[inventory;"..S("All the items you've transferred to the market to sell and the items you've\npurchased with buy orders. Double-click on an item to bring it back into your\npersonal inventory.").."]" .."table[0,0;9.75,4;inventory;" if show_icons then formspec[#formspec+1] = "0," end if show_itemnames then formspec[#formspec+1] = S("Item").."," end if show_descriptions then formspec[#formspec+1] = S("Description").."," end formspec[#formspec+1] = S("Quantity") if show_icons then formspec[#formspec+1] = ",0" end if show_itemnames then formspec[#formspec+1] = ","..S("Item") end if show_descriptions then formspec[#formspec+1] = ","..S("Description") end formspec[#formspec+1] = ","..S("Quantity") for i, entry in ipairs(inventory) do if show_icons then formspec[#formspec+1] = "," .. i end if show_itemnames then formspec[#formspec+1] = "," .. truncate_string(entry.item, truncate_item_names_to) end if show_descriptions then -- no need to formspec_escape description here, it gets done when it's initially added to the inventory table formspec[#formspec+1] = "," .. entry.description end formspec[#formspec+1] = "," .. entry.quantity end formspec[#formspec+1] = "]container[0.5,4.5]" if account.inventory_item_selected then formspec[#formspec+1] = "button[0,0;1.3,1;retrieveitem;"..S("Retrieve:") .."]" .."label[0,0.75;"..get_item_description(account.inventory_item_selected).."]" end formspec[#formspec+1] = "list[detached:commoditymarket:" .. market.name .. ";add;1.2,0;1,1;]" .."label[2.2,0;"..S("Drop items here to\nadd to your account").."]" .."listring[current_player;main]listring[detached:commoditymarket:" .. market.name .. ";add]" if mcl_formspec_itemslots then formspec[#formspec+1] = mcl_formspec_itemslots(1.2,0,1,1) end if market_def.inventory_limit then formspec[#formspec+1] = "label[4.3,0;"..S("Inventory limit:").."\n" .. inventory_count.."/" .. market_def.inventory_limit .. "]" .. "tooltip[4.2,0;1.5,1;"..S("You can still receive purchased items if you've exceeded your inventory limit,\nbut you won't be able to transfer items from your personal inventory into\nthe market until you've emptied it back down below the limit again.").."]" end local x_start_inventory = 1 -- default for 8 slot per rows if inventory_row_size == 9 then x_start_inventory = 0.5 end formspec[#formspec+1] = "label[6.1,0;"..S("Balance:") .. "\n" .. market_def.currency_symbol .. account.balance .. "]" .."tooltip[6.1,0;3.5,1;"..S("Enter the amount of currency you'd like to withdraw then click the 'Withdraw'\nbutton to convert it into items and transfer it to your personal inventory.").."]" .."field[7.3,0.325;1,1;withdrawamount;;]" .."field_close_on_enter[withdrawamount;false]" .."button[7.9,0;1.2,1;withdraw;"..S("Withdraw").."]" .."container_end[]" .."container["..x_start_inventory..",5.75]list[current_player;main;0,0;"..inventory_row_size..",1;]" .."list[current_player;main;0,1.25;"..inventory_row_size..",3;"..inventory_row_size.."]" if mcl_formspec_itemslots then formspec[#formspec+1] = mcl_formspec_itemslots(0,0,inventory_row_size,1) .. mcl_formspec_itemslots(0,1.25,inventory_row_size,3) end formspec[#formspec+1] = "container_end[]" return table.concat(formspec) end -- Market formspec -------------------------------------------------------------------------------------------------------- local compare_market_item = function(mkt1, mkt2) return mkt1.item < mkt2.item end local compare_market_desc = function(mkt1, mkt2) return get_item_description(mkt1.item) < get_item_description(mkt2.item) end local compare_buy_volume = function(mkt1, mkt2) return mkt1.buy_volume > mkt2.buy_volume end local compare_buy_max = function(mkt1, mkt2) return ((mkt1.buy_orders[#mkt1.buy_orders] or {}).price or -2^30) > ((mkt2.buy_orders[#mkt2.buy_orders] or {}).price or -2^30) end local compare_sell_volume = function(mkt1, mkt2) return mkt1.sell_volume > mkt2.sell_volume end local compare_sell_min = function(mkt1, mkt2) return ((mkt1.sell_orders[#mkt1.sell_orders] or {}).price or 2^31) < ((mkt2.sell_orders[#mkt2.sell_orders] or {}).price or 2^31) end local compare_last_price = function(mkt1, mkt2) return (mkt1.last_price or 2^31) < (mkt2.last_price or 2^31) end local sort_marketlist = function(item_list, account) -- I think tonumber is now redundant here, leaving it in in case upgrading a world that has text recorded in this field for an existing player account local sort_by = tonumber(account.sort_markets_by_column) if sort_by == nil then return end local show_itemnames = account.show_itemnames == "true" local show_icons = global_enable_item_icons and ((account.show_icons or "true") == "true") local icon_displace = 0 if show_icons then icon_displace = 1 end local itemname_displace = 0 if show_itemnames then itemname_displace = 1 end -- "Icon,Item,Description,#00FF00,Buy Vol,Buy Max,#FF0000,Sell Vol,Sell Min,Last Price" if sort_by == 1 + icon_displace and show_itemnames then table.sort(item_list, compare_market_item) elseif sort_by == 1 + icon_displace + itemname_displace then table.sort(item_list, compare_market_desc) elseif sort_by == 3 + icon_displace + itemname_displace then table.sort(item_list, compare_buy_volume) elseif sort_by == 4 + icon_displace + itemname_displace then table.sort(item_list, compare_buy_max) elseif sort_by == 6 + icon_displace + itemname_displace then table.sort(item_list, compare_sell_volume) elseif sort_by == 7 + icon_displace + itemname_displace then table.sort(item_list, compare_sell_min) elseif sort_by == 8 + icon_displace + itemname_displace then table.sort(item_list, compare_last_price) elseif sort_by == 9 + icon_displace + itemname_displace then table.sort(item_list, function(mkt1, mkt2) -- Define locally so that account is available return (account.inventory[mkt1.item] or 0) > (account.inventory[mkt2.item] or 0) end) end end local make_marketlist = function(market, account) local market_list = {} local search_filter = account.search or "" for item, row in pairs(market.orders_for_items) do if (search_filter == "" or string.find(item, search_filter)) then if account.filter_participating == "true" then local found = false for _, order in ipairs(row.buy_orders) do if account == order.account then found = true break end end if not found then for _, order in ipairs(row.sell_orders) do if account == order.account then found = true break end end end if found then table.insert(market_list, row) end else table.insert(market_list, row) end end end sort_marketlist(market_list, account) return market_list end local get_account_name = function(target_account, this_account, anonymous) if anonymous and target_account ~= this_account then return "" end return target_account.name end local get_market_formspec = function(market, account) local market_def = market.def local selected = account.selected local market_list = make_marketlist(market, account) local show_itemnames = account.show_itemnames == "true" local show_descriptions = account.show_descriptions == "true" local show_icons = global_enable_item_icons and ((account.show_icons or "true") == "true") local anonymous = market_def.anonymous local formspec = { "size[10,10]" .."tabheader[0,0;tabs;"..market_def.description..","..S("Your Inventory")..","..S("Market Orders")..";3;false;true]" } -- column definitions formspec[#formspec+1] = "tablecolumns[" if show_icons then formspec[#formspec+1] = "image" -- icon for i, row in ipairs(market_list) do formspec[#formspec+1] = "," .. i .. "=" .. get_icon(row.item) end formspec[#formspec+1] = ";" end if show_itemnames then formspec[#formspec+1] = "text;" -- itemname end if show_descriptions then formspec[#formspec+1] = "text;" -- description end formspec[#formspec+1] = "color,span=2;" .."text,align=right,tooltip="..S("Number of items there's demand for in the market.")..";" .."text,align=right,tooltip="..S("Maximum price being offered to buy one of these.")..";" .."color,span=2;" .."text,align=right,tooltip="..S("Number of items available for sale in the market.")..";" .."text,align=right,tooltip="..S("Minimum price being demanded to sell one of these.")..";" .."text,align=right,tooltip="..S("Price paid for one of these the last time one was sold.")..";" .."text,align=right,tooltip="..S("Quantity of this item that you have in your inventory ready to sell.").."]" .."table[0,0;9.75,5;summary;" if show_icons then formspec[#formspec+1] = "0,"-- icon end -- header row if show_itemnames then formspec[#formspec+1] = "Item," -- itemname end if show_descriptions then formspec[#formspec+1] = S("Description") .. "," end formspec[#formspec+1] = "#00FF00,"..S("Buy Vol")..","..S("Buy Max") ..",#FF0000,"..S("Sell Vol")..","..S("Sell Min")..","..S("Last Price")..","..S("Inventory") local selected_idx local selected_row -- Show list of item market summaries for i, row in ipairs(market_list) do if show_icons then formspec[#formspec+1] = ","..i -- icon end if show_itemnames then formspec[#formspec+1] = "," .. truncate_string(row.item, truncate_item_names_to) end if show_descriptions then formspec[#formspec+1] = "," .. get_item_description(row.item) end formspec[#formspec+1] = ",#00FF00," .. row.buy_volume .. "," .. ((row.buy_orders[#row.buy_orders] or {}).price or "-") .. ",#FF0000," .. row.sell_volume .. "," .. ((row.sell_orders[#row.sell_orders] or {}).price or "-") .. "," .. (row.last_price or "-") .. "," .. (account.inventory[row.item] or "-") -- we happen to be processing the row that matches the item this player has selected. Record that. if selected == row.item then selected_row = row selected_idx = i + 1 end end -- a row that's visible is marked as the selected item, so make it selected in the formspec if selected_row then formspec[#formspec+1] = ";"..selected_idx end formspec[#formspec+1] = "]" -- search field formspec[#formspec+1] = "container[2.5,5]field_close_on_enter[search_filter;false]" .."field[0,0.85;2.5,1;search_filter;;"..minetest.formspec_escape(account.search or "").."]" .."image_button[2.05,0.65;0.8,0.8;commoditymarket_search.png;apply_search;]" .."image_button[2.7,0.65;0.8,0.8;commoditymarket_clear.png;clear_search;]" .."checkbox[1.77,0;filter_participating;"..S("My orders")..";".. account.filter_participating .."]" .."tooltip[filter_participating;"..S("Select this to show only the markets where you have either a buy or a sell order pending.").."]" .."tooltip[search_filter;"..S("Enter substring to search item identifiers for.").."]" .."tooltip[apply_search;"..S("Apply search to outputs.").."]" .."tooltip[clear_search;"..S("Clear search.").."]" .."container_end[]" -- if a visible item market is selected, show the orders for it in detail if selected_row then local current_time = minetest.get_gametime() local desc_display = get_item_description(selected) -- player inventory for this item and for currency formspec[#formspec+1] = "label[0.1,5.1;"..desc_display.."\n"..S("In inventory:").." " .. tostring(account.inventory[selected] or 0) .."\n"..S("Balance:").." "..market_def.currency_symbol..account.balance .."]" -- buy/sell controls .. "container[6.1,5]" local sell_limit = market_def.sell_limit if sell_limit then local total_sell = 0 for item, orders in pairs(market.orders_for_items) do for _, order in ipairs(orders.sell_orders) do if order.account == account then total_sell = total_sell + order.quantity end end end formspec[#formspec+1] = "label[0,0;"..S("Sell limit:").." ".. total_sell .. "/" .. sell_limit .."]" .."tooltip[0,0;2,0.25;"..S("This market limits the total number of items a given seller can have for sale at a time.\nYou have @1 items remaining. Cancel old sell orders to free up space.", sell_limit-total_sell).."]" end -- Buy, sell, quantity and price button formspec[#formspec+1] = "tooltip[0,0.25;3.75,1;"..S("Use these fields to enter buy and sell orders for the selected item.").."]" .."button[0,0.55;1,1;buy;"..S("Buy").."]field[1.2,0.85;1,1;quantity;"..S("Quantity")..";]" .."field[2.1,0.85;1,1;price;"..S("Price per")..";]button[2.7,0.55;1,1;sell;"..S("Sell").."]" .."field_close_on_enter[quantity;false]field_close_on_enter[price;false]" .."container_end[]" -- table of buy and sell orders .."tablecolumns[color;text;" .."text,align=right,tooltip="..S("The price per item in this order.")..";" .."text,align=right,tooltip="..S("The total amount of items in this particular order.")..";" .."text,align=right,tooltip="..S("The total amount of items available at this price accounting for the other orders also currently being offered.")..";" .."text,tooltip="..S("The name of the player who placed this order.\nDouble-click your own orders to cancel them.")..";" .."text,align=right,tooltip="..S("How many days ago this order was placed.").."]" .."table[0,6.5;9.75,3.5;orders;#FFFFFF,"..S("Order")..","..S("Price")..","..S("Quantity")..","..S("Total Volume")..","..S("Player")..","..S("Days Old") local sell_volume = selected_row.sell_volume for i, sell in ipairs(selected_row.sell_orders) do formspec[#formspec+1] = ",#FF0000,"..S("Sell").."," ..sell.price.."," ..sell.quantity.."," ..sell_volume.."," ..get_account_name(sell.account, account, anonymous).."," ..math.floor((current_time-sell.timestamp)/86400) sell_volume = sell_volume - sell.quantity end local buy_volume = 0 local buy_orders = selected_row.buy_orders local buy_count = #buy_orders -- Show buy orders in reverse order for i = buy_count, 1, -1 do local buy = buy_orders[i] buy_volume = buy_volume + buy.quantity formspec[#formspec+1] = ",#00FF00,"..S("Buy").."," ..buy.price.."," ..buy.quantity.."," ..buy_volume.."," ..get_account_name(buy.account, account, anonymous).."," ..math.floor((current_time-buy.timestamp)/86400) end formspec[#formspec+1] = "]" else formspec[#formspec+1] = "label[0.1,5.1;"..S("Select an item to view or place orders.").."]" end return table.concat(formspec) end ------------------------------------------------------------------------------------- -- Information formspec --{item=item, quantity=quantity, price=price, purchaser=purchaser, seller=seller, timestamp = minetest.get_gametime()} local log_to_string = function(market, log_entry, account) local anonymous = market.def.anonymous local purchaser = log_entry.purchaser local seller = log_entry.seller local purchaser_name if purchaser == seller then purchaser_name = S("yourself") elseif anonymous and purchaser ~= account then purchaser_name = S("someone") elseif purchaser == account then purchaser_name = S("you") else purchaser_name = purchaser.name end local seller_name if anonymous and seller ~= account then seller_name = S("someone") elseif seller == account then seller_name = S("you") else seller_name = seller.name end local colour local new local last_acknowledged = account.last_acknowledged or 0 if log_entry.timestamp > last_acknowledged then colour = "#FFFF00" new = true else colour = "#FFFFFF" new = false end local show_itemnames = account.show_itemnames == "true" local itemname = log_entry.item if not show_itemnames then local item_def = minetest.registered_items[log_entry.item] if item_def then itemname = get_item_description(log_entry.item) end end local currency_symbol = market.def.currency_symbol return colour .. S("On day @1 @2 sold @3 @4 to @5 at @6@7 each for a total of @8@9.", math.ceil(log_entry.timestamp/86400), seller_name, log_entry.quantity, itemname, purchaser_name, currency_symbol, log_entry.price, currency_symbol, log_entry.quantity*log_entry.price), new end local get_info_formspec = function(market, account) local formspec = { "size[10,10]" .."tabheader[0,0;tabs;"..market.def.description..","..S("Your Inventory")..","..S("Market Orders")..";1;false;true]" .."textarea[0.75,0.5;9.25,2.5;;"..S("Description:")..";"..market.def.long_description.."]" .."label[0.5,2.6;"..S("Your Recent Purchases and Sales:").."]" .."textlist[0.5,3.1;8.75,4;log_entries;" } if next(account.log) then local new = false for _, log_entry in ipairs(account.log) do local log_string, new_log = log_to_string(market, log_entry, account) new = new or new_log formspec[#formspec+1] = log_string formspec[#formspec+1] = "," end formspec[#formspec] = "]" -- Note: there's no +1 here deliberately, that way the "]" overwrites the last comma added by the loop above. if new then formspec[#formspec+1] = "button[7.1,7.3;2,0.5;acknowledge_log;"..S("Mark logs as read").."]" .. "tooltip[acknowledge_log;"..S("Log entries in yellow are new since last time you marked your log as read.").."]" end else formspec[#formspec+1] = "#CCCCCC"..S("No logged activities in this market yet.").."]" end local show_itemnames = account.show_itemnames or "false" formspec[#formspec+1] = "]container[0.5, 7.6]label[0,0;"..S("Settings")..":]checkbox[0,0.25;show_itemnames;"..S("Show Itemnames")..";" ..show_itemnames.."]" local show_descriptions = account.show_descriptions or "false" formspec[#formspec+1] = "checkbox[2.1,0.25;show_descriptions;"..S("Show Descriptions")..";"..show_descriptions.."]" if global_enable_item_icons then local show_icons = account.show_icons or "true" formspec[#formspec+1] = "checkbox[4.2,0.25;show_icons;"..S("Show Icons")..";"..show_icons.."]" end formspec[#formspec+1] = "container_end[]" return table.concat(formspec) end --------------------------------------------------------------------------------------- commoditymarket.get_formspec = function(market, account) local tab = account.tab lang_code = minetest.get_player_information(account.name).lang_code if tab == 1 then return get_info_formspec(market, account) elseif tab == 2 then return get_account_formspec(market, account) else return get_market_formspec(market, account) end end ------------------------------------------------------------------------------------ -- Handling recieve_fields local add_to_player_inventory = function(name, item, amount) local playerinv = minetest.get_inventory({type="player", name=name}) local not_full = true while amount > 0 and not_full do local stack = ItemStack(item .. " " .. amount) amount = amount - stack:get_count() local leftover = playerinv:add_item("main", stack) if leftover:get_count() > 0 then amount = amount + leftover:get_count() return amount end end return amount end local give_back_item = function(name, item, account) local amount = account.inventory[item] local remaining = add_to_player_inventory(name, item, amount) if remaining == 0 then account.inventory[item] = nil account.inventory_item_selected = nil else account.inventory[item] = remaining end if remaining ~= amount then return true end return false end minetest.register_on_player_receive_fields(function(player, formname, fields) local formname_split = formname:split(":") if formname_split[1] ~= "commoditymarket" then return false end local market = commoditymarket.registered_markets[formname_split[2]] if not market then return false end local name = formname_split[3] if name ~= player:get_player_name() then return false end local account = market:get_account(name) local show_icons = global_enable_item_icons and ((account.show_icons or "true") == "true") local something_changed = false if fields.tabs then account.tab = tonumber(fields.tabs) something_changed = true end -- player clicked on an item in the market summary table if fields.summary then local summaryevent = minetest.explode_table_event(fields.summary) if summaryevent.type == "DCL" or summaryevent.type == "CHG" then if summaryevent.row == 1 then -- header clicked, sort by column local column = tonumber(summaryevent.column) if not (column == 1 and show_icons) then -- ignore clicks on the icon column header account.sort_markets_by_column = column end else -- item clicked, recreate the list to find out which one local marketlist = make_marketlist(market, account) local selected = marketlist[summaryevent.row-1] if selected then account.selected = selected.item end end elseif summaryevent.type == "INV" then account.selected = nil end something_changed = true end if fields.orders then local ordersevent = minetest.explode_table_event(fields.orders) if ordersevent.type == "DCL" and ordersevent.column > 0 then local selected_idx = ordersevent.row - 1 -- account for header local selected_row = market.orders_for_items[account.selected] -- sell orders come first local sell_orders = selected_row.sell_orders local sell_order_count = #sell_orders local selected_order if selected_idx <= sell_order_count then -- if the index is within the range of sell orders, selected_order = sell_orders[selected_idx] if selected_order and selected_order.account == account then -- and the order belongs to the current player, market:cancel_sell(account.selected, selected_order) -- cancel it something_changed = true end else -- otherwise we're in the buy group, shift the index up by sell_order_count and reverse index order local buy_orders = selected_row.buy_orders local buy_orders_count = #buy_orders selected_order = buy_orders[buy_orders_count - (selected_idx - sell_order_count - 1)] if selected_order and selected_order.account == account then market:cancel_buy(account.selected, selected_order) something_changed = true end end end end if fields.buy then local quantity = tonumber(fields.quantity) local price = tonumber(fields.price) if price ~= nil and quantity ~= nil then market:buy(name, account.selected, quantity, price) something_changed = true end end if fields.sell then local quantity = tonumber(fields.quantity) local price = tonumber(fields.price) if price ~= nil and quantity ~= nil then market:sell(name, account.selected, quantity, price) something_changed = true end end -- player clicked in their inventory table, may need to give him his stuff back if fields.inventory then local invevent = minetest.explode_table_event(fields.inventory) local invevent_type = invevent.type if invevent_type == "INV" then -- no row selected if account.inventory_item_selected then account.inventory_item_selected = nil something_changed = true end elseif invevent.column > 0 then -- Find the item that was clicked on local col_count = 8 local show_itemnames = account.show_itemnames == "true" local show_descriptions = account.show_descriptions == "true" if not show_itemnames then col_count = col_count - 2 end if not show_icons then col_count = col_count - 2 end if not show_descriptions then col_count = col_count -2 end local index = math.floor(((invevent.row-1)*col_count + invevent.column - 1)/(col_count/2)) - 1 local account = market:get_account(name) -- build a local copy of the inventory that would be displayed in the formspec so we can -- figure out what item the index we were given is pointing to local inventory = {} lang_code = minetest.get_player_information(account.name).lang_code -- needed by get_item_description for item, quantity in pairs(account.inventory) do table.insert(inventory, {item=item, quantity=quantity, description=get_item_description(item)}) end if show_itemnames then table.sort(inventory, inventory_item_comp) elseif show_descriptions then table.sort(inventory, inventory_desc_comp) end if inventory[index] then local item = inventory[index].item if account.inventory_item_selected ~= item then -- update selected item account.inventory_item_selected = item something_changed = true end if invevent_type == "DCL" then -- double-click, give the item back immediately something_changed = something_changed or give_back_item(name, item, account) end end end end if fields.retrieveitem and account.inventory_item_selected then local account = market:get_account(name) something_changed = give_back_item(name, account.inventory_item_selected, account) end if fields.withdraw or fields.key_enter_field == "withdrawamount" then local withdrawvalue = tonumber(fields.withdrawamount) if withdrawvalue then local account = market:get_account(name) withdrawvalue = math.min(withdrawvalue, account.balance) for _, currency in ipairs(market.def.currency_ordered) do this_unit_amount = math.floor(withdrawvalue/currency.amount) if this_unit_amount > 0 then local remaining = add_to_player_inventory(name, currency.item, this_unit_amount) local value_given = (this_unit_amount - remaining) * currency.amount account.balance = account.balance - value_given withdrawvalue = withdrawvalue - value_given something_changed = true end end end end if fields.search_filter then local value = string.lower(fields.search_filter) if account.search ~= value then account.search = value end end local process_checkbox = function(property_name, fields, account) if (fields[property_name] == "true" and account[property_name] ~= "true") or (fields[property_name] == "false" and account[property_name] ~= "false") then account[property_name] = fields[property_name] return true end return false end if process_checkbox("filter_participating", fields, account) then something_changed = true end if process_checkbox("show_itemnames", fields, account) then something_changed = true end if process_checkbox("show_descriptions", fields, account) then something_changed = true end if process_checkbox("show_icons", fields, account) then something_changed = true end if fields.acknowledge_log then account.last_acknowledged = minetest.get_gametime() something_changed = true end if fields.apply_search or fields.key_enter_field == "search_filter" then something_changed = true end if fields.clear_search then account.search = "" something_changed = true end if something_changed then minetest.show_formspec(name, formname, market:get_formspec(account)) end end)