mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2024-11-22 05:20:05 +00:00
Mob-related cleanup.
* NPCs now keep track of their chunk information like PlayerView does for players * NPCManager::sendToViewable() parallels PlayerManager::sendToViewable() * Nano damage and debuffs now count as attacking a mob * Mobs will de-aggro if something else killed their target
This commit is contained in:
parent
72d625fd8d
commit
279cb78d5f
@ -60,33 +60,10 @@ void MobManager::pcAttackNpcs(CNSocket *sock, CNPacketData *data) {
|
||||
}
|
||||
Mob *mob = Mobs[pktdata[i]];
|
||||
|
||||
// cannot kill mobs multiple times; cannot harm retreating mobs
|
||||
if (mob->state != MobState::ROAMING && mob->state != MobState::COMBAT) {
|
||||
respdata[i].iID = mob->appearanceData.iNPC_ID;
|
||||
respdata[i].iHP = mob->appearanceData.iHP;
|
||||
respdata[i].iHitFlag = 2; // probably still necessary
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mob->state == MobState::ROAMING) {
|
||||
assert(mob->target == nullptr);
|
||||
mob->target = sock;
|
||||
mob->state = MobState::COMBAT;
|
||||
mob->nextMovement = getTime();
|
||||
//std::cout << "combat state\n";
|
||||
}
|
||||
|
||||
mob->appearanceData.iHP -= 100;
|
||||
|
||||
// wake up sleeping monster
|
||||
// TODO: remove client-side bit somehow
|
||||
mob->appearanceData.iConditionBitFlag &= ~CSB_BIT_MEZ;
|
||||
|
||||
if (mob->appearanceData.iHP <= 0)
|
||||
killMob(mob->target, mob);
|
||||
int damage = hitMob(sock, mob, 100);
|
||||
|
||||
respdata[i].iID = mob->appearanceData.iNPC_ID;
|
||||
respdata[i].iDamage = 100;
|
||||
respdata[i].iDamage = damage;
|
||||
respdata[i].iHP = mob->appearanceData.iHP;
|
||||
respdata[i].iHitFlag = 2; // hitscan, not a rocket or a grenade
|
||||
}
|
||||
@ -131,7 +108,6 @@ void MobManager::npcAttackPc(Mob *mob) {
|
||||
if (plr->HP <= 0) {
|
||||
mob->target = nullptr;
|
||||
mob->state = MobState::RETREAT;
|
||||
//std::cout << "retreat state\n";
|
||||
}
|
||||
}
|
||||
|
||||
@ -184,14 +160,37 @@ void MobManager::giveReward(CNSocket *sock) {
|
||||
|
||||
}
|
||||
|
||||
int MobManager::hitMob(CNSocket *sock, Mob *mob, int damage) {
|
||||
// cannot kill mobs multiple times; cannot harm retreating mobs
|
||||
if (mob->state != MobState::ROAMING && mob->state != MobState::COMBAT) {
|
||||
return 0; // no damage
|
||||
}
|
||||
|
||||
if (mob->state == MobState::ROAMING) {
|
||||
assert(mob->target == nullptr);
|
||||
mob->target = sock;
|
||||
mob->state = MobState::COMBAT;
|
||||
mob->nextMovement = getTime();
|
||||
}
|
||||
|
||||
mob->appearanceData.iHP -= damage;
|
||||
|
||||
// wake up sleeping monster
|
||||
// TODO: remove client-side bit somehow
|
||||
mob->appearanceData.iConditionBitFlag &= ~CSB_BIT_MEZ;
|
||||
|
||||
if (mob->appearanceData.iHP <= 0)
|
||||
killMob(mob->target, mob);
|
||||
|
||||
return damage;
|
||||
}
|
||||
|
||||
void MobManager::killMob(CNSocket *sock, Mob *mob) {
|
||||
mob->state = MobState::DEAD;
|
||||
mob->target = nullptr;
|
||||
mob->appearanceData.iConditionBitFlag = 0;
|
||||
mob->killedTime = getTime(); // XXX: maybe introduce a shard-global time for each step?
|
||||
|
||||
//std::cout << "dead state mob " << mob->appearanceData.iNPC_ID << std::endl;
|
||||
|
||||
giveReward(sock);
|
||||
MissionManager::mobKilled(sock, mob->appearanceData.iNPCType);
|
||||
|
||||
@ -199,9 +198,6 @@ void MobManager::killMob(CNSocket *sock, Mob *mob) {
|
||||
}
|
||||
|
||||
void MobManager::deadStep(Mob *mob, time_t currTime) {
|
||||
auto chunk = ChunkManager::grabChunk(mob->appearanceData.iX, mob->appearanceData.iY);
|
||||
auto chunks = ChunkManager::grabChunks(chunk);
|
||||
|
||||
// despawn the mob after a short delay
|
||||
if (mob->killedTime != 0 && !mob->despawned && currTime - mob->killedTime > 2000) {
|
||||
mob->despawned = true;
|
||||
@ -210,11 +206,7 @@ void MobManager::deadStep(Mob *mob, time_t currTime) {
|
||||
|
||||
pkt.iNPC_ID = mob->appearanceData.iNPC_ID;
|
||||
|
||||
for (Chunk *chunk : chunks) {
|
||||
for (CNSocket *s : chunk->players) {
|
||||
s->sendPacket(&pkt, P_FE2CL_NPC_EXIT, sizeof(sP_FE2CL_NPC_EXIT));
|
||||
}
|
||||
}
|
||||
NPCManager::sendToViewable(mob, &pkt, P_FE2CL_NPC_EXIT, sizeof(sP_FE2CL_NPC_EXIT));
|
||||
|
||||
// if it was summoned, remove it permanently
|
||||
if (mob->summoned) {
|
||||
@ -231,18 +223,13 @@ void MobManager::deadStep(Mob *mob, time_t currTime) {
|
||||
|
||||
mob->appearanceData.iHP = mob->maxHealth;
|
||||
mob->state = MobState::ROAMING;
|
||||
//std::cout << "roaming state\n";
|
||||
|
||||
INITSTRUCT(sP_FE2CL_NPC_NEW, pkt);
|
||||
|
||||
pkt.NPCAppearanceData = mob->appearanceData;
|
||||
|
||||
// notify all nearby players
|
||||
for (Chunk *chunk : chunks) {
|
||||
for (CNSocket *s : chunk->players) {
|
||||
s->sendPacket(&pkt, P_FE2CL_NPC_NEW, sizeof(sP_FE2CL_NPC_NEW));
|
||||
}
|
||||
}
|
||||
NPCManager::sendToViewable(mob, &pkt, P_FE2CL_NPC_NEW, sizeof(sP_FE2CL_NPC_NEW));
|
||||
}
|
||||
|
||||
void MobManager::combatStep(Mob *mob, time_t currTime) {
|
||||
@ -252,14 +239,16 @@ void MobManager::combatStep(Mob *mob, time_t currTime) {
|
||||
if (PlayerManager::players.find(mob->target) == PlayerManager::players.end()) {
|
||||
mob->target = nullptr;
|
||||
mob->state = MobState::RETREAT;
|
||||
//std::cout << "RETREAT state\n";
|
||||
return;
|
||||
}
|
||||
Player *plr = PlayerManager::getPlayer(mob->target);
|
||||
|
||||
// skip attack/move if stunned or asleep
|
||||
if (mob->appearanceData.iConditionBitFlag & (CSB_BIT_STUN|CSB_BIT_MEZ))
|
||||
// did something else kill the player in the mean time?
|
||||
if (plr->HP <= 0) {
|
||||
mob->target = nullptr;
|
||||
mob->state = MobState::RETREAT;
|
||||
return;
|
||||
}
|
||||
|
||||
int distance = hypot(plr->x - mob->appearanceData.iX, plr->y - mob->appearanceData.iY);
|
||||
|
||||
@ -268,7 +257,6 @@ void MobManager::combatStep(Mob *mob, time_t currTime) {
|
||||
* No, I'm not 100% sure this is how it's supposed to work.
|
||||
*/
|
||||
if (distance <= (int)mob->data["m_iAtkRange"]) {
|
||||
//std::cout << "attack logic\n";
|
||||
// attack logic
|
||||
if (mob->nextAttack == 0) {
|
||||
mob->nextAttack = currTime + (int)mob->data["m_iInitalTime"] * 100; // I *think* this is what this is
|
||||
@ -278,8 +266,6 @@ void MobManager::combatStep(Mob *mob, time_t currTime) {
|
||||
npcAttackPc(mob);
|
||||
}
|
||||
} else {
|
||||
//std::cout << "movement logic\n";
|
||||
//std::cout << "distance: " << distance << " attack range: " << mob->data["m_iAtkRange"] << std::endl;
|
||||
// movement logic
|
||||
if (mob->nextMovement != 0 && currTime < mob->nextMovement)
|
||||
return;
|
||||
@ -301,15 +287,8 @@ void MobManager::combatStep(Mob *mob, time_t currTime) {
|
||||
pkt.iToY = mob->appearanceData.iY = targ.second;
|
||||
pkt.iToZ = mob->appearanceData.iZ;
|
||||
|
||||
auto chunk = ChunkManager::grabChunk(mob->appearanceData.iX, mob->appearanceData.iY);
|
||||
auto chunks = ChunkManager::grabChunks(chunk);
|
||||
|
||||
// notify all nearby players
|
||||
for (Chunk *chunk : chunks) {
|
||||
for (CNSocket *s : chunk->players) {
|
||||
s->sendPacket(&pkt, P_FE2CL_NPC_MOVE, sizeof(sP_FE2CL_NPC_MOVE));
|
||||
}
|
||||
}
|
||||
NPCManager::sendToViewable(mob, &pkt, P_FE2CL_NPC_MOVE, sizeof(sP_FE2CL_NPC_MOVE));
|
||||
}
|
||||
|
||||
// retreat if kited too far
|
||||
@ -317,7 +296,6 @@ void MobManager::combatStep(Mob *mob, time_t currTime) {
|
||||
if (distance >= mob->data["m_iCombatRange"]) {
|
||||
mob->target = nullptr;
|
||||
mob->state = MobState::RETREAT;
|
||||
//std::cout << "retreat state\n";
|
||||
}
|
||||
}
|
||||
|
||||
@ -338,10 +316,6 @@ void MobManager::roamingStep(Mob *mob, time_t currTime) {
|
||||
int delay = (int)mob->data["m_iDelayTime"] * 1000;
|
||||
mob->nextMovement = currTime + delay/2 + rand() % (delay/2);
|
||||
|
||||
// skip move if stunned or asleep
|
||||
if (mob->appearanceData.iConditionBitFlag & (CSB_BIT_STUN|CSB_BIT_MEZ))
|
||||
return;
|
||||
|
||||
INITSTRUCT(sP_FE2CL_NPC_MOVE, pkt);
|
||||
int xStart = mob->spawnX - mob->idleRange/2;
|
||||
int yStart = mob->spawnY - mob->idleRange/2;
|
||||
@ -362,15 +336,8 @@ void MobManager::roamingStep(Mob *mob, time_t currTime) {
|
||||
pkt.iToY = mob->appearanceData.iY = targ.second;
|
||||
pkt.iToZ = mob->appearanceData.iZ;
|
||||
|
||||
auto chunk = ChunkManager::grabChunk(mob->appearanceData.iX, mob->appearanceData.iY);
|
||||
auto chunks = ChunkManager::grabChunks(chunk);
|
||||
|
||||
// notify all nearby players
|
||||
for (Chunk *chunk : chunks) {
|
||||
for (CNSocket *s : chunk->players) {
|
||||
s->sendPacket(&pkt, P_FE2CL_NPC_MOVE, sizeof(sP_FE2CL_NPC_MOVE));
|
||||
}
|
||||
}
|
||||
NPCManager::sendToViewable(mob, &pkt, P_FE2CL_NPC_MOVE, sizeof(sP_FE2CL_NPC_MOVE));
|
||||
}
|
||||
|
||||
void MobManager::retreatStep(Mob *mob, time_t currTime) {
|
||||
@ -380,9 +347,6 @@ void MobManager::retreatStep(Mob *mob, time_t currTime) {
|
||||
if (distance > mob->data["m_iIdleRange"]) {
|
||||
INITSTRUCT(sP_FE2CL_NPC_MOVE, pkt);
|
||||
|
||||
auto chunk = ChunkManager::grabChunk(mob->appearanceData.iX, mob->appearanceData.iY);
|
||||
auto chunks = ChunkManager::grabChunks(chunk);
|
||||
|
||||
auto targ = lerp(mob->appearanceData.iX, mob->appearanceData.iY, mob->spawnX, mob->spawnY, mob->data["m_iRunSpeed"]);
|
||||
|
||||
pkt.iNPC_ID = mob->appearanceData.iNPC_ID;
|
||||
@ -392,17 +356,12 @@ void MobManager::retreatStep(Mob *mob, time_t currTime) {
|
||||
pkt.iToZ = mob->appearanceData.iZ;
|
||||
|
||||
// notify all nearby players
|
||||
for (Chunk *chunk : chunks) {
|
||||
for (CNSocket *s : chunk->players) {
|
||||
s->sendPacket(&pkt, P_FE2CL_NPC_MOVE, sizeof(sP_FE2CL_NPC_MOVE));
|
||||
}
|
||||
}
|
||||
NPCManager::sendToViewable(mob, &pkt, P_FE2CL_NPC_MOVE, sizeof(sP_FE2CL_NPC_MOVE));
|
||||
}
|
||||
|
||||
// if we got there
|
||||
if (distance <= mob->data["m_iIdleRange"]) {
|
||||
mob->state = MobState::ROAMING;
|
||||
//std::cout << "roaming state\n";
|
||||
mob->appearanceData.iHP = mob->maxHealth;
|
||||
mob->killedTime = 0;
|
||||
mob->nextAttack = 0;
|
||||
@ -423,6 +382,11 @@ void MobManager::step(CNServer *serv, time_t currTime) {
|
||||
if (!settings::SIMULATEMOBS && pair.second->state != MobState::DEAD)
|
||||
continue;
|
||||
|
||||
// skip attack/move if stunned or asleep
|
||||
if (pair.second->appearanceData.iConditionBitFlag & (CSB_BIT_STUN|CSB_BIT_MEZ)
|
||||
&& (pair.second->state == MobState::ROAMING || pair.second->state == MobState::COMBAT))
|
||||
continue;
|
||||
|
||||
switch (pair.second->state) {
|
||||
case MobState::INACTIVE:
|
||||
// no-op
|
||||
|
@ -94,6 +94,7 @@ namespace MobManager {
|
||||
void dotDamageOnOff(CNSocket *sock, CNPacketData *data);
|
||||
|
||||
void npcAttackPc(Mob *mob);
|
||||
int hitMob(CNSocket *sock, Mob *mob, int damage);
|
||||
void killMob(CNSocket *sock, Mob *mob);
|
||||
void giveReward(CNSocket *sock);
|
||||
std::pair<int,int> lerp(int, int, int, int, int);
|
||||
|
@ -1,11 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "CNStructs.hpp"
|
||||
#include "ChunkManager.hpp"
|
||||
|
||||
class BaseNPC {
|
||||
public:
|
||||
sNPCAppearanceData appearanceData;
|
||||
NPCClass npcClass;
|
||||
std::pair<int, int> chunkPos;
|
||||
std::vector<Chunk*> currentChunks;
|
||||
|
||||
BaseNPC() {};
|
||||
BaseNPC(int x, int y, int z, int type, int id) {
|
||||
@ -18,6 +21,9 @@ public:
|
||||
appearanceData.iConditionBitFlag = 0;
|
||||
appearanceData.iBarkerType = 0;
|
||||
appearanceData.iNPC_ID = id;
|
||||
|
||||
chunkPos = ChunkManager::grabChunk(x, y);
|
||||
currentChunks = ChunkManager::grabChunks(chunkPos);
|
||||
};
|
||||
BaseNPC(int x, int y, int z, int type, int id, NPCClass classType) : BaseNPC(x, y, z, type, id) {
|
||||
npcClass = classType;
|
||||
|
@ -147,6 +147,14 @@ void NPCManager::updateNPCPosition(int32_t id, int X, int Y, int Z) {
|
||||
ChunkManager::addNPC(X, Y, npc->appearanceData.iNPC_ID);
|
||||
}
|
||||
|
||||
void NPCManager::sendToViewable(BaseNPC *npc, void *buf, uint32_t type, size_t size) {
|
||||
for (Chunk *chunk : npc->currentChunks) {
|
||||
for (CNSocket *s : chunk->players) {
|
||||
s->sendPacket(buf, type, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NPCManager::npcVendorBuy(CNSocket* sock, CNPacketData* data) {
|
||||
if (data->size != sizeof(sP_CL2FE_REQ_PC_VENDOR_ITEM_BUY))
|
||||
return; // malformed packet
|
||||
|
@ -28,6 +28,8 @@ namespace NPCManager {
|
||||
void removeNPC(int32_t);
|
||||
void updateNPCPosition(int32_t, int X, int Y, int Z);
|
||||
|
||||
void sendToViewable(BaseNPC* npc, void* buf, uint32_t type, size_t size);
|
||||
|
||||
void npcBarkHandler(CNSocket* sock, CNPacketData* data);
|
||||
void npcSummonHandler(CNSocket* sock, CNPacketData* data);
|
||||
void npcUnsummonHandler(CNSocket* sock, CNPacketData* data);
|
||||
|
@ -309,13 +309,10 @@ bool doDebuff(CNSocket *sock, int32_t *pktdata, sSkillResult_Damage_N_Debuff *re
|
||||
|
||||
Mob* mob = MobManager::Mobs[pktdata[i]];
|
||||
|
||||
mob->appearanceData.iHP -= amount;
|
||||
|
||||
if (mob->appearanceData.iHP <= 0)
|
||||
MobManager::killMob(sock, mob);
|
||||
int damage = MobManager::hitMob(sock, mob, amount);
|
||||
|
||||
respdata[i].eCT = 4;
|
||||
respdata[i].iDamage = amount;
|
||||
respdata[i].iDamage = damage;
|
||||
respdata[i].iID = mob->appearanceData.iNPC_ID;
|
||||
respdata[i].iHP = mob->appearanceData.iHP;
|
||||
respdata[i].iConditionBitFlag = mob->appearanceData.iConditionBitFlag |= iCBFlag;
|
||||
@ -380,16 +377,10 @@ bool doDamage(CNSocket *sock, int32_t *pktdata, sSkillResult_Damage *respdata, i
|
||||
}
|
||||
Mob* mob = MobManager::Mobs[pktdata[i]];
|
||||
|
||||
mob->appearanceData.iHP -= amount;
|
||||
|
||||
// wake up sleeping monster
|
||||
mob->appearanceData.iConditionBitFlag &= ~CSB_BIT_MEZ;
|
||||
|
||||
if (mob->appearanceData.iHP <= 0)
|
||||
MobManager::killMob(sock, mob);
|
||||
int damage = MobManager::hitMob(sock, mob, amount);
|
||||
|
||||
respdata[i].eCT = 4;
|
||||
respdata[i].iDamage = amount;
|
||||
respdata[i].iDamage = damage;
|
||||
respdata[i].iID = mob->appearanceData.iNPC_ID;
|
||||
respdata[i].iHP = mob->appearanceData.iHP;
|
||||
|
||||
@ -433,16 +424,10 @@ bool doLeech(CNSocket *sock, int32_t *pktdata, sSkillResult_Heal_HP *healdata, i
|
||||
}
|
||||
Mob* mob = MobManager::Mobs[pktdata[i]];
|
||||
|
||||
mob->appearanceData.iHP -= amount;
|
||||
|
||||
// wake up sleeping monster
|
||||
mob->appearanceData.iConditionBitFlag &= ~CSB_BIT_MEZ;
|
||||
|
||||
if (mob->appearanceData.iHP <= 0)
|
||||
MobManager::killMob(sock, mob);
|
||||
int damage = MobManager::hitMob(sock, mob, amount);
|
||||
|
||||
damagedata->eCT = 4;
|
||||
damagedata->iDamage = amount;
|
||||
damagedata->iDamage = damage;
|
||||
damagedata->iID = mob->appearanceData.iNPC_ID;
|
||||
damagedata->iHP = mob->appearanceData.iHP;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user