Initial commit

master
Diego Martínez 2013-02-06 22:12:13 -02:00
commit a848727201
41 changed files with 953 additions and 0 deletions

59
API.txt Normal file
View File

@ -0,0 +1,59 @@
Survival Mod API
----------------
This is the Application Program Interface for the Survival Lib.
Please note that this API is still WIP, so it may change in the future.
Core
----
survival.create_meter(name, meterdef)
Creates a new custom meter. <name> must follow the `modname:itemname'
naming convention.
meterdef = {
description = "My Fabulous Meter"
Description for the inventory.
command = <metercmddef>
Used to register a chat command to see the current value. See below.
recipe = { <recipe table> }
Recipe used to craft the meter item.
image = "modname_texname.png"
Image used both for wielding and in inventory.
get_value = func(player)
Must return the value to show both in the status message and in the
meter item. Returned value must be in range [0..100].
on_use = func(itemstack, user, pointed_thing)
Callback for when the item is used. Same as for itemdef.
}
metercmddef = {
name = "foo"
Name for the chat command.
label = "Foo"
Label for the chat command. If not specified defaults to <name>.
Invoking /cmdname outputs the following:
Label: [67%] ||||||
}
survival.distance3d(p1, p2)
Returns the distance between two 3D points. <p1> and <p2> are position
tables ({x=X,y=Y,z=Z}).
Drowning Mod
------------
survival.drowning.register_liquid(name)
Registers the specified node name as liquid. Please rememeber you need
to register BOTH the _source and _flowing variants! Liquids registered
by this function are checked to see if the player is drowning, and are
also checked by the air bubbles. The mod already registers water, lava,
and oil (from the Oil Mod), both in source and flowing form.
Hunger Mod
----------
survival.hunger.register_food(name)
Registers a new item as food. Please note that the mod registers most
known food items automatically, and also registers any item with the
"food" and/or "eatable" groups.

57
LICENSE.txt Normal file
View File

@ -0,0 +1,57 @@
Unless otherwise noted, all code files fall under the BSD 2 clause license
reproduced below:
--- START OF BSD LICENSE ---
Copyright (c) 2013, Diego Martínez
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
--- END OF BSD LICENSE ---
Textures are released under WTFPL, reproduced below.
--- START OF WTFPL ---
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
--- END OF WTFPL ---
Sound files were obtained from various sites on the net. They were released
under the Creative Commons Zero license.
See http://creativecommons.org/publicdomain/zero/1.0/legalcode

39
README.txt Normal file
View File

@ -0,0 +1,39 @@
Survival Mod for Minetest
-------------------------
Copyright (C) 2013 Diego Martínez <lkaezadl3@gmail.com>
This mod adds new hazards to the survival aspect of the game.
Currently, this adds hunger and drowning. It's planned to add thirst,
stamina (tiredness), and heat in the future.
See the file `LICENSE.txt' for information about distribution.
The survival_drowning mod is a modified version of the drowning mod by [who?].
The survival_hunger mod was written from scratch inspired by the existing
hunger mod by [who?].
Forum topic: [TODO: Reserve topic]
Download: https://github.com/kaeza/minetest-survival_modpack/archive/master.zip
Github Repo: https://github.com/kaeza/minetest-survival_modpack
INSTALLING
----------
Unpack the modpack into one of the directories where Minetest looks for mods.
For more information, see http://wiki.minetest.com/wiki/Installing_mods
See the respective mods' README.txt for information about technical aspects
of the implementation and known bugs.
CONFIGURING
-----------
The mod can be configured by means of a `survival_lib.conf' file. This file
is read first from the survival_lib mod's directory, and then from the
current world directory (so you can have different settings per world).
Options set in `survival_lib/survival_lib.conf' override the defaults, and
those set in `<worlddir>/survival_lib.conf' override these, so you can
specify global defaults in the mod dir, and override those settings in the
world dir.
See `survival_lib.conf.example' for an example of what can be configured.

1
modpack.txt Normal file
View File

@ -0,0 +1 @@
# This is a modpack. YAY!

View File

@ -0,0 +1,18 @@
Drowning Mod for Minetest
This mod is part of the Survival Modpack for Minetest.
Copyright (C) 2013 Diego Martínez <lkaezadl3@gmail.com>
Inspired by the existing drowning mod [TODO: who's the author?]
See the file `../LICENSE.txt' for information about distribution.
TECHNICAL NOTES
---------------
To detect if the player is under water (to restore the oxygen timer), this
mod has to know about all the nodes considered "liquid". It currently handles
water_{source|flowing}, lava_{source|flowing}, and oil_{source|flowing} from
the Oil Mod.
The original drowning mod checked whether or not there was an air node at the
player's head, but this was inaccurate as you could "drown" by standing
"inside" a torch node, or other walkable nodes.

View File

@ -0,0 +1,2 @@
default
survival_lib

138
survival_drowning/init.lua Normal file
View File

@ -0,0 +1,138 @@
drowning = { };
local players_under_water = { };
local START_DROWNING_TIME = survival.conf_getnum("drowning.damage_start_time", 20);
local DROWNING_TIME = survival.conf_getnum("drowning.damage_interval", 2);
local DROWNING_DAMAGE = survival.conf_getnum("drowning.damage", 1);
local DTIME = survival.conf_getnum("drowning.check_interval", 0.5);
local timer = 0;
local liquids = { };
-- Boilerplate to support localized strings if intllib mod is installed.
local S;
if (minetest.get_modpath("intllib")) then
dofile(minetest.get_modpath("intllib").."/intllib.lua");
S = intllib.load_strings("survival_drowning");
else
S = function ( s ) return s; end
end
minetest.register_entity("survival_drowning:bubbles", {
physical = false;
timer = 0;
textures = { "survival_drowning_bubbles.png" };
collisionbox = { 0, 0, 0, 0, 0, 0 };
on_step = function ( self, dtime )
self.timer = self.timer + dtime;
if (self.timer > 0.5) then
self.timer = self.timer - 0.5;
local pos = self.object:getpos();
pos.y = pos.y + 1;
if (not liquids[minetest.env:get_node(pos).name]) then
self.object:remove();
end
end
end;
});
if (minetest.setting_getbool("enable_damage") and survival.conf_getbool("drowning.enabled", true)) then
print("survival_drowning: Drowning is enabled!");
minetest.register_globalstep(function ( dtime )
timer = timer + dtime;
if (timer < DTIME) then
return;
end
timer = timer - DTIME;
for k, v in pairs(minetest.get_connected_players()) do
name = v:get_player_name();
if (not players_under_water[name]) then
players_under_water[name] = { count=0, drowning=false };
end
local puw = players_under_water[name];
local pos = v:getpos()
pos.y = pos.y + 1;
if (is_player_under_liquid(v)) then
puw.count = puw.count + 0.5;
if (math.random(1, 100) < 20) then
if (not liquids[minetest.env:get_node({ x=pos.x; y=pos.y+8; z=pos.z}).name]) then
local bub = minetest.env:add_entity(pos, "survival_drowning:bubbles");
bub:setvelocity({ x=0; y=1; z=0 });
end
end
if ((not puw.drowning) and (puw.count >= START_DROWNING_TIME)) then
players_under_water[name] = {count=0, drowning=true}
v:set_hp(v:get_hp() - DROWNING_DAMAGE);
minetest.sound_play({ name="drowning_gurp"; }, { pos = pos; gain = 1.0; max_hear_distance = 16; });
puw.drowning = true;
puw.count = puw.count - START_DROWNING_TIME;
minetest.chat_send_player(name, S("You are out of oxygen."));
elseif (puw.drowning and (puw.count >= DROWNING_TIME)) then
v:set_hp(v:get_hp() - DROWNING_DAMAGE);
minetest.sound_play({ name="drowning_gurp"; }, { pos = pos; gain = 1.0; max_hear_distance = 16; });
puw.count = puw.count - DROWNING_TIME;
if (v:get_hp() <= 0) then
minetest.chat_send_player(name, S("You drowned."));
end
end
else
if (puw.count > 0) then
pos = v:getpos();
pos.y = pos.y + 1;
minetest.sound_play({ name="drowning_gasp" }, { pos = pos; gain = 1.0; max_hear_distance = 32; });
end
puw.count = 0;
puw.drowning = false;
end
end
end)
end
function is_player_under_liquid(player)
local pos = player:getpos()
pos.y = pos.y + 1.5;
return (liquids[minetest.env:get_node(pos).name]);
end
survival.drowning = { };
survival.drowning.register_liquid = function ( name )
liquids[name] = true;
end
survival.drowning.is_liquid = function ( name )
return liquids[name];
end
survival.drowning.is_liquid_at_pos = function ( pos )
local name = minetest.env:get_node(pos).name;
return liquids[name];
end
survival.drowning.register_liquid("default:water_source");
survival.drowning.register_liquid("default:water_flowing");
survival.drowning.register_liquid("default:lava_source");
survival.drowning.register_liquid("default:lava_flowing");
survival.drowning.register_liquid("oil:oil_source");
survival.drowning.register_liquid("oil:oil_flowing");
survival.create_meter("survival_drowning:meter", {
description = S("Oxygen Meter");
command = {
name = "o2";
label = S("Oxygen");
};
image = "survival_drowning_meter.png";
get_value = function ( player )
local name = player:get_player_name();
if (players_under_water[name].drowning) then
return 0.01;
else
return 100 * (START_DROWNING_TIME - players_under_water[name].count) / START_DROWNING_TIME;
end
end;
});

View File

@ -0,0 +1,8 @@
# Language: Español
# Author: Diego Martínez <lkaezadl3@gmail.com>
You are out of oxygen.::Te has quedado sin oxigeno.
You drowned.::Te has ahogado.
Oxygen Meter::Medidor de Oxigeno
Oxygen::Oxigeno

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 B

View File

@ -0,0 +1,26 @@
Hunger Mod for Minetest
This mod is part of the Survival Modpack for Minetest.
Copyright (C) 2013 Diego Martínez <lkaezadl3@gmail.com>
Inspired by the existing hunger mod [TODO: who's the author?]
See the file `../LICENSE.txt' for information about distribution.
TECHNICAL NOTES
---------------
In order to detect if the player ate an item (to restore the hunger timer),
this mod overrides the on_use callback of all known food items, resetting the
timer and calling the callback to perform the actual healing. This has the
advantage that the hunger timer is reset whenever the player "eats" the item,
but new food items must be explicitly listed on the script for the mod to
"know" it's food.
At first, this was implemented by checking whether the player's HP increased
since the last check, but this has several disadvantages:
- It's not possible to have separate "hunger" and "thirst" if the drink
item increases HP, because this would also be taken as eating.
- When the HP is at max, eating a food item does not reset the hunger timer
since there was no increase in HP.
- Other healing mechanisms (such as the medikit blocks) may interfere with
the detection.

View File

@ -0,0 +1,2 @@
default
survival_lib

154
survival_hunger/init.lua Normal file
View File

@ -0,0 +1,154 @@
local START_HUNGER_TIME = survival.conf_getnum("hunger.damage_start_time", 720);
local HUNGER_TIME = survival.conf_getnum("hunger.damage_interval", 30);
local HUNGER_DAMAGE = survival.conf_getnum("hunger.damage", 4);
local DTIME = survival.conf_getnum("hunger.check_interval", 0.5);
-- Boilerplate to support localized strings if intllib mod is installed.
local S;
if (minetest.get_modpath("intllib")) then
dofile(minetest.get_modpath("intllib").."/intllib.lua");
S = intllib.load_strings("survival_hunger");
else
S = function ( s ) return s; end
end
local timer = 0;
local hungry_players = { };
if (minetest.setting_getbool("enable_damage") and survival.conf_getbool("hunger.enabled", true)) then
minetest.register_globalstep(function ( dtime )
timer = timer + dtime;
if (timer < DTIME) then return; end
timer = timer - DTIME;
for i, v in ipairs(minetest.get_connected_players()) do
local name = v:get_player_name();
if (not hungry_players[name]) then
hungry_players[name] = {
count = 0;
hungry = false;
next = START_HUNGER_TIME;
};
end
local hdata = hungry_players[name];
hdata.count = hdata.count + DTIME;
if ((v:get_hp() > 0) and (hdata.count >= hdata.next)) then
v:set_hp(v:get_hp() - HUNGER_DAMAGE);
if (v:get_hp() <= 0) then
minetest.chat_send_player(name, S("You died from starvation."));
end
hdata.count = hdata.count - hdata.next;
hdata.next = HUNGER_TIME;
hdata.hungry = true;
minetest.sound_play({ name="survival_hunger_stomach" }, {
pos = v:getpos();
gain = 1.0;
max_hear_distance = 16;
});
end
end
end);
end
survival.create_meter("survival_hunger:meter", {
description = S("Hunger Meter");
command = {
name = "hunger";
label = S("Hunger");
};
image = "survival_hunger_meter.png";
get_value = function ( player )
local name = player:get_player_name();
if (hungry_players[name].hungry) then
return 0;
else
return 100 * (START_HUNGER_TIME - hungry_players[name].count) / START_HUNGER_TIME;
end
end;
});
-- Known food items (more suggestions are welcome)
local known_foods = {
-- Default game --
"default:apple",
-- PilzAdam's farming[_plus] --
"farming:bread",
"farming:pumpkin_bread",
"farming_plus:orange_item",
"farming_plus:tomato_item",
"farming_plus:strawberry_item",
"farming_plus:carrot_item",
"farming_plus:banana",
-- rubenwardy's food --
"food:cheese", "food:chocolate_dark", "food:chocolate_milk",
"food:coffee", "food:hotchoco", "food:ms_chocolate", "food:bread_slice",
"food:bun", "food:sw_meat", "food:sw_cheese", "food:cake",
"food:cake_chocolate", "food:cake_carrot", "food:crumble_rhubarb",
"food:banana_split", "food:bread", "food:strawberry", "food:carrot",
"food:banana", "food:meat_raw", "food:milk",
-- These will be better for thirst
--"food:apple_juice", "food:cactus_juice",
-- GloopMaster's gloopores --
-- "gloopores:kalite_lump", -- TODO: Should this be considered "food"?
-- Sapier's animals_modpack (MOB Framework) --
"animalmaterials:meat_pork", "animalmaterials:meat_beef",
"animalmaterials:meat_chicken", "animalmaterials:meat_lamb",
"animalmaterials:meat_venison", "animalmaterials:meat_toxic",
"animalmaterials:meat_ostrich", "animalmaterials:meat_undead",
"animalmaterials:fish_bluewhite", "animalmaterials:fish_clownfish",
"animalmaterials:milk",
};
local function override_on_use ( def )
local on_use = def.on_use;
def.on_use = function ( itemstack, user, pointed_thing )
if (on_use) then
local r = on_use(itemstack, user, pointed_thing);
hungry_players[user:get_player_name()] = {
count = 0;
hungry = false;
next = START_HUNGER_TIME;
};
minetest.sound_play({ name="survival_hunger_eat" }, {
to_player = user:getpos();
gain = 1.0;
});
return r;
end
return itemstack;
end
end
-- Try to override the on_use callback of as many food items as possible.
minetest.after(1, function ( )
for _,name in ipairs(known_foods) do
local def = minetest.registered_items[name] or minetest.registered_nodes[name];
if (def) then
override_on_use(def);
end
end
for name, def in pairs(minetest.registered_items) do
if (def.groups and def.groups.food and (def.groups.food > 0)) then
override_on_use(def);
end
end
end);
minetest.register_on_dieplayer(function ( player )
local name = player:get_player_name();
hungry_players[name] = {
count = 0;
hungry = false;
next = START_HUNGER_TIME;
};
end);

View File

@ -0,0 +1,7 @@
# Language: Español
# Author: Diego Martínez <lkaezadl3@gmail.com>
You died from starvation.::Has muerto de hambre.
Hunger Meter::Medidor de Hambre
Hunger::Hambre

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 B

87
survival_lib/config.lua Normal file
View File

@ -0,0 +1,87 @@
local config;
local config_modified;
local function do_load_config ( f )
for line in f:lines() do
line = line:trim();
local c1 = line:sub(1, 1);
local eq_pos = line:find("=", 1, true);
if ((c1 ~= "#") and (c1 ~= ";") and eq_pos) then
local name = line:sub(1, eq_pos - 1):trim();
local value = line:sub(eq_pos + 1):trim();
config[name] = value;
end
end
end
local CONF_FILE = minetest.get_worldpath().."/survival_lib.conf";
local config_files = {
minetest.get_modpath("survival_lib").."/survival_lib.conf",
CONF_FILE,
};
local function load_config ( )
config = { };
for _,file in ipairs(config_files) do
local f = io.open(file);
if (f) then
do_load_config(f);
f:close();
print("survival_lib: Loaded config from `"..file.."'");
end
end
config_modified = false;
end
load_config();
survival.conf_set = function ( name, value )
config[name] = tostring(value);
config_modified = true;
end
-- Just an alias
survival.conf_setnum = survival.conf_set;
survival.conf_setbool = function ( name, value )
config[name] = (value and "true") or "false";
config_modified = true;
end
survival.conf_setpos = function ( name, value )
config[name] = minetest.pos_to_string(value);
config_modified = true;
end
survival.conf_get = function ( name, default )
return (config[name] or default);
end
survival.conf_getnum = function ( name, default )
return (tonumber(config[name]) or default);
end
survival.conf_getpos = function ( name, default )
return ((config[name] and minetest.string_to_pos(config[name])) or default);
end
survival.conf_getbool = function ( name, default )
local val = config[name];
if (not val) then return default; end
val = val:lower();
return (
(val == "true")
or (val == "on")
or (val == "enabled")
or (val == "yes")
or (tonumber(val) and (tonumber(val) ~= 0))
);
end

1
survival_lib/depends.txt Normal file
View File

@ -0,0 +1 @@
default

123
survival_lib/init.lua Normal file
View File

@ -0,0 +1,123 @@
local TESTING = false;
survival = { };
survival.meters = { };
survival.distance3d = function ( p1, p2 )
local lenx = math.abs(p2.x - p1.x);
local leny = math.abs(p2.y - p1.y);
local lenz = math.abs(p2.z - p1.z);
local hypotxz = math.sqrt((lenx * lenx) + (lenz * lenz));
return math.sqrt((hypotxz * hypotxz) + (leny * leny));
end
dofile(minetest.get_modpath("survival_lib").."/config.lua");
survival.create_meter = function ( name, def )
minetest.register_tool(name, {
description = def.description;
inventory_image = def.image;
on_use = def.on_use;
});
if (def.command and def.command.name) then
local lbl = (def.command.label or def.command.name);
def.command.func = function ( name, param )
local ply = minetest.env:get_player_by_name(name);
local val = math.floor(def.get_value(ply));
local val2 = math.max(0, math.min(val / 10, 10));
minetest.chat_send_player(name, lbl..": ["..val.."%] "..string.rep("|", val2));
end;
minetest.register_chatcommand(def.command.name, {
params = "";
description = "Display "..lbl;
func = def.command.func;
});
end
if (def.recipe) then
minetest.register_craft({
output = name;
recipe = def.recipe;
});
end
def.name = name;
survival.meters[name] = def;
survival.meters[#survival.meters + 1] = def;
end
local chat_cmd_def = {
params = "";
description = "Display all player stats";
func = function ( name, param )
for i, def in ipairs(survival.meters) do
if (def.command and def.command.func and (not def.command.not_in_plstats)) then
def.command.func(name, "");
end
end
end;
};
minetest.register_chatcommand("plstats", chat_cmd_def);
minetest.register_chatcommand("s", chat_cmd_def);
local timer = 0;
local MAX_TIMER = 1;
minetest.register_globalstep(function ( dtime )
timer = timer + dtime;
if (timer < MAX_TIMER) then return; end
timer = timer - MAX_TIMER;
for _,player in pairs(minetest.get_connected_players()) do
local inv = player:get_inventory();
for name, def in pairs(survival.meters) do
if (def.on_step) then
def.on_step(player);
end
if (survival.conf_getbool("meters_enabled", true)
and inv:contains_item("main", ItemStack(name))) then
for i = 1, inv:get_size("main") do
local stack = inv:get_stack("main", i);
if (stack:get_name() == name) then
local value = (65535 * def.get_value(player) / 100);
--local wear = stack:get_wear();
inv:remove_item("main", stack);
stack:add_wear(-65535);
stack:add_wear(65534);
stack:add_wear(-(value - 2));
inv:set_stack("main", i, stack);
break;
end
end
end
end
end
end);
if (TESTING) then
local ORIGIN = { x=0; y=0; z=0; };
local RESOLUTION = 50;
survival.create_meter("survival_lib:test_meter", {
description = "Survival Lib Test";
recipe = {
{ "", "default:wood", "" },
{ "default:wood", "", "default:wood" },
{ "", "default:wood", "" },
};
image = "survival_utils_test_meter.png";
get_value = function ( player )
local dist = survival.distance3d(player:getpos(), ORIGIN);
dist = math.min(dist, RESOLUTION);
return 100 * dist / RESOLUTION;
end;
on_use = function ( itemstack, user, pointed_thing )
local dist = survival.distance3d(user:getpos(), ORIGIN);
minetest.chat_send_player(user:get_player_name(), "Distance to origin: "..dist);
return itemstack;
end;
});
end

View File

@ -0,0 +1,42 @@
# ==================
# ===== common =====
# ==================
# Whether tool-based meters are enabled (boolean).
meters_enabled = true
# Interval between meter updates (in seconds)
meter_check_interval = 1
# ===========================
# ===== survival_hunger =====
# ===========================
# Time before player starts to take hunger damage (in seconds)
hunger.damage_start_time = 720
# Interval before player takes damage again when hungry (in seconds)
hunger.damage_interval = 30
# Damage inflicted by hunger (in half hearts)
hunger.damage = 4
# Interval between state checks (in seconds)
hunger.check_interval = 0.5
# =============================
# ===== survival_drowning =====
# =============================
# Time before player starts to take drowning damage (in seconds)
drowning.damage_start_time = 20
# Interval before player takes damage again when drowning (in seconds)
drowning.damage_interval = 2
# Damage inflicted by drowning (in half hearts)
drowning.damage = 2
# Interval between state checks (in seconds)
drowning.check_interval = 0.5

View File

@ -0,0 +1,26 @@
Hunger Mod for Minetest
This mod is part of the Survival Modpack for Minetest.
Copyright (C) 2013 Diego Martínez <lkaezadl3@gmail.com>
Inspired by the existing hunger mod [TODO: who's the author?]
See the file `../LICENSE.txt' for information about distribution.
TECHNICAL NOTES
---------------
In order to detect if the player ate an item (to restore the hunger timer),
this mod overrides the on_use callback of all known food items, resetting the
timer and calling the callback to perform the actual healing. This has the
advantage that the hunger timer is reset whenever the player "eats" the item,
but new food items must be explicitly listed on the script for the mod to
"know" it's food.
At first, this was implemented by checking whether the player's HP increased
since the last check, but this has several disadvantages:
- It's not possible to have separate "hunger" and "thirst" if the drink
item increases HP, because this would also be taken as eating.
- When the HP is at max, eating a food item does not reset the hunger timer
since there was no increase in HP.
- Other healing mechanisms (such as the medikit blocks) may interfere with
the detection.

View File

@ -0,0 +1,2 @@
default
survival_lib

154
survival_thirst/init.lua Normal file
View File

@ -0,0 +1,154 @@
local START_HUNGER_TIME = survival.conf_getnum("hunger.damage_start_time", 720);
local HUNGER_TIME = survival.conf_getnum("hunger.damage_interval", 30);
local HUNGER_DAMAGE = survival.conf_getnum("hunger.damage", 4);
local DTIME = survival.conf_getnum("hunger.check_interval", 0.5);
-- Boilerplate to support localized strings if intllib mod is installed.
local S;
if (minetest.get_modpath("intllib")) then
dofile(minetest.get_modpath("intllib").."/intllib.lua");
S = intllib.load_strings("survival_hunger");
else
S = function ( s ) return s; end
end
local timer = 0;
local hungry_players = { };
if (minetest.setting_getbool("enable_damage") and survival.conf_getbool("hunger.enabled", true)) then
minetest.register_globalstep(function ( dtime )
timer = timer + dtime;
if (timer < DTIME) then return; end
timer = timer - DTIME;
for i, v in ipairs(minetest.get_connected_players()) do
local name = v:get_player_name();
if (not hungry_players[name]) then
hungry_players[name] = {
count = 0;
hungry = false;
next = START_HUNGER_TIME;
};
end
local hdata = hungry_players[name];
hdata.count = hdata.count + DTIME;
if ((v:get_hp() > 0) and (hdata.count >= hdata.next)) then
v:set_hp(v:get_hp() - HUNGER_DAMAGE);
if (v:get_hp() <= 0) then
minetest.chat_send_player(name, S("You died from starvation."));
end
hdata.count = hdata.count - hdata.next;
hdata.next = HUNGER_TIME;
hdata.hungry = true;
minetest.sound_play({ name="survival_hunger_stomach" }, {
pos = v:getpos();
gain = 1.0;
max_hear_distance = 16;
});
end
end
end);
end
survival.create_meter("survival_hunger:meter", {
description = S("Hunger Meter");
command = {
name = "hunger";
label = S("Hunger");
};
image = "survival_hunger_meter.png";
get_value = function ( player )
local name = player:get_player_name();
if (hungry_players[name].hungry) then
return 0;
else
return 100 * (START_HUNGER_TIME - hungry_players[name].count) / START_HUNGER_TIME;
end
end;
});
-- Known food items (more suggestions are welcome)
local known_foods = {
-- Default game --
"default:apple",
-- PilzAdam's farming[_plus] --
"farming:bread",
"farming:pumpkin_bread",
"farming_plus:orange_item",
"farming_plus:tomato_item",
"farming_plus:strawberry_item",
"farming_plus:carrot_item",
"farming_plus:banana",
-- rubenwardy's food --
"food:cheese", "food:chocolate_dark", "food:chocolate_milk",
"food:coffee", "food:hotchoco", "food:ms_chocolate", "food:bread_slice",
"food:bun", "food:sw_meat", "food:sw_cheese", "food:cake",
"food:cake_chocolate", "food:cake_carrot", "food:crumble_rhubarb",
"food:banana_split", "food:bread", "food:strawberry", "food:carrot",
"food:banana", "food:meat_raw", "food:milk",
-- These will be better for thirst
--"food:apple_juice", "food:cactus_juice",
-- GloopMaster's gloopores --
-- "gloopores:kalite_lump", -- TODO: Should this be considered "food"?
-- Sapier's animals_modpack (MOB Framework) --
"animalmaterials:meat_pork", "animalmaterials:meat_beef",
"animalmaterials:meat_chicken", "animalmaterials:meat_lamb",
"animalmaterials:meat_venison", "animalmaterials:meat_toxic",
"animalmaterials:meat_ostrich", "animalmaterials:meat_undead",
"animalmaterials:fish_bluewhite", "animalmaterials:fish_clownfish",
"animalmaterials:milk",
};
local function override_on_use ( def )
local on_use = def.on_use;
def.on_use = function ( itemstack, user, pointed_thing )
if (on_use) then
local r = on_use(itemstack, user, pointed_thing);
hungry_players[user:get_player_name()] = {
count = 0;
hungry = false;
next = START_HUNGER_TIME;
};
minetest.sound_play({ name="survival_hunger_eat" }, {
to_player = user:getpos();
gain = 1.0;
});
return r;
end
return itemstack;
end
end
-- Try to override the on_use callback of as many food items as possible.
minetest.after(1, function ( )
for _,name in ipairs(known_foods) do
local def = minetest.registered_items[name] or minetest.registered_nodes[name];
if (def) then
override_on_use(def);
end
end
for name, def in pairs(minetest.registered_items) do
if (def.groups and def.groups.food and (def.groups.food > 0)) then
override_on_use(def);
end
end
end);
minetest.register_on_dieplayer(function ( player )
local name = player:get_player_name();
hungry_players[name] = {
count = 0;
hungry = false;
next = START_HUNGER_TIME;
};
end);

View File

@ -0,0 +1,7 @@
# Language: Español
# Author: Diego Martínez <lkaezadl3@gmail.com>
You died from starvation.::Has muerto de hambre.
Hunger Meter::Medidor de Hambre
Hunger::Hambre

View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 B