yapb-noob-edition/inc/manager.h
jeefo dedbf8ab82
bot: add more support for zombie mod scenario (ref #563)
added multiple items to custom.cfg, so game delay timer cvar is now configurable, the infected team is configurable as well.

creature (well, just zombies) now correctly detects their "creature" status even with custom model names (assumes that bot is on an infected team = zombie)

Co-Authored-By: Max <161382234+dyspose@users.noreply.github.com>
2024-05-15 22:56:35 +03:00

311 lines
8.4 KiB
C++

//
// YaPB, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright © YaPB Project Developers <yapb@jeefo.net>.
//
// SPDX-License-Identifier: MIT
//
#pragma once
// bot creation tab
struct BotRequest {
bool manual;
int difficulty;
int team;
int skin;
int personality;
String name;
};
// manager class
class BotManager final : public Singleton <BotManager> {
public:
using ForEachBot = const Lambda <bool (Bot *)> &;
using UniqueBot = UniquePtr <Bot>;
private:
float m_timeRoundStart {}; // time round has started
float m_timeRoundEnd {}; // time round ended
float m_timeRoundMid {}; // middle point timestamp of a round
float m_difficultyBalanceTime {}; // time to balance difficulties ?
float m_autoKillCheckTime {}; // time to kill all the bots ?
float m_maintainTime {}; // time to maintain bot creation
float m_quotaMaintainTime {}; // time to maintain bot quota
float m_grenadeUpdateTime {}; // time to update active grenades
float m_entityUpdateTime {}; // time to update interesting entities
float m_plantSearchUpdateTime {}; // time to update for searching planted bomb
float m_lastChatTime {}; // global chat time timestamp
float m_timeBombPlanted {}; // time the bomb were planted
float m_lastRadioTime[kGameTeamNum] {}; // global radio time
int m_lastWinner {}; // the team who won previous round
int m_lastDifficulty {}; // last bots difficulty
int m_bombSayStatus {}; // some bot is issued whine about bomb
int m_lastRadio[kGameTeamNum] {}; // last radio message for team
bool m_leaderChoosen[kGameTeamNum] {}; // is team leader choose thees round
bool m_economicsGood[kGameTeamNum] {}; // is team able to buy anything
bool m_bombPlanted {}; // is bomb planted ?
bool m_botsCanPause {}; // bots can do a little pause ?
bool m_roundOver {}; // well, round is over>
Array <edict_t *> m_activeGrenades {}; // holds currently active grenades on the map
Array <edict_t *> m_interestingEntities {}; // holds currently interesting entities on the map
Deque <String> m_saveBotNames {}; // bots names that persist upon changelevel
Deque <BotRequest> m_addRequests {}; // bot creation tab
SmallArray <BotTask> m_filters {}; // task filters
SmallArray <UniqueBot> m_bots {}; // all available bots
edict_t *m_killerEntity {}; // killer entity for bots
protected:
BotCreateResult create (StringRef name, int difficulty, int personality, int team, int skin);
public:
BotManager ();
~BotManager () = default;
public:
Twin <int, int> countTeamPlayers ();
Bot *findBotByIndex (int index);
Bot *findBotByEntity (edict_t *ent);
Bot *findAliveBot ();
Bot *findHighestFragBot (int team);
int getHumansCount (bool ignoreSpectators = false);
int getAliveHumansCount ();
int getPlayerPriority (edict_t *ent);
float getConnectTime (StringRef name, float original);
float getAverageTeamKPD (bool calcForBots);
void setBombPlanted (bool isPlanted);
void frame ();
void createKillerEntity ();
void destroyKillerEntity ();
void touchKillerEntity (Bot *bot);
void destroy ();
void addbot (StringRef name, int difficulty, int personality, int team, int skin, bool manual);
void addbot (StringRef name, StringRef difficulty, StringRef personality, StringRef team, StringRef skin, bool manual);
void serverFill (int selection, int personality = Personality::Normal, int difficulty = -1, int numToAdd = -1);
void kickEveryone (bool instant = false, bool zeroQuota = true);
void kickBot (int index);
void kickFromTeam (Team team, bool removeAll = false);
void killAllBots (int team = -1, bool silent = false);
void maintainQuota ();
void maintainAutoKill ();
void maintainLeaders ();
void initQuota ();
void initRound ();
void decrementQuota (int by = 1);
void selectLeaders (int team, bool reset);
void listBots ();
void setWeaponMode (int selection);
void updateTeamEconomics (int team, bool setTrue = false);
void updateBotDifficulties ();
void balanceBotDifficulties ();
void reset ();
void initFilters ();
void resetFilters ();
void updateActiveGrenade ();
void updateInterestingEntities ();
void captureChatRadio (StringRef cmd, StringRef arg, edict_t *ent);
void notifyBombDefuse ();
void execGameEntity (edict_t *ent);
void forEach (ForEachBot handler);
void erase (Bot *bot);
void handleDeath (edict_t *killer, edict_t *victim);
void setLastWinner (int winner);
void checkBotModel (edict_t *ent, char *infobuffer);
void checkNeedsToBeKicked ();
void refreshCreatureStatus ();
bool isTeamStacked (int team);
bool kickRandom (bool decQuota = true, Team fromTeam = Team::Unassigned);
bool hasCustomCSDMSpawnEntities ();
bool isLineBlockedBySmoke (const Vector &from, const Vector &to, float grenadeBloat = 1.0f);
public:
const Array <edict_t *> &getActiveGrenades () {
return m_activeGrenades;
}
const Array <edict_t *> &getInterestingEntities () {
return m_interestingEntities;
}
bool hasActiveGrenades () const {
return !m_activeGrenades.empty ();
}
bool hasInterestingEntities () const {
return !m_interestingEntities.empty ();
}
bool checkTeamEco (int team) const {
return m_economicsGood[team];
}
int32_t getLastWinner () const {
return m_lastWinner;
}
int32_t getBotCount () const {
return m_bots.length <int32_t> ();
}
// get the list of filters
SmallArray <BotTask> &getFilters () {
return m_filters;
}
void createRandom (bool manual = false) {
addbot ("", -1, -1, -1, -1, manual);
}
bool isBombPlanted () const {
return m_bombPlanted;
}
float getTimeBombPlanted () const {
return m_timeBombPlanted;
}
float getRoundStartTime () const {
return m_timeRoundStart;
}
float getRoundMidTime () const {
return m_timeRoundMid;
}
float getRoundEndTime () const {
return m_timeRoundEnd;
}
bool isRoundOver () const {
return m_roundOver;
}
bool canPause () const {
return m_botsCanPause;
}
void setCanPause (const bool pause) {
m_botsCanPause = pause;
}
bool hasBombSay (int type) const {
return (m_bombSayStatus & type) == type;
}
void clearBombSay (int type) {
m_bombSayStatus &= ~type;
}
void setPlantedBombSearchTimestamp (const float timestamp) {
m_plantSearchUpdateTime = timestamp;
}
float getPlantedBombSearchTimestamp () const {
return m_plantSearchUpdateTime;
}
void setLastRadioTimestamp (const int team, const float timestamp) {
if (team == Team::CT || team == Team::Terrorist) {
m_lastRadioTime[team] = timestamp;
}
}
float getLastRadioTimestamp (const int team) const {
if (team == Team::CT || team == Team::Terrorist) {
return m_lastRadioTime[team];
}
return 0.0f;
}
void setLastRadio (const int team, const int radio) {
m_lastRadio[team] = radio;
}
int getLastRadio (const int team) const {
return m_lastRadio[team];
}
void setLastChatTimestamp (const float timestamp) {
m_lastChatTime = timestamp;
}
float getLastChatTimestamp () const {
return m_lastChatTime;
}
// some bots are online ?
bool hasBotsOnline () const {
return getBotCount () > 0;
}
public:
Bot *operator [] (int index) {
return findBotByIndex (index);
}
Bot *operator [] (edict_t *ent) {
return findBotByEntity (ent);
}
public:
UniqueBot *begin () {
return m_bots.begin ();
}
UniqueBot *begin () const {
return m_bots.begin ();
}
UniqueBot *end () {
return m_bots.end ();
}
UniqueBot *end () const {
return m_bots.end ();
}
};
// bot async worker wrapper
class BotThreadWorker final : public Singleton <BotThreadWorker> {
private:
ThreadPool m_botWorker {};
public:
explicit BotThreadWorker () = default;
~BotThreadWorker () = default;
public:
void shutdown ();
void startup (int workers);
public:
template <typename F> void enqueue (F &&fn) {
if (!available ()) {
fn (); // no threads, no fun, just run task in current thread
return;
}
m_botWorker.enqueue (cr::move (fn));
}
public:
bool available () {
return m_botWorker.threadCount () > 0;
}
};
// expose global
CR_EXPOSE_GLOBAL_SINGLETON (BotManager, bots);
// expose async worker
CR_EXPOSE_GLOBAL_SINGLETON (BotThreadWorker, worker);