mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2024-11-22 13:30:06 +00:00
(WIP) onDeath hook implementation
This commit is contained in:
parent
45742e90a2
commit
68d53feea3
@ -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);
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user