ReGameDLL_CS/regamedll/game_shared/bot/bot.h

443 lines
11 KiB
C
Raw Normal View History

2015-06-30 12:46:07 +03:00
/*
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* In addition, as a special exception, the author gives permission to
* link the code of this program with the Half-Life Game Engine ("HL
* Engine") and Modified Game Libraries ("MODs") developed by Valve,
* L.L.C ("Valve"). You must obey the GNU General Public License in all
* respects for all of the code used other than the HL Engine and MODs
* from Valve. If you modify this file, you may extend this exception
* to your version of the file, but you are not obligated to do so. If
* you do not wish to do so, delete this exception statement from your
* version.
*
*/
#ifndef BOT_H
#define BOT_H
#ifdef _WIN32
#pragma once
#endif
#include "gamerules.h"
2015-06-30 12:46:07 +03:00
class BotProfile;
template <class T, class TWrap>
T *CreateBot(const BotProfile *profile)
2015-06-30 12:46:07 +03:00
{
edict_t *pentBot;
if (UTIL_ClientsInGame() >= gpGlobals->maxClients)
{
2015-09-16 23:19:21 +03:00
CONSOLE_ECHO("Unable to create bot: Server is full (%d/%d clients).\n", UTIL_ClientsInGame(), gpGlobals->maxClients);
2015-06-30 12:46:07 +03:00
return NULL;
}
char netname[64];
UTIL_ConstructBotNetName(netname, sizeof(netname), profile);
pentBot = CREATE_FAKE_CLIENT(netname);
2015-06-30 12:46:07 +03:00
if (FNullEnt(pentBot))
{
CONSOLE_ECHO("Unable to create bot: pfnCreateFakeClient() returned null.\n");
2015-06-30 12:46:07 +03:00
return NULL;
}
else
{
T *pBot = NULL;
FREE_PRIVATE(pentBot);
pBot = GetClassPtr<TWrap>((T *)VARS(pentBot));
2015-06-30 12:46:07 +03:00
pBot->Initialize(profile);
2015-06-30 12:46:07 +03:00
return pBot;
}
}
// The base bot class from which bots for specific games are derived
2015-09-16 23:19:21 +03:00
class CBot: public CBasePlayer
2015-06-30 12:46:07 +03:00
{
public:
// constructor initializes all values to zero
CBot();
2015-06-30 12:46:07 +03:00
virtual void Spawn();
2015-06-30 12:46:07 +03:00
// invoked when injured by something
virtual int TakeDamage(entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType)
{
return CBasePlayer::TakeDamage(pevInflictor, pevAttacker, flDamage, bitsDamageType);
}
// invoked when killed
virtual void Killed(entvars_t *pevAttacker, int iGib)
{
CBasePlayer::Killed(pevAttacker, iGib);
}
virtual void Think() {};
virtual BOOL IsBot() { return TRUE; }
virtual Vector GetAutoaimVector(float flDelta);
2015-06-30 12:46:07 +03:00
// invoked when in contact with a CWeaponBox
virtual void OnTouchingWeapon(CWeaponBox *box) {}
// prepare bot for action
virtual bool Initialize(const BotProfile *profile);
2015-06-30 12:46:07 +03:00
virtual void SpawnBot() = 0;
2015-06-30 12:46:07 +03:00
// lightweight maintenance, invoked frequently
virtual void Upkeep() = 0;
2015-06-30 12:46:07 +03:00
// heavyweight algorithms, invoked less often
virtual void Update() = 0;
2015-06-30 12:46:07 +03:00
virtual void Run();
virtual void Walk();
virtual void Crouch();
virtual void StandUp();
virtual void MoveForward();
virtual void MoveBackward();
virtual void StrafeLeft();
virtual void StrafeRight();
2015-06-30 12:46:07 +03:00
// returns true if jump was started
#define MUST_JUMP true
virtual bool Jump(bool mustJump = false);
2015-06-30 12:46:07 +03:00
// zero any MoveForward(), Jump(), etc
virtual void ClearMovement();
2015-06-30 12:46:07 +03:00
// Weapon interface
virtual void UseEnvironment();
virtual void PrimaryAttack();
virtual void ClearPrimaryAttack();
virtual void TogglePrimaryAttack();
virtual void SecondaryAttack();
virtual void Reload();
2015-06-30 12:46:07 +03:00
// invoked when event occurs in the game (some events have NULL entities)
2015-09-16 23:19:21 +03:00
virtual void OnEvent(GameEventType event, CBaseEntity *entity = NULL, CBaseEntity *other = NULL) {};
2015-06-30 12:46:07 +03:00
// return true if we can see the point
virtual bool IsVisible(const Vector *pos, bool testFOV = false) const = 0;
// return true if we can see any part of the player
virtual bool IsVisible(CBasePlayer *player, bool testFOV = false, unsigned char *visParts = NULL) const = 0;
enum VisiblePartType:uint8
2015-06-30 12:46:07 +03:00
{
NONE = 0x00,
CHEST = 0x01,
HEAD = 0x02,
LEFT_SIDE = 0x04, // the left side of the object from our point of view (not their left side)
RIGHT_SIDE = 0x08, // the right side of the object from our point of view (not their right side)
FEET = 0x10
};
// if enemy is visible, return the part we see
virtual bool IsEnemyPartVisible(VisiblePartType part) const = 0;
// return true if player is facing towards us
virtual bool IsPlayerFacingMe(CBasePlayer *other) const;
2015-09-16 23:19:21 +03:00
2015-06-30 12:46:07 +03:00
// returns true if other player is pointing right at us
virtual bool IsPlayerLookingAtMe(CBasePlayer *other) const;
virtual void ExecuteCommand();
virtual void SetModel(const char *modelName);
2015-06-30 12:46:07 +03:00
#ifdef HOOK_GAMEDLL
void Spawn_();
2015-06-30 12:46:07 +03:00
Vector GetAutoaimVector_(float flDelta);
bool Initialize_(const BotProfile *profile);
void Crouch_();
void StandUp_();
void MoveForward_();
void MoveBackward_();
void StrafeLeft_();
void StrafeRight_();
2015-06-30 12:46:07 +03:00
bool Jump_(bool mustJump = false);
void ClearMovement_();
void UseEnvironment_();
void PrimaryAttack_();
void ClearPrimaryAttack_();
void TogglePrimaryAttack_();
void SecondaryAttack_();
bool IsPlayerFacingMe_(CBasePlayer *other) const;
bool IsPlayerLookingAtMe_(CBasePlayer *other) const;
void Reload_();
void ExecuteCommand_();
void SetModel_(const char *modelName);
2015-06-30 12:46:07 +03:00
#endif
2015-06-30 12:46:07 +03:00
public:
// return bot's unique ID
unsigned int GetID() const { return m_id; }
bool IsRunning() const { return m_isRunning; }
bool IsCrouching() const { return m_isCrouching; }
2015-06-30 12:46:07 +03:00
// push the current posture context onto the top of the stack
void PushPostureContext();
2015-06-30 12:46:07 +03:00
// restore the posture context to the next context on the stack
void PopPostureContext();
bool IsJumping();
2015-06-30 12:46:07 +03:00
// return time last jump began
float GetJumpTimestamp() const { return m_jumpTimestamp; }
2015-06-30 12:46:07 +03:00
// returns ratio of ammo left to max ammo (1 = full clip, 0 = empty)
float GetActiveWeaponAmmoRatio() const;
2015-06-30 12:46:07 +03:00
// return true if active weapon has any empty clip
bool IsActiveWeaponClipEmpty() const;
2015-06-30 12:46:07 +03:00
// return true if active weapon has no ammo at all
bool IsActiveWeaponOutOfAmmo() const;
2015-06-30 12:46:07 +03:00
// is the weapon in the middle of a reload
bool IsActiveWeaponReloading() const;
2015-06-30 12:46:07 +03:00
// return true if active weapon's bullet spray has become large and inaccurate
bool IsActiveWeaponRecoilHigh() const;
2015-06-30 12:46:07 +03:00
// return the weapon the bot is currently using
CBasePlayerWeapon *GetActiveWeapon() const;
2015-06-30 12:46:07 +03:00
// return true if looking thru weapon's scope
bool IsUsingScope() const;
2015-06-30 12:46:07 +03:00
// returns TRUE if given entity is our enemy
bool IsEnemy(CBaseEntity *ent) const;
2015-06-30 12:46:07 +03:00
// return number of enemies left alive
int GetEnemiesRemaining() const;
2015-06-30 12:46:07 +03:00
// return number of friends left alive
int GetFriendsRemaining() const;
2015-06-30 12:46:07 +03:00
// return true if local player is observing this bot
bool IsLocalPlayerWatchingMe() const;
2015-06-30 12:46:07 +03:00
// output message to console
void Print(char *format,...) const;
2015-06-30 12:46:07 +03:00
// output message to console if we are being watched by the local player
void PrintIfWatched(char *format,...) const;
void BotThink();
bool IsNetClient() const { return false; }
2015-06-30 12:46:07 +03:00
int Save(CSave &save) const;
int Restore(CRestore &restor) const;
// return our personality profile
const BotProfile *GetProfile() const { return m_profile; }
enum BotRelationshipTeam: uint8
{
BOT_TEAMMATE = 0,
BOT_ENEMY
};
BotRelationshipTeam BotRelationship(CBasePlayer *pTarget) const;
2015-08-20 13:35:01 +03:00
#ifndef HOOK_GAMEDLL
2015-06-30 12:46:07 +03:00
protected:
#endif
2015-06-30 12:46:07 +03:00
// Do a "client command" - useful for invoking menu choices, etc.
void ClientCommand(const char *cmd, const char *arg1 = NULL, const char *arg2 = NULL, const char *arg3 = NULL);
// the "personality" profile of this bot
const BotProfile *m_profile;
private:
void ResetCommand();
byte ThrottledMsec() const;
2015-06-30 12:46:07 +03:00
// returns current movement speed (for walk/run)
float GetMoveSpeed();
2015-06-30 12:46:07 +03:00
// unique bot ID
unsigned int m_id;
// Think mechanism variables
float m_flNextBotThink;
float m_flNextFullBotThink;
// Command interface variables
float m_flPreviousCommandTime;
// run/walk mode
bool m_isRunning;
// true if crouching (ducking)
bool m_isCrouching;
float m_forwardSpeed;
float m_strafeSpeed;
float m_verticalSpeed;
// bitfield of movement buttons
unsigned short m_buttonFlags;
// time when we last began a jump
float m_jumpTimestamp;
// the PostureContext represents the current settings of walking and crouching
struct PostureContext
{
bool isRunning;
bool isCrouching;
};
enum { MAX_POSTURE_STACK = 8 };
PostureContext m_postureStack[MAX_POSTURE_STACK];
// index of top of stack
int m_postureStackIndex;
};
2015-06-30 12:46:07 +03:00
inline void CBot::__MAKE_VHOOK(SetModel)(const char *modelName)
2015-06-30 12:46:07 +03:00
{
SET_CLIENT_KEY_VALUE(entindex(), GET_INFO_BUFFER(edict()), "model", (char *)modelName);
2015-06-30 12:46:07 +03:00
}
inline float CBot::GetMoveSpeed()
2015-06-30 12:46:07 +03:00
{
if (m_isRunning || m_isCrouching)
return pev->maxspeed;
return 0.4f * pev->maxspeed;
}
inline void CBot::Run()
2015-06-30 12:46:07 +03:00
{
m_isRunning = true;
}
inline void CBot::Walk()
2015-06-30 12:46:07 +03:00
{
m_isRunning = false;
}
inline CBasePlayerWeapon *CBot::GetActiveWeapon() const
2015-06-30 12:46:07 +03:00
{
return static_cast<CBasePlayerWeapon *>(m_pActiveItem);
}
inline bool CBot::IsActiveWeaponReloading() const
2015-06-30 12:46:07 +03:00
{
CBasePlayerWeapon *weapon = GetActiveWeapon();
if (weapon == NULL)
2015-06-30 12:46:07 +03:00
return false;
return (weapon->m_fInReload || weapon->m_fInSpecialReload) != 0;
2015-06-30 12:46:07 +03:00
}
inline bool CBot::IsActiveWeaponRecoilHigh() const
2015-06-30 12:46:07 +03:00
{
CBasePlayerWeapon *weapon = GetActiveWeapon();
if (weapon != NULL)
2015-06-30 12:46:07 +03:00
{
const float highRecoil = 0.4f;
return (weapon->m_flAccuracy > highRecoil) != 0;
2015-06-30 12:46:07 +03:00
}
2015-06-30 12:46:07 +03:00
return false;
}
inline void CBot::PushPostureContext()
2015-06-30 12:46:07 +03:00
{
if (m_postureStackIndex == MAX_POSTURE_STACK)
{
if (pev != NULL)
{
2015-06-30 12:46:07 +03:00
PrintIfWatched("PushPostureContext() overflow error!\n");
}
2015-06-30 12:46:07 +03:00
return;
}
2015-06-30 12:46:07 +03:00
m_postureStack[m_postureStackIndex].isRunning = m_isRunning;
m_postureStack[m_postureStackIndex].isCrouching = m_isCrouching;
++m_postureStackIndex;
}
inline void CBot::PopPostureContext()
2015-06-30 12:46:07 +03:00
{
if (m_postureStackIndex == 0)
{
if (pev != NULL)
{
2015-06-30 12:46:07 +03:00
PrintIfWatched("PopPostureContext() underflow error!\n");
}
2015-06-30 12:46:07 +03:00
m_isRunning = true;
m_isCrouching = false;
return;
}
--m_postureStackIndex;
m_isRunning = m_postureStack[m_postureStackIndex].isRunning;
m_isCrouching = m_postureStack[m_postureStackIndex].isCrouching;
}
inline bool CBot::__MAKE_VHOOK(IsPlayerFacingMe)(CBasePlayer *other) const
2015-06-30 12:46:07 +03:00
{
Vector toOther = other->pev->origin - pev->origin;
UTIL_MakeVectors(other->pev->v_angle + other->pev->punchangle);
Vector otherDir = gpGlobals->v_forward;
if (otherDir.x * toOther.x + otherDir.y * toOther.y < 0.0f)
return true;
2015-06-30 12:46:07 +03:00
return false;
}
inline bool CBot::__MAKE_VHOOK(IsPlayerLookingAtMe)(CBasePlayer *other) const
2015-06-30 12:46:07 +03:00
{
Vector toOther = other->pev->origin - pev->origin;
toOther.NormalizeInPlace();
UTIL_MakeVectors(other->pev->v_angle + other->pev->punchangle);
Vector otherDir = gpGlobals->v_forward;
const float lookAtCos = 0.9f;
if (otherDir.x * toOther.x + otherDir.y * toOther.y < -lookAtCos)
{
Vector vec(other->EyePosition());
if (IsVisible(&vec))
return true;
}
2015-06-30 12:46:07 +03:00
return false;
}
inline CBot::BotRelationshipTeam CBot::BotRelationship(CBasePlayer *pTarget) const
{
#ifdef REGAMEDLL_ADD
if (CSGameRules()->IsFreeForAll())
return BOT_ENEMY;
#endif
return pTarget->m_iTeam == m_iTeam ? BOT_TEAMMATE : BOT_ENEMY;
}
extern float g_flBotCommandInterval;
extern float g_flBotFullThinkInterval;
extern const char *BotArgs[4];
extern bool UseBotArgs;
extern bool AreBotsAllowed();
2015-06-30 12:46:07 +03:00
#endif // BOT_H