diff options
Diffstat (limited to 'Northstar.Custom/scripts')
5 files changed, 1123 insertions, 10 deletions
diff --git a/Northstar.Custom/scripts/vscripts/client/cl_gamestate.gnut b/Northstar.Custom/scripts/vscripts/client/cl_gamestate.gnut new file mode 100644 index 00000000..93be01ea --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/client/cl_gamestate.gnut @@ -0,0 +1,1119 @@ +untyped + +global function ClGameState_Init +global function ClGameState_Think +global function ClGameState_GetRui +global function ClGameState_RegisterNetworkFunctions +global function ClGameState_SetInfoStatusText + +global function ServerCallback_AnnounceWinner +global function ServerCallback_AnnounceRoundWinner + +global function ShowRoundScoresInAnnouncement + +global function PlayRoundWonConversationWithAnnouncementDelay + +global function UpdatePilotTitanStatusDisplay +global function UpdatePilotTitanStatusDisplayWithCount + +global function SetGameModeScoreBarUpdateRules +global function SetGameModeScoreBarUpdateRulesWithFlags + +global function DisplayPostMatchTop3 + +global function GetGameStartTime + +global function ClGameState_RegisterGameStateAsset + +global function ClGameState_SetPilotTitanStatusCallback +global function GetPilotTitanStatusForPlayer + +global enum ePlayerStatusType +{ + PTS_TYPE_NONE, + PTS_TYPE_DEAD, + PTS_TYPE_DEAD_PILOT_TITAN, + PTS_TYPE_DEAD_READY, + PTS_TYPE_PILOT, + PTS_TYPE_READY, + PTS_TYPE_PILOT_TITAN, + PTS_TYPE_EVAC, + PTS_TYPE_ION, + PTS_TYPE_SCORCH, + PTS_TYPE_RONIN, + PTS_TYPE_TONE, + PTS_TYPE_LEGION, + PTS_TYPE_NORTHSTAR, + PTS_TYPE_VANGUARD, + PTS_TYPE_WAVE_READY, + PTS_TYPE_WAVE_NOT_READY, + PTS_TYPE_COUNT, +} + +struct PilotTitanStatus +{ + int playerCount + int[8] slotTypes +} + +global enum sbflag +{ + SKIP_STANDARD_UPDATE = (1<<0), + _count = 1 +} + +struct +{ + var gamestate_info + var letterBoxRui = null + bool thirtySecondWarningDone = false + + void functionref( var ) gameModeScoreBarUpdateRules + int gameModeScoreBarUpdateFlags = 0 + + table< int, PilotTitanStatus> pilotTitanStatus + int functionref( entity,int ) pilotTitanStatusCallback + + asset gameStateRuiAsset = $"" +} file + + +const DEV_COUNTDOWNTIMER = 0//turn on to see the countdown timer to match starts - helps with lining up intros + +function ClGameState_Init() +{ + RegisterSignal( "GameStateChanged" ) + RegisterServerVarChangeCallback( "gameState", ClGameState_Changed ) + RegisterServerVarChangeCallback( "badRepPresent", UpdateScoreboardBadRepPresentMessage ) + + AddCallback_OnClientScriptInit( ClGameState_AddClient ) + + var rui = CreatePermanentCockpitRui( GetGameStateAsset(), MINIMAP_Z_BASE - 1 ) + file.gamestate_info = rui + + if ( file.gameModeScoreBarUpdateRules == null ) + SetGameModeScoreBarUpdateRules( GameModeScoreBarUpdateRules_Default ) +} + +void function GameModeScoreBarUpdateRules_Default( var rui ) +{ +} + +void function SetGameModeScoreBarUpdateRulesWithFlags( void functionref( var ) rules, int sbflags ) +{ + file.gameModeScoreBarUpdateRules = rules + file.gameModeScoreBarUpdateFlags = sbflags +} + +void function SetGameModeScoreBarUpdateRules( void functionref( var ) rules ) +{ + SetGameModeScoreBarUpdateRulesWithFlags( rules, 0 ) +} + +void function ClGameState_RegisterNetworkFunctions() +{ + RegisterNetworkedVariableChangeCallback_int( "gameInfoStatusText", ClGameState_UpdateInfoStatusText ) +} + +void function ClGameState_UpdateInfoStatusText( entity player, int oldValue, int newValue, bool actuallyChanged ) +{ + printt( "UPDATE", player, oldValue, newValue ) + + if ( newValue == -1 ) + { + RuiSetString( file.gamestate_info, "statusText", "" ) + return + } + + string statusText = GetStringFromNetworkableID( newValue ) + printt( "SET", player, statusText, Localize( statusText ) ) + RuiSetString( file.gamestate_info, "statusText", Localize( statusText ) ) +} + + +void function ClGameState_SetInfoStatusText( string statusText ) +{ + RuiSetString( file.gamestate_info, "statusText", Localize( statusText ) ) +} + + +asset function GetGameStateAsset() +{ + if ( GAMETYPE == ATTRITION ) + return $"ui/gamestate_info_at.rpak" + + if ( GAMETYPE == FREE_AGENCY ) + return $"ui/gamestate_info_fra.rpak" + + if ( GAMETYPE == FFA ) + return $"ui/gamestate_info_ffa.rpak" + + if ( GAMETYPE == FORT_WAR ) + return $"ui/gamestate_info_fw.rpak" + + if ( GAMETYPE == LAST_TITAN_STANDING ) + return $"ui/gamestate_info_lts.rpak" + + if ( GAMETYPE == LTS_BOMB ) + return $"ui/gamestate_info_lts_bomb.rpak" + + if ( GAMETYPE == CAPTURE_POINT ) + return $"ui/gamestate_info_cp.rpak" + + if ( GAMETYPE == PILOT_SKIRMISH ) + return $"ui/gamestate_info_ps.rpak" + + if ( GAMETYPE == COLISEUM ) + return $"ui/gamestate_info_coliseum.rpak" + + if ( GAMETYPE == SPEEDBALL ) + return $"ui/gamestate_info_speedball.rpak" + + if ( GAMETYPE == MARKED_FOR_DEATH ) + return $"ui/gamestate_info_mfd.rpak" + + if ( file.gameStateRuiAsset != $"" ) + return file.gameStateRuiAsset + + return $"ui/gamestate_info.rpak" +} + +void function ClGameState_RegisterGameStateAsset( asset gameStateAsset ) +{ + file.gameStateRuiAsset = gameStateAsset +} + +void function ClGameState_AddClient( entity player ) +{ + player.cv.scoreboardBadRepPresentMessage <- HudElement( "ScoreboardBadRepPresentMessage", HudElement( "Scoreboard" ) ) + + #if PC_PROG + player.cv.scoreboardBadRepPresentMessage.SetText( "#ASTERISK_FAIRFIGHT_CHEATER" ) + #else + player.cv.scoreboardBadRepPresentMessage.SetText( "#ASTERISK_BAD_REPUTATION" ) + #endif + + player.cv.hudCheaterMessage <- HudElement( "HudCheaterMessage" ) + if ( !Durango_IsDurango() && !IsLobby() && player.HasBadReputation() ) + player.cv.hudCheaterMessage.Show() + else + player.cv.hudCheaterMessage.Hide() +} + + +var function ClGameState_GetRui() +{ + return file.gamestate_info +} + + +void function ClGameState_Think() +{ + entity player = GetLocalClientPlayer() + + TEMP_UpdateRuiScores( player ) + switch( GetGameState() ) + { + case eGameState.WaitingForPlayers: + ClGameStateThink_WaitingForPlayers( player ) + break + + case eGameState.PickLoadout: + ClGameStateThink_PickLoadOut( player ) + break + + case eGameState.Prematch: + ClGameStateThink_Prematch( player ) + break + + case eGameState.Playing: + ClGameStateThink_Playing( player ) + break + + case eGameState.SwitchingSides: + ClGameStateThink_SwitchingSides( player ) + break + + case eGameState.Postmatch: + ClGameStateThink_Postmatch( player ) + break + } +} + + +void function ClGameStateThink_Prematch( entity player ) +{ + string factionChoice = GetFactionChoice( player ) + ItemDisplayData displayData = GetItemDisplayData( factionChoice ) + asset factionLogo = displayData.image + string factionTitle = displayData.name + + RuiSetImage( ClGameState_GetRui(), "factionImage", factionLogo ) + + //removing countdown timer all together + if ( !DEV_COUNTDOWNTIMER ) + return + + local timeRemaining = ceil( level.nv.gameStartTime - Time() ) + + player.cv.prematchTimer.Show() + player.cv.prematchTimerGlow.Show() + player.cv.prematchDesc.Show() + + player.cv.prematchTimer.SetText( string( timeRemaining ) ) + player.cv.prematchTimerGlow.SetText( string( timeRemaining ) ) +} + +void function ClGameStateThink_WaitingForPlayers( player ) +{ + int reservedCount + int connectingCount + int loadingCount + if ( IsFFAGame() ) + { + reservedCount = GetTotalPendingPlayersReserved() + connectingCount = GetTotalPendingPlayersConnecting() + loadingCount = GetTotalPendingPlayersLoading() + } + else + { + reservedCount = GetTeamPendingPlayersReserved( TEAM_MILITIA ) + GetTeamPendingPlayersReserved( TEAM_IMC ) + connectingCount = GetTeamPendingPlayersConnecting( TEAM_MILITIA ) + GetTeamPendingPlayersConnecting( TEAM_IMC ) + loadingCount = GetTeamPendingPlayersLoading( TEAM_MILITIA ) + GetTeamPendingPlayersLoading( TEAM_IMC ) + } + + int connectedCount = GetPlayerArray().len() + int allKnownPlayersCount = reservedCount + connectingCount + loadingCount + connectedCount + int minPlayers = GetCurrentPlaylistVarInt( "min_players", 0 ) + int expectedPlayers = maxint( minPlayers, allKnownPlayersCount ) + + if ( !("lastTimeRemaining" in player.cv) ) + player.cv.lastTimeRemaining <- null + + if ( Time() <= level.nv.connectionTimeout ) + { + local timeRemainingFloat = level.nv.connectionTimeout - Time() + local timeRemaining = ceil( timeRemainingFloat ) + + //player.cv.waitingForPlayersDesc.SetText( "#HUD_WAITING_FOR_PLAYERS", connectedCount, expectedPlayers, timeRemaining ) + //player.cv.waitingForPlayersDesc.Show() + + if ( player.cv.lastTimeRemaining && timeRemaining != player.cv.lastTimeRemaining ) + EmitSoundOnEntity( player, WAITING_FOR_PLAYERS_COUNTDOWN_SOUND ) + + player.cv.lastTimeRemaining = timeRemaining + } + else + { + //player.cv.waitingForPlayersDesc.SetText( "#HUD_WAITING_FOR_PLAYERS", connectedCount, expectedPlayers, "" ) + //player.cv.waitingForPlayersDesc.Show() + } +} + +void function ClGameStateThink_PickLoadOut( entity player ) +{ + if ( IsMultiplayerPlaylist() && ClassicMP_Client_GetGameStateThinkFunc_PickLoadOut() != null ) + ClassicMP_Client_GetGameStateThinkFunc_PickLoadOut()( player ) +} + +entity function GetTopCompetitor( int team ) +{ + array<entity> players = GetPlayerArrayOfEnemies( team ) + + entity topCompetitor + foreach ( player in players ) + { + if ( !topCompetitor || (GameRules_GetTeamScore( player.GetTeam()) ) > GameRules_GetTeamScore( topCompetitor.GetTeam()) ) + topCompetitor = player + } + + return topCompetitor +} + + +string function GetTopCompetitorName( int team ) +{ + entity topCompetitor = GetTopCompetitor( team ) + return topCompetitor != null ? topCompetitor.GetPlayerName() : "None" +} + + +int function GetTopCompetitorTeam( int team ) +{ + entity topCompetitor = GetTopCompetitor( team ) + return topCompetitor != null ? topCompetitor.GetTeam() : 0 +} + + +void function TEMP_UpdateRuiScores( entity player ) +{ + float endTime + if ( IsRoundBased() ) + { + endTime = expect float( level.nv.roundEndTime ) + if ( + GetGameState() == eGameState.Prematch || + GetGameState() == eGameState.Playing + ) + RuiSetString( file.gamestate_info, "roundText", Localize( "#GAMESTATE_ROUND_N", GetRoundsPlayed() + 1 ) ) + } + else + { + endTime = expect float( level.nv.gameEndTime ) + } + + RuiSetInt( file.gamestate_info, "maxTeamScore", GetScoreLimit_FromPlaylist() ) + int maxPlayers = GetCurrentPlaylistVarInt( "max_players", 12 ) + RuiSetInt( file.gamestate_info, "maxTeamPlayers", maxPlayers / 2 ) + + int friendlyTeam = player.GetTeam() + RuiSetFloat( file.gamestate_info, "leftTeamScore", float( GameRules_GetTeamScore( friendlyTeam ) ) ) + + UpdatePilotTitanStatusDisplay( friendlyTeam ) + + int enemyTeam + if ( IsFFAGame() ) + { + enemyTeam = GetTopCompetitorTeam( player.GetTeam() ) + RuiSetImage( file.gamestate_info, "friendlyPlayerCardImage", CallsignIcon_GetImage( PlayerCallsignIcon_GetActive( player ) ) ) + array<entity> enemyPlayers = GetPlayerArrayOfTeam( enemyTeam ) + if ( enemyPlayers.len() > 0 ) + RuiSetImage( file.gamestate_info, "enemyPlayerCardImage", CallsignIcon_GetImage( PlayerCallsignIcon_GetActive( enemyPlayers[0] ) ) ) + } + else + { + enemyTeam = friendlyTeam == TEAM_IMC ? TEAM_MILITIA : TEAM_IMC + } + + RuiSetFloat( file.gamestate_info, "rightTeamScore", float( GameRules_GetTeamScore( enemyTeam ) ) ) + + //Run any gamemode specific score bar logic (e.g. the bonus segment on the scorebar in bounty hunt ). No special logic is run by default. + file.gameModeScoreBarUpdateRules( file.gamestate_info ) + + if ( !endTime ) + return + + if ( Time() > endTime ) + { + RuiSetGameTime( file.gamestate_info, "endTime", RUI_BADGAMETIME ) + return + } + + RuiSetGameTime( file.gamestate_info, "endTime", endTime ) +} + + +void function ClGameStateThink_Playing( entity player ) +{ + //TEMP_UpdateRuiScores( player ) + + local endTime + if ( IsRoundBased() ) + endTime = level.nv.roundEndTime + else + endTime = level.nv.gameEndTime + + if ( !endTime ) + return + + if ( Time() > endTime ) + return + + if ( endTime - Time() > 30.0 ) + return + + if ( file.thirtySecondWarningDone ) + return + + thread ThirtySecondWarning() + file.thirtySecondWarningDone = true +} + + +void function ThirtySecondWarning() +{ + string conversation = GameMode_GetGameEndingConversation( GameRules_GetGameMode() ) + if ( conversation != "" ) + PlayConversationToLocalClient( conversation ) + + int lastSecond = -1 + while ( Time() < level.nv.gameEndTime && GetGameState() == eGameState.Playing ) + { + int second = int( floor( level.nv.gameEndTime - Time() ) ) + if ( lastSecond == -1 ) + { + lastSecond = second + } + else if ( second != lastSecond && second < 30 ) + { + if ( second > 5 ) + { + EmitSoundOnEntity( GetLocalClientPlayer(), "HUD_match_start_timer_tick_1P" ) + } + else if ( second >= 0 ) + { + EmitSoundOnEntity( GetLocalClientPlayer(), "HUD_match_start_timer_5_seconds_1P" ) + } + } + + lastSecond = second + + WaitFrame() + } + + EmitSoundOnEntity( GetLocalClientPlayer(), "HUD_match_start_timer_0_seconds_1P" ) + +} + + +string function GetTitanClass( entity titan ) +{ + entity soul = titan.GetTitanSoul() + string settingsName = PlayerSettingsIndexToName( soul.GetPlayerSettingsNum() ) + + return expect string( Dev_GetPlayerSettingByKeyField_Global( settingsName, "titanCharacterName" ) ) +} + + +array<int> function GetPilotTitanStatusForTeam( int team ) +{ + array<int> statusTypes = [ ePlayerStatusType.PTS_TYPE_NONE, ePlayerStatusType.PTS_TYPE_NONE, ePlayerStatusType.PTS_TYPE_NONE, ePlayerStatusType.PTS_TYPE_NONE, ePlayerStatusType.PTS_TYPE_NONE, ePlayerStatusType.PTS_TYPE_NONE, ePlayerStatusType.PTS_TYPE_NONE, ePlayerStatusType.PTS_TYPE_NONE ] + + int playerIndex = 0 + foreach ( teamPlayer in GetPlayerArrayOfTeam( team ) ) + { + // fix for situations where number of players > 8, which happens in infection + // could be good to move this to northstar.client at some point, unsure + if ( playerIndex == statusTypes.len() ) + break + + statusTypes[playerIndex] = GetPilotTitanStatusForPlayer( teamPlayer ) + playerIndex++ + } + + statusTypes.sort( PilotTitanStatusSort ) + + return statusTypes +} + +int function GetPilotTitanStatusForPlayer( entity teamPlayer ) +{ + int statusIndex = ePlayerStatusType.PTS_TYPE_NONE + entity titan + if ( teamPlayer.GetPetTitan() ) + titan = teamPlayer.GetPetTitan() + else if ( teamPlayer.IsTitan() ) + titan = teamPlayer + + entity playerParent = teamPlayer.GetParent() + bool playerIsInDropship = playerParent != null && IsDropship( playerParent ) + + if ( playerIsInDropship && ( GetWaveSpawnType() == eWaveSpawnType.DROPSHIP || GetGameState() == eGameState.Epilogue ) ) + { + statusIndex = ePlayerStatusType.PTS_TYPE_EVAC + } + else if ( titan && titan.GetTitanSoul() ) + { + if ( !teamPlayer.IsTitan() ) + { + if ( IsAlive( teamPlayer ) ) + statusIndex = ePlayerStatusType.PTS_TYPE_PILOT_TITAN + else + statusIndex = ePlayerStatusType.PTS_TYPE_DEAD_PILOT_TITAN + } + else + { + if ( !IsAlive( teamPlayer ) ) + { + statusIndex = ePlayerStatusType.PTS_TYPE_DEAD + } + else + { + switch ( GetTitanClass( titan ) ) + { + case "ion": + statusIndex = ePlayerStatusType.PTS_TYPE_ION + break + case "scorch": + statusIndex = ePlayerStatusType.PTS_TYPE_SCORCH + break + case "ronin": + statusIndex = ePlayerStatusType.PTS_TYPE_RONIN + break + case "tone": + statusIndex = ePlayerStatusType.PTS_TYPE_TONE + break + case "legion": + statusIndex = ePlayerStatusType.PTS_TYPE_LEGION + break + case "northstar": + statusIndex = ePlayerStatusType.PTS_TYPE_NORTHSTAR + break + case "vanguard": + statusIndex = ePlayerStatusType.PTS_TYPE_VANGUARD + break + } + } + } + } + else + { + if ( IsAlive( teamPlayer ) ) + statusIndex = IsTitanAvailable( teamPlayer ) ? ePlayerStatusType.PTS_TYPE_READY : ePlayerStatusType.PTS_TYPE_PILOT + else + statusIndex = IsTitanAvailable( teamPlayer ) ? ePlayerStatusType.PTS_TYPE_DEAD_READY : ePlayerStatusType.PTS_TYPE_DEAD + } + + if ( file.pilotTitanStatusCallback != null ) + statusIndex = file.pilotTitanStatusCallback( teamPlayer, statusIndex ) + + return statusIndex +} + +int function PilotTitanStatusSort( int a, int b ) +{ + if ( a > b ) + return -1 + else if ( a < b ) + return 1 + + return 0 +} + + +void function UpdatePilotTitanStatusDisplayWithCount( int team, int friendlyCount, int enemyCount ) +{ + if ( IsFFAGame() ) + return + var statusRui = ClGameState_GetRui() + + array<int> playerStatus = GetPilotTitanStatusForTeam( team ) + for ( int index = 0; index < friendlyCount; index++ ) + RuiSetInt( statusRui, "friendlyPlayerStatusType" + (index + 1), playerStatus[index] ) + + array<int> enemyPlayerStatus = GetPilotTitanStatusForTeam( GetOtherTeam( team ) ) + for ( int index = 0; index < enemyCount; index++ ) + RuiSetInt( statusRui, "enemyPlayerStatusType" + (index + 1), enemyPlayerStatus[index] ) +} + +void function UpdatePilotTitanStatusDisplay( int team ) +{ + if ( GAMETYPE == "fd" ) + UpdatePilotTitanStatusDisplayWithCount( team, 4, 0 ) + else + UpdatePilotTitanStatusDisplayWithCount( team, 8, 8 ) +} + +void function ClGameStateThink_Postmatch( player ) +{ +} + +void function ClGameStateThink_SwitchingSides( player ) +{ + //player.cv.vignette.SetColor( 0, 0, 0, 255 ) + //player.cv.vignette.Show() +} + + +void function ClGameState_Changed() +{ + entity player = GetLocalClientPlayer() + player.Signal( "GameStateChanged" ) + + foreach ( callbackFunc in clGlobal.gameStateEnterCallbacks[ GetGameState() ] ) + { + callbackFunc() + } + + switch ( GetGameState() ) + { + case eGameState.WaitingForCustomStart: + //player.cv.gamescomWaitTillReady.Show() + break + + case eGameState.WaitingForPlayers: + if ( IsMultiplayerPlaylist() && ClassicMP_Client_GetGameStateEnterFunc_WaitingForPlayers() != null ) + ClassicMP_Client_GetGameStateEnterFunc_WaitingForPlayers()( player ) + //player.cv.gamescomWaitTillReady.Hide() + + //player.cv.waitingForPlayersDesc.SetText( "#HUD_WAITING_FOR_PLAYERS_BASIC" ) + //player.cv.waitingForPlayersDesc.Show() + //player.cv.waitingForPlayersLine.Show() + //player.cv.waitingForPlayersTimer.Show() + break + + case eGameState.PickLoadout: + if ( IsMultiplayerPlaylist() && ClassicMP_Client_GetGameStateEnterFunc_PickLoadOut() != null ) + ClassicMP_Client_GetGameStateEnterFunc_PickLoadOut()( player ) + + break + + case eGameState.Prematch: + RemoveAllRagdolls() + HideEventNotification() + + SetCrosshairPriorityState( crosshairPriorityLevel.PREMATCH, CROSSHAIR_STATE_HIDE_ALL ) + + //player.cv.waitingForPlayersDesc.HideOverTime( 0.25 ) + //player.cv.waitingForPlayersLine.HideOverTime( 0.25 ) + //player.cv.waitingForPlayersTimer.HideOverTime( 0.25 ) + + file.letterBoxRui = RuiCreate( $"ui/letter_box.rpak", clGlobal.topoFullScreen, RUI_DRAW_HUD, 0 ) + + //player.cv.prematchTimer.SetAlpha( 255 ) + //player.cv.prematchTimerGlow.SetAlpha( 255 ) + //player.cv.prematchDesc.SetAlpha( 255 ) + + file.thirtySecondWarningDone = false + + StopPlayerDeathSound( player ) //If you were watching your own kill replay, don't want this playing through till next round + + clGlobal.levelEnt.Signal( "AnnoucementPurge" ) + + break + + case eGameState.Playing: + ClearCrosshairPriority( crosshairPriorityLevel.PREMATCH ) + //player.cv.vignette.HideOverTime( 0.25 ) + if ( file.letterBoxRui != null ) + RuiSetGameTime( file.letterBoxRui, "hideStartTime", Time() ) + //player.cv.prematchTimer.HideOverTime( 0.25 ) + //player.cv.prematchTimerGlow.HideOverTime( 0.25 ) + //player.cv.prematchDesc.HideOverTime( 0.25 ) + + ShowScriptHUD( player ) + break + + case eGameState.SuddenDeath: + AnnouncementData announcement = Announcement_Create( "#GAMEMODE_ANNOUNCEMENT_SUDDEN_DEATH" ) + + switch ( GAMETYPE ) + { + case CAPTURE_THE_FLAG: + case RAID: + Announcement_SetSubText( announcement, "#GAMEMODE_ANNOUNCEMENT_SUDDEN_DEATH_CTF" ) + break + + case TEAM_DEATHMATCH: + case TITAN_BRAWL: + case HARDCORE_TDM: + Announcement_SetSubText( announcement, "#GAMEMODE_ANNOUNCEMENT_SUDDEN_DEATH_TDM" ) + break + + default: + Announcement_SetSubText( announcement, "" ) + } + + Announcement_SetHideOnDeath( announcement, false ) + Announcement_SetDuration( announcement, 7.0 ) + Announcement_SetPurge( announcement, true ) + AnnouncementFromClass( player, announcement ) + break + + case eGameState.WinnerDetermined: + player.cv.roundSpawnCount = 0 + break + + case eGameState.Epilogue: + thread MainHud_Outro( level.nv.winningTeam ) + break + + case eGameState.SwitchingSides: + + float announcementDuration = 7.0 + if ( IsRoundWinningKillReplayEnabled() == true && ( !IsRoundBased() && IsSwitchSidesBased() ) ) //Ideally this should be a call to WillShowRoundWinningKillReplay(), but that's only available on the server + announcementDuration = SWITCHING_SIDES_DELAY + ROUND_WINNING_KILL_REPLAY_TOTAL_LENGTH + + AnnouncementData announcement = Announcement_Create( "#GameState_HALFTIME" ) + announcement.sortKey = RUI_SORT_SCREENFADE + 1 // Draw over screen fade + announcement.drawOverScreenFade = true + Announcement_SetSubText( announcement, "#GameState_SWITCHING_SIDES" ) + Announcement_SetHideOnDeath( announcement, false ) + Announcement_SetDuration( announcement, announcementDuration ) + Announcement_SetPurge( announcement, true ) + + EmitSoundOnEntity( player, "UI_InGame_HalftimeText_Enter" ) + EmitSoundOnEntityAfterDelay( player, "UI_InGame_HalftimeText_Exit", announcementDuration ) + + local friendlyTeam = player.GetTeam() + local enemyTeam = friendlyTeam == TEAM_IMC ? TEAM_MILITIA : TEAM_IMC + + if ( friendlyTeam == TEAM_IMC ) + { + Announcement_SetLeftIcon( announcement, TEAM_ICON_IMC ) + Announcement_SetRightIcon( announcement, TEAM_ICON_MILITIA ) + } + else + { + Announcement_SetLeftIcon( announcement, TEAM_ICON_MILITIA ) + Announcement_SetRightIcon( announcement, TEAM_ICON_IMC ) + } + + if ( IsRoundBased() ) + { + Announcement_SetSubText2( announcement, "#GAMEMODE_ROUND_WIN_CONDITION", GetRoundScoreLimit_FromPlaylist() ) + Announcement_SetLeftText( announcement, "#GAMEMODE_JUST_THE_SCORE", GameRules_GetTeamScore2( friendlyTeam ) ) + Announcement_SetRightText( announcement, "#GAMEMODE_JUST_THE_SCORE", GameRules_GetTeamScore2( enemyTeam ) ) + } + else + { + Announcement_SetLeftText( announcement, "#GAMEMODE_JUST_THE_SCORE", GameRules_GetTeamScore( friendlyTeam ) ) + Announcement_SetRightText( announcement, "#GAMEMODE_JUST_THE_SCORE", GameRules_GetTeamScore( enemyTeam ) ) + } + + AnnouncementFromClass( player, announcement ) + + break + + case eGameState.Postmatch: + ShowScoreboard() + //wait for scoreboard to be up + delaythread( 1.5 ) ServerCallback_ResetMapSettings() + break + } +} + +void function SwitchingSides_Changed() +{ + if ( IsMenuLevel() ) + return + + thread SwitchingSides_Changed_threaded() +} + +void function SwitchingSides_Changed_threaded() +{ + //entity player = GetLocalViewPlayer() + // + //if ( level.nv.switchingSides ) + //{ + // player.cv.halfTimeText.SetText( "Test - Switching Sides" ) + // player.cv.halfTimeText.Show() + // wait 1.5 + //} + //else + //{ + // player.cv.halfTimeText.Hide() + // + //} +} + +void function ServerCallback_AnnounceWinner( int teamIndex, int subStringIndex, float winnerDeterminedWait ) +{ + entity player = GetLocalClientPlayer() + + string subString = "" + if ( subStringIndex ) + subString = GetStringFromID( subStringIndex ) + + if ( !level.nv.winningTeam ) + { + AnnouncementData outcomeAnnouncement = Announcement_Create( "#GAMEMODE_DRAW" ) + outcomeAnnouncement.drawOverScreenFade = true + Announcement_SetSubText( outcomeAnnouncement, subString ) + //Announcement_SetTitleColor( outcomeAnnouncement, TEAM_COLOR_YOU ) + Announcement_SetHideOnDeath( outcomeAnnouncement, false ) + Announcement_SetDuration( outcomeAnnouncement, winnerDeterminedWait ) + Announcement_SetPriority( outcomeAnnouncement, 500 ) + Announcement_SetPurge( outcomeAnnouncement, true ) + AnnouncementFromClass( player, outcomeAnnouncement ) + } + else if ( IsFFAGame() ) + { + array<entity> players = GetPlayerArrayOfTeam( level.nv.winningTeam ) + if ( players.len() ) + { + string winText = ((players[0] == player) ? "#GAMEMODE_MATCH_WON_BY_SELF" : "#GAMEMODE_MATCH_WON_BY_ENEMY_TEAM") + AnnouncementData outcomeAnnouncement = Announcement_Create( winText ) + outcomeAnnouncement.drawOverScreenFade = true + Announcement_SetSubText( outcomeAnnouncement, subString ) + Announcement_SetOptionalTextArgsArray( outcomeAnnouncement, [players[0].GetPlayerName()] ) + Announcement_SetTitleColor( outcomeAnnouncement, TEAM_COLOR_YOU ) + Announcement_SetHideOnDeath( outcomeAnnouncement, false ) + Announcement_SetDuration( outcomeAnnouncement, winnerDeterminedWait ) + Announcement_SetPriority( outcomeAnnouncement, 500 ) + Announcement_SetPurge( outcomeAnnouncement, true ) + AnnouncementFromClass( player, outcomeAnnouncement ) + } + } + else if ( player.GetTeam() == level.nv.winningTeam ) + { + + AnnouncementData outcomeAnnouncement = Announcement_Create( "#GAMEMODE_VICTORY" ) + outcomeAnnouncement.announcementStyle = ANNOUNCEMENT_STYLE_RESULTS + outcomeAnnouncement.drawOverScreenFade = true + outcomeAnnouncement.soundAlias = "HUD_MP_Match_End_WinLoss_UI_Sweep_1P" + Announcement_SetSubText( outcomeAnnouncement, subString ) + Announcement_SetTitleColor( outcomeAnnouncement, TEAM_COLOR_PARTY ) + Announcement_SetHideOnDeath( outcomeAnnouncement, false ) + Announcement_SetDuration( outcomeAnnouncement, winnerDeterminedWait ) + Announcement_SetPriority( outcomeAnnouncement, 500 ) + Announcement_SetPurge( outcomeAnnouncement, true ) + AnnouncementFromClass( player, outcomeAnnouncement ) + } + else if ( level.nv.winningTeam != TEAM_UNASSIGNED ) + { + AnnouncementData outcomeAnnouncement = Announcement_Create( "#GAMEMODE_DEFEATED" ) + outcomeAnnouncement.announcementStyle = ANNOUNCEMENT_STYLE_RESULTS + outcomeAnnouncement.drawOverScreenFade = true + outcomeAnnouncement.soundAlias = "HUD_MP_Match_End_WinLoss_UI_Sweep_1P" + Announcement_SetSubText( outcomeAnnouncement, subString ) + Announcement_SetTitleColor( outcomeAnnouncement, TEAM_COLOR_ENEMY ) + Announcement_SetHideOnDeath( outcomeAnnouncement, false ) + Announcement_SetDuration( outcomeAnnouncement, winnerDeterminedWait ) + Announcement_SetPriority( outcomeAnnouncement, 500 ) + Announcement_SetPurge( outcomeAnnouncement, true ) + AnnouncementFromClass( player, outcomeAnnouncement ) + } +} + +void function ServerCallback_AnnounceRoundWinner( int teamIndex, int subStringIndex, float winnerDeterminedWait, int imcTeamScore2, int militiaTeamScore2 ) +{ + string subString = "" + if ( subStringIndex ) + subString = GetStringFromID( subStringIndex ) + + entity player = GetLocalClientPlayer() + + // ANNOUNCEMENT_FLICKER_BUFFER Ensures that the INTERPOLATOR_FLICKER animation is played before the message is hidden in + // AnnouncementMessage_DisplayOnHud(). I'm not certain why this is needed. There may be a delay before + // ServerCallback_AnnounceRoundWinner is actually received and/or the hud fades may be framerate + // dependent (they don't seem to play nice with timescale), or it could be something much more simple that I'm missing. + const ANNOUNCEMENT_FLICKER_BUFFER = 0.2 + + float announcementDuration = winnerDeterminedWait - ANNOUNCEMENT_FLICKER_BUFFER + float subtext2IconDelay = winnerDeterminedWait - 4.5 + float conversationDelay = subtext2IconDelay + 1.5 // Bit of time to give it to breathe, and to let RoundWinningKillReplay be fully over before starting the conversation. + + if ( !level.nv.winningTeam ) + { + AnnouncementData announcement = Announcement_Create( "#GAMEMODE_ROUND_DRAW" ) + announcement.drawOverScreenFade = true + Announcement_SetSubText( announcement, subString ) + //Announcement_SetTitleColor( announcement, TEAM_COLOR_YOU ) + Announcement_SetHideOnDeath( announcement, false ) + Announcement_SetPurge( announcement, true ) + Announcement_SetDuration( announcement, announcementDuration) + + ShowRoundScoresInAnnouncement( announcement, subtext2IconDelay, imcTeamScore2, militiaTeamScore2 ) + AnnouncementFromClass( player, announcement ) + } + else if ( player.GetTeam() == level.nv.winningTeam ) + { + AnnouncementData announcement = Announcement_Create( "#GAMEMODE_ROUND_WIN" ) + announcement.drawOverScreenFade = true + Announcement_SetSubText( announcement, subString ) + Announcement_SetTitleColor(announcement, TEAM_COLOR_FRIENDLY ) + Announcement_SetHideOnDeath( announcement, false ) + Announcement_SetPurge( announcement, true ) + Announcement_SetDuration( announcement, announcementDuration) + + ShowRoundScoresInAnnouncement( announcement, subtext2IconDelay, imcTeamScore2, militiaTeamScore2 ) + thread PlayRoundWonConversationWithAnnouncementDelay( conversationDelay ) + + AnnouncementFromClass( player, announcement ) + } + else if ( level.nv.winningTeam != TEAM_UNASSIGNED ) + { + AnnouncementData announcement = Announcement_Create( "#GAMEMODE_ROUND_LOSS" ) + announcement.drawOverScreenFade = true + Announcement_SetSubText( announcement, subString ) + Announcement_SetTitleColor( announcement, TEAM_COLOR_ENEMY ) + Announcement_SetHideOnDeath( announcement, false ) + Announcement_SetPurge( announcement, true ) + Announcement_SetDuration( announcement, announcementDuration) + + ShowRoundScoresInAnnouncement( announcement, subtext2IconDelay, imcTeamScore2, militiaTeamScore2 ) + thread PlayRoundWonConversationWithAnnouncementDelay( conversationDelay ) + + AnnouncementFromClass( player, announcement ) + } +} + +//Note that RoundWinningKillReplay doesn't send imcTeamScore2 and militiaTeamScore2 overrides. +void function ShowRoundScoresInAnnouncement( AnnouncementData announcement, float subtext2IconDelay, int ornull imcTeamScore2 = null, int ornull militiaTeamScore2 = null ) +{ + entity player = GetLocalClientPlayer() + + local friendlyTeam = player.GetTeam() + local enemyTeam = friendlyTeam == TEAM_IMC ? TEAM_MILITIA : TEAM_IMC + + asset leftIcon + asset rightIcon + + if ( friendlyTeam == TEAM_IMC ) + { + leftIcon = TEAM_ICON_IMC + rightIcon = TEAM_ICON_MILITIA + } + else + { + leftIcon = TEAM_ICON_MILITIA + rightIcon = TEAM_ICON_IMC + } + + if ( level.nv.roundScoreLimitComplete == true ) //Generally this is never true except for modes with RoundWinningKillReplay enabled + { + if ( friendlyTeam == level.nv.winningTeam ) + { + Announcement_SetSubText( announcement, "#GAMEMODE_MATCH_WON_BY_FRIENDLY_TEAM" ) + string friendlyTeamString = friendlyTeam == TEAM_IMC ? "#TEAM_IMC" : "#TEAM_MCOR" + Announcement_SetOptionalSubTextArgsArray( announcement, [ friendlyTeamString ] ) + } + else if ( enemyTeam == level.nv.winningTeam ) + { + Announcement_SetSubText( announcement, "#GAMEMODE_MATCH_WON_BY_ENEMY_TEAM" ) + string enemyTeamString = enemyTeam == TEAM_IMC ? "#TEAM_IMC" : "#TEAM_MCOR" + Announcement_SetOptionalSubTextArgsArray( announcement, [ enemyTeamString ] ) + } + + } + else + { + Announcement_SetSubText2( announcement, "#GAMEMODE_ROUND_WIN_CONDITION", GetRoundScoreLimit_FromPlaylist() ) + Announcement_SetSubText2AndIconDelay( announcement, subtext2IconDelay ) + } + + //Hack: GetTeamScore2 doesn't work mid-kill replay because we get the rewound values as opposed to the current values. + //Fix for R2 when we get the ability to flag certain values as "use current value instead of rewound value" + if ( imcTeamScore2 == null && militiaTeamScore2 == null ) + { + Announcement_SetLeftText( announcement, "#GAMEMODE_JUST_THE_SCORE", GameRules_GetTeamScore2( friendlyTeam ) ) + Announcement_SetRightText( announcement, "#GAMEMODE_JUST_THE_SCORE", GameRules_GetTeamScore2( enemyTeam ) ) + } + else + { + Assert( imcTeamScore2 != null && militiaTeamScore2 != null ) //Don't have only one team with teamScore2 override + if ( friendlyTeam == TEAM_IMC ) + { + Announcement_SetLeftText( announcement, "#GAMEMODE_JUST_THE_SCORE", imcTeamScore2 ) + Announcement_SetRightText( announcement, "#GAMEMODE_JUST_THE_SCORE", militiaTeamScore2 ) + + } + else + { + Announcement_SetLeftText( announcement, "#GAMEMODE_JUST_THE_SCORE", militiaTeamScore2 ) + Announcement_SetRightText( announcement, "#GAMEMODE_JUST_THE_SCORE", imcTeamScore2 ) + } + } + + Announcement_SetLeftIcon( announcement, leftIcon ) + Announcement_SetRightIcon( announcement, rightIcon ) +} + +void function PlayRoundWonConversationWithAnnouncementDelay( conversationDelay ) +{ + WaitEndFrame() //Necessary so we don't get the AnnouncementPurge signal from the same announcement we are originating from + clGlobal.levelEnt.EndSignal( "AnnoucementPurge" ) + + if ( conversationDelay != 0 ) + wait conversationDelay + + if ( level.nv.winningTeam == null ) + return + + entity player = GetLocalClientPlayer() + if ( player.GetTeam() == level.nv.winningTeam ) + PlayConversationToLocalClient( "RoundWonAnnouncement" ) + else if ( level.nv.winningTeam != TEAM_UNASSIGNED ) + PlayConversationToLocalClient( "RoundLostAnnouncement" ) +} + +void function UpdateScoreboardBadRepPresentMessage() +{ + if ( IsLobby() ) + return + + entity player = GetLocalClientPlayer() + + if ( level.nv.badRepPresent ) + player.cv.scoreboardBadRepPresentMessage.Show() + else + player.cv.scoreboardBadRepPresentMessage.Hide() +} + +void function UpdateChatHudLocationForTop3() +{ + var hudElement = HudElement( "IngameTextChat" ) + var height = hudElement.GetHeight() + var screenSize = Hud.GetScreenSize() + var position = hudElement.GetPos() + HudElement( "IngameTextChat" ).SetPos( position[0], -1 * ( screenSize[1] - ( height + screenSize[1] * 0.10 ) ) ) +} + +void function DisplayPostMatchTop3() +{ + array<entity> players = GetPlayerArray() + + for ( int i = players.len() - 1; i >= 0; i-- ) + { + if ( IsPrivateMatchSpectator( players[ i ] ) ) + players.remove( i ) + } + + if ( players.len() >= 1 ) + { + int localTeam = GetLocalClientPlayer().GetTeam() + #if PC_PROG + var rui = RuiCreate( $"ui/scoreboard_postmatch_top3_pc.rpak", clGlobal.topoFullScreen, RUI_DRAW_HUD, 1001 ) + UpdateChatHudLocationForTop3() + #else + var rui = RuiCreate( $"ui/scoreboard_postmatch_top3.rpak", clGlobal.topoFullScreen, RUI_DRAW_HUD, 1001 ) + #endif + string gamemode = GameRules_GetGameMode() + int functionref( entity, entity ) compareFunc = GameMode_GetScoreCompareFunc( gamemode ) + if ( compareFunc == null ) + { + printt( "gamemode doesn't have a compare func to display the top 3") + return + } + players.sort( compareFunc ) + int playerCount = players.len() + int currentPlace = 1 + for ( int i = 0; i < 3; i++ ) + { + if ( i >= playerCount ) + continue + + string playerRank + if ( i > 0 && compareFunc( players[i - 1], players[i] ) != 0 ) + currentPlace += 1 + + switch( currentPlace ) + { + case 1: + playerRank = "#GENERATION_NUMERIC_1" + break + case 2: + playerRank = "#GENERATION_NUMERIC_2" + break + case 3: + playerRank = "#GENERATION_NUMERIC_3" + break + } + + float cardScale = currentPlace == 1 ? 1.0 : 0.9 + + RuiSetString( rui, "playerName" + i, players[i].GetPlayerName() ) + RuiSetString( rui, "playerRank" + i, Localize( playerRank ) ) + RuiSetFloat( rui, "cardScale" + i, cardScale ) + RuiSetImage( rui, "cardImage" + i, CallingCard_GetImage( PlayerCallingCard_GetActive( players[i] ) ) ) + RuiSetImage( rui, "iconImage" + i, CallsignIcon_GetImage( PlayerCallsignIcon_GetActive( players[i] ) ) ) + RuiSetInt( rui, "layoutType" + i, CallingCard_GetLayout( PlayerCallingCard_GetActive( players[i] ) ) ) + RuiSetImage( rui, "cardGenImage" + i, PlayerXPGetGenIcon( players[i] ) ) + RuiSetString( rui, "playerLevel" + i, PlayerXPDisplayGenAndLevel( players[i].GetGen(), players[i].GetLevel() ) ) + RuiSetBool( rui, "isFriendly" + i, localTeam == players[i].GetTeam() ) + } + } +} + + +float function GetGameStartTime() +{ + return expect float( level.nv.gameStartTime.tofloat() ) +} + +void function ClGameState_SetPilotTitanStatusCallback( int functionref(entity,int) func ) +{ + file.pilotTitanStatusCallback = func +}
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_fastball.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_fastball.gnut index 52b563d4..815a2bb2 100644 --- a/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_fastball.gnut +++ b/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_fastball.gnut @@ -15,6 +15,7 @@ void function GamemodeFastball_Init() // used for respawn PrecacheParticleSystem( $"P_pod_screen_lasers_OUT" ) + SetShouldUseRoundWinningKillReplay( true ) SetRoundBased( true ) SetRespawnsEnabled( false ) Riff_ForceSetEliminationMode( eEliminationMode.Pilots ) diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_fastball_intro.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_fastball_intro.gnut index 6a1d0bbd..75482558 100644 --- a/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_fastball_intro.gnut +++ b/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_fastball_intro.gnut @@ -131,9 +131,6 @@ void function FastballPlayer( entity player ) ClearPlayerAnimViewEntity( player ) player.DeployWeapon() player.PlayerCone_Disable() - - RemoveButtonPressedPlayerInputCallback( player, IN_JUMP, PlayerHoldingJumpInIntro ) - RemoveButtonReleasedPlayerInputCallback( player, IN_JUMP, PlayerNoLongerHoldingJumpInIntro ) }) FirstPersonSequenceStruct throwSequence @@ -179,14 +176,10 @@ void function FastballPlayer( entity player ) vector throwVel = AnglesToForward( player.EyeAngles() ) * 950 throwVel.z = 675.0 - // allow players to gain extra height by holding jump after this point too - AddButtonPressedPlayerInputCallback( player, IN_JUMP, PlayerHoldingJumpInIntro ) - AddButtonReleasedPlayerInputCallback( player, IN_JUMP, PlayerNoLongerHoldingJumpInIntro ) - // wait for it to finish buddy.WaitSignal( "fastball_release" ) - if ( player in file.playersHoldingJump && file.playersHoldingJump[ player ] ) + if ( player.IsInputCommandHeld( IN_JUMP ) ) throwVel.z = 850.0 // have to correct this manually here since due to no 3p animation our position isn't set right during this sequence diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_gg.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_gg.gnut index 99a5ccf7..bd9b695a 100644 --- a/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_gg.gnut +++ b/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_gg.gnut @@ -32,7 +32,7 @@ void function OnPlayerRespawned_Threaded( entity player ) void function OnPlayerKilled( entity victim, entity attacker, var damageInfo ) { - if ( !victim.IsPlayer() || !attacker.IsPlayer() ) + if ( !victim.IsPlayer() || !attacker.IsPlayer() || GetGameState() != eGameState.Playing ) return if ( attacker == victim ) // suicide diff --git a/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_inf.gnut b/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_inf.gnut index cc9df116..2f045a47 100644 --- a/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_inf.gnut +++ b/Northstar.Custom/scripts/vscripts/gamemodes/_gamemode_inf.gnut @@ -103,7 +103,7 @@ void function RespawnInfected( entity player ) player.kv.airAcceleration = 2500 // scale health with num of infected, with 50 as base health - player.SetMaxHealth( 50 + ( GetPlayerArrayOfTeam( INFECTION_TEAM_SURVIVOR ).len() * 25 ) ) + player.SetMaxHealth( GetPlayerArrayOfTeam( INFECTION_TEAM_SURVIVOR ).len() * 10 ) // set loadout foreach ( entity weapon in player.GetMainWeapons() ) |