Files
2026-05-19 19:43:23 -07:00

501 lines
11 KiB
C++

// Bot_weapons.cpp
//
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
idCVar bot_weaponsfile( "bot_weaponsfile", "weapons.c", CVAR_GAME | CVAR_CHEAT, "which file to load the weapons weights from" );
idBotWeaponInfoManager botWeaponInfoManager;
/*
=========================
idBotWeaponInfoManager::BotValidWeaponNumber
=========================
*/
int idBotWeaponInfoManager::BotValidWeaponNumber( int weaponnum )
{
if( weaponnum < 0 || weaponnum >= BOT_MAX_WEAPONS )
{
gameLocal.Error( "weapon number out of range\n" );
return false;
}
return true;
}
/*
=========================
idBotWeaponInfoManager::BotWeaponStateFromHandle
=========================
*/
bot_weaponstate_t* idBotWeaponInfoManager::BotWeaponStateFromHandle( int handle )
{
if( handle <= 0 || handle > MAX_CLIENTS )
{
gameLocal.Error( "move state handle %d out of range\n", handle );
return NULL;
}
return &botweaponstates[handle];
}
/*
========================
idBotWeaponInfoManager::ParseProjectileInfo
========================
*/
void idBotWeaponInfoManager::ParseProjectileInfo( idParser& parser, projectileinfo_t& newProjectileInfo )
{
idToken token;
idToken valueToken;
parser.ExpectTokenString( "{" );
while( true )
{
if( !parser.ReadToken( &token ) )
{
parser.Error( "Unexpected end of file found while parsing weaponinfo!" );
}
if( token == "}" )
{
break;
}
if( !parser.ReadToken( &valueToken ) )
{
parser.Error( "Failed to read value token, encountered EOF parsing weapon info!" );
}
if( token == "name" )
{
newProjectileInfo.name = valueToken; // name of the projectile
}
else if( token == "model" )
{
newProjectileInfo.model = valueToken; // model of the projectile
}
else if( token == "flags" )
{
newProjectileInfo.flags = valueToken.GetIntValue(); // special flags
}
else if( token == "gravity" )
{
newProjectileInfo.gravity = valueToken.GetFloatValue(); // amount of gravity applied to the projectile [0,1]
}
else if( token == "damage" )
{
newProjectileInfo.damage = valueToken.GetIntValue(); // damage of the projectile
}
else if( token == "radius" )
{
newProjectileInfo.radius = valueToken.GetFloatValue(); // radius of damage
}
else if( token == "visdamage" )
{
newProjectileInfo.visdamage = valueToken.GetIntValue(); // damage of the projectile to visible entities
}
else if( token == "damagetype" )
{
newProjectileInfo.damagetype = valueToken.GetIntValue(); // type of damage (combination of the DAMAGETYPE_? flags)
}
else if( token == "healthinc" )
{
newProjectileInfo.healthinc = valueToken.GetIntValue(); // health increase the owner gets
}
else if( token == "push" )
{
newProjectileInfo.push = valueToken.GetFloatValue(); // amount a player is pushed away from the projectile impact
}
else if( token == "detonation" )
{
newProjectileInfo.detonation = valueToken.GetFloatValue(); // time before projectile explodes after fire pressed
}
else if( token == "bounce" )
{
newProjectileInfo.bounce = valueToken.GetFloatValue(); // amount the projectile bounces
}
else if( token == "bouncefric" )
{
newProjectileInfo.bouncefric = valueToken.GetFloatValue(); // amount the bounce decreases per bounce
}
else if( token == "bouncestop" )
{
newProjectileInfo.bouncestop = valueToken.GetFloatValue(); // minimum bounce value before bouncing stops
}
else
{
parser.Error( "ParseProjectileInfo: Unexpected token %s\n", token.c_str() );
}
}
}
/*
========================
idBotWeaponInfoManager::ParseWeaponInfo
========================
*/
void idBotWeaponInfoManager::ParseWeaponInfo( idParser& parser, weaponinfo_t& newWeaponInfo )
{
idToken token;
idToken valueToken;
parser.ExpectTokenString( "{" );
while( true )
{
if( !parser.ReadToken( &token ) )
{
parser.Error( "Unexpected end of file found while parsing weaponinfo!" );
}
if( token == "}" )
{
break;
}
if( !parser.ReadToken( &valueToken ) )
{
parser.Error( "Failed to read value token, encountered EOF parsing weapon info!" );
}
if( token == "number" )
{
newWeaponInfo.number = valueToken.GetIntValue(); //weapon number
}
else if( token == "name" )
{
newWeaponInfo.name = valueToken; //name of the weapon
}
else if( token == "level" )
{
newWeaponInfo.level = valueToken.GetIntValue();
}
else if( token == "model" )
{
newWeaponInfo.model = valueToken; //model of the weapon
}
else if( token == "weaponindex" )
{
newWeaponInfo.weaponindex = valueToken.GetIntValue(); //index of weapon in inventory
}
else if( token == "flags" )
{
newWeaponInfo.flags = valueToken.GetIntValue(); //special flags
}
else if( token == "projectile" )
{
newWeaponInfo.projectile = valueToken; //projectile used by the weapon
}
else if( token == "numprojectiles" )
{
newWeaponInfo.numprojectiles = valueToken.GetIntValue(); //number of projectiles
}
else if( token == "hspread" )
{
newWeaponInfo.hspread = valueToken.GetFloatValue(); //horizontal spread of projectiles (degrees from middle)
}
else if( token == "vspread" )
{
newWeaponInfo.vspread = valueToken.GetFloatValue(); //vertical spread of projectiles (degrees from middle)
}
else if( token == "speed" )
{
newWeaponInfo.speed = valueToken.GetFloatValue(); //speed of the projectile (0 = instant hit)
}
else
{
parser.Error( "ParseWeaponInfo: Unexpected token %s\n", token.c_str() );
}
}
}
/*
========================
idBotWeaponInfoManager::LoadWeaponConfig
========================
*/
void idBotWeaponInfoManager::LoadWeaponConfig( char* filename )
{
idParser parser;
idToken token;
int j;
if( !parser.LoadFile( filename ) )
{
gameLocal.Error( "Failed to load bot weapon config %s\n", filename );
}
while( parser.ReadToken( &token ) )
{
if( token == "weaponinfo" )
{
weaponinfo_t newWeaponInfo;
ParseWeaponInfo( parser, newWeaponInfo );
if( newWeaponInfo.number < 0 || newWeaponInfo.number >= BOT_MAX_WEAPONS )
{
parser.Error( "weapon info number %d out of range\n", newWeaponInfo.number );
return;
}
weaponinfo[newWeaponInfo.number] = newWeaponInfo;
weaponinfo[newWeaponInfo.number].valid = true;
}
else if( token == "projectileinfo" )
{
projectileinfo_t newProjectileInfo;
ParseProjectileInfo( parser, newProjectileInfo );
projectileinfo.Append( newProjectileInfo );
}
else
{
parser.Error( "LoadWeaponConfig: Unknown definintion %s", token.c_str() );
}
}
// fix up weapons.
for( int i = 0; i < BOT_MAX_WEAPONS; i++ )
{
if( !weaponinfo[i].valid )
{
continue;
}
if( weaponinfo[i].name.Length() <= 0 )
{
parser.Error( "weapon %d has no name\n", i );
return;
}
if( weaponinfo[i].projectile.Length() <= 0 )
{
parser.Error( "weapon %s has no projectile\n", weaponinfo[i].name );
return;
}
//find the projectile info and copy it to the weapon info
for( j = 0; j < projectileinfo.Num(); j++ )
{
if( projectileinfo[j].name == weaponinfo[i].projectile )
{
memcpy( &weaponinfo[i].proj, &projectileinfo[j], sizeof( projectileinfo_t ) );
break;
}
}
if( j == projectileinfo.Num() )
{
parser.Error( "weapon %s uses undefined projectile\n", weaponinfo[i].name.c_str() );
return;
}
}
}
/*
========================
idBotWeaponInfoManager::Init
========================
*/
void idBotWeaponInfoManager::Init( void )
{
rvmScopedLexerBaseFolder scopedBaseFolder( BOTFILESBASEFOLDER );
LoadWeaponConfig( ( char* )bot_weaponsfile.GetString() );
}
/*
=====================
WeaponWeightIndex
=====================
*/
void idBotWeaponInfoManager::WeaponWeightIndex( weightconfig_t* wwc, bot_weaponstate_t* weaponState )
{
for( int i = 0; i < BOT_MAX_WEAPONS; i++ )
{
weaponState->weaponweightindex[i] = botFuzzyWeightManager.FindFuzzyWeight( wwc, ( char* )weaponinfo[i].name.c_str() );
}
}
/*
=====================
idBotWeaponInfoManager::WeaponWeightIndex
=====================
*/
void idBotWeaponInfoManager::BotFreeWeaponWeights( int weaponstate )
{
bot_weaponstate_t* ws;
ws = BotWeaponStateFromHandle( weaponstate );
if( !ws )
{
return;
}
// if (ws->weaponweightconfig)
// FreeWeightConfig(ws->weaponweightconfig);
// if (ws->weaponweightindex) FreeMemory(ws->weaponweightindex);
}
/*
=====================
BotLoadWeaponWeights
=====================
*/
int idBotWeaponInfoManager::BotLoadWeaponWeights( int weaponstate, char* filename )
{
bot_weaponstate_t* ws;
ws = BotWeaponStateFromHandle( weaponstate );
if( !ws )
{
return BLERR_CANNOTLOADWEAPONWEIGHTS;
}
BotFreeWeaponWeights( weaponstate );
ws->weaponweightconfig = botFuzzyWeightManager.ReadWeightConfig( filename );
if( !ws->weaponweightconfig )
{
gameLocal.Error( "couldn't load weapon config %s\n", filename );
return BLERR_CANNOTLOADWEAPONWEIGHTS;
}
WeaponWeightIndex( ws->weaponweightconfig, ws );
return BLERR_NOERROR;
}
/*
=====================
idBotWeaponInfoManager::BotGetWeaponInfo
=====================
*/
void idBotWeaponInfoManager::BotGetWeaponInfo( int weaponstate, int weapon, weaponinfo_t* weaponinfo )
{
if( weaponinfo == NULL )
{
return;
}
if( BotValidWeaponNumber( weapon ) && this->weaponinfo[weapon].valid )
{
*weaponinfo = this->weaponinfo[weapon];
return;
}
// Fallback: return the first loaded weapon so callers never keep an
// uninitialized weaponinfo_t when a bot has not picked a valid weapon yet.
for( int i = 0; i < BOT_MAX_WEAPONS; i++ )
{
if( this->weaponinfo[i].valid )
{
*weaponinfo = this->weaponinfo[i];
return;
}
}
}
/*
=====================
idBotWeaponInfoManager::BotChooseBestFightWeapon
=====================
*/
int idBotWeaponInfoManager::BotChooseBestFightWeapon( int weaponstate, int* inventory )
{
int i, index, bestweapon;
float weight, bestweight;
bot_weaponstate_t* ws;
ws = BotWeaponStateFromHandle( weaponstate );
if( !ws )
{
return 0;
}
//if the bot has no weapon weight configuration
if( !ws->weaponweightconfig )
{
return 0;
}
bestweight = 0;
bestweapon = 0;
for( i = 0; i < BOT_MAX_WEAPONS; i++ )
{
if( !weaponinfo[i].valid )
{
continue;
}
index = ws->weaponweightindex[i];
if( index < 0 )
{
continue;
}
weight = botFuzzyWeightManager.FuzzyWeight( inventory, ws->weaponweightconfig, index );
if( weight > bestweight )
{
bestweight = weight;
bestweapon = i;
}
}
return bestweapon;
}
/*
=====================
idBotWeaponInfoManager::BotResetWeaponState
=====================
*/
void idBotWeaponInfoManager::BotResetWeaponState( int weaponstate )
{
weightconfig_t* weaponweightconfig;
int* weaponweightindex;
bot_weaponstate_t* ws;
ws = BotWeaponStateFromHandle( weaponstate );
if( !ws )
{
return;
}
weaponweightconfig = ws->weaponweightconfig;
weaponweightindex = ws->weaponweightindex;
//Com_Memset(ws, 0, sizeof(bot_weaponstate_t));
ws->weaponweightconfig = weaponweightconfig;
memcpy( ws->weaponweightindex, weaponweightindex, sizeof( int ) * BOT_MAX_WEAPONS );
}
/*
=====================
idBotWeaponInfoManager::BotAllocWeaponState
=====================
*/
int idBotWeaponInfoManager::BotAllocWeaponState( void )
{
int i;
for( i = 1; i <= MAX_CLIENTS; i++ )
{
if( !botweaponstates[i].inUse )
{
botweaponstates[i].Reset();
botweaponstates[i].inUse = true;
return i;
}
}
return 0;
}
/*
=====================
idBotWeaponInfoManager::BotAllocWeaponState
=====================
*/
void idBotWeaponInfoManager::BotFreeWeaponState( int ws )
{
botweaponstates[ws].inUse = false;
botweaponstates[ws].Reset();
}