// // C++ Interface: bot // // Description: // // Main bot header // // // Author: Rick // // // /* TODO list -bots that "cover" each other in team matches ? -If a bot gets ammo it should ignore that kind of ammo for the next 10 seconds. ? -bots should ignore players that are too far away ? -bots should dodge/jump when shot at -If the bot's only weapon is the fist it should make looking for ammo its priority - Done ? -bots should rocket jump to get quads and the 150 armor if normal jumping doesn't work (only if health is x so they don't suicide). -Make all the "UNDONE's" done :) -Finish waypoint navigation -Make bot personalities -More advanced enemy scoring -Bots should less bump into walls -(Better) Coop support -Check for reachable instead of visible in GetNearestWaypoint -Waypoint experience file -Bots should avoid team mates -Handle failed goals better(ie remember them) -Test multiplayer -Fix bots getting stuck in each other -Optimize hunting code -Check/Fix msg code */ #ifndef BOT_H #define BOT_H #include "../cube.h" //#define RELEASE_BUILD // Set when you want to make a release build // Set for which mod the code is //#define VANILLA_CUBE #define AC_CUBE //#define CUBE_X #include "bot_util.h" #include "bot_waypoint.h" extern bool dedserv; extern itemstat itemstats[]; #ifdef RELEASE_BUILD inline void condebug(const char *s, int a = 0, int b = 0, int c = 0) {}; inline void debugnav(const char *s, int a = 0, int b = 0, int c = 0) {}; inline void debugbeam(vec &s, vec &e) { }; #else inline void condebug(const char *s, int a = 0, int b = 0, int c = 0) { /*conoutf(s, a, b, c);*/ }; inline void debugnav(const char *s, int a = 0, int b = 0, int c = 0) { /*conoutf(s, a, b, c);*/ }; inline void debugbeam(vec &s, vec &e) { /*if (!dedserv) particle_trail(1, 500, s, e);*/ }; #endif #define JUMP_HEIGHT 4.0f // NOT accurate #define FORWARD (1<<1) #define BACKWARD (1<<2) #define LEFT (1<<3) #define RIGHT (1<<4) #define UP (1<<5) #define DOWN (1<<6) #define DIR_NONE 0 //fixmebot #define m_sp 1000 #define m_classicsp 1000 enum EBotCommands // For voting of bot commands { COMMAND_ADDBOT=0, COMMAND_KICKBOT, COMMAND_BOTSKILL }; struct bot_skill_s { float flMinReactionDelay; // Minimal reaction time float flMaxReactionDelay; // Maximal reaction time float flMinAimXSpeed; // Minimal X aim speed in degrees(base value) float flMaxAimXSpeed; // Maximal X aim speed in degrees(base value) float flMinAimYSpeed; // Minimal Y aim speed in degrees(base value) float flMaxAimYSpeed; // Maximal Y aim speed in degrees(base value) float flMinAimXOffset; // Minimal X aim offset in degrees(base value) float flMaxAimXOffset; // Maximal X aim offset in degrees(base value) float flMinAimYOffset; // Minimal Y aim offset in degrees(base value) float flMaxAimYOffset; // Maximal Y aim offset in degrees(base value) float flMinAttackDelay; // Minimal delay for when a bot can attack again float flMaxAttackDelay; // Maximal delay for when a bot can attack again float flMinEnemySearchDelay; // Minimal delay for when a bot can search for an // enemy again float flMaxEnemySearchDelay; // Maximal delay for when a bot can search for an // enemy again short sShootAtFeetWithRLPercent; // Percent that a bot shoot with a rocket // launcher at the enemy feet. int iMaxHearVolume; // Max volume that bot can hear bool bCanPredict; // Can this bot predict his enemy position? bool bCircleStrafe; // Can this bot circle strafe? bool bCanSearchItemsInCombat; }; enum EBotWeaponTypes { TYPE_MELEE, // Fist, knife etc TYPE_NORMAL, // Normal weapon, distance doesn't really matter(ie pistol) TYPE_SHOTGUN, TYPE_ROCKET, TYPE_SNIPER, // Rifle, sniper etc TYPE_GRENADE, TYPE_AUTO // Chain gun, machine gun etc }; struct weaponinfo_s { EBotWeaponTypes eWeaponType; float flMinDesiredDistance; float flMaxDesiredDistance; float flMinFireDistance; float flMaxFireDistance; short sMinDesiredAmmo; }; enum ECurrentBotState { STATE_ENEMY, // Bot has an enemy STATE_HUNT, // Bot is hunting an enemy STATE_ENT, // Bot is heading to an entity STATE_SP, // Bot is doing sp specific stuff STATE_NORMAL // Bot is doing normal navigation }; struct unreachable_ent_s { entity *ent; int time; unreachable_ent_s(entity *e, int t) : ent(e), time(t) { }; }; class CBot { public: botent *m_pMyEnt; int m_iLastBotUpdate; // Combat variabels playerent *m_pPrevEnemy; int m_iShootDelay; int m_iChangeWeaponDelay; int m_iCombatNavTime; int m_iEnemySearchDelay; int m_iSawEnemyTime; bool m_bCombatJump; float m_iCombatJumpDelay; bool m_bShootAtFeet; // Hunting variabeles int m_iHuntDelay; vec m_vHuntLocation, m_vPrevHuntLocation; playerent *m_pHuntTarget; float m_fPrevHuntDist; int m_iHuntPauseTime, m_iHuntPlayerUpdateTime, m_iHuntLastTurnLessTime; int m_iLookAroundDelay, m_iLookAroundTime, m_iLookAroundUpdateTime; float m_fLookAroundYaw; bool m_bLookLeft; // Navigation variabeles int m_iCheckEnvDelay; int m_iLastJumpPad; vec m_vPrevOrigin; float m_iStuckCheckDelay; bool m_bStuck; int m_iStuckTime; int m_iStrafeTime; int m_iStrafeCheckDelay; int m_iMoveDir; int m_iSPMoveTime; bool m_bCalculatingAStarPath; TLinkedList m_AStarNodeList; TPriorList m_AStarOpenList[2]; TLinkedList m_AStarClosedList[2]; vec m_vWaterGoal; // Place to go to when underwater // Waypoint variabeles TLinkedList m_WaypointList[MAX_MAP_GRIDS][MAX_MAP_GRIDS]; float m_iWaypointTime; waypoint_s *m_pCurrentWaypoint; waypoint_s *m_pCurrentGoalWaypoint; vec m_vGoal; waypoint_s *m_pPrevWaypoints[5]; int m_iRandomWaypointTime; float m_fPrevWaypointDistance; int m_iLookForWaypointTime; int m_iWaypointHeadLastTurnLessTime; // Last time the didn't had to turn much wile // heading to a WP int m_iWaypointHeadPauseTime; // Pause time to give the bot time to aim. bool m_bGoToDebugGoal; // Misc stuff ECurrentBotState m_eCurrentBotState; entity *m_pTargetEnt; TLinkedList m_UnreachableEnts; int m_iCheckTeleporterDelay; int m_iCheckJumppadsDelay; int m_iCheckEntsDelay; int m_iCheckTriggersDelay; int m_iAimDelay; float m_fYawToTurn, m_fPitchToTurn; bot_skill_s *m_pBotSkill; // Pointer to current bot skill short m_sSkillNr; // Skill number, 0 == best 4 == worst void AimToVec(const vec &o); void AimToIdeal(void); float ChangeAngle(float speed, float ideal, float current); bool SelectGun(int Gun); virtual void CheckItemPickup(void) = 0; void SendBotInfo(void); float GetDistance(const vec &o); float GetDistance(const vec &v1, const vec &v2); float GetDistance(entity *e); float Get2DDistance(const vec &v) { return ::Get2DDistance(m_pMyEnt->o, v); }; vec GetViewAngles(void) { return vec(m_pMyEnt->pitch, m_pMyEnt->yaw, m_pMyEnt->roll); }; void ResetMoveSpeed(void) { m_pMyEnt->move = m_pMyEnt->strafe = 0; }; void SetMoveDir(int iMoveDir, bool add); void ResetCurrentTask(); // AI Functions bool FindEnemy(void); void CheckReload(void); void ShootEnemy(void); bool CheckHunt(void); bool HuntEnemy(void); void DoCombatNav(void); void MainAI(void); bool CheckStuck(void); bool CheckJump(void); bool CheckStrafe(void); void CheckFOV(void); bool IsVisible(const vec &o, bool CheckPlayers = false) { return ::IsVisible(m_pMyEnt->o, o, (CheckPlayers) ? m_pMyEnt : NULL); }; bool IsVisible(playerent *d, bool CheckPlayers = false) { return ::IsVisible(m_pMyEnt->o, d->o, (CheckPlayers) ? m_pMyEnt : NULL); }; bool IsVisible(entity *e, bool CheckPlayers = false); bool IsVisible(vec o, int Dir, float flDist, bool CheckPlayers, float *pEndDist = NULL); bool IsVisible(int Dir, float flDist, bool CheckPlayers) { return IsVisible(m_pMyEnt->o, Dir, flDist, CheckPlayers); }; bool IsInFOV(const vec &o); bool IsInFOV(playerent *d) { return IsInFOV(d->o); }; int GetShootDelay(void); virtual bool ChoosePreferredWeapon(void); virtual entity *SearchForEnts(bool bUseWPs, float flRange=9999.0f, float flMaxHeight=JUMP_HEIGHT) = NULL; virtual bool HeadToTargetEnt(void) = 0; bool CheckItems(void); bool InUnreachableList(entity *e); virtual bool DoSPStuff(void) = 0; vec GetEnemyPos(playerent *d); bool AStar(void); float AStarCost(waypoint_s *pWP1, waypoint_s *pWP2); void CleanAStarLists(bool bPathFailed); bool IsReachable(vec to, float flMaxHeight=JUMP_HEIGHT); bool WaterNav(void); void HearSound(int n, vec *o); // Waypoint functions bool HeadToWaypoint(void); bool FindWaypoint(void); void ResetWaypointVars(void); void SetCurrentWaypoint(node_s *pWP); void SetCurrentWaypoint(waypoint_s *pWP); void SetCurrentGoalWaypoint(node_s *pNode); void SetCurrentGoalWaypoint(waypoint_s *pWP); bool CurrentWPIsValid(void); bool ReachedGoalWP(void); bool HeadToGoal(void); waypoint_s *GetWPFromNode(node_s *pNode); waypoint_s *GetNearestWaypoint(vec v_src, float flRange); waypoint_s *GetNearestWaypoint(float flRange) { return GetNearestWaypoint(m_pMyEnt->o, flRange); }; waypoint_s *GetNearestTriggerWaypoint(vec v_src, float flRange); #ifdef WP_FLOOD waypoint_s *GetNearestFloodWP(vec v_origin, float flRange); waypoint_s *GetNearestFloodWP(float flRange) { return GetNearestFloodWP(m_pMyEnt->o, flRange); }; waypoint_s *GetNearestTriggerFloodWP(vec v_origin, float flRange); #endif void SyncWaypoints(void); friend class CBotManager; friend class CWaypointClass; bool m_bSendC2SInit; virtual ~CBot(void); virtual void Spawn(void); virtual void Think(void); void GoToDebugGoal(vec o); }; class CStoredBot // Used to store bots after mapchange, so that they can be readded { public: char m_szName[32]; char m_szTeam[32]; short m_sSkillNr; CStoredBot(char *name, char *team, short skill) : m_sSkillNr(skill) { strcpy(m_szName, name); strcpy(m_szTeam, team); }; }; extern vector bots; class CBotManager { char m_szBotNames[150][16]; // Max 150 bot names with a length of 16 characters short m_sBotNameCount; char m_szBotTeams[20][5]; // Max 100 bot teams co a length of 5 characters short m_sBotTeamCount; bool m_bInit; bool m_bBotsShoot; bool m_bIdleBots; bot_skill_s m_BotSkills[5]; // 5 different bot skills, 0 = best 4 = worst int m_iFrameTime; int m_iPrevTime; short m_sBotSkill; // Bad - Worse - Medium - Good - Best short m_sMaxAStarBots; // Max bots that can use a* at the same time short m_sUsingAStarBotsCount; // Number of bots that are using a* short m_sCurrentTriggerNr; // Current waypoint trigger bots should use TLinkedList m_StoredBots; // List of bots that should be re-added after map change float m_fReAddBotDelay; void LoadBotNamesFile(void); char *GetBotName(void); void LoadBotTeamsFile(void); char *GetBotTeam(void); void CreateSkillData(void); void InitSkillData(void); void InitBotItems(void); friend class CBot; friend class CCubeBot; friend class CACBot; friend class CWaypointClass; public: botent *m_pBotToView; // Construction CBotManager(void) { m_bInit = true; m_fReAddBotDelay = -1.0f; }; // Destruction ~CBotManager(void); void Init(void); void Think(void); void RenderBots(void); void RespawnBots(void) { loopv(bots) if (bots[i] && bots[i]->pBot) bots[i]->pBot->Spawn(); }; void ClearStoredBots(void) { while(!m_StoredBots.Empty()) delete m_StoredBots.Pop(); } void ReAddBot(CStoredBot *bot) { CreateBot(bot->m_szTeam, SkillNrToSkillName(bot->m_sSkillNr), bot->m_szName); }; void EndMap(void); void BeginMap(char *szMapName); int GetBotIndex(botent *m); void LetBotsUpdateStats(void); void LetBotsHear(int n, vec *loc); void AddWaypoint(node_s *pNode); void DelWaypoint(node_s *pNode); bool BotsShoot(void) { return m_bBotsShoot; }; bool IdleBots(void) { return m_bIdleBots; }; void SetBotsShoot(bool bShoot) { m_bBotsShoot = bShoot; }; void SetIdleBots(bool bIdle) { m_bIdleBots = bIdle; }; void EnableBotView(botent *bot) { m_pBotToView = bot; }; void DisableBotView(void); void ChangeBotSkill(short Skill, botent *bot = NULL); botent *CreateBot(const char *team, const char *skill, const char *name); void ViewBot(void); void CalculateMaxAStarCount(void); void PickNextTrigger(void); void MakeBotFileName(const char *szFileName, const char *szDir1, const char *szDir2, char *szOutput); }; #if defined VANILLA_CUBE #include "cube_bot.h" #elif defined AC_CUBE #include "ac_bot.h" #endif extern CBotManager BotManager; extern const vec g_vecZero; #if defined AC_CUBE extern CACWaypointClass WaypointClass; #elif defined VANILLA_CUBE extern CCubeWaypointClass WaypointClass; #endif extern void kickallbots(); #endif