forked from minetest-mods/global_exchange
		
	Compare commits
	
		
			7 Commits
		
	
	
		
			0de7a34fcf
			...
			81517a5b3d
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 81517a5b3d | |||
| 
						 | 
					73407cd6fa | ||
| 
						 | 
					b52e813d45 | ||
| 
						 | 
					5cfb580545 | ||
| 
						 | 
					141de27c97 | ||
| 
						 | 
					808b3cbf04 | ||
| 
						 | 
					2a5a726cc0 | 
							
								
								
									
										127
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										127
									
								
								README.md
									
									
									
									
									
								
							@@ -15,87 +15,86 @@ Nodes
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Using the Exchange
 | 
					Using the Exchange
 | 
				
			||||||
==================
 | 
					==================
 | 
				
			||||||
Main Screen
 | 
					Overview
 | 
				
			||||||
-----------
 | 
					-----------
 | 
				
			||||||
The first screen you see is where you can search and post new buy/sell orders.
 | 
					At the top of the exchange form there are four tabs: Market Summary, Buy, Sell,
 | 
				
			||||||
Here is an overview of each element:
 | 
					and Your Orders. Pressing each tab will take you to the indicated screen as
 | 
				
			||||||
 - Market Summary - Pressing this will take you to the market summary screen.
 | 
					described below.
 | 
				
			||||||
 - Your Orders - This will take you to a screen where you can view and cancel
 | 
					 | 
				
			||||||
 your existing orders.
 | 
					 | 
				
			||||||
 - Item - This field is for entering the item name (e.g. default:cobble) of the
 | 
					 | 
				
			||||||
 item you want to search or post an order for.
 | 
					 | 
				
			||||||
 - Amount - This field is for entering how many of the item you want to buy/sell
 | 
					 | 
				
			||||||
 when posting an order. It has no purpose in searches.
 | 
					 | 
				
			||||||
 - Select Item - This button takes you to a screen for choosing your item
 | 
					 | 
				
			||||||
 graphically, instead of manually typing an item name.
 | 
					 | 
				
			||||||
 - Rate - This field is for entering the desired price per item when posting an
 | 
					 | 
				
			||||||
 order. For buy orders, this is the maximum price - your order will also accept
 | 
					 | 
				
			||||||
 items that are cheaper. For sell orders, this is the minimum price - your
 | 
					 | 
				
			||||||
 order will also accept buyers that are willing to pay more. The Rate field has
 | 
					 | 
				
			||||||
 no effect on searches
 | 
					 | 
				
			||||||
 - Search - This button searches existing orders for the selected item. If you
 | 
					 | 
				
			||||||
 have the "Sell" box checked, it will only display buy orders, and will display
 | 
					 | 
				
			||||||
 them in descending rate. If you have the box unchecked, it will show sell
 | 
					 | 
				
			||||||
 orders in order of ascending rate.
 | 
					 | 
				
			||||||
 - Post Order - This posts a new order for the item with the given amount and
 | 
					 | 
				
			||||||
 rate. If the "Sell" box is checked, this is a sell order, so the exchange will
 | 
					 | 
				
			||||||
 remove the items from your inventory. If it's unchecked, you are making a buy
 | 
					 | 
				
			||||||
 order, so it will deduct credits from your account. If there are already
 | 
					 | 
				
			||||||
 matching orders, it will immediately fill your order up to the amount possible,
 | 
					 | 
				
			||||||
 and the remainder will stay as a new order.
 | 
					 | 
				
			||||||
 - Sell - This checkbox determines what kind of orders to search for, and also
 | 
					 | 
				
			||||||
 what kind of order you are posting.
 | 
					 | 
				
			||||||
 - Search Results - This will display the results of your search. Clicking on an
 | 
					 | 
				
			||||||
 element here will automatically fill the "Amount" and "Rate" fields, so that if
 | 
					 | 
				
			||||||
 you click "Post Order", it will match the order you clicked.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
Market Summary
 | 
					Market Summary
 | 
				
			||||||
--------------
 | 
					--------------
 | 
				
			||||||
This summarizes the various items available on the exchange. From left to right,
 | 
					This summarizes the various items available on the exchange. From left to right,
 | 
				
			||||||
the columns display the item name, the description (what is shown in inventory),
 | 
					the columns display the item name, the description (what is shown in inventory),
 | 
				
			||||||
the amount requested by buyers, the maximum rate offered by buyers, the amount
 | 
					the tool wear if applicable, the amount requested by buyers, the maximum rate
 | 
				
			||||||
offered by sellers, and the minimum rate offered by sellers. It is updated
 | 
					offered by buyers, the amount offered by sellers, and the minimum rate offered
 | 
				
			||||||
periodically.
 | 
					by sellers. It is updated periodically.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Buy
 | 
				
			||||||
 | 
					-----------
 | 
				
			||||||
 | 
					The upper-left corner shows the open order book for the selected item,
 | 
				
			||||||
 | 
					consisting of the three best sell offers (lowest asking price and least tool
 | 
				
			||||||
 | 
					wear) followed by the three best buy offers (highest offer price and highest
 | 
				
			||||||
 | 
					acceptable tool wear). To the right is an image of the selected item, entry
 | 
				
			||||||
 | 
					fields for the quantity and offer price, and a drop-down field for the desired
 | 
				
			||||||
 | 
					tool wear: "New" (no wear), "Good" (up to 10% wear), "Worn" (up to 50% wear),
 | 
				
			||||||
 | 
					and "Junk" (any amount of wear). To qualify, a seller must offer an item with
 | 
				
			||||||
 | 
					wear equal to or less than the indicated amount (e.g. if you select "Good" you
 | 
				
			||||||
 | 
					may receive an item with only 5% wear, but not one 15% worn). Similarly, the
 | 
				
			||||||
 | 
					offer price is the highest price you are willing to pay; in practice you may
 | 
				
			||||||
 | 
					pay less depending on the open sell orders. Orders are always fulfilled at the
 | 
				
			||||||
 | 
					price listed in the open order book.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					At the bottom of the form is a grid similar to the creative inventory. You can
 | 
				
			||||||
 | 
					page through the available items using the "<<" and ">>" buttons and select the
 | 
				
			||||||
 | 
					item you wish to buy simply by clicking on it.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					To finalize the order, press the "Place Bid" button. Note that it is permitted
 | 
				
			||||||
 | 
					to enter an order which exceeds your available funds at the time the order is
 | 
				
			||||||
 | 
					placed. As matching sell orders are located, the order will be fulfulled until
 | 
				
			||||||
 | 
					there are insufficient funds to cover the next item, at which point the
 | 
				
			||||||
 | 
					remainder of the order will be cancelled.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Sell
 | 
				
			||||||
 | 
					-----------
 | 
				
			||||||
 | 
					As with the Buy screen, the upper-left corner shows the open order book for
 | 
				
			||||||
 | 
					the selected item. Instead of a static image, the selected item appears as a
 | 
				
			||||||
 | 
					1x1 inventory. At the bottom of the screen is the standard 8x4 player
 | 
				
			||||||
 | 
					inventory. To choose the type and quantity of items to sell, move the items
 | 
				
			||||||
 | 
					from your player inventory to the exchange inventory. Below the exchange
 | 
				
			||||||
 | 
					inventory is an entry field for the desired asking price. To finalize the
 | 
				
			||||||
 | 
					order, press the "Sell" button. As with buy orders, the asking price is the
 | 
				
			||||||
 | 
					lowest price you are willing to accept, and you may receive a higher price
 | 
				
			||||||
 | 
					depending on the open buy orders.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Unsold items in the exchange inventory are returned to the player inventory
 | 
				
			||||||
 | 
					when the form is closed, or to the player's Inbox if the inventory is full.
 | 
				
			||||||
 | 
					(However, items in the exchange inventory at the time of a server crash, or
 | 
				
			||||||
 | 
					other abnormal condition, may be lost.)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Your Orders
 | 
					Your Orders
 | 
				
			||||||
-----------
 | 
					-----------
 | 
				
			||||||
This screen lets you see and cancel your orders. To cancel an order, click the
 | 
					This screen lets you see and cancel your orders. To cancel an order, click the
 | 
				
			||||||
order and press the "Cancel" button.
 | 
					order and press the "Cancel" button. If the order was sell order, the items
 | 
				
			||||||
 | 
					held in "escrow" are returned to your player inventory. If any of the items do
 | 
				
			||||||
Select Item
 | 
					not fit in the inventory they are placed in your Inbox instead to be claimed
 | 
				
			||||||
-----------
 | 
					later.
 | 
				
			||||||
This displays a creative-style inventory menu for selecting an item for searches
 | 
					 | 
				
			||||||
or posting orders. To select an item, drag it from the inventory to the box near
 | 
					 | 
				
			||||||
the bottom of the form
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
Buying/Selling
 | 
					Buying/Selling
 | 
				
			||||||
==============
 | 
					==============
 | 
				
			||||||
Once you have opened the exchange, you have a few options. If you don't already
 | 
					Once you have opened the exchange, you have a few options. If you don't already
 | 
				
			||||||
know what you want to buy or sell, you can look at the Market Summary to get a
 | 
					know what you want to buy or sell, you can look at the Market Summary to get a
 | 
				
			||||||
glance at what people are offering. After you have decided on what you are
 | 
					glance at what people are offering. After you have decided on what you are
 | 
				
			||||||
going to do, return to the exchange page.
 | 
					going to do, return to the Buy or Sell page.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
If you are selling an item, you should check the "Sell" checkbox. Otherwise,
 | 
					When selling, the Ask field is the minimum price you will accept for each
 | 
				
			||||||
leave it unchecked. Next, you need to select the item you want to deal in. There
 | 
					item. When buying, the Bid field determines the maximum amount you are willing
 | 
				
			||||||
are two ways: typing the item name (e.g. default:cobble) in manually to the item
 | 
					to pay. If there are matching offers (i.e. there are one or more offers with a
 | 
				
			||||||
field, or using the "Select Item" menu. If you haven't already decided on a price,
 | 
					price at least as good and a compatible tool wear level), then that part of
 | 
				
			||||||
or you want to make sure your order is filled quickly, you can conduct a search.
 | 
					your offer will immediately be filled. For example, if you post a buy order
 | 
				
			||||||
To do this, click the "Search" button. This will give you a list of results. If
 | 
					for 10 cobblestone at 5 credits each, and there is a sell offer for 5
 | 
				
			||||||
you checked the "Sell" box, then these will be buy orders, and will show the
 | 
					cobblestone at 3 credits each, it will give you 5 cobble immediately at a
 | 
				
			||||||
maximum price per item each buyer is willing to accept. Otherwise, these will be
 | 
					total cost of 15 credits (the order-book price), and leave an order on the
 | 
				
			||||||
sell orders, displaying the minimum price each seller will accept. If you click
 | 
					exchange for 5 more cobblestone at 5 credits each.
 | 
				
			||||||
on a search result, it will automatically fill your Amount and Rate fields to
 | 
					 | 
				
			||||||
match.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The Amount and Rate fields are used to decide how much and how expensively you
 | 
					 | 
				
			||||||
want to make your order. When selling, the Rate field is the minimum price you
 | 
					 | 
				
			||||||
will accept. When buying, it is the maximum. Once everything is filled out how
 | 
					 | 
				
			||||||
you want it, press the "Post Order" button. If there are matching offers (when
 | 
					 | 
				
			||||||
you post a buy/sell offer, there are one or more sell/buy offers with a price
 | 
					 | 
				
			||||||
at least as good), then that part of your offer will immediately be filled. For
 | 
					 | 
				
			||||||
example, if you post a buy order for 10 cobblestone at 5 credits each, and there
 | 
					 | 
				
			||||||
is a sell offer for 5 cobblestone 3 credits each, it will give you 5 cobble
 | 
					 | 
				
			||||||
immediately, and leave an order on the exchange for 5 more cobblestone.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
Once your offer is on the exchange, you can view or cancel it from the "Your
 | 
					Once your offer is on the exchange, you can view or cancel it from the "Your
 | 
				
			||||||
Orders" menu.
 | 
					Orders" menu.
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										262
									
								
								atm.lua
									
									
									
									
									
								
							
							
						
						
									
										262
									
								
								atm.lua
									
									
									
									
									
								
							@@ -1,16 +1,15 @@
 | 
				
			|||||||
-- A telling machine. Call this file with the exchange argument.
 | 
					-- A telling machine. Call this file with the exchange argument.
 | 
				
			||||||
local exchange = ...
 | 
					local exchange, formlib = ...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local atm_form = "global_exchange:atm_form"
 | 
					local atm_form = "global_exchange:atm_form"
 | 
				
			||||||
local atm_pos = {}
 | 
					local atm_pos = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local main_menu =[[
 | 
					local unique = (function(unique_num)
 | 
				
			||||||
size[6,2]
 | 
						return function()
 | 
				
			||||||
button[0,0;2,1;deposit;Deposit]
 | 
							unique_num = unique_num + 1
 | 
				
			||||||
button[2,0;2,1;info;Account Info]
 | 
							return unique_num
 | 
				
			||||||
button[4,0;2,1;wire;Wire Monies]
 | 
						end
 | 
				
			||||||
button[1,1;4,1;transaction_log;Transaction Log]
 | 
					end)(0)
 | 
				
			||||||
]]
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
local coins_convert = {
 | 
					local coins_convert = {
 | 
				
			||||||
	["minercantile:copper_coin"]=1, ["minercantile:silver_coin"]=9, ["minercantile:gold_coin"]=81,
 | 
						["minercantile:copper_coin"]=1, ["minercantile:silver_coin"]=9, ["minercantile:gold_coin"]=81,
 | 
				
			||||||
@@ -18,165 +17,154 @@ local coins_convert = {
 | 
				
			|||||||
	["bitchange:mineninth"]=729, ["bitchange:minecoin"]=6561, ["bitchange:minecoinblock"]=59049
 | 
						["bitchange:mineninth"]=729, ["bitchange:minecoin"]=6561, ["bitchange:minecoinblock"]=59049
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local function logout(x,y)
 | 
					local function deposit_fs(fs, p_name)
 | 
				
			||||||
	return "button[" .. x .. "," .. y .. ";2,1;logout;Log Out]"
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function label(x,y,text)
 | 
					 | 
				
			||||||
	return "label[" .. x .. "," .. y .. ";"
 | 
					 | 
				
			||||||
		.. minetest.formspec_escape(text) .. "]"
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function field(x,y, w,h, name, label, default)
 | 
					 | 
				
			||||||
	return "field[" .. x .. "," .. y .. ";" .. w .. "," .. h .. ";"
 | 
					 | 
				
			||||||
		.. name .. ";" .. minetest.formspec_escape(label) .. ";"
 | 
					 | 
				
			||||||
		.. minetest.formspec_escape(default) .. "]"
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local unique_num = 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function unique()
 | 
					 | 
				
			||||||
	local ret = unique_num
 | 
					 | 
				
			||||||
	unique_num = unique_num + 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return ret
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function deposit_fs(p_name)
 | 
					 | 
				
			||||||
	local balance = exchange:get_balance(p_name)
 | 
						local balance = exchange:get_balance(p_name)
 | 
				
			||||||
	local spos = atm_pos[p_name].x..","..atm_pos[p_name].y..","..atm_pos[p_name].z
 | 
						local spos = atm_pos[p_name].x..","..atm_pos[p_name].y..","..atm_pos[p_name].z
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	local fs
 | 
						fs:size(8,9)
 | 
				
			||||||
 | 
						
 | 
				
			||||||
	if not balance then
 | 
						if not balance then
 | 
				
			||||||
		fs = label(0.5,0.5, "You don't have an account.")
 | 
							fs:label(0.5,0.5, "You don't have an account.")
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
		fs = label(0.5,0.5, "Balance: " .. balance) ..
 | 
							fs:label(0.5,0.5, "Balance: " .. balance)
 | 
				
			||||||
			label(1,1,"Put your coins to credit your account")..
 | 
							fs:label(1,1,"Put your coins to credit your account")
 | 
				
			||||||
			"list[nodemeta:"..spos..";main;3.5,2.5;1,1;]"..
 | 
							fs:list(3.5,2.5, 1,1, "nodemeta:"..spos, "main")
 | 
				
			||||||
			"list[current_player;main;0,4.85;8,1;]"..
 | 
							fs:list(0,4, 8,4, "current_player", "main")
 | 
				
			||||||
			"list[current_player;main;0,6.08;8,3;8]" ..
 | 
							--fs("list[nodemeta:"..spos..";main;3.5,2.5;1,1;]"..
 | 
				
			||||||
			"listring[nodemeta:"..spos..";main]"..
 | 
							--		"list[current_player;main;0,4.85;8,1;]"..
 | 
				
			||||||
			"listring[current_player;main]"
 | 
							--		"list[current_player;main;0,6.08;8,3;8]" ..
 | 
				
			||||||
 | 
							fs("listring[nodemeta:"..spos..";main]"..
 | 
				
			||||||
 | 
									"listring[current_player;main]"
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return "size[8,9]" .. fs .. logout(0.5,2)
 | 
						fs:button(1,2, 2,1, "logout", "Log Out")
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local function info_fs(p_name)
 | 
					local function info_fs(fs, p_name)
 | 
				
			||||||
	local balance = exchange:get_balance(p_name)
 | 
						local balance = exchange:get_balance(p_name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	local fs
 | 
						fs:size(4,3)
 | 
				
			||||||
	if not balance then
 | 
					
 | 
				
			||||||
		fs = label(0.5,0.5, "You don't have an account.")
 | 
						if balance then
 | 
				
			||||||
 | 
							fs:label(0.5,0.5, "Balance: " .. balance)
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
		fs = label(0.5,0.5, "Balance: " .. balance)
 | 
							fs:label(0.5,0.5, "You don't have an account.")
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return "size[4,3]" .. fs .. logout(0.5,2)
 | 
						fs:button(1,2, 2,1, "logout", "Log Out")
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local function wire_fs(p_name)
 | 
					local function wire_fs(fs, p_name)
 | 
				
			||||||
	local balance = exchange:get_balance(p_name)
 | 
						local balance = exchange:get_balance(p_name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	local fs = "size[4,5]" .. logout(0,4)
 | 
						fs:size(4,5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if not balance then
 | 
						if balance then
 | 
				
			||||||
		return fs .. label(0.5,0.5, "You don't have an account.")
 | 
							-- To detect duplicate/stale form submission
 | 
				
			||||||
 | 
							fs:field(-100, -100, 0,0, "trans_id", "", unique())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							fs:label(0.50,0.325, "Balance: " .. balance)
 | 
				
			||||||
 | 
							fs:field(0.75,1.750, 3,1, "recipient", "Send to:", "")
 | 
				
			||||||
 | 
							fs:field(0.75,3.000, 3,1, "amount", "Amount", "")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							fs:button(0,4.25, 2,1, "logout", "Log Out")
 | 
				
			||||||
 | 
							fs:button(2,4.25, 2,1, "send", "Send")
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							fs:button(0,4, 2,1, "logout", "Back")
 | 
				
			||||||
 | 
							fs:label(0.5,0.5, "You don't have an account.")
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					 | 
				
			||||||
	-- To prevent duplicates
 | 
					 | 
				
			||||||
	return fs .. field(-100, -100, 0,0, "trans_id", "", unique()) ..
 | 
					 | 
				
			||||||
		label(0.5,0.5, "Balance: " .. balance) ..
 | 
					 | 
				
			||||||
		field(0.5,1.5, 2,1, "recipient", "Send to:", "") ..
 | 
					 | 
				
			||||||
		field(0.5,2.5, 2,1, "amount", "Amount", "") ..
 | 
					 | 
				
			||||||
		"button[2,4;2,1;send;Send]"
 | 
					 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local function send_fs(p_name, receiver, amt_str)
 | 
					local function send_fs(fs, p_name, receiver, amt_str)
 | 
				
			||||||
	local fs = "size[7,3]"
 | 
						fs:size(10,3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fs:button(4,2, 2,1, "wire", "Back")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	local amt = tonumber(amt_str)
 | 
						local amt = tonumber(amt_str)
 | 
				
			||||||
 | 
						local msg = nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if not amt or amt <= 0 then
 | 
						if not amt or amt <= 0 then
 | 
				
			||||||
		return fs .. label(0.5,0.5, "Invalid transfer amount.") ..
 | 
							msg = "Invalid transfer amount."
 | 
				
			||||||
			"button[0.5,2;2,1;wire;Back]"
 | 
						else
 | 
				
			||||||
 | 
							local succ, err = exchange:transfer_credits(p_name, receiver, amt)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if not succ then
 | 
				
			||||||
 | 
								msg = "Error: " .. err
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								msg = "Successfully sent " .. amt ..
 | 
				
			||||||
 | 
									" credits to " .. receiver .. "."
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	local succ, err = exchange:transfer_credits(p_name, receiver, amt)
 | 
						fs:label(0.5,0.5, msg)
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if not succ then
 | 
					 | 
				
			||||||
		return fs .. label(0.5,0.5, "Error: " .. err) ..
 | 
					 | 
				
			||||||
			"button[0.5,2;2,1;wire;Back]"
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
	return fs.. label(0.5,0.5, "Successfully sent " ..
 | 
					 | 
				
			||||||
		amt .. " credits to " .. receiver) ..
 | 
					 | 
				
			||||||
		"button[0.5,2;2,1;wire;Back]"
 | 
					 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local function log_fs(p_name)
 | 
					local function log_fs(fs, p_name)
 | 
				
			||||||
	local res = {
 | 
						fs:size(14,8)
 | 
				
			||||||
		"size[8,8]label[0,0;Transaction Log]button[0,7;2,1;logout;Log Out]",
 | 
					 | 
				
			||||||
		"tablecolumns[text;text]",
 | 
					 | 
				
			||||||
		"table[0,1;8,6;log_table;Time,Message",
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for i, entry in ipairs(exchange:player_log(p_name)) do
 | 
						fs:label(0,0, "Transaction Log")
 | 
				
			||||||
		i = i*4
 | 
					
 | 
				
			||||||
		res[i] = ","
 | 
						fs:element("tablecolumns", "text", "text")
 | 
				
			||||||
		res[i+1] = tostring(entry.Time)
 | 
					
 | 
				
			||||||
		res[i+2] = ","
 | 
						fs("table[0,0.75;13.75,6.75;log_table;Time,Message")
 | 
				
			||||||
		res[i+3] = entry.Message
 | 
						for _, entry in ipairs(exchange:player_log(p_name)) do
 | 
				
			||||||
 | 
							fs(","):escape_list(entry.Time, entry.Message)
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
	res[#res+1] ="]"
 | 
						fs("]")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return table.concat(res)
 | 
						fs:button(6,7.5, 2,1, "logout", "Log Out")
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function main_menu_fs(fs, p_name)
 | 
				
			||||||
 | 
						fs:size(6,2)
 | 
				
			||||||
 | 
						fs:button(0,0.125, 2,1, "deposit", "Cash Deposit")
 | 
				
			||||||
 | 
						fs:button(2,0.125, 2,1, "info", "Account Info")
 | 
				
			||||||
 | 
						fs:button(4,0.125, 2,1, "wire", "Wire Monies")
 | 
				
			||||||
 | 
						fs:button(0.50,1.125, 5.0,1, "transaction_log", "Transaction Log")
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function show_atm_form(fs_fn, p_name, ...)
 | 
				
			||||||
 | 
						local fs = formlib.Builder()
 | 
				
			||||||
 | 
						fs_fn(fs, p_name, ...)
 | 
				
			||||||
 | 
						minetest.show_formspec(p_name, atm_form, tostring(fs))
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local trans_ids = {}
 | 
					local trans_ids = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
 | 
					minetest.register_on_player_receive_fields(function(player, formname, fields)
 | 
				
			||||||
	if formname ~= atm_form then return end
 | 
						if formname ~= atm_form then return end
 | 
				
			||||||
	if fields.quit then return true end
 | 
						if fields.quit then return true end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	local p_name = player:get_player_name()
 | 
						local p_name = player:get_player_name()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	local this_id = fields.trans_id
 | 
						local this_id = tonumber(fields.trans_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if this_id and this_id == trans_ids[p_name] then
 | 
						if this_id and trans_ids[p_name] and this_id <= trans_ids[p_name] then
 | 
				
			||||||
 | 
							-- Ignore duplicate/stale form submittal
 | 
				
			||||||
		return true
 | 
							return true
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	trans_ids[p_name] = this_id
 | 
						trans_ids[p_name] = this_id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if fields.logout then
 | 
						if fields.logout then
 | 
				
			||||||
		minetest.show_formspec(p_name, atm_form, main_menu)
 | 
							show_atm_form(main_menu_fs, p_name)
 | 
				
			||||||
	end
 | 
						elseif fields.info then
 | 
				
			||||||
 | 
							show_atm_form(info_fs, p_name)
 | 
				
			||||||
	if fields.info then
 | 
						elseif fields.wire then
 | 
				
			||||||
		minetest.show_formspec(p_name, atm_form, info_fs(p_name))
 | 
							show_atm_form(wire_fs, p_name)
 | 
				
			||||||
	end
 | 
						elseif fields.send then
 | 
				
			||||||
 | 
							show_atm_form(send_fs, p_name, fields.recipient, fields.amount)
 | 
				
			||||||
	if fields.wire then
 | 
						elseif fields.transaction_log then
 | 
				
			||||||
		minetest.show_formspec(p_name, atm_form, wire_fs(p_name))
 | 
							show_atm_form(log_fs, p_name)
 | 
				
			||||||
	end
 | 
						elseif fields.deposit then
 | 
				
			||||||
 | 
							show_atm_form(deposit_fs, p_name)
 | 
				
			||||||
	if fields.send then
 | 
					 | 
				
			||||||
		minetest.show_formspec(p_name, atm_form,
 | 
					 | 
				
			||||||
			send_fs(p_name, fields.recipient, fields.amount))
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if fields.transaction_log then
 | 
					 | 
				
			||||||
		minetest.show_formspec(p_name, atm_form, log_fs(p_name))
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if fields.deposit then
 | 
					 | 
				
			||||||
		minetest.show_formspec(p_name, atm_form, deposit_fs(p_name))
 | 
					 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return true
 | 
						return true
 | 
				
			||||||
@@ -210,16 +198,16 @@ minetest.register_node("global_exchange:atm_bottom", {
 | 
				
			|||||||
	selection_box = {
 | 
						selection_box = {
 | 
				
			||||||
		type = "fixed",
 | 
							type = "fixed",
 | 
				
			||||||
		fixed = {
 | 
							fixed = {
 | 
				
			||||||
			{-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
 | 
								{-0.500, -0.500, -0.5000,  0.500, 0.500,  0.50},
 | 
				
			||||||
			{-0.5, 0.5, -0.5, -0.375, 1.125, -0.25},
 | 
								{-0.500,  0.500, -0.5000, -0.375, 1.125, -0.25},
 | 
				
			||||||
			{0.375, 0.5, -0.5, 0.5, 1.125, -0.25},
 | 
								{ 0.375,  0.500, -0.5000,  0.500, 1.125, -0.25},
 | 
				
			||||||
			{-0.5, 0.5, -0.25, 0.5, 1.5, 0.5},
 | 
								{-0.500,  0.500, -0.2500,  0.500, 1.500,  0.50},
 | 
				
			||||||
			{-0.5, 1.125, -0.4375, -0.375, 1.25, -0.25},
 | 
								{-0.500,  1.125, -0.4375, -0.375, 1.250, -0.25},
 | 
				
			||||||
			{0.375, 1.125, -0.4375, 0.5, 1.25, -0.25},
 | 
								{ 0.375,  1.125, -0.4375,  0.500, 1.250, -0.25},
 | 
				
			||||||
			{-0.5, 1.25, -0.375, -0.375, 1.375, -0.25},
 | 
								{-0.500,  1.250, -0.3750, -0.375, 1.375, -0.25},
 | 
				
			||||||
			{0.375, 1.25, -0.375, 0.5, 1.375, -0.25},
 | 
								{ 0.375,  1.250, -0.3750,  0.500, 1.375, -0.25},
 | 
				
			||||||
			{-0.5, 1.375, -0.3125, -0.375, 1.5, -0.25},
 | 
								{-0.500,  1.375, -0.3125, -0.375, 1.500, -0.25},
 | 
				
			||||||
			{0.375, 1.375, -0.3125, 0.5, 1.5, -0.25},
 | 
								{ 0.375,  1.375, -0.3125,  0.500, 1.500, -0.25},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	groups = {cracky=2, atm = 1},
 | 
						groups = {cracky=2, atm = 1},
 | 
				
			||||||
@@ -286,7 +274,8 @@ minetest.register_node("global_exchange:atm_bottom", {
 | 
				
			|||||||
			if succ then
 | 
								if succ then
 | 
				
			||||||
				inv:set_stack(listname, index, nil)
 | 
									inv:set_stack(listname, index, nil)
 | 
				
			||||||
				minetest.log("action", p_name.." put "..nb.." "..stack:get_name() .. " to ATM at " .. minetest.pos_to_string(pos))
 | 
									minetest.log("action", p_name.." put "..nb.." "..stack:get_name() .. " to ATM at " .. minetest.pos_to_string(pos))
 | 
				
			||||||
				minetest.show_formspec(p_name, atm_form, deposit_fs(p_name))
 | 
									show_atm_form(deposit_fs, p_name)
 | 
				
			||||||
 | 
									--minetest.show_formspec(p_name, atm_form, deposit_fs(p_name))
 | 
				
			||||||
			else
 | 
								else
 | 
				
			||||||
				minetest.log("error", p_name.." want to put "..nb.." "..stack:get_name().." to ATM at ".. minetest.pos_to_string(pos).." but: "..msg)
 | 
									minetest.log("error", p_name.." want to put "..nb.." "..stack:get_name().." to ATM at ".. minetest.pos_to_string(pos).." but: "..msg)
 | 
				
			||||||
			end
 | 
								end
 | 
				
			||||||
@@ -296,7 +285,7 @@ minetest.register_node("global_exchange:atm_bottom", {
 | 
				
			|||||||
		local p_name = clicker:get_player_name()
 | 
							local p_name = clicker:get_player_name()
 | 
				
			||||||
		atm_pos[p_name] = pos
 | 
							atm_pos[p_name] = pos
 | 
				
			||||||
		minetest.sound_play("atm_beep", {pos = pos, gain = 0.3, max_hear_distance = 5})
 | 
							minetest.sound_play("atm_beep", {pos = pos, gain = 0.3, max_hear_distance = 5})
 | 
				
			||||||
		minetest.show_formspec(p_name, atm_form, main_menu)
 | 
							show_atm_form(main_menu_fs, clicker:get_player_name())
 | 
				
			||||||
	end,
 | 
						end,
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -317,15 +306,15 @@ minetest.register_node("global_exchange:atm_top", {
 | 
				
			|||||||
	node_box = {
 | 
						node_box = {
 | 
				
			||||||
		type = "fixed",
 | 
							type = "fixed",
 | 
				
			||||||
		fixed = {
 | 
							fixed = {
 | 
				
			||||||
			{-0.5, -0.5, -0.5, -0.375, 0.125, -0.25},
 | 
								{-0.500, -0.500, -0.5000, -0.375, 0.125, -0.25},
 | 
				
			||||||
			{0.375, -0.5, -0.5, 0.5, 0.125, -0.25},
 | 
								{ 0.375, -0.500, -0.5000,  0.500, 0.125, -0.25},
 | 
				
			||||||
			{-0.5, -0.5, -0.25, 0.5, 0.5, 0.5},
 | 
								{-0.500, -0.500, -0.2500,  0.500, 0.500,  0.50},
 | 
				
			||||||
			{-0.5, 0.125, -0.4375, -0.375, 0.25, -0.25},
 | 
								{-0.500,  0.125, -0.4375, -0.375, 0.250, -0.25},
 | 
				
			||||||
			{0.375, 0.125, -0.4375, 0.5, 0.25, -0.25},
 | 
								{ 0.375,  0.125, -0.4375,  0.500, 0.250, -0.25},
 | 
				
			||||||
			{-0.5, 0.25, -0.375, -0.375, 0.375, -0.25},
 | 
								{-0.500,  0.250, -0.3750, -0.375, 0.375, -0.25},
 | 
				
			||||||
			{0.375, 0.25, -0.375, 0.5, 0.375, -0.25},
 | 
								{ 0.375,  0.250, -0.3750,  0.500, 0.375, -0.25},
 | 
				
			||||||
			{-0.5, 0.375, -0.3125, -0.375, 0.5, -0.25},
 | 
								{-0.500,  0.375, -0.3125, -0.375, 0.500, -0.25},
 | 
				
			||||||
			{0.375, 0.375, -0.3125, 0.5, 0.5, -0.25},
 | 
								{ 0.375,  0.375, -0.3125,  0.500, 0.500, -0.25},
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	selection_box = {
 | 
						selection_box = {
 | 
				
			||||||
@@ -341,10 +330,11 @@ minetest.register_node("global_exchange:atm_top", {
 | 
				
			|||||||
minetest.register_craft( {
 | 
					minetest.register_craft( {
 | 
				
			||||||
	output = "global_exchange:atm",
 | 
						output = "global_exchange:atm",
 | 
				
			||||||
	recipe = {
 | 
						recipe = {
 | 
				
			||||||
		{ "default:stone", "default:stone", "default:stone" },
 | 
							{ "default:stone", "default:stone",      "default:stone" },
 | 
				
			||||||
		{ "default:stone", "default:gold_ingot", "default:stone" },
 | 
							{ "default:stone", "default:gold_ingot", "default:stone" },
 | 
				
			||||||
		{ "default:stone", "default:stone", "default:stone" },
 | 
							{ "default:stone", "default:stone",      "default:stone" },
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
minetest.register_alias("global_exchange:atm", "global_exchange:atm_bottom")
 | 
					minetest.register_alias("global_exchange:atm", "global_exchange:atm_bottom")
 | 
				
			||||||
 | 
					-- vim:set ts=4 sw=4 noet:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,60 +1,61 @@
 | 
				
			|||||||
 | 
					local exchange, formlib = ...
 | 
				
			||||||
local exchange = ...
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
local mailbox_form = "global_exchange:digital_mailbox"
 | 
					local mailbox_form = "global_exchange:digital_mailbox"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local mailbox_contents = {}
 | 
					local mailbox_contents = {}
 | 
				
			||||||
local selected_index = {}
 | 
					local selected_index = {}
 | 
				
			||||||
-- Map from player names to their most recent search result
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- Map from player names to their most recent search result
 | 
				
			||||||
local function get_mail(p_name)
 | 
					local function get_mail(p_name)
 | 
				
			||||||
	local mail_maybe = mailbox_contents[p_name]
 | 
						local mail_maybe = mailbox_contents[p_name]
 | 
				
			||||||
	if mail_maybe then
 | 
					
 | 
				
			||||||
		return mail_maybe
 | 
						if not mail_maybe then
 | 
				
			||||||
	else
 | 
							local _,res = exchange:view_inbox(p_name)
 | 
				
			||||||
		mailbox_contents[p_name] = {}
 | 
							mail_maybe = res or {}
 | 
				
			||||||
		return mailbox_contents[p_name]
 | 
							mailbox_contents[p_name] = mail_maybe
 | 
				
			||||||
 | 
							selected_index[p_name] = math.min(selected_index[p_name] or 0, #mail_maybe)
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return mail_maybe
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local function mk_inbox_list(results, x, y, w, h)
 | 
					local function wear_string(wear)
 | 
				
			||||||
	local res = {
 | 
						return "-" .. math.ceil(100 * wear / 65535) .. "%"
 | 
				
			||||||
		"textlist[",
 | 
					 | 
				
			||||||
		tostring(x),
 | 
					 | 
				
			||||||
		",",
 | 
					 | 
				
			||||||
		tostring(y),
 | 
					 | 
				
			||||||
		";",
 | 
					 | 
				
			||||||
		tostring(w),
 | 
					 | 
				
			||||||
		",",
 | 
					 | 
				
			||||||
		tostring(h),
 | 
					 | 
				
			||||||
		";result_list;"
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for i, row in ipairs(results) do
 | 
					 | 
				
			||||||
		res[i*2+8] = row.Amount .. " " .. row.Item
 | 
					 | 
				
			||||||
		res[i*2+9] = ","
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
	res[#res+1] = "]"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return table.concat(res)
 | 
					 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local function mk_mail_fs(p_name, results, err_str)
 | 
					local function mk_inbox_list(fs, results, x, y, w, h)
 | 
				
			||||||
	fs = "size[6,8]" ..
 | 
						fs:textlist(x,y, w,h, "result_list", function(add_row)
 | 
				
			||||||
		"label[0,0;Inbox]"
 | 
							for i, row in ipairs(results) do
 | 
				
			||||||
 | 
								local wear_suffix = nil
 | 
				
			||||||
 | 
								if row.Wear > 0 then
 | 
				
			||||||
 | 
									wear_suffix = " (" .. wear_string(row.Wear) .. ")"
 | 
				
			||||||
 | 
								end
 | 
				
			||||||
 | 
								add_row(row.Amount, " ", row.Item, wear_suffix)
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
						end)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function mk_mail_fs(fs, p_name, results, err_str)
 | 
				
			||||||
 | 
						fs:size(8,8)
 | 
				
			||||||
 | 
						fs:label(0,0, "Inbox")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err_str then
 | 
						if err_str then
 | 
				
			||||||
		fs = fs .. "label[3,0;Error: " .. err_str .. "]"
 | 
							fs:label(3,0, "Error: " .. err_str)
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return fs .. mk_inbox_list(results, 0, 1, 6, 6) ..
 | 
						mk_inbox_list(fs, results, 0, 1, 7.75, 6.25)
 | 
				
			||||||
		"button[0,7;2,1;claim;Claim]"
 | 
					
 | 
				
			||||||
 | 
						fs:button(3,7.35, 2,1, "claim", "Claim")
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local function show_mail(p_name, results, err_str)
 | 
					local function show_mail(p_name, err_str)
 | 
				
			||||||
	minetest.show_formspec(p_name, mailbox_form, mk_mail_fs(p_name, results, err_str))
 | 
						local fs = formlib.Builder()
 | 
				
			||||||
 | 
						mk_mail_fs(fs, p_name, get_mail(p_name), err_str)
 | 
				
			||||||
 | 
						minetest.show_formspec(p_name, mailbox_form, tostring(fs))
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -63,35 +64,6 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
 | 
				
			|||||||
	if fields.quit then return true end
 | 
						if fields.quit then return true end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	local p_name = player:get_player_name()
 | 
						local p_name = player:get_player_name()
 | 
				
			||||||
	local idx = selected_index[p_name]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if fields.claim
 | 
					 | 
				
			||||||
	and idx then
 | 
					 | 
				
			||||||
		local row = get_mail(p_name)[idx]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if row then
 | 
					 | 
				
			||||||
			local stack = ItemStack(row.Item)
 | 
					 | 
				
			||||||
			stack:set_count(row.Amount)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			local p_inv = player:get_inventory()
 | 
					 | 
				
			||||||
			if not p_inv:room_for_item("main", stack) then
 | 
					 | 
				
			||||||
				show_mail(p_name, get_mail(p_name), "Not enough room.")
 | 
					 | 
				
			||||||
				return true
 | 
					 | 
				
			||||||
			end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			local succ, res = exchange:take_inbox(row.Id, row.Amount)
 | 
					 | 
				
			||||||
			if not succ then
 | 
					 | 
				
			||||||
				show_mail(p_name, get_mail(p_name), res)
 | 
					 | 
				
			||||||
			end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			stack:set_count(res)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			p_inv:add_item("main", stack)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			table.remove(get_mail(p_name), idx)
 | 
					 | 
				
			||||||
			show_mail(p_name, get_mail(p_name))
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if fields.result_list then
 | 
						if fields.result_list then
 | 
				
			||||||
		local event = minetest.explode_textlist_event(fields.result_list)
 | 
							local event = minetest.explode_textlist_event(fields.result_list)
 | 
				
			||||||
@@ -101,22 +73,48 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
 | 
				
			|||||||
		end
 | 
							end
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if fields.claim then
 | 
				
			||||||
 | 
							local idx = selected_index[p_name]
 | 
				
			||||||
 | 
							local row = get_mail(p_name)[idx]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if row then
 | 
				
			||||||
 | 
								local stack = ItemStack(row.Item)
 | 
				
			||||||
 | 
								stack:set_count(row.Amount)
 | 
				
			||||||
 | 
								stack:set_wear(row.Wear)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								local p_inv = player:get_inventory()
 | 
				
			||||||
 | 
								local leftover = p_inv:add_item("main", stack)
 | 
				
			||||||
 | 
								local took_amount = row.Amount - leftover:get_count()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								mailbox_contents[p_name] = nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								local succ, res = exchange:take_inbox(row.Id, took_amount)
 | 
				
			||||||
 | 
								if succ then
 | 
				
			||||||
 | 
									show_mail(p_name)
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
									show_mail(p_name, res)
 | 
				
			||||||
 | 
								end
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return true
 | 
						return true
 | 
				
			||||||
end)
 | 
					end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
minetest.register_node("global_exchange:mailbox", {
 | 
					minetest.register_node("global_exchange:mailbox", {
 | 
				
			||||||
	description = "Digital Mailbox",
 | 
						description = "Digital Mailbox",
 | 
				
			||||||
	tiles = {"global_exchange_box.png",
 | 
						tiles = {
 | 
				
			||||||
 | 
							"global_exchange_box.png",
 | 
				
			||||||
		"global_exchange_box.png",
 | 
							"global_exchange_box.png",
 | 
				
			||||||
		"global_exchange_box.png^global_exchange_mailbox_side.png",
 | 
							"global_exchange_box.png^global_exchange_mailbox_side.png",
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
						is_ground_content = false,
 | 
				
			||||||
 | 
						stack_max = 1,
 | 
				
			||||||
	groups = {cracky=2},
 | 
						groups = {cracky=2},
 | 
				
			||||||
	on_rightclick = function(pos, node, clicker)
 | 
						on_rightclick = function(pos, node, clicker)
 | 
				
			||||||
		local p_name = clicker:get_player_name()
 | 
							local p_name = clicker:get_player_name()
 | 
				
			||||||
		local _,res = exchange:view_inbox(p_name)
 | 
							mailbox_contents[p_name] = nil
 | 
				
			||||||
		mailbox_contents[p_name] = res
 | 
							show_mail(p_name)
 | 
				
			||||||
		minetest.show_formspec(p_name, mailbox_form, mk_mail_fs(p_name, res))
 | 
					 | 
				
			||||||
	end,
 | 
						end,
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -125,7 +123,8 @@ minetest.register_craft( {
 | 
				
			|||||||
	output = "global_exchange:mailbox",
 | 
						output = "global_exchange:mailbox",
 | 
				
			||||||
	recipe = {
 | 
						recipe = {
 | 
				
			||||||
		{ "default:stone", "default:gold_ingot", "default:stone" },
 | 
							{ "default:stone", "default:gold_ingot", "default:stone" },
 | 
				
			||||||
		{ "default:stone", "default:chest", "default:stone" },
 | 
							{ "default:stone", "default:chest",      "default:stone" },
 | 
				
			||||||
		{ "default:stone", "default:stone", "default:stone" },
 | 
							{ "default:stone", "default:stone",      "default:stone" },
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					-- vim:set ts=4 sw=4 noet:
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1018
									
								
								exchange.lua
									
									
									
									
									
								
							
							
						
						
									
										1018
									
								
								exchange.lua
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,35 +1,52 @@
 | 
				
			|||||||
 | 
					local exchange, formlib = ...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local exchange = ...
 | 
					 | 
				
			||||||
local search_cooldown = 2
 | 
					local search_cooldown = 2
 | 
				
			||||||
local summary_interval = 600
 | 
					local summary_interval = 600
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local global_inv = nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function is_integer(x)
 | 
				
			||||||
 | 
						return math.floor(x) == x
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function wear_string(wear)
 | 
				
			||||||
 | 
						if wear > 0 then
 | 
				
			||||||
 | 
							return "-" .. math.ceil(100 * wear / 65535) .. "%"
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							return "----"
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local summary_fs = ""
 | 
					local summary_fs = ""
 | 
				
			||||||
local function mk_summary_fs()
 | 
					local function mk_summary_fs()
 | 
				
			||||||
	local res = {
 | 
						local fs = formlib.Builder()
 | 
				
			||||||
		"size[8,8]",
 | 
					 | 
				
			||||||
		"label[0,0;Updated Periodically]",
 | 
					 | 
				
			||||||
		"tablecolumns[text;text;text;text;text;text]",
 | 
					 | 
				
			||||||
		"table[0,1;8,6;summary_table;",
 | 
					 | 
				
			||||||
		"Item,Description,Buy Vol,Buy Max,Sell Vol,Sell Min"
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	local all_items = minetest.registered_items
 | 
						fs:tablecolumns("text", "text", "text", "text", "text", "text", "text")
 | 
				
			||||||
	for i, row in ipairs(exchange:market_summary()) do
 | 
						fs:table(0,0, 11.75,9, "summary_table", function(add_row)
 | 
				
			||||||
		local n = #res+1
 | 
							add_row("Item",
 | 
				
			||||||
		res[n] = "," .. row.item_name
 | 
							        "Description",
 | 
				
			||||||
		local def = all_items[row.item_name] or {}
 | 
									"Wear",
 | 
				
			||||||
		res[n+1] = "," .. (def.description or "Unknown Item")
 | 
							        "Buy Vol",
 | 
				
			||||||
		res[n+2] = "," .. (row.buy_volume or 0)
 | 
							        "Buy Max",
 | 
				
			||||||
		res[n+3] = "," .. (row.buy_max or "N/A")
 | 
							        "Sell Vol",
 | 
				
			||||||
		res[n+4] = "," .. (row.sell_volume or 0)
 | 
							        "Sell Min")
 | 
				
			||||||
		res[n+5] = "," .. (row.sell_min or "N/A")
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	res[#res+1] = "]"
 | 
							local all_items = minetest.registered_items
 | 
				
			||||||
	res[#res+1] = "button[3,7;2,1;back;Back]"
 | 
							for i, row in ipairs(exchange:market_summary()) do
 | 
				
			||||||
 | 
								local def = all_items[row.Item] or {}
 | 
				
			||||||
 | 
								add_row(row.Item,
 | 
				
			||||||
 | 
								        def.description or "Unknown Item",
 | 
				
			||||||
 | 
										wear_string(row.Wear),
 | 
				
			||||||
 | 
								        row.Buy_Volume  or 0,
 | 
				
			||||||
 | 
								        row.Buy_Max     or "N/A",
 | 
				
			||||||
 | 
								        row.Sell_Volume or 0,
 | 
				
			||||||
 | 
								        row.Sell_Min    or "N/A")
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
						end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	summary_fs = table.concat(res)
 | 
						summary_fs = tostring(fs)
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
minetest.after(0, mk_summary_fs)
 | 
					minetest.after(0, mk_summary_fs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -42,26 +59,34 @@ minetest.register_globalstep(function(dtime)
 | 
				
			|||||||
	end
 | 
						end
 | 
				
			||||||
end)
 | 
					end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local summary_form = "global_exchange:summary"
 | 
					
 | 
				
			||||||
local function show_summary(p_name)
 | 
					local wear_levels = {
 | 
				
			||||||
	minetest.show_formspec(p_name, summary_form, summary_fs)
 | 
						[1] = { index = 1, text = "New (-0%)",    wear = math.floor(0.00*65535) },
 | 
				
			||||||
 | 
						[2] = { index = 2, text = "Good (-10%)",  wear = math.floor(0.10*65535) },
 | 
				
			||||||
 | 
						[3] = { index = 3, text = "Worn (-50%)",  wear = math.floor(0.50*65535) },
 | 
				
			||||||
 | 
						[4] = { index = 4, text = "Junk (-100%)", wear = math.floor(1.00*65535) },
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- Allow lookup by text label as well as index
 | 
				
			||||||
 | 
					for _,v in ipairs(wear_levels) do
 | 
				
			||||||
 | 
						wear_levels[tostring(v.text)] = v
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local main_state = {}
 | 
					local main_state = {}
 | 
				
			||||||
-- ^ A per-player state for the main form. It contains these values:
 | 
					-- ^ A per-player state for the main form.
 | 
				
			||||||
--     old_fields: Keeps track of the fields before this update, when changing
 | 
					 | 
				
			||||||
--                 things slightly
 | 
					 | 
				
			||||||
--     search_results: The last search results the player obtained
 | 
					 | 
				
			||||||
--     last_search_time: The last time the player did a search. Used to implement
 | 
					 | 
				
			||||||
--                       a cooldown on searches
 | 
					 | 
				
			||||||
--     sell: A boolean whether the player has sell selected
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
minetest.register_on_joinplayer(function(player)
 | 
					minetest.register_on_joinplayer(function(player)
 | 
				
			||||||
	exchange:new_account(player:get_player_name()) --just to make sure
 | 
						exchange:new_account(player:get_player_name()) --just to make sure
 | 
				
			||||||
	main_state[player:get_player_name()] = {
 | 
						main_state[player:get_player_name()] = {
 | 
				
			||||||
		old_fields = {},
 | 
							tab            = 1,
 | 
				
			||||||
		search_results = {},
 | 
							buy_item       = "",
 | 
				
			||||||
		last_search_time = 0,
 | 
							buy_wear       = wear_levels[1].text,
 | 
				
			||||||
 | 
							buy_price      = "",
 | 
				
			||||||
 | 
							buy_amount     = "1",
 | 
				
			||||||
 | 
							sell_price     = "",
 | 
				
			||||||
 | 
							buy_page       = 1,
 | 
				
			||||||
 | 
							selected_index = 0,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
end)
 | 
					end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -70,268 +95,268 @@ minetest.register_on_leaveplayer(function(player)
 | 
				
			|||||||
end)
 | 
					end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local main_form = "global_exchange:exchange_main"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local tablecolumns =
 | 
					 | 
				
			||||||
	"tablecolumns[text;text;text;text;text;text]"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function table_from_results(results, x, y, w, h, selected)
 | 
					 | 
				
			||||||
	local fs_tab
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	local function insert(str)
 | 
					 | 
				
			||||||
		fs_tab[#fs_tab+1] = str
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	fs_tab = {
 | 
					 | 
				
			||||||
		tablecolumns,
 | 
					 | 
				
			||||||
		"table[" .. x .. "," .. y .. ";" .. w .. "," .. h .. ";",
 | 
					 | 
				
			||||||
		"result_table;",
 | 
					 | 
				
			||||||
		"Poster,Type,Item,Description,Amount,Rate"
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	local all_items = minetest.registered_items
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for i, row in ipairs(results) do
 | 
					 | 
				
			||||||
		insert(",")
 | 
					 | 
				
			||||||
		insert(tostring(row.Poster))
 | 
					 | 
				
			||||||
		insert(",")
 | 
					 | 
				
			||||||
		insert(tostring(row.Type))
 | 
					 | 
				
			||||||
		insert(",")
 | 
					 | 
				
			||||||
		insert(tostring(row.Item))
 | 
					 | 
				
			||||||
		insert(",")
 | 
					 | 
				
			||||||
		local def = all_items[row.Item] or {}
 | 
					 | 
				
			||||||
		insert(def.description or "Unknown Item")
 | 
					 | 
				
			||||||
		insert(",")
 | 
					 | 
				
			||||||
		insert(tostring(row.Amount))
 | 
					 | 
				
			||||||
		insert(",")
 | 
					 | 
				
			||||||
		insert(tostring(row.Rate))
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if selected and selected ~= "" then
 | 
					 | 
				
			||||||
		insert(";")
 | 
					 | 
				
			||||||
		insert(selected)
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
	insert("]")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return table.concat(fs_tab)
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function mk_main_fs(p_name, new_item, err_str, success)
 | 
					 | 
				
			||||||
	local state = main_state[p_name]
 | 
					 | 
				
			||||||
	if not state then return end -- Should have been initialized on player join
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	local old_fields = state.old_fields
 | 
					 | 
				
			||||||
	local results = state.search_results
 | 
					 | 
				
			||||||
	local item_def = new_item or old_fields.item or ""
 | 
					 | 
				
			||||||
	local amount_def = old_fields.amount or ""
 | 
					 | 
				
			||||||
	local rate_def = old_fields.rate or ""
 | 
					 | 
				
			||||||
	local sell_def = state.sell or false
 | 
					 | 
				
			||||||
	local selected_def = old_fields.selected or ""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	local bal = exchange:get_balance(p_name)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	local fs
 | 
					 | 
				
			||||||
	if bal then
 | 
					 | 
				
			||||||
		fs = "label[0,0;Balance: " .. bal .. "]"
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
		fs = "label[0.2,0.5;Use an ATM to make your account.]"
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	fs = fs .. "button[4,0;2,1;summary;Market Summary]" ..
 | 
					 | 
				
			||||||
		"button[6,0;2,1;your_orders;Your Orders]" ..
 | 
					 | 
				
			||||||
		"field[0.2,1.5;3,1;item;Item: ;" .. item_def .. "]" ..
 | 
					 | 
				
			||||||
		"field[3.2,1.5;3,1;amount;Amount: ;" .. amount_def .. "]" ..
 | 
					 | 
				
			||||||
		"button[6,1;2,1.4;select_item;Select Item]" ..
 | 
					 | 
				
			||||||
		"checkbox[5,3;sell;Sell;" .. tostring(sell_def) .. "]" ..
 | 
					 | 
				
			||||||
		"field[0.2,2.5;2,1;rate;Rate: ;" .. rate_def .. "]" ..
 | 
					 | 
				
			||||||
		"button[2,2;2,1.4;search;Search]" ..
 | 
					 | 
				
			||||||
		"button[4,2;3,1.4;post_order;Post Order]"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err_str then
 | 
					 | 
				
			||||||
		fs = fs .. "label[0,3;Error: " .. err_str .. "]"
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if success then
 | 
					 | 
				
			||||||
		fs = fs .. "label[0,3;Success!]"
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return "size[8,9]" .. fs .. table_from_results(results, 0, 4, 8, 5, selected_def)
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function show_main(p_name, new_item, err_str, success)
 | 
					 | 
				
			||||||
	minetest.show_formspec(p_name, main_form, mk_main_fs(p_name, new_item, err_str, success))
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
-- Something similar to creative inventory
 | 
					-- Something similar to creative inventory
 | 
				
			||||||
local pagemax = 1
 | 
					local pagemax = 1
 | 
				
			||||||
 | 
					local pagewidth = 12
 | 
				
			||||||
 | 
					local pageheight = 4
 | 
				
			||||||
 | 
					local pageitems = pagewidth * pageheight
 | 
				
			||||||
 | 
					local selectable_list = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- Create detached inventory after loading all mods
 | 
					-- Create inventory list after loading all mods
 | 
				
			||||||
minetest.after(0, function()
 | 
					minetest.after(0, function()
 | 
				
			||||||
	local inv = minetest.create_detached_inventory("global_exchange", {
 | 
					 | 
				
			||||||
		allow_move = function(inv, from_list, _, to_list, _,_, player)
 | 
					 | 
				
			||||||
			local p_name = player:get_player_name()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if from_list == "main"
 | 
					 | 
				
			||||||
			and to_list == "p_" .. p_name then
 | 
					 | 
				
			||||||
				return 1
 | 
					 | 
				
			||||||
			else
 | 
					 | 
				
			||||||
				return 0
 | 
					 | 
				
			||||||
			end
 | 
					 | 
				
			||||||
		end,
 | 
					 | 
				
			||||||
		allow_put = function()
 | 
					 | 
				
			||||||
			return 0
 | 
					 | 
				
			||||||
		end,
 | 
					 | 
				
			||||||
		allow_take = function()
 | 
					 | 
				
			||||||
			return 0
 | 
					 | 
				
			||||||
		end,
 | 
					 | 
				
			||||||
		on_move = function(inv, _, _, _, _, _, player)
 | 
					 | 
				
			||||||
			local p_name = player:get_player_name()
 | 
					 | 
				
			||||||
			local p_list = "p_" .. p_name
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			local item_name = inv:get_list(p_list)[1]:get_name()
 | 
					 | 
				
			||||||
			inv:set_list(p_list, {})
 | 
					 | 
				
			||||||
			inv:add_item("main", item_name)
 | 
					 | 
				
			||||||
			show_main(p_name, item_name)
 | 
					 | 
				
			||||||
		end,
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	local selectable_list,n = {},1
 | 
					 | 
				
			||||||
	for name, def in pairs(minetest.registered_items) do
 | 
						for name, def in pairs(minetest.registered_items) do
 | 
				
			||||||
		if (not def.groups.not_in_creative_inventory
 | 
							if (def.groups.not_in_creative_inventory or 0) == 0 and
 | 
				
			||||||
			or def.groups.not_in_creative_inventory == 0)
 | 
							   (def.description or "") ~= "" then
 | 
				
			||||||
		and def.description
 | 
								selectable_list[#selectable_list + 1] = name
 | 
				
			||||||
		and def.description ~= "" then
 | 
					 | 
				
			||||||
			selectable_list[n] = name
 | 
					 | 
				
			||||||
			n = n+1
 | 
					 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
	table.sort(selectable_list)
 | 
						table.sort(selectable_list)
 | 
				
			||||||
	inv:set_size("main", #selectable_list)
 | 
					
 | 
				
			||||||
	for _,itemstring in ipairs(selectable_list) do
 | 
						pagemax = math.max(math.ceil(#selectable_list / pageitems), 1)
 | 
				
			||||||
		inv:add_item("main", ItemStack(itemstring))
 | 
					end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local main_form = "global_exchange:exchange_main"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function table_from_results(fs, results, name, x, y, w, h, selected)
 | 
				
			||||||
 | 
						fs:tablecolumns("text", "text", "text", "text", "text", "text", "text")
 | 
				
			||||||
 | 
						fs:table(x,y, w,h, name, function(add_row)
 | 
				
			||||||
 | 
							add_row("Poster", "Type", "Item",
 | 
				
			||||||
 | 
							        "Description",
 | 
				
			||||||
 | 
									"Wear", "Amount", "Rate")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							local all_items = minetest.registered_items
 | 
				
			||||||
 | 
							for i, row in ipairs(results) do
 | 
				
			||||||
 | 
								local def = all_items[row.Item] or {}
 | 
				
			||||||
 | 
								add_row(row.Poster, row.Type, row.Item,
 | 
				
			||||||
 | 
								        def.description or "Unknown Item",
 | 
				
			||||||
 | 
										wear_string(row.Wear), row.Amount, row.Rate)
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
						end, math.max(0, tonumber(selected) or 0) + 1)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function mk_main_market_fs(fs, p_name, state)
 | 
				
			||||||
 | 
						fs(summary_fs)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function mk_main_order_book_fs(fs, p_name, x, y, w, h, item_name)
 | 
				
			||||||
 | 
						local order_book = exchange:order_book("", item_name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fs:tablecolumns("text", "text", "text", "text")
 | 
				
			||||||
 | 
						fs:table(x,y, w,h, "order_book", function(add_row)
 | 
				
			||||||
 | 
							add_row("Type", "Rate", "Wear", "Amount")
 | 
				
			||||||
 | 
							for _,row in ipairs(order_book) do
 | 
				
			||||||
 | 
								add_row(row.Type, row.Rate, wear_string(row.Wear), row.Amount)
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
						end, 1)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function mk_main_buy_fs(fs, p_name, state)
 | 
				
			||||||
 | 
						mk_main_order_book_fs(fs, p_name, 0, 0, 8.75, 3.75, state.buy_item)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fs:item_image_button(9,0, 1,1, "buy_item", state.buy_item)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fs:field(10.25,0.40, 2,1, "buy_amount", "Quantity", state.buy_amount, false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						local wear = wear_levels[state.buy_wear] or wear_levels[1]
 | 
				
			||||||
 | 
						fs:dropdown(9,1, 3, "buy_wear", function(add_item)
 | 
				
			||||||
 | 
							for _,v in ipairs(wear_levels) do
 | 
				
			||||||
 | 
								add_item(v.text)
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
						end, wear.index)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fs:field(9.35,2.40, 2.9,1, "buy_price", "Bid (ea.)", state.buy_price, false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fs:button(9,3, 3,1, "buy", "Place Bid")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fs:container(0,4, function()
 | 
				
			||||||
 | 
							fs:button( 0,0.25, 1,1, "buy_left",  "<<")
 | 
				
			||||||
 | 
							fs:button( 5,0.25, 2,1, "position",  state.buy_page .. "/" .. pagemax)
 | 
				
			||||||
 | 
							fs:button(11,0.25, 1,1, "buy_right", ">>")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							local firstitem = ((state.buy_page - 1) * pageitems)
 | 
				
			||||||
 | 
							for y=0,(pageheight-1) do
 | 
				
			||||||
 | 
								for x=0,(pagewidth-1) do
 | 
				
			||||||
 | 
									local index = firstitem + (pagewidth * y) + x + 1
 | 
				
			||||||
 | 
									if selectable_list[index] then
 | 
				
			||||||
 | 
										fs:item_image_button(x,1.25+y, 1,1, "select_" .. index,
 | 
				
			||||||
 | 
											selectable_list[index])
 | 
				
			||||||
 | 
									end
 | 
				
			||||||
 | 
								end
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
						end)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function mk_main_sell_fs(fs, p_name, state)
 | 
				
			||||||
 | 
						local sell_stack = global_inv:get_stack("p_" .. p_name, 1)
 | 
				
			||||||
 | 
						local sell_item = (not sell_stack:is_empty()
 | 
				
			||||||
 | 
						                   and sell_stack:get_name()) or ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mk_main_order_book_fs(fs, p_name, 0, 0, 8.75, 3.75, sell_item)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fs:list(9,0, 1,1, "detached:global_exchange", "p_" .. p_name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fs:field(9.35,2.40, 2.9,1, "sell_price", "Ask (ea.)", state.sell_price, false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fs:button(9,3, 3,1, "sell", "Sell")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fs:box(1.9375,5.1875, 7.96875,4.03, "#00000020")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fs:list(2,5.25, 8,4, "current_player", "main")
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function mk_main_own_orders_fs(fs, p_name, state)
 | 
				
			||||||
 | 
						if not state.own_results then
 | 
				
			||||||
 | 
							state.own_results = exchange:search_player_orders(p_name) or {}
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pagemax = math.ceil((#selectable_list - 1) / (8 * 4))
 | 
						state.selected_index = math.min(state.selected_index or 0, #state.own_results)
 | 
				
			||||||
end)
 | 
					
 | 
				
			||||||
 | 
						table_from_results(fs, state.own_results, "result_table", 0, 0, 11.75, 8.5, state.selected_index)
 | 
				
			||||||
 | 
						fs:button(4.5,8.5, 3,1, "cancel", "Cancel Order")
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local main_tabs = {
 | 
				
			||||||
 | 
						[1] = { text = "Market",    mk_fs = mk_main_market_fs     },
 | 
				
			||||||
 | 
						[2] = { text = "Buy",       mk_fs = mk_main_buy_fs        },
 | 
				
			||||||
 | 
						[3] = { text = "Sell",      mk_fs = mk_main_sell_fs       },
 | 
				
			||||||
 | 
						[4] = { text = "My Orders", mk_fs = mk_main_own_orders_fs },
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function mk_main_fs(fs, p_name, err_str, success)
 | 
				
			||||||
 | 
						local state = main_state[p_name]
 | 
				
			||||||
 | 
						if not state then return end -- Should have been initialized on player join
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						fs:size(12,10)
 | 
				
			||||||
 | 
						fs:bgcolor("#606060", false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fs:tabheader(0,0.65, "tab", function(add_tab)
 | 
				
			||||||
 | 
							for _,tab in ipairs(main_tabs) do
 | 
				
			||||||
 | 
								add_tab(tab.text)
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
						end, state.tab or 1, false, true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						local bal = exchange:get_balance(p_name)
 | 
				
			||||||
 | 
						fs:label(0,0.37, "Balance: " .. bal)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err_str then
 | 
				
			||||||
 | 
							fs:label(4,0.37, err_str)
 | 
				
			||||||
 | 
						elseif success then
 | 
				
			||||||
 | 
							fs:label(4,0.37, "Success!")
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if main_tabs[state.tab] then
 | 
				
			||||||
 | 
							fs:container(0,1, main_tabs[state.tab].mk_fs, p_name, state)
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function show_main(p_name, err_str, success)
 | 
				
			||||||
 | 
						local fs = formlib.Builder()
 | 
				
			||||||
 | 
						mk_main_fs(fs, p_name, err_str, success)
 | 
				
			||||||
 | 
						minetest.show_formspec(p_name, main_form, tostring(fs))
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
minetest.register_on_joinplayer(function(player)
 | 
					minetest.register_on_joinplayer(function(player)
 | 
				
			||||||
	-- the inventory list name is "p_"..player_name
 | 
						-- the inventory list name (for selling) is "p_"..player_name
 | 
				
			||||||
	minetest.get_inventory({
 | 
						global_inv:set_size("p_"  .. player:get_player_name(), 1)
 | 
				
			||||||
		type="detached",
 | 
					 | 
				
			||||||
		name="global_exchange"
 | 
					 | 
				
			||||||
	}):set_size("p_" .. player:get_player_name(), 1)
 | 
					 | 
				
			||||||
end)
 | 
					end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local select_form = "global_exchange:select_form"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function mk_select_formspec(p_name, start_i, pagenum)
 | 
					 | 
				
			||||||
	return "size[9.3,8]" ..
 | 
					 | 
				
			||||||
		"list[detached:global_exchange;main;0.3,0.5;8,4;" .. tostring(start_i) .. "]" ..
 | 
					 | 
				
			||||||
		"button[0.3,4.5;1.6,1;select_prev;<<]"..
 | 
					 | 
				
			||||||
		"button[6.7,4.5;1.6,1;select_next;>>]"..
 | 
					 | 
				
			||||||
		"label[2.0,5.55;"..tostring(math.floor(pagenum)).."/"..tostring(pagemax).."]"..
 | 
					 | 
				
			||||||
		"list[detached:global_exchange;p_" .. p_name .. ";0.3,7;1,1;]"
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local player_pages = {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function show_select(p_name)
 | 
					 | 
				
			||||||
	local pagenum = player_pages[p_name] or 1
 | 
					 | 
				
			||||||
	local start_i = (pagenum - 1) * 8 * 4
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	local fs = mk_select_formspec(p_name, start_i, pagenum)
 | 
					 | 
				
			||||||
	minetest.show_formspec(p_name, select_form, fs)
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local own_form = "global_exchange:my_orders"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local own_state = {}
 | 
					 | 
				
			||||||
-- ^ Per=player state for the own orders form. Contains these fields:
 | 
					 | 
				
			||||||
--     selected_index: The selected index
 | 
					 | 
				
			||||||
--     own_results: Results for own orders.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function mk_own_orders_fs(p_name, results, selected)
 | 
					 | 
				
			||||||
	return "size[8,8]" ..
 | 
					 | 
				
			||||||
		"label[0.5,0.2;Your Orders]" ..
 | 
					 | 
				
			||||||
		"button[6,0;2,1;refresh;Refresh]" ..
 | 
					 | 
				
			||||||
		table_from_results(results, 0, 2, 8, 4.5, selected or "") ..
 | 
					 | 
				
			||||||
		"button[0,7;2,1;cancel;Cancel]" ..
 | 
					 | 
				
			||||||
		"button[3,7;2,1;back;Back]"
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function show_own_orders(p_name, results, selected)
 | 
					 | 
				
			||||||
	minetest.show_formspec(p_name, own_form, mk_own_orders_fs(p_name, results, selected))
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
-- Returns success, and also returns an error message if failed.
 | 
					-- Returns success, and also returns an error message if failed.
 | 
				
			||||||
local function post_order(player, ex_name, order_type, item_name, amount_str, rate_str)
 | 
					local function post_buy(player, ex_name, item_name, wear_str, amount_str, rate_str)
 | 
				
			||||||
	local p_name = player:get_player_name()
 | 
						local p_name = player:get_player_name()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if item_name == "" then
 | 
						if (item_name or "") == "" then
 | 
				
			||||||
		return false, "You must input an item"
 | 
							return false, "You must input an item"
 | 
				
			||||||
	end
 | 
						elseif not minetest.registered_items[item_name] then
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if not minetest.registered_items[item_name] then
 | 
					 | 
				
			||||||
		return false, "That item does not exist."
 | 
							return false, "That item does not exist."
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						local wear_level = wear_levels[wear_str]
 | 
				
			||||||
 | 
						if not wear_level then
 | 
				
			||||||
 | 
							return false, "Invalid wear."
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	local amount = tonumber(amount_str)
 | 
						local amount = tonumber(amount_str)
 | 
				
			||||||
	local rate = tonumber(rate_str)
 | 
						local rate   = tonumber(rate_str)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if not amount then
 | 
						if not amount or not is_integer(amount) or amount < 1 then
 | 
				
			||||||
		return false, "Invalid amount."
 | 
							return false, "Invalid amount."
 | 
				
			||||||
	end
 | 
						elseif not rate or not is_integer(rate) or rate < 1 then
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if not rate then
 | 
					 | 
				
			||||||
		return false, "Invalid rate."
 | 
							return false, "Invalid rate."
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if amount > 1000 then
 | 
					 | 
				
			||||||
		return false, "Max amount is 1000"
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	local p_inv = player:get_inventory()
 | 
						local p_inv = player:get_inventory()
 | 
				
			||||||
	local stack = ItemStack(item_name)
 | 
						local stack = ItemStack(item_name)
 | 
				
			||||||
	stack:set_count(amount)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if order_type == "buy" then
 | 
						local succ, res = exchange:buy(p_name, ex_name, item_name, wear_level.wear, amount, rate)
 | 
				
			||||||
		if not p_inv:room_for_item("main", stack) then
 | 
						if not succ then
 | 
				
			||||||
			return false, "Not enough space in inventory."
 | 
							return false, res
 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		local succ, res = exchange:buy(p_name, ex_name, item_name, amount, rate)
 | 
					 | 
				
			||||||
		if not succ then
 | 
					 | 
				
			||||||
			return false, res
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		stack:set_count(res)
 | 
					 | 
				
			||||||
		p_inv:add_item("main", stack)
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
		if not p_inv:contains_item("main", stack) then
 | 
					 | 
				
			||||||
			return false, "Items not in inventory."
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		local succ, res = exchange:sell(p_name, ex_name, item_name, amount, rate)
 | 
					 | 
				
			||||||
		if not succ then
 | 
					 | 
				
			||||||
			return false, res
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		p_inv:remove_item("main", stack)
 | 
					 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _,row in ipairs(res) do
 | 
				
			||||||
 | 
							stack:set_count(row.amount)
 | 
				
			||||||
 | 
							stack:set_wear(row.wear)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							local leftover = p_inv:add_item("main", stack)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							-- Put anything that won't fit in the inventory in the player's inbox
 | 
				
			||||||
 | 
							if not leftover:is_empty() then
 | 
				
			||||||
 | 
								exchange:put_in_inbox(p_name, item_name, row.wear, leftover:get_count())
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						-- Refresh market summary "soonish"
 | 
				
			||||||
 | 
						elapsed = math.max(elapsed, summary_interval - 5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- Returns success, and also returns an error message if failed.
 | 
				
			||||||
 | 
					-- The item to sell is determined by the player's list in global_inv.
 | 
				
			||||||
 | 
					local function post_sell(player, ex_name, rate_str)
 | 
				
			||||||
 | 
						local p_name = player:get_player_name()
 | 
				
			||||||
 | 
						local stack  = global_inv:get_stack("p_" .. p_name, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if not stack or stack:is_empty() then
 | 
				
			||||||
 | 
							return false, "You must input an item"
 | 
				
			||||||
 | 
						elseif not minetest.registered_items[stack:get_name()] then
 | 
				
			||||||
 | 
							return false, "That item does not exist."
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if stack.get_meta then
 | 
				
			||||||
 | 
							local meta     = stack:get_meta()
 | 
				
			||||||
 | 
							local def_meta = ItemStack(stack:get_name()):get_meta()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if not stack:get_meta():equals(def_meta) then
 | 
				
			||||||
 | 
								return false, "Cannot sell an item with metadata."
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
						elseif (stack:get_metadata() or "") ~= "" then
 | 
				
			||||||
 | 
							return false, "Cannot sell an item with metadata."
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						local rate = tonumber(rate_str)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if not rate or not is_integer(rate) or rate < 1 then
 | 
				
			||||||
 | 
							return false, "Invalid rate."
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						local item_name = stack:get_name()
 | 
				
			||||||
 | 
						local wear      = stack:get_wear()
 | 
				
			||||||
 | 
						local amount    = stack:get_count()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						local succ, res = exchange:sell(p_name, ex_name, item_name, wear, amount, rate)
 | 
				
			||||||
 | 
						if not succ then
 | 
				
			||||||
 | 
							return false, res
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						stack:clear()
 | 
				
			||||||
 | 
						global_inv:set_stack("p_" .. p_name, 1, stack)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						-- Refresh market summary "soonish"
 | 
				
			||||||
 | 
						elapsed = math.max(elapsed, summary_interval - 5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return true
 | 
						return true
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -339,185 +364,113 @@ end
 | 
				
			|||||||
local function handle_main(player, fields)
 | 
					local function handle_main(player, fields)
 | 
				
			||||||
	local p_name = player:get_player_name()
 | 
						local p_name = player:get_player_name()
 | 
				
			||||||
	local state = main_state[p_name]
 | 
						local state = main_state[p_name]
 | 
				
			||||||
	local old_fields = state.old_fields
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for k, v in pairs(fields) do
 | 
						local copy_fields = {
 | 
				
			||||||
		old_fields[k] = v
 | 
							"buy_wear",
 | 
				
			||||||
 | 
							"buy_amount",
 | 
				
			||||||
 | 
							"buy_price",
 | 
				
			||||||
 | 
							"sell_price"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _,k in ipairs(copy_fields) do
 | 
				
			||||||
 | 
							if fields[k] then
 | 
				
			||||||
 | 
								state[k] = fields[k]
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if fields.select_item then
 | 
						if fields.tab then
 | 
				
			||||||
		show_select(p_name)
 | 
							state.tab = tonumber(fields.tab) or 1
 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if fields.search then
 | 
					 | 
				
			||||||
		local now = os.time()
 | 
					 | 
				
			||||||
		local last_search = state.last_search_time
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if now - last_search < search_cooldown then
 | 
					 | 
				
			||||||
			show_main(p_name, nil, "Please wait before searching again.")
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		-- If the player is selling, she wants "buy" type offers.
 | 
					 | 
				
			||||||
		local order_type
 | 
					 | 
				
			||||||
		if state.sell then
 | 
					 | 
				
			||||||
			order_type = "buy"
 | 
					 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
			order_type = "sell"
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
		local item_name = fields["item"]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		local results = exchange:search_orders("", order_type, item_name)
 | 
					 | 
				
			||||||
		state.search_results = results
 | 
					 | 
				
			||||||
		state.last_search_time = now
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		show_main(p_name)
 | 
							show_main(p_name)
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if fields.buy_left then
 | 
				
			||||||
 | 
							state.buy_page = (((state.buy_page or 1) + ((2*pagemax-1) - 1)) % pagemax) + 1
 | 
				
			||||||
 | 
							show_main(p_name)
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if fields.buy_right then
 | 
				
			||||||
 | 
							state.buy_page = (((state.buy_page or 1) + ((2*pagemax-1) + 1)) % pagemax) + 1
 | 
				
			||||||
 | 
							show_main(p_name)
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for name in pairs(fields) do
 | 
				
			||||||
 | 
							local index = tonumber(string.match(name, "select_([0-9]+)"))
 | 
				
			||||||
 | 
							if index and index >= 1 and index < #selectable_list then
 | 
				
			||||||
 | 
								state.buy_item = selectable_list[index]
 | 
				
			||||||
 | 
								show_main(p_name)
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if fields.buy then
 | 
				
			||||||
 | 
							local succ, err =
 | 
				
			||||||
 | 
								post_buy(player, "", state.buy_item, fields.buy_wear,
 | 
				
			||||||
 | 
								         fields.buy_amount, fields.buy_price)
 | 
				
			||||||
 | 
							if succ then
 | 
				
			||||||
 | 
								state.buy_amount = "1"
 | 
				
			||||||
 | 
								state.buy_price = ""
 | 
				
			||||||
 | 
								state.own_results = nil
 | 
				
			||||||
 | 
								show_main(p_name, nil, true)
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								show_main(p_name, err)
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if fields.sell then
 | 
						if fields.sell then
 | 
				
			||||||
		state.sell = fields.sell == "true"
 | 
							local succ, err = post_sell(player, "", fields.sell_price)
 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if fields.post_order then
 | 
					 | 
				
			||||||
		local now = os.time()
 | 
					 | 
				
			||||||
		local last_search = state.last_search_time
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if now - last_search < search_cooldown then
 | 
					 | 
				
			||||||
			show_main(p_name, nil, "Please wait before posting.")
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		local order_type
 | 
					 | 
				
			||||||
		if state.sell then
 | 
					 | 
				
			||||||
			order_type = "sell"
 | 
					 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
			order_type = "buy"
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		local succ, err =
 | 
					 | 
				
			||||||
			post_order(player, "", order_type, fields.item, fields.amount, fields.rate)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if succ then
 | 
							if succ then
 | 
				
			||||||
			state.search_results = {}
 | 
								state.sell_price = ""
 | 
				
			||||||
			show_main(p_name, nil, nil, true)
 | 
								state.own_results = nil
 | 
				
			||||||
 | 
								show_main(p_name, nil, true)
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
			show_main(p_name, nil, err)
 | 
								show_main(p_name, err)
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if fields.result_table then
 | 
					 | 
				
			||||||
		local results = state.search_results
 | 
					 | 
				
			||||||
		local event = minetest.explode_table_event(fields.result_table)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if event.type ~= "CHG" then
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		local index = event.row - 1
 | 
					 | 
				
			||||||
		result = results[index]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if result then
 | 
					 | 
				
			||||||
			old_fields.amount = tostring(result.Amount)
 | 
					 | 
				
			||||||
			old_fields.rate = tostring(result.Rate)
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		show_main(p_name)
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if fields.summary then
 | 
					 | 
				
			||||||
		show_summary(p_name)
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if fields.your_orders then
 | 
					 | 
				
			||||||
		if not own_state[p_name] then
 | 
					 | 
				
			||||||
			own_state[p_name] = {}
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
		local o_state = own_state[p_name]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		o_state.own_results = exchange:search_player_orders(p_name) or {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		show_own_orders(p_name, o_state.own_results)
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function handle_select(player, fields)
 | 
					 | 
				
			||||||
	local p_name = player:get_player_name()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	local pagenum = player_pages[p_name] or 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if fields.select_prev then
 | 
					 | 
				
			||||||
		player_pages[p_name] = math.max(1, pagenum - 1)
 | 
					 | 
				
			||||||
		show_select(p_name)
 | 
					 | 
				
			||||||
	elseif fields.select_next then
 | 
					 | 
				
			||||||
		player_pages[p_name] = math.min(pagemax, pagenum + 1)
 | 
					 | 
				
			||||||
		show_select(p_name)
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function handle_own_orders(player, fields)
 | 
					 | 
				
			||||||
	local p_name = player:get_player_name()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	local state = own_state[p_name] or {}
 | 
					 | 
				
			||||||
	local results = state.own_results or {}
 | 
					 | 
				
			||||||
	local idx = state.selected_index
 | 
						local idx = state.selected_index
 | 
				
			||||||
 | 
						local own_results = state.own_results or {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if fields.refresh then
 | 
						if fields.cancel and own_results[idx] then
 | 
				
			||||||
		state.own_results = exchange:search_player_orders(p_name) or {}
 | 
							local succ, res = exchange:cancel_order(p_name, own_results[idx].Id)
 | 
				
			||||||
		show_own_orders(p_name, state.own_results)
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if fields.cancel and idx then
 | 
					 | 
				
			||||||
		local row = results[idx]
 | 
					 | 
				
			||||||
		if not row then return true end
 | 
					 | 
				
			||||||
		local p_inv = player:get_inventory()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		local amount = row.Amount
 | 
					 | 
				
			||||||
		local item = row.Item
 | 
					 | 
				
			||||||
		local stack = ItemStack(item)
 | 
					 | 
				
			||||||
		stack:set_count(amount)
 | 
					 | 
				
			||||||
		if row.Type == "sell" then
 | 
					 | 
				
			||||||
			if not p_inv:room_for_item("main", stack) then
 | 
					 | 
				
			||||||
				show_own_orders(p_name, state.own_results, "Not enough room.")
 | 
					 | 
				
			||||||
				return true
 | 
					 | 
				
			||||||
			end
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		local succ, err =
 | 
					 | 
				
			||||||
			exchange:cancel_order(p_name, row.Id, row.Type, row.Item, row.Amount, row.Rate)
 | 
					 | 
				
			||||||
		if succ then
 | 
							if succ then
 | 
				
			||||||
			table.remove(results, idx)
 | 
								if res.Type == "sell" then
 | 
				
			||||||
			if row.Type == "sell" then
 | 
									local p_inv = player:get_inventory()
 | 
				
			||||||
				p_inv:add_item("main", stack)
 | 
									local stack = ItemStack(res.Item)
 | 
				
			||||||
 | 
									stack:set_count(res.Amount)
 | 
				
			||||||
 | 
									stack:set_wear(res.Wear)
 | 
				
			||||||
 | 
									local leftover = p_inv:add_item("main", stack)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									-- Put anything that won't fit in the inventory in the player's inbox
 | 
				
			||||||
 | 
									if not leftover:is_empty() then
 | 
				
			||||||
 | 
										exchange:put_in_inbox(p_name, res.Item, res.Wear, leftover:get_count())
 | 
				
			||||||
 | 
									end
 | 
				
			||||||
			end
 | 
								end
 | 
				
			||||||
		else
 | 
					
 | 
				
			||||||
			-- Refresh the results, since there might have been a problem.
 | 
								-- Refresh market summary "soonish"
 | 
				
			||||||
			state.own_results = exchange:search_player_orders(p_name) or {}
 | 
								elapsed = math.max(elapsed, summary_interval - 5)
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		show_own_orders(p_name, state.own_results)
 | 
							state.own_results = nil
 | 
				
			||||||
 | 
							show_main(p_name)
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if fields.result_table then
 | 
						if fields.result_table then
 | 
				
			||||||
		local event = minetest.explode_table_event(fields.result_table)
 | 
							local event = minetest.explode_table_event(fields.result_table)
 | 
				
			||||||
		if event.type == "CHG" then
 | 
							if event.type == "CHG" then
 | 
				
			||||||
			state.selected_index = event.row - 1
 | 
								state.selected_index = event.row - 1
 | 
				
			||||||
			show_own_orders(p_name, results, event.row)
 | 
								show_main(p_name)
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if fields.back then
 | 
						if fields.quit then
 | 
				
			||||||
		show_main(p_name)
 | 
							-- Return the player's unsold inventory, if any
 | 
				
			||||||
	end
 | 
							local stack = global_inv:get_stack("p_" .. p_name, 1)
 | 
				
			||||||
end
 | 
							local p_inv = player:get_inventory()
 | 
				
			||||||
 | 
							local leftover = p_inv:add_item("main", stack)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							-- Whatever doesn't fit in the player's inventory stays in the form.
 | 
				
			||||||
 | 
							-- Note that any items in the form when the server exits are lost.
 | 
				
			||||||
 | 
							global_inv:set_stack("p_" .. p_name, 1, leftover)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local function handle_summary(player, fields)
 | 
							state.own_results = nil
 | 
				
			||||||
	if fields.back then
 | 
					 | 
				
			||||||
		show_main(player:get_player_name())
 | 
					 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -525,12 +478,6 @@ end
 | 
				
			|||||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
 | 
					minetest.register_on_player_receive_fields(function(player, formname, fields)
 | 
				
			||||||
	if formname == main_form then
 | 
						if formname == main_form then
 | 
				
			||||||
		handle_main(player, fields)
 | 
							handle_main(player, fields)
 | 
				
			||||||
	elseif formname == select_form then
 | 
					 | 
				
			||||||
		handle_select(player, fields)
 | 
					 | 
				
			||||||
	elseif formname == own_form then
 | 
					 | 
				
			||||||
		handle_own_orders(player, fields)
 | 
					 | 
				
			||||||
	elseif formname == summary_form then
 | 
					 | 
				
			||||||
		handle_summary(player, fields)
 | 
					 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
@@ -538,6 +485,37 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
 | 
				
			|||||||
end)
 | 
					end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					global_inv = minetest.create_detached_inventory("global_exchange", {
 | 
				
			||||||
 | 
						allow_move = function(inv,from_list,from_index,to_list,to_index,count,player)
 | 
				
			||||||
 | 
							return 0
 | 
				
			||||||
 | 
						end,
 | 
				
			||||||
 | 
						allow_put = function(inv,to_list,to_index,stack,player)
 | 
				
			||||||
 | 
							local p_name = player:get_player_name()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if to_list == "p_" .. p_name then
 | 
				
			||||||
 | 
								return stack:get_count()
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								return 0
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
						end,
 | 
				
			||||||
 | 
						allow_take = function(inv,from_list,from_index,stack,player)
 | 
				
			||||||
 | 
							local p_name = player:get_player_name()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if from_list == "p_" .. p_name then
 | 
				
			||||||
 | 
								return stack:get_count()
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								return 0
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
						end,
 | 
				
			||||||
 | 
						on_put = function(inv,to_list,to_index,stack,player)
 | 
				
			||||||
 | 
							show_main(player:get_player_name())
 | 
				
			||||||
 | 
						end,
 | 
				
			||||||
 | 
						on_take = function(inv,from_list,from_index,stack,player)
 | 
				
			||||||
 | 
							show_main(player:get_player_name())
 | 
				
			||||||
 | 
						end,
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
minetest.register_node("global_exchange:exchange", {
 | 
					minetest.register_node("global_exchange:exchange", {
 | 
				
			||||||
	description = "Exchange Terminal",
 | 
						description = "Exchange Terminal",
 | 
				
			||||||
	drawtype = "nodebox",
 | 
						drawtype = "nodebox",
 | 
				
			||||||
@@ -552,18 +530,19 @@ minetest.register_node("global_exchange:exchange", {
 | 
				
			|||||||
	paramtype = "light",
 | 
						paramtype = "light",
 | 
				
			||||||
	paramtype2 = "facedir",
 | 
						paramtype2 = "facedir",
 | 
				
			||||||
	groups = {cracky=2},
 | 
						groups = {cracky=2},
 | 
				
			||||||
 | 
						is_ground_content = false,
 | 
				
			||||||
	stack_max = 1,
 | 
						stack_max = 1,
 | 
				
			||||||
	light_source = 3,
 | 
						light_source = 3,
 | 
				
			||||||
	node_box = {
 | 
						node_box = {
 | 
				
			||||||
		type = "fixed",
 | 
							type = "fixed",
 | 
				
			||||||
		fixed = {
 | 
							fixed = {
 | 
				
			||||||
			{-8/16, -4/16, 3/16, 0.5, 0.5, 5/16},--screens
 | 
								{-8/16, -4/16,  3/16, 8/16,  8/16,  5/16},--screens
 | 
				
			||||||
			{-1/16, -7/16, 5/16, 1/16, 5/16, 7/16},--screen leg
 | 
								{-1/16, -7/16,  5/16, 1/16,  5/16,  7/16},--screen leg
 | 
				
			||||||
			{-3/16, -8/16, 4/16, 3/16, -7/16, 8/16},--leg platform
 | 
								{-3/16, -8/16,  4/16, 3/16, -7/16,  8/16},--leg platform
 | 
				
			||||||
			{-7/16, -8/16, -8/16, 2/16, -6/16, -3/16},--keyboard
 | 
								{-7/16, -8/16, -8/16, 2/16, -6/16, -3/16},--keyboard
 | 
				
			||||||
			{3/16, -8/16, -3/16, 7/16, -7/16, 3/16},--phone low
 | 
								{ 3/16, -8/16, -3/16, 7/16, -7/16,  3/16},--phone low
 | 
				
			||||||
			{4/16, -7/16, -1/16, 6/16, -6/16, 3/16},--phone hi
 | 
								{ 4/16, -7/16, -1/16, 6/16, -6/16,  3/16},--phone hi
 | 
				
			||||||
			{2/16, -7/16, 0, 8/16, -5/16, 2/16},--phone speaker
 | 
								{ 2/16, -7/16,  0/16, 8/16, -5/16,  2/16},--phone speaker
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	on_rightclick = function(_, _, clicker)
 | 
						on_rightclick = function(_, _, clicker)
 | 
				
			||||||
@@ -581,8 +560,9 @@ minetest.register_node("global_exchange:exchange", {
 | 
				
			|||||||
minetest.register_craft( {
 | 
					minetest.register_craft( {
 | 
				
			||||||
	output = "global_exchange:exchange",
 | 
						output = "global_exchange:exchange",
 | 
				
			||||||
	recipe = {
 | 
						recipe = {
 | 
				
			||||||
		{ "default:steel_ingot", "default:steel_ingot", "default:steel_ingot" },
 | 
							{ "default:steel_ingot",  "default:steel_ingot", "default:steel_ingot" },
 | 
				
			||||||
		{ "default:mese_crystal", "default:steel_ingot", "default:diamond" },
 | 
							{ "default:mese_crystal", "default:steel_ingot", "default:diamond"     },
 | 
				
			||||||
		{ "default:steel_ingot", "default:steel_ingot", "default:steel_ingot" },
 | 
							{ "default:steel_ingot",  "default:steel_ingot", "default:steel_ingot" },
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					-- vim:set ts=4 sw=4 noet:
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										172
									
								
								formlib.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								formlib.lua
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,172 @@
 | 
				
			|||||||
 | 
					local formlib = {}
 | 
				
			||||||
 | 
					local builder_methods = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function formlib.escape(x)
 | 
				
			||||||
 | 
						if x == nil then return "" end
 | 
				
			||||||
 | 
						return minetest.formspec_escape(tostring(x))
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function formlib.bool(x)
 | 
				
			||||||
 | 
						-- nil and false are returned as-is, everything else maps to true
 | 
				
			||||||
 | 
						return x and true
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function builder_methods.append(fs, ...)
 | 
				
			||||||
 | 
						for i=1,select("#", ...) do
 | 
				
			||||||
 | 
							local x = select(i, ...)
 | 
				
			||||||
 | 
							if x ~= nil then table.insert(fs, tostring(x)) end
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
						return fs
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function builder_methods.escape(fs, ...)
 | 
				
			||||||
 | 
						for i=1,select("#", ...) do
 | 
				
			||||||
 | 
							local x = select(i, ...)
 | 
				
			||||||
 | 
							if x ~= nil then table.insert(fs, formlib.escape(x)) end
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
						return fs
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function builder_methods.escape_list(fs, ...)
 | 
				
			||||||
 | 
						for i=1,select("#", ...) do
 | 
				
			||||||
 | 
							local x = select(i, ...)
 | 
				
			||||||
 | 
							if i > 1 then fs(",") end
 | 
				
			||||||
 | 
							fs:escape(x)
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
						return fs
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function builder_methods.escape_groups(fs, ...)
 | 
				
			||||||
 | 
						for i=1,select("#", ...) do
 | 
				
			||||||
 | 
							local group = select(i, ...)
 | 
				
			||||||
 | 
							if i > 1 then fs(";") end
 | 
				
			||||||
 | 
							if type(group) == "table" then
 | 
				
			||||||
 | 
								fs:escape_list(unpack(group))
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								fs:escape(group)
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
						return fs
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function builder_methods.element(fs, name, ...)
 | 
				
			||||||
 | 
						return fs(name, "["):escape_groups(...):append("]")
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function builder_methods.size(fs, w,h, fixed)
 | 
				
			||||||
 | 
						if fixed == nil then
 | 
				
			||||||
 | 
							return fs:element("size", {w,h})
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							return fs:element("size", {w,h, formlib.bool(fixed)})
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function builder_methods.bgcolor(fs, color, fullscreen)
 | 
				
			||||||
 | 
						if fullscreen == nil then
 | 
				
			||||||
 | 
							return fs:element("bgcolor", {color})
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							return fs:element("bgcolor", {color}, {formlib.bool(fullscreen)})
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function builder_methods.list(fs, x,y, w,h, inv_loc, inv_list, start_idx)
 | 
				
			||||||
 | 
						return fs:element("list", {inv_loc}, {inv_list}, {x,y}, {w,h}, {start_idx})
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function builder_methods.button(fs, x,y, w,h, name, text)
 | 
				
			||||||
 | 
						return fs:element("button", {x,y}, {w,h}, {name}, {text})
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function builder_methods.item_image_button(fs, x,y, w,h, name, item, text)
 | 
				
			||||||
 | 
						return fs:element("item_image_button", {x,y}, {w,h}, {item}, {name}, {text})
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function builder_methods.label(fs, x,y, text)
 | 
				
			||||||
 | 
						return fs:element("label", {x,y}, {text})
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function builder_methods.field(fs, x,y, w,h, name, label, default, close_on_enter)
 | 
				
			||||||
 | 
						fs:element("field", {x,y}, {w,h}, {name}, {label}, {default})
 | 
				
			||||||
 | 
						if close_on_enter ~= nil then
 | 
				
			||||||
 | 
							fs:element("field_close_on_enter", {name}, {formlib.bool(close_on_enter)})
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
						return fs
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function builder_methods.box(fs, x,y, w,h, color)
 | 
				
			||||||
 | 
						return fs:element("box", {x,y}, {w,h}, {color})
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function builder_methods.dropdown(fs, x,y, w, name, body_fn, selected_idx)
 | 
				
			||||||
 | 
						fs("dropdown["):escape_groups({x,y}, {w}, {name}):append(";")
 | 
				
			||||||
 | 
						local first = true
 | 
				
			||||||
 | 
						local results = { body_fn(function(...)
 | 
				
			||||||
 | 
							if first then first = false else fs(",") end
 | 
				
			||||||
 | 
							fs:escape(...)
 | 
				
			||||||
 | 
						end) }
 | 
				
			||||||
 | 
						if selected_idx ~= nil then fs(";"):escape(selected_idx) end
 | 
				
			||||||
 | 
						return fs("]"), unpack(results)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function builder_methods.tabheader(fs, x,y, name, body_fn, current_tab, transparent, draw_border)
 | 
				
			||||||
 | 
						fs("tabheader["):escape_groups({x,y}, {name}):append(";")
 | 
				
			||||||
 | 
						local first = true
 | 
				
			||||||
 | 
						local results = { body_fn(function(...)
 | 
				
			||||||
 | 
							if first then first = false else fs(",") end
 | 
				
			||||||
 | 
							fs:escape(...)
 | 
				
			||||||
 | 
						end) }
 | 
				
			||||||
 | 
						fs(";"):escape_groups({current_tab}, {formlib.bool(transparent)}, {formlib.bool(draw_border)})
 | 
				
			||||||
 | 
						return fs("]"), unpack(results)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function builder_methods.textlist(fs, x,y, w,h, name, body_fn, selected_idx, transparent)
 | 
				
			||||||
 | 
						fs("textlist["):escape_groups({x,y}, {w,h}, {name}):append(";")
 | 
				
			||||||
 | 
						local first = true
 | 
				
			||||||
 | 
						local results = { body_fn(function(...)
 | 
				
			||||||
 | 
							if first then first = false else fs(",") end
 | 
				
			||||||
 | 
							fs:escape(...)
 | 
				
			||||||
 | 
						end) }
 | 
				
			||||||
 | 
						fs(";"):escape_groups({selected_idx}, {formlib.bool(transparent)})
 | 
				
			||||||
 | 
						return fs("]"), unpack(results)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function builder_methods.tableoptions(fs, ...)
 | 
				
			||||||
 | 
						return fs:element("tableoptions", ...)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function builder_methods.tablecolumns(fs, ...)
 | 
				
			||||||
 | 
						return fs:element("tablecolumns", ...)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function builder_methods.table(fs, x,y, w,h, name, body_fn, selected_idx)
 | 
				
			||||||
 | 
						fs("table["):escape_groups({x,y}, {w,h}, {name}):append(";")
 | 
				
			||||||
 | 
						local first = true
 | 
				
			||||||
 | 
						local results = { body_fn(function(...)
 | 
				
			||||||
 | 
							if first then first = false else fs(",") end
 | 
				
			||||||
 | 
							fs:escape_list(...)
 | 
				
			||||||
 | 
						end) }
 | 
				
			||||||
 | 
						if selected_idx ~= nil then fs(";"):escape(selected_idx) end
 | 
				
			||||||
 | 
						return fs("]"), unpack(results)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function builder_methods.container(fs, x,y, sub_fn, ...)
 | 
				
			||||||
 | 
						fs:element("container", {x,y})
 | 
				
			||||||
 | 
						local results = { sub_fn(fs, ...) }
 | 
				
			||||||
 | 
						return fs("container_end[]"), unpack(results)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local builder_meta = {
 | 
				
			||||||
 | 
						__metatable = "protected",
 | 
				
			||||||
 | 
						__index = builder_methods,
 | 
				
			||||||
 | 
						__call = builder_methods.append,
 | 
				
			||||||
 | 
						__tostring = table.concat,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function formlib.Builder()
 | 
				
			||||||
 | 
						local fs = {}
 | 
				
			||||||
 | 
						setmetatable(fs, builder_meta)
 | 
				
			||||||
 | 
						return fs
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return formlib
 | 
				
			||||||
 | 
					-- vim:set ts=4 sw=4 noet:
 | 
				
			||||||
							
								
								
									
										71
									
								
								init.lua
									
									
									
									
									
								
							
							
						
						
									
										71
									
								
								init.lua
									
									
									
									
									
								
							@@ -1,54 +1,45 @@
 | 
				
			|||||||
 | 
					 | 
				
			||||||
local insecure_env = minetest.request_insecure_environment()
 | 
					local insecure_env = minetest.request_insecure_environment()
 | 
				
			||||||
assert(insecure_env,
 | 
					assert(insecure_env, "global_exchange needs to be trusted to run under mod security.")
 | 
				
			||||||
	"global_exchange needs to be trusted to run under mod security.")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
local modpath = minetest.get_modpath(minetest.get_current_modname()) .. "/"
 | 
					local modpath = minetest.get_modpath(minetest.get_current_modname()) .. "/"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--[[local income = tonumber(minetest.setting_get("citizens_income")) or 10
 | 
					local exchange = assert(loadfile(modpath .. "exchange.lua"))(insecure_env).
 | 
				
			||||||
local income_interval = 1200
 | 
					   open_exchange(minetest.get_worldpath() .. "/global_exchange.db")
 | 
				
			||||||
local income_msg = "You receive your citizen's income (+" .. income .. ")"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
local next_payout = os.time() + income_interval
 | 
					local formlib = assert(loadfile(modpath .. "formlib.lua"))()
 | 
				
			||||||
]]--
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local exchange =
 | 
					 | 
				
			||||||
	assert(loadfile(modpath .. "exchange.lua"))(insecure_env).open_exchange(
 | 
					 | 
				
			||||||
		minetest.get_worldpath() .. "/global_exchange.db"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
minetest.register_on_shutdown(function()
 | 
					minetest.register_on_shutdown(function()
 | 
				
			||||||
	exchange:close()
 | 
					   exchange:close()
 | 
				
			||||||
end)
 | 
					end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function handle_setbalance_command(caller, name, newbalance)
 | 
				
			||||||
--[[local function check_giving()
 | 
					   return exchange:set_balance(name, newbalance)
 | 
				
			||||||
	local now = os.time()
 | 
					 | 
				
			||||||
	if now < next_payout then
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	next_payout = now + income_interval
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for _, player in ipairs(minetest.get_connected_players()) do
 | 
					 | 
				
			||||||
		local p_name = player:get_player_name()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		local succ = exchange:give_credits(p_name, income,
 | 
					 | 
				
			||||||
			"Citizen's Income (+" .. income .. ")")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if succ then
 | 
					 | 
				
			||||||
			minetest.chat_send_player(p_name, income_msg)
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	minetest.after(5, check_giving)
 | 
					 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
minetest.after(5, check_giving)
 | 
					minetest.register_privilege("balance", {
 | 
				
			||||||
]]--
 | 
					   description = "Can use /setbalance",
 | 
				
			||||||
 | 
					   give_to_singleplayer = false
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
assert(loadfile(modpath .. "atm.lua"))(exchange)
 | 
					minetest.register_chatcommand("setbalance", {
 | 
				
			||||||
assert(loadfile(modpath .. "exchange_machine.lua"))(exchange)
 | 
					   params      = "[<name>] <balance>",
 | 
				
			||||||
assert(loadfile(modpath .. "digital_mailbox.lua"))(exchange)
 | 
					   description = "set a player's trading balance",
 | 
				
			||||||
 | 
					   privs       = {balance=true},
 | 
				
			||||||
 | 
					   func = function(caller, param)
 | 
				
			||||||
 | 
					      local name, balancestr = string.match(param, "([^ ]+) ([0-9]+)")
 | 
				
			||||||
 | 
					      if not name or not balancestr then
 | 
				
			||||||
 | 
					         name = caller
 | 
				
			||||||
 | 
					         balancestr = string.match(param, "([0-9]+)")
 | 
				
			||||||
 | 
					         if not balancestr then
 | 
				
			||||||
 | 
					            return false, "Invalid parameters (see /help setbalance)"
 | 
				
			||||||
 | 
					         end
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					      return handle_setbalance_command(caller, name, tonumber(balancestr))
 | 
				
			||||||
 | 
					   end,
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					assert(loadfile(modpath .. "atm.lua"))(exchange, formlib)
 | 
				
			||||||
 | 
					assert(loadfile(modpath .. "exchange_machine.lua"))(exchange, formlib)
 | 
				
			||||||
 | 
					assert(loadfile(modpath .. "digital_mailbox.lua"))(exchange, formlib)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
minetest.log("action", "[global_exchange] loaded.")
 | 
					minetest.log("action", "[global_exchange] loaded.")
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user