From 65462d01e3640ff47bdc5b7424b4f3ade0791f3b Mon Sep 17 00:00:00 2001 From: dongresource Date: Wed, 31 Mar 2021 21:05:49 +0200 Subject: [PATCH] Generalize NPC AI stepping logic The MobAI::Mobs map still needs to be removed. --- src/Entities.hpp | 14 ++++------ src/MobAI.cpp | 66 +++++++++++++++++++--------------------------- src/MobAI.hpp | 8 ++++-- src/NPCManager.cpp | 26 +++++++++++++++++- src/NPCManager.hpp | 3 ++- src/TableData.hpp | 1 - src/main.cpp | 2 -- 7 files changed, 65 insertions(+), 55 deletions(-) diff --git a/src/Entities.hpp b/src/Entities.hpp index 52cdf09..6b88ae6 100644 --- a/src/Entities.hpp +++ b/src/Entities.hpp @@ -30,12 +30,8 @@ struct Entity { virtual bool isAlive() { return true; } // stubs - virtual void enterIntoViewOf(CNSocket *sock) {} - virtual void disappearFromViewOf(CNSocket *sock) {} - - // we don't want objects of this base class to exist -protected: - Entity() {} + virtual void enterIntoViewOf(CNSocket *sock) = 0; + virtual void disappearFromViewOf(CNSocket *sock) = 0; }; struct EntityRef { @@ -106,16 +102,16 @@ struct CombatNPC : public BaseNPC { int spawnZ = 0; int level = 0; - void (*_stepAI)() = nullptr; + void (*_stepAI)(CombatNPC*, time_t) = nullptr; // XXX 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), maxHealth(maxHP) {} - virtual void stepAI() { + virtual void stepAI(time_t currTime) { if (_stepAI != nullptr) - _stepAI(); + _stepAI(this, currTime); } virtual bool isAlive() override { return appearanceData.iHP > 0; } diff --git a/src/MobAI.cpp b/src/MobAI.cpp index a3ad09c..afd61e1 100644 --- a/src/MobAI.cpp +++ b/src/MobAI.cpp @@ -12,9 +12,8 @@ using namespace MobAI; std::map MobAI::Mobs; -static std::queue RemovalQueue; -bool MobAI::simulateMobs; +bool MobAI::simulateMobs = settings::SIMULATEMOBS; 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 (mob->summoned) { std::cout << "[INFO] Queueing killed summoned mob for removal" << std::endl; - RemovalQueue.push(mob->appearanceData.iNPC_ID); + NPCManager::queueNPCRemoval(mob->appearanceData.iNPC_ID); return; } @@ -774,44 +773,33 @@ static void retreatStep(Mob *mob, time_t currTime) { } } -static void step(CNServer *serv, time_t currTime) { - for (auto& pair : Mobs) { - if (pair.second->playersInView < 0) - std::cout << "[WARN] Weird playerview value " << pair.second->playersInView << std::endl; +void MobAI::step(CombatNPC *npc, time_t currTime) { + assert(npc->type == EntityType::MOB); + auto mob = (Mob*)npc; - // skip mob movement and combat if disabled or not in view - if ((!simulateMobs || pair.second->playersInView == 0) && pair.second->state != MobState::DEAD - && pair.second->state != MobState::RETREAT) - continue; + if (mob->playersInView < 0) + std::cout << "[WARN] Weird playerview value " << mob->playersInView << std::endl; - switch (pair.second->state) { - case MobState::INACTIVE: - // no-op - break; - 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; - } - } + // skip mob movement and combat if disabled or not in view + if ((!simulateMobs || mob->playersInView == 0) && mob->state != MobState::DEAD + && mob->state != MobState::RETREAT) + return; - // deallocate all NPCs queued for removal - while (RemovalQueue.size() > 0) { - NPCManager::destroyNPC(RemovalQueue.front()); - RemovalQueue.pop(); + switch (mob->state) { + case MobState::INACTIVE: + // no-op + 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; -} diff --git a/src/MobAI.hpp b/src/MobAI.hpp index 066d010..abb4cec 100644 --- a/src/MobAI.hpp +++ b/src/MobAI.hpp @@ -11,6 +11,11 @@ enum class MobState { DEAD }; +namespace MobAI { + // needs to be declared before Mob's constructor + void step(CombatNPC*, time_t); +}; + struct Mob : public CombatNPC { // general MobState state = MobState::INACTIVE; @@ -76,6 +81,7 @@ struct Mob : public CombatNPC { appearanceData.iHP = maxHealth; type = EntityType::MOB; + _stepAI = MobAI::step; } // constructor for /summon @@ -98,8 +104,6 @@ namespace MobAI { extern bool simulateMobs; extern std::map Mobs; - void init(); - // TODO: make this internal later void incNextMovement(Mob *mob, time_t currTime=0); bool aggroCheck(Mob *mob, time_t currTime); diff --git a/src/NPCManager.cpp b/src/NPCManager.cpp index 9f244ea..0c49f4e 100644 --- a/src/NPCManager.cpp +++ b/src/NPCManager.cpp @@ -23,11 +23,13 @@ using namespace NPCManager; -std::map NPCManager::NPCs; +std::unordered_map NPCManager::NPCs; std::map NPCManager::Warps; std::vector NPCManager::RespawnPoints; nlohmann::json NPCManager::NPCData; +static std::queue RemovalQueue; + /* * Initialized at the end of TableData::init(). * This allows us to summon and kill mobs in arbitrary order without @@ -349,10 +351,32 @@ std::vector NPCManager::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() { 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_NPC_SUMMON, npcSummonHandler); REGISTER_SHARD_PACKET(P_CL2FE_REQ_NPC_UNSUMMON, npcUnsummonHandler); REGISTER_SHARD_PACKET(P_CL2FE_REQ_BARKER, npcBarkHandler); + + REGISTER_SHARD_TIMER(step, 200); } diff --git a/src/NPCManager.hpp b/src/NPCManager.hpp index 5c290ab..9c245a1 100644 --- a/src/NPCManager.hpp +++ b/src/NPCManager.hpp @@ -34,7 +34,7 @@ struct WarpLocation { }; namespace NPCManager { - extern std::map NPCs; + extern std::unordered_map NPCs; extern std::map Warps; extern std::vector RespawnPoints; extern std::vector NPCEvents; @@ -42,6 +42,7 @@ namespace NPCManager { extern int32_t nextId; void init(); + void queueNPCRemoval(int32_t); void destroyNPC(int32_t); void updateNPCPosition(int32_t, int X, int Y, int Z, uint64_t I, int angle); diff --git a/src/TableData.hpp b/src/TableData.hpp index 61bb858..35c1340 100644 --- a/src/TableData.hpp +++ b/src/TableData.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include "JSON.hpp" #include "NPCManager.hpp" namespace TableData { diff --git a/src/main.cpp b/src/main.cpp index 4ab766f..daf2858 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,7 +6,6 @@ #include "Buddies.hpp" #include "CustomCommands.hpp" #include "Combat.hpp" -#include "MobAI.hpp" #include "Items.hpp" #include "Missions.hpp" #include "Nanos.hpp" @@ -107,7 +106,6 @@ int main() { CustomCommands::init(); Combat::init(); Chat::init(); - MobAI::init(); Items::init(); Eggs::init(); Missions::init();