//============================================================================= // HUD: Superclass of the heads-up display. // //Copyright 1998-2013 Epic Games, Inc. All Rights Reserved. //============================================================================= class HUD extends Actor native config(Game) transient dependson(Canvas); //============================================================================= // Variables. var const color WhiteColor, GreenColor, RedColor; var PlayerController PlayerOwner; // always the actual owner /** Tells whether the game was paused due to lost focus */ var transient bool bLostFocusPaused; // Visibility flags var config bool bShowHUD; // Is the hud visible var bool bShowScores; // Is the Scoreboard visible var bool bShowDebugInfo; // If true, show properties of current ViewTarget var() bool bShowBadConnectionAlert; // Display indication of bad connection (set in C++ based on lag and packetloss). var config bool bShowDirectorInfoDebug; // If true matinee/director information will be visible in the HUD in DebugText var config bool bShowDirectorInfoHUD; // If true matinee/director information will be visible in the HUD (using KismetTextInfo) var globalconfig bool bMessageBeep; // If true, any console messages will make a beep var globalconfig float HudCanvasScale; // Specifies amount of screen-space to use (for TV's). /** Use the full screen extents for the canvas. Ignores splitscreen and cinematic mode scaling. */ var bool bRenderFullScreen; /** Scale the canvas in with the cinematic black bars. Default behavior. */ var bool bScaleCanvasForCinematicMode; /** If true, render actor overlays */ var bool bShowOverlays; /** Holds a list of Actors that need PostRender calls */ var array PostRenderedActors; // Console Messages struct native ConsoleMessage { var string Text; var color TextColor; var float MessageLife; var PlayerReplicationInfo PRI; }; var array ConsoleMessages; var const Color ConsoleColor; var config int ConsoleMessageCount; var globalconfig int ConsoleFontSize; var globalconfig int MessageFontOffset; var int MaxHUDAreaMessageCount; // Localized Messages struct native HudLocalizedMessage { // The following block of variables are set when the message is entered; // (Message being set indicates that a message is in the list). var class Message; var String StringMessage; var int Switch; var float EndOfLife; var float Lifetime; var float PosY; var Color DrawColor; var int FontSize; // The following block of variables are cached on first render; // (StringFont being set indicates that they've been rendered). var Font StringFont; var float DX, DY; var bool Drawn; var int Count; var object OptionalObject; }; var() transient HudLocalizedMessage LocalMessages[8]; var() float ConsoleMessagePosX, ConsoleMessagePosY; // DP_LowerLeft /** * Canvas to Draw HUD on. * NOTE: a new Canvas is given every frame, only draw on it from the HUD::PostRender() event */ var /*const*/ Canvas Canvas; // // Useful variables // /** Used to create DeltaTime */ var transient float LastHUDRenderTime; /** Time since last render */ var transient float RenderDelta; /** Size of ViewPort in pixels */ var transient float SizeX, SizeY; /** Center of Viewport */ var transient float CenterX, CenterY; /** Ratio of viewport compared to native resolution 1024x768 */ var transient float RatioX, RatioY; var globalconfig array DebugDisplay; // array of strings specifying what debug info to display for viewtarget actor // base engine types include "AI", "physics", "weapon", "net", "camera", and "collision" struct native KismetDrawTextInfo { var() string MessageText; var string AppendedText; var() Font MessageFont; var() vector2d MessageFontScale; var() vector2d MessageOffset; var() Color MessageColor; var float MessageEndTime; }; var array KismetTextInfo; //============================================================================= // Utils //============================================================================= // Draw3DLine - draw line in world space. native final function Draw3DLine(vector Start, vector End, color LineColor); native final function Draw2DLine(int X1, int Y1, int X2, int Y2, color LineColor); event PostBeginPlay() { super.PostBeginPlay(); PlayerOwner = PlayerController(Owner); // e.g. getting material pointers to control effects for gameplay NotifyBindPostProcessEffects(); } /* DrawActorOverlays() draw overlays for actors that were rendered this tick and have added themselves to the PostRenderedActors array */ native function DrawActorOverlays(vector Viewpoint, rotator ViewRotation); /************************************************************************************************************ Actor Render - These functions allow for actors in the world to gain access to the hud and render information on it. ************************************************************************************************************/ /** RemovePostRenderedActor() remove an actor from the PostRenderedActors array */ function RemovePostRenderedActor(Actor A) { local int i; for ( i=0; i 0 ) { FirstRouteCache = C.RouteCache[0]; } // show where pawn is going if ( (C == PlayerOwner) || (C.MoveTarget == FirstRouteCache) && (C.MoveTarget != None) ) { if ( (C == PlayerOwner) && (Dest != vect(0,0,0)) ) { if ( C.PointReachable(Dest) ) { Draw3DLine(C.Pawn.Location, Dest, MakeColor(255,255,255,255)); return; } C.FindPathTo(Dest); } if( C.RouteCache.Length > 0 ) { for ( i=0; i InMessageClass, PlayerReplicationInfo PRI, optional float LifeTime) { local int Idx, MsgIdx; MsgIdx = -1; // check for beep on message receipt if( bMessageBeep && InMessageClass.default.bBeep ) { PlayerOwner.PlayBeepSound(); } // find the first available entry if (ConsoleMessages.Length < ConsoleMessageCount) { MsgIdx = ConsoleMessages.Length; } else { // look for an empty entry for (Idx = 0; Idx < ConsoleMessages.Length && MsgIdx == -1; Idx++) { if (ConsoleMessages[Idx].Text == "") { MsgIdx = Idx; } } } if( MsgIdx == ConsoleMessageCount || MsgIdx == -1) { // push up the array for(Idx = 0; Idx < ConsoleMessageCount-1; Idx++ ) { ConsoleMessages[Idx] = ConsoleMessages[Idx+1]; } MsgIdx = ConsoleMessageCount - 1; } // fill in the message entry if (MsgIdx >= ConsoleMessages.Length) { ConsoleMessages.Length = MsgIdx + 1; } ConsoleMessages[MsgIdx].Text = M; if (LifeTime != 0.f) { ConsoleMessages[MsgIdx].MessageLife = WorldInfo.TimeSeconds + LifeTime; } else { ConsoleMessages[MsgIdx].MessageLife = WorldInfo.TimeSeconds + InMessageClass.default.LifeTime; } ConsoleMessages[MsgIdx].TextColor = InMessageClass.static.GetConsoleColor(PRI); ConsoleMessages[MsgIdx].PRI = PRI; } //=============================================== // Localized Message rendering function LocalizedMessage ( class InMessageClass, PlayerReplicationInfo RelatedPRI_1, PlayerReplicationInfo RelatedPRI_2, string CriticalString, int Switch, float Position, float LifeTime, int FontSize, color DrawColor, optional object OptionalObject ) { local int i, LocalMessagesArrayCount, MessageCount; if( InMessageClass == None || CriticalString == "" ) { return; } if( bMessageBeep && InMessageClass.default.bBeep ) PlayerOwner.PlayBeepSound(); if( !InMessageClass.default.bIsSpecial ) { AddConsoleMessage( CriticalString, InMessageClass, RelatedPRI_1 ); return; } LocalMessagesArrayCount = ArrayCount(LocalMessages); i = LocalMessagesArrayCount; if( InMessageClass.default.bIsUnique ) { for( i = 0; i < LocalMessagesArrayCount; i++ ) { if( LocalMessages[i].Message == InMessageClass ) { if ( InMessageClass.default.bCountInstances && (LocalMessages[i].StringMessage ~= CriticalString) ) { MessageCount = (LocalMessages[i].Count == 0) ? 2 : LocalMessages[i].Count + 1; } break; } } } else if ( InMessageClass.default.bIsPartiallyUnique ) { for( i = 0; i < LocalMessagesArrayCount; i++ ) { if( ( LocalMessages[i].Message == InMessageClass ) && InMessageClass.static.PartiallyDuplicates(Switch, LocalMessages[i].Switch, OptionalObject, LocalMessages[i].OptionalObject) ) break; } } if( i == LocalMessagesArrayCount ) { for( i = 0; i < LocalMessagesArrayCount; i++ ) { if( LocalMessages[i].Message == None ) break; } } if( i == LocalMessagesArrayCount ) { for( i = 0; i < LocalMessagesArrayCount - 1; i++ ) LocalMessages[i] = LocalMessages[i+1]; } ClearMessage( LocalMessages[i] ); // Add the local message to the spot. AddLocalizedMessage(i, InMessageClass, CriticalString, Switch, Position, LifeTime, FontSize, DrawColor, MessageCount, OptionalObject); } /** * Add the actual message to the array. Made easier to tweak in a subclass * * @Param Index The index in to the LocalMessages array to place it. * @Param InMessageClass Class of the message * @Param CriticialString String of the message * @Param Switch The message switch * @Param Position Where on the screen is the message * @Param LifeTime How long does this message live * @Param FontSize How big is the message * @Param DrawColor The Color of the message */ function AddLocalizedMessage ( int Index, class InMessageClass, string CriticalString, int Switch, float Position, float LifeTime, int FontSize, color DrawColor, optional int MessageCount, optional object OptionalObject ) { LocalMessages[Index].Message = InMessageClass; LocalMessages[Index].Switch = Switch; LocalMessages[Index].EndOfLife = LifeTime + WorldInfo.TimeSeconds; LocalMessages[Index].StringMessage = CriticalString; LocalMessages[Index].LifeTime = LifeTime; LocalMessages[Index].PosY = Position; LocalMessages[Index].DrawColor = DrawColor; LocalMessages[Index].FontSize = FontSize; LocalMessages[Index].Count = MessageCount; LocalMessages[Index].OptionalObject = OptionalObject; } function GetScreenCoords(float PosY, out float ScreenX, out float ScreenY, out HudLocalizedMessage InMessage ) { ScreenX = 0.5 * Canvas.ClipX; ScreenY = (PosY * HudCanvasScale * Canvas.ClipY) + (((1.0f - HudCanvasScale) * 0.5f) * Canvas.ClipY); ScreenX -= InMessage.DX * 0.5; ScreenY -= InMessage.DY * 0.5; } function DrawMessage(int i, float PosY, out float DX, out float DY ) { local float FadeValue; local float ScreenX, ScreenY; FadeValue = FMin(1.0, LocalMessages[i].EndOfLife - WorldInfo.TimeSeconds); Canvas.DrawColor = LocalMessages[i].DrawColor; Canvas.DrawColor.A = FadeValue * Canvas.DrawColor.A; Canvas.Font = LocalMessages[i].StringFont; GetScreenCoords( PosY, ScreenX, ScreenY, LocalMessages[i] ); DX = LocalMessages[i].DX / Canvas.ClipX; DY = LocalMessages[i].DY / Canvas.ClipY; DrawMessageText(LocalMessages[i], ScreenX, ScreenY); LocalMessages[i].Drawn = true; } function DrawMessageText(HudLocalizedMessage LocalMessage, float ScreenX, float ScreenY) { local FontRenderInfo FontInfo; Canvas.SetPos(ScreenX, ScreenY); FontInfo.bClipText = true; Canvas.DrawText(LocalMessage.StringMessage, FALSE,,, FontInfo); } function DisplayLocalMessages() { local float PosY, DY, DX; local int i, j, LocalMessagesArrayCount, AreaMessageCount; local float FadeValue; local int FontSize; // early out if ( LocalMessages[0].Message == None ) return; Canvas.Reset(true); LocalMessagesArrayCount = ArrayCount(LocalMessages); // Pass 1: Layout anything that needs it and cull dead stuff. for( i = 0; i < LocalMessagesArrayCount; i++ ) { if( LocalMessages[i].Message == None ) { break; } LocalMessages[i].Drawn = false; if( LocalMessages[i].StringFont == None ) { FontSize = LocalMessages[i].FontSize + MessageFontOffset; LocalMessages[i].StringFont = GetFontSizeIndex(FontSize); Canvas.Font = LocalMessages[i].StringFont; Canvas.TextSize( LocalMessages[i].StringMessage, DX, DY ); LocalMessages[i].DX = DX; LocalMessages[i].DY = DY; if( LocalMessages[i].StringFont == None ) { // `warn( "LayoutMessage("$LocalMessages[i].Message$") failed!" ); for( j = i; j < LocalMessagesArrayCount - 1; j++ ) LocalMessages[j] = LocalMessages[j+1]; ClearMessage( LocalMessages[j] ); i--; continue; } } FadeValue = (LocalMessages[i].EndOfLife - WorldInfo.TimeSeconds); if( FadeValue <= 0.0 ) { for( j = i; j < LocalMessagesArrayCount - 1; j++ ) LocalMessages[j] = LocalMessages[j+1]; ClearMessage( LocalMessages[j] ); i--; continue; } } // Pass 2: Go through the list and draw each stack: for( i = 0; i < LocalMessagesArrayCount; i++ ) { if( LocalMessages[i].Message == None ) break; if( LocalMessages[i].Drawn ) continue; PosY = LocalMessages[i].PosY; AreaMessageCount = 0; for( j = i; j < LocalMessagesArrayCount; j++ ) { if( LocalMessages[j].Drawn || (LocalMessages[i].PosY != LocalMessages[j].PosY) ) { continue; } DrawMessage( j, PosY, DX, DY ); PosY += DY; AreaMessageCount++; } if ( AreaMessageCount > MaxHUDAreaMessageCount ) { LocalMessages[i].EndOfLife = WorldInfo.TimeSeconds; } } } function DisplayKismetMessages() { local int KismetTextIdx; KismetTextIdx = 0; while( KismetTextIdx < KismetTextInfo.length ) { if( KismetTextInfo[KismetTextIdx].MessageEndTime > 0 && KismetTextInfo[KismetTextIdx].MessageEndTime <= WorldInfo.TimeSeconds) { KismetTextInfo.Remove(KismetTextIdx,1); } else { DrawText(KismetTextInfo[KismetTextIdx].MessageText$KismetTextInfo[KismetTextIdx].AppendedText, KismetTextInfo[KismetTextIdx].MessageOffset, KismetTextInfo[KismetTextIdx].MessageFont, KismetTextInfo[KismetTextIdx].MessageFontScale, KismetTextInfo[KismetTextIdx].MessageColor); ++KismetTextIdx; } } } function DrawText(string Text, vector2d Position, Font TextFont, vector2d FontScale, Color TextColor, optional const out FontRenderInfo RenderInfo) { local float XL, YL; Canvas.Font = TextFont; Canvas.TextSize(Text, XL, YL); Canvas.SetPos(Canvas.ClipX/2 - XL/2 + Position.X, Canvas.ClipY/3 - YL/2 + Position.Y); Canvas.SetDrawColor(TextColor.R, TextColor.G, TextColor.B, TextColor.A); Canvas.DrawText(Text, FALSE, FontScale.X, FontScale.Y, RenderInfo); } static function Font GetFontSizeIndex(int FontSize) { if ( FontSize == 0 ) { return class'Engine'.Static.GetTinyFont(); } else if ( FontSize == 1 ) { return class'Engine'.Static.GetSmallFont(); } else if ( FontSize == 2 ) { return class'Engine'.Static.GetMediumFont(); } else if ( FontSize == 3 ) { return class'Engine'.Static.GetLargeFont(); } else { return class'Engine'.Static.GetLargeFont(); } } /** * Called when the player owner has died. */ function PlayerOwnerDied() { } /** * Called in PostBeginPlay or postprocessing chain has changed (happens because of the worldproperties can define it's own chain and this one is set late). */ function NotifyBindPostProcessEffects() { // overload with custom code e.g. getting material pointers to control effects for gameplay. } /** * Pauses or unpauses the game due to main window's focus being lost. * @param Enable tells whether to enable or disable the pause state */ event OnLostFocusPause(bool bEnable) { if ( bLostFocusPaused == bEnable ) return; if ( WorldInfo.NetMode != NM_Client ) { bLostFocusPaused = bEnable; PlayerOwner.SetPause(bEnable); } } defaultproperties { TickGroup=TG_DuringAsyncWork bHidden=true RemoteRole=ROLE_None WhiteColor=(R=255,G=255,B=255,A=255) ConsoleColor=(R=153,G=216,B=253,A=255) GreenColor=(R=0,G=255,B=0,A=255) RedColor=(R=255,G=0,B=0,A=255) ConsoleMessagePosY=0.8 MaxHUDAreaMessageCount=3 bLostFocusPaused=false bScaleCanvasForCinematicMode=true bRenderFullScreen=false }