mirror of
https://github.com/YGGverse/hlsdk-portable.git
synced 2025-01-25 22:24:16 +00:00
Merge original botman's bot 10 source code.
This commit is contained in:
parent
7d33351f77
commit
e308b0c7d1
@ -26,7 +26,8 @@ LOCAL_C_INCLUDES := $(SDL_PATH)/include \
|
||||
$(LOCAL_PATH)/../engine \
|
||||
$(LOCAL_PATH)/../public \
|
||||
$(LOCAL_PATH)/../pm_shared \
|
||||
$(LOCAL_PATH)/../game_shared
|
||||
$(LOCAL_PATH)/../game_shared \
|
||||
$(LOCAL_PATH)/bot
|
||||
|
||||
LOCAL_SRC_FILES := agrunt.cpp airtank.cpp \
|
||||
aflock.cpp \
|
||||
@ -126,6 +127,9 @@ LOCAL_SRC_FILES := agrunt.cpp airtank.cpp \
|
||||
world.cpp \
|
||||
xen.cpp \
|
||||
zombie.cpp \
|
||||
bot/bot_combat.cpp \
|
||||
bot/bot.cpp \
|
||||
bot/botcam.cpp \
|
||||
../pm_shared/pm_debug.c \
|
||||
../pm_shared/pm_math.c \
|
||||
../pm_shared/pm_shared.c
|
||||
|
@ -128,12 +128,15 @@ set (SVDLL_SOURCES
|
||||
world.cpp
|
||||
xen.cpp
|
||||
zombie.cpp
|
||||
bot/bot_combat.cpp
|
||||
bot/bot.cpp
|
||||
bot/botcam.cpp
|
||||
../pm_shared/pm_debug.c
|
||||
../pm_shared/pm_math.c
|
||||
../pm_shared/pm_shared.c
|
||||
)
|
||||
|
||||
include_directories (. wpn_shared ../common ../engine ../pm_shared ../game_shared ../public)
|
||||
include_directories (. wpn_shared ../common ../engine ../pm_shared ../game_shared ../public bot)
|
||||
|
||||
if(USE_VOICEMGR)
|
||||
set(SVDLL_SOURCES
|
||||
|
2109
dlls/bot/bot.cpp
Normal file
2109
dlls/bot/bot.cpp
Normal file
File diff suppressed because it is too large
Load Diff
168
dlls/bot/bot.h
Normal file
168
dlls/bot/bot.h
Normal file
@ -0,0 +1,168 @@
|
||||
// botman's Half-Life bot example
|
||||
//
|
||||
// http://planethalflife.com/botman/
|
||||
//
|
||||
// bot.h
|
||||
//
|
||||
|
||||
#ifndef BOT_H
|
||||
#define BOT_H
|
||||
|
||||
#define LADDER_UP 1
|
||||
#define LADDER_DOWN 2
|
||||
|
||||
#define WANDER_LEFT 1
|
||||
#define WANDER_RIGHT 2
|
||||
|
||||
#define MODEL_HGRUNT 1
|
||||
#define MODEL_BARNEY 2
|
||||
#define MODEL_SCIENTIST 3
|
||||
|
||||
#define BOT_YAW_SPEED 20 // degrees per 10th of second turning speed
|
||||
|
||||
#define BOT_SKIN_LEN 16
|
||||
#define BOT_NAME_LEN 31
|
||||
|
||||
|
||||
typedef struct // used in checking if bot can pick up ammo
|
||||
{
|
||||
char *ammo_name;
|
||||
char *weapon_name;
|
||||
int max_carry;
|
||||
} ammo_check_t;
|
||||
|
||||
#define BOT_IDLE 0
|
||||
#define BOT_NEED_TO_KICK 1
|
||||
#define BOT_NEED_TO_RESPAWN 2
|
||||
#define BOT_IS_RESPAWNING 3
|
||||
|
||||
typedef struct // used to respawn bot at end of round (time/frag limit)
|
||||
{
|
||||
BOOL is_used; // is this slot in use?
|
||||
int state; // current state of the bot
|
||||
char skin[BOT_SKIN_LEN+1];
|
||||
char name[BOT_NAME_LEN+1];
|
||||
char skill[2];
|
||||
CBasePlayer *pBot;
|
||||
} respawn_t;
|
||||
|
||||
#define HG_SND1 "hgrunt/gr_pain1.wav"
|
||||
#define HG_SND2 "hgrunt/gr_pain2.wav"
|
||||
#define HG_SND3 "hgrunt/gr_pain3.wav"
|
||||
#define HG_SND4 "hgrunt/gr_pain4.wav"
|
||||
#define HG_SND5 "hgrunt/gr_pain5.wav"
|
||||
|
||||
#define BA_SND1 "barney/ba_bring.wav"
|
||||
#define BA_SND2 "barney/ba_pain1.wav"
|
||||
#define BA_SND3 "barney/ba_pain2.wav"
|
||||
#define BA_SND4 "barney/ba_pain3.wav"
|
||||
#define BA_SND5 "barney/ba_die1.wav"
|
||||
|
||||
#define SC_SND1 "scientist/sci_fear8.wav"
|
||||
#define SC_SND2 "scientist/sci_fear13.wav"
|
||||
#define SC_SND3 "scientist/sci_fear14.wav"
|
||||
#define SC_SND4 "scientist/sci_fear15.wav"
|
||||
#define SC_SND5 "scientist/sci_pain3.wav"
|
||||
|
||||
#define BA_TNT1 "barney/ba_another.wav"
|
||||
#define BA_TNT2 "barney/ba_endline.wav"
|
||||
#define BA_TNT3 "barney/ba_gotone.wav"
|
||||
#define BA_TNT4 "barney/ba_seethat.wav"
|
||||
#define BA_TNT5 "barney/ba_tomb.wav"
|
||||
|
||||
#define SC_TNT1 "scientist/odorfromyou.wav"
|
||||
#define SC_TNT2 "scientist/smellburn.wav"
|
||||
#define SC_TNT3 "scientist/somethingfoul.wav"
|
||||
#define SC_TNT4 "scientist/youlookbad.wav"
|
||||
#define SC_TNT5 "scientist/youneedmedic.wav"
|
||||
|
||||
#define USE_TEAMPLAY_SND "barney/teamup2.wav"
|
||||
#define USE_TEAMPLAY_LATER_SND "barney/seeya.wav"
|
||||
#define USE_TEAMPLAY_ENEMY_SND "barney/ba_raincheck.wav"
|
||||
|
||||
|
||||
void BotDebug( char *buffer ); // print out message to HUD for debugging
|
||||
|
||||
|
||||
class CBot : public CBasePlayer //Derive a bot class from CBasePlayer
|
||||
{
|
||||
public:
|
||||
Vector v_prev_origin; // previous origin (i.e. location)
|
||||
float f_shoot_time; // next time to shoot weapon at
|
||||
float f_max_speed; // last sv_maxspeed setting
|
||||
float f_speed_check_time; // check sv_maxspeed every so often
|
||||
float f_move_speed; // speed at which the bot will move
|
||||
int ladder_dir; // direction traveling on ladder (UP or DOWN)
|
||||
int wander_dir; // randomly wander left or right
|
||||
float f_pause_time; // timeout for periods when the bot pauses
|
||||
float f_find_item; // timeout for not looking for items
|
||||
char model_name[20];
|
||||
int bot_model;
|
||||
int bot_skill; // bot skill level (0=very good, 4=very bad)
|
||||
float f_pain_time; // time when pain sound can be spoken
|
||||
BOOL b_use_health_station; // set if bot should "use" health station
|
||||
float f_use_health_time; // time when b_use_health_station is set
|
||||
BOOL b_use_HEV_station; // set if bot should "use" HEV station
|
||||
float f_use_HEV_time; // time when b_use_HEV_station is set
|
||||
BOOL b_use_button; // set if bot should "use" button
|
||||
float f_use_button_time; // time when b_use_button is set
|
||||
BOOL b_lift_moving; // flag set when lift (elevator) is moving
|
||||
float f_use_ladder_time; // time when bot sees a ladder
|
||||
BOOL b_see_tripmine; // set if bot "sees" a tripmine
|
||||
BOOL b_shoot_tripmine; // set if bot should shoot a tripmine
|
||||
Vector v_tripmine_origin; // origin of tripmine
|
||||
float f_fire_gauss; // time to release secondary fire on gauss gun
|
||||
BOOL bot_was_paused; // TRUE if bot was previously "paused"
|
||||
float f_weapon_inventory_time; // time to check weapon inventory
|
||||
int respawn_index; // index in respawn structure for this bot
|
||||
float f_dont_avoid_wall_time; // time when avoiding walls is OK
|
||||
float f_bot_use_time; // time the bot was "used" by player
|
||||
float f_wall_on_left; // time since bot has had a wall on the left
|
||||
float f_wall_on_right; // time since bot has had a wall on the right
|
||||
|
||||
CBaseEntity *pBotEnemy; // pointer to bot's enemy
|
||||
CBaseEntity *pBotUser; // pointer to player using bot
|
||||
CBaseEntity *pBotPickupItem; // pointer to item we are trying to get
|
||||
CBasePlayerItem *weapon_ptr[MAX_WEAPONS]; // pointer array to weapons
|
||||
int primary_ammo[MAX_WEAPONS]; // amount of primary ammo available
|
||||
int secondary_ammo[MAX_WEAPONS]; // amount of secondary ammo available
|
||||
|
||||
char message[256]; // buffer for debug messages
|
||||
|
||||
void Spawn( void );
|
||||
void BotThink( void ); // think function for the bot
|
||||
|
||||
// Bots should return FALSE for this, they can't receive NET messages
|
||||
virtual BOOL IsNetClient( void ) { return FALSE; }
|
||||
|
||||
int BloodColor() { return BLOOD_COLOR_RED; }
|
||||
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker,
|
||||
float flDamage, int bitsDamageType );
|
||||
int ObjectCaps() { return FCAP_IMPULSE_USE; };
|
||||
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller,
|
||||
USE_TYPE useType, float value );
|
||||
|
||||
int BotInFieldOfView( Vector dest );
|
||||
BOOL BotEntityIsVisible( Vector dest );
|
||||
float BotChangeYaw( float speed );
|
||||
void BotOnLadder( float moved_distance );
|
||||
void BotUnderWater( void );
|
||||
CBaseEntity * BotFindEnemy( void );
|
||||
Vector BotBodyTarget( CBaseEntity *pBotEnemy );
|
||||
void BotWeaponInventory( void );
|
||||
BOOL BotFireWeapon( Vector enemy, int weapon_choice = 0, BOOL primary = TRUE );
|
||||
void BotShootAtEnemy( void );
|
||||
void BotFindItem( void );
|
||||
void BotUseLift( float moved_distance );
|
||||
void BotTurnAtWall( TraceResult *tr );
|
||||
BOOL BotCantMoveForward( TraceResult *tr );
|
||||
BOOL BotCanJumpUp( void );
|
||||
BOOL BotCanDuckUnder( void );
|
||||
BOOL BotShootTripmine( void );
|
||||
BOOL BotFollowUser( void ); // returns FALSE if can find "user"
|
||||
BOOL BotCheckWallOnLeft( void );
|
||||
BOOL BotCheckWallOnRight( void );
|
||||
};
|
||||
|
||||
#endif // BOT_H
|
||||
|
909
dlls/bot/bot_combat.cpp
Normal file
909
dlls/bot/bot_combat.cpp
Normal file
@ -0,0 +1,909 @@
|
||||
// botman's Half-Life bot example
|
||||
//
|
||||
// http://planethalflife.com/botman/
|
||||
//
|
||||
// bot_combat.cpp
|
||||
//
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "client.h"
|
||||
#include "cbase.h"
|
||||
#include "player.h"
|
||||
#include "items.h"
|
||||
#include "effects.h"
|
||||
#include "weapons.h"
|
||||
#include "soundent.h"
|
||||
#include "gamerules.h"
|
||||
#include "animation.h"
|
||||
|
||||
#include "bot.h"
|
||||
|
||||
|
||||
extern int f_Observer; // flag to indicate if player is in observer mode
|
||||
|
||||
// weapon firing delay based on skill (min and max delay for each weapon)
|
||||
float primary_fire_delay[WEAPON_SNARK+1][5][2] = {
|
||||
// WEAPON_NONE - NOT USED
|
||||
{{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}},
|
||||
// WEAPON_CROWBAR
|
||||
{{0.0, 0.1}, {0.2, 0.3}, {0.3, 0.5}, {0.4, 0.6}, {0.6, 1.0}},
|
||||
// WEAPON_GLOCK (9mm)
|
||||
{{0.0, 0.1}, {0.1, 0.2}, {0.2, 0.3}, {0.3, 0.4}, {0.4, 0.5}},
|
||||
// WEAPON_PYTHON (357)
|
||||
{{0.0, 0.25}, {0.2, 0.5}, {0.4, 0.8}, {1.0, 1.3}, {1.5, 2.0}},
|
||||
// WEAPON_MP5 (9mmAR)
|
||||
{{0.0, 0.1}, {0.1, 0.3}, {0.3, 0.5}, {0.4, 0.6}, {0.5, 0.8}},
|
||||
// WEAPON_CHAINGUN - NOT USED
|
||||
{{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}},
|
||||
// WEAPON_CROSSBOW
|
||||
{{0.0, 0.25}, {0.2, 0.4}, {0.5, 0.7}, {0.8, 1.0}, {1.0, 1.3}},
|
||||
// WEAPON_SHOTGUN
|
||||
{{0.0, 0.25}, {0.2, 0.5}, {0.4, 0.8}, {0.6, 1.2}, {0.8, 2.0}},
|
||||
// WEAPON_RPG
|
||||
{{1.0, 3.0}, {2.0, 4.0}, {3.0, 5.0}, {4.0, 6.0}, {5.0, 7.0}},
|
||||
// WEAPON_GAUSS
|
||||
{{0.0, 0.1}, {0.2, 0.3}, {0.3, 0.5}, {0.5, 0.8}, {1.0, 1.2}},
|
||||
// WEAPON_EGON
|
||||
{{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}},
|
||||
// WEAPON_HORNETGUN
|
||||
{{0.0, 0.1}, {0.25, 0.4}, {0.4, 0.7}, {0.6, 1.0}, {1.0, 1.5}},
|
||||
// WEAPON_HANDGRENADE
|
||||
{{1.0, 1.4}, {1.4, 2.0}, {1.8, 2.6}, {2.0, 3.0}, {2.5, 3.8}},
|
||||
// WEAPON_TRIPMINE
|
||||
{{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}},
|
||||
// WEAPON_SATCHEL
|
||||
{{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}},
|
||||
// WEAPON_SNARK
|
||||
{{0.0, 0.1}, {0.1, 0.2}, {0.2, 0.5}, {0.5, 0.7}, {0.6, 1.0}},
|
||||
};
|
||||
|
||||
float secondary_fire_delay[WEAPON_SNARK+1][5][2] = {
|
||||
// WEAPON_NONE - NOT USED
|
||||
{{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}},
|
||||
// WEAPON_CROWBAR - Not applicable
|
||||
{{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}},
|
||||
// WEAPON_GLOCK (9mm)
|
||||
{{0.0, 0.1}, {0.0, 0.1}, {0.1, 0.2}, {0.1, 0.2}, {0.2, 0.4}},
|
||||
// WEAPON_PYTHON (357)
|
||||
{{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}},
|
||||
// WEAPON_MP5 (9mmAR)
|
||||
{{0.0, 0.3}, {0.5, 0.8}, {0.7, 1.0}, {1.0, 1.6}, {1.4, 2.0}},
|
||||
// WEAPON_CHAINGUN - NOT USED
|
||||
{{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}},
|
||||
// WEAPON_CROSSBOW
|
||||
{{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}},
|
||||
// WEAPON_SHOTGUN
|
||||
{{0.0, 0.25}, {0.2, 0.5}, {0.4, 0.8}, {0.6, 1.2}, {0.8, 2.0}},
|
||||
// WEAPON_RPG - Not applicable
|
||||
{{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}},
|
||||
// WEAPON_GAUSS
|
||||
{{0.2, 0.5}, {0.3, 0.7}, {0.5, 1.0}, {0.8, 1.5}, {1.0, 2.0}},
|
||||
// WEAPON_EGON - Not applicable
|
||||
{{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}},
|
||||
// WEAPON_HORNETGUN
|
||||
{{0.0, 0.1}, {0.2, 0.3}, {0.3, 0.5}, {0.5, 0.8}, {0.7, 1.2}},
|
||||
// WEAPON_HANDGRENADE - Not applicable
|
||||
{{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}},
|
||||
// WEAPON_TRIPMINE - Not applicable
|
||||
{{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}},
|
||||
// WEAPON_SATCHEL
|
||||
{{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}},
|
||||
// WEAPON_SNARK - Not applicable
|
||||
{{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}
|
||||
};
|
||||
|
||||
ammo_check_t ammo_check[] = {
|
||||
{"ammo_glockclip", "9mm", _9MM_MAX_CARRY},
|
||||
{"ammo_9mmclip", "9mm", _9MM_MAX_CARRY},
|
||||
{"ammo_9mmAR", "9mm", _9MM_MAX_CARRY},
|
||||
{"ammo_9mmbox", "9mm", _9MM_MAX_CARRY},
|
||||
{"ammo_mp5clip", "9mm", _9MM_MAX_CARRY},
|
||||
{"ammo_chainboxclip", "9mm", _9MM_MAX_CARRY},
|
||||
{"ammo_mp5grenades", "ARgrenades", M203_GRENADE_MAX_CARRY},
|
||||
{"ammo_ARgrenades", "ARgrenades", M203_GRENADE_MAX_CARRY},
|
||||
{"ammo_buckshot", "buckshot", BUCKSHOT_MAX_CARRY},
|
||||
{"ammo_crossbow", "bolts", BOLT_MAX_CARRY},
|
||||
{"ammo_357", "357", _357_MAX_CARRY},
|
||||
{"ammo_rpgclip", "rockets", ROCKET_MAX_CARRY},
|
||||
{"ammo_egonclip", "uranium", URANIUM_MAX_CARRY},
|
||||
{"ammo_gaussclip", "uranium", URANIUM_MAX_CARRY},
|
||||
{"", 0, 0}};
|
||||
|
||||
// sounds for Bot taunting after a kill...
|
||||
char barney_taunt[][30] = { BA_TNT1, BA_TNT2, BA_TNT3, BA_TNT4, BA_TNT5 };
|
||||
char scientist_taunt[][30] = { SC_TNT1, SC_TNT2, SC_TNT3, SC_TNT4, SC_TNT5 };
|
||||
|
||||
|
||||
CBaseEntity * CBot::BotFindEnemy( void )
|
||||
{
|
||||
Vector vecEnd;
|
||||
static BOOL flag=TRUE;
|
||||
char sound[40]; // for taunting sounds
|
||||
|
||||
if (pBotEnemy != NULL) // does the bot already have an enemy?
|
||||
{
|
||||
vecEnd = pBotEnemy->EyePosition();
|
||||
|
||||
// if the enemy is dead or has switched to botcam mode...
|
||||
if (!pBotEnemy->IsAlive() || (pBotEnemy->pev->effects & EF_NODRAW))
|
||||
{
|
||||
if (!pBotEnemy->IsAlive()) // is the enemy dead?, assume bot killed it
|
||||
{
|
||||
// the enemy is dead, jump for joy about 10% of the time
|
||||
if (RANDOM_LONG(1, 100) <= 10)
|
||||
pev->button |= IN_JUMP;
|
||||
|
||||
// check if this player is not a bot (i.e. not fake client)...
|
||||
if (pBotEnemy->IsNetClient() && !IS_DEDICATED_SERVER())
|
||||
{
|
||||
// speak taunt sounds about 10% of the time
|
||||
if (RANDOM_LONG(1, 100) <= 10)
|
||||
{
|
||||
if (bot_model == MODEL_BARNEY)
|
||||
strcpy( sound, barney_taunt[RANDOM_LONG(0,4)] );
|
||||
else if (bot_model == MODEL_SCIENTIST)
|
||||
strcpy( sound, scientist_taunt[RANDOM_LONG(0,4)] );
|
||||
|
||||
EMIT_SOUND(ENT(pBotEnemy->pev), CHAN_VOICE, sound,
|
||||
RANDOM_FLOAT(0.9, 1.0), ATTN_NORM);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// don't have an enemy anymore so null out the pointer...
|
||||
pBotEnemy = NULL;
|
||||
}
|
||||
else if (FInViewCone( &vecEnd ) && FVisible( vecEnd ))
|
||||
{
|
||||
// if enemy is still visible and in field of view, keep it
|
||||
|
||||
// face the enemy
|
||||
Vector v_enemy = pBotEnemy->pev->origin - pev->origin;
|
||||
Vector bot_angles = UTIL_VecToAngles( v_enemy );
|
||||
|
||||
pev->ideal_yaw = bot_angles.y;
|
||||
|
||||
// check for wrap around of angle...
|
||||
if (pev->ideal_yaw > 180)
|
||||
pev->ideal_yaw -= 360;
|
||||
if (pev->ideal_yaw < -180)
|
||||
pev->ideal_yaw += 360;
|
||||
|
||||
return (pBotEnemy);
|
||||
}
|
||||
}
|
||||
|
||||
int i;
|
||||
float nearestdistance = 1000;
|
||||
CBaseEntity *pNewEnemy = NULL;
|
||||
|
||||
// search the world for players...
|
||||
for (i = 1; i <= gpGlobals->maxClients; i++)
|
||||
{
|
||||
CBaseEntity *pPlayer = UTIL_PlayerByIndex( i );
|
||||
|
||||
// skip invalid players and skip self (i.e. this bot)
|
||||
if ((!pPlayer) || (pPlayer == this))
|
||||
continue;
|
||||
|
||||
// skip this player if not alive (i.e. dead or dying)
|
||||
if (pPlayer->pev->deadflag != DEAD_NO)
|
||||
continue;
|
||||
|
||||
// skip players that are not bots in observer mode...
|
||||
if (pPlayer->IsNetClient() && f_Observer)
|
||||
continue;
|
||||
|
||||
// skip players that are in botcam mode...
|
||||
if (pPlayer->pev->effects & EF_NODRAW)
|
||||
continue;
|
||||
|
||||
// BigGuy - START
|
||||
// is team play enabled?
|
||||
if (g_pGameRules->IsTeamplay())
|
||||
{
|
||||
// don't target your teammates if team names match...
|
||||
if (UTIL_TeamsMatch(g_pGameRules->GetTeamID(this),
|
||||
g_pGameRules->GetTeamID(pPlayer)))
|
||||
continue;
|
||||
}
|
||||
// BigGuy - END
|
||||
|
||||
vecEnd = pPlayer->EyePosition();
|
||||
|
||||
// see if bot can see the player...
|
||||
if (FInViewCone( &vecEnd ) && FVisible( vecEnd ))
|
||||
{
|
||||
float distance = (pPlayer->pev->origin - pev->origin).Length();
|
||||
if (distance < nearestdistance)
|
||||
{
|
||||
nearestdistance = distance;
|
||||
pNewEnemy = pPlayer;
|
||||
|
||||
pBotUser = NULL; // don't follow user when enemy found
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pNewEnemy)
|
||||
{
|
||||
// face the enemy
|
||||
Vector v_enemy = pNewEnemy->pev->origin - pev->origin;
|
||||
Vector bot_angles = UTIL_VecToAngles( v_enemy );
|
||||
|
||||
pev->ideal_yaw = bot_angles.y;
|
||||
|
||||
// check for wrap around of angle...
|
||||
if (pev->ideal_yaw > 180)
|
||||
pev->ideal_yaw -= 360;
|
||||
if (pev->ideal_yaw < -180)
|
||||
pev->ideal_yaw += 360;
|
||||
}
|
||||
|
||||
return (pNewEnemy);
|
||||
}
|
||||
|
||||
|
||||
Vector CBot::BotBodyTarget( CBaseEntity *pBotEnemy )
|
||||
{
|
||||
Vector target;
|
||||
float f_distance;
|
||||
float f_scale;
|
||||
int d_x, d_y, d_z;
|
||||
|
||||
f_distance = (pBotEnemy->pev->origin - pev->origin).Length();
|
||||
|
||||
if (f_distance > 1000)
|
||||
f_scale = 1.0;
|
||||
else if (f_distance > 100)
|
||||
f_scale = f_distance / 1000.0;
|
||||
else
|
||||
f_scale = 0.1;
|
||||
|
||||
switch (bot_skill)
|
||||
{
|
||||
case 0:
|
||||
// VERY GOOD, same as from CBasePlayer::BodyTarget (in player.h)
|
||||
target = pBotEnemy->Center() + pBotEnemy->pev->view_ofs * RANDOM_FLOAT( 0.5, 1.1 );
|
||||
d_x = 0; // no offset
|
||||
d_y = 0;
|
||||
d_z = 0;
|
||||
break;
|
||||
case 1:
|
||||
// GOOD, offset a little for x, y, and z
|
||||
target = pBotEnemy->Center() + pBotEnemy->pev->view_ofs;
|
||||
d_x = RANDOM_FLOAT(-5, 5) * f_scale;
|
||||
d_y = RANDOM_FLOAT(-5, 5) * f_scale;
|
||||
d_z = RANDOM_FLOAT(-9, 9) * f_scale;
|
||||
break;
|
||||
case 2:
|
||||
// FAIR, offset somewhat for x, y, and z
|
||||
target = pBotEnemy->Center() + pBotEnemy->pev->view_ofs;
|
||||
d_x = RANDOM_FLOAT(-9, 9) * f_scale;
|
||||
d_y = RANDOM_FLOAT(-9, 9) * f_scale;
|
||||
d_z = RANDOM_FLOAT(-15, 15) * f_scale;
|
||||
break;
|
||||
case 3:
|
||||
// POOR, offset for x, y, and z
|
||||
target = pBotEnemy->Center() + pBotEnemy->pev->view_ofs;
|
||||
d_x = RANDOM_FLOAT(-16, 16) * f_scale;
|
||||
d_y = RANDOM_FLOAT(-16, 16) * f_scale;
|
||||
d_z = RANDOM_FLOAT(-20, 20) * f_scale;
|
||||
break;
|
||||
case 4:
|
||||
// BAD, offset lots for x, y, and z
|
||||
target = pBotEnemy->Center() + pBotEnemy->pev->view_ofs;
|
||||
d_x = RANDOM_FLOAT(-20, 20) * f_scale;
|
||||
d_y = RANDOM_FLOAT(-20, 20) * f_scale;
|
||||
d_z = RANDOM_FLOAT(-27, 27) * f_scale;
|
||||
break;
|
||||
}
|
||||
|
||||
target = target + Vector(d_x, d_y, d_z);
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
|
||||
void CBot::BotWeaponInventory( void )
|
||||
{
|
||||
int i;
|
||||
|
||||
// initialize the elements of the weapons arrays...
|
||||
for (i = 0; i < MAX_WEAPONS; i++)
|
||||
{
|
||||
weapon_ptr[i] = NULL;
|
||||
primary_ammo[i] = 0;
|
||||
secondary_ammo[i] = 0;
|
||||
}
|
||||
|
||||
// find out which weapons the bot is carrying...
|
||||
for (i = 0; i < MAX_ITEM_TYPES; i++)
|
||||
{
|
||||
CBasePlayerItem *pItem = NULL;
|
||||
|
||||
if (m_rgpPlayerItems[i])
|
||||
{
|
||||
pItem = m_rgpPlayerItems[i];
|
||||
while (pItem)
|
||||
{
|
||||
weapon_ptr[pItem->m_iId] = pItem; // store pointer to item
|
||||
|
||||
pItem = pItem->m_pNext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// find out how much ammo of each type the bot is carrying...
|
||||
for (i = 0; i < MAX_AMMO_SLOTS; i++)
|
||||
{
|
||||
if (!CBasePlayerItem::AmmoInfoArray[i].pszName)
|
||||
continue;
|
||||
|
||||
if (strcmp("9mm", CBasePlayerItem::AmmoInfoArray[i].pszName) == 0)
|
||||
{
|
||||
primary_ammo[WEAPON_GLOCK] = m_rgAmmo[i];
|
||||
primary_ammo[WEAPON_MP5] = m_rgAmmo[i];
|
||||
}
|
||||
else if (strcmp("357", CBasePlayerItem::AmmoInfoArray[i].pszName) == 0)
|
||||
{
|
||||
primary_ammo[WEAPON_PYTHON] = m_rgAmmo[i];
|
||||
}
|
||||
else if (strcmp("ARgrenades", CBasePlayerItem::AmmoInfoArray[i].pszName) == 0)
|
||||
secondary_ammo[WEAPON_MP5] = m_rgAmmo[i];
|
||||
else if (strcmp("bolts", CBasePlayerItem::AmmoInfoArray[i].pszName) == 0)
|
||||
{
|
||||
primary_ammo[WEAPON_CROSSBOW] = m_rgAmmo[i];
|
||||
}
|
||||
else if (stricmp("buckshot", CBasePlayerItem::AmmoInfoArray[i].pszName) == 0)
|
||||
{
|
||||
primary_ammo[WEAPON_SHOTGUN] = m_rgAmmo[i];
|
||||
}
|
||||
else if (stricmp("rockets", CBasePlayerItem::AmmoInfoArray[i].pszName) == 0)
|
||||
primary_ammo[WEAPON_RPG] = m_rgAmmo[i];
|
||||
else if (strcmp("uranium", CBasePlayerItem::AmmoInfoArray[i].pszName) == 0)
|
||||
{
|
||||
primary_ammo[WEAPON_GAUSS] = m_rgAmmo[i];
|
||||
primary_ammo[WEAPON_EGON] = m_rgAmmo[i];
|
||||
}
|
||||
else if (stricmp("Hornets", CBasePlayerItem::AmmoInfoArray[i].pszName) == 0)
|
||||
primary_ammo[WEAPON_HORNETGUN] = m_rgAmmo[i];
|
||||
else if (stricmp("Hand Grenade", CBasePlayerItem::AmmoInfoArray[i].pszName) == 0)
|
||||
primary_ammo[WEAPON_HANDGRENADE] = m_rgAmmo[i];
|
||||
else if (stricmp("Trip Mine", CBasePlayerItem::AmmoInfoArray[i].pszName) == 0)
|
||||
primary_ammo[WEAPON_TRIPMINE] = m_rgAmmo[i];
|
||||
else if (stricmp("Satchel Charge", CBasePlayerItem::AmmoInfoArray[i].pszName) == 0)
|
||||
primary_ammo[WEAPON_SATCHEL] = m_rgAmmo[i];
|
||||
else if (stricmp("Snarks", CBasePlayerItem::AmmoInfoArray[i].pszName) == 0)
|
||||
primary_ammo[WEAPON_SNARK] = m_rgAmmo[i];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// specifing a weapon_choice allows you to choose the weapon the bot will
|
||||
// use (assuming enough ammo exists for that weapon)
|
||||
// BotFireWeapon will return TRUE if weapon was fired, FALSE otherwise
|
||||
// primary is used to indicate whether you want primary or secondary fire
|
||||
// if you have specified a weapon using weapon_choice
|
||||
|
||||
BOOL CBot::BotFireWeapon( Vector v_enemy_origin, int weapon_choice, BOOL primary )
|
||||
{
|
||||
CBasePlayerItem *new_weapon;
|
||||
BOOL enemy_below;
|
||||
|
||||
// is it time to check weapons inventory yet?
|
||||
if (f_weapon_inventory_time <= gpGlobals->time)
|
||||
{
|
||||
// check weapon and ammo inventory then update check time...
|
||||
BotWeaponInventory();
|
||||
f_weapon_inventory_time = gpGlobals->time + 1.0;
|
||||
}
|
||||
|
||||
Vector v_enemy = v_enemy_origin - GetGunPosition( );
|
||||
|
||||
float distance = v_enemy.Length(); // how far away is the enemy?
|
||||
|
||||
// is enemy at least 45 units below bot? (for handgrenades and snarks)
|
||||
if (v_enemy_origin.z < (pev->origin.z - 45))
|
||||
enemy_below = TRUE;
|
||||
else
|
||||
enemy_below = FALSE;
|
||||
|
||||
// if bot is carrying the crowbar...
|
||||
if (pev->weapons & (1<<WEAPON_CROWBAR))
|
||||
{
|
||||
// if close to enemy, and skill level is 1, 2 or 3, use the crowbar
|
||||
if (((distance <= 40) && (bot_skill <= 2) && (weapon_choice == 0)) ||
|
||||
(weapon_choice == WEAPON_CROWBAR))
|
||||
{
|
||||
new_weapon = weapon_ptr[WEAPON_CROWBAR];
|
||||
|
||||
// check if the bot isn't already using this item...
|
||||
if (m_pActiveItem != new_weapon)
|
||||
SelectItem("weapon_crowbar"); // select the crowbar
|
||||
|
||||
pev->button |= IN_ATTACK; // use primary attack (whack! whack!)
|
||||
|
||||
// set next time to "shoot"
|
||||
f_shoot_time = gpGlobals->time + 0.3 +
|
||||
RANDOM_FLOAT(primary_fire_delay[WEAPON_CROWBAR][bot_skill][0],
|
||||
primary_fire_delay[WEAPON_CROWBAR][bot_skill][1]);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// if bot is carrying any hand grenades and enemy is below bot...
|
||||
if ((pev->weapons & (1<<WEAPON_HANDGRENADE)) && (enemy_below))
|
||||
{
|
||||
long use_grenade = RANDOM_LONG(1,100);
|
||||
|
||||
// use hand grenades about 30% of the time...
|
||||
if (((distance > 250) && (distance < 750) &&
|
||||
(weapon_choice == 0) && (use_grenade <= 30)) ||
|
||||
(weapon_choice == WEAPON_HANDGRENADE))
|
||||
{
|
||||
// BigGuy - START
|
||||
new_weapon = weapon_ptr[WEAPON_HANDGRENADE];
|
||||
|
||||
// check if the bot isn't already using this item...
|
||||
if (m_pActiveItem != new_weapon)
|
||||
SelectItem("weapon_handgrenade"); // select the hand grenades
|
||||
|
||||
pev->button |= IN_ATTACK; // use primary attack (boom!)
|
||||
|
||||
// set next time to "shoot"
|
||||
f_shoot_time = gpGlobals->time + 0.1 +
|
||||
RANDOM_FLOAT(primary_fire_delay[WEAPON_HANDGRENADE][bot_skill][0],
|
||||
primary_fire_delay[WEAPON_HANDGRENADE][bot_skill][1]);
|
||||
return TRUE;
|
||||
// BigGuy - END
|
||||
}
|
||||
}
|
||||
|
||||
// if bot is carrying any snarks (can't use underwater) and enemy is below bot...
|
||||
if ((pev->weapons & (1<<WEAPON_SNARK)) && (pev->waterlevel != 3) &&
|
||||
(enemy_below))
|
||||
{
|
||||
long use_snark = RANDOM_LONG(1,100);
|
||||
|
||||
// use snarks about 50% of the time...
|
||||
if (((distance > 150) && (distance < 500) &&
|
||||
(weapon_choice == 0) && (use_snark <= 50)) ||
|
||||
(weapon_choice == WEAPON_SNARK))
|
||||
{
|
||||
// BigGuy - START
|
||||
new_weapon = weapon_ptr[WEAPON_SNARK];
|
||||
|
||||
// check if the bot isn't already using this item...
|
||||
if (m_pActiveItem != new_weapon)
|
||||
SelectItem("weapon_snark"); // select the "squeak grenades"
|
||||
|
||||
pev->button |= IN_ATTACK; // use primary attack (eek! eek!)
|
||||
|
||||
// set next time to "shoot"
|
||||
f_shoot_time = gpGlobals->time + 0.1 +
|
||||
RANDOM_FLOAT(primary_fire_delay[WEAPON_SNARK][bot_skill][0],
|
||||
primary_fire_delay[WEAPON_SNARK][bot_skill][1]);
|
||||
return TRUE;
|
||||
// BigGuy - END
|
||||
}
|
||||
}
|
||||
|
||||
// if the bot is carrying the egon gun (can't use underwater)...
|
||||
if ((pev->weapons & (1<<WEAPON_EGON)) && (pev->waterlevel != 3))
|
||||
{
|
||||
if ((weapon_choice == 0) || (weapon_choice == WEAPON_EGON))
|
||||
{
|
||||
new_weapon = weapon_ptr[WEAPON_EGON];
|
||||
|
||||
// check if the bot has any ammo left for this weapon...
|
||||
if (primary_ammo[WEAPON_EGON] > 0)
|
||||
{
|
||||
// check if the bot isn't already using this item...
|
||||
if (m_pActiveItem != new_weapon)
|
||||
SelectItem("weapon_egon"); // select the egon gun
|
||||
|
||||
pev->button |= IN_ATTACK; // use primary attack (bang! bang!)
|
||||
|
||||
// set next time to shoot
|
||||
f_shoot_time = gpGlobals->time;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if the bot is carrying the gauss gun (can't use underwater)...
|
||||
if ((pev->weapons & (1<<WEAPON_GAUSS)) && (pev->waterlevel != 3))
|
||||
{
|
||||
if ((weapon_choice == 0) || (weapon_choice == WEAPON_GAUSS))
|
||||
{
|
||||
new_weapon = weapon_ptr[WEAPON_GAUSS];
|
||||
|
||||
// check if the bot has any ammo left for this weapon...
|
||||
if (primary_ammo[WEAPON_GAUSS] > 1)
|
||||
{
|
||||
// check if the bot isn't already using this item...
|
||||
if (m_pActiveItem != new_weapon)
|
||||
SelectItem("weapon_gauss"); // select the gauss gun
|
||||
|
||||
long use_secondary = RANDOM_LONG(1,100);
|
||||
|
||||
// are we charging the gauss gun?
|
||||
if (f_fire_gauss > 0)
|
||||
{
|
||||
// is it time to fire the charged gauss gun?
|
||||
if (f_fire_gauss >= gpGlobals->time)
|
||||
{
|
||||
// we DON'T set pev->button here to release the secondary
|
||||
// fire button which will fire the charged gauss gun
|
||||
|
||||
f_fire_gauss = -1; // -1 means not charging gauss gun
|
||||
|
||||
// set next time to shoot
|
||||
f_shoot_time = gpGlobals->time + 1.0 +
|
||||
RANDOM_FLOAT(secondary_fire_delay[WEAPON_GAUSS][bot_skill][0],
|
||||
secondary_fire_delay[WEAPON_GAUSS][bot_skill][1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
pev->button |= IN_ATTACK2; // charge the gauss gun
|
||||
f_shoot_time = gpGlobals->time; // keep charging
|
||||
}
|
||||
}
|
||||
else if ((use_secondary <= 20) &&
|
||||
(primary_ammo[WEAPON_GAUSS] >= 10))
|
||||
{
|
||||
// release secondary fire in 0.5 seconds...
|
||||
f_fire_gauss = gpGlobals->time + 0.5;
|
||||
|
||||
pev->button |= IN_ATTACK2; // charge the gauss gun
|
||||
f_shoot_time = gpGlobals->time; // keep charging
|
||||
}
|
||||
else
|
||||
{
|
||||
pev->button |= IN_ATTACK; // use primary attack (bang! bang!)
|
||||
|
||||
// set next time to shoot
|
||||
f_shoot_time = gpGlobals->time + 0.2 +
|
||||
RANDOM_FLOAT(primary_fire_delay[WEAPON_GAUSS][bot_skill][0],
|
||||
primary_fire_delay[WEAPON_GAUSS][bot_skill][1]);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if the bot is carrying the shotgun (can't use underwater)...
|
||||
if ((pev->weapons & (1<<WEAPON_SHOTGUN)) && (pev->waterlevel != 3))
|
||||
{
|
||||
// if close enough for good shotgun blasts...
|
||||
if (((distance > 30) && (distance < 150) && (weapon_choice == 0)) ||
|
||||
(weapon_choice == WEAPON_SHOTGUN))
|
||||
{
|
||||
new_weapon = weapon_ptr[WEAPON_SHOTGUN];
|
||||
|
||||
// check if the bot has any ammo left for this weapon...
|
||||
if (primary_ammo[WEAPON_SHOTGUN] > 0)
|
||||
{
|
||||
// check if the bot isn't already using this item...
|
||||
if (m_pActiveItem != new_weapon)
|
||||
SelectItem("weapon_shotgun"); // select the shotgun
|
||||
|
||||
long use_secondary = RANDOM_LONG(1,100);
|
||||
|
||||
// use secondary attack about 30% of the time
|
||||
if ((use_secondary <= 30) && (primary_ammo[WEAPON_SHOTGUN] >= 2))
|
||||
{
|
||||
// BigGuy - START
|
||||
pev->button |= IN_ATTACK2; // use secondary attack (bang! bang!)
|
||||
|
||||
// set next time to shoot
|
||||
f_shoot_time = gpGlobals->time + 1.5 +
|
||||
RANDOM_FLOAT(secondary_fire_delay[WEAPON_SHOTGUN][bot_skill][0],
|
||||
secondary_fire_delay[WEAPON_SHOTGUN][bot_skill][1]);
|
||||
}
|
||||
// BigGuy - END
|
||||
else
|
||||
{
|
||||
pev->button |= IN_ATTACK; // use primary attack (bang! bang!)
|
||||
|
||||
// set next time to shoot
|
||||
f_shoot_time = gpGlobals->time + 0.75 +
|
||||
RANDOM_FLOAT(primary_fire_delay[WEAPON_SHOTGUN][bot_skill][0],
|
||||
primary_fire_delay[WEAPON_SHOTGUN][bot_skill][1]);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if the bot is carrying the 357/PYTHON, (can't use underwater)...
|
||||
if ((pev->weapons & (1<<WEAPON_PYTHON)) && (pev->waterlevel != 3))
|
||||
{
|
||||
// if close enough for 357 shot...
|
||||
if (((distance > 30) && (distance < 700) && (weapon_choice == 0)) ||
|
||||
(weapon_choice == WEAPON_PYTHON))
|
||||
{
|
||||
new_weapon = weapon_ptr[WEAPON_PYTHON];
|
||||
|
||||
// check if the bot has any ammo left for this weapon...
|
||||
if (primary_ammo[WEAPON_PYTHON] > 0)
|
||||
{
|
||||
// check if the bot isn't already using this item...
|
||||
if (m_pActiveItem != new_weapon)
|
||||
SelectItem("weapon_357"); // select the 357 python
|
||||
|
||||
pev->button |= IN_ATTACK; // use primary attack (bang! bang!)
|
||||
|
||||
// set next time to shoot
|
||||
f_shoot_time = gpGlobals->time + 0.75 +
|
||||
RANDOM_FLOAT(primary_fire_delay[WEAPON_PYTHON][bot_skill][0],
|
||||
primary_fire_delay[WEAPON_PYTHON][bot_skill][1]);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if the bot is carrying the hornet gun...
|
||||
if (pev->weapons & (1<<WEAPON_HORNETGUN))
|
||||
{
|
||||
// if close enough for hornet gun...
|
||||
if (((distance > 30) && (distance < 1000) && (weapon_choice == 0)) ||
|
||||
(weapon_choice == WEAPON_HORNETGUN))
|
||||
{
|
||||
new_weapon = weapon_ptr[WEAPON_HORNETGUN];
|
||||
|
||||
// check if the bot has any ammo left for this weapon...
|
||||
if (primary_ammo[WEAPON_HORNETGUN] > 0)
|
||||
{
|
||||
// check if the bot isn't already using this item...
|
||||
if (m_pActiveItem != new_weapon)
|
||||
SelectItem("weapon_hornetgun"); // select the hornet gun
|
||||
|
||||
long use_secondary = RANDOM_LONG(1,100);
|
||||
|
||||
// use secondary attack about 50% of the time (if fully reloaded)
|
||||
if ((use_secondary <= 50) &&
|
||||
(primary_ammo[WEAPON_HORNETGUN] >= HORNET_MAX_CARRY))
|
||||
{
|
||||
// BigGuy - START
|
||||
pev->button |= IN_ATTACK2; // use secondary attack (buzz! buzz!)
|
||||
|
||||
// set next time to shoot
|
||||
f_shoot_time = gpGlobals->time + 0.1 +
|
||||
RANDOM_FLOAT(secondary_fire_delay[WEAPON_HORNETGUN][bot_skill][0],
|
||||
secondary_fire_delay[WEAPON_HORNETGUN][bot_skill][1]);
|
||||
// BigGuy - END
|
||||
}
|
||||
else
|
||||
{
|
||||
pev->button |= IN_ATTACK; // use primary attack (buzz! buzz!)
|
||||
|
||||
// set next time to shoot
|
||||
f_shoot_time = gpGlobals->time + 0.25 +
|
||||
RANDOM_FLOAT(primary_fire_delay[WEAPON_HORNETGUN][bot_skill][0],
|
||||
primary_fire_delay[WEAPON_HORNETGUN][bot_skill][1]);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if the bot is carrying the MP5 (can't use underwater)...
|
||||
if ((pev->weapons & (1<<WEAPON_MP5)) && (pev->waterlevel != 3))
|
||||
{
|
||||
long use_secondary = RANDOM_LONG(1,100);
|
||||
|
||||
// use secondary attack about 10% of the time...
|
||||
if (((distance > 300) && (distance < 600) &&
|
||||
(weapon_choice == 0) && (use_secondary <= 10)) ||
|
||||
((weapon_choice == WEAPON_MP5) && (primary == FALSE)))
|
||||
{
|
||||
// at some point we need to fire upwards in the air slightly
|
||||
// for long distance kills. for right now, just fire the
|
||||
// grenade at the poor sucker.
|
||||
|
||||
// BigGuy - START
|
||||
new_weapon = weapon_ptr[WEAPON_MP5];
|
||||
|
||||
// check if the bot has any ammo left for this weapon...
|
||||
if (secondary_ammo[WEAPON_MP5] > 0)
|
||||
{
|
||||
// check if the bot isn't already using this item...
|
||||
if (m_pActiveItem != new_weapon)
|
||||
SelectItem("weapon_9mmAR"); // select the 9mmAR (MP5)
|
||||
|
||||
pev->button |= IN_ATTACK2; // use secodnary attack (boom!)
|
||||
|
||||
// set next time to shoot
|
||||
f_shoot_time = gpGlobals->time + 1.0 +
|
||||
RANDOM_FLOAT(secondary_fire_delay[WEAPON_MP5][bot_skill][0],
|
||||
secondary_fire_delay[WEAPON_MP5][bot_skill][1]);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
// BigGuy - END
|
||||
}
|
||||
|
||||
// if close enough for good MP5 shot...
|
||||
if (((distance < 250) && (weapon_choice == 0)) ||
|
||||
(weapon_choice == WEAPON_MP5))
|
||||
{
|
||||
new_weapon = weapon_ptr[WEAPON_MP5];
|
||||
|
||||
// check if the bot has any ammo left for this weapon...
|
||||
if (primary_ammo[WEAPON_MP5] > 0)
|
||||
{
|
||||
// check if the bot isn't already using this item...
|
||||
if (m_pActiveItem != new_weapon)
|
||||
SelectItem("weapon_9mmAR"); // select the 9mmAR (MP5)
|
||||
|
||||
pev->button |= IN_ATTACK; // use primary attack (bang! bang!)
|
||||
|
||||
// set next time to shoot
|
||||
f_shoot_time = gpGlobals->time + 0.1 +
|
||||
RANDOM_FLOAT(primary_fire_delay[WEAPON_MP5][bot_skill][0],
|
||||
primary_fire_delay[WEAPON_MP5][bot_skill][1]);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if the bot is carrying the crossbow...
|
||||
if (pev->weapons & (1<<WEAPON_CROSSBOW))
|
||||
{
|
||||
// if bot is not too close for crossbow and not too far...
|
||||
if (((distance > 100) && (distance < 1000) && (weapon_choice == 0)) ||
|
||||
(weapon_choice == WEAPON_CROSSBOW))
|
||||
{
|
||||
new_weapon = weapon_ptr[WEAPON_CROSSBOW];
|
||||
|
||||
// check if the bot has any ammo left for this weapon...
|
||||
if (primary_ammo[WEAPON_CROSSBOW] > 0)
|
||||
{
|
||||
// check if the bot isn't already using this item...
|
||||
if (m_pActiveItem != new_weapon)
|
||||
SelectItem("weapon_crossbow"); // select the crossbow
|
||||
|
||||
pev->button |= IN_ATTACK; // use primary attack (bang! bang!)
|
||||
|
||||
// set next time to shoot
|
||||
f_shoot_time = gpGlobals->time + 0.75 +
|
||||
RANDOM_FLOAT(primary_fire_delay[WEAPON_CROSSBOW][bot_skill][0],
|
||||
primary_fire_delay[WEAPON_CROSSBOW][bot_skill][1]);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if the bot is carrying the RPG...
|
||||
if (pev->weapons & (1<<WEAPON_RPG))
|
||||
{
|
||||
// don't use the RPG unless the enemy is pretty far away...
|
||||
if (((distance > 300) && (weapon_choice == 0)) ||
|
||||
(weapon_choice == WEAPON_RPG))
|
||||
{
|
||||
new_weapon = weapon_ptr[WEAPON_RPG];
|
||||
|
||||
// check if the bot has any ammo left for this weapon...
|
||||
if (primary_ammo[WEAPON_RPG] > 0)
|
||||
{
|
||||
// check if the bot isn't already using this item...
|
||||
if (m_pActiveItem != new_weapon)
|
||||
SelectItem("weapon_rpg"); // select the RPG rocket launcher
|
||||
|
||||
pev->button |= IN_ATTACK; // use primary attack (bang! bang!)
|
||||
|
||||
// set next time to shoot
|
||||
f_shoot_time = gpGlobals->time + 1.5 +
|
||||
RANDOM_FLOAT(primary_fire_delay[WEAPON_RPG][bot_skill][0],
|
||||
primary_fire_delay[WEAPON_RPG][bot_skill][1]);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if the bot is carrying the 9mm glock...
|
||||
if (pev->weapons & (1<<WEAPON_GLOCK))
|
||||
{
|
||||
// if nothing else was selected, try the good ol' 9mm glock...
|
||||
if (((distance < 1200) && (weapon_choice == 0)) ||
|
||||
(weapon_choice == WEAPON_GLOCK))
|
||||
{
|
||||
new_weapon = weapon_ptr[WEAPON_GLOCK];
|
||||
|
||||
// check if the bot has any ammo left for this weapon...
|
||||
if (primary_ammo[WEAPON_GLOCK] > 0)
|
||||
{
|
||||
// check if the bot isn't already using this item...
|
||||
if (m_pActiveItem != new_weapon)
|
||||
SelectItem("weapon_9mmhandgun"); // select the trusty 9mm glock
|
||||
|
||||
long use_secondary = RANDOM_LONG(1,100);
|
||||
|
||||
// use secondary attack about 30% of the time
|
||||
if (use_secondary <= 30)
|
||||
{
|
||||
// BigGuy - START
|
||||
pev->button |= IN_ATTACK2; // use secondary attack (bang! bang!)
|
||||
|
||||
// set next time to shoot
|
||||
f_shoot_time = gpGlobals->time + 0.2 +
|
||||
RANDOM_FLOAT(secondary_fire_delay[WEAPON_GLOCK][bot_skill][0],
|
||||
secondary_fire_delay[WEAPON_GLOCK][bot_skill][1]);
|
||||
// BigGuy - END
|
||||
}
|
||||
else
|
||||
{
|
||||
pev->button |= IN_ATTACK; // use primary attack (bang! bang!)
|
||||
|
||||
// set next time to shoot
|
||||
f_shoot_time = gpGlobals->time + 0.3 +
|
||||
RANDOM_FLOAT(primary_fire_delay[WEAPON_GLOCK][bot_skill][0],
|
||||
primary_fire_delay[WEAPON_GLOCK][bot_skill][1]);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// didn't have any available weapons or ammo, return FALSE
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
void CBot::BotShootAtEnemy( void )
|
||||
{
|
||||
float f_distance;
|
||||
|
||||
// aim for the head and/or body
|
||||
Vector v_enemy = BotBodyTarget( pBotEnemy ) - GetGunPosition();
|
||||
|
||||
pev->v_angle = UTIL_VecToAngles( v_enemy );
|
||||
|
||||
pev->angles.x = 0;
|
||||
pev->angles.y = pev->v_angle.y;
|
||||
pev->angles.z = 0;
|
||||
|
||||
pev->ideal_yaw = pev->v_angle.y;
|
||||
|
||||
// check for wrap around of angle...
|
||||
if (pev->ideal_yaw > 180)
|
||||
pev->ideal_yaw -= 360;
|
||||
if (pev->ideal_yaw < -180)
|
||||
pev->ideal_yaw += 360;
|
||||
|
||||
pev->v_angle.x = -pev->v_angle.x; //adjust pitch to point gun
|
||||
|
||||
// is it time to shoot yet?
|
||||
if (f_shoot_time <= gpGlobals->time)
|
||||
{
|
||||
// select the best weapon to use at this distance and fire...
|
||||
BotFireWeapon( pBotEnemy->pev->origin );
|
||||
}
|
||||
|
||||
v_enemy.z = 0; // ignore z component (up & down)
|
||||
|
||||
f_distance = v_enemy.Length(); // how far away is the enemy scum?
|
||||
|
||||
if (f_distance > 200) // run if distance to enemy is far
|
||||
f_move_speed = f_max_speed;
|
||||
else if (f_distance > 20) // walk if distance is closer
|
||||
f_move_speed = f_max_speed / 2;
|
||||
else // don't move if close enough
|
||||
f_move_speed = 0.0;
|
||||
}
|
||||
|
||||
|
||||
|
142
dlls/bot/botcam.cpp
Normal file
142
dlls/bot/botcam.cpp
Normal file
@ -0,0 +1,142 @@
|
||||
// botman's Half-Life bot example
|
||||
//
|
||||
// http://planethalflife.com/botman/
|
||||
//
|
||||
// botcam.cpp
|
||||
//
|
||||
|
||||
#include "extdll.h"
|
||||
#include "util.h"
|
||||
#include "client.h"
|
||||
#include "cbase.h"
|
||||
#include "player.h"
|
||||
|
||||
#include "botcam.h"
|
||||
|
||||
|
||||
LINK_ENTITY_TO_CLASS(entity_botcam, CBotCam);
|
||||
|
||||
|
||||
CBotCam *CBotCam::Create( CBasePlayer *pPlayer, CBasePlayer *pBot )
|
||||
{
|
||||
CBotCam *pBotCam = GetClassPtr( (CBotCam *)NULL );
|
||||
|
||||
PRECACHE_MODEL( "models/mechgibs.mdl" );
|
||||
|
||||
pBotCam->m_pPlayer = pPlayer;
|
||||
pBotCam->m_pBot = pBot;
|
||||
|
||||
pPlayer->pev->effects |= EF_NODRAW;
|
||||
pPlayer->pev->solid = SOLID_NOT;
|
||||
pPlayer->pev->takedamage = DAMAGE_NO;
|
||||
pPlayer->m_iHideHUD |= HIDEHUD_ALL;
|
||||
|
||||
pBotCam->Spawn();
|
||||
|
||||
return pBotCam;
|
||||
}
|
||||
|
||||
|
||||
void CBotCam::Spawn( void )
|
||||
{
|
||||
pev->classname = MAKE_STRING("entity_botcam");
|
||||
|
||||
UTIL_MakeVectors(m_pBot->pev->v_angle);
|
||||
|
||||
TraceResult tr;
|
||||
UTIL_TraceLine(m_pBot->pev->origin + m_pBot->pev->view_ofs,
|
||||
m_pBot->pev->origin + m_pBot->pev->view_ofs + gpGlobals->v_forward * -16 + gpGlobals->v_up * 10,
|
||||
dont_ignore_monsters, m_pBot->edict(), &tr );
|
||||
|
||||
UTIL_SetOrigin(pev, tr.vecEndPos);
|
||||
|
||||
pev->angles = m_pBot->pev->v_angle;
|
||||
|
||||
pev->fixangle = TRUE;
|
||||
|
||||
SET_VIEW (m_pPlayer->edict(), edict());
|
||||
|
||||
// mechgibs seems to be an "invisible" model. Other players won't see
|
||||
// anything when this model is used as the botcam...
|
||||
|
||||
SET_MODEL(ENT(pev), "models/mechgibs.mdl");
|
||||
|
||||
pev->movetype = MOVETYPE_FLY;
|
||||
pev->solid = SOLID_NOT;
|
||||
pev->takedamage = DAMAGE_NO;
|
||||
|
||||
pev->renderamt = 0;
|
||||
|
||||
m_pPlayer->EnableControl(FALSE);
|
||||
|
||||
SetTouch( NULL );
|
||||
|
||||
SetThink( IdleThink );
|
||||
|
||||
pev->nextthink = gpGlobals->time + 0.1;
|
||||
}
|
||||
|
||||
|
||||
void CBotCam::IdleThink( void )
|
||||
{
|
||||
// make sure bot is still in the game...
|
||||
|
||||
if (m_pBot->pev->takedamage != DAMAGE_NO) // not "kicked"
|
||||
{
|
||||
UTIL_MakeVectors(m_pBot->pev->v_angle);
|
||||
|
||||
TraceResult tr;
|
||||
UTIL_TraceLine(m_pBot->pev->origin + m_pBot->pev->view_ofs,
|
||||
m_pBot->pev->origin + m_pBot->pev->view_ofs + gpGlobals->v_forward * -16 + gpGlobals->v_up * 10,
|
||||
dont_ignore_monsters, m_pBot->edict(), &tr );
|
||||
|
||||
UTIL_SetOrigin(pev, tr.vecEndPos);
|
||||
|
||||
pev->angles = m_pBot->pev->v_angle;
|
||||
|
||||
pev->fixangle = TRUE;
|
||||
|
||||
SET_VIEW (m_pPlayer->edict(), edict());
|
||||
|
||||
pev->nextthink = gpGlobals->time;
|
||||
}
|
||||
else
|
||||
{
|
||||
SET_VIEW (m_pPlayer->edict(), m_pPlayer->edict());
|
||||
|
||||
m_pPlayer->pev->effects &= ~EF_NODRAW;
|
||||
m_pPlayer->pev->solid = SOLID_SLIDEBOX;
|
||||
m_pPlayer->pev->takedamage = DAMAGE_AIM;
|
||||
m_pPlayer->m_iHideHUD &= ~HIDEHUD_ALL;
|
||||
|
||||
m_pPlayer->EnableControl(TRUE);
|
||||
|
||||
m_pPlayer->pBotCam = NULL; // player's botcam is no longer valid
|
||||
|
||||
m_pBot = NULL;
|
||||
m_pPlayer = NULL;
|
||||
|
||||
REMOVE_ENTITY( ENT(pev) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CBotCam::Disconnect( void )
|
||||
{
|
||||
SET_VIEW (m_pPlayer->edict(), m_pPlayer->edict());
|
||||
|
||||
m_pPlayer->pev->effects &= ~EF_NODRAW;
|
||||
m_pPlayer->pev->solid = SOLID_SLIDEBOX;
|
||||
m_pPlayer->pev->takedamage = DAMAGE_AIM;
|
||||
m_pPlayer->m_iHideHUD &= ~HIDEHUD_ALL;
|
||||
|
||||
m_pPlayer->EnableControl(TRUE);
|
||||
|
||||
m_pPlayer->pBotCam = NULL; // player's botcam is no longer valid
|
||||
|
||||
m_pBot = NULL;
|
||||
m_pPlayer = NULL;
|
||||
|
||||
REMOVE_ENTITY( ENT(pev) );
|
||||
}
|
||||
|
26
dlls/bot/botcam.h
Normal file
26
dlls/bot/botcam.h
Normal file
@ -0,0 +1,26 @@
|
||||
// botman's Half-Life bot example
|
||||
//
|
||||
// http://planethalflife.com/botman/
|
||||
//
|
||||
// botcam.h
|
||||
//
|
||||
|
||||
#ifndef BOTCAM_H
|
||||
#define BOTCAM_H
|
||||
|
||||
class CBotCam : public CBaseEntity
|
||||
{
|
||||
public:
|
||||
|
||||
CBasePlayer *m_pPlayer;
|
||||
CBasePlayer *m_pBot;
|
||||
|
||||
static CBotCam *Create( CBasePlayer *pPlayer, CBasePlayer *pBot );
|
||||
void Spawn( void );
|
||||
void Disconnect( void );
|
||||
|
||||
void EXPORT IdleThink( void );
|
||||
};
|
||||
|
||||
#endif
|
||||
|
596
dlls/client.cpp
596
dlls/client.cpp
@ -40,6 +40,20 @@
|
||||
#include "netadr.h"
|
||||
#include "pm_shared.h"
|
||||
|
||||
// START BOT
|
||||
#include "bot.h"
|
||||
#include "botcam.h"
|
||||
|
||||
void BotCreate(const char *skin, const char *name, const char *skill);
|
||||
extern int f_Observer; // flag for observer mode
|
||||
extern int f_botskill; // default bot skill level
|
||||
extern int f_botdontshoot; // flag to disable targeting other bots
|
||||
extern respawn_t bot_respawn[32];
|
||||
float bot_check_time = 10.0;
|
||||
int min_bots = 0;
|
||||
int max_bots = 0;
|
||||
// END BOT
|
||||
|
||||
extern DLL_GLOBAL ULONG g_ulModelIndexPlayer;
|
||||
extern DLL_GLOBAL BOOL g_fGameOver;
|
||||
extern DLL_GLOBAL int g_iSkillLevel;
|
||||
@ -85,6 +99,41 @@ called when a player connects to a server
|
||||
*/
|
||||
BOOL ClientConnect( edict_t *pEntity, const char *pszName, const char *pszAddress, char szRejectReason[128] )
|
||||
{
|
||||
int i;
|
||||
int count = 0;
|
||||
|
||||
// check if this is NOT a bot joining the server...
|
||||
if( strcmp( pszAddress, "127.0.0.1" ) != 0 )
|
||||
{
|
||||
// don't try to add bots for 30 seconds, give client time to get added
|
||||
bot_check_time = gpGlobals->time + 30.0;
|
||||
|
||||
for( i = 0; i < 32; i++ )
|
||||
{
|
||||
if( bot_respawn[i].is_used ) // count the number of bots in use
|
||||
count++;
|
||||
}
|
||||
|
||||
// if there are currently more than the minimum number of bots running
|
||||
// then kick one of the bots off the server...
|
||||
if( ( min_bots != 0 ) && ( count > min_bots ) )
|
||||
{
|
||||
for( i = 0; i < 32; i++ )
|
||||
{
|
||||
if( bot_respawn[i].is_used ) // is this slot used?
|
||||
{
|
||||
char cmd[40];
|
||||
|
||||
sprintf( cmd, "kick \"%s\"\n", bot_respawn[i].name );
|
||||
|
||||
bot_respawn[i].state = BOT_IDLE;
|
||||
|
||||
SERVER_COMMAND( cmd ); // kick the bot using (kick "name")
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return g_pGameRules->ClientConnected( pEntity, pszName, pszAddress, szRejectReason );
|
||||
|
||||
// a client connecting during an intermission can cause problems
|
||||
@ -599,6 +648,132 @@ void ClientCommand( edict_t *pEntity )
|
||||
if( pPlayer->IsObserver() )
|
||||
pPlayer->Observer_FindNextPlayer( atoi( CMD_ARGV( 1 ) ) ? true : false );
|
||||
}
|
||||
// START BOT
|
||||
else if( FStrEq( pcmd, "addbot" ) )
|
||||
{
|
||||
if( !IS_DEDICATED_SERVER() )
|
||||
{
|
||||
// If user types "addbot" in console, add a bot with skin and name
|
||||
BotCreate( CMD_ARGV( 1 ), CMD_ARGV( 2 ), CMD_ARGV( 3 ) );
|
||||
}
|
||||
else
|
||||
CLIENT_PRINTF( pEntity, print_console, "addbot not allowed from client!\n" );
|
||||
}
|
||||
else if( FStrEq( pcmd, "observer" ) )
|
||||
{
|
||||
if( !IS_DEDICATED_SERVER() )
|
||||
{
|
||||
if( CMD_ARGC() > 1 ) // is there an argument to the command?
|
||||
{
|
||||
f_Observer = atoi( CMD_ARGV( 1 ) ); // set observer flag
|
||||
CLIENT_PRINTF( pEntity, print_console, UTIL_VarArgs( "\"observer\" set to %d\n", (int)f_Observer ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
CLIENT_PRINTF( pEntity, print_console, UTIL_VarArgs( "\"observer\" is %d\n", (int)f_Observer ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
CLIENT_PRINTF( pEntity, print_console, "observer not allowed from client!\n" );
|
||||
}
|
||||
else if( FStrEq( pcmd, "botskill" ) )
|
||||
{
|
||||
if( !IS_DEDICATED_SERVER() )
|
||||
{
|
||||
if( CMD_ARGC() > 1 )
|
||||
{
|
||||
f_botskill = atoi( CMD_ARGV( 1 ) ); // set default bot skill level
|
||||
CLIENT_PRINTF( pEntity, print_console, UTIL_VarArgs( "\"botskill\" set to %d\n", (int)f_botskill ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
CLIENT_PRINTF( pEntity, print_console, UTIL_VarArgs( "\"botskill\" is %d\n", (int)f_botskill ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
CLIENT_PRINTF( pEntity, print_console, "botskill not allowed from client!\n" );
|
||||
}
|
||||
else if( FStrEq( pcmd, "botdontshoot" ) )
|
||||
{
|
||||
if( !IS_DEDICATED_SERVER() )
|
||||
{
|
||||
if( CMD_ARGC() > 1) // is there an argument to the command?
|
||||
{
|
||||
f_botdontshoot = atoi( CMD_ARGV( 1 ) ); // set bot shoot flag
|
||||
CLIENT_PRINTF( pEntity, print_console, UTIL_VarArgs( "\"botdontshoot\" set to %d\n", (int)f_botdontshoot ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
CLIENT_PRINTF( pEntity, print_console, UTIL_VarArgs( "\"botdontshoot\" is %d\n", (int)f_botdontshoot ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
CLIENT_PRINTF( pEntity, print_console, "botdontshoot not allowed from client!\n" );
|
||||
}
|
||||
else if( FStrEq( pcmd, "botcam" ) )
|
||||
{
|
||||
CBasePlayer *pPlayer = GetClassPtr( (CBasePlayer *)pev );
|
||||
CBasePlayer *pBot = NULL;
|
||||
char botname[BOT_NAME_LEN + 1];
|
||||
int index;
|
||||
|
||||
botname[0] = 0;
|
||||
|
||||
if( CMD_ARGC() > 1 ) // is there an argument to the command?
|
||||
{
|
||||
if( strstr( CMD_ARGV( 1 ), "\"" ) == NULL )
|
||||
strcpy( botname, CMD_ARGV( 1 ) );
|
||||
else
|
||||
sscanf( CMD_ARGV( 1 ), "\"%s\"", &botname[0] );
|
||||
|
||||
index = 0;
|
||||
|
||||
while( index < 32 )
|
||||
{
|
||||
if( ( bot_respawn[index].is_used ) &&
|
||||
( stricmp( bot_respawn[index].name, botname ) == 0 ) )
|
||||
break;
|
||||
else
|
||||
index++;
|
||||
}
|
||||
|
||||
if( index < 32 )
|
||||
pBot = bot_respawn[index].pBot;
|
||||
}
|
||||
else
|
||||
{
|
||||
index = 0;
|
||||
|
||||
while( ( bot_respawn[index].is_used == FALSE ) && ( index < 32 ) )
|
||||
index++;
|
||||
|
||||
if( index < 32 )
|
||||
pBot = bot_respawn[index].pBot;
|
||||
}
|
||||
|
||||
if( pBot == NULL )
|
||||
{
|
||||
if( botname[0] )
|
||||
CLIENT_PRINTF( pEntity, print_console, UTIL_VarArgs( "there is no bot named \"%s\"!\n", botname ) );
|
||||
else
|
||||
CLIENT_PRINTF( pEntity, print_console, UTIL_VarArgs( "there are no bots!\n" ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( pPlayer->pBotCam ) // if botcam in use, disconnect first...
|
||||
pPlayer->pBotCam->Disconnect();
|
||||
|
||||
pPlayer->pBotCam = CBotCam::Create( pPlayer, pBot );
|
||||
}
|
||||
}
|
||||
else if( FStrEq( pcmd, "nobotcam" ) )
|
||||
{
|
||||
CBasePlayer *pPlayer = GetClassPtr( (CBasePlayer *)pev );
|
||||
|
||||
if( pPlayer->pBotCam )
|
||||
pPlayer->pBotCam->Disconnect();
|
||||
}
|
||||
//END BOT
|
||||
else if( g_pGameRules->ClientCommand( GetClassPtr( (CBasePlayer *)pev ), pcmd ) )
|
||||
{
|
||||
// MenuSelect returns true only if the command is properly handled, so don't print a warning
|
||||
@ -798,10 +973,357 @@ void ParmsChangeLevel( void )
|
||||
//
|
||||
void StartFrame( void )
|
||||
{
|
||||
// START BOT
|
||||
static BOOL file_opened = FALSE;
|
||||
static int length;
|
||||
static char *pFileList, *aFileList;
|
||||
static char cmd_line[80];
|
||||
static char server_cmd[80];
|
||||
static int index, i;
|
||||
static float pause_time;
|
||||
static float check_server_cmd = 0;
|
||||
char *cmd, *arg1, *arg2, *arg3;
|
||||
static float respawn_time = 0;
|
||||
static float previous_time = 0.0;
|
||||
char msg[120];
|
||||
// END BOT
|
||||
|
||||
// START BOT - thanks Jehannum!
|
||||
|
||||
// loop through all the players...
|
||||
for( i = 1; i <= gpGlobals->maxClients; i++ )
|
||||
{
|
||||
CBaseEntity *pPlayer = UTIL_PlayerByIndex( i );
|
||||
|
||||
if( !pPlayer ) // if invalid then continue with next index...
|
||||
continue;
|
||||
|
||||
// check if this is a FAKECLIENT (i.e. is it a bot?)
|
||||
if( FBitSet( pPlayer->pev->flags, FL_FAKECLIENT ) )
|
||||
{
|
||||
CBot *pBot = (CBot*)pPlayer;
|
||||
|
||||
// call the think function for the bot...
|
||||
pBot->BotThink();
|
||||
}
|
||||
}
|
||||
// END BOT
|
||||
|
||||
// START BOT
|
||||
if( ( g_fGameOver ) && ( respawn_time < 1.0 ) )
|
||||
{
|
||||
// if the game is over (time/frag limit) set the respawn time...
|
||||
respawn_time = 5.0;
|
||||
|
||||
// check if any players are using the botcam...
|
||||
for( i = 1; i <= gpGlobals->maxClients; i++ )
|
||||
{
|
||||
CBasePlayer *pPlayer = (CBasePlayer *)UTIL_PlayerByIndex( i );
|
||||
|
||||
if( !pPlayer )
|
||||
continue; // if invalid then continue with next index...
|
||||
|
||||
if( pPlayer->pBotCam )
|
||||
pPlayer->pBotCam->Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
// check if a map was changed via "map" without kicking bots...
|
||||
if( previous_time > gpGlobals->time )
|
||||
{
|
||||
bot_check_time = gpGlobals->time + 10.0;
|
||||
|
||||
for( index = 0; index < 32; index++ )
|
||||
{
|
||||
if( ( bot_respawn[index].is_used) && // is this slot used?
|
||||
( bot_respawn[index].state != BOT_NEED_TO_RESPAWN ) )
|
||||
{
|
||||
// bot has already been "kicked" by server so just set flag
|
||||
bot_respawn[index].state = BOT_NEED_TO_RESPAWN;
|
||||
|
||||
// if the map was changed set the respawn time...
|
||||
respawn_time = 5.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// is new game started and time to respawn bots yet?
|
||||
if( ( !g_fGameOver ) && ( respawn_time > 1.0 ) &&
|
||||
( gpGlobals->time >= respawn_time ) )
|
||||
{
|
||||
int index = 0;
|
||||
bot_check_time = gpGlobals->time + 5.0;
|
||||
|
||||
// find bot needing to be respawned...
|
||||
while( ( index < 32 ) &&
|
||||
( bot_respawn[index].state != BOT_NEED_TO_RESPAWN ) )
|
||||
index++;
|
||||
|
||||
if( index < 32 )
|
||||
{
|
||||
bot_respawn[index].state = BOT_IS_RESPAWNING;
|
||||
bot_respawn[index].is_used = FALSE; // free up this slot
|
||||
|
||||
// respawn 1 bot then wait a while (otherwise engine crashes)
|
||||
BotCreate( bot_respawn[index].skin,
|
||||
bot_respawn[index].name,
|
||||
bot_respawn[index].skill );
|
||||
|
||||
respawn_time = gpGlobals->time + 1.0; // set next respawn time
|
||||
}
|
||||
else
|
||||
{
|
||||
respawn_time = 0.0;
|
||||
}
|
||||
}
|
||||
// END BOT
|
||||
//ALERT( at_console, "SV_Physics( %g, frametime %g )\n", gpGlobals->time, gpGlobals->frametime );
|
||||
|
||||
if( g_pGameRules )
|
||||
{
|
||||
g_pGameRules->Think();
|
||||
// START BOT
|
||||
if( !file_opened ) // have we open bot.cfg file yet?
|
||||
{
|
||||
ALERT( at_console, "Executing bot.cfg\n" );
|
||||
pFileList = (char *)LOAD_FILE_FOR_ME( "bot.cfg", &length );
|
||||
file_opened = TRUE;
|
||||
if( pFileList == NULL )
|
||||
ALERT( at_console, "bot.cfg file not found\n" );
|
||||
|
||||
pause_time = gpGlobals->time;
|
||||
|
||||
index = 0;
|
||||
cmd_line[index] = 0; // null out command line
|
||||
}
|
||||
|
||||
// if the bot.cfg file is still open and time to execute command...
|
||||
while( ( pFileList && *pFileList ) && ( pause_time <= gpGlobals->time ) )
|
||||
{
|
||||
while( *pFileList == ' ' ) // skip any leading blanks
|
||||
pFileList++;
|
||||
|
||||
while( ( *pFileList != '\r' ) && ( *pFileList != '\n' ) &&
|
||||
( *pFileList != 0 ) )
|
||||
{
|
||||
if( *pFileList == '\t' ) // convert tabs to spaces
|
||||
*pFileList = ' ';
|
||||
|
||||
cmd_line[index] = *pFileList;
|
||||
pFileList++;
|
||||
|
||||
while( ( cmd_line[index] == ' ' ) && ( *pFileList == ' ' ) )
|
||||
pFileList++; // skip multiple spaces
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
if( *pFileList == '\r' )
|
||||
{
|
||||
pFileList++; // skip the carriage return
|
||||
pFileList++; // skip the linefeed
|
||||
}
|
||||
else if( *pFileList == '\n' )
|
||||
{
|
||||
pFileList++; // skip the newline
|
||||
}
|
||||
|
||||
cmd_line[index] = 0; // terminate the command line
|
||||
|
||||
// copy the command line to a server command buffer...
|
||||
strcpy( server_cmd, cmd_line );
|
||||
strcat( server_cmd, "\n" );
|
||||
|
||||
index = 0;
|
||||
cmd = cmd_line;
|
||||
arg1 = arg2 = arg3 = NULL;
|
||||
|
||||
// skip to blank or end of string...
|
||||
while( ( cmd_line[index] != ' ' ) && ( cmd_line[index] != 0 ) )
|
||||
index++;
|
||||
|
||||
if( cmd_line[index] == ' ' )
|
||||
{
|
||||
cmd_line[index++] = 0;
|
||||
arg1 = &cmd_line[index];
|
||||
|
||||
// skip to blank or end of string...
|
||||
while( ( cmd_line[index] != ' ' ) && ( cmd_line[index] != 0 ) )
|
||||
index++;
|
||||
|
||||
if( cmd_line[index] == ' ' )
|
||||
{
|
||||
cmd_line[index++] = 0;
|
||||
arg2 = &cmd_line[index];
|
||||
|
||||
// skip to blank or end of string...
|
||||
while( ( cmd_line[index] != ' ' ) && ( cmd_line[index] != 0 ) )
|
||||
index++;
|
||||
|
||||
if( cmd_line[index] == ' ' )
|
||||
{
|
||||
cmd_line[index++] = 0;
|
||||
arg3 = &cmd_line[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
index = 0; // reset for next input line
|
||||
|
||||
if( ( cmd_line[0] == '#' ) || ( cmd_line[0] == 0 ) )
|
||||
{
|
||||
continue; // ignore comments or blank lines
|
||||
}
|
||||
else if( strcmp( cmd, "addbot" ) == 0 )
|
||||
{
|
||||
BotCreate( arg1, arg2, arg3 );
|
||||
|
||||
// have to delay here or engine gives "Tried to write to
|
||||
// uninitialized sizebuf_t" error and crashes...
|
||||
pause_time = gpGlobals->time + 1;
|
||||
break;
|
||||
}
|
||||
else if( strcmp( cmd, "botskill" ) == 0 )
|
||||
{
|
||||
f_botskill = atoi( arg1 ); // set default bot skill level
|
||||
}
|
||||
else if( strcmp( cmd, "observer" ) == 0 )
|
||||
{
|
||||
f_Observer = atoi( arg1 ); // set observer flag
|
||||
}
|
||||
else if( strcmp( cmd, "botdontshoot" ) == 0 )
|
||||
{
|
||||
f_botdontshoot = atoi( arg1 ); // set bot shoot flag
|
||||
}
|
||||
else if( strcmp( cmd, "min_bots" ) == 0 )
|
||||
{
|
||||
min_bots = atoi( arg1 );
|
||||
|
||||
if( min_bots < 0 )
|
||||
min_bots = 0;
|
||||
|
||||
if( IS_DEDICATED_SERVER() )
|
||||
{
|
||||
sprintf( msg, "min_bots set to %d\n", min_bots );
|
||||
printf( msg );
|
||||
}
|
||||
}
|
||||
else if( strcmp( cmd, "max_bots" ) == 0 )
|
||||
{
|
||||
max_bots = atoi( arg1 );
|
||||
|
||||
if( max_bots >= gpGlobals->maxClients )
|
||||
max_bots = gpGlobals->maxClients - 1;
|
||||
|
||||
if( IS_DEDICATED_SERVER() )
|
||||
{
|
||||
sprintf( msg, "max_bots set to %d\n", max_bots );
|
||||
printf( msg );
|
||||
}
|
||||
}
|
||||
else if( strcmp( cmd, "pause" ) == 0 )
|
||||
{
|
||||
pause_time = gpGlobals->time + atoi( arg1 );
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf( msg, "executing server command: %s\n", server_cmd );
|
||||
ALERT( at_console, msg );
|
||||
|
||||
if( IS_DEDICATED_SERVER() )
|
||||
printf( msg );
|
||||
|
||||
SERVER_COMMAND( server_cmd );
|
||||
}
|
||||
}
|
||||
|
||||
// if bot.cfg file is open and reached end of file, then close and free it
|
||||
if( pFileList && ( *pFileList == 0 ) )
|
||||
{
|
||||
FREE_FILE( aFileList );
|
||||
pFileList = NULL;
|
||||
}
|
||||
|
||||
// if time to check for server commands then do so...
|
||||
if( check_server_cmd <= gpGlobals->time )
|
||||
{
|
||||
check_server_cmd = gpGlobals->time + 1.0;
|
||||
|
||||
char *cvar_bot = (char *)CVAR_GET_STRING( "bot" );
|
||||
|
||||
if( cvar_bot && cvar_bot[0] )
|
||||
{
|
||||
strcpy( cmd_line, cvar_bot );
|
||||
|
||||
index = 0;
|
||||
cmd = cmd_line;
|
||||
arg1 = arg2 = arg3 = NULL;
|
||||
|
||||
// skip to blank or end of string...
|
||||
while( ( cmd_line[index] != ' ' ) && ( cmd_line[index] != 0 ) )
|
||||
index++;
|
||||
|
||||
if( cmd_line[index] == ' ' )
|
||||
{
|
||||
cmd_line[index++] = 0;
|
||||
arg1 = &cmd_line[index];
|
||||
|
||||
// skip to blank or end of string...
|
||||
while( ( cmd_line[index] != ' ' ) && ( cmd_line[index] != 0 ) )
|
||||
index++;
|
||||
|
||||
if( cmd_line[index] == ' ' )
|
||||
{
|
||||
cmd_line[index++] = 0;
|
||||
arg2 = &cmd_line[index];
|
||||
|
||||
// skip to blank or end of string...
|
||||
while( ( cmd_line[index] != ' ' ) && ( cmd_line[index] != 0 ) )
|
||||
index++;
|
||||
|
||||
if( cmd_line[index] == ' ' )
|
||||
{
|
||||
cmd_line[index++] = 0;
|
||||
arg3 = &cmd_line[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( strcmp( cmd, "addbot" ) == 0 )
|
||||
{
|
||||
printf( "adding new bot...\n" );
|
||||
|
||||
BotCreate( arg1, arg2, arg3 );
|
||||
}
|
||||
else if( strcmp( cmd, "botskill" ) == 0 )
|
||||
{
|
||||
if( arg1 != NULL )
|
||||
{
|
||||
printf( "setting botskill to %d\n", atoi( arg1 ) );
|
||||
|
||||
f_botskill = atoi( arg1 ); // set default bot skill level
|
||||
}
|
||||
else
|
||||
printf( "botskill is %d\n", f_botskill );
|
||||
}
|
||||
else if( strcmp( cmd, "botdontshoot" ) == 0 )
|
||||
{
|
||||
if( arg1 != NULL )
|
||||
{
|
||||
printf( "setting botdontshoot to %d\n", atoi( arg1 ) );
|
||||
|
||||
f_botdontshoot = atoi( arg1 ); // set bot shoot flag
|
||||
}
|
||||
else
|
||||
printf( "botdontshoot is %d\n", f_botdontshoot );
|
||||
}
|
||||
|
||||
CVAR_SET_STRING( "bot", "" );
|
||||
}
|
||||
}
|
||||
// END BOT
|
||||
}
|
||||
|
||||
if( g_fGameOver )
|
||||
return;
|
||||
@ -809,6 +1331,38 @@ void StartFrame( void )
|
||||
gpGlobals->teamplay = teamplay.value;
|
||||
g_ulFrameCount++;
|
||||
|
||||
// START BOT
|
||||
// check if time to see if a bot needs to be created...
|
||||
if( bot_check_time < gpGlobals->time )
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
bot_check_time = gpGlobals->time + 5.0;
|
||||
|
||||
for( i = 1; i <= gpGlobals->maxClients; i++ )
|
||||
{
|
||||
CBaseEntity *pPlayer = UTIL_PlayerByIndex( i );
|
||||
|
||||
if( !pPlayer )
|
||||
continue; // if invalid then continue with next index...
|
||||
|
||||
if( pPlayer->pev->takedamage == DAMAGE_NO )
|
||||
continue; // if bot was kicked, don't count as a player...
|
||||
|
||||
count++; // count the number of bots and players
|
||||
}
|
||||
|
||||
// if there are currently less than the maximum number of "players"
|
||||
// then add another bot using the default skill level...
|
||||
if( count < max_bots )
|
||||
{
|
||||
BotCreate( NULL, NULL, NULL );
|
||||
}
|
||||
}
|
||||
|
||||
previous_time = gpGlobals->time; // keep track of last time in StartFrame()
|
||||
// END BOT
|
||||
|
||||
int oldBhopcap = g_bhopcap;
|
||||
g_bhopcap = ( g_pGameRules->IsMultiplayer() && bhopcap.value != 0.0f ) ? 1 : 0;
|
||||
if( g_bhopcap != oldBhopcap )
|
||||
@ -929,6 +1483,48 @@ void ClientPrecache( void )
|
||||
|
||||
if( giPrecacheGrunt )
|
||||
UTIL_PrecacheOther( "monster_human_grunt" );
|
||||
|
||||
// START BOT
|
||||
if( !IS_DEDICATED_SERVER() )
|
||||
{
|
||||
PRECACHE_SOUND( HG_SND1 );
|
||||
PRECACHE_SOUND( HG_SND2 );
|
||||
PRECACHE_SOUND( HG_SND3 );
|
||||
PRECACHE_SOUND( HG_SND4 );
|
||||
PRECACHE_SOUND( HG_SND5 );
|
||||
|
||||
PRECACHE_SOUND( BA_SND1 );
|
||||
PRECACHE_SOUND( BA_SND2 );
|
||||
PRECACHE_SOUND( BA_SND3 );
|
||||
PRECACHE_SOUND( BA_SND4 );
|
||||
PRECACHE_SOUND( BA_SND5 );
|
||||
|
||||
PRECACHE_SOUND( SC_SND1 );
|
||||
PRECACHE_SOUND( SC_SND2 );
|
||||
PRECACHE_SOUND( SC_SND3 );
|
||||
PRECACHE_SOUND( SC_SND4 );
|
||||
PRECACHE_SOUND( SC_SND5 );
|
||||
|
||||
PRECACHE_SOUND( BA_TNT1 );
|
||||
PRECACHE_SOUND( BA_TNT2 );
|
||||
PRECACHE_SOUND( BA_TNT3 );
|
||||
PRECACHE_SOUND( BA_TNT4 );
|
||||
PRECACHE_SOUND( BA_TNT5 );
|
||||
|
||||
PRECACHE_SOUND( SC_TNT1 );
|
||||
PRECACHE_SOUND( SC_TNT2 );
|
||||
PRECACHE_SOUND( SC_TNT3 );
|
||||
PRECACHE_SOUND( SC_TNT4 );
|
||||
PRECACHE_SOUND( SC_TNT5 );
|
||||
|
||||
PRECACHE_SOUND( USE_TEAMPLAY_SND );
|
||||
PRECACHE_SOUND( USE_TEAMPLAY_LATER_SND );
|
||||
PRECACHE_SOUND( USE_TEAMPLAY_ENEMY_SND );
|
||||
}
|
||||
|
||||
UTIL_PrecacheOther( "entity_botcam" );
|
||||
PRECACHE_MODEL( "models/mechgibs.mdl" );
|
||||
// END BOT
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -446,6 +446,10 @@ cvar_t sk_player_leg3 = { "sk_player_leg3","1" };
|
||||
|
||||
// END Cvars for Skill Level settings
|
||||
|
||||
//START BOT
|
||||
cvar_t cvar_bot = { "bot", "" };
|
||||
//END BOT
|
||||
|
||||
// Register your console variables here
|
||||
// This gets called one time when the game is initialied
|
||||
void GameDLLInit( void )
|
||||
@ -858,6 +862,10 @@ void GameDLLInit( void )
|
||||
CVAR_REGISTER( &sk_player_leg3 );
|
||||
// END REGISTER CVARS FOR SKILL LEVEL STUFF
|
||||
|
||||
//START BOT
|
||||
CVAR_REGISTER( &cvar_bot );
|
||||
//END BOT
|
||||
|
||||
SERVER_COMMAND( "exec skill.cfg\n" );
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,12 @@
|
||||
#endif
|
||||
#include "hltv.h"
|
||||
|
||||
// START BOT
|
||||
#include "bot.h"
|
||||
#include "botcam.h"
|
||||
extern respawn_t bot_respawn[32];
|
||||
// END BOT
|
||||
|
||||
extern DLL_GLOBAL CGameRules *g_pGameRules;
|
||||
extern DLL_GLOBAL BOOL g_fGameOver;
|
||||
extern int gmsgDeathMsg; // client dll messages
|
||||
@ -1626,6 +1632,38 @@ void CHalfLifeMultiplay::ChangeLevel( void )
|
||||
|
||||
g_fGameOver = TRUE;
|
||||
|
||||
// START BOT
|
||||
// loop through all the players...
|
||||
for( int i = 1; i <= gpGlobals->maxClients; i++ )
|
||||
{
|
||||
CBaseEntity *pEntity = UTIL_PlayerByIndex( i );
|
||||
|
||||
if( !pEntity ) // if invalid then continue with next index...
|
||||
continue;
|
||||
|
||||
CBasePlayer *pPlayer = (CBasePlayer *)pEntity;
|
||||
|
||||
// if botcam is in use, disconnect so buttons will work...
|
||||
if( pPlayer->pBotCam )
|
||||
pPlayer->pBotCam->Disconnect();
|
||||
}
|
||||
|
||||
// kick any bot off of the server after time/frag limit...
|
||||
for( int index = 0; index < 32; index++ )
|
||||
{
|
||||
if( bot_respawn[index].is_used ) // is this slot used?
|
||||
{
|
||||
char cmd[40];
|
||||
|
||||
sprintf( cmd, "kick \"%s\"\n", bot_respawn[index].name );
|
||||
|
||||
bot_respawn[index].state = BOT_NEED_TO_RESPAWN;
|
||||
|
||||
SERVER_COMMAND( cmd ); // kick the bot using (kick "name")
|
||||
}
|
||||
}
|
||||
// END BOT
|
||||
|
||||
ALERT( at_console, "CHANGE LEVEL: %s\n", szNextMap );
|
||||
if( minplayers || maxplayers )
|
||||
{
|
||||
|
@ -2870,6 +2870,9 @@ void CBasePlayer::Spawn( void )
|
||||
|
||||
m_flNextChatTime = gpGlobals->time;
|
||||
|
||||
// START BOT
|
||||
pBotCam = NULL;
|
||||
// END BOT
|
||||
g_pGameRules->PlayerSpawn( this );
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,10 @@
|
||||
|
||||
#include "pm_materials.h"
|
||||
|
||||
//START BOT
|
||||
class CBotCam;
|
||||
//END BOT
|
||||
|
||||
#define PLAYER_FATAL_FALL_SPEED 1024// approx 60 feet
|
||||
#define PLAYER_MAX_SAFE_FALL_SPEED 580// approx 20 feet
|
||||
#define DAMAGE_FOR_FALL_SPEED (float) 100 / ( PLAYER_FATAL_FALL_SPEED - PLAYER_MAX_SAFE_FALL_SPEED )// damage per unit per second.
|
||||
@ -196,6 +200,10 @@ public:
|
||||
|
||||
char m_szTeamName[TEAM_NAME_LENGTH];
|
||||
|
||||
//START BOT
|
||||
CBotCam *pBotCam;
|
||||
//END BOT
|
||||
|
||||
virtual void Spawn( void );
|
||||
void Pain( void );
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user