(WIP) onDeath hook implementation

This commit is contained in:
gsemaj 2022-04-13 00:23:53 -04:00
parent 45742e90a2
commit 68d53feea3
3 changed files with 79 additions and 78 deletions

View File

@ -79,7 +79,7 @@ int CombatNPC::takeDamage(EntityRef src, int amt) {
} }
if (mob->hp <= 0) if (mob->hp <= 0)
killMob(mob->target, mob); transition(AIState::DEAD, src);
return amt; return amt;
} }
@ -144,6 +144,11 @@ void CombatNPC::transition(AIState newState, EntityRef src) {
onRetreat(); onRetreat();
break; break;
case AIState::DEAD: case AIState::DEAD:
/* TODO: fire any triggered events
for (NPCEvent& event : NPCManager::NPCEvents)
if (event.trigger == ON_KILLED && event.npcType == type)
event.handler(src, this);
*/
onDeath(src); onDeath(src);
break; break;
} }
@ -323,7 +328,7 @@ void Combat::npcAttackPc(Mob *mob, time_t currTime) {
* single RNG roll per mission task, and every group member shares that same * single RNG roll per mission task, and every group member shares that same
* set of rolls. * set of rolls.
*/ */
static void genQItemRolls(Player *leader, std::map<int, int>& rolls) { void Combat::genQItemRolls(Player *leader, std::map<int, int>& rolls) {
for (int i = 0; i < leader->groupCnt; i++) { for (int i = 0; i < leader->groupCnt; i++) {
if (leader->groupIDs[i] == 0) if (leader->groupIDs[i] == 0)
continue; continue;
@ -340,79 +345,6 @@ static void genQItemRolls(Player *leader, std::map<int, int>& rolls) {
} }
} }
void Combat::killMob(CNSocket *sock, Mob *mob) {
mob->state = AIState::DEAD;
mob->target = nullptr;
mob->cbf = 0;
mob->skillStyle = -1;
mob->unbuffTimes.clear();
mob->killedTime = getTime(); // XXX: maybe introduce a shard-global time for each step?
// check for the edge case where hitting the mob did not aggro it
if (sock != nullptr) {
Player* plr = PlayerManager::getPlayer(sock);
Items::DropRoll rolled;
Items::DropRoll eventRolled;
std::map<int, int> qitemRolls;
Player *leader = PlayerManager::getPlayerFromID(plr->iIDGroup);
assert(leader != nullptr); // should never happen
genQItemRolls(leader, qitemRolls);
if (plr->groupCnt == 1 && plr->iIDGroup == plr->iID) {
Items::giveMobDrop(sock, mob, rolled, eventRolled);
Missions::mobKilled(sock, mob->type, qitemRolls);
} else {
for (int i = 0; i < leader->groupCnt; i++) {
CNSocket* sockTo = PlayerManager::getSockFromID(leader->groupIDs[i]);
if (sockTo == nullptr)
continue;
Player *otherPlr = PlayerManager::getPlayer(sockTo);
// only contribute to group members' kills if they're close enough
int dist = std::hypot(plr->x - otherPlr->x + 1, plr->y - otherPlr->y + 1);
if (dist > 5000)
continue;
Items::giveMobDrop(sockTo, mob, rolled, eventRolled);
Missions::mobKilled(sockTo, mob->type, qitemRolls);
}
}
}
// delay the despawn animation
mob->despawned = false;
// fire any triggered events
for (NPCEvent& event : NPCManager::NPCEvents)
if (event.trigger == ON_KILLED && event.npcType == mob->type)
event.handler(sock, mob);
auto it = Transport::NPCQueues.find(mob->id);
if (it == Transport::NPCQueues.end() || it->second.empty())
return;
// rewind or empty the movement queue
if (mob->staticPath) {
/*
* This is inelegant, but we wind forward in the path until we find the point that
* corresponds with the Mob's spawn point.
*
* IMPORTANT: The check in TableData::loadPaths() must pass or else this will loop forever.
*/
auto& queue = it->second;
for (auto point = queue.front(); point.x != mob->spawnX || point.y != mob->spawnY; point = queue.front()) {
queue.pop();
queue.push(point);
}
} else {
Transport::NPCQueues.erase(mob->id);
}
}
static void combatBegin(CNSocket *sock, CNPacketData *data) { static void combatBegin(CNSocket *sock, CNPacketData *data) {
Player *plr = PlayerManager::getPlayer(sock); Player *plr = PlayerManager::getPlayer(sock);

View File

@ -17,11 +17,13 @@ struct Bullet {
int bulletType; int bulletType;
}; };
namespace Combat { namespace Combat {
extern std::map<int32_t, std::map<int8_t, Bullet>> Bullets; extern std::map<int32_t, std::map<int8_t, Bullet>> Bullets;
void init(); void init();
void npcAttackPc(Mob *mob, time_t currTime); void npcAttackPc(Mob *mob, time_t currTime);
void killMob(CNSocket *sock, Mob *mob); void genQItemRolls(Player* leader, std::map<int, int>& rolls);
} }

View File

@ -6,6 +6,8 @@
#include "Combat.hpp" #include "Combat.hpp"
#include "Abilities.hpp" #include "Abilities.hpp"
#include "Rand.hpp" #include "Rand.hpp"
#include "Items.hpp"
#include "Missions.hpp"
#include <cmath> #include <cmath>
#include <limits.h> #include <limits.h>
@ -431,7 +433,7 @@ static void drainMobHP(Mob *mob, int amount) {
NPCManager::sendToViewable(mob, (void*)&respbuf, P_FE2CL_CHAR_TIME_BUFF_TIME_TICK, resplen); NPCManager::sendToViewable(mob, (void*)&respbuf, P_FE2CL_CHAR_TIME_BUFF_TIME_TICK, resplen);
if (mob->hp <= 0) if (mob->hp <= 0)
Combat::killMob(mob->target, mob); mob->transition(AIState::DEAD, mob->target);
} }
void Mob::deadStep(time_t currTime) { void Mob::deadStep(time_t currTime) {
@ -775,5 +777,70 @@ void Mob::onRetreat() {
} }
void Mob::onDeath(EntityRef src) { void Mob::onDeath(EntityRef src) {
// stub target = nullptr;
cbf = 0;
skillStyle = -1;
unbuffTimes.clear();
killedTime = getTime(); // XXX: maybe introduce a shard-global time for each step?
// check for the edge case where hitting the mob did not aggro it
if (src.type == EntityType::PLAYER && src.isValid()) {
Player* plr = PlayerManager::getPlayer(src.sock);
Items::DropRoll rolled;
Items::DropRoll eventRolled;
std::map<int, int> qitemRolls;
Player* leader = PlayerManager::getPlayerFromID(plr->iIDGroup);
assert(leader != nullptr); // should never happen
Combat::genQItemRolls(leader, qitemRolls);
if (plr->groupCnt == 1 && plr->iIDGroup == plr->iID) {
Items::giveMobDrop(src.sock, this, rolled, eventRolled);
Missions::mobKilled(src.sock, type, qitemRolls);
}
else {
for (int i = 0; i < leader->groupCnt; i++) {
CNSocket* sockTo = PlayerManager::getSockFromID(leader->groupIDs[i]);
if (sockTo == nullptr)
continue;
Player* otherPlr = PlayerManager::getPlayer(sockTo);
// only contribute to group members' kills if they're close enough
int dist = std::hypot(plr->x - otherPlr->x + 1, plr->y - otherPlr->y + 1);
if (dist > 5000)
continue;
Items::giveMobDrop(sockTo, this, rolled, eventRolled);
Missions::mobKilled(sockTo, type, qitemRolls);
}
}
}
// delay the despawn animation
despawned = false;
auto it = Transport::NPCQueues.find(id);
if (it == Transport::NPCQueues.end() || it->second.empty())
return;
// rewind or empty the movement queue
if (staticPath) {
/*
* This is inelegant, but we wind forward in the path until we find the point that
* corresponds with the Mob's spawn point.
*
* IMPORTANT: The check in TableData::loadPaths() must pass or else this will loop forever.
*/
auto& queue = it->second;
for (auto point = queue.front(); point.x != spawnX || point.y != spawnY; point = queue.front()) {
queue.pop();
queue.push(point);
}
}
else {
Transport::NPCQueues.erase(id);
}
} }