Generalize NPC AI stepping logic

The MobAI::Mobs map still needs to be removed.
This commit is contained in:
dongresource 2021-03-31 21:05:49 +02:00
parent 3325397d17
commit 65462d01e3
7 changed files with 65 additions and 55 deletions

View File

@ -30,12 +30,8 @@ struct Entity {
virtual bool isAlive() { return true; } virtual bool isAlive() { return true; }
// stubs // stubs
virtual void enterIntoViewOf(CNSocket *sock) {} virtual void enterIntoViewOf(CNSocket *sock) = 0;
virtual void disappearFromViewOf(CNSocket *sock) {} virtual void disappearFromViewOf(CNSocket *sock) = 0;
// we don't want objects of this base class to exist
protected:
Entity() {}
}; };
struct EntityRef { struct EntityRef {
@ -106,16 +102,16 @@ struct CombatNPC : public BaseNPC {
int spawnZ = 0; int spawnZ = 0;
int level = 0; int level = 0;
void (*_stepAI)() = nullptr; void (*_stepAI)(CombatNPC*, time_t) = nullptr;
// XXX // XXX
CombatNPC(int x, int y, int z, int angle, uint64_t iID, int t, int id, int maxHP) : CombatNPC(int x, int y, int z, int angle, uint64_t iID, int t, int id, int maxHP) :
BaseNPC(x, y, z, angle, iID, t, id), BaseNPC(x, y, z, angle, iID, t, id),
maxHealth(maxHP) {} maxHealth(maxHP) {}
virtual void stepAI() { virtual void stepAI(time_t currTime) {
if (_stepAI != nullptr) if (_stepAI != nullptr)
_stepAI(); _stepAI(this, currTime);
} }
virtual bool isAlive() override { return appearanceData.iHP > 0; } virtual bool isAlive() override { return appearanceData.iHP > 0; }

View File

@ -12,9 +12,8 @@
using namespace MobAI; using namespace MobAI;
std::map<int32_t, Mob*> MobAI::Mobs; std::map<int32_t, Mob*> MobAI::Mobs;
static std::queue<int32_t> RemovalQueue;
bool MobAI::simulateMobs; bool MobAI::simulateMobs = settings::SIMULATEMOBS;
static void roamingStep(Mob *mob, time_t currTime); static void roamingStep(Mob *mob, time_t currTime);
@ -451,7 +450,7 @@ static void deadStep(Mob *mob, time_t currTime) {
// if it was summoned, mark it for removal // if it was summoned, mark it for removal
if (mob->summoned) { if (mob->summoned) {
std::cout << "[INFO] Queueing killed summoned mob for removal" << std::endl; std::cout << "[INFO] Queueing killed summoned mob for removal" << std::endl;
RemovalQueue.push(mob->appearanceData.iNPC_ID); NPCManager::queueNPCRemoval(mob->appearanceData.iNPC_ID);
return; return;
} }
@ -774,44 +773,33 @@ static void retreatStep(Mob *mob, time_t currTime) {
} }
} }
static void step(CNServer *serv, time_t currTime) { void MobAI::step(CombatNPC *npc, time_t currTime) {
for (auto& pair : Mobs) { assert(npc->type == EntityType::MOB);
if (pair.second->playersInView < 0) auto mob = (Mob*)npc;
std::cout << "[WARN] Weird playerview value " << pair.second->playersInView << std::endl;
// skip mob movement and combat if disabled or not in view if (mob->playersInView < 0)
if ((!simulateMobs || pair.second->playersInView == 0) && pair.second->state != MobState::DEAD std::cout << "[WARN] Weird playerview value " << mob->playersInView << std::endl;
&& pair.second->state != MobState::RETREAT)
continue;
switch (pair.second->state) { // skip mob movement and combat if disabled or not in view
case MobState::INACTIVE: if ((!simulateMobs || mob->playersInView == 0) && mob->state != MobState::DEAD
// no-op && mob->state != MobState::RETREAT)
break; return;
case MobState::ROAMING:
roamingStep(pair.second, currTime);
break;
case MobState::COMBAT:
combatStep(pair.second, currTime);
break;
case MobState::RETREAT:
retreatStep(pair.second, currTime);
break;
case MobState::DEAD:
deadStep(pair.second, currTime);
break;
}
}
// deallocate all NPCs queued for removal switch (mob->state) {
while (RemovalQueue.size() > 0) { case MobState::INACTIVE:
NPCManager::destroyNPC(RemovalQueue.front()); // no-op
RemovalQueue.pop(); break;
case MobState::ROAMING:
roamingStep(mob, currTime);
break;
case MobState::COMBAT:
combatStep(mob, currTime);
break;
case MobState::RETREAT:
retreatStep(mob, currTime);
break;
case MobState::DEAD:
deadStep(mob, currTime);
break;
} }
} }
void MobAI::init() {
REGISTER_SHARD_TIMER(step, 200);
simulateMobs = settings::SIMULATEMOBS;
}

View File

@ -11,6 +11,11 @@ enum class MobState {
DEAD DEAD
}; };
namespace MobAI {
// needs to be declared before Mob's constructor
void step(CombatNPC*, time_t);
};
struct Mob : public CombatNPC { struct Mob : public CombatNPC {
// general // general
MobState state = MobState::INACTIVE; MobState state = MobState::INACTIVE;
@ -76,6 +81,7 @@ struct Mob : public CombatNPC {
appearanceData.iHP = maxHealth; appearanceData.iHP = maxHealth;
type = EntityType::MOB; type = EntityType::MOB;
_stepAI = MobAI::step;
} }
// constructor for /summon // constructor for /summon
@ -98,8 +104,6 @@ namespace MobAI {
extern bool simulateMobs; extern bool simulateMobs;
extern std::map<int32_t, Mob*> Mobs; extern std::map<int32_t, Mob*> Mobs;
void init();
// TODO: make this internal later // TODO: make this internal later
void incNextMovement(Mob *mob, time_t currTime=0); void incNextMovement(Mob *mob, time_t currTime=0);
bool aggroCheck(Mob *mob, time_t currTime); bool aggroCheck(Mob *mob, time_t currTime);

View File

@ -23,11 +23,13 @@
using namespace NPCManager; using namespace NPCManager;
std::map<int32_t, BaseNPC*> NPCManager::NPCs; std::unordered_map<int32_t, BaseNPC*> NPCManager::NPCs;
std::map<int32_t, WarpLocation> NPCManager::Warps; std::map<int32_t, WarpLocation> NPCManager::Warps;
std::vector<WarpLocation> NPCManager::RespawnPoints; std::vector<WarpLocation> NPCManager::RespawnPoints;
nlohmann::json NPCManager::NPCData; nlohmann::json NPCManager::NPCData;
static std::queue<int32_t> RemovalQueue;
/* /*
* Initialized at the end of TableData::init(). * Initialized at the end of TableData::init().
* This allows us to summon and kill mobs in arbitrary order without * This allows us to summon and kill mobs in arbitrary order without
@ -349,10 +351,32 @@ std::vector<NPCEvent> NPCManager::NPCEvents = {
#pragma endregion NPCEvents #pragma endregion NPCEvents
void NPCManager::queueNPCRemoval(int32_t id) {
RemovalQueue.push(id);
}
static void step(CNServer *serv, time_t currTime) {
for (auto& pair : NPCs) {
if (pair.second->type != EntityType::COMBAT_NPC && pair.second->type != EntityType::MOB)
continue;
auto npc = (CombatNPC*)pair.second;
npc->stepAI(currTime);
}
// deallocate all NPCs queued for removal
while (RemovalQueue.size() > 0) {
NPCManager::destroyNPC(RemovalQueue.front());
RemovalQueue.pop();
}
}
void NPCManager::init() { void NPCManager::init() {
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_WARP_USE_NPC, npcWarpHandler); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_WARP_USE_NPC, npcWarpHandler);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TIME_TO_GO_WARP, npcWarpTimeMachine); REGISTER_SHARD_PACKET(P_CL2FE_REQ_PC_TIME_TO_GO_WARP, npcWarpTimeMachine);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_NPC_SUMMON, npcSummonHandler); REGISTER_SHARD_PACKET(P_CL2FE_REQ_NPC_SUMMON, npcSummonHandler);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_NPC_UNSUMMON, npcUnsummonHandler); REGISTER_SHARD_PACKET(P_CL2FE_REQ_NPC_UNSUMMON, npcUnsummonHandler);
REGISTER_SHARD_PACKET(P_CL2FE_REQ_BARKER, npcBarkHandler); REGISTER_SHARD_PACKET(P_CL2FE_REQ_BARKER, npcBarkHandler);
REGISTER_SHARD_TIMER(step, 200);
} }

View File

@ -34,7 +34,7 @@ struct WarpLocation {
}; };
namespace NPCManager { namespace NPCManager {
extern std::map<int32_t, BaseNPC*> NPCs; extern std::unordered_map<int32_t, BaseNPC*> NPCs;
extern std::map<int32_t, WarpLocation> Warps; extern std::map<int32_t, WarpLocation> Warps;
extern std::vector<WarpLocation> RespawnPoints; extern std::vector<WarpLocation> RespawnPoints;
extern std::vector<NPCEvent> NPCEvents; extern std::vector<NPCEvent> NPCEvents;
@ -42,6 +42,7 @@ namespace NPCManager {
extern int32_t nextId; extern int32_t nextId;
void init(); void init();
void queueNPCRemoval(int32_t);
void destroyNPC(int32_t); void destroyNPC(int32_t);
void updateNPCPosition(int32_t, int X, int Y, int Z, uint64_t I, int angle); void updateNPCPosition(int32_t, int X, int Y, int Z, uint64_t I, int angle);

View File

@ -1,7 +1,6 @@
#pragma once #pragma once
#include <map> #include <map>
#include "JSON.hpp"
#include "NPCManager.hpp" #include "NPCManager.hpp"
namespace TableData { namespace TableData {

View File

@ -6,7 +6,6 @@
#include "Buddies.hpp" #include "Buddies.hpp"
#include "CustomCommands.hpp" #include "CustomCommands.hpp"
#include "Combat.hpp" #include "Combat.hpp"
#include "MobAI.hpp"
#include "Items.hpp" #include "Items.hpp"
#include "Missions.hpp" #include "Missions.hpp"
#include "Nanos.hpp" #include "Nanos.hpp"
@ -107,7 +106,6 @@ int main() {
CustomCommands::init(); CustomCommands::init();
Combat::init(); Combat::init();
Chat::init(); Chat::init();
MobAI::init();
Items::init(); Items::init();
Eggs::init(); Eggs::init();
Missions::init(); Missions::init();