// 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 "monsters.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}}; typedef struct { const char *enemy_name; byte enemy_id; byte preferred_weapons_id[3]; } enemy_weapon_check_t; enemy_weapon_check_t enemy_weapon_check[] = { { "monster_heacrab", ENEMY_HEADCRAB, {WEAPON_GLOCK, WEAPON_CROWBAR, WEAPON_NONE} }, { "monster_zombie", ENEMY_ZOMBIE, {WEAPON_MP5, WEAPON_SHOTGUN, WEAPON_GLOCK} }, { "monster_bullsquid", ENEMY_BULLSQUID, {WEAPON_PYTHON, WEAPON_SHOTGUN, WEAPON_MP5} }, { "monster_aslave", ENEMY_ASLAVE, {WEAPON_MP5, WEAPON_SHOTGUN, WEAPON_GLOCK} }, { "monster_houndeye", ENEMY_HOUNDEYE, {WEAPON_GLOCK, WEAPON_CROWBAR, WEAPON_NONE} }, { "monster_alien_grunt", ENEMY_AGRUNT, {WEAPON_PYTHON, WEAPON_SHOTGUN, WEAPON_MP5} }, { "monster_human_grunt", ENEMY_HGRUNT, {WEAPON_MP5, WEAPON_SHOTGUN, WEAPON_PYTHON} }, { "monster_alien_controller", ENEMY_CONTROLLER, {WEAPON_PYTHON, WEAPON_MP5, WEAPON_SHOTGUN} }, { "monster_alien_flyer", ENEMY_FLYER, {WEAPON_RPG, WEAPON_NONE, WEAPON_NONE} }, { "unknown", ENEMY_UNKNOWN, {WEAPON_MP5, WEAPON_SHOTGUN, WEAPON_GLOCK} }, }; #define WEAPON_CHECK_COUNT 10 /* #define WEAPON_NONE 0 #define WEAPON_CROWBAR 1 #define WEAPON_GLOCK 2 #define WEAPON_PYTHON 3 #define WEAPON_MP5 4 #define WEAPON_CHAINGUN 5 #define WEAPON_CROSSBOW 6 #define WEAPON_SHOTGUN 7 #define WEAPON_RPG 8 #define WEAPON_GAUSS 9 #define WEAPON_EGON 10 #define WEAPON_HORNETGUN 11 #define WEAPON_HANDGRENADE 12 #define WEAPON_TRIPMINE 13 #define WEAPON_SATCHEL 14 #define WEAPON_SNARK 15 #define WEAPON_DISPLACER 16 */ // sounds for Bot taunting after a kill... char gina_taunt[][30] = { GI_TNT1, GI_TNT2, GI_TNT3, GI_TNT4, GI_TNT5 }; char colette_taunt[][30] = { CO_TNT1, CO_TNT2, CO_TNT3, CO_TNT4, CO_TNT5 }; bool BOT_VICTIM_EXISTS( CBaseEntity *victim, EOFFSET victimOffset) { CBaseEntity *pTest = CBaseEntity::Instance( victimOffset ); if ( pTest ) { if ( !FNullEnt( pTest->pev )) { if ( pTest->eoffset() == victim->eoffset() ) return true; else return false; } else return false; } else return false; } byte GetBotEnemyClassId( int classname ) { char szEnemyClassName[64]; strcpy( szEnemyClassName, STRING( classname ) ); for ( int i = 0; i < WEAPON_CHECK_COUNT; i++ ) { if ( stricmp( enemy_weapon_check[i].enemy_name, szEnemyClassName ) == 0 ) return enemy_weapon_check[i].enemy_id; } return ENEMY_UNKNOWN; } CBaseEntity * CBot::BotFindEnemy( void ) { Vector vecEnd; static BOOL flag=TRUE; char sound[128]; // for taunting sounds // does the bot already have an enemy? //if (pBotEnemy != NULL) //&& (pBotEnemy)) if ( pBotEnemy ) if ( BOT_VICTIM_EXISTS( pBotEnemy, pBotEnemyOffset ) ) // if (pBotEnemy->pev->deadflag == DEAD_NO) { 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 // speak taunt sounds about 10% of the time if (RANDOM_LONG(1, 100) <= 10) { if (bot_model == MODEL_GINA) strcpy( sound, gina_taunt[RANDOM_LONG(0,4)] ); else if (bot_model == MODEL_COLETTE) strcpy( sound, colette_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; pBotEnemyOffset = 0; } // <-- if dead or in botcam 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); } // <-- if alive and visible } int i; float nearestdistance = 1000; CBaseEntity *pNewEnemy = NULL; //=================================================================== // BOTMAN'S CODE FOR FINDING PLAYER ENEMY //=================================================================== /* // 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; 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 } } } */ //=================================================================== // END OF BOTMANS ENEMY SEARCHO CODE //=================================================================== int iDistance = 2048; CBaseEntity *pSightEnt = NULL;// the current visible entity that we're dealing with CBasePlayer *pPlayer = (CBasePlayer *)UTIL_PlayerByIndex( 1 ); if (!pPlayer) { ALERT( at_console, "Can not find player!\n" ); return NULL; } /* for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { pPlayer = UTIL_PlayerByIndex( i ); if ( pPlayer ) }*/ Vector delta = Vector( iDistance, iDistance, iDistance ); // Find only monsters/clients in box, NOT limited to PVS CBaseEntity *pList[100]; int count = UTIL_EntitiesInBox( pList, 100, pev->origin - delta, pev->origin + delta, /*FL_CLIENT|*/ FL_MONSTER ); for ( int i = 0; i < count; i++ ) { pSightEnt = pList[i]; // skip invalid players and skip self (i.e. this bot) if ((!pSightEnt) || (pSightEnt == this)) continue; // skip this player if not alive (i.e. dead or dying) if (pSightEnt->pev->deadflag != DEAD_NO) continue; if ( !FBitSet(pSightEnt->pev->flags, FL_MONSTER) ) continue; // !!!temporarily only considering other monsters and clients, don't see prisoners if ( //pSightEnt != this && !FBitSet( pSightEnt->pev->spawnflags, SF_MONSTER_PRISONER ) && pSightEnt->pev->health > 0 ) { vecEnd = pSightEnt->EyePosition(); // the looker will want to consider this entity // don't check anything else about an entity that can't be seen, or an entity that you don't care about. // OLD STYLE: if ( pPlayer->IRelationship( pSightEnt ) != R_NO && // if relationship not neutral //CBaseMonster *gNPC = (CBaseMonster*)pSightEnt; // NEW STYLE: //if ( gNPC->IRelationship( pPlayer ) > 0 && // if relationship is agressive FInViewCone( &vecEnd ) && // if we see it !FBitSet( pSightEnt->pev->flags, FL_NOTARGET ) && // if it can be targeted FVisible( pSightEnt->pev->origin ) // if it is visible ) { /* if ( pSightEnt->IsPlayer() ) { // do nothing (yet) } */ // see if bot can see the enemy... float distance = (pSightEnt->pev->origin - pev->origin).Length(); if (distance < nearestdistance) { nearestdistance = distance; pNewEnemy = pSightEnt; pBotUser = NULL; // don't follow user when enemy found } } } } // // END OF DECAY'S ENEMY SEARCH CODE // ****************************************************************** if (pNewEnemy) { pBotEnemyOffset = pNewEnemy->eoffset(); pBotEnemyClass = GetBotEnemyClassId( pNewEnemy->pev->classname ); // 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; } else { pBotEnemyOffset = 0; pBotEnemyClass = ENEMY_NONE; } 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; BOOL IsAboveWater = pev->waterlevel != 3; // 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<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< 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< 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< 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< 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< 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< 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< 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< 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< 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< 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< 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; if (!pBotEnemy) return; // 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 = pev->v_angle.z; // HOAXER'S TEST 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) { int iPreferredWeapon = 0; bool bPrimaryAttack = true; int iCurEnemyId = -1; if ( ( pBotEnemyClass != ENEMY_NONE ) && ( pBotEnemyClass != ENEMY_UNKNOWN ) ) { for (int i=0; i 200 ) ) bPrimaryAttack = false; } }; // select the best weapon to use at this distance and fire... BotFireWeapon( pBotEnemy->pev->origin, iPreferredWeapon, bPrimaryAttack ); } // HOAXER'S TEST (was uncommented) 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; }