well I also has made a "engine independant" structure for my new bot, but it's not very advanced; I just stuffed most of engine specific stuff in classes. Here's my bot.cpp file:
PHP Code:
/*
* The GINA Bot - a computer opponent for Valve Software's game Half-Life
* Copyright (c) 2004, Wei Mingzhi <whistler_wmz@users.sf.net>
*
* 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.
*
* [In other words, you are welcome to use, share and improve this
* program. You are forbidden to forbid anyone else to use, share
* and improve what you give them.]
*/
//
// bot.cpp
//
// Basic bot handling.
//
#include "main.h"
CBaseBot::CBaseBot():
m_iStartAction(0),
m_flMoveSpeed(0),
m_flSideMoveSpeed(0),
m_flUpMoveSpeed(0),
m_fIsStarted(false),
m_fNeedToInitialize(true),
m_flTimeThink(0),
m_flTimePrevThink(0),
m_iPrevHealth(0),
m_pAINode(NULL)
{
bi.actionflags = 0;
bi.dir = g_vecZero;
bi.idealangles = g_vecZero;
bi.weapon = 0;
// initialize subsystems...
nav = new CBotNav(this);
chat = new CBotChat(this);
if (!nav || !chat)
g_General.TerminateOnError("Memory allocation error !");
}
CBaseBot::~CBaseBot()
{
// delete subsystems...
delete nav;
delete chat;
if (m_pAINode)
delete m_pAINode; // free memory allocated for current AI node
m_pProfile->is_used = false; // this profile is not used now
}
void CBaseBot::BotThink()
{
if (!IsValid() || !GetNetName()[0]) {
g_General.TerminateOnError("BotThink(): Bot entity is invalid!");
}
MarkAsBot(); // mark this entity as a bot
if (!m_fIsStarted) {
// if the bot hasn't selected stuff to start the game yet, go do that...
StartGame();
} else if (!IsAlive()) {
// if the bot is dead, run the dead think code (MOD specific)
DeadThink();
m_fNeedToInitialize = true;
// delete the AI node data if exist
if (m_pAINode)
delete m_pAINode;
m_pAINode = NULL;
} else {
if (m_fNeedToInitialize) {
SpawnInit(); // initialize if we need to
m_fNeedToInitialize = false;
}
if (m_flTimeThink < g_pServer->GetTime()) {
BotAI(); // run the main bot AI routine at 10Hz
m_flTimePrevThink = g_pServer->GetTime();
m_flTimeThink = g_pServer->GetTime() + 0.1;
}
}
PerformMovement(); // Pass through the engine
}
void CBaseBot::DeadThink()
{
// press fire to respawn...
// NOTE: For some MODs like CS, we needn't press fire.
// so leave this virtual...
bi.actionflags |= ACTION_RESPAWN;
}
void CBaseBot::SpawnInit()
{
ResetState();
AIEnter(new CAINode_Normal(this)); // enter the normal AI node
}
void CBaseBot::FacePosition(const Vector &vecPos)
{
// Adjust bots ideal angles to face an absolute vector
bi.idealangles = VecToAngles(vecPos - GetGunPosition());
bi.idealangles.x *= -1.0; // invert for engine
ClampAngles(bi.idealangles);
}
void CBaseBot::TranslateBotInput()
{
float flMoveSpeed = GetMaxspeed();
ReleaseAllButtons();
m_flMoveSpeed = 0;
m_flSideMoveSpeed = 0;
m_flUpMoveSpeed = 0;
// if bot is pressing the walk button...
if (bi.actionflags & ACTION_WALK) {
PushButton(IN_RUN); // push the walk button
flMoveSpeed *= 0.3; // reduce his move speed
}
// bot want to respawn?
if (bi.actionflags & ACTION_RESPAWN) {
// push attack randomly to respawn
if (RandomLong(0, 1))
PushButton(IN_ATTACK);
}
// bot want to fire?
if (bi.actionflags & ACTION_ATTACK) {
PushButton(IN_ATTACK); // push attack to fire
}
// bot want to use secondary attack?
if (bi.actionflags & ACTION_ATTACK2) {
PushButton(IN_ATTACK2); // push attack to fire
}
// bot want to reload?
if (bi.actionflags & ACTION_RELOAD) {
PushButton(IN_RELOAD); // push the reload button
}
// bot want to use something?
if (bi.actionflags & ACTION_USE) {
PushButton(IN_USE);
}
// Change bots body angle and view angle
float turn_skill = 0.3 * GetSkill() / 100, speed;
// if bot is aiming at something, aim fast, else take our time...
if (m_pEnemy)
speed = 0.7 + turn_skill; // fast aim
else
speed = 0.2 + turn_skill / 2; // slow aim
ChangeAngles(bi.idealangles, speed); // change bot angles
float forwardspeed = 0, sidespeed = 0;
// bot want to move forward?
if (bi.actionflags & ACTION_MOVEFORWARD) {
PushButton(IN_FORWARD); // push the forward button
forwardspeed += flMoveSpeed; // set the move speed
}
// bot want to move back?
if (bi.actionflags & ACTION_MOVEBACK) {
PushButton(IN_BACK); // push the moveback button
forwardspeed -= flMoveSpeed; // set the move speed
}
// bot want to move left?
if (bi.actionflags & ACTION_MOVELEFT) {
PushButton(IN_MOVELEFT);
sidespeed -= flMoveSpeed; // set the sidemove speed
}
// bot want to move right?
if (bi.actionflags & ACTION_MOVERIGHT) {
PushButton(IN_MOVERIGHT); // press the button
sidespeed += flMoveSpeed; // set the sidemove speed
}
// if move direction is specified...
if (bi.dir != g_vecZero) {
// set the view independent movement
Vector vecDirectionNormal = bi.dir.Normalize();
vecDirectionNormal.z = 0;
// movement is relative to the REAL bot angles
Vector forward;
AngleVectors(Vector(0, GetViewAngles().y, 0), &forward, NULL, NULL);
// calculate movement speed
float flCos = DotProduct(forward, vecDirectionNormal);
float flSin = sqrt(1 - flCos * flCos);
m_flMoveSpeed = flCos * forwardspeed;
m_flMoveSpeed -= flSin * sidespeed;
m_flSideMoveSpeed = flCos * sidespeed;
m_flSideMoveSpeed += flSin * forwardspeed;
} else {
m_flMoveSpeed = forwardspeed;
m_flSideMoveSpeed = sidespeed;
}
// bot want to jump ?
if (bi.actionflags & ACTION_JUMP) {
PushButton(IN_JUMP); // press the button
m_flUpMoveSpeed += flMoveSpeed; // set the upmove speed
}
// bot want to crouch ?
if (bi.actionflags & ACTION_CROUCH) {
PushButton(IN_DUCK); // press the button
m_flUpMoveSpeed -= flMoveSpeed; // set the upmove speed
}
// switch to correct weapon
if (bi.weapon != 0)
SelectWeapon(bi.weapon);
}
void CBaseBot::SelectWeapon(int iId)
{
if (iId != m_iCurrentWeaponId) {
// is the specified weapon ID valid ?
if (iId < 0 || iId > MAX_WEAPONS) {
g_General.TerminateOnError("CBaseBot::SelectWeapon(): Invalid weapon ID !\n");
}
// issue a client command to select this weapon...
g_General.FakeClientCommand(this, g_General.GetWeaponName(iId));
}
}
void CBaseBot::PerformMovement()
{
// Translate bot input
TranslateBotInput();
// pass to the engine
g_engfuncs.pfnRunPlayerMove(edict(), (float *)GetViewAngles(), m_flMoveSpeed,
m_flSideMoveSpeed, m_flUpMoveSpeed, pev->button,
pev->impulse, g_pServer->GetMsec());
}
void CBaseBot::ResetState()
{
// reset bot input parameters
bi.actionflags = 0;
bi.dir = g_vecZero;
bi.idealangles = g_vecZero;
bi.weapon = 0;
m_iPrevHealth = 0;
m_pEnemy = NULL; // null out the enemy pointer
m_vecEnemy = g_vecZero;
m_ucVisibility = 0;
Nav()->Init(); // initialize the navigation system
}
void CBaseBot::DebugMsg(int flag, const char *fmt, ...)
{
#ifndef _DEBUG
if (!g_Debug.IsDebugOn(flag))
return; // debug for this flag is off; don't proceed
#endif
va_list argptr;
char string[256];
va_start(argptr, fmt);
_vsnprintf(string, sizeof(string), fmt, argptr);
va_end(argptr);
printf("[BOT DEBUG] %s: %s\n", GetNetName(), string); // print this message to the console
g_Debug.DebugLog(flag, "(%s) %s", GetNetName(), string); // also store it to the log file
}