Refactor PlayerBot to use a separate controller class, alongside classes that manage bots

This commit is contained in:
smallmodel 2024-10-08 22:16:57 +02:00
parent 7dbf1d7bf5
commit 0b07858bfc
No known key found for this signature in database
GPG key ID: 9F2D623CEDF08512
8 changed files with 607 additions and 249 deletions

View file

@ -159,10 +159,16 @@ Begin spawning a new bot entity
*/
void G_BotBegin(gentity_t *ent)
{
PlayerBot *player;
BotController *controller;
level.spawn_entnum = ent->s.number;
new PlayerBot;
player = new PlayerBot;
G_ClientBegin(ent, NULL);
controller = botManager.getControllerManager().createController(player);
player->setController(controller);
}
/*
@ -174,6 +180,7 @@ Called each server frame to make bots think
*/
void G_BotThink(gentity_t *ent, int msec)
{
/*
usercmd_t ucmd;
usereyes_t eyeinfo;
PlayerBot *bot;
@ -189,6 +196,7 @@ void G_BotThink(gentity_t *ent, int msec)
bot->GetEyeInfo(&eyeinfo);
G_ClientThink(ent, &ucmd, &eyeinfo);
*/
}
/*
@ -468,6 +476,12 @@ Remove the specified bot
*/
void G_RemoveBot(gentity_t *ent)
{
if (ent->entity) {
BotController *controller = botManager.getControllerManager().findController(ent->entity);
botManager.getControllerManager().removeController(controller);
}
G_ClientDisconnect(ent);
current_bot_count--;
}
@ -658,10 +672,36 @@ void G_ResetBots()
{
G_SaveBots();
botManager.Cleanup();
current_bot_count = 0;
botId = 0;
}
/*
===========
G_BotInit
Called to initialize bots
============
*/
void G_BotInit()
{
botManager.Init();
}
/*
===========
G_BotFrame
Called each frame to manage bots
============
*/
void G_BotFrame()
{
botManager.Frame();
}
/*
===========
G_SpawnBots

View file

@ -42,4 +42,6 @@ void G_RemoveBots(unsigned int num);
bool G_IsBot(gentity_t *ent);
bool G_IsPlayer(gentity_t *ent);
void G_ResetBots();
void G_BotInit();
void G_BotFrame();
void G_SpawnBots();

View file

@ -271,7 +271,7 @@ void G_InitGame(int levelTime, int randomSeed)
Director.Reset();
Actor::Init();
PlayerBot::Init();
G_BotInit();
sv_numtraces = 0;
sv_numpmtraces = 0;
@ -556,11 +556,7 @@ void G_RunFrame(int levelTime, int frameTime)
start = clock();
}
for (edict = active_edicts.next; edict != &active_edicts; edict = edict->next) {
if (edict->entity->IsSubclassOfBot()) {
G_BotThink(edict, frameTime);
}
}
G_BotFrame();
for (edict = active_edicts.next; edict != &active_edicts; edict = edict->next) {
for (num = edict->s.parent; num != ENTITYNUM_NONE; num = g_entities[num].s.parent) {

View file

@ -1687,15 +1687,13 @@ void G_BroadcastAIEvent(Entity *originator, Vector origin, int iType, float radi
continue;
}
if (!ent->IsSubclassOfActor() && !ent->IsSubclassOfBot()) {
if (!ent->IsSubclassOfActor()) {
continue;
}
if (ent->IsSubclassOfActor()) {
act = static_cast<Actor *>(ent);
if (act->IgnoreSound(iType)) {
continue;
}
act = static_cast<Actor*>(ent);
if (act->IgnoreSound(iType)) {
continue;
}
delta = origin - ent->centroid;
@ -1717,15 +1715,10 @@ void G_BroadcastAIEvent(Entity *originator, Vector origin, int iType, float radi
continue;
}
if (ent->IsSubclassOfActor()) {
act->ReceiveAIEvent(origin, iType, originator, dist2, r2);
} else if (ent->IsSubclassOfBot()) {
PlayerBot *bot = static_cast<PlayerBot *>(ent);
bot->NoticeEvent(origin, iType, originator, dist2, r2);
}
act->ReceiveAIEvent(origin, iType, originator, dist2, r2);
}
botManager.BroadcastEvent(originator, origin, iType, radius);
#if 0
gi.DPrintf("Broadcast event %s to %d entities\n", ev->getName(), count);
#endif

View file

@ -678,9 +678,9 @@ qboolean G_RemoveBotCommand(gentity_t *ent)
qboolean G_BotCommand(gentity_t *ent)
{
const char *command;
PlayerBot *bot;
BotController *bot;
if (!G_GetFirstBot() || !G_GetFirstBot()->entity) {
if (botManager.getControllerManager().getControllers().NumObjects() < 1) {
gi.Printf("No bot spawned.\n");
return qfalse;
}
@ -690,7 +690,7 @@ qboolean G_BotCommand(gentity_t *ent)
return qfalse;
}
bot = (PlayerBot *)G_GetFirstBot()->entity;
bot = botManager.getControllerManager().getControllers().ObjectAt(1);
command = gi.Argv(1);
@ -713,7 +713,7 @@ qboolean G_BotCommand(gentity_t *ent)
bot->AvoidPath(ent->entity->origin, rad);
} else if (!Q_stricmp(command, "telehere")) {
bot->setOrigin(ent->s.origin);
bot->getControlledEntity()->setOrigin(ent->s.origin);
}
return qtrue;

File diff suppressed because it is too large Load diff

View file

@ -34,19 +34,42 @@ typedef struct nodeAttract_s {
AttractiveNodePtr m_pNode;
} nodeAttract_t;
class BotController;
class PlayerBot : public Player
{
public:
CLASS_PROTOTYPE(PlayerBot);
public:
PlayerBot();
void setController(BotController *controlledBy);
void Spawned(void);
void Killed(Event *ev);
void GotKill(Event *ev);
private:
BotController *controller;
};
class BotController : public Listener
{
public:
struct botfunc_t {
bool (PlayerBot::*CheckCondition)(void);
void (PlayerBot::*BeginState)(void);
void (PlayerBot::*EndState)(void);
void (PlayerBot::*ThinkState)(void);
bool (BotController::*CheckCondition)(void);
void (BotController::*BeginState)(void);
void (BotController::*EndState)(void);
void (BotController::*ThinkState)(void);
};
private:
static botfunc_t botfuncs[];
// Paths
Vector m_vCurrentOrigin;
ActorPath m_Path;
Vector m_vTargetPos;
Vector m_vCurrentGoal;
@ -138,9 +161,9 @@ private:
void CheckStates(void);
public:
CLASS_PROTOTYPE(PlayerBot);
CLASS_PROTOTYPE(BotController);
PlayerBot();
BotController();
static void Init(void);
@ -175,15 +198,61 @@ public:
void SendCommand(const char *text);
void setAngles(Vector angles) override;
void updateOrigin(void) override;
void Think();
void Spawned(void) override;
void Spawned(void);
void Killed(Event *ev) override;
void Killed(Event *ev);
void GotKill(Event *ev);
void EventStuffText(Event *ev);
private:
void NewMove();
public:
void setControlledEntity(Player *player);
Player *getControlledEntity() const;
private:
SafePtr<Player> controlledEnt;
};
class BotControllerManager : public Listener
{
public:
CLASS_PROTOTYPE(BotControllerManager);
public:
~BotControllerManager();
BotController *createController(Player *player);
void removeController(BotController *controller);
BotController *findController(Entity *ent);
const Container<BotController *>& getControllers() const;
void Init();
void Cleanup();
void ThinkControllers();
private:
Container<BotController *> controllers;
};
class BotManager : public Listener
{
public:
CLASS_PROTOTYPE(BotManager);
public:
BotControllerManager& getControllerManager();
void Init();
void Cleanup();
void Frame();
void BroadcastEvent(Entity *originator, Vector origin, int iType, float radius);
private:
BotControllerManager botControllerManager;
};
extern BotManager botManager;

View file

@ -0,0 +1,114 @@
#include "playerbot.h"
/*
===========================================================================
Copyright (C) 2024 the OpenMoHAA team
This file is part of OpenMoHAA source code.
OpenMoHAA source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
OpenMoHAA source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OpenMoHAA source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// playerbot_master.cpp: Multiplayer bot system.
#include "g_local.h"
#include "actor.h"
#include "playerbot.h"
#include "consoleevent.h"
#include "debuglines.h"
#include "scriptexception.h"
#include "vehicleturret.h"
#include "weaputils.h"
BotManager botManager;
CLASS_DECLARATION(Listener, BotControllerManager, NULL) {
{NULL, NULL}
};
CLASS_DECLARATION(Listener, BotManager, NULL) {
{NULL, NULL}
};
void BotManager::Init()
{
botControllerManager.Init();
}
void BotManager::Cleanup()
{
botControllerManager.Cleanup();
}
void BotManager::Frame()
{
botControllerManager.ThinkControllers();
}
void BotManager::BroadcastEvent(Entity *originator, Vector origin, int iType, float radius)
{
Sentient *ent;
Actor *act;
Vector delta;
str name;
float r2;
float dist2;
int i;
int iNumSentients;
int iAreaNum;
BotController *controller;
if (radius <= 0.0f) {
radius = G_AIEventRadius(iType);
}
assert(originator);
r2 = Square(radius);
const Container<BotController *>& controllers = getControllerManager().getControllers();
for (i = 1; i <= controllers.NumObjects(); i++) {
controller = controllers.ObjectAt(i);
ent = controller->getControlledEntity();
if (!ent || ent == originator || ent->deadflag) {
continue;
}
delta = origin - ent->centroid;
// dot product returns length squared
dist2 = Square(delta);
if (originator) {
iAreaNum = originator->edict->r.areanum;
} else {
iAreaNum = gi.AreaForPoint(origin);
}
if (dist2 > r2) {
continue;
}
if (iAreaNum != ent->edict->r.areanum && !gi.AreasConnected(iAreaNum, ent->edict->r.areanum)) {
continue;
}
controller->NoticeEvent(origin, iType, originator, dist2, r2);
}
}
BotControllerManager& BotManager::getControllerManager()
{
return botControllerManager;
}