Treasure chest squashed commit of previous work
This commit is contained in:
commit
4e63572b7c
85
README.txt
Normal file
85
README.txt
Normal file
@ -0,0 +1,85 @@
|
||||
== Treasure Chest mod for Minetest ==
|
||||
|
||||
01. What is it?
|
||||
02. License Info
|
||||
03. Current Version
|
||||
04. Installation Instructions
|
||||
05. Dependencies
|
||||
06. Bugs/contact info
|
||||
|
||||
01. What is it?
|
||||
===============
|
||||
Treasure Chest is a small mod for the Minetest game that adds a kind of chest made for world designers.
|
||||
The chest has no crafting recipe, so it has to be obtained by /giveme or other commands.
|
||||
|
||||
The intended use and original idea, comes from trying to design challenges in a survival world,
|
||||
and having some way to automatically reward players who complete the challenges.
|
||||
|
||||
The rewards can be somewhat randomized, with a probability for each one,and can reset after
|
||||
a specified time, on a per user basis, after being given out.
|
||||
|
||||
When the chest is used by someone without the "give" privilege, the chest will attempt to give
|
||||
a copy of the items inside it (with some chance) to the user. The chest then records the last time
|
||||
this user has tried to get the items, and the user will then have to wait for a timeout period to
|
||||
expire before he/she can have a chance of obtaining the items again. This timeout period is per-user.
|
||||
|
||||
For someone with the "give" privilege, the chest will display a GUI that allows you to configure it.
|
||||
|
||||
- 1st input: Refresh Time: An integer value
|
||||
The number of minutes of gametime that must pass before the chest can give its items out again.
|
||||
This is on a per-user basis, so two users can always obtain the reward if they use the chest,
|
||||
but if the same user tries to use it before the refresh timeout, he will get nothing.
|
||||
|
||||
- 2nd line: Six input: Integer values
|
||||
Probabilities, ranging 0..100, of how likely a reward is to be given to a user. Randomly
|
||||
determined each time the chest is used. Associated with the inventory slot below each one
|
||||
|
||||
- 3rd line: Six inventory slots
|
||||
The items to be given out, as associated by the probabilities above them. Each slot can hold
|
||||
a regular item stack. Items stacks are given out as a whole, so the user will get either the
|
||||
whole item stack, or nothing from that slot. Item stacks in these slots are not taken by
|
||||
regular users using the chest, instead they get copied.
|
||||
|
||||
02. License Info:
|
||||
=================
|
||||
License for Code
|
||||
----------------
|
||||
|
||||
Copyright (C) 2017-2022 <Zenon.Seth@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
License for Textures, Models and Sounds
|
||||
---------------------------------------
|
||||
CC-BY-SA 3.0 UNPORTED. Created by Zenon Seth
|
||||
|
||||
|
||||
03. Current Version
|
||||
===================
|
||||
v1.0 : Mod release
|
||||
|
||||
04. Installation Instructions
|
||||
=============================
|
||||
Copy the entire folder containing this file into your minetest/mods folder.
|
||||
When running the game through GUI, configure world, select and enable the mod.
|
||||
When running a server through command line, edit world.mt and mark this mod as 'true' to be loaded
|
||||
|
||||
05. Dependencies
|
||||
================
|
||||
Minetest engine and Minetest game (see https://www.minetest.net)
|
||||
|
||||
06. Bugs/contact info
|
||||
=====================
|
||||
Submit bugs on github: https://github.com/ZenonSeth/treasure_chest
|
1
depends.txt
Normal file
1
depends.txt
Normal file
@ -0,0 +1 @@
|
||||
default
|
238
init.lua
Normal file
238
init.lua
Normal file
@ -0,0 +1,238 @@
|
||||
|
||||
|
||||
dofile(minetest.get_modpath("treasure_chest") .. "/utils.lua")
|
||||
|
||||
local openedTreasureChestConfigs = {};
|
||||
|
||||
local metaStrType = "type";
|
||||
local metaExpectedType = "traesurechest";
|
||||
local metaStrOwner = "owner";
|
||||
local metaIntRefresh = "refresh";
|
||||
local metaInt0p = "0p";
|
||||
local metaInt1p = "1p";
|
||||
local metaInt2p = "2p";
|
||||
local metaInt3p = "3p";
|
||||
local metaInt4p = "4p";
|
||||
local metaInt5p = "5p";
|
||||
|
||||
local fieldRefresh = "refresh_interval";
|
||||
local fieldI0P = "i0p";
|
||||
local fieldI1P = "i1p";
|
||||
local fieldI2P = "i2p";
|
||||
local fieldI3P = "i3p";
|
||||
local fieldI4P = "i4p";
|
||||
local fieldI5P = "i5p";
|
||||
local buttonExit = "exit";
|
||||
|
||||
local strDescription = "A chest that gives semi-randomized rewards per player";
|
||||
local strOneTime = "This is a one-time use chest, and you already opened it!";
|
||||
local strTooSoon = "To get another reward come back in ";
|
||||
local strFromRefreshLabel = "Refresh time, in minutes, integer. E.g.: 60 = 1 hour, 1440 = 1 day, 10080 = 1 week";
|
||||
local strProbabiltiesLabel = "Item probability of being given, integer, range 0..100: 0 = never, 100 = always";
|
||||
|
||||
minetest.register_node("treasure_chest:treasure_chest", {
|
||||
description = strDescription,
|
||||
|
||||
tiles = {
|
||||
"treasurechest_u.png",
|
||||
"treasurechest_d.png",
|
||||
"treasurechest_r.png",
|
||||
"treasurechest_l.png",
|
||||
"treasurechest_b.png",
|
||||
"treasurechest_f.png"
|
||||
},
|
||||
|
||||
groups = {cracky = 3},
|
||||
drop = "",
|
||||
paramtype2 = "facedir",
|
||||
can_dig = function(pos, player)
|
||||
local playerName = player:get_player_name();
|
||||
local meta = minetest.get_meta(pos);
|
||||
local privs = minetest.get_player_privs(playerName);
|
||||
local owner = meta:get_string(metaStrOwner);
|
||||
|
||||
if player:get_player_name() == owner or privs.give then
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
end
|
||||
end,
|
||||
|
||||
after_place_node =
|
||||
function(pos, placer, itemstack, pointed_thing)
|
||||
local meta = minetest.get_meta(pos);
|
||||
|
||||
meta:set_string(metaStrOwner, placer:get_player_name());
|
||||
meta:set_int(metaIntRefresh, 1);
|
||||
meta:set_string(metaStrType, metaExpectedType);
|
||||
meta:set_int(metaInt0p, 100);
|
||||
meta:set_int(metaInt1p, 100);
|
||||
meta:set_int(metaInt2p, 100);
|
||||
meta:set_int(metaInt3p, 100);
|
||||
meta:set_int(metaInt4p, 100);
|
||||
meta:set_int(metaInt5p, 100);
|
||||
|
||||
local inv = meta:get_inventory();
|
||||
inv:set_size("main", 6);
|
||||
end,
|
||||
|
||||
on_rightclick =
|
||||
function(nodePos, node, player, itemstack, pointed_thing)
|
||||
local playerName = player:get_player_name();
|
||||
local spos = nodePos.x..","..nodePos.y..","..nodePos.z;
|
||||
local gameTime = minetest.get_gametime();
|
||||
local privs = minetest.get_player_privs(playerName);
|
||||
|
||||
local meta = minetest.get_meta(nodePos);
|
||||
local owner = meta:get_string(metaStrOwner);
|
||||
local refresh = meta:get_int(metaIntRefresh);
|
||||
local i0p = meta:get_int(metaInt0p);
|
||||
local i1p = meta:get_int(metaInt1p);
|
||||
local i2p = meta:get_int(metaInt2p);
|
||||
local i3p = meta:get_int(metaInt3p);
|
||||
local i4p = meta:get_int(metaInt4p);
|
||||
local i5p = meta:get_int(metaInt5p);
|
||||
|
||||
-- clean up some metadata
|
||||
local tmp = meta:to_table()
|
||||
local newMetaTable = tmp
|
||||
if refresh > 0 then
|
||||
for k,v in pairs(tmp["fields"]) do
|
||||
if k ~= metaStrOwner
|
||||
and k ~= metaStrType
|
||||
and k ~= metaIntRefresh
|
||||
and k ~= metaInt0p
|
||||
and k ~= metaInt1p
|
||||
and k ~= metaInt2p
|
||||
and k ~= metaInt3p
|
||||
and k ~= metaInt4p
|
||||
and k ~= metaInt5p then
|
||||
local tv = tonumber(v)
|
||||
if tv then
|
||||
diff = gameTime - tv
|
||||
if diff > refresh * 60 then
|
||||
newMetaTable["fields"] = table.removeKey(newMetaTable["fields"], k)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
meta:from_table(newMetaTable)
|
||||
end
|
||||
-- end clean-up
|
||||
|
||||
if privs.server or owner == playerName then
|
||||
openedTreasureChestConfigs[playerName] = nodePos;
|
||||
minetest.show_formspec(playerName, "treasure_chest:setup_inventory",
|
||||
"size[8,8]" ..
|
||||
|
||||
"field[0.2,0.2;7.0,0.9;"..fieldRefresh..";"..strFromRefreshLabel..";".. refresh .."]"..
|
||||
|
||||
"label[0.2,0.6;"..strProbabiltiesLabel.."]"..
|
||||
|
||||
"field[0.5,1.2;1,1;"..fieldI0P..";;"..i0p.."]"..
|
||||
"field[1.5,1.2;1,1;"..fieldI1P..";;"..i1p.."]"..
|
||||
"field[2.5,1.2;1,1;"..fieldI2P..";;"..i2p.."]"..
|
||||
"field[3.5,1.2;1,1;"..fieldI3P..";;"..i3p.."]"..
|
||||
"field[4.5,1.2;1,1;"..fieldI4P..";;"..i4p.."]"..
|
||||
"field[5.5,1.2;1,1;"..fieldI5P..";;"..i5p.."]"..
|
||||
|
||||
"list[nodemeta:"..spos..";main;0.2,1.8;6.0,1.0;]"..
|
||||
"button_exit[1.0,2.8;3.0,1.0;"..buttonExit..";Save & Close]"..
|
||||
|
||||
"list[current_player;main;0.0,4.0;8.0,4.0;]");
|
||||
|
||||
else
|
||||
local lastTime = meta:get_int(playerName);
|
||||
local diff;
|
||||
if lastTime and lastTime > 0 then
|
||||
diff = gameTime - lastTime;
|
||||
else
|
||||
diff = refresh*60 + 1;
|
||||
end
|
||||
|
||||
local singleUseUsed = (lastTime ~= 0) and (refresh < 0);
|
||||
local notSingleUseButUsed = (refresh > 0) and (lastTime ~= 0) and (diff <= refresh*60);
|
||||
|
||||
if singleUseUsed or notSingleUseButUsed then
|
||||
local reason
|
||||
if refresh < 0 then
|
||||
reason = strOneTime
|
||||
else
|
||||
diff = (lastTime + refresh * 60) - gameTime
|
||||
diff = math.floor(diff / 60 + 0.5)
|
||||
local time = ""
|
||||
if diff <= 1 then
|
||||
time = "1 minute"
|
||||
elseif diff < 60 then
|
||||
time = diff .. " minutes"
|
||||
elseif diff < 1440 then
|
||||
time = math.floor(diff/60 + 0.5) .. " hours"
|
||||
else
|
||||
time = math.floor(diff/1440 + 0.5) .. " days"
|
||||
end
|
||||
reason = strTooSoon .. time
|
||||
end
|
||||
|
||||
minetest.chat_send_player(playerName, reason);
|
||||
|
||||
else
|
||||
local nodeInv = meta:get_inventory(); --minetest.get_inventory({type="node", pos=nodePos});
|
||||
local playerInv = player:get_inventory();
|
||||
local playerWieldedItem = player:get_wielded_item();
|
||||
-- bit of hard-coding, relying we only have 6 slots. Consider that the formspec is also hardcoded, it's not a huge deal
|
||||
for index=0,5,1 do
|
||||
local metaAccessString = index.."p";
|
||||
local probability = meta:get_int(metaAccessString);
|
||||
print("wield list name = "..player:get_wield_list());
|
||||
if (randomCheck(probability)) then
|
||||
local itemStackToAdd = nodeInv:get_stack("main", index+1); -- +1 for inventory indexing begins at 1
|
||||
itemStackToAdd = playerInv:add_item("main", itemStackToAdd);
|
||||
if not itemStackToAdd:is_empty() then
|
||||
minetest.item_drop(itemStackToAdd, player, player:get_pos());
|
||||
end
|
||||
end
|
||||
end
|
||||
meta:set_int(playerName, gameTime);
|
||||
return playerInv:get_stack(player:get_wield_list(), player:get_wield_index()); -- the itemstack we have as input may no longer be valid due to the add_item call above
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if formname == "treasure_chest:setup_inventory" then
|
||||
local playerName = player:get_player_name();
|
||||
|
||||
if (not fields[fieldRefresh]) then
|
||||
-- User cancelled, quit now
|
||||
return true;
|
||||
end
|
||||
|
||||
local pos = openedTreasureChestConfigs[playerName];
|
||||
if pos == nil then
|
||||
return;
|
||||
end
|
||||
openedTreasureChestConfigs[playerName] = nil;
|
||||
|
||||
local meta = minetest.get_meta(pos);
|
||||
if meta:get_string(metaStrType) ~= metaExpectedType then
|
||||
return;
|
||||
end
|
||||
|
||||
meta:set_int(metaIntRefresh, clamp(toNum(fields[fieldRefresh], meta:get_int(metaIntRefresh)), -1, nil) );
|
||||
meta:set_int(metaInt0p, clamp(toNum(fields[fieldI0P], meta:get_int(metaInt0p)), 0, 100));
|
||||
meta:set_int(metaInt1p, clamp(toNum(fields[fieldI1P], meta:get_int(metaInt1p)), 0, 100));
|
||||
meta:set_int(metaInt2p, clamp(toNum(fields[fieldI2P], meta:get_int(metaInt2p)), 0, 100));
|
||||
meta:set_int(metaInt3p, clamp(toNum(fields[fieldI3P], meta:get_int(metaInt3p)), 0, 100));
|
||||
meta:set_int(metaInt4p, clamp(toNum(fields[fieldI4P], meta:get_int(metaInt4p)), 0, 100));
|
||||
meta:set_int(metaInt5p, clamp(toNum(fields[fieldI5P], meta:get_int(metaInt5p)), 0, 100));
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end)
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
local playerName = player:get_player_name()
|
||||
openedTreasureChestConfigs[playerName] = nil;
|
||||
end)
|
BIN
textures/treasurechest_b.png
Normal file
BIN
textures/treasurechest_b.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 655 B |
BIN
textures/treasurechest_d.png
Normal file
BIN
textures/treasurechest_d.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 603 B |
BIN
textures/treasurechest_f.png
Normal file
BIN
textures/treasurechest_f.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 687 B |
BIN
textures/treasurechest_l.png
Normal file
BIN
textures/treasurechest_l.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 669 B |
BIN
textures/treasurechest_r.png
Normal file
BIN
textures/treasurechest_r.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 663 B |
BIN
textures/treasurechest_u.png
Normal file
BIN
textures/treasurechest_u.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 642 B |
81
utils.lua
Normal file
81
utils.lua
Normal file
@ -0,0 +1,81 @@
|
||||
function table.removeKey(t, k)
|
||||
local i = 0
|
||||
local keys, values = {},{}
|
||||
for k,v in pairs(t) do
|
||||
i = i + 1
|
||||
keys[i] = k
|
||||
values[i] = v
|
||||
end
|
||||
|
||||
while i>0 do
|
||||
if keys[i] == k then
|
||||
table.remove(keys, i)
|
||||
table.remove(values, i)
|
||||
break
|
||||
end
|
||||
i = i - 1
|
||||
end
|
||||
|
||||
local a = {}
|
||||
for i = 1,#keys do
|
||||
a[keys[i]] = values[i]
|
||||
end
|
||||
|
||||
return a
|
||||
end
|
||||
|
||||
function splitStringToTable(inputString, splitter)
|
||||
local ret = {};
|
||||
local tmp;
|
||||
|
||||
if inputString == nil then return nil; end
|
||||
|
||||
if (splitter == nil) then
|
||||
table.insert(ret, inputString);
|
||||
return ret;
|
||||
end
|
||||
|
||||
-- print("inputString: " .. inputString .. ", splitter:" .. splitter);
|
||||
local found = true;
|
||||
while found do
|
||||
local s,e = inputString:find(splitter);
|
||||
if s == nil then
|
||||
table.insert(ret, inputString);
|
||||
found = false;
|
||||
else
|
||||
-- print("s/e=" .. s .. "/" .. e);
|
||||
tmp = inputString:sub(0,s - 1);
|
||||
table.insert(ret, tmp);
|
||||
inputString = inputString:sub(e + 1);
|
||||
end
|
||||
end
|
||||
-- for k,v in pairs(ret) do print(k,v) end
|
||||
return ret;
|
||||
end
|
||||
|
||||
|
||||
function tableLength(table)
|
||||
if (table == nil) then return 0; end
|
||||
local count = 0
|
||||
for _ in pairs(table) do count = count + 1 end
|
||||
return count
|
||||
end
|
||||
|
||||
function clamp(value, min, max)
|
||||
if value == nil then return nil; end
|
||||
if max == nil and min == nil then return value; end
|
||||
if min == nil then return math.min(value, max); end
|
||||
if max == nil then return math.max(value, min); end
|
||||
return math.max(math.min(value, max), min);
|
||||
end
|
||||
|
||||
function toNum(number, default)
|
||||
default = default or 0;
|
||||
return tonumber(number) or default;
|
||||
end
|
||||
|
||||
function randomCheck(normalizedIntProb, minValue, maxValue)
|
||||
minValue = toNum(minValue, 1);
|
||||
maxValue = toNum(maxValue, 100);
|
||||
return math.random(1,100) <= toNum(normalizedIntProb);
|
||||
end
|
Loading…
x
Reference in New Issue
Block a user