/* _killstreaks modded Author: INeedGames Date: 09/22/2020 Adds killstreak rollover and killstreak HUD.origin DVARS: - scr_killstreak_rollover 0 - (default) killstreaks do not rollover, only get one set of killstreaks 1 - killstreaks rollover, earn killstreaks over again without dying 2 - killstreaks rollover only with hardline pro - scr_maxKillstreakRollover 10 - (default) allow to rollover killstreaks times. (remember you are limited to 10 rollovers as defined in _class.gsc) - scr_killstreak_mod 0 - (default) offsets all killstreaks reward costs by amount - scr_killstreakHud 0 - (default) no HUD 1 - use Puffiamo's killstreak HUD 2 - use NoFate's MW3 killstreak HUD - scr_killstreak_print false - (default) enables the CoD4 (10 Kill Streak!) messages - scr_specialist false - (default) enable specialist from mw3, a player must only have the nuke selected as their killstreak - scr_specialist_perks1 - perks that appear in the first slot (2 killstreak) of specialist - scr_specialist_perks2 - perks that appear in the second slot (4 killstreak) of specialist - scr_specialist_perks3 - perks that appear in the third slot (6 killstreak) of specialist Thanks: H3X1C, Emosewaj, NoFate, Puffiamo, Intricate */ #include maps\mp\_utility; #include maps\mp\gametypes\_hud_util; #include common_scripts\utility; KILLSTREAK_STRING_TABLE = "mp/killstreakTable.csv"; init() { // &&1 Kill Streak! precacheString( &"MP_KILLSTREAK_N" ); precacheString( &"MP_NUKE_ALREADY_INBOUND" ); precacheString( &"MP_UNAVILABLE_IN_LASTSTAND" ); precacheString( &"MP_UNAVAILABLE_WHEN_EMP" ); precacheString( &"MP_UNAVAILABLE_USING_TURRET" ); precacheString( &"MP_UNAVAILABLE_WHEN_INCAP" ); precacheString( &"MP_HELI_IN_QUEUE" ); initKillstreakData(); level.killstreakFuncs = []; level.killstreakSetupFuncs = []; level.killstreakWeapons = []; thread maps\mp\killstreaks\_ac130::init(); thread maps\mp\killstreaks\_remotemissile::init(); thread maps\mp\killstreaks\_uav::init(); thread maps\mp\killstreaks\_airstrike::init(); thread maps\mp\killstreaks\_airdrop::init(); thread maps\mp\killstreaks\_helicopter::init(); thread maps\mp\killstreaks\_autosentry::init(); thread maps\mp\killstreaks\_tank::init(); thread maps\mp\killstreaks\_emp::init(); thread maps\mp\killstreaks\_nuke::init(); level.killstreakRoundDelay = getIntProperty( "scr_game_killstreakdelay", 8 ); setDvarIfUninitialized( "scr_killstreak_rollover", false ); setDvarIfUninitialized( "scr_maxKillstreakRollover", 10 ); setDvarIfUninitialized( "scr_killstreakHud", false ); setDvarIfUninitialized( "scr_killstreak_mod", 0 ); setDvarIfUninitialized( "scr_killstreak_print", false ); setDvarIfUninitialized( "scr_specialist", false ); setDvarIfUninitialized( "scr_specialist_perks1", "specialty_scavenger,specialty_fastreload,specialty_marathon" ); setDvarIfUninitialized( "scr_specialist_perks2", "specialty_bulletdamage,specialty_lightweight,specialty_coldblooded,specialty_explosivedamage,specialty_hardline" ); setDvarIfUninitialized( "scr_specialist_perks3", "specialty_bulletaccuracy,specialty_heartbreaker,specialty_detectexplosive,specialty_extendedmelee,specialty_localjammer" ); level.killstreaksRollOver = getDvarInt("scr_killstreak_rollover"); level.maxKillstreakRollover = getDvarInt("scr_maxKillstreakRollover"); level.killstreakHud = getDvarInt("scr_killstreakHud"); level.killStreakMod = getDvarInt( "scr_killstreak_mod" ); level.killstreakPrint = getDvarInt( "scr_killstreak_print" ); level.allowSpecialist = getDvarInt( "scr_specialist" ); level.specialistPerk1 = getDvar("scr_specialist_perks1"); level.specialistPerk2 = getDvar("scr_specialist_perks2"); level.specialistPerk3 = getDvar("scr_specialist_perks3"); level.specialistData = []; if (level.allowSpecialist) { initSpecialist(); } level thread onPlayerConnect(); } initKillstreakData() { for ( i = 1; true; i++ ) { retVal = tableLookup( KILLSTREAK_STRING_TABLE, 0, i, 1 ); if ( !isDefined( retVal ) || retVal == "" ) break; streakRef = tableLookup( KILLSTREAK_STRING_TABLE, 0, i, 1 ); assert( streakRef != "" ); streakUseHint = tableLookupIString( KILLSTREAK_STRING_TABLE, 0, i, 6 ); assert( streakUseHint != &"" ); precacheString( streakUseHint ); streakEarnDialog = tableLookup( KILLSTREAK_STRING_TABLE, 0, i, 8 ); assert( streakEarnDialog != "" ); game[ "dialog" ][ streakRef ] = streakEarnDialog; streakAlliesUseDialog = tableLookup( KILLSTREAK_STRING_TABLE, 0, i, 9 ); assert( streakAlliesUseDialog != "" ); game[ "dialog" ][ "allies_friendly_" + streakRef + "_inbound" ] = "use_" + streakAlliesUseDialog; game[ "dialog" ][ "allies_enemy_" + streakRef + "_inbound" ] = "enemy_" + streakAlliesUseDialog; streakAxisUseDialog = tableLookup( KILLSTREAK_STRING_TABLE, 0, i, 10 ); assert( streakAxisUseDialog != "" ); game[ "dialog" ][ "axis_friendly_" + streakRef + "_inbound" ] = "use_" + streakAxisUseDialog; game[ "dialog" ][ "axis_enemy_" + streakRef + "_inbound" ] = "enemy_" + streakAxisUseDialog; streakWeapon = tableLookup( KILLSTREAK_STRING_TABLE, 0, i, 12 ); precacheItem( streakWeapon ); streakPoints = int( tableLookup( KILLSTREAK_STRING_TABLE, 0, i, 13 ) ); assert( streakPoints != 0 ); maps\mp\gametypes\_rank::registerScoreInfo( "killstreak_" + streakRef, streakPoints ); streakShader = tableLookup( KILLSTREAK_STRING_TABLE, 0, i, 14 ); precacheShader( streakShader ); streakShader = tableLookup( KILLSTREAK_STRING_TABLE, 0, i, 15 ); if ( streakShader != "" ) precacheShader( streakShader ); } } onPlayerConnect() { for ( ;; ) { level waittill( "connected", player ); if( !isDefined ( player.pers[ "killstreaks" ] ) ) player.pers[ "killstreaks" ] = []; if( !isDefined ( player.pers[ "kID" ] ) ) player.pers[ "kID" ] = 10; if( !isDefined ( player.pers[ "kIDs_valid" ] ) ) player.pers[ "kIDs_valid" ] = []; player.lifeId = 0; if ( isDefined( player.pers["deaths"] ) ) player.lifeId = player.pers["deaths"]; player VisionSetMissilecamForPlayer( game["thermal_vision"] ); player thread onPlayerSpawned(); player thread onPlayerChangeKit(); } } onPlayerSpawned() { self endon( "disconnect" ); for ( ;; ) { self waittill( "spawned_player" ); self thread killstreakUseWaiter(); self thread waitForChangeTeam(); self giveOwnedKillstreakItem( true ); } } onPlayerChangeKit() { self endon( "disconnect" ); for ( ;; ) { self waittill( "changed_kit" ); self giveOwnedKillstreakItem(); self startSpecialist(); self startKSHud(); if (level.killstreakPrint) self thread watchNotifyKSMessage(); } } waitForChangeTeam() { self endon ( "disconnect" ); self notify ( "waitForChangeTeam" ); self endon ( "waitForChangeTeam" ); for ( ;; ) { self waittill ( "joined_team" ); clearKillstreaks(); } } isRideKillstreak( streakName ) { switch( streakName ) { case "helicopter_minigun": case "helicopter_mk19": case "ac130": case "predator_missile": return true; default: return false; } } isCarryKillstreak( streakName ) { switch( streakName ) { case "sentry": case "sentry_gl": return true; default: return false; } } deadlyKillstreak( streakName ) { switch ( streakName ) { case "predator_missile": case "precision_airstrike": case "harrier_airstrike": //case "helicopter": //case "helicopter_flares": case "stealth_airstrike": //case "helicopter_minigun": case "ac130": return true; } return false; } killstreakUsePressed() { streakName = self.pers["killstreaks"][0].streakName; lifeId = self.pers["killstreaks"][0].lifeId; isEarned = self.pers["killstreaks"][0].earned; awardXp = self.pers["killstreaks"][0].awardXp; kID = self.pers["killstreaks"][0].kID; assert( isDefined( streakName ) ); assert( isDefined( level.killstreakFuncs[ streakName ] ) ); if ( !self isOnGround() && ( isRideKillstreak( streakName ) || isCarryKillstreak( streakName ) ) ) return ( false ); if ( self isUsingRemote() ) return ( false ); if ( isDefined( self.selectingLocation ) ) return ( false ); if ( deadlyKillstreak( streakName ) && level.killstreakRoundDelay && getGametypeNumLives() ) { if ( level.gracePeriod - level.inGracePeriod < level.killstreakRoundDelay ) { self iPrintLnBold( &"MP_UNAVAILABLE_FOR_N", (level.killstreakRoundDelay - (level.gracePeriod - level.inGracePeriod)) ); return ( false ); } } if ( (level.teamBased && level.teamEMPed[self.team]) || (!level.teamBased && isDefined( level.empPlayer ) && level.empPlayer != self) ) { self iPrintLnBold( &"MP_UNAVAILABLE_WHEN_EMP" ); return ( false ); } if ( self IsUsingTurret() && ( isRideKillstreak( streakName ) || isCarryKillstreak( streakName ) ) ) { self iPrintLnBold( &"MP_UNAVAILABLE_USING_TURRET" ); return ( false ); } if ( isDefined( self.lastStand ) && isRideKillstreak( streakName ) ) { self iPrintLnBold( &"MP_UNAVILABLE_IN_LASTSTAND" ); return ( false ); } if ( !self isWeaponEnabled() ) return ( false ); if ( streakName == "airdrop" || streakName == "airdrop_sentry_minigun" || streakName == "airdrop_mega" ) { if ( !self [[ level.killstreakFuncs[ streakName ] ]]( lifeId, kID ) ) return ( false ); } else { if ( !self [[ level.killstreakFuncs[ streakName ] ]]( lifeId ) ) return ( false ); } self usedKillstreak( streakName, awardXp ); self shuffleKillStreaksFILO( streakName, kID ); self giveOwnedKillstreakItem(); return ( true ); } //this overwrites killstreak at index 0 and decrements all other killstreaks (FCLS style) shuffleKillStreaksFILO( streakName, kID ) { self _setActionSlot( 4, "" ); arraySize = self.pers["killstreaks"].size; streakIndex = -1; for ( i = 0; i < arraySize; i++ ) { if ( self.pers["killstreaks"][i].streakName != streakName ) continue; if ( isDefined( kID ) && self.pers["killstreaks"][i].kID != kID ) continue; streakIndex = i; break; } assert( streakIndex >= 0 ); self.pers["killstreaks"][streakIndex] = undefined; for( i = streakIndex + 1; i < arraySize; i++ ) { if ( i == arraySize - 1 ) { self.pers["killstreaks"][i-1] = self.pers["killstreaks"][i]; self.pers["killstreaks"][i] = undefined; } else { self.pers["killstreaks"][i-1] = self.pers["killstreaks"][i]; } } } usedKillstreak( streakName, awardXp ) { self playLocalSound( "weap_c4detpack_trigger_plr" ); if ( awardXp ) self thread [[ level.onXPEvent ]]( "killstreak_" + streakName ); self thread maps\mp\gametypes\_missions::useHardpoint( streakName ); awardref = maps\mp\_awards::getKillstreakAwardRef( streakName ); if ( isDefined( awardref ) ) self thread incPlayerStat( awardref, 1 ); team = self.team; if ( level.teamBased ) { thread leaderDialog( team + "_friendly_" + streakName + "_inbound", team ); if ( getKillstreakInformEnemy( streakName ) ) thread leaderDialog( team + "_enemy_" + streakName + "_inbound", level.otherTeam[ team ] ); } else { self thread leaderDialogOnPlayer( team + "_friendly_" + streakName + "_inbound" ); if ( getKillstreakInformEnemy( streakName ) ) { excludeList[0] = self; thread leaderDialog( team + "_enemy_" + streakName + "_inbound", undefined, undefined, excludeList ); } } } clearKillstreaks() { foreach ( index, streakStruct in self.pers["killstreaks"] ) self.pers["killstreaks"][index] = undefined; } getFirstPrimaryWeapon() { weaponsList = self getWeaponsListPrimaries(); assert ( isDefined( weaponsList[0] ) ); assert ( !isKillstreakWeapon( weaponsList[0] ) ); if ( weaponsList[0] == "onemanarmy_mp" ) { assert ( isDefined( weaponsList[1] ) ); assert ( !isKillstreakWeapon( weaponsList[1] ) ); return weaponsList[1]; } return weaponsList[0]; } killstreakUseWaiter() { self endon( "disconnect" ); self endon( "finish_death" ); level endon( "game_ended" ); self.lastKillStreak = 0; if ( !isDefined( self.pers["lastEarnedStreak"] ) ) self.pers["lastEarnedStreak"] = undefined; self thread finishDeathWaiter(); for ( ;; ) { self waittill ( "weapon_change", newWeapon ); if ( !isAlive( self ) ) continue; if ( !isDefined( self.pers["killstreaks"][0] ) ) continue; if ( newWeapon != getKillstreakWeapon( self.pers["killstreaks"][0].streakName ) ) continue; waittillframeend; streakName = self.pers["killstreaks"][0].streakName; result = self killstreakUsePressed(); //no force switching weapon for ridable killstreaks if ( !isRideKillstreak( streakName ) || !result ) { if ( !self hasWeapon( self getLastWeapon() ) ) self switchToWeapon( self getFirstPrimaryWeapon() ); else self switchToWeapon( self getLastWeapon() ); } // give time to switch to the near weapon; when the weapon is none (such as during a "disableWeapon()" period // re-enabling the weapon immediately does a "weapon_change" to the killstreak weapon we just used. In the case that // we have two of that killstreak, it immediately uses the second one if ( self getCurrentWeapon() == "none" ) { while ( self getCurrentWeapon() == "none" ) wait ( 0.05 ); waittillframeend; } } } finishDeathWaiter() { self endon ( "disconnect" ); level endon ( "game_ended" ); self waittill ( "death" ); wait ( 0.05 ); self notify ( "finish_death" ); self.pers["lastEarnedStreak"] = undefined; } checkKillstreakReward( streakCount ) { self notify( "got_killstreak", streakCount ); maxVal = 0; killStreaks = []; foreach ( streakVal, streakName in self.killStreaks ) { killStreaks[streakName] = streakVal; if ( streakVal > maxVal ) maxVal = streakVal; } foreach ( streakVal, streakName in self.killStreaks ) { actualVal = streakVal + level.killStreakMod; if ( actualVal > streakCount ) break; if ( isDefined( self.pers["lastEarnedStreak"] ) && killStreaks[streakName] <= killStreaks[self.pers["lastEarnedStreak"]] ) continue; if ( isSubStr( streakName, "-rollover" ) ) { if (!level.killstreaksRollover || (level.killstreaksRollover == 2 && !self _hasPerk("specialty_rollover"))) continue; else { curRollover = int(strtok(strtok(streakName, "-")[1], "rollover")[0]); if (curRollover > level.maxKillstreakRollover) continue; if ( isDefined( game["defcon"] ) && game["defcon"] > 2 ) { self.pers["lastEarnedStreak"] = streakName; continue; } useStreakName = strTok( streakName, "-" )[0]; } } else { useStreakName = streakName; } if ( self tryGiveKillstreak( useStreakName, int(max( actualVal, streakCount )) ) ) { self thread killstreakEarned( useStreakName ); self.pers["lastEarnedStreak"] = streakName; } } } killstreakEarned( streakName ) { if ( self getPlayerData( "killstreaks", 0 ) == streakName ) { self.firstKillstreakEarned = getTime(); } else if ( self getPlayerData( "killstreaks", 2 ) == streakName && isDefined( self.firstKillstreakEarned ) ) { if ( getTime() - self.firstKillstreakEarned < 20000 ) self thread maps\mp\gametypes\_missions::genericChallenge( "wargasm" ); } } rewardNotify( streakName, streakVal ) { self endon( "disconnect" ); data = level.specialistData[streakName]; if (isDefined(data)) { perk = streakName; name = data.name; description = data.description; shader = data.shader; proPerk = tablelookup( "mp/perktable.csv", 1, perk, 8 ); hasProPerk = self isItemUnlocked(proPerk); if (hasProPerk) shader = data.shader_pro; notifyData = spawnStruct(); notifyData.glowColor = getGoodColor(); notifyData.hideWhenInMenu = false; notifyData.titleText = name; notifyData.notifyText = description; notifyData.iconName = shader; notifyData.sound = "mp_bonus_start"; if (perk == "specialty_onemanarmy") { notifyData.titleText = "Specialist Bonus"; notifyData.notifyText = "Received all Perks!"; } self maps\mp\gametypes\_hud_message::notifyMessage( notifyData ); return; } self maps\mp\gametypes\_hud_message::killstreakSplashNotify( streakName, streakVal ); } tryGiveKillstreak( streakName, streakVal ) { level notify ( "gave_killstreak", streakName ); if ( !level.gameEnded ) self thread rewardNotify( streakName, streakVal ); self giveKillstreak( streakName, streakVal, true ); return true; } giveKillstreak( streakName, isEarned, awardXp, owner ) { self endon ( "disconnect" ); weapon = getKillstreakWeapon( streakName ); self giveKillstreakWeapon( weapon ); // shuffle existing killstreaks up a notch for( i = self.pers["killstreaks"].size; i >= 0; i-- ) self.pers["killstreaks"][i + 1] = self.pers["killstreaks"][i]; self.pers["killstreaks"][0] = spawnStruct(); self.pers["killstreaks"][0].streakName = streakName; self.pers["killstreaks"][0].earned = isDefined( isEarned ) && isEarned; self.pers["killstreaks"][0].awardxp = isDefined( awardXp ) && awardXp; self.pers["killstreaks"][0].owner = owner; self.pers["killstreaks"][0].kID = self.pers["kID"]; self.pers["kIDs_valid"][self.pers["kID"]] = true; self.pers["kID"]++; if ( !self.pers["killstreaks"][0].earned ) self.pers["killstreaks"][0].lifeId = -1; else self.pers["killstreaks"][0].lifeId = self.pers["deaths"]; // probably obsolete unless we bring back the autoshotty if ( isdefined( level.killstreakSetupFuncs[ streakName ] ) ) self [[ level.killstreakSetupFuncs[ streakName ] ]]( streakName ); if ( isDefined( isEarned ) && isEarned && isDefined( awardXp ) && awardXp ) self notify( "received_earned_killstreak" ); } giveKillstreakWeapon( weapon ) { weaponList = self getWeaponsListItems(); foreach ( item in weaponList ) { if ( !isSubStr( item, "killstreak" ) ) continue; if ( self getCurrentWeapon() == item ) continue; self takeWeapon( item ); } self _giveWeapon( weapon, 0 ); self _setActionSlot( 4, "weapon", weapon ); } getStreakCost( streakName ) { return int( tableLookup( KILLSTREAK_STRING_TABLE, 1, streakName, 4 ) ); } getKillstreakHint( streakName ) { return tableLookupIString( KILLSTREAK_STRING_TABLE, 1, streakName, 6 ); } getKillstreakInformEnemy( streakName ) { return int( tableLookup( KILLSTREAK_STRING_TABLE, 1, streakName, 11 ) ); } getKillstreakSound( streakName ) { return tableLookup( KILLSTREAK_STRING_TABLE, 1, streakName, 7 ); } getKillstreakDialog( streakName ) { return tableLookup( KILLSTREAK_STRING_TABLE, 1, streakName, 8 ); } getKillstreakWeapon( streakName ) { return tableLookup( KILLSTREAK_STRING_TABLE, 1, streakName, 12 ); } getKillstreakIcon( streakName ) { data = level.specialistData[streakName]; if (isDefined(data)) { perk = streakName; shader = data.shader; proPerk = tablelookup( "mp/perktable.csv", 1, perk, 8 ); hasProPerk = self isItemUnlocked(proPerk); if (hasProPerk) shader = data.shader_pro; return shader; } return tableLookup( KILLSTREAK_STRING_TABLE, 1, streakName, 14 ); } getKillstreakCrateIcon( streakName ) { return tableLookup( KILLSTREAK_STRING_TABLE, 1, streakName, 15 ); } giveOwnedKillstreakItem( skipDialog ) { if ( !isDefined( self.pers["killstreaks"][0] ) ) return; streakName = self.pers["killstreaks"][0].streakName; weapon = getKillstreakWeapon( streakName ); self giveKillstreakWeapon( weapon ); if ( !isDefined( skipDialog ) && !level.inGracePeriod ) self leaderDialogOnPlayer( streakName, "killstreak_earned" ); } initRideKillstreak() { self _disableUsability(); result = self initRideKillstreak_internal(); if ( isDefined( self ) ) self _enableUsability(); return result; } initRideKillstreak_internal() { laptopWait = self waittill_any_timeout( 1.0, "disconnect", "death", "weapon_switch_started" ); if ( laptopWait == "weapon_switch_started" ) return ( "fail" ); if ( !isAlive( self ) ) return "fail"; if ( laptopWait == "disconnect" || laptopWait == "death" ) { if ( laptopWait == "disconnect" ) return ( "disconnect" ); if ( self.team == "spectator" ) return "fail"; return ( "success" ); } if ( self isEMPed() || self isNuked() ) { return ( "fail" ); } self VisionSetNakedForPlayer( "black_bw", 0.75 ); blackOutWait = self waittill_any_timeout( 0.80, "disconnect", "death" ); if ( blackOutWait != "disconnect" ) { self thread clearRideIntro( 1.0 ); if ( self.team == "spectator" ) return "fail"; } if ( !isAlive( self ) ) return "fail"; if ( self isEMPed() || self isNuked() ) return "fail"; if ( blackOutWait == "disconnect" ) return ( "disconnect" ); else return ( "success" ); } clearRideIntro( delay ) { self endon( "disconnect" ); if ( isDefined( delay ) ) wait( delay ); //self freezeControlsWrapper( false ); self VisionSetNakedForPlayer( getMapVision(), 0 ); } destroyOnEvents(elem) { self waittill_either("disconnect", "start_killstreak_hud"); elem destroy(); } initKillstreakHud(inity) { self endon( "disconnect" ); self notify( "start_killstreak_hud" ); self endon( "start_killstreak_hud" ); streakVals = GetArrayKeys(self.killStreaks); self.killStreakHudElems = []; // the killstreak counter index = self.killStreakHudElems.size; self.killStreakHudElems[index] = self createFontString( "objective", 2 ); self.killStreakHudElems[index].foreground = false; self.killStreakHudElems[index].hideWhenInMenu = true; self.killStreakHudElems[index].fontScale = 0.60; self.killStreakHudElems[index].font = "hudbig"; self.killStreakHudElems[index].alpha = 1; self.killStreakHudElems[index].glow = 1; self.killStreakHudElems[index].glowColor = ( 0, 0, 1 ); self.killStreakHudElems[index].glowAlpha = 1; self.killStreakHudElems[index].color = ( 1.0, 1.0, 1.0 ); self thread destroyOnEvents(self.killStreakHudElems[index]); highestStreak = -1; for (i = 0; i < streakVals.size; i++) { streakVal = streakVals[i]; streakName = self.killStreaks[streakVal]; if (isSubStr(streakName, "-rollover")) continue; streakShader = getKillstreakIcon( streakName ); streakCost = streakVal; // each killstreak icon index = self.killStreakHudElems.size; self.killStreakHudElems[index] = self createFontString( "objective", 2 ); self.killStreakHudElems[index].foreground = false; self.killStreakHudElems[index].hideWhenInMenu = true; self.killStreakHudElems[index].fontScale = 0.60; self.killStreakHudElems[index].font = "hudbig"; self.killStreakHudElems[index].glow = 1; self.killStreakHudElems[index].glowColor = ( 0, 0, 1 ); self.killStreakHudElems[index].glowAlpha = 1; self.killStreakHudElems[index].color = ( 1.0, 1.0, 1.0 ); self.killStreakHudElems[index] setPoint( "RIGHT", "RIGHT", 0, inity - 25 * i ); self.killStreakHudElems[index] setShader( streakShader, 20, 20 ); self.killStreakHudElems[index].ks_cost = streakCost; self.killStreakHudElems[index].ks_name = streakName; self thread destroyOnEvents(self.killStreakHudElems[index]); if (streakCost > highestStreak) highestStreak = streakCost; } for(first=true;;) { if (first) first = false; else self waittill( "killed_enemy" ); curStreak = self.pers["cur_kill_streak"]; timesRolledOver = int(curStreak / highestStreak); if (level.killstreaksRollover == 1 || (level.killstreaksRollover == 2 && self _hasPerk("specialty_rollover"))) curStreak %= highestStreak; if (timesRolledOver > level.maxKillstreakRollover) curStreak = highestStreak; isUnderAStreak = false; for (i = self.killStreakHudElems.size - 1; i >= 1; i--) { streakElem = self.killStreakHudElems[i]; if (curStreak >= streakElem.ks_cost || (timesRolledOver > 0 && isSubStr(streakElem.ks_name, "specialty_"))) streakElem.alpha = 1; else { isUnderAStreak = true; self.killStreakHudElems[0] setPoint( "RIGHT", "RIGHT", -25, inity - 25 * (i - 1) ); self.killStreakHudElems[0] setText( streakElem.ks_cost - curStreak ); streakElem.alpha = 0.5; } } if (!isUnderAStreak && self.killStreakHudElems.size) { self.killStreakHudElems[0] setPoint( "RIGHT", "RIGHT", -25, inity - 25 * (self.killStreakHudElems.size - 1 - 1) ); self.killStreakHudElems[0] setText( "Done" ); } } } initMW3KillstreakHud() { self endon( "disconnect" ); self notify( "start_killstreak_hud" ); self endon( "start_killstreak_hud" ); streakVals = GetArrayKeys(self.killStreaks); self.killStreakHudElems = []; self.killStreakShellsElems = []; highestStreak = -1; for (i = 0; i < streakVals.size; i++) { streakVal = streakVals[i]; streakName = self.killStreaks[streakVal]; if (isSubStr(streakName, "-rollover")) continue; streakShader = getKillstreakIcon( streakName ); streakCost = streakVal; if (streakCost > highestStreak) highestStreak = streakCost; // the shader ksIcon = createIcon( streakShader, 20, 20 ); ksIcon setPoint( "BOTTOM RIGHT", "BOTTOM RIGHT", -32, -90 + -25 * i ); ksIcon.alpha = 0.4; ksIcon.hideWhenInMenu = true; ksIcon.foreground = true; ksIcon.ks_cost = streakCost; ksIcon.ks_name = streakName; self thread destroyOnEvents(ksIcon); self.killStreakHudElems[self.killStreakHudElems.size] = ksIcon; } // the shells if (highestStreak > 0) { h = -53; for(i = 0; i < highestStreak; i++) { ksShell = NewClientHudElem( self ); ksShell.x = 40; ksShell.y = h; ksShell.alignX = "right"; ksShell.alignY = "bottom"; ksShell.horzAlign = "right"; ksShell.vertAlign = "bottom"; ksShell setshader("white", 10, 2); ksShell.alpha = 0.3; ksShell.hideWhenInMenu = true; ksShell.foreground = false; self thread destroyOnEvents(ksShell); self.killStreakShellsElems[i] = ksShell; h -= 4; } } for(first=true;;) { if (first) first = false; else self waittill( "killed_enemy" ); curStreak = self.pers["cur_kill_streak"]; timesRolledOver = int(curStreak / highestStreak); if (level.killstreaksRollover == 1 || (level.killstreaksRollover == 2 && self _hasPerk("specialty_rollover"))) curStreak %= highestStreak; if (timesRolledOver > level.maxKillstreakRollover) curStreak = highestStreak; nextHighest = 999; // update the ks icons for (i = 0; i < self.killStreakHudElems.size; i++) { elem = self.killStreakHudElems[i]; if (curStreak >= elem.ks_cost || (timesRolledOver > 0 && isSubStr(elem.ks_name, "specialty_"))) elem.alpha = 0.9; else { elem.alpha = 0.4; if (nextHighest > elem.ks_cost) nextHighest = elem.ks_cost; } } // update the shells for (i = 0; i < self.killStreakShellsElems.size; i++) { elem = self.killStreakShellsElems[i]; if (curStreak > i) elem.alpha = 0.85; else if (i >= nextHighest) elem.alpha = 0; else elem.alpha = 0.3; } } } getGoodColor() { color = []; //Intricate - This is momo5502's code, rather interesting way too :D. for( i = 0; i < 3; i++ ) { color[i] = randomint( 2 ); } if( color[0] == color[1] && color[1] == color[2] ) { rand = randomint(3); color[rand] += 1; color[rand] %= 2; } return ( color[0], color[1], color[2] ); } getPerkMaterial( perk ) { return tableLookUp( "mp/perkTable.csv", 1, perk, 3 ); } initSpecialist() { PrecacheShader(getPerkMaterial(tablelookup( "mp/perktable.csv", 1, "specialty_scavenger", 8 ))); PrecacheShader(getPerkMaterial(tablelookup( "mp/perktable.csv", 1, "specialty_fastreload", 8 ))); PrecacheShader(getPerkMaterial(tablelookup( "mp/perktable.csv", 1, "specialty_marathon", 8 ))); PrecacheShader(getPerkMaterial(tablelookup( "mp/perktable.csv", 1, "specialty_bulletdamage", 8 ))); PrecacheShader(getPerkMaterial(tablelookup( "mp/perktable.csv", 1, "specialty_lightweight", 8 ))); PrecacheShader(getPerkMaterial(tablelookup( "mp/perktable.csv", 1, "specialty_coldblooded", 8 ))); PrecacheShader(getPerkMaterial(tablelookup( "mp/perktable.csv", 1, "specialty_explosivedamage", 8 ))); PrecacheShader(getPerkMaterial(tablelookup( "mp/perktable.csv", 1, "specialty_hardline", 8 ))); PrecacheShader(getPerkMaterial(tablelookup( "mp/perktable.csv", 1, "specialty_bulletaccuracy", 8 ))); PrecacheShader(getPerkMaterial(tablelookup( "mp/perktable.csv", 1, "specialty_heartbreaker", 8 ))); PrecacheShader(getPerkMaterial(tablelookup( "mp/perktable.csv", 1, "specialty_detectexplosive", 8 ))); PrecacheShader(getPerkMaterial(tablelookup( "mp/perktable.csv", 1, "specialty_extendedmelee", 8 ))); PrecacheShader(getPerkMaterial(tablelookup( "mp/perktable.csv", 1, "specialty_localjammer", 8 ))); PrecacheShader(getPerkMaterial("specialty_scavenger")); PrecacheShader(getPerkMaterial("specialty_fastreload")); PrecacheShader(getPerkMaterial("specialty_marathon")); PrecacheShader(getPerkMaterial("specialty_bulletdamage")); PrecacheShader(getPerkMaterial("specialty_lightweight")); PrecacheShader(getPerkMaterial("specialty_coldblooded")); PrecacheShader(getPerkMaterial("specialty_explosivedamage")); PrecacheShader(getPerkMaterial("specialty_hardline")); PrecacheShader(getPerkMaterial("specialty_bulletaccuracy")); PrecacheShader(getPerkMaterial("specialty_heartbreaker")); PrecacheShader(getPerkMaterial("specialty_detectexplosive")); PrecacheShader(getPerkMaterial("specialty_extendedmelee")); PrecacheShader(getPerkMaterial("specialty_localjammer")); PrecacheShader("specialty_onemanarmy"); PrecacheShader("specialty_onemanarmy_upgrade"); PrecacheShader("specialty_none"); //Strings PrecacheString( &"PERKS_MARATHON" ); PrecacheString( &"PERKS_SLEIGHT_OF_HAND" ); PrecacheString( &"PERKS_SCAVENGER" ); //-- PrecacheString( &"PERKS_STOPPING_POWER" ); PrecacheString( &"PERKS_LIGHTWEIGHT" ); PrecacheString( &"PERKS_COLDBLOODED" ); PrecacheString( &"PERKS_DANGERCLOSE" ); PrecacheString( &"PERKS_HARDLINE" ); //-- PrecacheString( &"PERKS_EXTENDEDMELEE" ); PrecacheString( &"PERKS_STEADY_AIM" ); PrecacheString( &"PERKS_LOCALJAMMER" ); PrecacheString( &"PERKS_BOMB_SQUAD" ); PrecacheString( &"PERKS_NINJA" ); //Description PrecacheString( &"PERKS_DESC_MARATHON" ); PrecacheString( &"PERKS_FASTER_RELOADING" ); PrecacheString( &"PERKS_DESC_SCAVENGER" ); //-- PrecacheString( &"PERKS_INCREASED_BULLET_DAMAGE" ); PrecacheString( &"PERKS_DESC_LIGHTWEIGHT" ); PrecacheString( &"PERKS_DESC_COLDBLOODED" ); PrecacheString( &"PERKS_HIGHER_EXPLOSIVE_WEAPON" ); PrecacheString( &"PERKS_DESC_HARDLINE" ); //-- PrecacheString( &"PERKS_DESC_EXTENDEDMELEE" ); PrecacheString( &"PERKS_INCREASED_HIPFIRE_ACCURACY" ); PrecacheString( &"PERKS_DESC_LOCALJAMMER" ); PrecacheString( &"PERKS_ABILITY_TO_SEEK_OUT_ENEMY" ); PrecacheString( &"PERKS_DESC_HEARTBREAKER" ); perks = []; perks[perks.size] = strtok(level.specialistPerk1, ","); perks[perks.size] = strtok(level.specialistPerk2, ","); perks[perks.size] = strtok(level.specialistPerk3, ","); perks[perks.size] = strtok("specialty_none,specialty_onemanarmy", ","); for (i = 0; i < perks.size; i++) { for (h = 0; h < perks[i].size; h++) { perk = perks[i][h]; data = spawnStruct(); data.shader = getPerkMaterial(perk); data.shader_pro = getPerkMaterial(tablelookup( "mp/perktable.csv", 1, perk, 8 )); data.name = tableLookUpIString( "mp/perkTable.csv", 1, perk, 2 ); data.description = tableLookUpIString( "mp/perkTable.csv", 1, perk, 4 ); level.specialistData[perk] = data; level.killstreakSetupFuncs[perk] = ::onGetPerkStreak; } } } onGetPerkStreak(perk, wasForced) { proPerk = tablelookup( "mp/perktable.csv", 1, perk, 8 ); hasProPerk = self isItemUnlocked(proPerk); if (!isDefined(wasForced)) { self shuffleKillStreaksFILO( perk ); self giveOwnedKillstreakItem(true); } if (perk == "specialty_none") { } else if (perk == "specialty_onemanarmy") { perks = []; perks[perks.size] = strtok(level.specialistPerk1, ","); perks[perks.size] = strtok(level.specialistPerk2, ","); perks[perks.size] = strtok(level.specialistPerk3, ","); for (i = 0; i < perks.size; i++) { for (h = 0; h < perks[i].size; h++) { perk = perks[i][h]; proPerk = tablelookup( "mp/perktable.csv", 1, perk, 8 ); self _setPerk(perk); if ( self isItemUnlocked( proPerk ) ) self _setPerk(proPerk); } } } else { self _setPerk(perk); if ( hasProPerk ) self _setPerk(proPerk); } self applySpecialistKillstreaks(); // maybe hardline changes the values } chooseAPerk(perks) { perks = strtok(perks, ","); while (perks.size) { perk = random(perks); perks = array_remove(perks, perk); if (self _hasPerk(perk)) continue; return perk; } return "specialty_none"; } startSpecialist() { if (!level.allowSpecialist) return; // only start if we have only the nuke killstreak shouldDoSpecialist = undefined; streakVals = GetArrayKeys(self.killStreaks); for (i = 0; i < streakVals.size; i++) { streakVal = streakVals[i]; streakName = self.killStreaks[streakVal]; if (isSubStr(streakName, "-rollover")) continue; if (isDefined(shouldDoSpecialist) && !shouldDoSpecialist) break; if (streakName == "nuke") shouldDoSpecialist = true; else shouldDoSpecialist = false; } if (!isDefined(shouldDoSpecialist) || !shouldDoSpecialist) return; if (!isDefined(self.pers["specialist_perks"])) self.pers["specialist_perks"] = []; if (!isDefined(self.pers["specialist_perks"][self.class_num])) { self.pers["specialist_perks"][self.class_num] = []; self.pers["specialist_perks"][self.class_num][0] = chooseAPerk(level.specialistPerk1); self.pers["specialist_perks"][self.class_num][1] = chooseAPerk(level.specialistPerk2); self.pers["specialist_perks"][self.class_num][2] = chooseAPerk(level.specialistPerk3); } self.startKillStreaks = self.killStreaks; self.startedWithHardline = (self _hasPerk( "specialty_hardline" )); self applySpecialistKillstreaks(); // check cur_streak for perks curStreak = self.pers["cur_kill_streak"]; streakVals = GetArrayKeys(self.killStreaks); for (i = 0; i < streakVals.size; i++) { streakVal = streakVals[i]; streakName = self.killStreaks[streakVal]; if (!isSubStr(streakName, "specialty_")) continue; if (curStreak < streakVal) continue; self onGetPerkStreak(streakName, true); } } getSpecialistKillstreakCount(slot, count) { dvarAmount = getDVarInt("scr_specialist_killCount_" + slot); if (dvarAmount < 2) return count; return dvarAmount; } applySpecialistKillstreaks() { if ( self _hasPerk( "specialty_hardline" ) ) modifier = -1; else modifier = 0; killstreaks = []; killstreaks[getSpecialistKillstreakCount(0, 2) + modifier] = self.pers["specialist_perks"][self.class_num][0]; killstreaks[getSpecialistKillstreakCount(1, 4) + modifier] = self.pers["specialist_perks"][self.class_num][1]; killstreaks[getSpecialistKillstreakCount(2, 6) + modifier] = self.pers["specialist_perks"][self.class_num][2]; killstreaks[getSpecialistKillstreakCount(3, 8) + modifier] = "specialty_onemanarmy"; maxVal = -1; oldStreaks = []; streakVals = GetArrayKeys(self.startKillStreaks); for (i = 0; i < streakVals.size; i++) { streakVal = streakVals[i]; streakName = self.startKillStreaks[streakVal]; if (isSubStr(streakName, "-rollover")) continue; if (!self.startedWithHardline) streakVal += modifier; if (streakVal > maxVal) maxVal = streakVal; oldStreaks[streakVal] = streakName; } if (maxVal < (8 + modifier)) maxVal = 8 + modifier; // build new killstreaks with merged specialists newKillstreaks = []; for (i = 0; i <= maxVal; i++) { if (isDefined(killstreaks[i])) { newKillstreaks[i] = killstreaks[i]; continue; } if (isDefined(oldStreaks[i])) { newKillstreaks[i] = oldStreaks[i]; continue; } } // defcon rollover maxRollOvers = 10; for ( rollOver = 1; rollOver <= maxRollOvers; rollOver++ ) { streakVals = GetArrayKeys(oldStreaks); for (i = 0; i < streakVals.size; i++) { streakVal = streakVals[i]; streakName = oldStreaks[streakVal]; newKillstreaks[ streakVal + (maxVal*rollOver) ] = streakName + "-rollover" + rollOver; } } self.killStreaks = newKillstreaks; // update the hud incase hardline changed the values self startKSHud(); // give xp every second kill like in mw3 self thread watchSpecialistOnKill(); } watchSpecialistOnKill() { self endon("disconnect"); waittillframeend; self notify("watchSpecialistOnKill"); self endon("watchSpecialistOnKill"); for (lastKs = self.pers["cur_kill_streak"];;) { self waittill( "killed_enemy" ); for (curStreak = lastKs + 1; curStreak <= self.pers["cur_kill_streak"]; curStreak++) { if (curStreak % 2 == 1) continue; self thread maps\mp\gametypes\_rank::giveRankXP( "specialist_bonus", 50 ); self thread underScorePopup("Specialist Bonus!", (1, 1, 0.5), 0); } lastKs = self.pers["cur_kill_streak"]; } } watchNotifyKSMessage() { self endon("disconnect"); self endon("changed_kit"); for (lastKs = self.pers["cur_kill_streak"];;) { self waittill( "killed_enemy" ); for (curStreak = lastKs + 1; curStreak <= self.pers["cur_kill_streak"]; curStreak++) { if (curStreak == 5) continue; if (curStreak % 5 != 0) continue; self thread streakNotify(curStreak); } lastKs = self.pers["cur_kill_streak"]; } } streakNotify( streakVal ) { self endon("disconnect"); self notify("streakNotifyCoD4"); self endon("streakNotifyCoD4"); wait 0.1; notifyData = spawnStruct(); notifyData.titleText = streakVal + " Kill Streak!"; self maps\mp\gametypes\_hud_message::notifyMessage( notifyData ); iprintln( self.name + " has a killstreak of " + streakVal + "!" ); } underScorePopup(string, hudColor, glowAlpha) { // Display text under the score popup self endon( "disconnect" ); if ( string == "" ) return; if (level.hardcoreMode) return; self notify( "underScorePopup" ); self endon( "underScorePopup" ); if (!isDefined(self.mw3_scorePopup)) { // Create the under score popup element self.mw3_scorePopup = newClientHudElem( self ); self.mw3_scorePopup.horzAlign = "center"; self.mw3_scorePopup.vertAlign = "middle"; self.mw3_scorePopup.alignX = "center"; self.mw3_scorePopup.alignY = "middle"; self.mw3_scorePopup.x = 35; self.mw3_scorePopup.y = -48; self.mw3_scorePopup.font = "hudbig"; self.mw3_scorePopup.fontscale = 0.65; self.mw3_scorePopup.archived = false; self.mw3_scorePopup.color = (0.5, 0.5, 0.5); self.mw3_scorePopup.sort = 10000; } self.mw3_scorePopup.color = hudColor; self.mw3_scorePopup.glowColor = hudColor; self.mw3_scorePopup.glowAlpha = glowAlpha; self.mw3_scorePopup setText(string); self.mw3_scorePopup.alpha = 0.85; wait 1.0; self.mw3_scorePopup fadeOverTime( 0.75 ); self.mw3_scorePopup.alpha = 0; wait 0.75; self.mw3_scorePopup destroy(); self.mw3_scorePopup = undefined; } startKSHud() { if (level.hardcoreMode) return; if (level.killstreakHud == 1) self thread initKillstreakHud( 145 ); else if (level.killstreakHud == 2) self thread initMW3KillstreakHud(); }