Bot fixes.

This commit is contained in:
Justin Marshall
2026-05-20 09:10:15 -07:00
parent ed6b826d23
commit 31a3805769
9 changed files with 571 additions and 421 deletions
+112 -103
View File
@@ -398,49 +398,50 @@ itemconfig_t* idBotGoalManager::LoadItemConfig( char* filename )
idBotGoalManager::ItemWeightIndex
========================
*/
/*
================
Bot_ItemWeightAlias
Some itemconfig entries come from Quake 3 / Team Arena style bot data, but the
Doom 3 weight config usually does not contain every one of those classnames.
The item weight index must still point at a valid fuzzy weight, so we alias
unsupported / missing items to the closest useful existing Doom 3 weight.
================
*/
static const char* Bot_ItemWeightAlias(const char* classname) {
if (!classname || !classname[0]) {
static const char* Bot_ItemWeightAlias( const char* classname )
{
if( classname == NULL || classname[0] == '\0' )
{
return NULL;
}
struct itemWeightAlias_t {
struct itemWeightAlias_t
{
const char* missingName;
const char* fallbackName;
};
static const itemWeightAlias_t aliases[] = {
// Missing weapon names commonly seen in Q3 bot item configs.
{ "weapon_fists", "weapon_pistol" },
{ "weapon_grapplinghook", "weapon_chainsaw" },
{ "weapon_grenadelauncher", "weapon_rocketlauncher" },
{ "weapon_lightning", "weapon_plasmagun" },
{ "weapon_machinegun", "weapon_machinegun" },
{ "weapon_railgun", "weapon_rocketlauncher" },
{ "weapon_nailgun", "weapon_chaingun" },
{ "weapon_prox_launcher", "weapon_rocketlauncher" },
static const itemWeightAlias_t aliases[] =
{
{ "weapon_machinegun_mp", "weapon_machinegun" },
{ "weapon_shotgun_mp", "weapon_shotgun" },
{ "weapon_rocketlauncher_mp", "weapon_rocketlauncher" },
{ "weapon_plasmagun_mp", "weapon_plasmagun" },
{ "weapon_bfg_mp", "weapon_bfg" },
{ "weapon_grenadelauncher", "weapon_rocketlauncher" },
{ "weapon_grenadelauncher_mp", "weapon_rocketlauncher" },
{ "weapon_lightning", "weapon_plasmagun" },
{ "weapon_lightning_mp", "weapon_plasmagun" },
{ "weapon_railgun", "weapon_rocketlauncher" },
{ "weapon_railgun_mp", "weapon_rocketlauncher" },
{ "weapon_fists", "weapon_pistol" },
{ "weapon_grapplinghook", "weapon_chainsaw" },
{ "weapon_nailgun", "weapon_chaingun" },
{ "weapon_nailgun_mp", "weapon_chaingun" },
{ "weapon_prox_launcher", "weapon_rocketlauncher" },
{ "weapon_prox_launcher_mp", "weapon_rocketlauncher" },
// Team Arena / CTF objective entities. These are not normal pickup goals.
// Give them a valid low-risk fallback so the index is never invalid.
{ "team_redobelisk", "item_armor_shard" },
{ "team_blueobelisk", "item_armor_shard" },
{ "team_neutralobelisk", "item_armor_shard" },
{ "team_redobelisk", "item_armor_shard" },
{ "team_blueobelisk", "item_armor_shard" },
{ "team_neutralobelisk", "item_armor_shard" },
{ NULL, NULL }
};
for (int i = 0; aliases[i].missingName != NULL; i++) {
if (idStr::Icmp(classname, aliases[i].missingName) == 0) {
for( int i = 0; aliases[i].missingName != NULL; i++ )
{
if( idStr::Icmp( classname, aliases[i].missingName ) == 0 )
{
return aliases[i].fallbackName;
}
}
@@ -448,81 +449,84 @@ static const char* Bot_ItemWeightAlias(const char* classname) {
return NULL;
}
/*
================
idBotGoalManager::ItemWeightIndex
================
*/
int* idBotGoalManager::ItemWeightIndex(weightconfig_t* iwc, itemconfig_t* ic) {
int* index;
int i;
if (iwc == NULL || ic == NULL || ic->numiteminfo <= 0) {
return NULL;
static bool Bot_ItemClassnamesMatch( const char* entityClassname, const char* itemClassname )
{
if( entityClassname == NULL || itemClassname == NULL )
{
return false;
}
if( idStr::Icmp( entityClassname, itemClassname ) == 0 )
{
return true;
}
// initialize item weight index
index = new int[ic->numiteminfo]; // jmarshall: Get this off of the heap!
for (i = 0; i < ic->numiteminfo; i++) {
const char* classname = ic->iteminfo[i].classname.c_str();
index[i] = botFuzzyWeightManager.FindFuzzyWeight(iwc, (char*)classname);
if (index[i] < 0) {
const char* aliasName = Bot_ItemWeightAlias(classname);
if (aliasName != NULL) {
index[i] = botFuzzyWeightManager.FindFuzzyWeight(iwc, (char*)aliasName);
if (index[i] >= 0) {
common->Printf(
"Bot item weight alias: item info %d \"%s\" using \"%s\"\n",
i,
classname,
aliasName
);
continue;
}
}
// Last-resort fallback. This keeps the bot goal system from storing
// invalid fuzzy weight indexes even if the expected alias is missing.
static const char* safeFallbacks[] = {
"item_armor_shard",
"item_armor",
"ammo_clip",
"weapon_pistol",
"weapon_shotgun",
"weapon_machinegun",
NULL
};
for (int j = 0; safeFallbacks[j] != NULL; j++) {
index[i] = botFuzzyWeightManager.FindFuzzyWeight(iwc, (char*)safeFallbacks[j]);
if (index[i] >= 0) {
common->Printf(
"Bot item weight fallback: item info %d \"%s\" using \"%s\"\n",
i,
classname,
safeFallbacks[j]
);
break;
}
}
if (index[i] < 0) {
common->Warning(
"item info %d \"%s\" has no fuzzy weight and no fallback weight\r\n",
i,
classname
);
}
idStr normalizedItem = itemClassname;
if( idStr::Icmp( normalizedItem.Right( 3 ).c_str(), "_mp" ) == 0 )
{
normalizedItem.StripTrailing( "_mp" );
if( idStr::Icmp( entityClassname, normalizedItem.c_str() ) == 0 )
{
return true;
}
}
const char* aliasName = Bot_ItemWeightAlias( itemClassname );
return aliasName != NULL && idStr::Icmp( entityClassname, aliasName ) == 0;
}
int* idBotGoalManager::ItemWeightIndex( weightconfig_t* iwc, itemconfig_t* ic )
{
int* index;
if( iwc == NULL || ic == NULL || ic->numiteminfo <= 0 )
{
return NULL;
}
//initialize item weight index
index = new int[ic->numiteminfo]; // jmarshall: Get this off of the heap!
for( int i = 0; i < ic->numiteminfo; i++ )
{
const char* classname = ic->iteminfo[i].classname.c_str();
index[i] = botFuzzyWeightManager.FindFuzzyWeight( iwc, ( char* )classname );
if( index[i] < 0 )
{
const char* aliasName = Bot_ItemWeightAlias( classname );
if( aliasName != NULL )
{
index[i] = botFuzzyWeightManager.FindFuzzyWeight( iwc, ( char* )aliasName );
}
}
if( index[i] < 0 )
{
static const char* safeFallbacks[] =
{
"item_armor_shard",
"item_armor",
"ammo_clip",
"weapon_machinegun",
"weapon_shotgun",
NULL
};
for( int j = 0; safeFallbacks[j] != NULL; j++ )
{
index[i] = botFuzzyWeightManager.FindFuzzyWeight( iwc, ( char* )safeFallbacks[j] );
if( index[i] >= 0 )
{
break;
}
}
}
if( index[i] < 0 )
{
common->Warning( "item info %d \"%s\" has no fuzzy weight\r\n", i, classname );
}
}
return index;
}
/*
========================
idBotGoalManager::InitLevelItemHeap
@@ -552,7 +556,7 @@ levelitem_t* idBotGoalManager::AllocLevelItem( void )
li = freelevelitems;
if( !li )
{
gameLocal.Error( "out of level items\n" );
gameLocal.Warning( "out of bot level items, increase MAX_BOT_LEVEL_ITEMS\n" );
return NULL;
}
@@ -751,7 +755,7 @@ void idBotGoalManager::InitLevelItems( void )
for( i = 0; i < ic->numiteminfo; i++ )
{
if( !strcmp( classname, ic->iteminfo[i].classname ) )
if( Bot_ItemClassnamesMatch( classname.c_str(), ic->iteminfo[i].classname.c_str() ) )
{
break;
}
@@ -1517,11 +1521,16 @@ void idBotGoalManager::BotPushGoal( int goalstate, bot_goal_t* goal )
{
return;
}
if( gs->goalstacktop > 0 && gs->goalstack[gs->goalstacktop].number == goal->number )
{
gs->goalstack[gs->goalstacktop] = *goal;
return;
}
if( gs->goalstacktop >= MAX_GOALSTACK - 1 )
{
gameLocal.Error( "goal heap overflow\n" );
gameLocal.Warning( "bot goal stack overflow, clearing stale goals\n" );
BotDumpGoalStack( goalstate );
return;
gs->goalstacktop = 0;
}
gs->goalstacktop++;
gs->goalstack[gs->goalstacktop] = *goal;