diff --git a/MCServer/Plugins/APIDump/Hooks/OnKilled.lua b/MCServer/Plugins/APIDump/Hooks/OnKilled.lua new file mode 100644 index 000000000..2eaf23904 --- /dev/null +++ b/MCServer/Plugins/APIDump/Hooks/OnKilled.lua @@ -0,0 +1,22 @@ +return +{ + HOOK_KILLED = + { + CalledWhen = "A player or a mob died.", + DefaultFnName = "OnKilled", + Desc = [[ + This hook is called whenever player or a mob dies. It can be used to change the death message. + ]] + Params = + { + { Name = "Victim", Type = "{{cEntity}}", Notes = "The player or mob that died" }, + { Name = "TDI", Type = "{{TakeDamageInfo}}", Notes = "Informations about the death" }, + { Name = "DeathMessage", Type = "string", Notes = "The default death message. An empty string if the victim is not a player" }, + }, + Returns = [[ + The function may return two values. The first value is a boolean specifying whether other plugins should be called. If it is true, the other plugins won't get notified of the death. If it is false, the other plugins will get notified.
+The second value is a string containing the death message. If the victim is a player, this death message is broadcasted instead of the default death message. If it is empty, no death message is broadcasted. If it is nil, the message is left unchanged. If the victim is not a player, the death message is never broadcasted.
+In either case, the victim is dead. + ]], + }, -- HOOK_KILLED +} diff --git a/MCServer/Plugins/APIDump/Hooks/OnKilling.lua b/MCServer/Plugins/APIDump/Hooks/OnKilling.lua index 5e84009db..d2339e60b 100644 --- a/MCServer/Plugins/APIDump/Hooks/OnKilling.lua +++ b/MCServer/Plugins/APIDump/Hooks/OnKilling.lua @@ -7,11 +7,7 @@ return Desc = [[ This hook is called whenever a {{cPawn|pawn}}'s (a player's or a mob's) health reaches zero. This means that the pawn is about to be killed, unless a plugin "revives" them by setting their health - back to a positive value.
-- FIXME: There is no HOOK_KILLED notification hook yet; this is deliberate because HOOK_KILLED has - been recently renamed to HOOK_KILLING, and plugins need to be updated. Once updated, the HOOK_KILLED - notification will be implemented. + back to a positive value. ]], Params = { diff --git a/src/Bindings/Plugin.h b/src/Bindings/Plugin.h index 1330bca0d..2fdf73a65 100644 --- a/src/Bindings/Plugin.h +++ b/src/Bindings/Plugin.h @@ -64,6 +64,7 @@ public: virtual bool OnHandshake (cClientHandle & a_Client, const AString & a_Username) = 0; virtual bool OnHopperPullingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_DstSlotNum, cBlockEntityWithItems & a_SrcEntity, int a_SrcSlotNum) = 0; virtual bool OnHopperPushingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum) = 0; + virtual bool OnKilled (cEntity & a_Victim, TakeDamageInfo & a_TDI, AString & a_DeathMessage) = 0; virtual bool OnKilling (cEntity & a_Victim, cEntity * a_Killer, TakeDamageInfo & a_TDI) = 0; virtual bool OnLogin (cClientHandle & a_Client, int a_ProtocolVersion, const AString & a_Username) = 0; virtual bool OnPlayerAnimation (cPlayer & a_Player, int a_Animation) = 0; diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp index d3ffcd0f4..9be4e0eb4 100644 --- a/src/Bindings/PluginLua.cpp +++ b/src/Bindings/PluginLua.cpp @@ -750,6 +750,30 @@ bool cPluginLua::OnHopperPushingItem(cWorld & a_World, cHopperEntity & a_Hopper, +bool cPluginLua::OnKilled(cEntity & a_Victim, TakeDamageInfo & a_TDI, AString & a_DeathMessage) +{ + cCSLock Lock(m_CriticalSection); + if (!m_LuaState.IsValid()) + { + return false; + } + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_KILLED]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Victim, &a_TDI, a_DeathMessage, cLuaState::Return, res, a_DeathMessage); + if (res) + { + return true; + } + } + return false; +} + + + + + bool cPluginLua::OnKilling(cEntity & a_Victim, cEntity * a_Killer, TakeDamageInfo & a_TDI) { cCSLock Lock(m_CriticalSection); diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h index a763cdfdf..7de70ef1f 100644 --- a/src/Bindings/PluginLua.h +++ b/src/Bindings/PluginLua.h @@ -123,6 +123,7 @@ public: virtual bool OnHandshake (cClientHandle & a_Client, const AString & a_Username) override; virtual bool OnHopperPullingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_DstSlotNum, cBlockEntityWithItems & a_SrcEntity, int a_SrcSlotNum) override; virtual bool OnHopperPushingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum) override; + virtual bool OnKilled (cEntity & a_Victim, TakeDamageInfo & a_TDI, AString & a_DeathMessage) override; virtual bool OnKilling (cEntity & a_Victim, cEntity * a_Killer, TakeDamageInfo & a_TDI) override; virtual bool OnLogin (cClientHandle & a_Client, int a_ProtocolVersion, const AString & a_Username) override; virtual bool OnPlayerAnimation (cPlayer & a_Player, int a_Animation) override; diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp index 5b6bec728..712c385c7 100644 --- a/src/Bindings/PluginManager.cpp +++ b/src/Bindings/PluginManager.cpp @@ -675,6 +675,25 @@ bool cPluginManager::CallHookHopperPushingItem(cWorld & a_World, cHopperEntity & +bool cPluginManager::CallHookKilled(cEntity & a_Victim, TakeDamageInfo & a_TDI, AString & a_DeathMessage) +{ + FIND_HOOK(HOOK_KILLED); + VERIFY_HOOK; + + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnKilled(a_Victim, a_TDI, a_DeathMessage)) + { + return true; + } + } + return false; +} + + + + + bool cPluginManager::CallHookKilling(cEntity & a_Victim, cEntity * a_Killer, TakeDamageInfo & a_TDI) { FIND_HOOK(HOOK_KILLING); diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h index 6bcef87bf..153f4996d 100644 --- a/src/Bindings/PluginManager.h +++ b/src/Bindings/PluginManager.h @@ -94,6 +94,7 @@ public: HOOK_HANDSHAKE, HOOK_HOPPER_PULLING_ITEM, HOOK_HOPPER_PUSHING_ITEM, + HOOK_KILLED, HOOK_KILLING, HOOK_LOGIN, HOOK_PLAYER_BREAKING_BLOCK, @@ -211,6 +212,7 @@ public: bool CallHookHandshake (cClientHandle & a_ClientHandle, const AString & a_Username); bool CallHookHopperPullingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_DstSlotNum, cBlockEntityWithItems & a_SrcEntity, int a_SrcSlotNum); bool CallHookHopperPushingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum); + bool CallHookKilled (cEntity & a_Victim, TakeDamageInfo & a_TDI, AString & a_DeathMessage); bool CallHookKilling (cEntity & a_Victim, cEntity * a_Killer, TakeDamageInfo & a_TDI); bool CallHookLogin (cClientHandle & a_Client, int a_ProtocolVersion, const AString & a_Username); bool CallHookPlayerAnimation (cPlayer & a_Player, int a_Animation); diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index 108f79e82..500204a98 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -744,6 +744,13 @@ void cEntity::KilledBy(TakeDamageInfo & a_TDI) return; } + // If the victim is a player the hook is handled by the cPlayer class + if (!IsPlayer()) + { + AString emptystring = AString(""); + cRoot::Get()->GetPluginManager()->CallHookKilled(*this, a_TDI, emptystring); + } + // Drop loot: cItems Drops; GetDrops(Drops, a_TDI.Attacker); diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 0ca560d75..97e2eca3a 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -920,11 +920,11 @@ void cPlayer::KilledBy(TakeDamageInfo & a_TDI) { Pickups.Add(cItem(E_ITEM_RED_APPLE)); } - m_Stats.AddValue(statItemsDropped, (StatValue)Pickups.Size()); m_World->SpawnItemPickups(Pickups, GetPosX(), GetPosY(), GetPosZ(), 10); SaveToDisk(); // Save it, yeah the world is a tough place ! + cPluginManager * PluginManager = cRoot::Get()->GetPluginManager(); if ((a_TDI.Attacker == nullptr) && m_World->ShouldBroadcastDeathMessages()) { @@ -950,7 +950,12 @@ void cPlayer::KilledBy(TakeDamageInfo & a_TDI) case dtExplosion: DamageText = "blew up"; break; default: DamageText = "died, somehow; we've no idea how though"; break; } - GetWorld()->BroadcastChatDeath(Printf("%s %s", GetName().c_str(), DamageText.c_str())); + AString DeathMessage = Printf("%s %s", GetName().c_str(), DamageText.c_str()); + PluginManager->CallHookKilled(*this, a_TDI, DeathMessage); + if (DeathMessage != AString("")) + { + GetWorld()->BroadcastChatDeath(DeathMessage); + } } else if (a_TDI.Attacker == nullptr) // && !m_World->ShouldBroadcastDeathMessages() by fallthrough { @@ -959,15 +964,23 @@ void cPlayer::KilledBy(TakeDamageInfo & a_TDI) else if (a_TDI.Attacker->IsPlayer()) { cPlayer * Killer = (cPlayer *)a_TDI.Attacker; - - GetWorld()->BroadcastChatDeath(Printf("%s was killed by %s", GetName().c_str(), Killer->GetName().c_str())); + AString DeathMessage = Printf("%s was killed by %s", GetName().c_str(), Killer->GetName().c_str()); + PluginManager->CallHookKilled(*this, a_TDI, DeathMessage); + if (DeathMessage != AString("")) + { + GetWorld()->BroadcastChatDeath(DeathMessage); + } } else { AString KillerClass = a_TDI.Attacker->GetClass(); KillerClass.erase(KillerClass.begin()); // Erase the 'c' of the class (e.g. "cWitch" -> "Witch") - - GetWorld()->BroadcastChatDeath(Printf("%s was killed by a %s", GetName().c_str(), KillerClass.c_str())); + AString DeathMessage = Printf("%s was killed by a %s", GetName().c_str(), KillerClass.c_str()); + PluginManager->CallHookKilled(*this, a_TDI, DeathMessage); + if (DeathMessage != AString("")) + { + GetWorld()->BroadcastChatDeath(DeathMessage); + } } m_Stats.AddValue(statDeaths);