mirror of
https://github.com/minetest/minetest.git
synced 2024-12-22 14:12:24 +01:00
Allow detection of damage greater than HP (#15160)
Co-authored-by: Gregor Parzefall <gregor.parzefall@posteo.de>
This commit is contained in:
parent
fbb0e82679
commit
610ddaba7c
@ -5850,8 +5850,13 @@ Call these functions only at load time!
|
|||||||
* `clicker`: ObjectRef - Object that acted upon `player`, may or may not be a player
|
* `clicker`: ObjectRef - Object that acted upon `player`, may or may not be a player
|
||||||
* `minetest.register_on_player_hpchange(function(player, hp_change, reason), modifier)`
|
* `minetest.register_on_player_hpchange(function(player, hp_change, reason), modifier)`
|
||||||
* Called when the player gets damaged or healed
|
* Called when the player gets damaged or healed
|
||||||
|
* When `hp == 0`, damage doesn't trigger this callback.
|
||||||
|
* When `hp == hp_max`, healing does still trigger this callback.
|
||||||
* `player`: ObjectRef of the player
|
* `player`: ObjectRef of the player
|
||||||
* `hp_change`: the amount of change. Negative when it is damage.
|
* `hp_change`: the amount of change. Negative when it is damage.
|
||||||
|
* Historically, the new HP value was clamped to [0, 65535] before
|
||||||
|
calculating the HP change. This clamping has been removed as of
|
||||||
|
Minetest 5.10.0
|
||||||
* `reason`: a PlayerHPChangeReason table.
|
* `reason`: a PlayerHPChangeReason table.
|
||||||
* The `type` field will have one of the following values:
|
* The `type` field will have one of the following values:
|
||||||
* `set_hp`: A mod or the engine called `set_hp` without
|
* `set_hp`: A mod or the engine called `set_hp` without
|
||||||
|
@ -42,41 +42,97 @@ unittests.register("test_hpchangereason", run_hpchangereason_tests, {player=true
|
|||||||
--
|
--
|
||||||
|
|
||||||
local expected_diff = nil
|
local expected_diff = nil
|
||||||
|
local hpchange_counter = 0
|
||||||
|
local die_counter = 0
|
||||||
core.register_on_player_hpchange(function(player, hp_change, reason)
|
core.register_on_player_hpchange(function(player, hp_change, reason)
|
||||||
if expected_diff then
|
if expected_diff then
|
||||||
assert(hp_change == expected_diff)
|
assert(hp_change == expected_diff)
|
||||||
|
hpchange_counter = hpchange_counter + 1
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
core.register_on_dieplayer(function()
|
||||||
|
die_counter = die_counter + 1
|
||||||
|
end)
|
||||||
|
|
||||||
|
local function hp_diference_test(player, hp_max)
|
||||||
|
assert(hp_max >= 22)
|
||||||
|
|
||||||
local function run_hp_difference_tests(player)
|
|
||||||
local old_hp = player:get_hp()
|
local old_hp = player:get_hp()
|
||||||
local old_hp_max = player:get_properties().hp_max
|
local old_hp_max = player:get_properties().hp_max
|
||||||
|
|
||||||
expected_diff = nil
|
hpchange_counter = 0
|
||||||
player:set_properties({hp_max = 30})
|
die_counter = 0
|
||||||
player:set_hp(22)
|
|
||||||
|
|
||||||
-- final HP value is clamped to >= 0 before difference calculation
|
expected_diff = nil
|
||||||
expected_diff = -22
|
player:set_properties({hp_max = hp_max})
|
||||||
|
player:set_hp(22)
|
||||||
|
assert(player:get_hp() == 22)
|
||||||
|
assert(hpchange_counter == 0)
|
||||||
|
assert(die_counter == 0)
|
||||||
|
|
||||||
|
-- HP difference is not clamped
|
||||||
|
expected_diff = -25
|
||||||
player:set_hp(-3)
|
player:set_hp(-3)
|
||||||
-- and actual final HP value is clamped to >= 0 too
|
-- actual final HP value is clamped to >= 0
|
||||||
assert(player:get_hp() == 0)
|
assert(player:get_hp() == 0)
|
||||||
|
assert(hpchange_counter == 1)
|
||||||
|
assert(die_counter == 1)
|
||||||
|
|
||||||
expected_diff = 22
|
expected_diff = 22
|
||||||
player:set_hp(22)
|
player:set_hp(22)
|
||||||
assert(player:get_hp() == 22)
|
assert(player:get_hp() == 22)
|
||||||
|
assert(hpchange_counter == 2)
|
||||||
|
assert(die_counter == 1)
|
||||||
|
|
||||||
-- final HP value is clamped to <= U16_MAX before difference calculation
|
-- Integer overflow is prevented
|
||||||
expected_diff = 65535 - 22
|
-- so result is S32_MIN, not S32_MIN - 22
|
||||||
|
expected_diff = -2147483648
|
||||||
|
player:set_hp(-2147483648)
|
||||||
|
-- actual final HP value is clamped to >= 0
|
||||||
|
assert(player:get_hp() == 0)
|
||||||
|
assert(hpchange_counter == 3)
|
||||||
|
assert(die_counter == 2)
|
||||||
|
|
||||||
|
-- Damage is ignored if player is already dead (hp == 0)
|
||||||
|
expected_diff = "never equal"
|
||||||
|
player:set_hp(-11)
|
||||||
|
assert(player:get_hp() == 0)
|
||||||
|
-- no on_player_hpchange or on_dieplayer call expected
|
||||||
|
assert(hpchange_counter == 3)
|
||||||
|
assert(die_counter == 2)
|
||||||
|
|
||||||
|
expected_diff = 11
|
||||||
|
player:set_hp(11)
|
||||||
|
assert(player:get_hp() == 11)
|
||||||
|
assert(hpchange_counter == 4)
|
||||||
|
assert(die_counter == 2)
|
||||||
|
|
||||||
|
-- HP difference is not clamped
|
||||||
|
expected_diff = 1000000 - 11
|
||||||
player:set_hp(1000000)
|
player:set_hp(1000000)
|
||||||
-- and actual final HP value is clamped to <= hp_max
|
-- actual final HP value is clamped to <= hp_max
|
||||||
assert(player:get_hp() == 30)
|
assert(player:get_hp() == hp_max)
|
||||||
|
assert(hpchange_counter == 5)
|
||||||
|
assert(die_counter == 2)
|
||||||
|
|
||||||
|
-- "Healing" is not ignored when hp == hp_max
|
||||||
|
expected_diff = 80000 - hp_max
|
||||||
|
player:set_hp(80000)
|
||||||
|
assert(player:get_hp() == hp_max)
|
||||||
|
-- on_player_hpchange_call expected
|
||||||
|
assert(hpchange_counter == 6)
|
||||||
|
assert(die_counter == 2)
|
||||||
|
|
||||||
expected_diff = nil
|
expected_diff = nil
|
||||||
player:set_properties({hp_max = old_hp_max})
|
player:set_properties({hp_max = old_hp_max})
|
||||||
player:set_hp(old_hp)
|
player:set_hp(old_hp)
|
||||||
core.close_formspec(player:get_player_name(), "") -- hide death screen
|
core.close_formspec(player:get_player_name(), "") -- hide death screen
|
||||||
end
|
end
|
||||||
|
local function run_hp_difference_tests(player)
|
||||||
|
hp_diference_test(player, 22)
|
||||||
|
hp_diference_test(player, 30)
|
||||||
|
hp_diference_test(player, 65535) -- U16_MAX
|
||||||
|
end
|
||||||
unittests.register("test_hp_difference", run_hp_difference_tests, {player=true})
|
unittests.register("test_hp_difference", run_hp_difference_tests, {player=true})
|
||||||
|
|
||||||
--
|
--
|
||||||
|
@ -519,12 +519,13 @@ void PlayerSAO::rightClick(ServerActiveObject *clicker)
|
|||||||
|
|
||||||
void PlayerSAO::setHP(s32 target_hp, const PlayerHPChangeReason &reason, bool from_client)
|
void PlayerSAO::setHP(s32 target_hp, const PlayerHPChangeReason &reason, bool from_client)
|
||||||
{
|
{
|
||||||
target_hp = rangelim(target_hp, 0, U16_MAX);
|
if (target_hp == m_hp || (m_hp == 0 && target_hp < 0))
|
||||||
|
|
||||||
if (target_hp == m_hp)
|
|
||||||
return; // Nothing to do
|
return; // Nothing to do
|
||||||
|
|
||||||
s32 hp_change = m_env->getScriptIface()->on_player_hpchange(this, target_hp - (s32)m_hp, reason);
|
// Protect against overflow.
|
||||||
|
s32 hp_change = std::max<s64>((s64)target_hp - (s64)m_hp, S32_MIN);
|
||||||
|
|
||||||
|
hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp_change, reason);
|
||||||
hp_change = std::min<s32>(hp_change, U16_MAX); // Protect against overflow
|
hp_change = std::min<s32>(hp_change, U16_MAX); // Protect against overflow
|
||||||
|
|
||||||
s32 hp = (s32)m_hp + hp_change;
|
s32 hp = (s32)m_hp + hp_change;
|
||||||
|
Loading…
Reference in New Issue
Block a user