#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( , , )" "Summary: Checks if a loadout item is restricted in the current match rules (returns true if not restricted)" "MandatoryArg: : The type of item ("weapon", "attachment", "killstreak", "perk")" "MandatoryArg: : The specific item" "OptionalArg: : 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; }