Browse Source

Everything

tags/nalc-1.0
raymoo 4 years ago
commit
90c497f68a
9 changed files with 1813 additions and 0 deletions
  1. +182
    -0
      atm.lua
  2. +1
    -0
      description.txt
  3. +1080
    -0
      exchange.lua
  4. +497
    -0
      exchange_machine.lua
  5. +52
    -0
      init.lua
  6. +1
    -0
      mod.conf
  7. BIN
      test.db
  8. BIN
      textures/global_exchange_atm_side.png
  9. BIN
      textures/global_exchange_atm_top.png

+ 182
- 0
atm.lua View File

@@ -0,0 +1,182 @@

-- A telling machine. Call this file with the exchange argument.
local exchange = ...

local atm_form = "global_exchange:atm_form"

local main_menu =[[
size[6,1]
button[0,0;2,1;new_account;New Account]
button[2,0;2,1;info;Account Info]
button[4,0;2,1;wire;Wire Monies]
]]


local function logout(x,y)
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 new_account_fs(p_name)
local fs = "size[4,3]"

local act_suc, err = exchange:new_account(p_name)

if not act_suc then
fs = fs .. label(0.5,0.5, "Error: " .. err)
else
fs = fs .. label(0.5,0.5, "Congratulations on \nyour new account.")
end

fs = fs .. logout(0.5,2)

return fs
end


local function info_fs(p_name)
local balance = exchange:get_balance(p_name)

local fs = "size[4,3]"

if not balance then
fs = fs .. label(0.5,0.5, "You don't have an account.")
else
fs = fs .. label(0.5,0.5, "Balance: " .. balance)
end

fs = fs .. logout(0.5,2)

return fs
end


local function wire_fs(p_name)
local balance = exchange:get_balance(p_name)

local fs = "size[4,5]"
fs = fs .. logout(0,4)

if not balance then
fs = fs .. label(0.5,0.5, "You don't have an account.")
return fs
end

-- To prevent duplicates
fs = fs .. field(-100, -100, 0,0, "trans_id", "", unique())
fs = fs .. label(0.5,0.5, "Balance: " .. balance)
fs = fs .. field(0.5,1.5, 2,1, "recipient", "Send to:", "")
fs = fs .. field(0.5,2.5, 2,1, "amount", "Amount", "")
fs = fs .. "button[2,4;2,1;send;Send]"

return fs
end


local function send_fs(p_name, receiver, amt_str)
local fs = "size[7,3]"

local amt = tonumber(amt_str)

if not amt or amt <= 0 then
fs = fs .. label(0.5,0.5, "Invalid transfer amount.")
fs = fs .. "button[0.5,2;2,1;wire;Back]"
return fs
end
local succ, err = exchange:transfer_credits(p_name, receiver, amt)

if not succ then
fs = fs .. label(0.5,0.5, "Error: " .. err)
fs = fs .. "button[0.5,2;2,1;wire;Back]"
else
fs = fs.. label(0.5,0.5, "Successfully sent "
.. amt .. " credits to " .. receiver)
fs = fs .. "button[0.5,2;2,1;wire;Back]"
end

return fs
end


local trans_ids = {}


local function handle_fields(player, formname, fields)
if formname ~= atm_form then return end
if fields["quit"] then return true end

local p_name = player:get_player_name()

local this_id = fields.trans_id

if this_id and this_id == trans_ids[p_name] then
return true
end

trans_ids[p_name] = this_id

if fields["logout"] then
minetest.show_formspec(p_name, atm_form, main_menu)
end

if fields["new_account"] then
minetest.show_formspec(p_name, atm_form, new_account_fs(p_name))
end

if fields["info"] then
minetest.show_formspec(p_name, atm_form, info_fs(p_name))
end

if fields["wire"] then
minetest.show_formspec(p_name, atm_form, wire_fs(p_name))
end

if fields["send"] then
minetest.show_formspec(p_name, atm_form,
send_fs(p_name, fields.recipient, fields.amount))
end

return true
end


minetest.register_on_player_receive_fields(handle_fields)


minetest.register_node("global_exchange:atm", {
description = "ATM",
tiles = {"global_exchange_atm_top.png",
"global_exchange_atm_top.png",
"global_exchange_atm_side.png",
},
groups = {cracky=2},
on_rightclick = function(pos, node, clicker)
local p_name = clicker:get_player_name()

minetest.show_formspec(p_name, atm_form, main_menu)
end,
})

+ 1
- 0
description.txt View File

@@ -0,0 +1 @@
Adds a server-wide commodities (item) exchange.

+ 1080
- 0
exchange.lua
File diff suppressed because it is too large
View File


+ 497
- 0
exchange_machine.lua View File

@@ -0,0 +1,497 @@

local exchange = ...
local search_cooldown = 2

local main_state = {}
-- ^ A per-player state for the main form. It contains these values:
-- 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


local function default_main_state()
return { old_fields = {},
search_results = {},
last_search_time = 0,
}
end


minetest.register_on_joinplayer(function(player)
main_state[player:get_player_name()] = default_main_state()
end)

minetest.register_on_leaveplayer(function(player)
main_state[player:get_player_name()] = nil
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)
table.insert(fs_tab, str)
end

insert(tablecolumns)
insert("table[" .. x .. "," .. y .. ";" .. w .. "," .. h .. ";")
insert("result_table;")
insert("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(",")
if all_items[row.Item] then
insert(all_items[row.Item].description)
else
insert("Unknown Item")
end
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 fs = "size[8,9]"

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)

if bal then
fs = fs .. "label[0.2,0.5;Balance: " .. bal
else
fs = fs .. "label[0.2,0.5;Use an ATM to make your account.]"
end

fs = fs .. "button[6,0,2,1;your_orders;Your Orders]"
fs = fs .. "field[0.2,1.5;3,1;item;Item: ;" .. item_def .. "]"
fs = fs .. "field[3.2,1.5;3,1;amount;Amount: ;" .. amount_def .. "]"
fs = fs .. "button[6,1;2,1.4;select_item;Select Item]"
fs = fs .. "checkbox[5,3;sell;Sell;" .. tostring(sell_def) .. "]"
fs = fs .. "field[0.2,2.5;2,1;rate;Rate: ;" .. rate_def .. "]"
fs = fs .. "button[2,2;2,1.4;search;Search]"
fs = fs .. "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

fs = fs .. table_from_results(results, 0, 4, 8, 5, selected_def)

return fs
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
local selectable_inventory_size = 0

-- Create detached inventory after loading all mods
minetest.after(0, function()
local inv = minetest.create_detached_inventory("global_exchange", {
allow_move = function(inv, from_list, from_index, to_list, to_index, count, 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, from_list, from_index, to_list, to_index, count, 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 = {}
for name, def in pairs(minetest.registered_items) do
if (not def.groups.not_in_creative_inventory or def.groups.not_in_creative_inventory == 0)
and def.description and def.description ~= "" then
table.insert(selectable_list, name)
end
end
table.sort(selectable_list)
inv:set_size("main", #selectable_list)
for _,itemstring in ipairs(selectable_list) do
inv:add_item("main", ItemStack(itemstring))
end

selectable_inventory_size = #selectable_list
end)


minetest.register_on_joinplayer(function(player)
local big_inv = minetest.get_inventory({type="detached", name="global_exchange"})
local p_list = "p_" .. player:get_player_name()

big_inv:set_size(p_list, 1)
end)

local select_form = "global_exchange:select_form"


local function mk_select_formspec(p_name, start_i, pagenum)
pagenum = math.floor(pagenum)
local pagemax = math.floor((selectable_inventory_size - 1) / (8 * 4) + 1)
local p_list = "p_" .. p_name

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(pagenum).."/"..tostring(pagemax).."]"..
"list[detached:global_exchange;" .. p_list .. ";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)
local fs = "size[8,8]"
local state = main_state[p_name]

fs = fs .. "label[0.5,0.2;Your Orders]"
fs = fs .. "button[6,0;2,1;refresh;Refresh]"
fs = fs .. table_from_results(results, 0, 2, 8, 4.5, selected or "")
fs = fs .. "button[0,7;2,1;cancel;Cancel]"
fs = fs .. "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.
local function post_order(player, ex_name, order_type, item_name, amount_str, rate_str)
local p_name = player:get_player_name()
if item_name == "" then
return false, "You must input an item"
end
if not minetest.registered_items[item_name] then
return false, "That item does not exist."
end

local amount = tonumber(amount_str)
local rate = tonumber(rate_str)

if not amount then
return false, "Invalid amount."
end

if not rate then
return false, "Invalid rate."
end

local p_inv = player:get_inventory()
local stack = ItemStack(item_name)
stack:set_count(amount)
if order_type == "buy" then
if not p_inv:room_for_item("main", stack) then
return false, "Not enough space in inventory."
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

return true
end


local function handle_main(player, formname, fields)
if formname ~= main_form then return end
local p_name = player:get_player_name()
local state = main_state[p_name]
local old_fields = state.old_fields

for k, v in pairs(fields) do
old_fields[k] = v
end

if fields["select_item"] then
show_select(p_name)
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 true
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)
end

if fields["sell"] then
if fields["sell"] == "true" then
state.sell = true
else
state.sell = false
end
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 true
end

local order_type
if state.sell then
order_type = "sell"
else
order_type = "buy"
end
local item_name = fields["item"]
local amount_str = fields["amount"]
local rate_str = fields["rate"]

local succ, err =
post_order(player, "", order_type, item_name, amount_str, rate_str)

if succ then
state.search_results = {}
show_main(p_name, nil, nil, true)
else
show_main(p_name, nil, err)
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 true
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["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

return true
end


local function handle_select(player, formname, fields)
if formname ~= select_form then return end

local p_name = player:get_player_name()
local pagemax = math.floor((selectable_inventory_size - 1) / (8 * 4) + 1)
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

return true
end


local function handle_own_orders(player, formname, fields)
if formname ~= own_form then return end

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

if fields["refresh"] then
state.own_results = exchange:search_player_orders(p_name) or {}
show_own_orders(p_name, state.own_results)
end

if fields["cancel"] and idx then
local row = results[idx]

local succ, err = exchange:cancel_order(p_name, row.Id)
if succ then
table.remove(results, idx)
else
-- Refresh the results, since there might have been a problem.
state.own_results = exchange:search_player_orders(p_name) or {}
end

show_own_orders(p_name, state.own_results)
end

if fields["result_table"] then
local event = minetest.explode_table_event(fields["result_table"])
if event.type == "CHG" then
state.selected_index = event.row - 1
show_own_orders(p_name, results, state.selected_index)
end
end

if fields["back"] then
show_main(p_name)
end

return true
end


minetest.register_on_player_receive_fields(handle_main)
minetest.register_on_player_receive_fields(handle_select)
minetest.register_on_player_receive_fields(handle_own_orders)


minetest.register_node("global_exchange:exchange", {
description = "Exchange",
tiles = {"global_exchange_atm_top.png",
"global_exchange_atm_top.png",
"global_exchange_atm_side.png",
},
groups = {cracky=2},
on_rightclick = function(pos, node, clicker)
local p_name = clicker:get_player_name()
local state = main_state[p_name]
if state then
state.search_results = {}
end

show_main(p_name)
end,
})

+ 52
- 0
init.lua View File

@@ -0,0 +1,52 @@

local modpath = minetest.get_modpath(minetest.get_current_modname()) .. "/"
local savepath = minetest.get_worldpath() .. "/global_exchange.db"

local income_str = minetest.setting_get("citizens_income")
local income = income_str and tonumber(income_str) or 10

local income_interval = 1200

local income_msg = "You receive your citizen's income (+" .. income .. ")"

local next_payout = os.time() + income_interval

local ex = dofile(modpath .. "exchange.lua")
local exchange = ex.open_exchange(savepath)


minetest.register_on_shutdown(function()
exchange:close()
end)


-- Only check once in a while
local elapsed = 0

minetest.register_globalstep(function(dtime)
elapsed = elapsed + dtime
if elapsed <= 5 then return end

elapsed = 0

local now = os.time()
if now < next_payout then return end

next_payout = now + income_interval

for i, 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
end)


assert(loadfile(modpath .. "atm.lua"))(exchange)
assert(loadfile(modpath .. "exchange_machine.lua"))(exchange)

+ 1
- 0
mod.conf View File

@@ -0,0 +1 @@
name=global_exchange

BIN
test.db View File


BIN
textures/global_exchange_atm_side.png View File

Before After
Width: 16  |  Height: 16  |  Size: 255 B

BIN
textures/global_exchange_atm_top.png View File

Before After
Width: 16  |  Height: 16  |  Size: 176 B

Loading…
Cancel
Save