mirror of
https://github.com/luanti-org/minetest_game.git
synced 2025-06-06 05:44:26 -04:00
~~~Hunger.~~~ Stamina
This code adds ~~~hunger~~~ stamina to minetest_game. This code is entirely derived from Blockmen's hunger+hud mod. Credits and copyright are preserved as they are compatible with minetest_game. gameplay: The player stat 'stamina' is added. The higher the value, the better. A value of 5 or higher causes the player to slowly get healed over time, at the cost of stamina. One can only be healed if the stamina value is higher than, or equal to the health value. Eating food causes 'stamina' to increase. The visual range of stamina is 0-20, but one can eat slightly over that. If the stamina is at max, items are not eaten anymore. At stamina 2 or lower, the player is damaged periodically, and will die if the stamina value isn't increased due to eating. If eating a food that has a negative hp_change value, such as red mushrooms, the player takes periodical damage to the amount of the hp_change value. During this time, the player is "poisoned". This poison damage can not kill the player. Added by me is code to add exhaustion on crafting, and on player combat. I also added extra exhaustion when the player is jumping code: This mod started as BlockMen's hunger+hud mod. I trimmed the entire hud mod away and placed the stamina bar on top of the hp hud element, which will make the relation between stamina and hp a bit more clear, and doesn't require moving the air hud element. The stamina mod was reduced to fit a single file. I trimmed some legacy code, revised all local/global usage. Renamed most internal variables, and generally did a lot of removals of needlessly complex code favoring simplicity. The food registration code is entirely removed. Instead we assume food just adds stamina or ticks damage as poison. Food eating callbacks are still called. Subsequently, this mod will likely not properly work with BlockMen's hud code anymore, nor is that intended to work. Food mods just have to call item_eat(). I've moved and credited authors for the used textures. I replaced the food sound of this mod with 3 different sounds. The code points out several issues with core: There's no decent place to store player stats. This mod solves it by creating a per player inventory purely for storing an integer value. Intercepting core.do_item_eat() also isn't the nicest way to make this work.
This commit is contained in:
parent
7d2dfe4101
commit
d905f66d30
9 changed files with 338 additions and 0 deletions
45
mods/stamina/README.txt
Normal file
45
mods/stamina/README.txt
Normal file
|
@ -0,0 +1,45 @@
|
|||
Minetest mod "Stamina"
|
||||
=====================
|
||||
|
||||
(C) 2015 - BlockMen
|
||||
(C) 2016 - Auke Kok <sofar@foo-projects.org>
|
||||
|
||||
|
||||
About this mod:
|
||||
---------------
|
||||
This mod adds a stamina, or "hunger" mechanic to Minetest. Actions like
|
||||
crafting, walking, digging or fighting make the player exhausted. When
|
||||
enough exhaustion has been accumulated, the player gets more hungry,
|
||||
and loses stamina.
|
||||
|
||||
If a player is low on stamina, they start taking periodical damage,
|
||||
and ultimately will die if they do not eat food.
|
||||
|
||||
Eating food no longer heals the player. Instead, it increases the
|
||||
stamina of the player. The stamina bar shows how well fed the player
|
||||
is. More bread pieces means more stamina.
|
||||
|
||||
|
||||
For Modders:
|
||||
------------
|
||||
This mod intercepts minetest.item_eat(), and applies the hp_change
|
||||
as stamina change. The value can be positive (increase stamina) or
|
||||
negative (periodically damage the player by 1 hp).
|
||||
|
||||
Callbacks that are registered via minetest.register_on_item_eat()
|
||||
are called after this mod, so the itemstack will have changed already
|
||||
when callbacks are called. You can get the original itemstack as 6th
|
||||
parameter of your function then.
|
||||
|
||||
|
||||
License:
|
||||
--------
|
||||
Code:
|
||||
- all code LGPL-2.1+
|
||||
Textures:
|
||||
- stamina_hud_poison.png - BlockMen (CC-BY 3.0)
|
||||
- stamina_hud_fg.png - PilzAdam (WTFPL), modified by BlockMen
|
||||
- stamina_hud_bg.png - PilzAdam (WTFPL), modified by BlockMen
|
||||
Sounds:
|
||||
- stamina_eat.*.ogg - http://www.freesound.org/people/sonictechtonic/sounds/242215/ CC-BY-3.0
|
||||
|
1
mods/stamina/depends.txt
Normal file
1
mods/stamina/depends.txt
Normal file
|
@ -0,0 +1 @@
|
|||
default
|
292
mods/stamina/init.lua
Normal file
292
mods/stamina/init.lua
Normal file
|
@ -0,0 +1,292 @@
|
|||
|
||||
stamina = {}
|
||||
|
||||
local stamina_players = {}
|
||||
|
||||
STAMINA_TICK = 800 -- time in seconds after that 1 stamina point is taken
|
||||
STAMINA_HEALTH_TICK = 4 -- time in seconds after player gets healed/damaged
|
||||
STAMINA_MOVE_TICK = 0.5 -- time in seconds after the movement is checked
|
||||
|
||||
STAMINA_EXHAUST_DIG = 3 -- exhaustion increased this value after digged node
|
||||
STAMINA_EXHAUST_PLACE = 1 -- .. after digging node
|
||||
STAMINA_EXHAUST_MOVE = 1.5 -- .. if player movement detected
|
||||
STAMINA_EXHAUST_JUMP = 5 -- .. if jumping
|
||||
STAMINA_EXHAUST_CRAFT = 20 -- .. if player crafts
|
||||
STAMINA_EXHAUST_PUNCH = 40 -- .. if player punches another player
|
||||
STAMINA_EXHAUST_LVL = 160 -- at what exhaustion player saturation gets lowered
|
||||
|
||||
STAMINA_HEAL = 1 -- number of HP player gets healed after STAMINA_HEALTH_TICK
|
||||
STAMINA_HEAL_LVL = 5 -- lower level of saturation needed to get healed
|
||||
STAMINA_STARVE = 1 -- number of HP player gets damaged by stamina after STAMINA_HEALTH_TICK
|
||||
STAMINA_STARVE_LVL = 3 -- level of staturation that causes starving
|
||||
|
||||
STAMINA_VISUAL_MAX = 20 -- hud bar extends only to 20
|
||||
|
||||
|
||||
local function stamina_read(player)
|
||||
local inv = player:get_inventory()
|
||||
if not inv then
|
||||
return nil
|
||||
end
|
||||
|
||||
-- itemstack storage is offest by 1 to make the math work
|
||||
local v = inv:get_stack("stamina", 1):get_count()
|
||||
if v == 0 then
|
||||
v = 21
|
||||
inv:set_stack("stamina", 1, ItemStack({name = ":", count = v}))
|
||||
end
|
||||
|
||||
return v - 1
|
||||
end
|
||||
|
||||
local function stamina_save(player)
|
||||
local inv = player:get_inventory()
|
||||
if not inv then
|
||||
return nil
|
||||
end
|
||||
local name = player:get_player_name()
|
||||
local level = stamina_players[name].level
|
||||
|
||||
level = math.max(level, 0)
|
||||
|
||||
inv:set_stack("stamina", 1, ItemStack({name = ":", count = level + 1}))
|
||||
return true
|
||||
end
|
||||
|
||||
local function stamina_update(player, level)
|
||||
local name = player:get_player_name()
|
||||
if not name then
|
||||
return false
|
||||
end
|
||||
local old = stamina_players[name].level
|
||||
if level == old then
|
||||
return
|
||||
end
|
||||
stamina_players[name].level = level
|
||||
|
||||
player:hud_change(stamina_players[name].hud_id, "number", math.min(STAMINA_VISUAL_MAX, level))
|
||||
stamina_save(player)
|
||||
end
|
||||
|
||||
local function exhaust_player(player, v)
|
||||
if not player or not player:is_player() then
|
||||
return
|
||||
end
|
||||
|
||||
local name = player:get_player_name()
|
||||
if not name then
|
||||
return
|
||||
end
|
||||
|
||||
local s = stamina_players[name]
|
||||
if not s then
|
||||
return
|
||||
end
|
||||
|
||||
local e = s.exhaust
|
||||
if not e then
|
||||
s.exhaust = 0
|
||||
end
|
||||
|
||||
e = e + v
|
||||
|
||||
if e > STAMINA_EXHAUST_LVL then
|
||||
e = 0
|
||||
local h = tonumber(stamina_players[name].level)
|
||||
if h > 0 then
|
||||
stamina_update(player, h - 1)
|
||||
end
|
||||
end
|
||||
|
||||
s.exhaust = e
|
||||
end
|
||||
|
||||
-- Time based stamina functions
|
||||
local stamina_timer = 0
|
||||
local health_timer = 0
|
||||
local action_timer = 0
|
||||
|
||||
local function stamina_globaltimer(dtime)
|
||||
stamina_timer = stamina_timer + dtime
|
||||
health_timer = health_timer + dtime
|
||||
action_timer = action_timer + dtime
|
||||
|
||||
if action_timer > STAMINA_MOVE_TICK then
|
||||
for _,player in ipairs(minetest.get_connected_players()) do
|
||||
local controls = player:get_player_control()
|
||||
-- Determine if the player is walking
|
||||
if controls.jump then
|
||||
exhaust_player(player, STAMINA_EXHAUST_JUMP)
|
||||
elseif controls.up or controls.down or controls.left or controls.right then
|
||||
exhaust_player(player, STAMINA_EXHAUST_MOVE)
|
||||
end
|
||||
end
|
||||
action_timer = 0
|
||||
end
|
||||
|
||||
-- lower saturation by 1 point after STAMINA_TICK second(s)
|
||||
if stamina_timer > STAMINA_TICK then
|
||||
for _,player in ipairs(minetest.get_connected_players()) do
|
||||
local name = player:get_player_name()
|
||||
local tab = stamina_players[name]
|
||||
if tab then
|
||||
local h = tab.level
|
||||
if h > 0 then
|
||||
stamina_update(player, h - 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
stamina_timer = 0
|
||||
end
|
||||
|
||||
-- heal or damage player, depending on saturation
|
||||
if health_timer > STAMINA_HEALTH_TICK then
|
||||
for _,player in ipairs(minetest.get_connected_players()) do
|
||||
local name = player:get_player_name()
|
||||
local tab = stamina_players[name]
|
||||
if tab then
|
||||
local air = player:get_breath() or 0
|
||||
local hp = player:get_hp()
|
||||
|
||||
-- don't heal if drowning or dead
|
||||
-- TODO: don't heal if poisoned?
|
||||
local h = tonumber(tab.level)
|
||||
if h >= STAMINA_HEAL_LVL and h >= hp and hp > 0 and air > 0 then
|
||||
player:set_hp(hp + STAMINA_HEAL)
|
||||
stamina_update(player, h - 1)
|
||||
end
|
||||
|
||||
-- or damage player by 1 hp if saturation is < 2 (of 30)
|
||||
if tonumber(tab.level) < STAMINA_STARVE_LVL then
|
||||
player:set_hp(hp - STAMINA_STARVE)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
health_timer = 0
|
||||
end
|
||||
end
|
||||
|
||||
local function poison_player(ticks, time, elapsed, user)
|
||||
if elapsed <= ticks then
|
||||
minetest.after(time, poison_player, ticks, time, elapsed + 1, user)
|
||||
else
|
||||
local name = user:get_player_name()
|
||||
user:hud_change(stamina_players[name].hud_id, "text", "stamina_hud_fg.png")
|
||||
end
|
||||
local hp = user:get_hp() -1 or 0
|
||||
if hp > 0 then
|
||||
user:set_hp(hp)
|
||||
end
|
||||
end
|
||||
|
||||
-- override core.do_item_eat() so we can redirect hp_change to stamina
|
||||
core.do_item_eat = function(hp_change, replace_with_item, itemstack, user, pointed_thing)
|
||||
local old_itemstack = itemstack
|
||||
itemstack = stamina.eat(hp_change, replace_with_item, itemstack, user, pointed_thing)
|
||||
for _, callback in pairs(core.registered_on_item_eats) do
|
||||
local result = callback(hp_change, replace_with_item, itemstack, user, pointed_thing, old_itemstack)
|
||||
if result then
|
||||
return result
|
||||
end
|
||||
end
|
||||
return itemstack
|
||||
end
|
||||
|
||||
-- not local since it's called from within core context
|
||||
function stamina.eat(hp_change, replace_with_item, itemstack, user, pointed_thing)
|
||||
if not itemstack then
|
||||
return itemstack
|
||||
end
|
||||
|
||||
if not user then
|
||||
return itemstack
|
||||
end
|
||||
|
||||
local name = user:get_player_name()
|
||||
if not stamina_players[name] then
|
||||
return itemstack
|
||||
end
|
||||
|
||||
local level = tonumber(stamina_players[name].level or 0)
|
||||
if level >= STAMINA_VISUAL_MAX then
|
||||
return itemstack
|
||||
end
|
||||
|
||||
if hp_change > 0 then
|
||||
level = level + hp_change
|
||||
stamina_update(user, level)
|
||||
else
|
||||
-- assume hp_change < 0.
|
||||
user:hud_change(stamina_players[name].hud_id, "text", "stamina_hud_poison.png")
|
||||
poison_player(2.0, -hp_change, 0, user)
|
||||
end
|
||||
|
||||
minetest.sound_play("stamina_eat", {to_player = name, gain = 0.7})
|
||||
|
||||
if replace_with_item then
|
||||
if itemstack:is_empty() then
|
||||
itemstack:add_item(replace_with_item)
|
||||
else
|
||||
local inv = user:get_inventory()
|
||||
if inv:room_for_item("main", {name=replace_with_item}) then
|
||||
inv:add_item("main", replace_with_item)
|
||||
else
|
||||
local pos = user:getpos()
|
||||
pos.y = math.floor(pos.y + 0.5)
|
||||
core.add_item(pos, replace_with_item)
|
||||
end
|
||||
end
|
||||
else
|
||||
itemstack:take_item()
|
||||
end
|
||||
|
||||
return itemstack
|
||||
end
|
||||
|
||||
-- stamina is disabled if damage is disabled
|
||||
if minetest.setting_getbool("enable_damage") and minetest.is_yes(minetest.setting_get("enable_stamina") or "1") then
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
local inv = player:get_inventory()
|
||||
inv:set_size("stamina", 1)
|
||||
|
||||
local name = player:get_player_name()
|
||||
stamina_players[name] = {}
|
||||
stamina_players[name].level = stamina_read(player)
|
||||
stamina_players[name].exhaust = 0
|
||||
local level = math.min(stamina_players[name].level, STAMINA_VISUAL_MAX)
|
||||
local id = player:hud_add({
|
||||
name = "stamina",
|
||||
hud_elem_type = "statbar",
|
||||
position = {x = 0.5, y = 1},
|
||||
size = {x = 24, y = 24},
|
||||
text = "stamina_hud_fg.png",
|
||||
number = level,
|
||||
alignment = {x = -1, y = -1},
|
||||
offset = {x = -266, y = -110},
|
||||
background = "stamina_hud_bg.png",
|
||||
max = 0,
|
||||
})
|
||||
stamina_players[name].hud_id = id
|
||||
end)
|
||||
|
||||
minetest.register_globalstep(stamina_globaltimer)
|
||||
|
||||
minetest.register_on_placenode(function(pos, oldnode, player, ext)
|
||||
exhaust_player(player, STAMINA_EXHAUST_PLACE)
|
||||
end)
|
||||
minetest.register_on_dignode(function(pos, oldnode, player, ext)
|
||||
exhaust_player(player, STAMINA_EXHAUST_DIG)
|
||||
end)
|
||||
minetest.register_on_craft(function(itemstack, player, old_craft_grid, craft_inv)
|
||||
exhaust_player(player, STAMINA_EXHAUST_CRAFT)
|
||||
end)
|
||||
minetest.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool_capabilities, dir, damage)
|
||||
exhaust_player(hitter, STAMINA_EXHAUST_PUNCH)
|
||||
end)
|
||||
|
||||
minetest.register_on_respawnplayer(function(player)
|
||||
stamina_update(player, STAMINA_VISUAL_MAX)
|
||||
end)
|
||||
end
|
||||
|
BIN
mods/stamina/sounds/stamina_eat.1.ogg
Normal file
BIN
mods/stamina/sounds/stamina_eat.1.ogg
Normal file
Binary file not shown.
BIN
mods/stamina/sounds/stamina_eat.2.ogg
Normal file
BIN
mods/stamina/sounds/stamina_eat.2.ogg
Normal file
Binary file not shown.
BIN
mods/stamina/sounds/stamina_eat.3.ogg
Normal file
BIN
mods/stamina/sounds/stamina_eat.3.ogg
Normal file
Binary file not shown.
BIN
mods/stamina/textures/stamina_hud_bg.png
Normal file
BIN
mods/stamina/textures/stamina_hud_bg.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 417 B |
BIN
mods/stamina/textures/stamina_hud_fg.png
Normal file
BIN
mods/stamina/textures/stamina_hud_fg.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 522 B |
BIN
mods/stamina/textures/stamina_hud_poison.png
Normal file
BIN
mods/stamina/textures/stamina_hud_poison.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
Loading…
Add table
Reference in a new issue