Allow nil puncher in object:punch (#14319)

This commit is contained in:
sfence 2024-04-28 17:55:04 +02:00 committed by GitHub
parent fc0ac64277
commit 72cb4e9bea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 89 additions and 36 deletions

@ -22,14 +22,16 @@ local function vector_absmax(v)
return max(max(abs(v.x), abs(v.y)), abs(v.z)) return max(max(abs(v.x), abs(v.y)), abs(v.z))
end end
core.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool_capabilities, unused_dir, damage) core.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool_capabilities, dir, damage)
if player:get_hp() == 0 then if player:get_hp() == 0 then
return -- RIP return -- RIP
end end
-- Server::handleCommand_Interact() adds eye offset to one but not the other if hitter then
-- so the direction is slightly off, calculate it ourselves -- Server::handleCommand_Interact() adds eye offset to one but not the other
local dir = vector.subtract(player:get_pos(), hitter:get_pos()) -- so the direction is slightly off, calculate it ourselves
dir = vector.subtract(player:get_pos(), hitter:get_pos())
end
local d = vector.length(dir) local d = vector.length(dir)
if d ~= 0.0 then if d ~= 0.0 then
dir = vector.divide(dir, d) dir = vector.divide(dir, d)

@ -5729,7 +5729,7 @@ Call these functions only at load time!
* Called when a player is punched * Called when a player is punched
* Note: This callback is invoked even if the punched player is dead. * Note: This callback is invoked even if the punched player is dead.
* `player`: ObjectRef - Player that was punched * `player`: ObjectRef - Player that was punched
* `hitter`: ObjectRef - Player that hit * `hitter`: ObjectRef - Player that hit. Can be nil.
* `time_from_last_punch`: Meant for disallowing spamming of clicks * `time_from_last_punch`: Meant for disallowing spamming of clicks
(can be nil). (can be nil).
* `tool_capabilities`: Capability table of used item (can be nil) * `tool_capabilities`: Capability table of used item (can be nil)
@ -7825,11 +7825,11 @@ child will follow movement and rotation of that bone.
* no-op if object is attached * no-op if object is attached
* `punch(puncher, time_from_last_punch, tool_capabilities, dir)` * `punch(puncher, time_from_last_punch, tool_capabilities, dir)`
* punches the object, triggering all consequences a normal punch would have * punches the object, triggering all consequences a normal punch would have
* `puncher`: another `ObjectRef` which punched the object * `puncher`: another `ObjectRef` which punched the object or `nil`
* `dir`: direction vector of punch * `dir`: direction vector of punch
* Other arguments: See `on_punch` for entities * Other arguments: See `on_punch` for entities
* All arguments except `puncher` can be `nil`, in which case a default * Arguments `time_from_last_punch`, `tool_capabilities`, and `dir`
value will be used will be replaced with a default value when the caller sets them to `nil`.
* `right_click(clicker)`: * `right_click(clicker)`:
* simulates using the 'place/use' key on the object * simulates using the 'place/use' key on the object
* triggers all consequences as if a real player had done this * triggers all consequences as if a real player had done this

@ -76,3 +76,26 @@ minetest.register_entity("callbacks:callback_step", {
message("on_step callback entity: on_step! pos="..spos(self).."; dtime="..dtime) message("on_step callback entity: on_step! pos="..spos(self).."; dtime="..dtime)
end, end,
}) })
-- Callback punch with nil puncher
minetest.register_entity("callbacks:callback_puncher", {
initial_properties = {
visual = "upright_sprite",
textures = { "callbacks_callback_entity.png" },
infotext = "Callback entity for nil puncher test.",
},
on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir, damage)
if puncher then
puncher:punch(nil, time_from_last_punch, tool_capabilities, dir)
self.object:punch(nil, time_from_last_punch, tool_capabilities, dir)
else
message(
"Callback entity: on_punch with nil puncher "..
"pos="..spos(self).."; "..
"time_from_last_punch="..time_from_last_punch.."; "..
"tool_capabilities="..dump(tool_capabilities).."; "..
"dir="..dump(dir).."; damage="..damage)
end
end,
})

@ -1,3 +1,4 @@
dofile(minetest.get_modpath("callbacks").."/items.lua") dofile(minetest.get_modpath("callbacks").."/items.lua")
dofile(minetest.get_modpath("callbacks").."/nodes.lua") dofile(minetest.get_modpath("callbacks").."/nodes.lua")
dofile(minetest.get_modpath("callbacks").."/entities.lua") dofile(minetest.get_modpath("callbacks").."/entities.lua")
dofile(minetest.get_modpath("callbacks").."/players.lua")

@ -0,0 +1,11 @@
local message = function(msg)
minetest.log("action", "[callbacks] "..msg)
minetest.chat_send_all(msg)
end
core.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool_capabilities, dir, damage)
if not hitter then
message("Player "..player:get_player_name().." punched without hitter.")
end
end)

@ -262,8 +262,6 @@ bool ScriptApiEntity::luaentity_Punch(u16 id,
{ {
SCRIPTAPI_PRECHECKHEADER SCRIPTAPI_PRECHECKHEADER
assert(puncher);
int error_handler = PUSH_ERROR_HANDLER(L); int error_handler = PUSH_ERROR_HANDLER(L);
// Get core.luaentities[id] // Get core.luaentities[id]
@ -278,7 +276,10 @@ bool ScriptApiEntity::luaentity_Punch(u16 id,
} }
luaL_checktype(L, -1, LUA_TFUNCTION); luaL_checktype(L, -1, LUA_TFUNCTION);
lua_pushvalue(L, object); // self lua_pushvalue(L, object); // self
objectrefGetOrCreate(L, puncher); // Clicker reference if (puncher)
objectrefGetOrCreate(L, puncher); // Puncher reference
else
lua_pushnil(L);
lua_pushnumber(L, time_from_last_punch); lua_pushnumber(L, time_from_last_punch);
push_tool_capabilities(L, *toolcap); push_tool_capabilities(L, *toolcap);
push_v3f(L, dir); push_v3f(L, dir);

@ -68,7 +68,10 @@ bool ScriptApiPlayer::on_punchplayer(ServerActiveObject *player,
lua_getfield(L, -1, "registered_on_punchplayers"); lua_getfield(L, -1, "registered_on_punchplayers");
// Call callbacks // Call callbacks
objectrefGetOrCreate(L, player); objectrefGetOrCreate(L, player);
objectrefGetOrCreate(L, hitter); if (hitter)
objectrefGetOrCreate(L, hitter);
else
lua_pushnil(L);
lua_pushnumber(L, time_from_last_punch); lua_pushnumber(L, time_from_last_punch);
push_tool_capabilities(L, *toolcap); push_tool_capabilities(L, *toolcap);
push_v3f(L, dir); push_v3f(L, dir);

@ -170,16 +170,21 @@ int ObjectRef::l_punch(lua_State *L)
{ {
NO_MAP_LOCK_REQUIRED; NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkObject<ObjectRef>(L, 1); ObjectRef *ref = checkObject<ObjectRef>(L, 1);
ObjectRef *puncher_ref = checkObject<ObjectRef>(L, 2); ObjectRef *puncher_ref = lua_isnoneornil(L, 2) ? nullptr : checkObject<ObjectRef>(L, 2);
ServerActiveObject *sao = getobject(ref); ServerActiveObject *sao = getobject(ref);
ServerActiveObject *puncher = getobject(puncher_ref); ServerActiveObject *puncher = puncher_ref ? getobject(puncher_ref) : nullptr;
if (sao == nullptr || puncher == nullptr) if (sao == nullptr)
return 0; return 0;
float time_from_last_punch = readParam<float>(L, 3, 1000000.0f); float time_from_last_punch = readParam<float>(L, 3, 1000000.0f);
ToolCapabilities toolcap = read_tool_capabilities(L, 4); ToolCapabilities toolcap = read_tool_capabilities(L, 4);
v3f dir = readParam<v3f>(L, 5, sao->getBasePosition() - puncher->getBasePosition()); v3f dir;
dir.normalize(); if (puncher) {
dir = readParam<v3f>(L, 5, sao->getBasePosition() - puncher->getBasePosition());
dir.normalize();
} else {
dir = readParam<v3f>(L, 5, v3f(0));
}
u32 wear = sao->punch(dir, &toolcap, puncher, time_from_last_punch); u32 wear = sao->punch(dir, &toolcap, puncher, time_from_last_punch);
lua_pushnumber(L, wear); lua_pushnumber(L, wear);

@ -332,16 +332,16 @@ u32 LuaEntitySAO::punch(v3f dir,
return 0; return 0;
} }
FATAL_ERROR_IF(!puncher, "Punch action called without SAO");
s32 old_hp = getHP(); s32 old_hp = getHP();
ItemStack selected_item, hand_item; ItemStack selected_item, hand_item;
ItemStack tool_item = puncher->getWieldedItem(&selected_item, &hand_item); ItemStack tool_item;
if (puncher)
tool_item = puncher->getWieldedItem(&selected_item, &hand_item);
PunchDamageResult result = getPunchDamage( PunchDamageResult result = getPunchDamage(
m_armor_groups, m_armor_groups,
toolcap, toolcap,
&tool_item, puncher ? &tool_item : nullptr,
time_from_last_punch, time_from_last_punch,
initial_wear); initial_wear);
@ -355,12 +355,16 @@ u32 LuaEntitySAO::punch(v3f dir,
} }
} }
actionstream << puncher->getDescription() << " (id=" << puncher->getId() << if (puncher) {
", hp=" << puncher->getHP() << ") punched " << actionstream << puncher->getDescription() << " (id=" << puncher->getId() <<
getDescription() << " (id=" << m_id << ", hp=" << m_hp << ", hp=" << puncher->getHP() << ")";
"), damage=" << (old_hp - (s32)getHP()) << } else {
(damage_handled ? " (handled by Lua)" : "") << std::endl; actionstream << "(none)";
}
actionstream << " punched " <<
getDescription() << " (id=" << m_id << ", hp=" << m_hp <<
"), damage=" << (old_hp - (s32)getHP()) <<
(damage_handled ? " (handled by Lua)" : "") << std::endl;
// TODO: give Lua control over wear // TODO: give Lua control over wear
return result.wear; return result.wear;
} }

@ -462,11 +462,9 @@ u32 PlayerSAO::punch(v3f dir,
if (!toolcap) if (!toolcap)
return 0; return 0;
FATAL_ERROR_IF(!puncher, "Punch action called without SAO");
// No effect if PvP disabled or if immortal // No effect if PvP disabled or if immortal
if (isImmortal() || !g_settings->getBool("enable_pvp")) { if (isImmortal() || !g_settings->getBool("enable_pvp")) {
if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) { if (puncher && puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
// create message and add to list // create message and add to list
sendPunchCommand(); sendPunchCommand();
return 0; return 0;
@ -493,11 +491,16 @@ u32 PlayerSAO::punch(v3f dir,
} }
} }
actionstream << puncher->getDescription() << " (id=" << puncher->getId() << if (puncher) {
", hp=" << puncher->getHP() << ") punched " << actionstream << puncher->getDescription() << " (id=" << puncher->getId() <<
getDescription() << " (id=" << m_id << ", hp=" << m_hp << ", hp=" << puncher->getHP() << ")";
"), damage=" << (old_hp - (s32)getHP()) << } else {
(damage_handled ? " (handled by Lua)" : "") << std::endl; actionstream << "(none)";
}
actionstream << " puched " <<
getDescription() << " (id=" << m_id << ", hp=" << m_hp <<
"), damage=" << (old_hp - (s32)getHP()) <<
(damage_handled ? " (handled by Lua)" : "") << std::endl;
return hitparams.wear; return hitparams.wear;
} }

@ -482,7 +482,7 @@ PunchDamageResult getPunchDamage(
{ {
HitParams hitparams = getHitParams(armor_groups, toolcap, HitParams hitparams = getHitParams(armor_groups, toolcap,
time_from_last_punch, time_from_last_punch,
punchitem->wear); punchitem ? punchitem->wear : 0);
result.did_punch = true; result.did_punch = true;
result.wear = hitparams.wear; result.wear = hitparams.wear;
result.damage = hitparams.hp; result.damage = hitparams.hp;