1564 lines
54 KiB
Plaintext
1564 lines
54 KiB
Plaintext
|
|
#include common_scripts\utility;
|
|
#include maps\mp\_utility;
|
|
#include maps\mp\bots\_bots_ks;
|
|
|
|
SCR_CONST_base_perk_slots = 8;
|
|
SCR_CONST_MAX_CUSTOM_DEFAULT_LOADOUTS = 6; // See recipes.raw: CustomGameClass defaultClasses[ PlayerGroup ][ 6 ];
|
|
|
|
init()
|
|
{
|
|
// Initialize tables of loadouts per personality and per difficulty
|
|
init_template_table();
|
|
init_class_table();
|
|
init_perktable();
|
|
init_bot_weap_statstable();
|
|
init_bot_attachmenttable();
|
|
init_bot_camotable();
|
|
|
|
// Needed to ensure we're not trying to set bot loadouts before they have been parsed
|
|
level.bot_loadouts_initialized = true;
|
|
}
|
|
|
|
init_class_table()
|
|
{
|
|
filename = "mp/botClassTable.csv";
|
|
|
|
level.botLoadoutSets = [];
|
|
fieldArray = bot_loadout_fields();
|
|
column = 0;
|
|
|
|
for(;;)
|
|
{
|
|
column++;
|
|
|
|
strPers = tableLookup( filename, 0, "botPersonalities", column );
|
|
strDiff = tableLookup( filename, 0, "botDifficulties", column );
|
|
|
|
if ( !isDefined( strPers ) || (strPers == "") )
|
|
break;
|
|
|
|
if ( !isDefined( strDiff ) || (strDiff == "") )
|
|
break;
|
|
|
|
loadoutValues = [];
|
|
foreach ( field in fieldArray )
|
|
{
|
|
loadoutValues[field] = tableLookup( filename, 0, field, column );
|
|
/#
|
|
assert_field_valid( field, loadoutValues[field] );
|
|
#/
|
|
}
|
|
|
|
personalities = StrTok( strPers, "| " );
|
|
difficulties = StrTok( strDiff, "| " );
|
|
|
|
foreach ( personality in personalities )
|
|
{
|
|
foreach ( difficulty in difficulties )
|
|
{
|
|
loadoutSet = bot_loadout_set( personality, difficulty, true );
|
|
loadout = SpawnStruct();
|
|
loadout.loadoutValues = loadoutValues;
|
|
loadoutSet.loadouts[loadoutSet.loadouts.size] = loadout;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
init_template_table()
|
|
{
|
|
filename = "mp/botTemplateTable.csv";
|
|
|
|
level.botLoadoutTemplates = [];
|
|
fieldArray = bot_loadout_fields();
|
|
column = 0;
|
|
|
|
for(;;)
|
|
{
|
|
column++;
|
|
|
|
strTemplateName = tableLookup( filename, 0, "template_", column );
|
|
|
|
if ( !isDefined( strTemplateName ) || (strTemplateName == "") )
|
|
break;
|
|
|
|
strTemplateLookupName = "template_" + strTemplateName;
|
|
|
|
level.botLoadoutTemplates[strTemplateLookupName] = [];
|
|
|
|
foreach ( field in fieldArray )
|
|
{
|
|
entry = tableLookup( filename, 0, field, column );
|
|
if ( IsDefined( entry ) && entry != "" )
|
|
{
|
|
level.botLoadoutTemplates[strTemplateLookupName][field] = entry;
|
|
/#
|
|
assert_field_valid( field, entry );
|
|
#/
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
=============
|
|
///ScriptDocBegin
|
|
"Name: bot_loadout_item_allowed( <type>, <item>, <streakType> )"
|
|
"Summary: Checks if a loadout item is restricted in the current match rules (returns true if not restricted)"
|
|
"MandatoryArg: <type> : The type of item ("weapon", "attachment", "killstreak", "perk")"
|
|
"MandatoryArg: <item> : The specific item"
|
|
"OptionalArg: <streakType> : Required when validating a killstreak"
|
|
"Example: bot_loadout_item_allowed( "weapon", "iw6_fads" );"
|
|
///ScriptDocEnd
|
|
============
|
|
*/
|
|
bot_loadout_item_allowed( type, item, streakType )
|
|
{
|
|
if ( !IsUsingMatchRulesData() )
|
|
return true;
|
|
|
|
if ( !GetMatchRulesData( "commonOption", "allowCustomClasses" ) )
|
|
return true;
|
|
|
|
if ( item == "specialty_null" )
|
|
return true;
|
|
|
|
if ( item == "none" )
|
|
return true;
|
|
|
|
if ( type == "equipment" )
|
|
{
|
|
// First test the equipment as a perk (since tacticals / lethals are in the perk enum in recipes.def for some reason)
|
|
if ( GetMatchRulesData( "commonOption", "perkRestricted", item ) )
|
|
return false;
|
|
|
|
// If it doesn't fail the perk test, then test it again as a weapon (since they're also listed in the Weapon enum)
|
|
type = "weapon";
|
|
}
|
|
|
|
itemRuleName = type + "Restricted";
|
|
classRuleName = type + "ClassRestricted";
|
|
class = "";
|
|
|
|
switch ( type )
|
|
{
|
|
case "weapon":
|
|
class = getWeaponClass( item );
|
|
break;
|
|
case "attachment":
|
|
class = getAttachmentType( item );
|
|
break;
|
|
case "killstreak":
|
|
assert( IsDefined( streakType ) );
|
|
class = streakType;
|
|
break;
|
|
case "perk":
|
|
assert( IsDefined( level.bot_perktypes[item] ) );
|
|
class = "ability_" + level.bot_perktypes[item];
|
|
break;
|
|
default:
|
|
AssertEx( 0, "bot_loadout_item_allowed() - Unsupported loadout type '" + type + "'" );
|
|
return false;
|
|
}
|
|
|
|
if ( GetMatchRulesData( "commonOption", itemRuleName, item ) )
|
|
return false;
|
|
|
|
if ( GetMatchRulesData( "commonOption", classRuleName, class ) )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bot_loadout_choose_fallback_primary( loadoutValueArray )
|
|
{
|
|
weapon = "none";
|
|
|
|
// First try to pick primary weapon randomly from all difficulties with my personality
|
|
difficulties = [ "veteran", "hardened", "regular", "recruit" ];
|
|
difficulties = array_randomize( difficulties );
|
|
foreach( difficulty in difficulties )
|
|
{
|
|
weapon = bot_loadout_choose_from_statstable( "weap_statstable", loadoutValueArray, "loadoutPrimary", self.personality, difficulty );
|
|
if ( weapon != "none" )
|
|
return weapon;
|
|
}
|
|
|
|
// Then try to pick primary weapon randomly from all personalities and difficulties
|
|
if ( IsDefined( level.bot_personality_list ) )
|
|
{
|
|
personalities = array_randomize( level.bot_personality_list );
|
|
foreach( personality in personalities )
|
|
{
|
|
foreach( difficulty in difficulties )
|
|
{
|
|
weapon = bot_loadout_choose_from_statstable( "weap_statstable", loadoutValueArray, "loadoutPrimary", personality, difficulty );
|
|
if ( weapon != "none" )
|
|
{
|
|
self.bot_fallback_personality = personality;
|
|
return weapon;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Still no primary weapon found, pick first one available in custom default classes
|
|
if ( IsUsingMatchRulesData() )
|
|
{
|
|
index = 0;
|
|
while( index < SCR_CONST_MAX_CUSTOM_DEFAULT_LOADOUTS && (!IsDefined( weapon ) || weapon == "none" || weapon == "") )
|
|
{
|
|
if ( getMatchRulesData( "defaultClasses", self.team, index, "class", "inUse" ) )
|
|
{
|
|
weapon = getMatchRulesData( "defaultClasses", self.team, index, "class", "weaponSetups", 0, "weapon" );
|
|
if ( weapon != "none" )
|
|
{
|
|
self.bot_fallback_personality = "weapon";
|
|
return weapon;
|
|
}
|
|
}
|
|
index++;
|
|
}
|
|
}
|
|
|
|
// Still no primary weapon found, pick level.bot_fallback_weapon (iw6_knifeonly)
|
|
self.bot_fallback_personality = "weapon";
|
|
return level.bot_fallback_weapon;
|
|
}
|
|
|
|
bot_pick_personality_from_weapon( weapon ) // self = bot player
|
|
{
|
|
if ( IsDefined( weapon ) )
|
|
{
|
|
weapPers = level.bot_weap_personality[weapon];
|
|
if ( IsDefined( weapPers ) )
|
|
{
|
|
personalityChoices = StrTok( weapPers, "| " );
|
|
if ( personalityChoices.size > 0 )
|
|
self maps\mp\bots\_bots_util::bot_set_personality( random( personalityChoices ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
/#
|
|
assert_field_valid( field, value )
|
|
{
|
|
// Make sure the killstreaks loaded are valid in general for bots
|
|
if ( field == "loadoutStreak1" || field == "loadoutStreak2" || field == "loadoutStreak3" )
|
|
{
|
|
killstreaks = StrTok( value, "| " );
|
|
foreach( streak in killstreaks )
|
|
{
|
|
if ( streak != "none" && GetSubStr( streak, 0, 9 ) != "template_" && GetSubStr( streak, 0, 5 ) != "class" )
|
|
assert_streak_valid_for_bots_in_general(streak);
|
|
}
|
|
}
|
|
}
|
|
#/
|
|
|
|
bot_loadout_fields()
|
|
{
|
|
result = [];
|
|
result[result.size] = "loadoutPrimary";
|
|
result[result.size] = "loadoutPrimaryBuff";
|
|
result[result.size] = "loadoutPrimaryAttachment";
|
|
result[result.size] = "loadoutPrimaryAttachment2";
|
|
result[result.size] = "loadoutPrimaryCamo";
|
|
result[result.size] = "loadoutPrimaryReticle";
|
|
result[result.size] = "loadoutSecondary";
|
|
result[result.size] = "loadoutSecondaryBuff";
|
|
result[result.size] = "loadoutSecondaryAttachment";
|
|
result[result.size] = "loadoutSecondaryAttachment2";
|
|
result[result.size] = "loadoutSecondaryCamo";
|
|
result[result.size] = "loadoutSecondaryReticle";
|
|
result[result.size] = "loadoutEquipment";
|
|
result[result.size] = "loadoutOffhand";
|
|
result[result.size] = "loadoutStreakType";
|
|
result[result.size] = "loadoutStreak1";
|
|
result[result.size] = "loadoutStreak2";
|
|
result[result.size] = "loadoutStreak3";
|
|
result[result.size] = "loadoutPerk1";
|
|
result[result.size] = "loadoutPerk2";
|
|
result[result.size] = "loadoutPerk3";
|
|
result[result.size] = "loadoutPerk4";
|
|
result[result.size] = "loadoutPerk5";
|
|
result[result.size] = "loadoutPerk6";
|
|
result[result.size] = "loadoutPerk7";
|
|
result[result.size] = "loadoutPerk8";
|
|
result[result.size] = "loadoutPerk9";
|
|
result[result.size] = "loadoutPerk10";
|
|
result[result.size] = "loadoutPerk11";
|
|
result[result.size] = "loadoutPerk12";
|
|
result[result.size] = "loadoutPerk13";
|
|
result[result.size] = "loadoutPerk14";
|
|
result[result.size] = "loadoutPerk15";
|
|
result[result.size] = "loadoutPerk16";
|
|
result[result.size] = "loadoutPerk17";
|
|
result[result.size] = "loadoutPerk18";
|
|
result[result.size] = "loadoutPerk19";
|
|
result[result.size] = "loadoutPerk20";
|
|
result[result.size] = "loadoutPerk21";
|
|
result[result.size] = "loadoutPerk22";
|
|
result[result.size] = "loadoutPerk23";
|
|
return result;
|
|
}
|
|
|
|
bot_loadout_set( personality, difficulty, createIfNeeded )
|
|
{
|
|
setName = difficulty + "_" + personality;
|
|
|
|
if ( !isDefined( level.botLoadoutSets ) )
|
|
level.botLoadoutSets = [];
|
|
|
|
if ( !isDefined( level.botLoadoutSets[setName] ) && createIfNeeded )
|
|
{
|
|
level.botLoadoutSets[setName] = SpawnStruct();
|
|
level.botLoadoutSets[setName].loadouts = [];
|
|
}
|
|
|
|
if ( isDefined( level.botLoadoutSets[setName] ) )
|
|
return level.botLoadoutSets[setName];
|
|
}
|
|
|
|
bot_loadout_pick( personality, difficulty )
|
|
{
|
|
loadoutSet = bot_loadout_set( personality, difficulty, false );
|
|
if ( IsDefined( loadoutSet ) && isDefined( loadoutSet.loadouts ) && loadoutSet.loadouts.size > 0 )
|
|
{
|
|
loadoutChoice = RandomInt( loadoutSet.loadouts.size );
|
|
return loadoutSet.loadouts[loadoutChoice].loadoutValues;
|
|
}
|
|
}
|
|
|
|
bot_validate_weapon( weaponName, attachment, attachment2, attachment3 )
|
|
{
|
|
validAttachments = getWeaponAttachmentArrayFromStats( weaponName );
|
|
|
|
if ( IsDefined( attachment ) && attachment != "none" && !bot_loadout_item_allowed( "attachment", attachment ) )
|
|
return false;
|
|
|
|
if ( IsDefined( attachment2 ) && attachment2 != "none" && !bot_loadout_item_allowed( "attachment", attachment2 ) )
|
|
return false;
|
|
|
|
if ( IsDefined( attachment3 ) && attachment3 != "none" && !bot_loadout_item_allowed( "attachment", attachment3 ) )
|
|
return false;
|
|
|
|
if ( attachment != "none" && !array_contains( validAttachments, attachment ) )
|
|
return false;
|
|
|
|
if ( attachment2 != "none" && !array_contains( validAttachments, attachment2 ) )
|
|
return false;
|
|
|
|
if ( IsDefined( attachment3 ) && attachment3 != "none" && !array_contains( validAttachments, attachment3 ) )
|
|
return false;
|
|
|
|
if ( (attachment == "none" || attachment2 == "none") && (!IsDefined( attachment3 ) || attachment3 == "none") )
|
|
return true;
|
|
|
|
// Testing a combination of attachments
|
|
|
|
if ( !IsDefined( level.bot_invalid_attachment_combos ) )
|
|
{
|
|
level.bot_invalid_attachment_combos = [];
|
|
level.allowable_double_attachments = []; // Attachments that are allowed twice
|
|
|
|
attachmentComboTable = "mp/attachmentcombos.csv";
|
|
column = 0;
|
|
while(1)
|
|
{
|
|
column++;
|
|
currentAttachment = TableLookupByRow( attachmentComboTable, 0, column );
|
|
if ( currentAttachment == "" )
|
|
break;
|
|
|
|
row = 0;
|
|
while(1)
|
|
{
|
|
row++;
|
|
attachmentTesting = TableLookupByRow( attachmentComboTable, row, 0 );
|
|
if ( attachmentTesting == "" )
|
|
break;
|
|
|
|
if ( attachmentTesting == currentAttachment )
|
|
{
|
|
if ( TableLookupByRow( attachmentComboTable, row, column ) != "no" )
|
|
level.allowable_double_attachments[attachmentTesting] = true;
|
|
}
|
|
else
|
|
{
|
|
if ( TableLookupByRow( attachmentComboTable, row, column ) == "no" )
|
|
level.bot_invalid_attachment_combos[currentAttachment][attachmentTesting] = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( attachment == attachment2 && !IsDefined(level.allowable_double_attachments[attachment]) )
|
|
return false;
|
|
|
|
if ( IsDefined( attachment3 ) )
|
|
{
|
|
if ( attachment2 == attachment3 && !IsDefined(level.allowable_double_attachments[attachment2]) )
|
|
return false;
|
|
|
|
if ( attachment == attachment3 && !IsDefined(level.allowable_double_attachments[attachment]) )
|
|
return false;
|
|
|
|
if ( attachment3 != "none" && attachment == attachment3 && attachment2 == attachment3 )
|
|
return false;
|
|
|
|
if ( IsDefined(level.bot_invalid_attachment_combos[attachment2]) && IsDefined(level.bot_invalid_attachment_combos[attachment2][attachment3]) )
|
|
return false;
|
|
|
|
if ( IsDefined(level.bot_invalid_attachment_combos[attachment]) && IsDefined(level.bot_invalid_attachment_combos[attachment][attachment3]) )
|
|
return false;
|
|
}
|
|
|
|
return !(IsDefined( level.bot_invalid_attachment_combos[attachment] ) && IsDefined(level.bot_invalid_attachment_combos[attachment][attachment2]) );
|
|
}
|
|
|
|
bot_validate_reticle( loadoutBaseName, loadoutValueArray, choice )
|
|
{
|
|
if ( isDefined( loadoutValueArray[loadoutBaseName + "Attachment"] ) && IsDefined( level.bot_attachment_reticle[loadoutValueArray[loadoutBaseName + "Attachment"]] ) )
|
|
return true;
|
|
if ( isDefined( loadoutValueArray[loadoutBaseName + "Attachment2"] ) && IsDefined( level.bot_attachment_reticle[loadoutValueArray[loadoutBaseName + "Attachment2"]] ) )
|
|
return true;
|
|
if ( isDefined( loadoutValueArray[loadoutBaseName + "Attachment3"] ) && IsDefined( level.bot_attachment_reticle[loadoutValueArray[loadoutBaseName + "Attachment3"]] ) )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bot_perk_cost( perkName )
|
|
{
|
|
Assert( IsDefined(level.perktable_costs[perkName]) );
|
|
return level.perktable_costs[perkName];
|
|
}
|
|
|
|
perktable_add( perkName, perkType )
|
|
{
|
|
Assert( perkName != "" );
|
|
if ( bot_perk_cost( perkName ) > 0 )
|
|
{
|
|
perk = [];
|
|
perk["type"] = perkType;
|
|
perk["name"] = perkName;
|
|
|
|
level.bot_perktable[level.bot_perktable.size] = perk;
|
|
|
|
level.bot_perktypes[perkName] = perkType;
|
|
}
|
|
}
|
|
|
|
init_perktable()
|
|
{
|
|
level.perktable_costs = [];
|
|
row = 1;
|
|
while(1)
|
|
{
|
|
perkName = TableLookupByRow( "mp/perktable.csv", row, 1 );
|
|
if ( perkName == "" )
|
|
break;
|
|
|
|
level.perktable_costs[perkName] = int(TableLookupByRow( "mp/perktable.csv", row, 10 ));
|
|
row++;
|
|
}
|
|
level.perktable_costs["none"] = 0;
|
|
level.perktable_costs["specialty_null"] = 0;
|
|
|
|
level.bot_perktable = [];
|
|
level.bot_perktypes = [];
|
|
|
|
row = 1;
|
|
abilityType = "ability_null";
|
|
|
|
while ( IsDefined( abilityType ) && (abilityType != "") )
|
|
{
|
|
// Strip the "ability_" from the abilityType
|
|
abilityType = GetSubStr( abilityType, 8 );
|
|
|
|
for ( col = 4; col <= 13; col++ )
|
|
{
|
|
perkName = TableLookupByRow( "mp/cacabilitytable.csv", row, col );
|
|
if ( perkName != "" )
|
|
perktable_add( perkName, abilityType );
|
|
}
|
|
|
|
row++;
|
|
abilityType = TableLookupByRow( "mp/cacabilitytable.csv", row, 1 );
|
|
}
|
|
}
|
|
|
|
init_bot_weap_statstable()
|
|
{
|
|
fileName = "mp/statstable.csv";
|
|
colName = 4;
|
|
colPers = 37;
|
|
colDiff = 38;
|
|
|
|
level.bot_weap_statstable = [];
|
|
level.bot_weap_personality = [];
|
|
|
|
// level.bot_weap_statstable[loadoutValueName][self.personality][self.difficulty]
|
|
|
|
row = 1;
|
|
while(1)
|
|
{
|
|
weapName = TableLookupByRow( fileName, row, colName );
|
|
|
|
if ( weapName == "specialty_null" )
|
|
break;
|
|
|
|
weapDiff = TableLookupByRow( fileName, row, colDiff );
|
|
weapPers = TableLookupByRow( fileName, row, colPers );
|
|
|
|
if ( weapName != "" && weapPers != "" )
|
|
level.bot_weap_personality[weapName] = weapPers;
|
|
|
|
if ( weapDiff != "" && weapName != "" && weapPers != "" )
|
|
{
|
|
slotType = "loadoutPrimary";
|
|
if ( maps\mp\gametypes\_class::isValidSecondary( weapName, false ) )
|
|
{
|
|
slotType = "loadoutSecondary";
|
|
}
|
|
else if ( !maps\mp\gametypes\_class::isValidPrimary( weapName, false ) )
|
|
{
|
|
row++;
|
|
continue;
|
|
}
|
|
|
|
if ( !IsDefined( level.bot_weap_statstable[slotType] ) )
|
|
level.bot_weap_statstable[slotType] = [];
|
|
|
|
persList = StrTok( weapPers, "| " );
|
|
diffList = StrTok( weapDiff, "| " );
|
|
|
|
foreach ( personality in persList )
|
|
{
|
|
if ( !IsDefined( level.bot_weap_statstable[slotType][personality] ) )
|
|
level.bot_weap_statstable[slotType][personality] = [];
|
|
|
|
foreach ( difficulty in diffList )
|
|
{
|
|
if ( !IsDefined( level.bot_weap_statstable[slotType][personality][difficulty] ) )
|
|
level.bot_weap_statstable[slotType][personality][difficulty] = [];
|
|
|
|
newIndex = level.bot_weap_statstable[slotType][personality][difficulty].size;
|
|
level.bot_weap_statstable[slotType][personality][difficulty][newIndex] = weapName;
|
|
}
|
|
}
|
|
}
|
|
|
|
row++;
|
|
}
|
|
}
|
|
|
|
bot_loadout_choose_from_statstable( loadoutValue, loadoutValueArray, loadoutValueName, personality, difficulty )
|
|
{
|
|
// Set fallbacks in case nothing is found that matches
|
|
result = "specialty_null";
|
|
if ( loadoutValueName == "loadoutPrimary" )
|
|
result = "iw6_honeybadger";
|
|
else if ( loadoutValueName == "loadoutSecondary" )
|
|
result = "iw6_p226";
|
|
|
|
if ( personality == "default" )
|
|
personality = "run_and_gun";
|
|
|
|
if ( loadoutValueName == "loadoutSecondary" && array_contains( loadoutValueArray, "specialty_twoprimaries" ) )
|
|
loadoutValueName = "loadoutPrimary";
|
|
|
|
if ( !IsDefined( level.bot_weap_statstable ) )
|
|
return result;
|
|
|
|
if ( !IsDefined( level.bot_weap_statstable[loadoutValueName] ) )
|
|
return result;
|
|
|
|
if ( !IsDefined( level.bot_weap_statstable[loadoutValueName][personality] ) )
|
|
return result;
|
|
|
|
if ( !IsDefined( level.bot_weap_statstable[loadoutValueName][personality][difficulty] ) )
|
|
return result;
|
|
|
|
result = bot_loadout_choose_from_set( level.bot_weap_statstable[loadoutValueName][personality][difficulty], loadoutValue, loadoutValueArray, loadoutValueName );
|
|
|
|
return result;
|
|
}
|
|
|
|
bot_loadout_choose_from_perktable( choice, loadoutValue, loadoutValueArray, loadoutValueName, personality, difficulty )
|
|
{
|
|
// Set fallbacks in case nothing is found that matches
|
|
result = "specialty_null";
|
|
|
|
if ( !IsDefined( level.bot_perktable ) )
|
|
return result;
|
|
|
|
if ( !IsDefined( level.bot_perktable_groups ) )
|
|
level.bot_perktable_groups = [];
|
|
|
|
if ( !IsDefined( level.bot_perktable_groups[choice] ) )
|
|
{
|
|
types = StrTok( choice, "_" );
|
|
assert( types[0] == "perktable" );
|
|
types[0] = "";
|
|
any = false;
|
|
if ( array_contains( types, "any" ) )
|
|
any = true;
|
|
|
|
choices = [];
|
|
|
|
foreach ( perk in level.bot_perktable )
|
|
{
|
|
if ( any || array_contains( types, perk["type"] ) )
|
|
choices[choices.size] = perk["name"];
|
|
}
|
|
|
|
level.bot_perktable_groups[choice] = choices;
|
|
}
|
|
|
|
if ( level.bot_perktable_groups[choice].size > 0 )
|
|
result = bot_loadout_choose_from_set( level.bot_perktable_groups[choice], loadoutValue, loadoutValueArray, loadoutValueName );
|
|
|
|
return result;
|
|
}
|
|
|
|
bot_validate_perk( choice, loadoutValueName, loadoutValueArray, costRangeStart, costRangeEnd, costAllowed )
|
|
{
|
|
totalCostAllowed = (costRangeEnd - costRangeStart) + 1;
|
|
if ( IsDefined( costAllowed ) )
|
|
totalCostAllowed = costAllowed;
|
|
allocatedCost = 0;
|
|
|
|
// Name will always be "loadoutPerkN[N][N]" from 1 to N
|
|
index = int( GetSubStr( loadoutValueName, 11 ) );
|
|
|
|
// TODO : Remove these when Eric Su says its ok to use these perks
|
|
if ( choice == "specialty_twoprimaries" )
|
|
return false;
|
|
if ( choice == "specialty_extra_attachment" )
|
|
return false;
|
|
|
|
if ( !bot_loadout_item_allowed( "perk", choice ) )
|
|
return false;
|
|
|
|
for ( i = index - 1; i > 0; i-- )
|
|
{
|
|
prevPerkName = "loadoutPerk" + i;
|
|
|
|
if ( loadoutValueArray[prevPerkName] == "none" || loadoutValueArray[prevPerkName] == "specialty_null" )
|
|
continue;
|
|
|
|
// Make sure we havent picked the same perk twice
|
|
if ( choice == loadoutValueArray[prevPerkName] )
|
|
return false;
|
|
|
|
// Keep track of total perk costs spent for the range we are looking for
|
|
if ( (i >= costRangeStart) && (i <= costRangeEnd) )
|
|
allocatedCost += bot_perk_cost( loadoutValueArray[prevPerkName] );
|
|
}
|
|
|
|
// Make sure this choice can be afforded with rest of choices
|
|
if ( (allocatedCost + bot_perk_cost( choice )) > totalCostAllowed )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bot_loadout_choose_from_default_class( class, loadoutValue, loadoutValueArray, loadoutValueName, personality, difficulty )
|
|
{
|
|
class_num = int(GetSubStr( class, 5, 6 )) - 1;
|
|
|
|
switch ( loadoutValueName )
|
|
{
|
|
case "loadoutPrimary":
|
|
return maps\mp\gametypes\_class::table_getWeapon( level.classTableName, class_num, 0 );
|
|
case "loadoutPrimaryAttachment":
|
|
return maps\mp\gametypes\_class::table_getWeaponAttachment( level.classTableName, class_num, 0, 0);
|
|
case "loadoutPrimaryAttachment2":
|
|
return maps\mp\gametypes\_class::table_getWeaponAttachment( level.classTableName, class_num, 0, 1 );
|
|
case "loadoutPrimaryBuff":
|
|
return maps\mp\gametypes\_class::table_getWeaponBuff( level.classTableName, class_num, 0 );
|
|
case "loadoutPrimaryCamo":
|
|
return maps\mp\gametypes\_class::table_getWeaponCamo( level.classTableName, class_num, 0 );
|
|
case "loadoutPrimaryReticle":
|
|
return maps\mp\gametypes\_class::table_getWeaponReticle( level.classTableName, class_num, 0 );
|
|
case "loadoutSecondary":
|
|
return maps\mp\gametypes\_class::table_getWeapon( level.classTableName, class_num, 1 );
|
|
case "loadoutSecondaryAttachment":
|
|
return maps\mp\gametypes\_class::table_getWeaponAttachment( level.classTableName, class_num, 1, 0);
|
|
case "loadoutSecondaryAttachment2":
|
|
return maps\mp\gametypes\_class::table_getWeaponAttachment( level.classTableName, class_num, 1, 1 );
|
|
case "loadoutSecondaryBuff":
|
|
return maps\mp\gametypes\_class::table_getWeaponBuff( level.classTableName, class_num, 1 );
|
|
case "loadoutSecondaryCamo":
|
|
return maps\mp\gametypes\_class::table_getWeaponCamo( level.classTableName, class_num, 1 );
|
|
case "loadoutSecondaryReticle":
|
|
return maps\mp\gametypes\_class::table_getWeaponReticle( level.classTableName, class_num, 1 );
|
|
case "loadoutEquipment":
|
|
return maps\mp\gametypes\_class::table_getEquipment( level.classTableName, class_num, 0 );
|
|
case "loadoutOffhand":
|
|
return maps\mp\gametypes\_class::table_getOffhand( level.classTableName, class_num, 0 );
|
|
case "loadoutStreak1":
|
|
return maps\mp\gametypes\_class::table_getKillstreak( level.classTableName, class_num, 0 );
|
|
case "loadoutStreak2":
|
|
return maps\mp\gametypes\_class::table_getKillstreak( level.classTableName, class_num, 1 );
|
|
case "loadoutStreak3":
|
|
return maps\mp\gametypes\_class::table_getKillstreak( level.classTableName, class_num, 2 );
|
|
case "loadoutPerk1":
|
|
case "loadoutPerk2":
|
|
case "loadoutPerk3":
|
|
case "loadoutPerk4":
|
|
case "loadoutPerk5":
|
|
case "loadoutPerk6":
|
|
{
|
|
perkIndex = int( GetSubStr( loadoutValueName, 11 ) );
|
|
perk = maps\mp\gametypes\_class::table_getPerk( level.classTableName, class_num, perkIndex );
|
|
if ( perk == "" )
|
|
return "specialty_null";
|
|
perkRow = int( GetSubStr( perk, 0, 1 ) );
|
|
perkCol = int( GetSubStr( perk, 1, 2 ) );
|
|
perkName = TableLookupByRow( "mp/cacabilitytable.csv", perkRow + 1, perkCol + 3 );
|
|
return perkName;
|
|
}
|
|
}
|
|
|
|
AssertMsg( "Loadout type '" + loadoutValueName + "' is not supported for bot_loadout_choose_from_default_class('" + class + "')" );
|
|
|
|
return class;
|
|
}
|
|
|
|
init_bot_attachmenttable()
|
|
{
|
|
fileName = "mp/attachmenttable.csv";
|
|
colName = 5;
|
|
colDiff = 19;
|
|
colReticle = 11;
|
|
|
|
level.bot_attachmenttable = [];
|
|
level.bot_attachment_reticle = [];
|
|
|
|
/#
|
|
level.bot_att_cross_reference = [];
|
|
#/
|
|
|
|
row = 1;
|
|
while(1)
|
|
{
|
|
attachmentName = TableLookupByRow( fileName, row, colName );
|
|
|
|
if ( attachmentName == "done" )
|
|
break;
|
|
|
|
attachmentDiff = TableLookupByRow( fileName, row, colDiff );
|
|
|
|
if ( attachmentName != "" && attachmentDiff != "" )
|
|
{
|
|
/#
|
|
if ( IsDefined( level.bot_att_cross_reference[attachmentName] ) && level.bot_att_cross_reference[attachmentName] != attachmentDiff )
|
|
AssertMsg( "base ref '" + attachmentName + "' is associated with conflicting Bot Difficulty in attachmenttable.csv" );
|
|
level.bot_att_cross_reference[attachmentName] = attachmentDiff;
|
|
#/
|
|
|
|
attachmentReticle = TableLookupByRow( fileName, row, colReticle );
|
|
if ( attachmentReticle == "TRUE" )
|
|
level.bot_attachment_reticle[attachmentName] = true;
|
|
|
|
diffList = StrTok( attachmentDiff, "| " );
|
|
|
|
foreach ( difficulty in diffList )
|
|
{
|
|
if ( !IsDefined( level.bot_attachmenttable[difficulty] ) )
|
|
level.bot_attachmenttable[difficulty] = [];
|
|
|
|
if ( !array_contains( level.bot_attachmenttable[difficulty], attachmentName ) )
|
|
{
|
|
newIndex = level.bot_attachmenttable[difficulty].size;
|
|
level.bot_attachmenttable[difficulty][newIndex] = attachmentName;
|
|
}
|
|
}
|
|
}
|
|
|
|
row++;
|
|
}
|
|
|
|
/#
|
|
level.bot_att_cross_reference = undefined;
|
|
#/
|
|
}
|
|
|
|
bot_loadout_choose_from_attachmenttable( loadoutValue, loadoutValueArray, loadoutValueName, personality, difficulty )
|
|
{
|
|
// Set fallbacks in case nothing is found that matches
|
|
result = "none";
|
|
|
|
if ( !IsDefined( level.bot_attachmenttable ) )
|
|
return result;
|
|
|
|
if ( !IsDefined( level.bot_attachmenttable[difficulty] ) )
|
|
return result;
|
|
|
|
result = bot_loadout_choose_from_set( level.bot_attachmenttable[difficulty], loadoutValue, loadoutValueArray, loadoutValueName );
|
|
|
|
return result;
|
|
}
|
|
|
|
init_bot_camotable()
|
|
{
|
|
fileName = "mp/camotable.csv";
|
|
colName = 1;
|
|
colBotValid = 5;
|
|
|
|
level.bot_camotable = [];
|
|
|
|
row = 0;
|
|
while ( 1 )
|
|
{
|
|
camoName = TableLookupByRow( fileName, row, colName );
|
|
|
|
if ( !IsDefined( camoName ) || camoName == "" )
|
|
break;
|
|
|
|
botValid = TableLookupByRow( fileName, row, colBotValid );
|
|
if ( IsDefined( botValid ) && Int( botValid ) )
|
|
level.bot_camotable[ level.bot_camotable.size ] = camoName;
|
|
|
|
row++;
|
|
}
|
|
}
|
|
|
|
bot_loadout_choose_from_camotable( loadoutValue, loadoutValueArray, loadoutValueName, personality, difficulty )
|
|
{
|
|
// Set fallbacks in case nothing is found that matches
|
|
result = "none";
|
|
|
|
if ( !IsDefined( level.bot_camotable ) )
|
|
return result;
|
|
|
|
result = bot_loadout_choose_from_set( level.bot_camotable, loadoutValue, loadoutValueArray, loadoutValueName );
|
|
|
|
return result;
|
|
}
|
|
|
|
bot_loadout_perk_slots( loadoutValueArray )
|
|
{
|
|
result = SCR_CONST_base_perk_slots;
|
|
if ( IsDefined( loadoutValueArray["loadoutPrimary"] ) && loadoutValueArray["loadoutPrimary"] == "none" )
|
|
result = result + 1;
|
|
if ( IsDefined( loadoutValueArray["loadoutSecondary"] ) && loadoutValueArray["loadoutSecondary"] == "none" )
|
|
result = result + 1;
|
|
if ( IsDefined( loadoutValueArray["loadoutEquipment"] ) && loadoutValueArray["loadoutEquipment"] == "none" )
|
|
result = result + 1;
|
|
if ( IsDefined( loadoutValueArray["loadoutOffhand"] ) && loadoutValueArray["loadoutOffhand"] == "none" )
|
|
result = result + 1;
|
|
return result;
|
|
}
|
|
|
|
bot_loadout_valid_choice( loadoutValueVerbatim, loadoutValueArray, loadoutValueName, choice )
|
|
{
|
|
valid = true;
|
|
|
|
switch ( loadoutValueName )
|
|
{
|
|
case "loadoutPrimary":
|
|
valid = bot_loadout_item_allowed( "weapon", choice );
|
|
break;
|
|
case "loadoutEquipment":
|
|
case "loadoutOffhand":
|
|
valid = bot_loadout_item_allowed( "equipment", choice );
|
|
break;
|
|
case "loadoutPrimaryBuff":
|
|
valid = maps\mp\gametypes\_class::isValidWeaponBuff( choice, loadoutValueArray["loadoutPrimary"] );
|
|
break;
|
|
case "loadoutPrimaryAttachment":
|
|
valid = self bot_validate_weapon( loadoutValueArray["loadoutPrimary"], choice, "none" );
|
|
break;
|
|
case "loadoutPrimaryAttachment2":
|
|
valid = self bot_validate_weapon( loadoutValueArray["loadoutPrimary"], loadoutValueArray["loadoutPrimaryAttachment"], choice );
|
|
break;
|
|
case "loadoutPrimaryAttachment3":
|
|
valid = self bot_validate_weapon( loadoutValueArray["loadoutPrimary"], loadoutValueArray["loadoutPrimaryAttachment"], loadoutValueArray["loadoutPrimaryAttachment2"], choice );
|
|
break;
|
|
case "loadoutPrimaryReticle":
|
|
valid = self bot_validate_reticle( "loadoutPrimary", loadoutValueArray, choice );
|
|
break;
|
|
case "loadoutPrimaryCamo":
|
|
valid = (!IsDefined( self.botLoadoutFavoriteCamo ) || (choice == self.botLoadoutFavoriteCamo));
|
|
break;
|
|
case "loadoutSecondary":
|
|
valid = (choice != loadoutValueArray["loadoutPrimary"]);
|
|
valid = valid && bot_loadout_item_allowed( "weapon", choice );
|
|
break;
|
|
case "loadoutSecondaryBuff":
|
|
valid = maps\mp\gametypes\_class::isValidWeaponBuff( choice, loadoutValueArray["loadoutSecondary"] );
|
|
break;
|
|
case "loadoutSecondaryAttachment":
|
|
valid = self bot_validate_weapon( loadoutValueArray["loadoutSecondary"], choice, "none" );
|
|
break;
|
|
case "loadoutSecondaryAttachment2":
|
|
valid = self bot_validate_weapon( loadoutValueArray["loadoutSecondary"], loadoutValueArray["loadoutSecondaryAttachment"], choice );
|
|
break;
|
|
case "loadoutSecondaryAttachment3":
|
|
valid = self bot_validate_weapon( loadoutValueArray["loadoutSecondary"], loadoutValueArray["loadoutSecondaryAttachment"], loadoutValueArray["loadoutSecondaryAttachment2"], choice );
|
|
break;
|
|
case "loadoutSecondaryReticle":
|
|
valid = self bot_validate_reticle( "loadoutSecondary", loadoutValueArray, choice );
|
|
break;
|
|
case "loadoutSecondaryCamo":
|
|
valid = (!IsDefined( self.botLoadoutFavoriteCamo ) || (choice == self.botLoadoutFavoriteCamo));
|
|
break;
|
|
case "loadoutStreak1":
|
|
case "loadoutStreak2":
|
|
case "loadoutStreak3":
|
|
valid = bot_killstreak_is_valid_internal( choice, "bots", undefined, loadoutValueArray["loadoutStreakType"] );
|
|
valid = valid && bot_loadout_item_allowed( "killstreak", choice, loadoutValueArray["loadoutStreakType"] );
|
|
break;
|
|
case "loadoutPerk1":
|
|
case "loadoutPerk2":
|
|
case "loadoutPerk3":
|
|
case "loadoutPerk4":
|
|
case "loadoutPerk5":
|
|
case "loadoutPerk6":
|
|
case "loadoutPerk7":
|
|
case "loadoutPerk8":
|
|
case "loadoutPerk9":
|
|
case "loadoutPerk10":
|
|
case "loadoutPerk11":
|
|
case "loadoutPerk12":
|
|
valid = bot_validate_perk( choice, loadoutValueName, loadoutValueArray, 1, 12, bot_loadout_perk_slots( loadoutValueArray ) );
|
|
break;
|
|
case "loadoutPerk13":
|
|
case "loadoutPerk14":
|
|
case "loadoutPerk15":
|
|
if ( loadoutValueArray["loadoutStreakType"] != "streaktype_specialist" )
|
|
valid = false;
|
|
else
|
|
valid = bot_validate_perk( choice, loadoutValueName, loadoutValueArray, -1, -1 );
|
|
break;
|
|
case "loadoutPerk16":
|
|
case "loadoutPerk17":
|
|
case "loadoutPerk18":
|
|
case "loadoutPerk19":
|
|
case "loadoutPerk20":
|
|
case "loadoutPerk21":
|
|
case "loadoutPerk22":
|
|
case "loadoutPerk23":
|
|
if ( loadoutValueArray["loadoutStreakType"] != "streaktype_specialist" )
|
|
valid = false;
|
|
else
|
|
valid = bot_validate_perk( choice, loadoutValueName, loadoutValueArray, 16, 23, SCR_CONST_base_perk_slots );
|
|
break;
|
|
};
|
|
|
|
return valid;
|
|
}
|
|
|
|
bot_loadout_choose_from_set( valueChoices, loadoutValue, loadoutValueArray, loadoutValueName, isTemplate )
|
|
{
|
|
chosenValue = "none";
|
|
chosenTemplate = undefined;
|
|
validCount = 0.0;
|
|
|
|
if ( array_contains( valueChoices, "specialty_null" ) )
|
|
chosenValue = "specialty_null";
|
|
|
|
foreach ( choice in valueChoices )
|
|
{
|
|
template = undefined;
|
|
|
|
if ( GetSubStr( choice, 0, 9 ) == "template_" )
|
|
{
|
|
/#
|
|
if ( IsDefined( isTemplate ) && isTemplate )
|
|
AssertMsg( "template_ entries should not reference other template_ entries as the random weighting does not work right for that" );
|
|
#/
|
|
|
|
template = choice;
|
|
templateValues = level.botLoadoutTemplates[choice][loadoutValueName];
|
|
assert( IsDefined( templateValues ) );
|
|
choice = bot_loadout_choose_from_set( StrTok( templateValues, "| " ), loadoutValue, loadoutValueArray, loadoutValueName, true );
|
|
|
|
// If we have already chosen this template for any field, always choose the same template again when given the option
|
|
if ( IsDefined( template ) && IsDefined( self.chosenTemplates[template] ) )
|
|
return choice;
|
|
}
|
|
|
|
if ( choice == "attachmenttable" )
|
|
return bot_loadout_choose_from_attachmenttable( loadoutValue, loadoutValueArray, loadoutValueName, self.personality, self.difficulty );
|
|
|
|
if ( choice == "weap_statstable" )
|
|
return bot_loadout_choose_from_statstable( loadoutValue, loadoutValueArray, loadoutValueName, self.personality, self.difficulty );
|
|
|
|
if ( choice == "camotable" )
|
|
return bot_loadout_choose_from_camotable( loadoutValue, loadoutValueArray, loadoutValueName, self.personality, self.difficulty );
|
|
|
|
if ( GetSubStr( choice, 0, 5 ) == "class" && int( GetSubStr( choice, 5, 6 ) ) > 0 )
|
|
choice = bot_loadout_choose_from_default_class( choice, loadoutValue, loadoutValueArray, loadoutValueName, self.personality, self.difficulty );
|
|
|
|
if ( IsDefined( level.bot_perktable ) && (GetSubStr( choice, 0, 10 ) == "perktable_") )
|
|
return bot_loadout_choose_from_perktable( choice, loadoutValue, loadoutValueArray, loadoutValueName, self.personality, self.difficulty );
|
|
|
|
if ( self bot_loadout_valid_choice( loadoutValue, loadoutValueArray, loadoutValueName, choice ) )
|
|
{
|
|
validCount = validCount + 1.0;
|
|
if ( RandomFloat( 1.0 ) <= (1.0 / validCount) )
|
|
{
|
|
chosenValue = choice;
|
|
chosenTemplate = template;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( IsDefined( chosenTemplate ) )
|
|
self.chosenTemplates[chosenTemplate] = true;
|
|
|
|
return chosenValue;
|
|
}
|
|
|
|
bot_loadout_choose_values( loadoutValueArray )
|
|
{
|
|
self.chosenTemplates = [];
|
|
|
|
foreach( loadoutValueName, loadoutValue in loadoutValueArray )
|
|
{
|
|
valueChoices = StrTok( loadoutValue, "| " );
|
|
|
|
chosenValue = self bot_loadout_choose_from_set( valueChoices, loadoutValue, loadoutValueArray, loadoutValueName );
|
|
/#
|
|
debugLoadoutValue = GetDvar( "bot_Debug" + loadoutValueName, "" );
|
|
if ( IsDefined( debugLoadoutValue ) && debugLoadoutValue != "" )
|
|
chosenValue = debugLoadoutValue;
|
|
#/
|
|
loadoutValueArray[loadoutValueName] = chosenValue;
|
|
}
|
|
|
|
return loadoutValueArray;
|
|
}
|
|
|
|
bot_load_setup_difficulty_squad_match( game_elo )
|
|
{
|
|
diff = "recruit";
|
|
|
|
for ( diffIndex = 18 ; diffIndex >= 0 ; diffIndex-- )
|
|
{
|
|
elo = Int( TableLookupByRow( "mp/squadEloTable.csv", diffIndex, 0 ) );
|
|
|
|
if ( game_elo >= elo || diffIndex == 0 )
|
|
{
|
|
return TableLookupByRow( "mp/squadEloTable.csv", diffIndex, self.pers["squadSlot"] + 1 );
|
|
}
|
|
}
|
|
|
|
return diff;
|
|
}
|
|
|
|
bot_loadout_get_difficulty()
|
|
{
|
|
difficulty = "regular";
|
|
|
|
if ( GetDvar( "squad_match" ) == "1" )
|
|
{
|
|
difficulty = bot_load_setup_difficulty_squad_match( GetSquadAssaultELO() );
|
|
}
|
|
else
|
|
{
|
|
difficulty = self BotGetDifficulty();
|
|
|
|
if ( difficulty == "default" )
|
|
{
|
|
// Make sure we pick a difficulty if its set to "default"
|
|
self maps\mp\bots\_bots_util::bot_set_difficulty( "default" );
|
|
difficulty = self BotGetDifficulty();
|
|
}
|
|
|
|
}
|
|
|
|
Assert( difficulty != "default" );
|
|
|
|
return difficulty;
|
|
}
|
|
|
|
bot_loadout_class_callback()
|
|
{
|
|
while( !IsDefined(level.bot_loadouts_initialized) )
|
|
wait(0.05);
|
|
|
|
while ( !IsDefined( self.personality ) )
|
|
wait(0.05);
|
|
|
|
loadoutValueArray = [];
|
|
|
|
difficulty = self bot_loadout_get_difficulty();
|
|
self.difficulty = difficulty;
|
|
personality = self BotGetPersonality();
|
|
|
|
// If Squad mode then use the loadout for that squad member
|
|
if ( GetDvar( "squad_match" ) == "1" )
|
|
{
|
|
loadoutValueArray = self bot_loadout_setup_squad_match( loadoutValueArray );
|
|
AssertEx(IsDefined(loadoutValueArray), "Bot '" + self.name + "' spawning (Squad Match) with loadoutValueArray not defined");
|
|
personality = self BotGetPersonality();
|
|
}
|
|
else if ( GetDvar( "squad_vs_squad" ) == "1" )
|
|
{
|
|
loadoutValueArray = self bot_loadout_setup_squad_vs_squad_match( loadoutValueArray );
|
|
AssertEx(IsDefined(loadoutValueArray), "Bot '" + self.name + "' spawning (Squad vs Squad Match) with loadoutValueArray not defined");
|
|
personality = self BotGetPersonality();
|
|
}
|
|
else if ( GetDvar( "squad_use_hosts_squad" ) == "1" && level.wargame_client.team == self.team )
|
|
{
|
|
loadoutValueArray = self bot_loadout_setup_wargame_match( loadoutValueArray );
|
|
AssertEx(IsDefined(loadoutValueArray), "Bot '" + self.name + "' spawning (Wargame Match) with loadoutValueArray not defined");
|
|
personality = self BotGetPersonality();
|
|
}
|
|
else
|
|
{
|
|
// If bot already has a loadout, stick with it most of the time
|
|
if ( IsDefined( self.botLastLoadout ) )
|
|
{
|
|
same_difficulty = self.botLastLoadoutDifficulty == difficulty;
|
|
same_personality = self.botLastLoadoutPersonality == personality;
|
|
if ( same_difficulty && same_personality && ( !IsDefined(self.hasDied) || self.hasDied ) && !IsDefined(self.respawn_with_launcher) )
|
|
{
|
|
return self.botLastLoadout;
|
|
}
|
|
}
|
|
|
|
loadoutValueArray = self bot_loadout_pick( personality, difficulty );
|
|
loadoutValueArray = self bot_loadout_choose_values( loadoutValueArray );
|
|
|
|
if ( isdefined( level.bot_funcs["gametype_loadout_modify"] ) )
|
|
loadoutValueArray = self [[level.bot_funcs["gametype_loadout_modify"]]]( loadoutValueArray );
|
|
|
|
AssertEx(IsDefined(loadoutValueArray), "Bot '" + self.name + "'spawning (randomized loadout) with loadoutValueArray not defined");
|
|
|
|
if ( loadoutValueArray["loadoutPrimary"] == "none" )
|
|
{
|
|
// Choose a fallback weapon and switch personality to match
|
|
self.bot_fallback_personality = undefined;
|
|
loadoutValueArray["loadoutPrimary"] = self bot_loadout_choose_fallback_primary( loadoutValueArray );
|
|
loadoutValueArray["loadoutPrimaryCamo"] = "none";
|
|
loadoutValueArray["loadoutPrimaryAttachment"] = "none";
|
|
loadoutValueArray["loadoutPrimaryAttachment2"] = "none";
|
|
loadoutValueArray["loadoutPrimaryAttachment3"] = "none";
|
|
loadoutValueArray["loadoutPrimaryReticle"] = "none";
|
|
if ( IsDefined( self.bot_fallback_personality ) )
|
|
{
|
|
if ( self.bot_fallback_personality == "weapon" )
|
|
self bot_pick_personality_from_weapon( loadoutValueArray[ "loadoutPrimary" ] );
|
|
else
|
|
self maps\mp\bots\_bots_util::bot_set_personality( self.bot_fallback_personality );
|
|
|
|
personality = self.personality;
|
|
|
|
self.bot_fallback_personality = undefined;
|
|
}
|
|
}
|
|
|
|
self.botLastLoadout = loadoutValueArray;
|
|
self.botLastLoadoutDifficulty = difficulty;
|
|
self.botLastLoadoutPersonality = personality;
|
|
|
|
if ( IsDefined( loadoutValueArray["loadoutPrimaryCamo"] ) && loadoutValueArray["loadoutPrimaryCamo"] != "none" )
|
|
self.botLoadoutFavoriteCamo = loadoutValueArray["loadoutPrimaryCamo"];
|
|
|
|
if ( IsDefined(self.respawn_with_launcher) )
|
|
{
|
|
if ( IsDefined( level.bot_respawn_launcher_name ) && bot_loadout_item_allowed( "weapon", level.bot_respawn_launcher_name ) )
|
|
{
|
|
loadoutValueArray["loadoutSecondary"] = level.bot_respawn_launcher_name;
|
|
loadoutValueArray["loadoutSecondaryAttachment"] = "none";
|
|
loadoutValueArray["loadoutSecondaryAttachment2"] = "none";
|
|
self.botLastLoadout = undefined; // Force bot to pick a new loadout next time
|
|
}
|
|
self.respawn_with_launcher = undefined;
|
|
}
|
|
}
|
|
|
|
loadoutValueArray = self bot_loadout_setup_perks( loadoutValueArray );
|
|
|
|
// Killstreaks should be valid now so its safe to check them here (these functions assert internally)
|
|
maps\mp\gametypes\_class::isValidKillstreak(loadoutValueArray["loadoutStreak1"]);
|
|
maps\mp\gametypes\_class::isValidKillstreak(loadoutValueArray["loadoutStreak2"]);
|
|
maps\mp\gametypes\_class::isValidKillstreak(loadoutValueArray["loadoutStreak3"]);
|
|
|
|
if ( self bot_israndom() )
|
|
{
|
|
if ( array_contains( self.pers[ "loadoutPerks" ], "specialty_twoprimaries" ) )
|
|
{
|
|
// Pick a second primary from the CQB loadouts as generally we would want something good for close range as a secondary
|
|
otherPrimaryLoadout = self bot_loadout_pick( "cqb", difficulty );
|
|
loadoutValueArray["loadoutSecondary"] = otherPrimaryLoadout["loadoutPrimary"];
|
|
loadoutValueArray["loadoutSecondaryAttachment"] = otherPrimaryLoadout["loadoutPrimaryAttachment"];
|
|
loadoutValueArray["loadoutSecondaryAttachment2"] = otherPrimaryLoadout["loadoutPrimaryAttachment2"];
|
|
loadoutValueArray = self bot_loadout_choose_values( loadoutValueArray );
|
|
loadoutValueArray = self bot_loadout_setup_perks( loadoutValueArray );
|
|
}
|
|
|
|
if ( array_contains( self.pers[ "loadoutPerks" ], "specialty_extra_attachment" ) )
|
|
{
|
|
// Pick again for attachment3 and attachment2 on secondary
|
|
otherAttachmentLoadout = self bot_loadout_pick( personality, difficulty );
|
|
loadoutValueArray["loadoutPrimaryAttachment3"] = otherAttachmentLoadout["loadoutPrimaryAttachment2"];
|
|
if ( array_contains( self.pers[ "loadoutPerks" ], "specialty_twoprimaries" ) )
|
|
loadoutValueArray["loadoutSecondaryAttachment2"] = otherAttachmentLoadout["loadoutPrimaryAttachment2"];
|
|
else
|
|
loadoutValueArray["loadoutSecondaryAttachment2"] = otherAttachmentLoadout["loadoutSecondaryAttachment2"];
|
|
loadoutValueArray = self bot_loadout_choose_values( loadoutValueArray );
|
|
loadoutValueArray = self bot_loadout_setup_perks( loadoutValueArray );
|
|
}
|
|
else
|
|
{
|
|
// Without specialty_extra_attachment secondary always only has one attachment
|
|
loadoutValueArray["loadoutSecondaryAttachment2"] = "none";
|
|
if ( !(self bot_validate_reticle( "loadoutSecondary", loadoutValueArray, loadoutValueArray["loadoutSecondaryReticle"]) ) )
|
|
loadoutValueArray["loadoutSecondaryReticle"] = "none";
|
|
}
|
|
}
|
|
|
|
AssertEx(IsDefined(loadoutValueArray), "Bot returning undefined from bot_loadout_class_callback");
|
|
return loadoutValueArray;
|
|
}
|
|
|
|
bot_loadout_setup_perks( loadoutValueArray )
|
|
{
|
|
self.pers[ "loadoutPerks" ] = [];
|
|
self.pers[ "specialistBonusStreaks" ] = [];
|
|
self.pers[ "specialistStreaks" ] = [];
|
|
self.pers[ "specialistStreakKills" ] = [];
|
|
|
|
streakIndex = 0;
|
|
isSpecialist = ( IsDefined( loadoutValueArray["loadoutStreakType"] ) && loadoutValueArray["loadoutStreakType"] == "streaktype_specialist" );
|
|
|
|
if ( isSpecialist )
|
|
{
|
|
loadoutValueArray[ "loadoutStreak1" ] = "none";
|
|
loadoutValueArray[ "loadoutStreak2" ] = "none";
|
|
loadoutValueArray[ "loadoutStreak3" ] = "none";
|
|
}
|
|
|
|
foreach ( itemName, item in loadoutValueArray )
|
|
{
|
|
if ( (item == "specialty_null") || (item == "none") )
|
|
continue;
|
|
|
|
if ( (GetSubStr( itemName, 0, 11 ) == "loadoutPerk") )
|
|
{
|
|
perkIndex = int( GetSubStr( itemName, 11 ) );
|
|
|
|
if ( !isSpecialist && perkIndex > 12 )
|
|
continue;
|
|
|
|
baseName = getBasePerkName( item );
|
|
|
|
if ( perkIndex <= 12 )
|
|
{
|
|
// Regular perks
|
|
self.pers[ "loadoutPerks" ][ self.pers[ "loadoutPerks" ].size ] = baseName;
|
|
}
|
|
else if ( perkIndex <= 15 )
|
|
{
|
|
// Specialist killstreaks
|
|
loadoutValueArray[ "loadoutStreak" + (streakIndex + 1) ] = baseName + "_ks";
|
|
self.pers[ "specialistStreaks" ][ self.pers[ "specialistStreaks" ].size ] = baseName + "_ks";
|
|
prevCost = 0;
|
|
if ( streakIndex > 0 )
|
|
prevCost = self.pers[ "specialistStreakKills" ][ self.pers[ "specialistStreakKills" ].size - 1 ];
|
|
self.pers[ "specialistStreakKills" ][ self.pers[ "specialistStreakKills" ].size ] = prevCost + bot_perk_cost( baseName ) + 2;
|
|
streakIndex++;
|
|
}
|
|
else
|
|
{
|
|
// Specialist bonus perks
|
|
self.pers[ "specialistBonusStreaks" ][ self.pers[ "specialistBonusStreaks" ].size ] = baseName;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( isSpecialist && !IsDefined( self.pers[ "specialistStreakKills" ][0] ) )
|
|
{
|
|
self.pers[ "specialistStreakKills" ][0] = 0;
|
|
self.pers[ "specialistStreaks" ][0] = "specialty_null";
|
|
}
|
|
if ( isSpecialist && !IsDefined( self.pers[ "specialistStreakKills" ][1] ) )
|
|
{
|
|
self.pers[ "specialistStreakKills" ][1] = self.pers[ "specialistStreakKills" ][0];
|
|
self.pers[ "specialistStreaks" ][1] = "specialty_null";
|
|
}
|
|
if ( isSpecialist && !IsDefined( self.pers[ "specialistStreakKills" ][2] ) )
|
|
{
|
|
self.pers[ "specialistStreakKills" ][2] = self.pers[ "specialistStreakKills" ][1];
|
|
self.pers[ "specialistStreaks" ][2] = "specialty_null";
|
|
}
|
|
|
|
return loadoutValueArray;
|
|
}
|
|
|
|
bot_setup_loadout_callback()
|
|
{
|
|
personality = self BotGetPersonality();
|
|
difficulty = self bot_loadout_get_difficulty();
|
|
|
|
loadoutSet = bot_loadout_set( personality, difficulty, false );
|
|
if ( IsDefined( loadoutSet ) && isDefined( loadoutSet.loadouts ) && loadoutSet.loadouts.size > 0 )
|
|
{
|
|
self.classCallback = ::bot_loadout_class_callback;
|
|
return true;
|
|
}
|
|
|
|
bot_name_without_personality = GetSubStr(self.name,0,self.name.size-10); // At this point in the setup, the bot personality might be wrong, so don't display it here
|
|
AssertMsg("Bot <" + bot_name_without_personality + "> has no possible loadouts for personality <" + personality + "> and difficulty <" + difficulty + ">");
|
|
self.classCallback = undefined;
|
|
return false;
|
|
}
|
|
|
|
bot_squad_lookup_private( owner, squad_slot, loadout_slot, fieldA, fieldAIndex, fieldB, fieldBIndex )
|
|
{
|
|
if ( IsDefined( fieldBIndex ) )
|
|
return owner GetPrivatePlayerData( "privateMatchSquadMembers", squad_slot, "loadouts", loadout_slot, fieldA, fieldAIndex, fieldB, fieldBIndex );
|
|
else if ( IsDefined( fieldB ) )
|
|
return owner GetPrivatePlayerData( "privateMatchSquadMembers", squad_slot, "loadouts", loadout_slot, fieldA, fieldAIndex, fieldB );
|
|
else if ( IsDefined( fieldAIndex ) )
|
|
return owner GetPrivatePlayerData( "privateMatchSquadMembers", squad_slot, "loadouts", loadout_slot, fieldA, fieldAIndex );
|
|
else
|
|
return owner GetPrivatePlayerData( "privateMatchSquadMembers", squad_slot, "loadouts", loadout_slot, fieldA );
|
|
}
|
|
|
|
bot_squad_lookup_ranked( owner, squad_slot, loadout_slot, fieldA, fieldAIndex, fieldB, fieldBIndex )
|
|
{
|
|
if ( IsDefined( fieldBIndex ) )
|
|
return owner GetRankedPlayerData( "squadMembers", squad_slot, "loadouts", loadout_slot, fieldA, fieldAIndex, fieldB, fieldBIndex );
|
|
else if ( IsDefined( fieldB ) )
|
|
return owner GetRankedPlayerData( "squadMembers", squad_slot, "loadouts", loadout_slot, fieldA, fieldAIndex, fieldB );
|
|
else if ( IsDefined( fieldAIndex ) )
|
|
return owner GetRankedPlayerData( "squadMembers", squad_slot, "loadouts", loadout_slot, fieldA, fieldAIndex );
|
|
else
|
|
return owner GetRankedPlayerData( "squadMembers", squad_slot, "loadouts", loadout_slot, fieldA );
|
|
}
|
|
|
|
bot_squad_lookup_enemy( owner, squad_slot, loadout_slot, fieldA, fieldAIndex, fieldB, fieldBIndex )
|
|
{
|
|
if ( IsDefined( fieldBIndex ) )
|
|
return GetEnemySquadData( "squadMembers", squad_slot, "loadouts", loadout_slot, fieldA, fieldAIndex, fieldB, fieldBIndex );
|
|
else if ( IsDefined( fieldB ) )
|
|
return GetEnemySquadData( "squadMembers", squad_slot, "loadouts", loadout_slot, fieldA, fieldAIndex, fieldB );
|
|
else if ( IsDefined( fieldAIndex ) )
|
|
return GetEnemySquadData( "squadMembers", squad_slot, "loadouts", loadout_slot, fieldA, fieldAIndex );
|
|
else
|
|
return GetEnemySquadData( "squadMembers", squad_slot, "loadouts", loadout_slot, fieldA );
|
|
}
|
|
|
|
bot_squad_lookup( owner, squad_slot, loadout_slot, fieldA, fieldAIndex, fieldB, fieldBIndex )
|
|
{
|
|
bot_squad_lookup_func = ::bot_squad_lookup_ranked;
|
|
|
|
if ( (GetDvar( "squad_match" ) == "1") && (self.team == "axis") )
|
|
bot_squad_lookup_func = ::bot_squad_lookup_enemy;
|
|
else if ( !matchMakingGame() )
|
|
bot_squad_lookup_func = ::bot_squad_lookup_private;
|
|
|
|
return self [[ bot_squad_lookup_func ]]( owner, squad_slot, loadout_slot, fieldA, fieldAIndex, fieldB, fieldBIndex );
|
|
}
|
|
|
|
bot_squadmember_lookup( owner, squad_slot, fieldA )
|
|
{
|
|
if ( (GetDvar( "squad_match" ) == "1") && (self.team == "axis") )
|
|
return GetEnemySquadData( "squadMembers", squad_slot, fieldA );
|
|
else if ( !matchMakingGame() )
|
|
return owner GetPrivatePlayerData( "privateMatchSquadMembers", squad_slot, fieldA );
|
|
else
|
|
return owner GetRankedPlayerData( "squadMembers", squad_slot, fieldA );
|
|
}
|
|
|
|
bot_loadout_copy_from_client( loadoutValueArray, owner, squad_slot, loadout_slot )
|
|
{
|
|
loadoutValueArray[ "loadoutPrimary" ] = self bot_squad_lookup( owner, squad_slot, loadout_slot, "weaponSetups", 0, "weapon" );
|
|
loadoutValueArray[ "loadoutPrimaryAttachment" ] = self bot_squad_lookup( owner, squad_slot, loadout_slot, "weaponSetups", 0, "attachment", 0 );
|
|
loadoutValueArray[ "loadoutPrimaryAttachment2" ] = self bot_squad_lookup( owner, squad_slot, loadout_slot, "weaponSetups", 0, "attachment", 1 );
|
|
loadoutValueArray[ "loadoutPrimaryAttachment3" ] = self bot_squad_lookup( owner, squad_slot, loadout_slot, "weaponSetups", 0, "attachment", 2 );
|
|
loadoutValueArray[ "loadoutPrimaryBuff" ] = self bot_squad_lookup( owner, squad_slot, loadout_slot, "weaponSetups", 0, "buff" );
|
|
loadoutValueArray[ "loadoutPrimaryCamo" ] = self bot_squad_lookup( owner, squad_slot, loadout_slot, "weaponSetups", 0, "camo" );
|
|
loadoutValueArray[ "loadoutPrimaryReticle" ] = self bot_squad_lookup( owner, squad_slot, loadout_slot, "weaponSetups", 0, "reticle" );
|
|
|
|
loadoutValueArray[ "loadoutSecondary" ] = self bot_squad_lookup( owner, squad_slot, loadout_slot, "weaponSetups", 1, "weapon" );
|
|
loadoutValueArray[ "loadoutSecondaryAttachment" ] = self bot_squad_lookup( owner, squad_slot, loadout_slot, "weaponSetups", 1, "attachment", 0 );
|
|
loadoutValueArray[ "loadoutSecondaryAttachment2" ] = self bot_squad_lookup( owner, squad_slot, loadout_slot, "weaponSetups", 1, "attachment", 1 );
|
|
loadoutValueArray[ "loadoutSecondaryBuff" ] = self bot_squad_lookup( owner, squad_slot, loadout_slot, "weaponSetups", 1, "buff" );
|
|
loadoutValueArray[ "loadoutSecondaryCamo" ] = self bot_squad_lookup( owner, squad_slot, loadout_slot, "weaponSetups", 1, "camo" );
|
|
loadoutValueArray[ "loadoutSecondaryReticle" ] = self bot_squad_lookup( owner, squad_slot, loadout_slot, "weaponSetups", 1, "reticle" );
|
|
|
|
loadoutValueArray[ "loadoutEquipment" ] = self bot_squad_lookup( owner, squad_slot, loadout_slot, "perks", 0 );
|
|
loadoutValueArray[ "loadoutOffhand" ] = self bot_squad_lookup( owner, squad_slot, loadout_slot, "perks", 1 );
|
|
|
|
// clear out in case the user has not set them up
|
|
loadoutValueArray[ "loadoutStreak1" ] = "none";
|
|
loadoutValueArray[ "loadoutStreak2" ] = "none";
|
|
loadoutValueArray[ "loadoutStreak3" ] = "none";
|
|
|
|
// adding skill streaks
|
|
kill_streak = self bot_squad_lookup( owner, squad_slot, loadout_slot, "perks", 5 );
|
|
if ( isDefined( kill_streak ) )
|
|
{
|
|
loadoutValueArray["loadoutStreakType"] = kill_streak;
|
|
|
|
if ( kill_streak == "streaktype_assault" )
|
|
{
|
|
loadoutValueArray[ "loadoutStreak1" ] = self bot_squad_lookup( owner, squad_slot, loadout_slot, "assaultStreaks", 0 );
|
|
loadoutValueArray[ "loadoutStreak2" ] = self bot_squad_lookup( owner, squad_slot, loadout_slot, "assaultStreaks", 1 );
|
|
loadoutValueArray[ "loadoutStreak3" ] = self bot_squad_lookup( owner, squad_slot, loadout_slot, "assaultStreaks", 2 );
|
|
}
|
|
else if ( kill_streak == "streaktype_support" )
|
|
{
|
|
loadoutValueArray[ "loadoutStreak1" ] = self bot_squad_lookup( owner, squad_slot, loadout_slot, "supportStreaks", 0 );
|
|
loadoutValueArray[ "loadoutStreak2" ] = self bot_squad_lookup( owner, squad_slot, loadout_slot, "supportStreaks", 1 );
|
|
loadoutValueArray[ "loadoutStreak3" ] = self bot_squad_lookup( owner, squad_slot, loadout_slot, "supportStreaks", 2 );
|
|
}
|
|
else if ( kill_streak == "streaktype_specialist" )
|
|
{
|
|
// Bots define their specialist streaks as [loadoutPerk13 ... loadoutPerk23]
|
|
loadoutValueArray[ "loadoutPerk13" ] = self bot_squad_lookup( owner, squad_slot, loadout_slot, "specialistStreaks", 0 );
|
|
loadoutValueArray[ "loadoutPerk14" ] = self bot_squad_lookup( owner, squad_slot, loadout_slot, "specialistStreaks", 1 );
|
|
loadoutValueArray[ "loadoutPerk15" ] = self bot_squad_lookup( owner, squad_slot, loadout_slot, "specialistStreaks", 2 );
|
|
}
|
|
}
|
|
|
|
ability_index = 1;
|
|
num_abils = maps\mp\gametypes\_class::getNumAbilityCategories();
|
|
num_sub_abils = maps\mp\gametypes\_class::getNumSubAbility();
|
|
|
|
for ( abilityCategoryIndex = 0 ; abilityCategoryIndex < num_abils ; abilityCategoryIndex++ )
|
|
{
|
|
for ( abilityIndex = 0 ; abilityIndex < num_sub_abils ; abilityIndex++ )
|
|
{
|
|
picked = self bot_squad_lookup( owner, squad_slot, loadout_slot, "abilitiesPicked", abilityCategoryIndex, abilityIndex );
|
|
if ( isDefined( picked ) && picked )
|
|
{
|
|
abilityRef = TableLookup( "mp/cacAbilityTable.csv", 0, abilityCategoryIndex + 1, 4 + abilityIndex );
|
|
loadoutValueArray[ "loadoutPerk" + ability_index ] = abilityRef;
|
|
ability_index++;
|
|
}
|
|
else
|
|
{
|
|
loadoutValueArray[ "loadoutPerk" + ability_index ] = "specialty_null";
|
|
}
|
|
}
|
|
}
|
|
|
|
ability_index = 16;
|
|
for ( abilityCategoryIndex = 0 ; abilityCategoryIndex < num_abils ; abilityCategoryIndex++ )
|
|
{
|
|
for ( abilityIndex = 0 ; abilityIndex < num_sub_abils ; abilityIndex++ )
|
|
{
|
|
picked = self bot_squad_lookup( owner, squad_slot, loadout_slot, "specialistBonusStreaks", abilityCategoryIndex, abilityIndex );
|
|
if ( isDefined( picked ) && picked )
|
|
{
|
|
abilityRef = TableLookup( "mp/cacAbilityTable.csv", 0, abilityCategoryIndex + 1, 4 + abilityIndex );
|
|
loadoutValueArray[ "loadoutPerk" + ability_index ] = abilityRef;
|
|
ability_index++;
|
|
}
|
|
else
|
|
{
|
|
loadoutValueArray[ "loadoutPerk" + ability_index ] = "specialty_null";
|
|
}
|
|
}
|
|
}
|
|
|
|
loadoutValueArray[ "loadoutCharacterType" ] = self bot_squad_lookup( owner, squad_slot, loadout_slot, "type" );
|
|
|
|
self bot_pick_personality_from_weapon( loadoutValueArray[ "loadoutPrimary" ] );
|
|
|
|
self.playerCardPatch = self bot_squadmember_lookup( owner, squad_slot, "patch" );
|
|
self.playerCardBackground = self bot_squadmember_lookup( owner, squad_slot, "background" );
|
|
|
|
if ( (GetDvar( "squad_match" ) == "1") && (self.team == "axis") )
|
|
self.squad_bot_dog_type = GetEnemySquadDogType();
|
|
else
|
|
self.squad_bot_dog_type = owner GetCommonPlayerDataReservedInt( "mp_dog_type" );
|
|
|
|
return loadoutValueArray;
|
|
}
|
|
|
|
bot_loadout_setup_squad_match( loadoutValueArray )
|
|
{
|
|
owner = level.players[ 0 ];
|
|
foreach( player in level.players )
|
|
{
|
|
if ( !IsAI( player ) && IsPlayer( player ) )
|
|
{
|
|
owner = player;
|
|
break;
|
|
}
|
|
}
|
|
|
|
squad_slot = self.pers["squadSlot"];
|
|
loadout_slot = self bot_squadmember_lookup( owner, squad_slot, "ai_loadout" );
|
|
|
|
// this is normally set in onPlayerGiveLoadout(), but we are overiding
|
|
self.pers[ "rankxp" ] = self bot_squadmember_lookup( owner, squad_slot, "squadMemXP" );
|
|
if ( self.team == "allies" )
|
|
{
|
|
if ( IsDefined( owner ) )
|
|
{
|
|
prestige = owner getRankedPlayerDataReservedInt( "prestigeLevel" );
|
|
self.pers[ "prestige_fake" ] = prestige;
|
|
}
|
|
}
|
|
else if ( self.team == "axis" )
|
|
{
|
|
self.pers[ "prestige_fake" ] = GetSquadAssaultEnemyPrestige();
|
|
}
|
|
|
|
loadoutValueArray = self bot_loadout_copy_from_client( loadoutValueArray, owner, squad_slot, loadout_slot );
|
|
|
|
return loadoutValueArray;
|
|
}
|
|
|
|
|
|
bot_loadout_setup_squad_vs_squad_match( loadoutValueArray )
|
|
{
|
|
owner = level.squad_vs_squad_allies_client;
|
|
if ( self.team == "axis" )
|
|
{
|
|
owner = level.squad_vs_squad_axis_client;
|
|
}
|
|
|
|
squad_slot = self.pers["squadSlot"];
|
|
loadout_slot = self bot_squadmember_lookup( owner, squad_slot, "ai_loadout" );
|
|
|
|
// this is normally set in onPlayerGiveLoadout(), but we are overiding
|
|
self.pers[ "rankxp" ] = self bot_squadmember_lookup( owner, squad_slot, "squadMemXP" );
|
|
if ( IsDefined( owner ) )
|
|
{
|
|
prestige = owner getRankedPlayerDataReservedInt( "prestigeLevel" );
|
|
self.pers[ "prestige_fake" ] = prestige;
|
|
}
|
|
|
|
loadoutValueArray = self bot_loadout_copy_from_client( loadoutValueArray, owner, squad_slot, loadout_slot );
|
|
|
|
return loadoutValueArray;
|
|
}
|
|
|
|
|
|
bot_loadout_setup_wargame_match( loadoutValueArray )
|
|
{
|
|
owner = level.wargame_client;
|
|
|
|
squad_slot = self.pers["squadSlot"];
|
|
loadout_slot = self bot_squadmember_lookup( owner, squad_slot, "ai_loadout" );
|
|
|
|
// this is normally set in onPlayerGiveLoadout(), but we are overiding
|
|
self.pers[ "rankxp" ] = self bot_squadmember_lookup( owner, squad_slot, "squadMemXP" );
|
|
if ( IsDefined( owner ) )
|
|
{
|
|
prestige = owner getRankedPlayerDataReservedInt( "prestigeLevel" );
|
|
self.pers[ "prestige_fake" ] = prestige;
|
|
}
|
|
|
|
loadoutValueArray = self bot_loadout_copy_from_client( loadoutValueArray, owner, squad_slot, loadout_slot );
|
|
|
|
return loadoutValueArray;
|
|
}
|
|
|