2025-05-21 16:23:17 +02:00

680 lines
17 KiB
Plaintext

#include maps\mp\_utility;
#include common_scripts\utility;
CONST_COOP_USE_BAR_TIME_MIN = 1.25;
CONST_COOP_USE_BAR_TIME_MAX = 2.5;
init()
{
if ( !level.teamBased )
return;
level.streakSupportQueueAllies = [];
level.streakSupportQueueAxis = [];
level.streakSupporDisabledCount = [];
SetDvar( "scr_coop_util_delay", "1" );
}
/*
=============
///ScriptDocBegin
"Name: promptForStreakSupport( <streakPlayer>, <joinText>, <needSupportVO>, <buddyJoinedVO> )"
"Summary: Shows a hold-x-to-support prompt on all friendly players in team based modes. Returns the id of the prompt so it can be stopped."
"Module: Utility"
"CallOn: Level"
"MandatoryArg: <streakTeam>: Team that owns the scorestreak, should match streakPlayer's team if defined."
"MandatoryArg: <joinText>: Text to display for the prompt to the allies."
"OptionalArg: <splashRef>: Reference string of the splash to show."
"OptionalArg: <needSupportVO>: VO reference to play on characters telling them coop support is needed."
"OptionalArg: <buddyJoinedVO>: VO reference to play on streak player telling him a buddy has joined."
"OptionalArg: <streakPlayer>: Player that owns the scorestreak."
"OptionalArg: <joinedVO>: VO reference to play on player telling him he has joined the streak."
"Example: promptID = promptForStreakSupport( player, &"MP_OSP_JOIN", "orbitalsupport_splash", "osp_assist", "osp_buddy_joined", player, "osp_joined" );"
"SPMP: MP"
///ScriptDocEnd
=============
*/
promptForStreakSupport( streakTeam, joinText, splashRef, needSupportVO, buddyJoinedVO, streakPlayer, joinedVO )
{
if ( !level.teamBased ) // only do coop prompts in team based modes
return;
AssertEx( !IsDefined( streakPlayer ) || streakPlayer.team == streakTeam, "promptForStreakSupport: streakPlayer's team needs to match streakTeam." );
streakPromptOrigin = ( 0, 0, 0 );
if ( IsDefined( streakPlayer ) )
streakPromptOrigin = streakPlayer.origin;
streakPrompt = Spawn( "script_model", streakPromptOrigin );
streakPrompt Hide();
streakPrompt.team = streakTeam;
streakPrompt.needSupportVO = needSupportVO;
streakPrompt.buddyJoinedVO = buddyJoinedVO;
streakPrompt.streakPlayer = streakPlayer;
streakPrompt.joinedVO = joinedVO;
streakPrompt.joinText = joinText;
streakPrompt.splashRef = splashRef;
streakPrompt.active = false;
streakPrompt.promptID = getUniqueStreakPromptID();
if ( IsDefined( streakPlayer ) )
streakPrompt DisablePlayerUse( streakPlayer );
addStreakSupportPrompt( streakPrompt );
return streakPrompt.promptID;
}
/*
=============
///ScriptDocBegin
"Name: stopPromptForStreakSupport( <promptID> )"
"Summary: Hides/removes the hold-x-to-support prompt for the prompt ID."
"Module: Utility"
"CallOn: Level"
"MandatoryArg: <promptID>: Id assigned to coop prompt when started."
"Example: stopPromptForStreakSupport( id );"
"SPMP: MP"
///ScriptDocEnd
=============
*/
stopPromptForStreakSupport( promptID )
{
if ( !level.teamBased ) // only do coop prompts in team based modes
return;
foreach ( streakPrompt in level.streakSupportQueueAllies )
{
if ( streakPrompt.promptID == promptID )
{
thread removeStreakSupportPrompt( streakPrompt );
return;
}
}
foreach ( streakPrompt in level.streakSupportQueueAxis )
{
if ( streakPrompt.promptID == promptID )
{
thread removeStreakSupportPrompt( streakPrompt );
return;
}
}
}
/*
=============
///ScriptDocBegin
"Name: waittillBuddyJoinedStreak( <promptID> )"
"Summary: Will wait until someone activates the prompt and return the player."
"Module: Utility"
"CallOn: Level"
"MandatoryArg: <promptID>: Id assigned to coop prompt when started."
"Example: buddy = waittillBuddyJoinedStreak( id );"
"SPMP: MP"
///ScriptDocEnd
=============
*/
waittillBuddyJoinedStreak( promptId )
{
while ( true )
{
level waittill( "buddyJoinedStreak", buddy, id );
if ( id == promptID )
return buddy;
}
}
/*
=============
///ScriptDocBegin
"Name: playerSetupCoopStreak()"
"Summary: Call this during the blackout to setup the players weapon so the hud hides."
"Module: Utility"
"CallOn: Player"
"Example: buddy playerSetupCoopStreak();"
"SPMP: MP"
///ScriptDocEnd
=============
*/
playerSetupCoopStreak( delay )
{
self playerSetupCoopStreakInternal( delay );
}
/*
=============
///ScriptDocBegin
"Name: playerResetAfterCoopStreak()"
"Summary: Will take away the laptop and reset the player."
"Module: Utility"
"CallOn: Player"
"Example: buddy playerResetAfterCoopStreak();"
"SPMP: MP"
///ScriptDocEnd
=============
*/
playerResetAfterCoopStreak()
{
self playerResetAfterCoopStreakInternal();
}
/*
=============
///ScriptDocBegin
"Name: playerStopPromptForStreakSupport()"
"Summary: Hides all hold-x-to-support prompts for the player."
"Module: Utility"
"CallOn: Level"
"Example: player playerStopPromptForStreakSupport();"
"SPMP: MP"
///ScriptDocEnd
=============
*/
playerStopPromptForStreakSupport()
{
if ( !level.teamBased ) // only do coop prompts in team based modes
return;
if( !IsDefined(level.streakSupporDisabledCount[self.guid]) )
level.streakSupporDisabledCount[self.guid] = 0;
level.streakSupporDisabledCount[self.guid]++;
if( level.streakSupporDisabledCount[self.guid] > 1 )
return;
if ( self.team == "allies" )
{
foreach ( streakPrompt in level.streakSupportQueueAllies )
{
streakPrompt DisablePlayerUse( self );
}
}
else
{
foreach ( streakPrompt in level.streakSupportQueueAxis )
{
streakPrompt DisablePlayerUse( self );
}
}
}
/*
=============
///ScriptDocBegin
"Name: playerStartPromptForStreakSupport()"
"Summary: Shows all hold-x-to-support prompts for the player."
"Module: Utility"
"CallOn: Level"
"Example: player playerStartPromptForStreakSupport();"
"SPMP: MP"
///ScriptDocEnd
=============
*/
playerStartPromptForStreakSupport()
{
if ( !level.teamBased ) // only do coop prompts in team based modes
return;
level.streakSupporDisabledCount[self.guid]--;
assert( level.streakSupporDisabledCount[self.guid] >= 0 );
if( level.streakSupporDisabledCount[self.guid] > 0 )
return;
if ( self.team == "allies" )
{
foreach ( streakPrompt in level.streakSupportQueueAllies )
{
if ( self != streakPrompt.streakPlayer )
streakPrompt EnablePlayerUse( self );
}
}
else
{
foreach ( streakPrompt in level.streakSupportQueueAxis )
{
if ( self != streakPrompt.streakPlayer )
streakPrompt EnablePlayerUse( self );
}
}
}
// ----------------------------------------------------------------------------------
// Queue management
addStreakSupportPrompt( streakPrompt )
{
if ( streakPrompt.team == "allies" )
{
level.streakSupportQueueAllies[ level.streakSupportQueueAllies.size ] = streakPrompt;
if ( level.streakSupportQueueAllies.size == 1 )
level thread startStreakSupportPrompt( streakPrompt );
}
else // team == "axis"
{
level.streakSupportQueueAxis[ level.streakSupportQueueAxis.size ] = streakPrompt;
if ( level.streakSupportQueueAxis.size == 1 )
level thread startStreakSupportPrompt( streakPrompt );
}
}
removeStreakSupportPrompt( streakPrompt )
{
wasActive = streakPrompt.active;
streakPrompt.active = false;
streakPrompt notify( "streakPromptStopped" );
if ( streakPrompt.team == "allies" )
{
level.streakSupportQueueAllies = array_remove( level.streakSupportQueueAllies, streakPrompt );
if ( wasActive && level.streakSupportQueueAllies.size > 0 )
level thread startStreakSupportPrompt( level.streakSupportQueueAllies[0] );
}
else // team == "axis"
{
level.streakSupportQueueAxis = array_remove( level.streakSupportQueueAxis, streakPrompt );
if ( wasActive && level.streakSupportQueueAxis.size > 0 )
level thread startStreakSupportPrompt( level.streakSupportQueueAxis[0] );
}
thread delayDeletePrompt( streakPrompt );
}
delayDeletePrompt( streakPrompt )
{
wait 1;
streakPrompt Delete();
}
getUniqueStreakPromptID( team )
{
maxID = 0;
foreach ( streakPrompt in level.streakSupportQueueAllies )
{
if ( streakPrompt.promptID >= maxID )
maxID = streakPrompt.promptID + 1;
}
foreach ( streakPrompt in level.streakSupportQueueAxis )
{
if ( streakPrompt.promptID >= maxID )
maxID = streakPrompt.promptID + 1;
}
return maxID;
}
// -----------------------------------------------------------------------------------------------------
// Prompt logic
startStreakSupportPrompt( streakPrompt )
{
streakPrompt.active = true;
level thread handlePrompt( streakPrompt );
level thread onConnectPrompt( streakPrompt );
foreach ( player in level.players )
{
if ( IsDefined( streakPrompt.streakPlayer ) && player == streakPrompt.streakPlayer )
continue;
if ( isReallyAlive( player ) && player.team == streakPrompt.team )
player thread playerSetupStreakPrompt( streakPrompt );
player thread playerOnSpawnPrompt( streakPrompt );
}
}
onConnectPrompt( streakPrompt ) // self == level
{
level endon( "game_ended" );
streakPrompt endon( "streakPromptStopped" );
while ( true )
{
level waittill( "connected", player );
player thread playerOnSpawnPrompt( streakPrompt );
}
}
playerOnSpawnPrompt( streakPrompt ) // self == player
{
level endon( "game_ended" );
self endon( "disconnect" );
streakPrompt endon( "streakPromptStopped" );
while ( true )
{
self waittill( "spawned_player" );
if ( self.team == streakPrompt.team )
self thread playerSetupStreakPrompt( streakPrompt );
}
}
playerSetupStreakPrompt( streakPrompt ) // self == player
{
level endon( "game_ended" );
self endon( "death" );
self endon( "disconnect" );
streakPrompt endon( "streakPromptStopped" );
while ( self isUsingRemote() || self isInRemoteTransition() )
waitframe();
playerDisabledWait( streakPrompt );
self thread playerDisplayJoinRequest( streakPrompt );
self thread playerTakeStreakSupportInput( streakPrompt );
}
playerDisabledWait( streakPrompt )
{
if( !IsDefined(level.streakSupporDisabledCount[self.guid]) )
return;
if( level.streakSupporDisabledCount[self.guid] > 0 )
{
streakPrompt DisablePlayerUse( self );
while( level.streakSupporDisabledCount[self.guid] > 0 )
waitframe();
}
}
playerDisplayJoinRequest( streakPrompt ) //self = player
{
level endon( "game_ended" );
self endon( "death" );
self endon( "disconnect" );
streakPrompt endon( "streakPromptStopped" );
if ( IsDefined( streakPrompt.splashRef ) )
self thread maps\mp\gametypes\_hud_message::coopKillstreakSplashNotify( streakPrompt.splashRef, streakPrompt.needSupportVO );
}
waittillPlayerCanBeBuddy( player, streakPrompt )
{
if ( isInRemoteTransition() )
player maps\mp\killstreaks\_killstreaks::playerWaittillRideKillstreakComplete();
waitframe();
if ( isUsingRemote() ) // cannot join if in a killstreak
player waittill( "stopped_using_remote" );
}
waittillPromptActivated( streakPrompt )
{
streakPrompt endon( "streakPromptStopped" );
streakPrompt waittill( "trigger" );
return true;
}
playerTakeStreakSupportInput( streakPrompt ) // self == player
{
level endon( "game_ended" );
self endon( "death" );
self endon( "disconnect" );
while ( true )
{
waittillPlayerCanBeBuddy( self );
result = waittillPromptActivated( streakPrompt );
if ( !IsDefined( result ) )
return;
if ( !streakPrompt.active )
return;
if ( IsDefined( self PlayerGetUseEnt() ) && self PlayerGetUseEnt() == streakPrompt && self UseButtonPressed() && self IsOnGround() )
{
useTime = self PlayerGetUseTime();
result = self playerHandleJoining( streakPrompt, useTime );
if ( result || !streakPrompt.active )
return;
}
}
}
playerGetUseTime()
{
if ( GetDvarInt( "scr_coop_util_delay", 1 ) == 0 )
{
return CONST_COOP_USE_BAR_TIME_MIN;
}
else
{
// give players with less score a shorter use time
minScore = self.score;
maxScore = self.score;
for ( i = 1; i < level.players.size; i++ )
{
player = level.players[i];
if ( player.team != self.team )
continue;
if ( player.score > maxScore )
maxScore = player.score;
else if ( player.score < minScore )
minScore = player.score;
}
scoreSpread = maxScore - minScore;
if ( scoreSpread == 0 )
return CONST_COOP_USE_BAR_TIME_MIN;
playerScorePercent = ( self.score - minScore ) / scoreSpread;
useTimeSpread = CONST_COOP_USE_BAR_TIME_MAX - CONST_COOP_USE_BAR_TIME_MIN;
useTime = CONST_COOP_USE_BAR_TIME_MIN + ( playerScorePercent * useTimeSpread );
return useTime;
}
}
handlePrompt( streakPrompt )
{
streakPrompt makeGloballyUsableByType( "coopStreakPrompt", streakPrompt.joinText, undefined, streakPrompt.team );
streakPrompt waittill( "streakPromptStopped" );
streakPrompt makeGloballyUnusableByType();
}
playerHandleJoining( streakPrompt, useTime ) // self == player
{
useTimeMS = useTime * 1000;
if ( streakPrompt useHoldThink( self, useTimeMS, streakPrompt ) )
{
level notify( "buddyJoinedStreak", self, streakPrompt.promptID );
self thread maps\mp\_events::killStreakJoinEvent();
if ( IsDefined( streakPrompt.streakPlayer ) && IsAlive( streakPrompt.streakPlayer ) )
{
if ( IsDefined( streakPrompt.joinedVO ) )
self thread leaderDialogOnPlayer( streakPrompt.joinedVO );
if ( IsDefined( streakPrompt.buddyJoinedVO ) )
streakPrompt.streakPlayer thread leaderDialogOnPlayer( streakPrompt.buddyJoinedVO );
if( isDefined( streakPrompt.streakPlayer.currentKillStreakIndex ) )
{
setMatchData( "killstreaks", streakPrompt.streakPlayer.currentKillStreakIndex, "coopPlayerIndex", self.clientId );
}
}
streakPrompt notify( "streakPromptStopped" );
return true;
}
return false;
}
useHoldThink( player, useTimeMS, streakPrompt )
{
Assert( IsPlayer( player ) );
player PlayerLinkTo( streakPrompt );
player PlayerLinkedOffsetEnable();
player.manuallyJoiningKillStreak = true;
self thread useHoldThinkCleanupOnPlayerDeath( player );
self.curProgress = 0;
self.inUse = true;
self.useRate = 0;
self.useTime = useTimeMS;
if ( IsDefined( player.inWater ) )
{
player AllowCrouch( false );
player AllowProne( false );
}
player _giveWeapon( "killstreak_remote_turret_mp" );
player SwitchToWeapon( "killstreak_remote_turret_mp" );
player DisableWeaponSwitch();
player thread personalUseBar( self, streakPrompt );
result = useHoldThinkLoop( player, streakPrompt );
if ( !IsDefined( result ) )
result = false;
if ( isAlive( player ) && !result )
player playerResetAfterCoopStreakInternal();
self.inUse = false;
self.curProgress = 0;
if ( IsDefined( player ) )
{
player.manuallyJoiningKillStreak = false;
player SetClientOmnvar( "ui_use_bar_text", 0 );
player SetClientOmnvar( "ui_use_bar_end_time", 0 );
player SetClientOmnvar( "ui_use_bar_start_time", 0 );
}
self notify( "coopUtilUseHoldThinkComplete" );
return result;
}
useHoldThinkCleanupOnPlayerDeath( player )
{
self endon( "coopUtilUseHoldThinkComplete" );
player waittill_any( "death", "disconnect" );
if ( IsDefined( player ) )
{
player playerResetAfterCoopStreakInternal();
player.manuallyJoiningKillStreak = false;
player SetClientOmnvar( "ui_use_bar_text", 0 );
player SetClientOmnvar( "ui_use_bar_end_time", 0 );
player SetClientOmnvar( "ui_use_bar_start_time", 0 );
}
}
playerResetAfterCoopStreakInternal()
{
self maps\mp\killstreaks\_killstreaks::takeKillstreakWeaponIfNoDupe( "killstreak_predator_missile_mp" );
self maps\mp\killstreaks\_killstreaks::takeKillstreakWeaponIfNoDupe( "killstreak_remote_turret_mp" );
self AllowCrouch( true );
self AllowProne( true );
self EnableWeaponSwitch();
self SwitchToWeapon( self getLastWeapon() );
self thread playerDelayControl();
self Unlink();
}
playerSetupCoopStreakInternal( delay )
{
if ( IsDefined( delay ) )
wait delay;
self EnableWeaponSwitch();
self _giveWeapon( "killstreak_predator_missile_mp" );
self SwitchToWeaponImmediate( "killstreak_predator_missile_mp" );
self maps\mp\killstreaks\_killstreaks::takeKillstreakWeaponIfNoDupe( "killstreak_remote_turret_mp" );
self DisableWeaponSwitch();
}
playerDelayControl() // self == player
{
self endon( "disconnect" );
self freezeControlsWrapper( true );
wait( 0.5 );
self freezeControlsWrapper( false );
}
personalUseBar( object, streakPrompt )
{
self endon( "disconnect" );
streakPrompt endon( "streakPromptStopped" );
self SetClientOmnvar( "ui_use_bar_text", 2 );
self SetClientOmnvar( "ui_use_bar_start_time", int( GetTime() ) );
lastRate = -1;
while ( isReallyAlive( self ) && isDefined( object ) && object.inUse && !level.gameEnded )
{
if ( lastRate != object.useRate )
{
if( object.curProgress > object.useTime)
object.curProgress = object.useTime;
if ( object.useRate > 0 )
{
now = GetTime();
current = object.curProgress / object.useTime;
endTime = now + ( 1 - current ) * ( object.useTime / object.useRate );
self SetClientOmnvar( "ui_use_bar_end_time", int( endTime ) );
}
lastRate = object.useRate;
}
wait ( 0.05 );
}
self SetClientOmnvar( "ui_use_bar_end_time", 0 );
}
useHoldThinkLoop( player, streakPrompt )
{
streakPrompt endon( "streakPromptStopped" );
while( !level.gameEnded && isDefined( self ) && isReallyAlive( player ) && player useButtonPressed() && self.curProgress < self.useTime )
{
self.curProgress += (50 * self.useRate);
if ( isDefined(self.objectiveScaler) )
self.useRate = 1 * self.objectiveScaler;
else
self.useRate = 1;
if ( self.curProgress >= self.useTime )
return ( isReallyAlive( player ) );
wait 0.05;
}
return false;
}