mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-13 00:31:10 +02:00
501 lines
11 KiB
C++
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();
|
|
} |