487 lines
15 KiB
Lua
487 lines
15 KiB
Lua
-- boneworld by rnd, 2016
|
|
|
|
-- for more interesting bone gameplay:
|
|
|
|
-- you no longer get extra bones if you pick bones from player with same ip address (no suicide bone farming)
|
|
-- each player has experience points (xp)
|
|
-- when you die you loose 20% of your xp, half of that is stored in bones
|
|
-- (when you kill other player you get 10% of his xp)
|
|
-- if you pick up bones you get xp stored in bones
|
|
-- if you pick up other player bones you get 20% of average of your and bone owner xp award in extra bones (for example if you have 10 xp and you pick noob bone will get 2 bones instead of normally 1)
|
|
|
|
dofile(minetest.get_modpath("boneworld").."/acid.lua") -- acid surprise when mining :)
|
|
dofile(minetest.get_modpath("boneworld").."/gloves.lua") -- acid surprise when mining :)
|
|
|
|
|
|
local version = "12/18/16"
|
|
|
|
local worldpath = minetest.get_worldpath();
|
|
|
|
--os.execute( "mkdir "..worldpath.. "\\boneworld")
|
|
minetest.mkdir(worldpath .. "\\boneworld") -- directory used to save xp data
|
|
|
|
boneworld = {};
|
|
boneworld.xp = {}; -- [name] = bonexp, bone collect xp
|
|
boneworld.digxp = {}; -- [name] = xp, mining xp
|
|
boneworld.thiefxp = {};
|
|
boneworld.killxp = {};
|
|
|
|
boneworld.bounty = {}; --[name]=1
|
|
|
|
|
|
boneworld.protect = {}; -- [name] = {timer, position}: time of last dig in unprotected area, position
|
|
|
|
-- those players get special dig xp when they join
|
|
boneworld.vipdig = {["abba"]=1000000}
|
|
|
|
|
|
--boneworld.killxp = {}; -- xp obtained through kills
|
|
|
|
boneworld.wastedxp = 0; -- xp thats stored in bones and not yet reclaimed
|
|
|
|
|
|
local share_bones_time = tonumber(minetest.setting_get("share_bones_time")) or 1200
|
|
--local share_bones_time = tonumber(minetest.setting_get("share_bones_time")) or 20;
|
|
local share_bones_time_early = tonumber(minetest.setting_get("share_bones_time_early")) or share_bones_time / 4
|
|
|
|
|
|
local function is_owner(pos, name)
|
|
local owner = minetest.get_meta(pos):get_string("owner")
|
|
if owner == "" or owner == name or minetest.check_player_privs(name, "protection_bypass") then
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
|
|
local on_timer = function(pos, elapsed)
|
|
local meta = minetest.get_meta(pos)
|
|
local time = meta:get_int("time")+elapsed;
|
|
if time >= share_bones_time then
|
|
|
|
meta:set_string("infotext", meta:get_string("owner").."'s old bones (died ".. meta:get_string("date") .."), bone xp " ..math.floor(meta:get_float("xp")*100)/100);
|
|
meta:set_string("owner", "")
|
|
|
|
else
|
|
|
|
if meta:get_int("active") == 0 then -- store data in bones, 1x
|
|
meta:set_int("active",1);
|
|
local owner = meta:get_string("owner");
|
|
meta:set_string("date",os.date("%x"));
|
|
meta:set_string("owner_orig",owner);
|
|
meta:set_string("ip", tostring(minetest.get_player_ip(owner)));
|
|
if not minetest.get_player_by_name(owner) then -- mob bones
|
|
boneworld.xp[owner] = 0.1 -- 0.1th of noob player xp in mobs bone
|
|
time=0.9*share_bones_time; -- 10x shorter old bone time
|
|
else
|
|
boneworld.xp[owner] = boneworld.xp[owner] or 1;
|
|
time = 0;
|
|
end
|
|
|
|
if boneworld.xp[owner]<1 then
|
|
meta:set_float("xp", 0.01) -- mobs or bones with 0 xp
|
|
else
|
|
meta:set_float("xp", 0.1); -- player bones give 0.1 xp, same as 10 mob bones
|
|
end
|
|
|
|
boneworld.wastedxp = boneworld.wastedxp + meta:get_float("xp");
|
|
|
|
|
|
if boneworld.bounty[owner] then
|
|
|
|
meta:set_string("owner","");
|
|
meta:set_string("infotext"," Here lies the KILLER " .. owner .. ", bone xp " .. math.floor(meta:get_float("xp")*100)/100 .. ". get his bones !");
|
|
else
|
|
meta:set_string("infotext"," Here lies " .. owner .. ", bone xp " .. math.floor(meta:get_float("xp")*100)/100);
|
|
end
|
|
|
|
end
|
|
|
|
|
|
meta:set_int("time", time)
|
|
return true
|
|
end
|
|
end
|
|
|
|
local on_punch = function(pos, node, player)
|
|
if(not is_owner(pos, player:get_player_name())) then
|
|
return
|
|
end
|
|
|
|
if(minetest.get_meta(pos):get_string("infotext") == "") then
|
|
return
|
|
end
|
|
|
|
local inv = minetest.get_meta(pos):get_inventory()
|
|
local player_inv = player:get_inventory()
|
|
local has_space = true
|
|
|
|
for i=1,inv:get_size("main") do
|
|
local stk = inv:get_stack("main", i)
|
|
if player_inv:room_for_item("main", stk) then
|
|
inv:set_stack("main", i, nil)
|
|
player_inv:add_item("main", stk)
|
|
else
|
|
has_space = false
|
|
break
|
|
end
|
|
end
|
|
|
|
-- remove bones if player emptied them
|
|
if has_space then
|
|
local meta = minetest.get_meta(pos);
|
|
local active = meta:get_int("active") == 1;
|
|
local puncher = player:get_player_name();
|
|
|
|
-- award extra bones/xp if you collect bones from different ip player
|
|
--debug
|
|
if active and meta:get_string("ip")~= tostring(minetest.get_player_ip(puncher)) then
|
|
local xp = meta:get_float("xp");if xp==0 then xp = 0.01 end
|
|
-- average of owners xp (at time of death) and puncher xp will be awarded as extra bones
|
|
-- with every 10 more xp one bone
|
|
|
|
local count;
|
|
if boneworld.xp[puncher]>100 then -- dont give more bones when bone xp exceeds 100
|
|
count = 1 + 0.1*100;
|
|
else
|
|
count = 1+0.1*boneworld.xp[puncher];
|
|
end
|
|
|
|
count = math.floor(count);
|
|
minetest.chat_send_player(puncher, "you find " .. count .. " bones in the corpse.");
|
|
|
|
if player_inv:room_for_item("main", ItemStack("bones:bones "..count)) then
|
|
player_inv:add_item("main", ItemStack("bones:bones "..count))
|
|
else
|
|
minetest.add_item(pos,ItemStack("bones:bones "..count))
|
|
end
|
|
|
|
-- add xp from bones to player who retrieved bones;
|
|
|
|
boneworld.xp[puncher] = boneworld.xp[puncher] + meta:get_float("xp");
|
|
boneworld.wastedxp = boneworld.wastedxp - meta:get_float("xp");
|
|
end
|
|
|
|
local owner_orig = meta:get_string("owner_orig")
|
|
if boneworld.bounty[owner_orig] and owner_orig~="" and owner_orig~= puncher then
|
|
boneworld.bounty[owner_orig]=false;
|
|
minetest.chat_send_all("*** BOUNTY *** for killer " .. owner_orig .. " has been cleared ! ")
|
|
end
|
|
|
|
|
|
minetest.remove_node(pos)
|
|
end
|
|
end
|
|
|
|
|
|
-- load xp
|
|
minetest.register_on_joinplayer(
|
|
function(player)
|
|
local name = player:get_player_name();
|
|
if not boneworld.xp[name] then -- load xp
|
|
local filename = worldpath .. "\\boneworld\\" .. name..".xp";
|
|
local f = io.open(filename, "r");
|
|
if not f then -- file does not yet exist
|
|
boneworld.xp[name] = 1;
|
|
boneworld.digxp[name] = 0;
|
|
boneworld.thiefxp[name] = 0;
|
|
boneworld.killxp[name] = 0;
|
|
|
|
else
|
|
local str = f:read("*a") or 1;
|
|
local words = {};
|
|
for w in str:gmatch("%S+") do
|
|
words[#words+1]=w
|
|
end
|
|
boneworld.xp[name] = tonumber(words[1] or 1);
|
|
boneworld.digxp[name] = tonumber(words[2] or 0);
|
|
boneworld.thiefxp[name] = tonumber(words[3] or 0);
|
|
boneworld.killxp[name] = tonumber(words[4] or 0);
|
|
f:close();
|
|
end
|
|
end
|
|
|
|
if boneworld.vipdig[name] then
|
|
if boneworld.digxp[name]<boneworld.vipdig[name] then
|
|
boneworld.digxp[name] = boneworld.vipdig[name];
|
|
end
|
|
end
|
|
|
|
end
|
|
)
|
|
|
|
-- save xp
|
|
minetest.register_on_leaveplayer(
|
|
function(player)
|
|
local name = player:get_player_name();
|
|
local bonexp = boneworld.xp[name] or 1;
|
|
local digxp = boneworld.digxp[name] or 0;
|
|
local thiefxp = boneworld.thiefxp[name] or 0;
|
|
local killxp = boneworld.killxp[name] or 0;
|
|
|
|
--debug
|
|
if bonexp > 1.5 or digxp>0.25 then -- save xp for serious players only
|
|
local filename = worldpath .. "\\boneworld\\" .. name..".xp";
|
|
|
|
local f = io.open(filename, "w");
|
|
if not f then return end
|
|
f:write(bonexp .. " " .. digxp .. " " .. thiefxp .. " " .. killxp );
|
|
f:close();
|
|
else
|
|
-- dont save, player didnt do anything
|
|
end
|
|
end
|
|
|
|
)
|
|
|
|
boneworld.save_xp = function(player)
|
|
if not player then return end
|
|
local name = player:get_player_name();
|
|
local bonexp = boneworld.xp[name] or 1;
|
|
local digxp = boneworld.digxp[name] or 0;
|
|
local thiefxp = boneworld.thiefxp[name] or 0;
|
|
|
|
--debug
|
|
if bonexp > 1.5 or digxp>0.25 then -- save xp for serious players only
|
|
local filename = worldpath .. "\\boneworld\\" .. name..".xp";
|
|
|
|
local f = io.open(filename, "w");
|
|
if not f then return end
|
|
f:write(bonexp .. " " .. digxp .. " " .. thiefxp );
|
|
f:close();
|
|
else
|
|
-- dont save, player didnt do anything
|
|
end
|
|
end
|
|
|
|
boneworld.save_all_xp = function()
|
|
local players = minetest.get_connected_players();
|
|
for _,player in pairs(players) do
|
|
boneworld.save_xp(player)
|
|
end
|
|
end
|
|
|
|
minetest.register_on_leaveplayer(boneworld.save_xp)
|
|
minetest.register_on_shutdown(boneworld.save_all_xp)
|
|
|
|
|
|
minetest.register_on_dieplayer( -- -1 bone xp each time you die; otherwise no motivation to be careful
|
|
function(player)
|
|
local name = player:get_player_name();
|
|
local xp = boneworld.xp[name] or 1;
|
|
if xp>2 then
|
|
xp=xp-1
|
|
else
|
|
xp = 1;
|
|
end
|
|
boneworld.xp[name]=xp;
|
|
end
|
|
)
|
|
|
|
minetest.register_on_punchplayer(
|
|
function(player, hitter, time_from_last_punch, tool_capabilities, dir, damage)
|
|
local php = player:get_hp();
|
|
|
|
--minetest.chat_send_all(" HIT " .. php .. " " .. damage)
|
|
|
|
if php>0 and php-damage<=0 then -- killed
|
|
local hname = hitter:get_player_name();
|
|
local killxp = boneworld.killxp[hname] or 0;
|
|
boneworld.killxp[hname] = killxp + 0.01;
|
|
|
|
local pname = player:get_player_name();
|
|
if boneworld.bounty[pname] and boneworld.bounty[pname]>0 then
|
|
boneworld.bounty[pname] = -1
|
|
minetest.chat_send_all("**** BOUNTY **** killer " .. pname .. " has been killed! You can now get his bones as a reward!");
|
|
local killxp = boneworld.killxp[pname] or 0;
|
|
killxp = killxp-0.1; if killxp <0 then killxp = 0 end
|
|
boneworld.killxp[pname] = killxp;
|
|
end
|
|
|
|
|
|
end
|
|
|
|
end
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
local tweak_bones = function()
|
|
local name = "bones:bones";
|
|
local table = minetest.registered_nodes[name];
|
|
--table.on_construct = on_construct;
|
|
table.on_punch = on_punch;
|
|
table.on_timer = on_timer;
|
|
minetest.register_node(":"..name, table);
|
|
end
|
|
|
|
minetest.after(0,tweak_bones);
|
|
|
|
minetest.register_chatcommand("xp", {
|
|
description = "xp name - show experience of target player " .. version,
|
|
privs = {
|
|
interact = true
|
|
},
|
|
func = function(name, param)
|
|
|
|
local msg;
|
|
if param == "" then
|
|
local xp = math.floor((boneworld.xp[name] or 1)*100)/100;
|
|
local digxp = math.floor((boneworld.digxp[name] or 0)*100)/100;
|
|
local thiefxp = math.floor((boneworld.thiefxp[name] or 0)*100)/100;
|
|
local killxp = math.floor((boneworld.killxp[name] or 0)*100)/100;
|
|
|
|
msg = "xp name - show experience of target player"
|
|
.."\n# "..name .. " has " .. xp .. " bone collecting experience, ".. digxp .. " digging experience" ..
|
|
" (can dig to ".. math.floor(200+50*math.sqrt(digxp)) .. "), " .. thiefxp.. " thief experience, "..
|
|
killxp .. " kill experience "..
|
|
"\nTotal xp stored in bones in world is " .. math.floor(boneworld.wastedxp*100)/100;
|
|
else
|
|
local xp = math.floor((boneworld.xp[param] or 1)*100)/100;
|
|
local digxp = math.floor((boneworld.digxp[param] or 0)*100)/100;
|
|
local thiefxp = math.floor((boneworld.thiefxp[param] or 0)*100)/100;
|
|
local killxp = math.floor((boneworld.killxp[param] or 0)*100)/100;
|
|
|
|
msg = "xp name - show experience of target player (10.04.16)"
|
|
.."\n# "..param .. " has " .. xp .. " bone collecting experience, ".. digxp .. " digging experience, " .. thiefxp .. " thief experience, " .. killxp .. " kill experience ";
|
|
|
|
end
|
|
minetest.chat_send_player(name, msg);
|
|
end
|
|
})
|
|
|
|
|
|
-- limit digging to above -(200+xp*5)
|
|
local old_is_protected = minetest.is_protected
|
|
function minetest.is_protected(pos, name)
|
|
|
|
local is_protected_new = old_is_protected(pos, name);
|
|
if pos.y>-200 or name == "" then
|
|
|
|
else
|
|
--to do : digxp here!!
|
|
local digxp = boneworld.digxp[name] or 0;
|
|
|
|
local maxdepth = 200+50*math.sqrt(digxp);
|
|
if pos.y<-maxdepth then
|
|
minetest.chat_send_player(name, "You can only dig above -"..math.floor(maxdepth) .. ". Get more dig experience to dig deeper");
|
|
local player = minetest.get_player_by_name(name); if not player then return true end
|
|
if pos.y<-maxdepth-5 then
|
|
player:setpos({x=0,y=1,z=0})
|
|
minetest.chat_send_player(name, "You tried to dig too deep. Get more dig experience to dig deeper");
|
|
end
|
|
is_protected_new = true
|
|
|
|
--local player = minetest.get_player_by_name(name); if not player then return true end
|
|
--local protect_data = boneworld.protect[name];
|
|
--local tpos;
|
|
-- if not protect_data then -- safety check
|
|
-- boneworld.protect[name] = {t=minetest.get_gametime(), pos=pos};
|
|
-- tpos = pos
|
|
-- else
|
|
-- tpos = boneworld.protect[name].pos;
|
|
-- end
|
|
-- player:setpos({x=tpos.x,y=tpos.y+1,z=tpos.z});
|
|
|
|
end
|
|
end
|
|
|
|
-- if not is_protected_new then -- remember time, pos of last dig in unprotected area
|
|
-- local t1 = minetest.get_gametime();
|
|
-- local t0;
|
|
-- local protect_data = boneworld.protect[name];
|
|
-- if not protect_data then
|
|
-- boneworld.protect[name] = {t=t1, pos=pos};
|
|
-- t0 = t1;
|
|
-- else
|
|
-- t0 = boneworld.protect[name].t;
|
|
-- end
|
|
|
|
-- if t1-t0>10 then -- "time" to remember new time, pos
|
|
-- boneworld.protect[name].t = t1;
|
|
-- boneworld.protect[name].pos = pos;
|
|
-- end
|
|
-- end
|
|
|
|
return is_protected_new;
|
|
end
|
|
|
|
-- mining xp
|
|
|
|
-- how much mining xp digging minerals yields
|
|
boneworld.mineralxp = {
|
|
["default:stone"] = 0.01,
|
|
["default:stone_with_coal"] = 0.03,
|
|
["default:stone_with_iron"] = 0.1,
|
|
["default:stone_with_copper"] = 0.1,
|
|
["default:stone_with_gold"] = 0.2,
|
|
["default:stone_with_mese"] = 0.5,
|
|
["default:stone_with_diamond"] = 1,
|
|
|
|
["moreores:mineral_silver"] = 0.2,
|
|
["moreores:mineral_tin"] = 0.1,
|
|
["moreores:mineral_mithril"] = 2,
|
|
}
|
|
|
|
|
|
local after_dig_node = function(pos, oldnode, oldmetadata, digger)
|
|
local nodename = oldnode.name;
|
|
local name = digger:get_player_name();
|
|
local digxp = boneworld.mineralxp[nodename] or 0; digxp = digxp*0.1; -- bonus xp
|
|
local xp = boneworld.digxp[name] or 0;
|
|
xp = xp + digxp;
|
|
boneworld.digxp[name] = xp;
|
|
|
|
if digxp>0 and pos.y<-100 then -- only underground; acid surprise :)
|
|
if math.random(100) == 1 then
|
|
minetest.set_node(pos, {name="boneworld:acid_source_active"})
|
|
end
|
|
end
|
|
|
|
-- extra reward with small probability
|
|
if xp<100 or nodename == "default:stone" or digxp == 0 then return end
|
|
|
|
local P;
|
|
if xp>10000 then
|
|
P=0.5
|
|
else
|
|
P = (xp/10000+0.0001)*0.5;
|
|
end
|
|
|
|
if math.random(1/P) == 1 then
|
|
P=1;
|
|
end
|
|
|
|
if P==1 then
|
|
|
|
local player_inv = digger:get_inventory()
|
|
local stk = ItemStack(nodename);
|
|
if player_inv:room_for_item("main", stk) then
|
|
--minetest.chat_send_player(name, "Congratulations! You found extra " .. nodename)
|
|
player_inv:add_item("main", stk)
|
|
end
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
local set_after_dig_node = function(name)
|
|
local tabl = minetest.registered_nodes[name];
|
|
if not tabl then return end
|
|
minetest.override_item(name, {after_dig_node = after_dig_node})
|
|
end
|
|
|
|
minetest.after(0,
|
|
function()
|
|
set_after_dig_node("default:stone");
|
|
set_after_dig_node("default:stone_with_iron");
|
|
set_after_dig_node("default:stone_with_copper");
|
|
set_after_dig_node("default:stone_with_coal");
|
|
set_after_dig_node("default:stone_with_gold");
|
|
set_after_dig_node("default:stone_with_mese");
|
|
set_after_dig_node("default:stone_with_diamond");
|
|
end
|
|
) |