From 3498bd096c5a37b23f76ae11cad6ffe67d3a9acb Mon Sep 17 00:00:00 2001 From: ineed bots Date: Mon, 27 Nov 2023 17:56:02 -0600 Subject: [PATCH] readd --- maps/mp/bots/_bot_script.gsc | 30 +++ maps/mp/bots/_bot_utility.gsc | 356 +++++++++++++++++++++++++++++++++- 2 files changed, 385 insertions(+), 1 deletion(-) diff --git a/maps/mp/bots/_bot_script.gsc b/maps/mp/bots/_bot_script.gsc index 7748a2c..19a1c80 100644 --- a/maps/mp/bots/_bot_script.gsc +++ b/maps/mp/bots/_bot_script.gsc @@ -52,6 +52,7 @@ connected() self thread onSpawned(); self thread onDeath(); + self thread onGiveLoadout(); self thread onKillcam(); @@ -1472,6 +1473,35 @@ onDeath() } } +/* + Watches when the bot is given a loadout +*/ +onGiveLoadout_loop() +{ + class = self.class; + + if ( isDefined( self.bot_oma_class ) ) + class = self.bot_oma_class; + + self botGiveLoadout( self.team, class, !isDefined( self.bot_oma_class ) ); + self.bot_oma_class = undefined; +} + +/* + Watches when the bot is given a loadout +*/ +onGiveLoadout() +{ + self endon( "disconnect" ); + + for ( ;; ) + { + self waittill( "giveLoadout" ); + + self onGiveLoadout_loop(); + } +} + /* When the bot spawns. */ diff --git a/maps/mp/bots/_bot_utility.gsc b/maps/mp/bots/_bot_utility.gsc index 545d9fb..61a80f3 100644 --- a/maps/mp/bots/_bot_utility.gsc +++ b/maps/mp/bots/_bot_utility.gsc @@ -6,7 +6,6 @@ Notes for engine obv the old bot behavior should be changed to use gsc built-ins to control bots - isItemUnlocked should only check for levels for testclients, everything else should return true setSpawnWeapon and switchToWeapon should be modified to work for testclients */ @@ -2527,3 +2526,358 @@ bombPlantedFix( destroyedObj, player ) maps\mp\gametypes\sd::sd_endGame( game["attackers"], game["strings"]["target_destroyed"] ); } + + +/* + Patches giveLoadout so that it doesn't use IsItemUnlocked +*/ +botGiveLoadout( team, class, allowCopycat ) +{ + self endon( "death" ); + + self takeAllWeapons(); + + primaryIndex = 0; + + // initialize specialty array + self.specialty = []; + + if ( !isDefined( allowCopycat ) ) + allowCopycat = true; + + primaryWeapon = undefined; + + if ( isDefined( self.pers["copyCatLoadout"] ) && self.pers["copyCatLoadout"]["inUse"] && allowCopycat ) + { + self maps\mp\gametypes\_class::setClass( "copycat" ); + self.class_num = maps\mp\gametypes\_class::getClassIndex( "copycat" ); + + clonedLoadout = self.pers["copyCatLoadout"]; + + loadoutPrimary = clonedLoadout["loadoutPrimary"]; + loadoutPrimaryAttachment = clonedLoadout["loadoutPrimaryAttachment"]; + loadoutPrimaryAttachment2 = clonedLoadout["loadoutPrimaryAttachment2"] ; + loadoutPrimaryCamo = clonedLoadout["loadoutPrimaryCamo"]; + loadoutSecondary = clonedLoadout["loadoutSecondary"]; + loadoutSecondaryAttachment = clonedLoadout["loadoutSecondaryAttachment"]; + loadoutSecondaryAttachment2 = clonedLoadout["loadoutSecondaryAttachment2"]; + loadoutSecondaryCamo = clonedLoadout["loadoutSecondaryCamo"]; + loadoutEquipment = clonedLoadout["loadoutEquipment"]; + loadoutPerk1 = clonedLoadout["loadoutPerk1"]; + loadoutPerk2 = clonedLoadout["loadoutPerk2"]; + loadoutPerk3 = clonedLoadout["loadoutPerk3"]; + loadoutOffhand = clonedLoadout["loadoutOffhand"]; + loadoutDeathStreak = "specialty_copycat"; + } + else if ( isSubstr( class, "custom" ) ) + { + class_num = maps\mp\gametypes\_class::getClassIndex( class ); + self.class_num = class_num; + + loadoutPrimary = maps\mp\gametypes\_class::cac_getWeapon( class_num, 0 ); + loadoutPrimaryAttachment = maps\mp\gametypes\_class::cac_getWeaponAttachment( class_num, 0 ); + loadoutPrimaryAttachment2 = maps\mp\gametypes\_class::cac_getWeaponAttachmentTwo( class_num, 0 ); + loadoutPrimaryCamo = maps\mp\gametypes\_class::cac_getWeaponCamo( class_num, 0 ); + loadoutSecondaryCamo = maps\mp\gametypes\_class::cac_getWeaponCamo( class_num, 1 ); + loadoutSecondary = maps\mp\gametypes\_class::cac_getWeapon( class_num, 1 ); + loadoutSecondaryAttachment = maps\mp\gametypes\_class::cac_getWeaponAttachment( class_num, 1 ); + loadoutSecondaryAttachment2 = maps\mp\gametypes\_class::cac_getWeaponAttachmentTwo( class_num, 1 ); + loadoutSecondaryCamo = maps\mp\gametypes\_class::cac_getWeaponCamo( class_num, 1 ); + loadoutEquipment = maps\mp\gametypes\_class::cac_getPerk( class_num, 0 ); + loadoutPerk1 = maps\mp\gametypes\_class::cac_getPerk( class_num, 1 ); + loadoutPerk2 = maps\mp\gametypes\_class::cac_getPerk( class_num, 2 ); + loadoutPerk3 = maps\mp\gametypes\_class::cac_getPerk( class_num, 3 ); + loadoutOffhand = maps\mp\gametypes\_class::cac_getOffhand( class_num ); + loadoutDeathStreak = maps\mp\gametypes\_class::cac_getDeathstreak( class_num ); + } + else + { + class_num = maps\mp\gametypes\_class::getClassIndex( class ); + self.class_num = class_num; + + loadoutPrimary = maps\mp\gametypes\_class::table_getWeapon( level.classTableName, class_num, 0 ); + loadoutPrimaryAttachment = maps\mp\gametypes\_class::table_getWeaponAttachment( level.classTableName, class_num, 0, 0 ); + loadoutPrimaryAttachment2 = maps\mp\gametypes\_class::table_getWeaponAttachment( level.classTableName, class_num, 0, 1 ); + loadoutPrimaryCamo = maps\mp\gametypes\_class::table_getWeaponCamo( level.classTableName, class_num, 0 ); + loadoutSecondaryCamo = maps\mp\gametypes\_class::table_getWeaponCamo( level.classTableName, class_num, 1 ); + loadoutSecondary = maps\mp\gametypes\_class::table_getWeapon( level.classTableName, class_num, 1 ); + loadoutSecondaryAttachment = maps\mp\gametypes\_class::table_getWeaponAttachment( level.classTableName, class_num, 1, 0 ); + loadoutSecondaryAttachment2 = maps\mp\gametypes\_class::table_getWeaponAttachment( level.classTableName, class_num, 1, 1 );; + loadoutSecondaryCamo = maps\mp\gametypes\_class::table_getWeaponCamo( level.classTableName, class_num, 1 ); + loadoutEquipment = maps\mp\gametypes\_class::table_getEquipment( level.classTableName, class_num, 0 ); + loadoutPerk1 = maps\mp\gametypes\_class::table_getPerk( level.classTableName, class_num, 1 ); + loadoutPerk2 = maps\mp\gametypes\_class::table_getPerk( level.classTableName, class_num, 2 ); + loadoutPerk3 = maps\mp\gametypes\_class::table_getPerk( level.classTableName, class_num, 3 ); + loadoutOffhand = maps\mp\gametypes\_class::table_getOffhand( level.classTableName, class_num ); + loadoutDeathstreak = maps\mp\gametypes\_class::table_getDeathstreak( level.classTableName, class_num ); + } + + if ( loadoutPerk1 != "specialty_bling" ) + { + loadoutPrimaryAttachment2 = "none"; + loadoutSecondaryAttachment2 = "none"; + } + + if ( loadoutPerk1 != "specialty_onemanarmy" && loadoutSecondary == "onemanarmy" ) + loadoutSecondary = maps\mp\gametypes\_class::table_getWeapon( level.classTableName, 10, 1 ); + + //loadoutSecondaryCamo = "none"; + + // stop default class op'ness + allowOp = ( getDvarInt( "bots_loadout_allow_op" ) >= 1 ); + + if ( !allowOp ) + { + loadoutDeathstreak = "specialty_null"; + + if ( loadoutPrimary == "riotshield" ) + loadoutPrimary = "m4"; + + if ( loadoutSecondary == "at4" ) + loadoutSecondary = "usp"; + + if ( loadoutPrimaryAttachment == "gl" ) + loadoutPrimaryAttachment = "none"; + + if ( loadoutPerk2 == "specialty_coldblooded" ) + loadoutPerk2 = "specialty_null"; + + if ( loadoutPerk3 == "specialty_localjammer" ) + loadoutPerk3 = "specialty_null"; + } + + + if ( level.killstreakRewards ) + { + if ( getDvarInt( "scr_classic" ) == 1 ) + { + loadoutKillstreak1 = "uav"; + loadoutKillstreak2 = "precision_airstrike"; + loadoutKillstreak3 = "helicopter"; + } + else + { + loadoutKillstreak1 = self getPlayerData( "killstreaks", 0 ); + loadoutKillstreak2 = self getPlayerData( "killstreaks", 1 ); + loadoutKillstreak3 = self getPlayerData( "killstreaks", 2 ); + } + } + else + { + loadoutKillstreak1 = "none"; + loadoutKillstreak2 = "none"; + loadoutKillstreak3 = "none"; + } + + secondaryName = maps\mp\gametypes\_class::buildWeaponName( loadoutSecondary, loadoutSecondaryAttachment, loadoutSecondaryAttachment2 ); + self _giveWeapon( secondaryName, int( tableLookup( "mp/camoTable.csv", 1, loadoutSecondaryCamo, 0 ) ) ); + + self.loadoutPrimaryCamo = int( tableLookup( "mp/camoTable.csv", 1, loadoutPrimaryCamo, 0 ) ); + self.loadoutPrimary = loadoutPrimary; + self.loadoutSecondary = loadoutSecondary; + self.loadoutSecondaryCamo = int( tableLookup( "mp/camoTable.csv", 1, loadoutSecondaryCamo, 0 ) ); + + self SetOffhandPrimaryClass( "other" ); + + // Action Slots + //self _SetActionSlot( 1, "" ); + self _SetActionSlot( 1, "nightvision" ); + self _SetActionSlot( 3, "altMode" ); + self _SetActionSlot( 4, "" ); + + // Perks + self _clearPerks(); + self maps\mp\gametypes\_class::_detachAll(); + + // these special case giving pistol death have to come before + // perk loadout to ensure player perk icons arent overwritten + if ( level.dieHardMode ) + self maps\mp\perks\_perks::givePerk( "specialty_pistoldeath" ); + + // only give the deathstreak for the initial spawn for this life. + if ( loadoutDeathStreak != "specialty_null" && ( getTime() - self.spawnTime ) < 0.1 ) + { + deathVal = int( tableLookup( "mp/perkTable.csv", 1, loadoutDeathStreak, 6 ) ); + + if ( self botGetPerkUpgrade( loadoutPerk1 ) == "specialty_rollover" || self botGetPerkUpgrade( loadoutPerk2 ) == "specialty_rollover" || self botGetPerkUpgrade( loadoutPerk3 ) == "specialty_rollover" ) + deathVal -= 1; + + if ( self.pers["cur_death_streak"] == deathVal ) + { + self thread maps\mp\perks\_perks::givePerk( loadoutDeathStreak ); + self thread maps\mp\gametypes\_hud_message::splashNotify( loadoutDeathStreak ); + } + else if ( self.pers["cur_death_streak"] > deathVal ) + { + self thread maps\mp\perks\_perks::givePerk( loadoutDeathStreak ); + } + } + + self botLoadoutAllPerks( loadoutEquipment, loadoutPerk1, loadoutPerk2, loadoutPerk3 ); + + self maps\mp\gametypes\_class::setKillstreaks( loadoutKillstreak1, loadoutKillstreak2, loadoutKillstreak3 ); + + if ( self hasPerk( "specialty_extraammo", true ) && getWeaponClass( secondaryName ) != "weapon_projectile" ) + self giveMaxAmmo( secondaryName ); + + // Primary Weapon + primaryName = maps\mp\gametypes\_class::buildWeaponName( loadoutPrimary, loadoutPrimaryAttachment, loadoutPrimaryAttachment2 ); + self _giveWeapon( primaryName, self.loadoutPrimaryCamo ); + + // fix changing from a riotshield class to a riotshield class during grace period not giving a shield + if ( primaryName == "riotshield_mp" && level.inGracePeriod ) + self notify ( "weapon_change", "riotshield_mp" ); + + if ( self hasPerk( "specialty_extraammo", true ) ) + self giveMaxAmmo( primaryName ); + + self setSpawnWeapon( primaryName ); + + primaryTokens = strtok( primaryName, "_" ); + self.pers["primaryWeapon"] = primaryTokens[0]; + + // Primary Offhand was given by givePerk (it's your perk1) + + // Secondary Offhand + offhandSecondaryWeapon = loadoutOffhand + "_mp"; + + if ( loadoutOffhand == "flash_grenade" ) + self SetOffhandSecondaryClass( "flash" ); + else + self SetOffhandSecondaryClass( "smoke" ); + + self giveWeapon( offhandSecondaryWeapon ); + + if ( loadOutOffhand == "smoke_grenade" ) + self setWeaponAmmoClip( offhandSecondaryWeapon, 1 ); + else if ( loadOutOffhand == "flash_grenade" ) + self setWeaponAmmoClip( offhandSecondaryWeapon, 2 ); + else if ( loadOutOffhand == "concussion_grenade" ) + self setWeaponAmmoClip( offhandSecondaryWeapon, 2 ); + else + self setWeaponAmmoClip( offhandSecondaryWeapon, 1 ); + + primaryWeapon = primaryName; + self.primaryWeapon = primaryWeapon; + self.secondaryWeapon = secondaryName; + + self botPlayerModelForWeapon( self.pers["primaryWeapon"], getBaseWeaponName( secondaryName ) ); + + self.isSniper = ( weaponClass( self.primaryWeapon ) == "sniper" ); + + self maps\mp\gametypes\_weapons::updateMoveSpeedScale( "primary" ); + + // cac specialties that require loop threads + self maps\mp\perks\_perks::cac_selector(); + + self notify ( "changed_kit" ); + self notify( "bot_giveLoadout", allowCopycat ); +} + +/* + Patches giveLoadout so that it doesn't use IsItemUnlocked +*/ +botGetPerkUpgrade( perkName ) +{ + perkUpgrade = tablelookup( "mp/perktable.csv", 1, perkName, 8 ); + + if ( perkUpgrade == "" || perkUpgrade == "specialty_null" ) + return "specialty_null"; + + if ( !isDefined( self.pers["bots"]["unlocks"]["upgraded_" + perkName] ) || !self.pers["bots"]["unlocks"]["upgraded_" + perkName] ) + return "specialty_null"; + + return ( perkUpgrade ); +} + +/* + Patches giveLoadout so that it doesn't use IsItemUnlocked +*/ +botLoadoutAllPerks( loadoutEquipment, loadoutPerk1, loadoutPerk2, loadoutPerk3 ) +{ + loadoutEquipment = maps\mp\perks\_perks::validatePerk( 1, loadoutEquipment ); + loadoutPerk1 = maps\mp\perks\_perks::validatePerk( 1, loadoutPerk1 ); + loadoutPerk2 = maps\mp\perks\_perks::validatePerk( 2, loadoutPerk2 ); + loadoutPerk3 = maps\mp\perks\_perks::validatePerk( 3, loadoutPerk3 ); + + self maps\mp\perks\_perks::givePerk( loadoutEquipment ); + self maps\mp\perks\_perks::givePerk( loadoutPerk1 ); + self maps\mp\perks\_perks::givePerk( loadoutPerk2 ); + self maps\mp\perks\_perks::givePerk( loadoutPerk3 ); + + perks[0] = loadoutPerk1; + perks[1] = loadoutPerk2; + perks[2] = loadoutPerk3; + + perkUpgrd[0] = tablelookup( "mp/perktable.csv", 1, loadoutPerk1, 8 ); + perkUpgrd[1] = tablelookup( "mp/perktable.csv", 1, loadoutPerk2, 8 ); + perkUpgrd[2] = tablelookup( "mp/perktable.csv", 1, loadoutPerk3, 8 ); + + for ( i = 0; i < perkUpgrd.size; i++ ) + { + upgrade = perkUpgrd[i]; + perk = perks[i]; + + if ( upgrade == "" || upgrade == "specialty_null" ) + continue; + + if ( isDefined( self.pers["bots"]["unlocks"]["upgraded_" + perk] ) && self.pers["bots"]["unlocks"]["upgraded_" + perk] ) + self maps\mp\perks\_perks::givePerk( upgrade ); + } + +} + +/* + Patches giveLoadout so that it doesn't use IsItemUnlocked +*/ +botPlayerModelForWeapon( weapon, secondary ) +{ + team = self.team; + + + if ( isDefined( game[team + "_model"][weapon] ) ) + { + [[game[team + "_model"][weapon]]](); + return; + } + + + weaponClass = tablelookup( "mp/statstable.csv", 4, weapon, 2 ); + + switch ( weaponClass ) + { + case "weapon_smg": + [[game[team + "_model"]["SMG"]]](); + break; + + case "weapon_assault": + weaponClass = tablelookup( "mp/statstable.csv", 4, secondary, 2 ); + + if ( weaponClass == "weapon_shotgun" ) + [[game[team + "_model"]["SHOTGUN"]]](); + else + [[game[team + "_model"]["ASSAULT"]]](); + + break; + + case "weapon_sniper": + if ( level.environment != "" && isDefined( self.pers["bots"]["unlocks"]["ghillie"] ) && self.pers["bots"]["unlocks"]["ghillie"] ) + [[game[team + "_model"]["GHILLIE"]]](); + else + [[game[team + "_model"]["SNIPER"]]](); + + break; + + case "weapon_lmg": + [[game[team + "_model"]["LMG"]]](); + break; + + case "weapon_riot": + [[game[team + "_model"]["RIOT"]]](); + break; + + default: + [[game[team + "_model"]["ASSAULT"]]](); + break; + } +}