2015-04-07 13:01:43 -07:00
|
|
|
--[[
|
|
|
|
|
|
|
|
Thirsty mod [thirsty]
|
|
|
|
==========================
|
|
|
|
|
|
|
|
A mod that adds a "thirst" mechanic, similar to hunger.
|
|
|
|
|
|
|
|
Copyright (C) 2015 Ben Deutsch <ben@bendeutsch.de>
|
|
|
|
|
2015-04-13 14:11:50 -07:00
|
|
|
License
|
|
|
|
-------
|
|
|
|
|
2015-04-07 13:01:43 -07:00
|
|
|
This library 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 library 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 library; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
|
|
|
USA
|
|
|
|
|
2015-04-13 14:11:50 -07:00
|
|
|
Terminology: "Thirst" vs. "hydration"
|
|
|
|
-------------------------------------
|
|
|
|
|
|
|
|
"Thirst" is the absence of "hydration" (a term suggested by
|
|
|
|
everamzah on the Minetest forums, thanks!). The overall mechanic
|
|
|
|
is still called "thirst", but the visible bar is that of
|
|
|
|
"hydration", filled with "hydro points".
|
|
|
|
|
2015-04-07 13:01:43 -07:00
|
|
|
]]
|
|
|
|
|
2015-04-11 14:06:31 -07:00
|
|
|
-- the main module variable
|
|
|
|
thirsty = {
|
|
|
|
|
|
|
|
-- Configuration variables
|
2015-05-12 22:06:50 -07:00
|
|
|
config = {
|
2015-05-26 21:49:22 -07:00
|
|
|
-- see configuration.lua
|
2015-04-26 13:53:09 -07:00
|
|
|
},
|
|
|
|
|
2015-04-11 14:06:31 -07:00
|
|
|
-- the players' values
|
|
|
|
players = {
|
|
|
|
--[[
|
|
|
|
name = {
|
2015-04-13 14:11:50 -07:00
|
|
|
hydro = 20,
|
2015-04-11 14:06:31 -07:00
|
|
|
last_pos = '-10:3',
|
|
|
|
time_in_pos = 0.0,
|
2015-04-12 13:12:48 -07:00
|
|
|
pending_dmg = 0.0,
|
2015-04-11 14:06:31 -07:00
|
|
|
}
|
2015-04-28 14:28:51 -07:00
|
|
|
]]
|
|
|
|
},
|
|
|
|
|
|
|
|
-- water fountains
|
|
|
|
fountains = {
|
|
|
|
--[[
|
|
|
|
x:y:z = {
|
|
|
|
pos = { x=x, y=y, z=z },
|
|
|
|
level = 4,
|
2015-05-17 21:59:22 -07:00
|
|
|
time_until_check = 20,
|
2015-04-28 14:28:51 -07:00
|
|
|
-- something about times
|
|
|
|
}
|
|
|
|
]]
|
2015-04-11 14:06:31 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
-- general settings
|
|
|
|
time_next_tick = 0.0,
|
2015-04-07 13:01:43 -07:00
|
|
|
}
|
|
|
|
|
2015-05-26 21:49:22 -07:00
|
|
|
dofile(minetest.get_modpath('thirsty')..'/configuration.lua')
|
2015-04-16 12:39:43 -07:00
|
|
|
|
2015-05-26 21:49:22 -07:00
|
|
|
thirsty.time_next_tick = thirsty.config.tick_time
|
2015-04-16 12:39:43 -07:00
|
|
|
|
2015-05-26 21:49:22 -07:00
|
|
|
dofile(minetest.get_modpath('thirsty')..'/hud.lua')
|
2015-04-07 13:01:43 -07:00
|
|
|
|
|
|
|
minetest.register_on_joinplayer(function(player)
|
2015-04-11 13:21:09 -07:00
|
|
|
local name = player:get_player_name()
|
2015-04-12 12:37:29 -07:00
|
|
|
-- default entry for new players
|
|
|
|
if not thirsty.players[name] then
|
|
|
|
local pos = player:getpos()
|
|
|
|
thirsty.players[name] = {
|
2015-04-13 14:11:50 -07:00
|
|
|
hydro = 20,
|
2015-04-12 12:37:29 -07:00
|
|
|
last_pos = math.floor(pos.x) .. ':' .. math.floor(pos.z),
|
|
|
|
time_in_pos = 0.0,
|
2015-04-12 13:12:48 -07:00
|
|
|
pending_dmg = 0.0,
|
2015-04-12 12:37:29 -07:00
|
|
|
}
|
|
|
|
end
|
2015-04-16 12:39:43 -07:00
|
|
|
thirsty.hud_init(player)
|
2015-04-07 13:01:43 -07:00
|
|
|
end)
|
|
|
|
|
2015-04-20 13:23:30 -07:00
|
|
|
minetest.register_on_dieplayer(function(player)
|
|
|
|
local name = player:get_player_name()
|
|
|
|
-- fill after death
|
|
|
|
thirsty.players[name].hydro = 20;
|
|
|
|
end)
|
|
|
|
|
2015-04-15 14:41:39 -07:00
|
|
|
--[[
|
|
|
|
|
|
|
|
Main Loop (Tier 0)
|
|
|
|
|
|
|
|
]]
|
|
|
|
|
|
|
|
|
2015-04-07 13:01:43 -07:00
|
|
|
minetest.register_globalstep(function(dtime)
|
|
|
|
-- get thirsty
|
2015-04-11 14:06:31 -07:00
|
|
|
thirsty.time_next_tick = thirsty.time_next_tick - dtime
|
|
|
|
while thirsty.time_next_tick < 0.0 do
|
2015-04-07 13:01:43 -07:00
|
|
|
-- time for thirst
|
2015-05-12 22:06:50 -07:00
|
|
|
thirsty.time_next_tick = thirsty.time_next_tick + thirsty.config.tick_time
|
2015-04-07 13:01:43 -07:00
|
|
|
for _,player in ipairs(minetest.get_connected_players()) do
|
2015-04-20 13:23:30 -07:00
|
|
|
|
|
|
|
if player:get_hp() <= 0 then
|
|
|
|
-- dead players don't get thirsty, or full for that matter :-P
|
|
|
|
break
|
|
|
|
end
|
|
|
|
|
2015-04-07 13:01:43 -07:00
|
|
|
local name = player:get_player_name()
|
|
|
|
local pos = player:getpos()
|
2015-04-11 14:06:31 -07:00
|
|
|
local pl = thirsty.players[name]
|
2015-04-11 13:21:09 -07:00
|
|
|
|
|
|
|
-- how long have we been standing "here"?
|
|
|
|
-- (the node coordinates in X and Z should be enough)
|
|
|
|
local pos_hash = math.floor(pos.x) .. ':' .. math.floor(pos.z)
|
2015-04-11 14:06:31 -07:00
|
|
|
if pl.last_pos == pos_hash then
|
2015-05-12 22:06:50 -07:00
|
|
|
pl.time_in_pos = pl.time_in_pos + thirsty.config.tick_time
|
2015-04-11 13:21:09 -07:00
|
|
|
else
|
|
|
|
-- you moved!
|
2015-04-11 14:06:31 -07:00
|
|
|
pl.last_pos = pos_hash
|
|
|
|
pl.time_in_pos = 0.0
|
2015-04-11 13:21:09 -07:00
|
|
|
end
|
2015-05-12 22:06:50 -07:00
|
|
|
local pl_standing = pl.time_in_pos > thirsty.config.stand_still_for_drink
|
|
|
|
local pl_afk = pl.time_in_pos > thirsty.config.stand_still_for_afk
|
2015-04-13 12:55:49 -07:00
|
|
|
--print("Standing: " .. (pl_standing and 'true' or 'false' ) .. ", AFK: " .. (pl_afk and 'true' or 'false'))
|
2015-04-11 13:21:09 -07:00
|
|
|
|
|
|
|
pos.y = pos.y + 0.1
|
2015-04-07 13:01:43 -07:00
|
|
|
local node = minetest.get_node(pos)
|
2015-05-12 22:06:50 -07:00
|
|
|
local drink_per_second = thirsty.config.regen_from_node[node.name] or 0
|
2015-04-28 14:28:51 -07:00
|
|
|
|
|
|
|
-- fountaining (uses pos, slight changes ok)
|
|
|
|
for k, fountain in pairs(thirsty.fountains) do
|
|
|
|
local dx = fountain.pos.x - pos.x
|
|
|
|
local dy = fountain.pos.y - pos.y
|
|
|
|
local dz = fountain.pos.z - pos.z
|
|
|
|
local dist2 = dx * dx + dy * dy + dz * dz
|
|
|
|
local fdist = fountain.level * 5 -- max 100 nodes radius
|
|
|
|
--print (string.format("Distance from %s (%d): %f out of %f", k, fountain.level, math.sqrt(dist2), fdist ))
|
|
|
|
if dist2 < fdist * fdist then
|
|
|
|
-- in range, drink as if standing (still) in water
|
2015-05-12 22:06:50 -07:00
|
|
|
drink_per_second = math.max(thirsty.config.regen_from_node['default:water_source'] or 0, drink_per_second)
|
2015-04-28 14:28:51 -07:00
|
|
|
pl_standing = true
|
|
|
|
break -- no need to check the other fountains
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-05-05 22:14:59 -07:00
|
|
|
-- amulets
|
2015-05-10 21:54:32 -07:00
|
|
|
-- TODO: I *guess* we need to optimize this, but I haven't
|
|
|
|
-- measured it yet. No premature optimizations!
|
|
|
|
local pl_inv = player:get_inventory()
|
|
|
|
if pl_inv:contains_item('main', 'thirsty:injector') or pl_inv:contains_item('main', 'thirsty:extractor') then
|
|
|
|
local extractor_found = false
|
|
|
|
local injector_found = false
|
|
|
|
local container_not_full = nil
|
|
|
|
local container_not_empty = nil
|
|
|
|
local inv_main = player:get_inventory():get_list('main')
|
|
|
|
for i, itemstack in ipairs(inv_main) do
|
|
|
|
local name = itemstack:get_name()
|
|
|
|
if name == 'thirsty:injector' then
|
|
|
|
injector_found = true
|
|
|
|
end
|
|
|
|
if name == 'thirsty:extractor' then
|
|
|
|
extractor_found = true
|
|
|
|
end
|
2015-05-12 22:06:50 -07:00
|
|
|
if thirsty.config.container_capacity[name] then
|
2015-05-10 21:54:32 -07:00
|
|
|
local wear = itemstack:get_wear()
|
|
|
|
-- can be both!
|
|
|
|
if wear == 0 or wear > 1 then
|
|
|
|
container_not_full = { i, itemstack }
|
|
|
|
end
|
|
|
|
if wear > 0 and wear < 65534 then
|
|
|
|
container_not_empty = { i, itemstack }
|
|
|
|
end
|
|
|
|
end
|
2015-05-05 22:14:59 -07:00
|
|
|
end
|
2015-05-10 21:54:32 -07:00
|
|
|
if extractor_found and container_not_full then
|
|
|
|
local i = container_not_full[1]
|
|
|
|
local itemstack = container_not_full[2]
|
2015-05-12 22:06:50 -07:00
|
|
|
local capacity = thirsty.config.container_capacity[itemstack:get_name()]
|
2015-05-10 21:54:32 -07:00
|
|
|
local wear = itemstack:get_wear()
|
|
|
|
if wear == 0 then wear = 65535.0 end
|
2015-05-12 22:06:50 -07:00
|
|
|
local drink = thirsty.config.extractor_speed * thirsty.config.tick_time
|
2015-05-10 21:54:32 -07:00
|
|
|
local drinkwear = drink / capacity * 65535.0
|
|
|
|
wear = wear - drinkwear
|
|
|
|
if wear < 1 then wear = 1 end
|
|
|
|
itemstack:set_wear(wear)
|
|
|
|
player:get_inventory():set_stack("main", i, itemstack)
|
2015-05-05 22:14:59 -07:00
|
|
|
end
|
2015-05-10 21:54:32 -07:00
|
|
|
if injector_found and container_not_empty then
|
|
|
|
local i = container_not_empty[1]
|
|
|
|
local itemstack = container_not_empty[2]
|
2015-05-12 22:06:50 -07:00
|
|
|
local capacity = thirsty.config.container_capacity[itemstack:get_name()]
|
2015-05-05 22:14:59 -07:00
|
|
|
local wear = itemstack:get_wear()
|
2015-05-10 21:54:32 -07:00
|
|
|
if wear == 0 then wear = 65535.0 end
|
2015-05-12 22:06:50 -07:00
|
|
|
local drink = thirsty.config.injector_speed * thirsty.config.tick_time
|
2015-05-10 21:54:32 -07:00
|
|
|
local drink_missing = 20 - pl.hydro
|
|
|
|
drink = math.max(math.min(drink, drink_missing), 0)
|
|
|
|
local drinkwear = drink / capacity * 65535.0
|
|
|
|
wear = wear + drinkwear
|
|
|
|
if wear > 65534 then wear = 65534 end
|
|
|
|
itemstack:set_wear(wear)
|
|
|
|
pl.hydro = pl.hydro + drink
|
|
|
|
if pl.hydro > 20 then pl.hydro = 20 end
|
|
|
|
player:get_inventory():set_stack("main", i, itemstack)
|
2015-05-05 22:14:59 -07:00
|
|
|
end
|
2015-05-11 22:07:52 -07:00
|
|
|
end -- if contains_item injector or extractor
|
2015-05-05 22:14:59 -07:00
|
|
|
|
|
|
|
|
2015-05-11 22:07:52 -07:00
|
|
|
if drink_per_second > 0 and pl_standing then
|
2015-05-12 22:06:50 -07:00
|
|
|
pl.hydro = pl.hydro + drink_per_second * thirsty.config.tick_time
|
2015-05-11 22:07:52 -07:00
|
|
|
-- Drinking from the ground won't give you more than max
|
|
|
|
if pl.hydro > 20 then pl.hydro = 20 end
|
2015-05-12 22:06:50 -07:00
|
|
|
--print("Raising hydration by "..(drink_per_second*thirsty.config.tick_time).." to "..pl.hydro)
|
2015-05-11 22:07:52 -07:00
|
|
|
else
|
|
|
|
if not pl_afk then
|
|
|
|
-- only get thirsty if not AFK
|
2015-05-12 22:06:50 -07:00
|
|
|
pl.hydro = pl.hydro - thirsty.config.thirst_per_second * thirsty.config.tick_time
|
2015-05-11 22:07:52 -07:00
|
|
|
if pl.hydro < 0 then pl.hydro = 0 end
|
2015-05-12 22:06:50 -07:00
|
|
|
--print("Lowering hydration by "..(thirsty.config.thirst_per_second*thirsty.config.tick_time).." to "..pl.hydro)
|
2015-04-11 13:21:09 -07:00
|
|
|
end
|
2015-05-11 22:07:52 -07:00
|
|
|
end
|
2015-04-28 14:28:51 -07:00
|
|
|
|
|
|
|
|
2015-04-11 13:21:09 -07:00
|
|
|
-- should we only update the hud on an actual change?
|
2015-04-16 12:39:43 -07:00
|
|
|
thirsty.hud_update(player, pl.hydro)
|
2015-04-12 13:12:48 -07:00
|
|
|
|
|
|
|
-- damage, if enabled
|
|
|
|
if minetest.setting_getbool("enable_damage") then
|
|
|
|
-- maybe not the best way to do this, but it does mean
|
|
|
|
-- we can do anything with one tick loop
|
2015-04-13 14:11:50 -07:00
|
|
|
if pl.hydro <= 0.0 and not pl_afk then
|
2015-05-12 22:06:50 -07:00
|
|
|
pl.pending_dmg = pl.pending_dmg + thirsty.config.damage_per_second * thirsty.config.tick_time
|
2015-04-13 12:55:49 -07:00
|
|
|
--print("Pending damage at " .. pl.pending_dmg)
|
2015-04-12 13:12:48 -07:00
|
|
|
if pl.pending_dmg > 1.0 then
|
|
|
|
local dmg = math.floor(pl.pending_dmg)
|
|
|
|
pl.pending_dmg = pl.pending_dmg - dmg
|
|
|
|
player:set_hp( player:get_hp() - dmg )
|
|
|
|
end
|
|
|
|
else
|
|
|
|
-- forget any pending damage when not thirsty
|
|
|
|
pl.pending_dmg = 0.0
|
|
|
|
end
|
|
|
|
end
|
2015-05-17 21:59:22 -07:00
|
|
|
end -- for players
|
|
|
|
|
|
|
|
-- check fountains for expiration
|
|
|
|
for k, fountain in pairs(thirsty.fountains) do
|
|
|
|
fountain.time_until_check = fountain.time_until_check - thirsty.config.tick_time
|
|
|
|
if fountain.time_until_check <= 0 then
|
|
|
|
-- remove fountain, the abm will set it again
|
|
|
|
--print("Removing fountain at " .. k)
|
|
|
|
thirsty.fountains[k] = nil
|
|
|
|
end
|
2015-04-07 13:01:43 -07:00
|
|
|
end
|
2015-05-17 21:59:22 -07:00
|
|
|
|
2015-04-07 13:01:43 -07:00
|
|
|
end
|
|
|
|
end)
|
2015-04-12 12:37:29 -07:00
|
|
|
|
|
|
|
--[[
|
|
|
|
|
2015-04-13 14:11:50 -07:00
|
|
|
Stash: persist the hydration values in a file in the world directory.
|
2015-04-12 12:37:29 -07:00
|
|
|
|
|
|
|
If this is missing or corrupted, then no worries: nobody's thirsty ;-)
|
|
|
|
|
2015-04-15 14:41:39 -07:00
|
|
|
]]
|
2015-04-12 12:37:29 -07:00
|
|
|
|
|
|
|
function thirsty.read_stash()
|
2015-05-12 22:06:50 -07:00
|
|
|
local filename = minetest.get_worldpath() .. "/" .. thirsty.config.stash_filename
|
2015-04-12 12:37:29 -07:00
|
|
|
local file, err = io.open(filename, "r")
|
|
|
|
if not file then
|
|
|
|
-- no problem, it's just not there
|
|
|
|
-- TODO: or parse err?
|
|
|
|
return
|
|
|
|
end
|
2015-04-13 14:11:50 -07:00
|
|
|
thirsty.players = {}
|
|
|
|
for line in file:lines() do
|
|
|
|
if string.match(line, '^%-%-') then
|
|
|
|
-- comment, ignore
|
|
|
|
elseif string.match(line, '^P [%d.]+ [%d.]+ .+') then
|
|
|
|
-- player line
|
|
|
|
-- is matching again really the best solution?
|
|
|
|
local hydro, dmg, name = string.match(line, '^P ([%d.]+) ([%d.]+) (.+)')
|
|
|
|
thirsty.players[name] = {
|
|
|
|
hydro = tonumber(hydro),
|
|
|
|
last_pos = '0:0', -- not true, but no matter
|
|
|
|
time_in_pos = 0.0,
|
|
|
|
pending_dmg = tonumber(dmg),
|
|
|
|
}
|
2015-04-12 13:11:55 -07:00
|
|
|
end
|
2015-04-12 12:37:29 -07:00
|
|
|
end
|
|
|
|
file:close()
|
|
|
|
end
|
|
|
|
|
|
|
|
function thirsty.write_stash()
|
2015-05-12 22:06:50 -07:00
|
|
|
local filename = minetest.get_worldpath() .. "/" .. thirsty.config.stash_filename
|
2015-04-12 12:37:29 -07:00
|
|
|
local file, err = io.open(filename, "w")
|
|
|
|
if not file then
|
2015-05-12 22:06:50 -07:00
|
|
|
minetest.log("error", "Thirsty: could not write " .. thirsty.config.stash_filename .. ": " ..err)
|
2015-04-12 12:37:29 -07:00
|
|
|
return
|
|
|
|
end
|
2015-04-13 14:11:50 -07:00
|
|
|
file:write('-- Stash file for Minetest mod [thirsty] --\n')
|
|
|
|
-- write players:
|
|
|
|
-- P <hydro> <pending_dmg> <name>
|
|
|
|
file:write('-- Player format: "P <hydro> <pending damage> <name>"\n')
|
|
|
|
for name, data in pairs(thirsty.players) do
|
|
|
|
file:write("P " .. data.hydro .. " " .. data.pending_dmg .. " " .. name .. "\n")
|
|
|
|
end
|
2015-04-12 12:37:29 -07:00
|
|
|
file:close()
|
|
|
|
end
|
|
|
|
|
2015-04-15 14:41:39 -07:00
|
|
|
--[[
|
|
|
|
|
2015-04-26 13:53:09 -07:00
|
|
|
General handler
|
2015-04-15 14:41:39 -07:00
|
|
|
|
2015-04-26 13:53:09 -07:00
|
|
|
Most tools, nodes and craftitems use the same code, so here it is:
|
2015-04-15 14:41:39 -07:00
|
|
|
|
|
|
|
]]
|
|
|
|
|
2015-04-26 13:53:09 -07:00
|
|
|
function thirsty.drink_handler(player, itemstack, node)
|
|
|
|
local pl = thirsty.players[player:get_player_name()]
|
|
|
|
local old_hydro = pl.hydro
|
|
|
|
|
|
|
|
-- selectors, always true, to make the following code easier
|
|
|
|
local item_name = itemstack and itemstack:get_name() or ':'
|
|
|
|
local node_name = node and node.name or ':'
|
|
|
|
|
2015-05-12 22:06:50 -07:00
|
|
|
if thirsty.config.node_drinkable[node_name] then
|
2015-04-26 13:53:09 -07:00
|
|
|
-- we found something to drink!
|
2015-05-12 22:06:50 -07:00
|
|
|
local cont_level = thirsty.config.drink_from_container[item_name] or 0
|
|
|
|
local node_level = thirsty.config.drink_from_node[node_name] or 0
|
2015-04-26 13:53:09 -07:00
|
|
|
-- drink until level
|
|
|
|
local level = math.max(cont_level, node_level)
|
|
|
|
--print("Drinking to level " .. level)
|
|
|
|
if pl.hydro < level then
|
|
|
|
pl.hydro = level
|
|
|
|
end
|
|
|
|
|
|
|
|
-- fill container, if applicable
|
2015-05-12 22:06:50 -07:00
|
|
|
if thirsty.config.container_capacity[item_name] then
|
|
|
|
--print("Filling a " .. item_name .. " to " .. thirsty.config.container_capacity[item_name])
|
2015-04-26 13:53:09 -07:00
|
|
|
itemstack:set_wear(1) -- "looks full"
|
|
|
|
end
|
|
|
|
|
2015-05-12 22:06:50 -07:00
|
|
|
elseif thirsty.config.container_capacity[item_name] then
|
2015-04-26 13:53:09 -07:00
|
|
|
-- drinking from a container
|
|
|
|
if itemstack:get_wear() ~= 0 then
|
2015-05-12 22:06:50 -07:00
|
|
|
local capacity = thirsty.config.container_capacity[item_name]
|
2015-04-26 13:53:09 -07:00
|
|
|
local hydro_missing = 20 - pl.hydro;
|
|
|
|
if hydro_missing > 0 then
|
|
|
|
local wear_missing = hydro_missing / capacity * 65535.0;
|
|
|
|
local wear = itemstack:get_wear()
|
|
|
|
local new_wear = math.ceil(math.max(wear + wear_missing, 1))
|
|
|
|
if (new_wear > 65534) then
|
|
|
|
wear_missing = 65534 - wear
|
|
|
|
new_wear = 65534
|
|
|
|
end
|
|
|
|
itemstack:set_wear(new_wear)
|
|
|
|
if wear_missing > 0 then -- rounding glitches?
|
|
|
|
pl.hydro = pl.hydro + (wear_missing * capacity / 65535.0)
|
2015-04-22 13:42:17 -07:00
|
|
|
end
|
2015-04-15 14:41:39 -07:00
|
|
|
end
|
|
|
|
end
|
2015-04-26 13:53:09 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
-- update HUD if value changed
|
|
|
|
if pl.hydro ~= old_hydro then
|
|
|
|
thirsty.hud_update(player, pl.hydro)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
--[[
|
|
|
|
|
|
|
|
Adapters for drink_handler to on_use and on_rightclick slots.
|
|
|
|
These close over the next handler to call in a chain, if desired.
|
|
|
|
|
|
|
|
]]
|
|
|
|
|
|
|
|
function thirsty.on_use( old_on_use )
|
|
|
|
return function(itemstack, player, pointed_thing)
|
|
|
|
local node = nil
|
|
|
|
if pointed_thing and pointed_thing.type == 'node' then
|
|
|
|
node = minetest.get_node(pointed_thing.under)
|
|
|
|
end
|
|
|
|
|
|
|
|
thirsty.drink_handler(player, itemstack, node)
|
|
|
|
|
|
|
|
-- call original on_use, if provided
|
2015-04-22 13:42:17 -07:00
|
|
|
if old_on_use ~= nil then
|
2015-04-26 13:53:09 -07:00
|
|
|
return old_on_use(itemstack, player, pointed_thing)
|
2015-04-22 13:42:17 -07:00
|
|
|
else
|
2015-04-26 13:53:09 -07:00
|
|
|
return itemstack
|
2015-04-22 13:42:17 -07:00
|
|
|
end
|
2015-04-15 14:41:39 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-04-26 13:53:09 -07:00
|
|
|
function thirsty.on_rightclick( old_on_rightclick )
|
|
|
|
return function(pos, node, player, itemstack, pointed_thing)
|
|
|
|
|
|
|
|
thirsty.drink_handler(player, itemstack, node)
|
|
|
|
|
|
|
|
-- call original on_rightclick, if provided
|
|
|
|
if old_on_rightclick ~= nil then
|
|
|
|
return old_on_rightclick(pos, node, player, itemstack, pointed_thing)
|
|
|
|
else
|
|
|
|
return itemstack
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
--[[
|
|
|
|
|
|
|
|
Drinking containers (Tier 1)
|
|
|
|
|
|
|
|
Defines a simple wooden bowl which can be used on water to fill
|
|
|
|
your hydration.
|
|
|
|
|
|
|
|
Optionally also augments the nodes from vessels to enable drinking
|
|
|
|
on use.
|
|
|
|
|
|
|
|
]]
|
|
|
|
|
|
|
|
function thirsty.augment_node_for_drinking( nodename, level )
|
2015-04-22 22:08:51 -07:00
|
|
|
local new_definition = {}
|
|
|
|
-- we need to be able to point at the water
|
|
|
|
new_definition.liquids_pointable = true
|
|
|
|
-- call closure generator with original on_use handler
|
2015-04-26 13:53:09 -07:00
|
|
|
new_definition.on_use = thirsty.on_use(
|
2015-04-22 22:08:51 -07:00
|
|
|
minetest.registered_nodes[nodename].on_use
|
|
|
|
)
|
|
|
|
-- overwrite the node definition with almost the original
|
|
|
|
minetest.override_item(nodename, new_definition)
|
2015-04-26 13:53:09 -07:00
|
|
|
|
|
|
|
-- add configuration settings
|
2015-05-12 22:06:50 -07:00
|
|
|
thirsty.config.drink_from_container[nodename] = level
|
2015-04-22 22:08:51 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
if (minetest.get_modpath("vessels")) then
|
|
|
|
-- add "drinking" to vessels
|
2015-04-26 13:53:09 -07:00
|
|
|
thirsty.augment_node_for_drinking('vessels:drinking_glass', 22)
|
|
|
|
thirsty.augment_node_for_drinking('vessels:glass_bottle', 24)
|
|
|
|
thirsty.augment_node_for_drinking('vessels:steel_bottle', 26)
|
2015-04-22 22:08:51 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
-- our own simple wooden bowl
|
|
|
|
minetest.register_craftitem('thirsty:wooden_bowl', {
|
|
|
|
description = "Wooden bowl",
|
|
|
|
inventory_image = "thirsty_bowl_16.png",
|
|
|
|
liquids_pointable = true,
|
2015-04-26 13:53:09 -07:00
|
|
|
on_use = thirsty.on_use(nil),
|
2015-04-22 22:08:51 -07:00
|
|
|
})
|
|
|
|
|
|
|
|
minetest.register_craft({
|
|
|
|
output = "thirsty:wooden_bowl",
|
|
|
|
recipe = {
|
|
|
|
{"group:wood", "", "group:wood"},
|
|
|
|
{"", "group:wood", ""}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
--[[
|
|
|
|
|
|
|
|
Hydro containers (Tier 2)
|
|
|
|
|
|
|
|
Defines canteens (currently two types, with different capacities),
|
|
|
|
tools which store hydro. They use wear to show their content
|
|
|
|
level in their durability bar; they do not disappear when used up.
|
|
|
|
|
|
|
|
Wear corresponds to hydro level as follows:
|
|
|
|
- a wear of 0 shows no durability bar -> empty (initial state)
|
|
|
|
- a wear of 1 shows a full durability bar -> full
|
|
|
|
- a wear of 65535 shows an empty durability bar -> empty
|
|
|
|
|
|
|
|
]]
|
|
|
|
|
2015-04-22 14:06:53 -07:00
|
|
|
minetest.register_tool('thirsty:steel_canteen', {
|
|
|
|
description = 'Steel canteen',
|
|
|
|
inventory_image = "thirsty_steel_canteen_16.png",
|
|
|
|
liquids_pointable = true,
|
|
|
|
stack_max = 1,
|
2015-04-26 13:53:09 -07:00
|
|
|
on_use = thirsty.on_use(nil),
|
2015-04-22 14:06:53 -07:00
|
|
|
})
|
|
|
|
|
|
|
|
minetest.register_tool('thirsty:bronze_canteen', {
|
|
|
|
description = 'Bronze canteen',
|
|
|
|
inventory_image = "thirsty_bronze_canteen_16.png",
|
|
|
|
liquids_pointable = true,
|
|
|
|
stack_max = 1,
|
2015-04-26 13:53:09 -07:00
|
|
|
on_use = thirsty.on_use(nil),
|
2015-04-22 14:06:53 -07:00
|
|
|
})
|
|
|
|
|
|
|
|
minetest.register_craft({
|
|
|
|
output = "thirsty:steel_canteen",
|
|
|
|
recipe = {
|
|
|
|
{ "group:wood", ""},
|
|
|
|
{ "default:steel_ingot", "default:steel_ingot"},
|
|
|
|
{ "default:steel_ingot", "default:steel_ingot"}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
minetest.register_craft({
|
|
|
|
output = "thirsty:bronze_canteen",
|
|
|
|
recipe = {
|
|
|
|
{ "group:wood", ""},
|
2015-04-24 12:54:38 -07:00
|
|
|
{ "default:bronze_ingot", "default:bronze_ingot"},
|
|
|
|
{ "default:bronze_ingot", "default:bronze_ingot"}
|
2015-04-22 14:06:53 -07:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2015-04-25 13:02:26 -07:00
|
|
|
--[[
|
|
|
|
|
|
|
|
Tier 3
|
|
|
|
|
|
|
|
]]
|
|
|
|
|
|
|
|
minetest.register_node('thirsty:drinking_fountain', {
|
|
|
|
description = 'Drinking fountain',
|
|
|
|
drawtype = 'nodebox',
|
|
|
|
tiles = {
|
|
|
|
-- top, bottom, right, left, front, back
|
|
|
|
'thirsty_drinkfount_top.png',
|
|
|
|
'thirsty_drinkfount_bottom.png',
|
|
|
|
'thirsty_drinkfount_side.png',
|
|
|
|
'thirsty_drinkfount_side.png',
|
|
|
|
'thirsty_drinkfount_side.png',
|
|
|
|
'thirsty_drinkfount_side.png',
|
|
|
|
},
|
|
|
|
paramtype = 'light',
|
2015-04-28 14:28:51 -07:00
|
|
|
groups = { cracky = 2 },
|
2015-04-25 13:02:26 -07:00
|
|
|
node_box = {
|
|
|
|
type = "fixed",
|
|
|
|
fixed = {
|
|
|
|
{ -3/16, -8/16, -3/16, 3/16, 3/16, 3/16 },
|
|
|
|
{ -8/16, 3/16, -8/16, 8/16, 6/16, 8/16 },
|
|
|
|
{ -8/16, 6/16, -8/16, 8/16, 8/16, -6/16 },
|
|
|
|
{ -8/16, 6/16, 6/16, 8/16, 8/16, 8/16 },
|
|
|
|
{ -8/16, 6/16, -6/16, -6/16, 8/16, 6/16 },
|
|
|
|
{ 6/16, 6/16, -6/16, 8/16, 8/16, 6/16 },
|
|
|
|
},
|
|
|
|
},
|
|
|
|
selection_box = {
|
|
|
|
type = "regular",
|
|
|
|
},
|
|
|
|
collision_box = {
|
|
|
|
type = "regular",
|
|
|
|
},
|
2015-04-26 13:53:09 -07:00
|
|
|
on_rightclick = thirsty.on_rightclick(nil),
|
2015-04-25 13:02:26 -07:00
|
|
|
})
|
2015-04-22 14:06:53 -07:00
|
|
|
|
2015-04-25 13:21:30 -07:00
|
|
|
minetest.register_craft({
|
|
|
|
output = "thirsty:drinking_fountain",
|
|
|
|
recipe = {
|
|
|
|
{ "default:stone", "bucket:bucket_water", "default:stone"},
|
|
|
|
{ "", "default:stone", ""},
|
|
|
|
{ "", "default:stone", ""}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2015-04-28 14:28:51 -07:00
|
|
|
--[[
|
|
|
|
|
|
|
|
Tier 4+: the water fountains, plus extenders
|
|
|
|
|
|
|
|
]]
|
|
|
|
|
|
|
|
minetest.register_node('thirsty:water_fountain', {
|
|
|
|
description = 'Water fountain',
|
|
|
|
tiles = {
|
|
|
|
-- top, bottom, right, left, front, back
|
|
|
|
'thirsty_waterfountain_top.png',
|
|
|
|
'thirsty_waterfountain_top.png',
|
|
|
|
'thirsty_waterfountain_side.png',
|
|
|
|
'thirsty_waterfountain_side.png',
|
|
|
|
'thirsty_waterfountain_side.png',
|
|
|
|
'thirsty_waterfountain_side.png',
|
|
|
|
},
|
|
|
|
paramtype = 'light',
|
|
|
|
groups = { cracky = 2 },
|
|
|
|
})
|
|
|
|
|
|
|
|
minetest.register_node('thirsty:water_extender', {
|
|
|
|
description = 'Water fountain extender',
|
|
|
|
tiles = {
|
|
|
|
'thirsty_waterextender_top.png',
|
|
|
|
'thirsty_waterextender_top.png',
|
|
|
|
'thirsty_waterextender_side.png',
|
|
|
|
'thirsty_waterextender_side.png',
|
|
|
|
'thirsty_waterextender_side.png',
|
|
|
|
'thirsty_waterextender_side.png',
|
|
|
|
},
|
|
|
|
paramtype = 'light',
|
|
|
|
groups = { cracky = 2 },
|
|
|
|
})
|
|
|
|
|
2015-04-28 22:06:06 -07:00
|
|
|
minetest.register_craft({
|
|
|
|
output = "thirsty:water_fountain",
|
|
|
|
recipe = {
|
|
|
|
{ "default:copper_ingot", "bucket:bucket_water", "default:copper_ingot"},
|
|
|
|
{ "", "default:copper_ingot", ""},
|
|
|
|
{ "default:copper_ingot", "default:mese_crystal", "default:copper_ingot"}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
minetest.register_craft({
|
|
|
|
output = "thirsty:water_extender",
|
|
|
|
recipe = {
|
|
|
|
{ "", "bucket:bucket_water", ""},
|
|
|
|
{ "", "default:copper_ingot", ""},
|
|
|
|
{ "default:copper_ingot", "default:mese_crystal", "default:copper_ingot"}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2015-04-28 14:28:51 -07:00
|
|
|
|
|
|
|
minetest.register_abm({
|
|
|
|
nodenames = {'thirsty:water_fountain'},
|
|
|
|
interval = 2,
|
2015-04-28 22:06:21 -07:00
|
|
|
chance = 5,
|
2015-04-28 14:28:51 -07:00
|
|
|
action = function(pos, node)
|
|
|
|
local fountain_count = 0
|
|
|
|
local water_count = 0
|
|
|
|
local total_count = 0
|
|
|
|
for y = 0, 4 do
|
|
|
|
for x = -y, y do
|
|
|
|
for z = -y, y do
|
|
|
|
local n = minetest.get_node({
|
|
|
|
x = pos.x + x,
|
|
|
|
y = pos.y - y + 1, -- start one *above* the fountain
|
|
|
|
z = pos.z + z
|
|
|
|
})
|
|
|
|
if n then
|
2015-05-05 22:14:59 -07:00
|
|
|
--print(string.format("%s at %d:%d:%d", n.name, pos.x+x, pos.y-y+1, pos.z+z))
|
2015-04-28 14:28:51 -07:00
|
|
|
total_count = total_count + 1
|
|
|
|
if n.name == 'thirsty:water_fountain' or n.name == 'thirsty:water_extender' then
|
|
|
|
fountain_count = fountain_count + 1
|
|
|
|
elseif n.name == 'default:water_source' or n.name == 'default:water_flowing' then
|
|
|
|
water_count = water_count + 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
local level = math.min(20, math.min(fountain_count, water_count))
|
2015-04-28 22:06:21 -07:00
|
|
|
--print(string.format("Fountain (%d): %d + %d / %d", level, fountain_count, water_count, total_count))
|
2015-04-28 14:28:51 -07:00
|
|
|
thirsty.fountains[string.format("%d:%d:%d", pos.x, pos.y, pos.z)] = {
|
|
|
|
pos = { x=pos.x, y=pos.y, z=pos.z },
|
|
|
|
level = level,
|
2015-05-17 21:59:22 -07:00
|
|
|
-- time until check is 20 seconds, or twice the average
|
|
|
|
-- time until the abm ticks again. Should be enough.
|
|
|
|
time_until_check = 20,
|
2015-04-28 14:28:51 -07:00
|
|
|
}
|
|
|
|
end
|
|
|
|
})
|
|
|
|
|
2015-05-05 22:14:59 -07:00
|
|
|
|
|
|
|
--[[
|
|
|
|
|
|
|
|
Tier 5
|
|
|
|
|
|
|
|
These amulets don't do much; the actual code is above, where
|
|
|
|
they are searched for in player's inventories
|
|
|
|
|
|
|
|
]]
|
|
|
|
|
|
|
|
minetest.register_craftitem('thirsty:injector', {
|
|
|
|
description = 'Water injector',
|
2015-05-06 22:02:14 -07:00
|
|
|
inventory_image = 'thirsty_injector.png',
|
2015-05-05 22:14:59 -07:00
|
|
|
})
|
|
|
|
minetest.register_craft({
|
|
|
|
output = "thirsty:injector",
|
|
|
|
recipe = {
|
|
|
|
{ "default:diamond", "default:mese_crystal", "default:diamond"},
|
|
|
|
{ "default:mese_crystal", "bucket:bucket_water", "default:mese_crystal"},
|
|
|
|
{ "default:diamond", "default:mese_crystal", "default:diamond"}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
minetest.register_craftitem('thirsty:extractor', {
|
|
|
|
description = 'Water extractor',
|
2015-05-06 22:02:14 -07:00
|
|
|
inventory_image = 'thirsty_extractor.png',
|
2015-05-05 22:14:59 -07:00
|
|
|
})
|
|
|
|
minetest.register_craft({
|
|
|
|
output = "thirsty:extractor",
|
|
|
|
recipe = {
|
|
|
|
{ "default:mese_crystal", "default:diamond", "default:mese_crystal"},
|
|
|
|
{ "default:diamond", "bucket:bucket_water", "default:diamond"},
|
|
|
|
{ "default:mese_crystal", "default:diamond", "default:mese_crystal"}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2015-04-12 12:37:29 -07:00
|
|
|
-- read on startup
|
|
|
|
thirsty.read_stash()
|
|
|
|
-- write on shutdown
|
|
|
|
minetest.register_on_shutdown(thirsty.write_stash)
|
2015-04-15 14:41:39 -07:00
|
|
|
|
|
|
|
|