Apply eat effect when hunger disabled

This commit is contained in:
Wuzzy 2019-02-06 21:23:05 +01:00
parent 93c86207d9
commit 4bd9be22e0
6 changed files with 247 additions and 158 deletions

@ -1,19 +1,35 @@
# API information (WIP)
This API information is WIP. The mod API is still pretty much unofficial;
this mod is mostly seen as standalone for now.
This API information is not complete yet.
The mod API is still pretty much unofficial; this mod is mostly seen
as standalone for now.
This may change in the future development of MineClone 2. Hopefully.
## Before using this mod
This mod is a no-op when the game is started with damage disabled.
Before using any of the functions, first check if global variable
“`mcl_hunger`” is present.
## Mod state
The hunger mechanic is disabled when damage is disabled
(setting `enable_damage=false`).
You can check the hunger state with `mcl_hunger.active`. If it's true,
then hunger is active.
## Hunger level
If the hunger is disabled, most of the functions are no-ops or return
default values.
## Player values
### Hunger level
The hunger level of the player is a whole number between 0 and 20 inclusive.
0 is starving and 20 is full. The hunger level is represented in
the HUD by a statbar with 20 half-icons.
### Saturation
To be written ...
### Exhaustion
To be written ...
## Functions
This API documentation is not complete yet, more documentation will
come.
### `mcl_hunger.get_hunger(player)`
Returns the current hunger level of `player` (ObjectRef).
@ -21,6 +37,15 @@ Returns the current hunger level of `player` (ObjectRef).
Sets the hunger level of `player` (ObjectRef) to `hunger` immediately.
`hunger` ***must*** be between 0 and 20 inclusive.
### `mcl_hunger.exhaust(player, exhaust)`
Increase exhaustion of player by `exhaust`.
### `mcl_hunger.stop_poison(player)`
Immediately stops all poisonings for player.
### More functions ...
There are more functions (of less importance) available, see `api.lua`.
## Groups
Items in group `food=3` will make a drinking sound and no particles.
Items in group `food` with any other rating will make an eating sound and particles,

@ -0,0 +1,148 @@
mcl_hunger.registered_foods = {}
if mcl_hunger.active then
function mcl_hunger.get_hunger(player)
local hunger = player:get_attribute("mcl_hunger:hunger") or 20
return tonumber(hunger)
end
function mcl_hunger.get_saturation(player)
local saturation = player:get_attribute("mcl_hunger:saturation") or mcl_hunger.SATURATION_INIT
return tonumber(saturation)
end
function mcl_hunger.get_exhaustion(player)
local exhaustion = player:get_attribute("mcl_hunger:exhaustion") or 0
return tonumber(exhaustion)
end
function mcl_hunger.set_hunger(player, hunger, update_hudbars)
hunger = math.min(20, math.max(0, hunger))
player:set_attribute("mcl_hunger:hunger", tostring(hunger))
if update_hudbars ~= false then
hb.change_hudbar(player, "hunger", hunger)
mcl_hunger.update_saturation_hud(player, nil, hunger)
end
return true
end
function mcl_hunger.set_saturation(player, saturation, update_hudbar)
saturation = math.min(mcl_hunger.get_hunger(player), math.max(0, saturation))
player:set_attribute("mcl_hunger:saturation", tostring(saturation))
if update_hudbar ~= false then
mcl_hunger.update_saturation_hud(player, saturation)
end
return true
end
function mcl_hunger.set_exhaustion(player, exhaustion, update_hudbar)
exhaustion = math.min(mcl_hunger.EXHAUST_LVL, math.max(0.0, exhaustion))
player:set_attribute("mcl_hunger:exhaustion", tostring(exhaustion))
if update_hudbar ~= false then
mcl_hunger.update_exhaustion_hud(player, exhaustion)
end
return true
end
function mcl_hunger.exhaust(playername, increase)
local player = minetest.get_player_by_name(playername)
if not player then return false end
mcl_hunger.set_exhaustion(player, mcl_hunger.get_exhaustion(player) + increase)
if mcl_hunger.get_exhaustion(player) >= mcl_hunger.EXHAUST_LVL then
mcl_hunger.set_exhaustion(player, 0.0)
local h = nil
local satuchanged = false
local s = mcl_hunger.get_saturation(player)
if s > 0 then
mcl_hunger.set_saturation(player, math.max(s - 1.0, 0))
satuchanged = true
elseif s <= 0.0001 then
h = mcl_hunger.get_hunger(player)
h = math.max(h-1, 0)
mcl_hunger.set_hunger(player, h)
satuchanged = true
end
if satuchanged then
if h ~= nil then h = h end
mcl_hunger.update_saturation_hud(player, mcl_hunger.get_saturation(player), h)
end
end
mcl_hunger.update_exhaustion_hud(player, mcl_hunger.get_exhaustion(player))
return true
end
function mcl_hunger.saturate(playername, increase, update_hudbar)
local player = minetest.get_player_by_name(playername)
local ok = mcl_hunger.set_saturation(player, math.min(mcl_hunger.get_saturation(player) + increase, mcl_hunger.get_hunger(player)))
if update_hudbar ~= false then
mcl_hunger.update_saturation_hud(player, mcl_hunger.get_saturation(player), mcl_hunger.get_hunger(player))
end
return ok
end
function mcl_hunger.register_food(name, hunger_change, replace_with_item, poisontime, poison, exhaust, poisonchance, sound)
if not mcl_hunger.active then
return
end
local food = mcl_hunger.registered_foods
food[name] = {}
food[name].saturation = hunger_change -- hunger points added
food[name].replace = replace_with_item -- what item is given back after eating
food[name].poisontime = poisontime -- time it is poisoning. If this is set, this item is considered poisonous,
-- otherwise the following poison/exhaust fields are ignored
food[name].poison = poison -- poison damage per tick for poisonous food
food[name].exhaust = exhaust -- exhaustion per tick for poisonous food
food[name].poisonchance = poisonchance -- chance percentage that this item poisons the player (default: 100% if poisoning is enabled)
food[name].sound = sound -- special sound that is played when eating
end
function mcl_hunger.stop_poison(player)
if not mcl_hunger.active then
return
end
mcl_hunger.poison_damage[player:get_player_name()] = 0
mcl_hunger.poison_hunger[player:get_player_name()] = 0
mcl_hunger.reset_bars_poison_damage(player)
mcl_hunger.reset_bars_poison_hunger(player)
end
else
-- When hunger is disabled, the functions are basically no-ops
function mcl_hunger.get_hunger()
return 20
end
function mcl_hunger.get_saturation()
return mcl_hunger.SATURATION_INIT
end
function mcl_hunger.get_exhaustion()
return 0
end
function mcl_hunger.set_hunger()
return false
end
function mcl_hunger.set_saturation()
return false
end
function mcl_hunger.set_exhaustion()
return false
end
function mcl_hunger.exhaust()
return false
end
function mcl_hunger.saturate()
return false
end
function mcl_hunger.register_food() end
function mcl_hunger.stop_poison() end
end

@ -31,7 +31,7 @@ minetest.do_item_eat = function(hp_change, replace_with_item, itemstack, user, p
-- FIXME: In singleplayer, there's a cheat to circumvent this, simply by pausing the game between eats.
-- This is because os.time() obviously does not care about the pause. A fix needs a different timer mechanism.
if no_eat_delay or (mcl_hunger.last_eat[name] < 0) or (os.difftime(os.time(), mcl_hunger.last_eat[name]) >= 2) then
local can_eat_when_full = creative or minetest.get_item_group(itemstack:get_name(), "can_eat_when_full") == 1
local can_eat_when_full = creative or (mcl_hunger.active == false) or minetest.get_item_group(itemstack:get_name(), "can_eat_when_full") == 1
-- Don't allow eating when player has full hunger bar (some exceptional items apply)
if can_eat_when_full or (mcl_hunger.get_hunger(user) < 20) then
itemstack = mcl_hunger.eat(hp_change, replace_with_item, itemstack, user, pointed_thing)
@ -48,24 +48,9 @@ minetest.do_item_eat = function(hp_change, replace_with_item, itemstack, user, p
return itemstack
end
-- food functions
local food = {}
function mcl_hunger.register_food(name, hunger_change, replace_with_item, poisontime, poison, exhaust, poisonchance, sound)
food[name] = {}
food[name].saturation = hunger_change -- hunger points added
food[name].replace = replace_with_item -- what item is given back after eating
food[name].poisontime = poisontime -- time it is poisoning. If this is set, this item is considered poisonous,
-- otherwise the following poison/exhaust fields are ignored
food[name].poison = poison -- poison damage per tick for poisonous food
food[name].exhaust = exhaust -- exhaustion per tick for poisonous food
food[name].poisonchance = poisonchance -- chance percentage that this item poisons the player (default: 100% if poisoning is enabled)
food[name].sound = sound -- special sound that is played when eating
end
function mcl_hunger.eat(hp_change, replace_with_item, itemstack, user, pointed_thing)
local item = itemstack:get_name()
local def = food[item]
local def = mcl_hunger.registered_foods[item]
if not def then
def = {}
if type(hp_change) ~= "number" then
@ -80,11 +65,11 @@ function mcl_hunger.eat(hp_change, replace_with_item, itemstack, user, pointed_t
end
-- Reset HUD bars after poisoning
local function reset_bars_poison_damage(player)
function mcl_hunger.reset_bars_poison_damage(player)
hb.change_hudbar(player, "health", nil, nil, "hudbars_icon_health.png", nil, "hudbars_bar_health.png")
end
local function reset_bars_poison_hunger(player)
function mcl_hunger.reset_bars_poison_hunger(player)
hb.change_hudbar(player, "hunger", nil, nil, "hbhunger_icon.png", nil, "hbhunger_bar.png")
if mcl_hunger.debug then
hb.change_hudbar(player, "exhaustion", nil, nil, nil, nil, "mcl_hunger_bar_exhaustion.png")
@ -93,6 +78,9 @@ end
-- Poison player
local function poisonp(tick, time, time_left, damage, exhaustion, name)
if not mcl_hunger.active then
return
end
local player = minetest.get_player_by_name(name)
-- First check if player is still there
if not player then
@ -114,10 +102,10 @@ local function poisonp(tick, time, time_left, damage, exhaustion, name)
mcl_hunger.poison_hunger [name] = mcl_hunger.poison_hunger[name] - 1
end
if mcl_hunger.poison_damage[name] <= 0 then
reset_bars_poison_damage(player)
mcl_hunger.reset_bars_poison_damage(player)
end
if mcl_hunger.poison_hunger[name] <= 0 then
reset_bars_poison_hunger(player)
mcl_hunger.reset_bars_poison_hunger(player)
end
end
@ -130,14 +118,6 @@ local function poisonp(tick, time, time_left, damage, exhaustion, name)
end
-- Immediately stop all poisonings for this player
function mcl_hunger.stop_poison(player)
mcl_hunger.poison_damage[player:get_player_name()] = 0
mcl_hunger.poison_hunger[player:get_player_name()] = 0
reset_bars_poison_damage(player)
reset_bars_poison_hunger(player)
end
local poisonrandomizer = PseudoRandom(os.time())
function mcl_hunger.item_eat(hunger_change, replace_with_item, poisontime, poison, exhaust, poisonchance, sound)
@ -202,7 +182,7 @@ function mcl_hunger.item_eat(hunger_change, replace_with_item, poisontime, poiso
})
end
if hunger_change then
if mcl_hunger.active and hunger_change then
-- Add saturation (must be defined in item table)
local _mcl_saturation = minetest.registered_items[itemname]._mcl_saturation
local saturation
@ -225,7 +205,7 @@ function mcl_hunger.item_eat(hunger_change, replace_with_item, poisontime, poiso
mcl_hunger.update_saturation_hud(user, mcl_hunger.get_saturation(user), h)
end
-- Poison
if poisontime then
if mcl_hunger.active and poisontime then
local do_poison = false
if poisonchance then
if poisonrandomizer:next(0,100) < poisonchance then
@ -251,7 +231,6 @@ function mcl_hunger.item_eat(hunger_change, replace_with_item, poisontime, poiso
end
end
--sound:eat
if not creative then
itemstack:add_item(replace_with_item)
end
@ -260,6 +239,7 @@ function mcl_hunger.item_eat(hunger_change, replace_with_item, poisontime, poiso
end
end
if mcl_hunger.active then
-- player-action based hunger changes
minetest.register_on_dignode(function(pos, oldnode, player)
-- is_fake_player comes from the pipeworks, we are not interested in those
@ -270,14 +250,5 @@ minetest.register_on_dignode(function(pos, oldnode, player)
-- dig event
mcl_hunger.exhaust(name, mcl_hunger.EXHAUST_DIG)
end)
end
-- Apply simple poison effect as long there are no real status effect
-- TODO: Remove this when status effects are in place
mcl_hunger.register_food("mcl_farming:potato_item_poison", 2, "", 4, 1, 0, 60)
mcl_hunger.register_food("mcl_mobitems:rotten_flesh", 4, "", 30, 0, 100, 80)
mcl_hunger.register_food("mcl_mobitems:chicken", 2, "", 30, 0, 100, 30)
mcl_hunger.register_food("mcl_mobitems:spider_eye", 2, "", 4, 1, 0)
mcl_hunger.register_food("mcl_fishing:pufferfish_raw", 1, "", 60, 1, 300)

@ -8,13 +8,17 @@ end
mcl_hunger = {}
-- This variable tells you if the hunger gameplay mechanic is active.
-- IMPORTANT: If damage is disabled on load time, most of the functions are NOT
-- available! Check if mcl_hunger is active before using the API.
--[[ This variable tells you if the hunger gameplay mechanic is active.
The state of the hunger mechanic will be determined at game start.
Hunger is enabled when damage is enabled.
If the damage setting is changed within the game, this does NOT
update the hunger mechanic, so the game must be restarted for this
to take effect. ]]
if minetest.settings:get_bool("enable_damage") == true then
mcl_hunger.active = true
else
mcl_hunger.active = false
mcl_hunger.exhaust = function() end
end
mcl_hunger.HUD_TICK = 0.1
@ -31,11 +35,22 @@ mcl_hunger.EXHAUST_LVL = 4000 -- at what exhaustion player saturation gets lower
mcl_hunger.SATURATION_INIT = 5 -- Initial saturation for new/respawning players
if minetest.settings:get_bool("enable_damage") then
mcl_hunger.active = true
-- Debug Mode. If enabled, saturation and exhaustion are shown as well.
-- NOTE: Read-only. The setting should only be read at the beginning, this mod is not
-- NOTE: Only updated when settings are loaded.
mcl_hunger.debug = false
-- Cooldown timers for each player, to force a short delay between consuming 2 food items
mcl_hunger.last_eat = {}
dofile(minetest.get_modpath("mcl_hunger").."/api.lua")
dofile(minetest.get_modpath("mcl_hunger").."/hunger.lua")
dofile(minetest.get_modpath("mcl_hunger").."/register_foods.lua")
--[[ IF HUNGER IS ENABLED ]]
if mcl_hunger.active == true then
-- Read debug mode setting
-- The setting should only be read at the beginning, this mod is not
-- prepared to change this setting later.
mcl_hunger.debug = minetest.settings:get_bool("mcl_hunger_debug")
if mcl_hunger.debug == nil then
@ -56,9 +71,6 @@ end
mcl_hunger.poison_damage = {} -- damaging poison
mcl_hunger.poison_hunger = {} -- food poisoning, increasing hunger
-- Cooldown timers for each player, to force a short delay between consuming 2 food items
mcl_hunger.last_eat = {}
-- HUD item ids
local hunger_hud = {}
@ -82,8 +94,6 @@ function mcl_hunger.update_exhaustion_hud(player, exhaustion)
end
end
dofile(minetest.get_modpath("mcl_hunger").."/hunger.lua")
-- register saturation hudbar
hb.register_hudbar("hunger", 0xFFFFFF, S("Food"), { icon = "hbhunger_icon.png", bgicon = "hbhunger_bgicon.png", bar = "hbhunger_bar.png" }, 20, 20, false)
if mcl_hunger.debug then
@ -91,53 +101,6 @@ if mcl_hunger.debug then
hb.register_hudbar("exhaustion", 0xFFFFFF, S("Exhaust."), { icon = "mcl_hunger_icon_exhaustion.png", bgicon = "mcl_hunger_bgicon_exhaustion.png", bar = "mcl_hunger_bar_exhaustion.png" }, 0, mcl_hunger.EXHAUST_LVL, false, S("%s: %d/%d"))
end
-- API START --
mcl_hunger.get_hunger = function(player)
local hunger = player:get_attribute("mcl_hunger:hunger") or 20
return tonumber(hunger)
end
mcl_hunger.get_saturation = function(player)
local saturation = player:get_attribute("mcl_hunger:saturation") or mcl_hunger.SATURATION_INIT
return tonumber(saturation)
end
mcl_hunger.get_exhaustion = function(player)
local exhaustion = player:get_attribute("mcl_hunger:exhaustion") or 0
return tonumber(exhaustion)
end
mcl_hunger.set_hunger = function(player, hunger, update_hudbars)
hunger = math.min(20, math.max(0, hunger))
player:set_attribute("mcl_hunger:hunger", tostring(hunger))
if update_hudbars ~= false then
hb.change_hudbar(player, "hunger", hunger)
mcl_hunger.update_saturation_hud(player, nil, hunger)
end
return true
end
mcl_hunger.set_saturation = function(player, saturation, update_hudbar)
saturation = math.min(mcl_hunger.get_hunger(player), math.max(0, saturation))
player:set_attribute("mcl_hunger:saturation", tostring(saturation))
if update_hudbar ~= false then
mcl_hunger.update_saturation_hud(player, saturation)
end
return true
end
mcl_hunger.set_exhaustion = function(player, exhaustion, update_hudbar)
exhaustion = math.min(mcl_hunger.EXHAUST_LVL, math.max(0.0, exhaustion))
player:set_attribute("mcl_hunger:exhaustion", tostring(exhaustion))
if update_hudbar ~= false then
mcl_hunger.update_exhaustion_hud(player, exhaustion)
end
return true
end
-- END OF API --
minetest.register_on_newplayer(function(player)
local name = player:get_player_name()
mcl_hunger.set_hunger(player, 20, false)
@ -177,41 +140,6 @@ minetest.register_on_punchplayer(function(victim, puncher, time_from_last_punch,
end
end)
function mcl_hunger.exhaust(playername, increase)
local player = minetest.get_player_by_name(playername)
if not player then return false end
mcl_hunger.set_exhaustion(player, mcl_hunger.get_exhaustion(player) + increase)
if mcl_hunger.get_exhaustion(player) >= mcl_hunger.EXHAUST_LVL then
mcl_hunger.set_exhaustion(player, 0.0)
local h = nil
local satuchanged = false
local s = mcl_hunger.get_saturation(player)
if s > 0 then
mcl_hunger.set_saturation(player, math.max(s - 1.0, 0))
satuchanged = true
elseif s <= 0.0001 then
h = mcl_hunger.get_hunger(player)
h = math.max(h-1, 0)
mcl_hunger.set_hunger(player, h)
satuchanged = true
end
if satuchanged then
if h ~= nil then h = h end
mcl_hunger.update_saturation_hud(player, mcl_hunger.get_saturation(player), h)
end
end
mcl_hunger.update_exhaustion_hud(player, mcl_hunger.get_exhaustion(player))
return true
end
function mcl_hunger.saturate(playername, increase, update_hudbar)
local player = minetest.get_player_by_name(playername)
mcl_hunger.set_saturation(player, math.min(mcl_hunger.get_saturation(player) + increase, mcl_hunger.get_hunger(player)))
if update_hudbar ~= false then
mcl_hunger.update_saturation_hud(player, mcl_hunger.get_saturation(player), mcl_hunger.get_hunger(player))
end
end
local main_timer = 0
local timer = 0 -- Half second timer
local timerMult = 1 -- Cycles from 0 to 7, each time when timer hits half a second
@ -255,4 +183,11 @@ minetest.register_globalstep(function(dtime)
end
end)
--[[ IF HUNGER IS NOT ENABLED ]]
else
minetest.register_on_joinplayer(function(player)
mcl_hunger.last_eat[player:get_player_name()] = -1
end)
end

@ -0,0 +1,11 @@
-- Apply simple poison effect as long there are no real status effect
-- TODO: Remove this when status effects are in place
-- TODO: Consider moving these to the respective mods
mcl_hunger.register_food("mcl_farming:potato_item_poison", 2, "", 4, 1, 0, 60)
mcl_hunger.register_food("mcl_mobitems:rotten_flesh", 4, "", 30, 0, 100, 80)
mcl_hunger.register_food("mcl_mobitems:chicken", 2, "", 30, 0, 100, 30)
mcl_hunger.register_food("mcl_mobitems:spider_eye", 2, "", 4, 1, 0)
mcl_hunger.register_food("mcl_fishing:pufferfish_raw", 1, "", 60, 1, 300)

@ -1,6 +1,5 @@
mcl_init
mcl_core
mcl_particles
mcl_hunger
mcl_death_messages
3d_armor?