/*
	--------------------------------------------------------------
	HUD_StoryMode
	--------------------------------------------------------------

	Custom HUD type for use in KF 'Story' maps.

	Author :  Alex Quick

	--------------------------------------------------------------
*/

class HUD_StoryMode extends HUDKillingFloorSP
dependson(KFStoryGameInfo) ;

/* Same sort of icon as the Trader arrow -  shows you roughly where your next objective is located */
var						KFShopDirectionPointer		    ObjectiveArrow;

var     				KF_StoryGRI				  	    SGRI;

/* The amount of time that has passed since the last HUD Tick.  Used for controlling interpolation */
var                     float                           RenderDelta;

var                     float                           LastHUDRenderTime;

var                     float                           LastIconUpdateTime;

/* Current game objective according to the game replication info */
var						KF_StoryObjective				CurrentObj;

/* The HUD's currently rendered objective.  Tends to lag behind the 'CurrentObj' var because it fades in/ out*/
var						KF_StoryObjective				RenderObj;

var						int								ObjRenderIdx;

/* Objectives which haven't yet been displayed on the player's HUD but are qued & awaiting fade in/ out effects*/
var array				<KF_StoryObjective>				HUDObjs;

/* Cached list of all the objectives in the map */
var array               <KF_StoryObjective>             AllObjs;

/* Opacity of the currently rendered objective on the HUD */
var						float							RenderObjOpacity;

var						bool							bFadingIn;

var						bool							bFadingOut;

var                     float                           PendingFadeOutStartTime;

var                     bool                            bPendingFadeOut;

var						bool							bSkipTransitions;

var						float				            ObjFadeOutTime,ObjFadeInTime;

/* The amount of delay after an objective completes before it actually fades out */
var						float				            ObjFadeOutDelay;

/* Skip the usual fading between objectives and just display the current one */
var						bool				            bInstantObjTransitions;

var						float				            LastNewRenderObjTime;

var						bool				            bFadingOutObj;

var						bool				            bShowObjectives;

var                     bool                            bCollapseConditions;


struct SConditionHint
{
    var      name                                       ConditionName;
	var		 string										DataString,WorldHint;
	var      int                                        bComplete;
	var      float                                      ProgressPct;
	var      color                                      ProgressBar_Clr;
	var      color                                      World_Clr;
	var      byte                                       ConditionType,HintStyle,DisplayStyle;  // 1 == success , 0 == failure
	var      Actor                                      ConditionLoc;
	var      Material                                   WorldTex;
	var      Material                                   Material_ProgressBarFill,Material_ProgressBarBG;
	var      int                                        FontScale;
	var      bool                                       bHideWorld;
	var      bool                                       bIgnoreWorldHidden;
	var      float                                      LastProgressUpdateTime ;
	var      float                                      LastProgressVal;
	var      float                                      WidestDataX;
	var      float                                      ProgBarheight;
	var      float                                      HintWidth;
	var      float                                      HintHeight;
	var      name                                       ObjOwner;
	var      bool                                       bShowCheckBox;
    var      bool                                       bShowStrikeThrough;
	var      float                                      World_Texture_Scale;
	var 	 name										PendingLocActorTag ;   	// we want to cache an actor, but have no reference to it yet .
};


var array<SConditionHint>                               ConditionHints;

var                     float                           lastConditionupdateTime;

/* === Dialogue Rendering =================================================================================*/

struct SDialogueRenderInfo
{
     var    string                                      Speaker;
     var    string                                      Message;
     var    float                                       Duration;
     var    Material                                    Portrait;
     var    float                                       FirstDisplayedTime;
     var    float                                       Opacity;
     var    bool                                        bWrapped;
     var    bool                                        bFirstDisplay;
     var    KFStoryGameInfo.SVect2D                     ScreenPos;
     var    KFStoryGameInfo.EDialogueAlignment          ScreenAlignment;
     var    LobbyMenuAd                                 BinkPortrait;
     var    Material                                    BackGroundMat;
     var    KFStoryGameInfo.EDialogueScaleStyle         ScreenScaleStyle;
};

var array<SDialogueRenderInfo>                          Dialogues;

var array<KF_DialogueSpot>                              DialogueSpots;

var int                                                 CurrentDlgIndex;

var  	                                                SpriteWidget        DialogueBackground;
var  	                                                TextWidget          DialogueTitleWidget;
var  	                                                TextWidget          DialogueTextWidget;
var                                                     RelativeCoordsInfo  DialogueCoords;

var  array<string>                                      WrappedDialogue;

var                     LobbyMenuAd                     Intro_BinkMovie;

var                     bool                            bPlayingIntroMovie;

var                     bool                            bPlayedIntroMovie;

var                     KF_HUDStyleManager              HUDStyleManager;

var						int								StoryIconOffsetX;
var						int								StoryIconOffsetY;

/* @todo - there's a bug in this code where bNoKFColorCorrection is ignored, needs fixing
for now just gonna empty out this func
simulated function DrawModOverlay( Canvas C ){}  */

/* 	toggles displaying properties of player's current viewtarget

	Cut out the StandAlone only condition so I can see what up in net-play
*/

simulated event PostBeginPlay()
{
    Super.PostBeginPlay();
    CacheDialogueActors();
    CacheObjectives();
}

simulated function CacheObjectives()
{
    local KF_StoryObjective Obj;

    foreach AllActors(class 'KF_StoryObjective', Obj)
    {
        AllObjs[AllObjs.length] = Obj;
    }
}

simulated function CacheDialogueActors()
{
    local KF_DialogueSpot   NewDlgSpot;

    foreach AllActors(class 'KF_DialogueSpot', NewDlgSpot)
    {
        DialogueSpots[DialogueSpots.length] = NewDlgSpot;
    }
}

simulated function KF_DialogueSpot FindDialogueActor(name DlgName)
{
    local int i;

    for(i = 0 ; i < DialogueSpots.length ; i ++)
    {
        if(DialogueSpots[i].name == DlgName)
        {
            return DialogueSpots[i];
        }
    }
}

exec function ShowDebug()
{
	bShowDebugInfo = !bShowDebugInfo;
}

exec simulated function ToggleObjectiveHUD()
{
	bShowObjectives = !bShowObjectives ;
}

simulated function DrawSpectatingHud(Canvas C)
{
	Super.DrawSpectatingHUD(C);
    DrawStoryHUDInfo(C);
}


simulated function DrawHudPassA (Canvas C)
{
    DrawStoryHUDInfo(C);
    Super.DrawHUDPassA(C);
}

simulated function DrawStoryHUDInfo(Canvas C)
{
	if(bHideHUD ||
    PlayerOwner != none &&
    PlayerOwner.Player != none &&
	GUIController(PlayerOwner.Player.GUIController) != none &&
	GUIController(PlayerOwner.Player.GUIController).ActivePage != none)  // we've got another menu open, dont show this stuff...
	{
        return;
    }

    RenderStoryItems(C);
    DrawObjectiveInfo(C);

    if(!bShowScoreBoard)
    {
        DrawDialogue(C);
    }
}

/* Specific function to use Canvas.DrawActor()
 Clear Z-Buffer once, prior to rendering all actors */
function CanvasDrawActors( Canvas C, bool bClearedZBuffer )
{
    PostRenderStoryInventory(C);
    Super.CanvasDrawActors(C,bClearedZBuffer);
}

function PostRenderStoryInventory(Canvas C)
{
	local Inventory CurInv;
	local KF_StoryInventoryItem StoryInv;

    if(PawnOwner == none)
    {
        return;
    }

	for ( CurInv = PawnOwner.Inventory; CurInv != none; CurInv = CurInv.Inventory )
	{
        StoryInv = KF_StoryInventoryItem(CurInv);
        if(StoryInv != none )
        {
            StoryInv.RenderOverlays(C);
        }
	}
}

/* Draws overlays for Pickup-able or currently carried items in story missions */
simulated function RenderStoryItems(Canvas C)
{
	local Inventory CurInv;
	local KF_StoryInventoryItem StoryInv;
	local float PosX,PosY;
	local float IconSizeX,IconSizeY;
	local Material InvIcon;
    local float ScaleX,ScaleY;
    local int i;
    local KF_StoryPRI PRI;
    local float Opacity;
	local float IconSize;
	local float XCentre,YCentre;
	local vector ScreenPos;
	local Material RenderMat;
	local vector CameraLocation;
	local rotator CameraRotation;
	local float Dist;
	local vector RenderLocation;
	local vector CurrentPawnPos,NextPawnPos;
	local float AbsoluteDist;
	local vector MovementDir;
	local float DistMax;
	local float InterpSpeed,InterpDist;
    local KF_StoryInventoryPickup Pickup;

    /* Render icons over players heads while they are carrying Story Items*/
    if(PlayerOwner != none && PlayerOwner.GameReplicationinfo != none)
    {
        for(i = 0 ; i < PlayerOwner.GameReplicationInfo.PRIArray.length ; i ++)
        {
            PRI = KF_StoryPRI(PlayerOwner.GameReplicationInfo.PRIArray[i]);
            if(PRI == none ||
            PRI == PlayerOwner.PlayerReplicationInfo ||
            PRI.GetFloatingIconMat() == none)
            {
                continue;
            }
            else
            {
                /* Looks like we've got a relevant pawn.  That makes things nice and easy.. */
                if(PRI.GetOwnerPawn() != none &&
                !PRI.GetOwnerPawn().bDeleteMe &&
                !PRI.GetOwnerPawn().bPendingDelete)
                {
                    RenderLocation = PRI.GetOwnerPawn().GetHoverIconPosition() ;
                }
                else   // he's not relevant -  do some interpolation mumbo jumbo and pray it doesn't look horrible ..
                {
                    CurrentPawnPos  =   PRI.GetCurrentPawnLoc();

                    if(CurrentPawnPos == vect(0,0,0) ||
                    PRI.GetLastPawnLoc() == vect(0,0,0))
                    {
                        continue;
                    }

                    /* Client prediction - Attempt to figure out where the next update will place the Icon*/

                    MovementDir = Normal(CurrentPawnPos - PRI.GetLastPawnLoc());
                    AbsoluteDist = VSize(CurrentPawnPos - PRI.GetLastPawnLoc());
                    NextPawnPos  = CurrentPawnPos + (MovementDir * AbsoluteDist);

                    DistMax = 500.f;
                    InterpDist = VSize(NextPawnPos - PRI.GetInterpolatedPawnLoc());
                    InterpSpeed = InterpDist;

//                    log("InterpDist : "@InterpDist@" === InterpSpeed : "@InterpSpeed);

                    /* Huge move -  re-position immediately */
                    if(InterpDist > DistMax)
                    {
                        PRI.SetInterpolatedPawnLoc(NextPawnPos);
                    }

                    PRI.SetInterpolatedPawnLoc(VInterpTo(PRI.GetInterpolatedPawnLoc(),NextPawnPos,RenderDelta,InterpSpeed));

                    RenderLocation = PRI.GetInterpolatedPawnLoc();
                }

                if(RenderLocation == Vect(0,0,0))
                {
                    continue;
                }

                C.GetCameraLocation(CameraLocation, CameraRotation);

            	/* fading jazz from PlayerBeacon code */

            	Dist = vsize(CameraLocation-RenderLocation);
            	Dist -= class 'HUDKillingFloor'.default.HealthBarFullVisDist;
            	Dist = FClamp(Dist, 0, class 'HUDKillingFloor'.default.HealthBarCutoffDist-class 'HUDKillingFloor'.default.HealthBarFullVisDist);
            	Dist = Dist / (class 'HUDKillingFloor'.default.HealthBarCutoffDist- class 'HUDKillingFloor'.default.HealthBarFullVisDist);
            	Opacity = Max(byte((1.f - Dist) * 255.f),100.f);

                RenderMat = PRI.GetFloatingIconMat();
            	IconSize = 48.f;

                ScreenPos = C.WorldToScreen(RenderLocation);

                XCentre = ScreenPos.X;
                YCentre = ScreenPos.Y;

                /* Dont render stuff behind the camera */
            	if ( (Normal(RenderLocation - CameraLocation) dot vector(CameraRotation)) < 0 )
            	{
                    continue;
                }

                C.DrawColor.R = 255;
                C.DrawColor.G = 255;
                C.DrawColor.B = 255;
                C.DrawColor.A = Opacity;

                C.SetPos(XCentre - (0.5 * IconSize) - StoryIconOffsetX, YCentre - (0.5 * IconSize) - StoryIconOffsetY);
                C.DrawTileScaled(RenderMat, IconSize/ RenderMat.MaterialVSize() ,IconSize/ RenderMat.MaterialVSize() );
            }
        }
	}


    if( PawnOwner == none )
	{
		return;
	}

    /* Draw Projected World Icons */

    foreach DynamicActors(class 'KF_StoryInventoryPickup', Pickup)
    {
        Pickup.RenderOverlays(C);
    }


    PosX = C.ClipX * 0.3  ;
    PosY = C.ClipY * 0.9  ;

	IconSizeX = C.ClipX / 32.f;
	IconSizeY = IconSizeX;

    /* Draw little HUD Icons on the owning player's HUD */
	for ( CurInv = PawnOwner.Inventory; CurInv != none; CurInv = CurInv.Inventory )
	{
        StoryInv = KF_StoryInventoryItem(CurInv);
        if(StoryInv != none )
        {
            if(StoryInv.CarriedMaterial != none)
            {
                InvIcon = StoryInv.CarriedMaterial ;

                ScaleX = IconSizeX/ InvIcon.MaterialVSize() ;
                ScaleY = IconSizeY / InvIcon.MaterialVSize() ;

                C.SetPos(PosX- IconSizeX/2,PosY+ IconSizeY/2);
                C.DrawColor = WhiteColor;
                C.DrawColor.A = KFHUDAlpha;

                C.DrawTileScaled(InvIcon,ScaleX,ScaleY);

                PosX += (IconSizeX*ScaleX) * 1.1;
            }
        }
	}
}


/** Interpolate vector from Current to Target with constant step */
simulated function Vector VInterpTo(Vector Current, Vector Target, FLOAT DeltaTime, FLOAT InterpSpeed)
{
	local Vector Delta;
	local FLOAT DeltaM;
	local FLOAT MaxStep ;
	local Vector DeltaN;

    Delta  = Target - Current;
	DeltaM = VSize(Delta);

	MaxStep = InterpSpeed * DeltaTime;

	if( DeltaM > MaxStep )
	{
		if( MaxStep > 0.f )
		{
			DeltaN = Delta / DeltaM;
			return Current + DeltaN * MaxStep;
		}
		else
		{
			return Current;
		}
	}

	return Target;
}

simulated function DrawHUD(Canvas Canvas)
{
    RenderDelta = Level.TimeSeconds - LastHUDRenderTime;
    LastHUDRenderTime = Level.TimeSeconds;

    if(PlayerOwner != none)
    {
        if(!bHideHUD)
        {
            Super.DrawHUD(Canvas);
            DrawStoryDebugInfo(Canvas);
        }
    }
}

simulated function RenderIntroMovie(Canvas C)
{
    if(PlayerOwner.GameReplicationInfo.bMatchHasBegun)
    {
    	if ( Intro_BinkMovie == none)
        {
            Intro_BinkMovie = new class'LobbyMenuAd';
            if (Intro_BinkMovie.MenuMovie == None)
            {
                Intro_BinkMovie.MenuMovie = new class'Movie';
                Intro_BinkMovie.MenuMovie.Callbacks = Intro_BinkMovie;
            }
        }
        else
        {
            if(Intro_BinkMovie.MenuMovie != none)
            {
                if(!bPlayedIntroMovie)
                {
                    bPlayedIntroMovie = true;
    			    Intro_BinkMovie.MenuMovie.Open("../Movies/Dummy_UI_Vid"$".bik");
                    Intro_BinkMovie.MenuMovie.Play(true);
                }

                bPlayingIntroMovie = Intro_BinkMovie.MenuMovie.IsPlaying() ;
                if(bPlayingIntroMovie)
                {
                    C.SetPos(0,0);
                    C.DrawColor = WhiteColor;
                    C.DrawTileScaled(Intro_BinkMovie.MenuMovie, C.SizeX/Intro_BinkMovie.MenuMovie.GetWidth(),C.SizeY/Intro_BinkMovie.MenuMovie.GetHeight());
                }
            }
        }
    }
}


simulated function DrawStoryDebugInfo(Canvas Canvas)
{
    local float XPos,YPos;
    local float XL,YL;
    local int i;
    local KFStoryGameInfo StoryGI;
    local string NewObjName;
    local KF_StoryObjective Obj;
    local string ObjName;

    if(KFPlayerController_Story(PlayerOwner) != none &&
	KFPlayerController_Story(PlayerOwner).bShowObjectiveDebug)
	{
        Canvas.Font = GetConsoleFont(Canvas);
        Canvas.Style = ERenderStyle.STY_Alpha;
        Canvas.DrawColor = ConsoleColor;

        Canvas.SetPos(XPos,YPos);
        Canvas.DrawText("==================== OBJECTIVES ===========================");

        StoryGI = KFStoryGameInfo(Level.Game);
        if(StoryGI == none)
        {
            return;
        }

        YPos = Canvas.ClipY * 0.5;

        for(i = 0 ; i < StoryGi.Allobjectives.length ; i ++)
        {
            Obj = StoryGI.AllObjectives[i];
            if(Obj.bCompleted)
            {
                Canvas.DrawColor = GreenColor;
            }
            else if (Obj.bFailed)
            {
                Canvas.DrawColor = RedColor;
            }
            else
            {
                Canvas.DrawColor = WhiteColor;
            }

            Canvas.Font = GetConsoleFont(Canvas);

            ObjName = ""$Obj.ObjectiveName ;
            if(Obj == StoryGI.CurrentObjective)
            {
                ObjName = "((("$Obj.ObjectiveName$")))" ;
            }

            NewObjName = "["$i+1$"]"@ObjName@"-->";
            Canvas.StrLen(NewObjName,XL,YL);
            XPos += XL ;

            Canvas.SetPos(XPos,YPos);
            Canvas.DrawText(NewObjName);
        }
    }
}

simulated function Tick(float DeltaTime)
{
	Super.Tick(DeltaTime);

	if(PlayerOwner != none && !bHideHUD)
	{
		UpdateObjFadeVals(deltaTime);
	}

	/*
	copied from parent class ..
	This seems like an unnnecessarily redundant way to cache the GRI ...
	*/

	if ( SGRI == None &&
    PlayerOwner.GameReplicationInfo != none)
	{
		SGRI = KF_StoryGRI(PlayerOwner.GameReplicationInfo);
		if(SGRI != none)
		{
            HUDStyleManager = SGRI.GetHUDStyleManager();
        }
    }

}

simulated function UpdateObjFadeVals(float DeltaTime)
{
	if(RenderObj == none)
	{
	   return;
	}

	/* Switch to the next Objective .. fade the current one out, first */
	if(RenderObj != CurrentObj &&
	RenderObjOpacity == 1.f &&
	!bFadingIn && !bPendingFadeout)
	{
	    PendingFadeOutStartTime = Level.TimeSeconds;
	    bPendingFadeOut = true;
	}

	if(bPendingFadeOut && Level.TimeSeconds - PendingFadeOutStartTime >= ObjFadeOutDelay)
	{
        bPendingFadeOut = false;
        bFadingOut = true;
	}

	/* We're rendering the right objective, it just needs to fade in */
	if(RenderObjOpacity < 1.f &&
	!bFadingOut && CurrentObj != none)
	{
		bFadingIn = true;
	}

	if(bFadingIn)
	{
		RenderObjOpacity = FClamp(RenderObjOpacity + (DeltaTime / ObjFadeInTime),0.f,1.f) ;
		if(RenderObjOpacity == 1.f || bSkipTransitions)
		{
			ObjFadeInComplete();
		}
	}
	if(bFadingOut)
	{
		RenderObjOpacity = FClamp(RenderObjOpacity - (DeltaTime / ObjFadeOutTime),0.f,1.f) ;
		if(RenderObjOpacity < 0.1 || bSkipTransitions)
		{
			ObjFadeOutComplete();
		}
	}
}

simulated function ObjFadeOutComplete()
{
	local int CurrentObjIdx,LastRenderidx;

	LastRenderidx = ObjRenderIdx;

	bFadingOut = false;
	RenderObjOpacity = 0.f;
    RemoveHUDObjective(HUDObjs[LastRenderidx]);

	if(CurrentObj != none &&
    FindHUDObjective(CurrentObj.name,CurrentObjIdx) != none)
	{
		ObjRenderIdx = CurrentObjIdx;
	}
}

simulated function ObjFadeInComplete()
{
//	log("+++++++++++++++++++ Fade In complete for  : "@RenderObj.ObjectiveName);
	bFadingIn = false;
	RenderObjOpacity = 1.f;
	LastNewRenderObjTime = Level.TimeSeconds;
}

/* Returns 0 if it's a failure condition, or 1 if it's a success condition */
simulated function bool FindConditionTypeFor(SConditionHint TestCondition, out byte ConditionType)
{
    local KF_StoryObjective OwningObj;

    OwningObj = FindObjectiveByName(TestCondition.ObjOwner);

    if(OwningObj != none)
    {
        ConditionType = OwningObj.GetNamedConditionType(TestCondition.ConditionName);
    }

    return ConditionType < 2;
}


simulated function ResolveObjectiveInfo()
{
	local KF_StoryObjective	NewObj;
	local int NewIndex;

    if(SGRI == none)
    {
        return;
    }

	NewObj = SGRI.GetCurrentObjective() ;
	if(CurrentObj != NewObj)
	{
		if(NewObj != none &&
        FindHUDObjective(NewObj.name,NewIndex) == none &&
        (SGRI.GetDebugTargetObjective() == none || SGRI.GetDebugTargetObjective() ==
        NewObj))
		{
			AddHUDObjective(NewObj);
			log("Add Hud Objective : "@NewObj, 'Story_Debug');
		}

		CurrentObj = NewObj;
	}


	if(HUDObjs.length > 0)
	{
		RenderObj = HUDObjs[Min(ObjRenderIdx,HUDObjs.length-1)] ;
//		log("======== Current RenderObj :::: "@RenderObj.ObjectiveName@" ========== "@RenderObjOpacity@"======== Target RenderObj :::: "@CurrentObj.ObjectiveName);
	}
}

simulated function KF_StoryObjective  FindObjectiveByName(name TestObj, optional out int i)
{
	for(i = 0 ; i < AllObjs.length ; i ++)
	{
		if(AllObjs[i].name == TestObj)
		{
			return AllObjs[i];
		}
	}
}

simulated function KF_StoryObjective  FindHUDObjective(name TestObj, optional out int i)
{
	for(i = 0 ; i < HUDObjs.length ; i ++)
	{
		if(HUDObjs[i].name == TestObj)
		{
			return HUDObjs[i];
		}
	}
}

simulated function AddHUDObjective(KF_StoryObjective NewObj)
{
    if(NewObj != none && NewObj.bShowOnHUD)
    {
//        log("** ADD HUD OBJ :: "@NewObj.ObjectiveName);

        /* Apply any pending style preset before adding it to the HUD */
        ApplyObjectiveStylePresets(NewObj);

 	    HUDObjs.insert(HUDObjs.length,1);
     	HUDObjs[HUDObjs.length - 1] = NewObj;
    }
}

simulated function ApplyObjectiveStylePresets(KF_StoryObjective NewObj)
{
    if(HUDStylemanager != none && HUDStylemanager.StylePreset.Objectives.bOverride)
    {
        NewObj.HUD_Background                = HUDStylemanager.StylePreset.Objectives.BackGround;
        NewObj.HUD_Header.Header_Color       = HUDStylemanager.StylePreset.Objectives.Header.Header_Color;
        NewObj.HUD_Header.Header_Scale       = HUDStylemanager.StylePreset.Objectives.Header.Header_Scale;
        NewObj.HUD_ScreenPosition            = HUDStyleManager.StylePreset.Objectives.Position;
    }
}

simulated function RemoveHUDObjective(KF_StoryObjective RemoveObj)
{
	local int RemovalIndex;
	local KF_StoryObjective ObjToRemove;
    local int i;

    log("Remove HUD Obj : "@RemoveObj, 'Story_Debug');

	if(RemoveObj !=none)
	{
//        log("** REMOVE HUD OBJ :: "@RemoveObj.ObjectiveName);

        ObjToRemove = FindHUDObjective(RemoveObj.name,RemovalIndex);
        HUDObjs.Remove(RemovalIndex,1);

        /* Remove any associated condition info as well */
        for(i = 0 ; i < ConditionHints.length ; i ++)
        {
            if(ConditionHints[i].ObjOwner == RemoveObj.Name)
            {
                RemoveConditionHint(ConditionHints[i].ConditionName);
            }
        }
    }
}


simulated function DrawObjWorldIcons(Canvas C)
{
	local vector CameraLocation, CamDir, TargetLocation, HBScreenPos;
	local rotator CameraRotation;
	local float Dist;
	local color OldDrawColor;
	local string HintString;
	local float XL,YL;
	local int i;
	local float IconX,IconY;
	local float LargestX,LargestY;
	local float OpacityModifier;
	local float DistX,DistY;
	local string DistString;
	local float FarthestDistSq,DistSq;
	local float FarthestDist;
	local float DistanceScale;
	local int FontScale;
	local float IconSize;

	// rjp --  don't draw the health bar if menus are open
	// exception being, the Veterancy menu

	if ( PlayerOwner.Player.GUIController.bActive && GUIController(PlayerOwner.Player.GUIController).ActivePage.Name != 'GUIVeterancyBinder' )
	{
		return;
	}

	OldDrawColor = C.DrawColor;
    C.Style = ERenderStyle.STY_Alpha;
	C.GetCameraLocation(CameraLocation, CameraRotation);
	CamDir  = vector(CameraRotation);

	/* Establish distance scaling values for the icons before drawing */

    for(i = 0 ; i < ConditionHints.length ; i ++)
    {
        if(!ShouldRenderCondition(ConditionHints[i],"World"))
        {
            continue;
        }

        DistSq = VSizeSquared(ConditionHints[i].ConditionLoc.Location - PlayerOwner.CalcViewLocation) ;
        if(DistSq > FarthestDistSq)
        {
            FarthestDistSq = DistSq;
        }
    }
    i=0;

    FarthestDist = Sqrt(FarthestDistSq) ;

	for(i = 0 ; i < ConditionHints.length ; i ++)
	{
	   if(!ShouldRenderCondition(ConditionHints[i],"World"))
	   {
	       continue;
	   }

        TargetLocation = Conditionhints[i].ConditionLoc.Location;
	    Dist = VSize(TargetLocation - CameraLocation);

     	/* Check Distance Threshold / behind camera cut off   */
	    if ( (Normal(TargetLocation - CameraLocation) dot CamDir) < 0 )
	    {
            continue;
        }

        /* Target is located behind camera
	    HBScreenPos = C.WorldToScreen(TargetLocation);

	    if ( HBScreenPos.X <= 0 || HBScreenPos.X >= C.SizeX || HBScreenPos.Y <= 0 || HBScreenPos.Y >= C.SizeY)
	    {
		    HBScreenPos = C.WorldToScreen(TargetLocation);

		    if ( HBScreenPos.X <= 0 || HBScreenPos.X >= C.ClipX || HBScreenPos.Y <= 0 || HBScreenPos.Y >= C.ClipY)
		    {
                return;

            }
        }     */


	    C.DrawColor = WhiteColor;
        HBScreenPos = C.WorldToScreen(TargetLocation);
        DistanceScale = 1.f;//FClamp(1.f - (Square(Dist) / FarthestDistSq) , 0.4f,1.f);
        if(DistanceScale <= 0.9)
        {
            FontScale = 1;
        }
        else if(DistanceScale <= 0.75)
        {
            FontScale = 0;
        }
        else if(DistanceScale <= 0.5)
        {
            FontScale = -1;
        }
    	C.Font = LoadFont(ResolveFontResolution(C,FontScale));


	    HintString = ConditionHints[i].WorldHint;
	    if ( HintString != "" )
	    {
		   //  HintString = HintString @ "(" @ (Round(VSize(TargetLocation  - C.ViewPort.Actor.Pawn.Location)/50.0)) @ "m )";
		     C.StrLen(HintString, XL, YL);
        }

        OpacityModifier = FClamp(1.f - ((VSize(ConditionHints[i].ConditionLoc.Location - PlayerOwner.CalcViewLocation) - 1500.f) / 1500.f),0.6f,1.f) ;
        C.DrawColor = ConditionHints[i].World_Clr;
        C.DrawColor.A = KFHUDAlpha * OpacityModifier;

        if(ConditionHints[i].WorldTex != none)
        {
            IconX = ConditionHints[i].WorldTex.MaterialUSize() * DistanceScale * ConditionHints[i].World_Texture_Scale * (C.SizeX/1920.f);
            IconY = IconX ;
            IconSize =  (IconX / ConditionHints[i].WorldTex.MaterialUSize()) ;
        }

        LargestX = FMax(XL,IconX);
        LargestY = FMax(YL,IconY);

        /* Clamp icons to the edges of the screeen */

//        HBScreenPos.X = FClamp(HBScreenPos.X,LargestX,C.ClipX - LargestX);
//        HBScreenPos.Y = FClamp(HBScreenPos.Y,LargestY,C.ClipY - LargestY);

        if(ConditionHints[i].WorldTex != none)
        {
            C.SetPos(HBScreenPos.X - (0.5*IconX), HBScreenPos.Y - (IconY) - 16.f);
            C.DrawTileScaled(ConditionHints[i].WorldTex, IconSize ,(IconY/ ConditionHints[i].WorldTex.MaterialVSize()));
        }

        if ( HintString != "" )
	    {
		    if ( XL > 0.125 * C.ClipY )
		    {
			 //    C.Font = GetFontSizeIndex(C, -2 );

                C.StrLen(HintString, XL, YL);
		    }

            C.SetPos(HBScreenPos.X - 0.5*XL , HBScreenPos.Y - YL/2 );
	       	C.DrawTextClipped(HintString);
        }


        DistString = int(Dist / 50)$"m" ;
        C.StrLen(DistString,DistX,DistY);
		C.SetPos(HBScreenPos.X - (0.5* IconSize) - DistX/2 , HBScreenPos.Y + YL/2);
		C.DrawTextClipped(DistString);
	 }

}


simulated function DrawObjectiveInfo(Canvas C)
{
	if(!bShowObjectives )
	{
	   return;
	}

	ResolveObjectiveInfo();

	if(RenderObj != none)
	{
	   DrawObjWorldIcons(C);
	   DrawObjectiveHints(C, RenderObj,RenderObjOpacity);
    }
}


simulated function bool  FindExistingCondition(name ConditionName, out Int Index)
{
    local int i;

    for(i = 0 ; i < ConditionHints.length ; i ++)
    {
      if(ConditionHints[i].ConditionName == ConditionName)
      {
          Index = i;
          return true;
      }
    }

    return false;
}

function  KF_ObjectiveCondition  FindConditionByName(name ConditionName)
{
    local KF_ObjectiveCondition Condition;

    foreach AllObjects(class 'KF_ObjectiveCondition' , Condition)
    {
        if(Condition.name == ConditionName)
        {
            return Condition;
        }
    }
}

simulated function UpdateConditionHint(
name UpdatedCondition,
name NewOwner,
float NewProgressPct,
Actor NewLocActor,
string NewDataString,
bool NewComplete,
name PendingLocActorTag)
{
    local int Index;
    local KF_ObjectiveCondition ConditionObject;

    ConditionObject = FindConditionByName(UpdatedCondition);
    if(ConditionObject == none)
    {
        log("Warning - Could not find Object Reference for condition of name : "@UpdatedCondition,'Story_Debug');
        return;
    }

    if(FindExistingCondition(UpdatedCondition,Index))
    {
        if(NewProgressPct != ConditionHints[Index].ProgressPct)
        {
            ConditionHints[Index].LastProgressUpdateTime = Level.TimeSeconds;
            ConditionHints[Index].LastProgressVal        = ConditionHints[Index].ProgressPct;
        }

        ConditionHints[index].ObjOwner     = NewOwner;
        ConditionHints[Index].DataString   = NewDataString;
        ConditionHints[Index].ProgressPct  = NewProgressPct;
        ConditionHints[Index].bComplete    = int (NewComplete);
        ConditionHints[Index].ConditionLoc = NewLocActor ;
        ConditionHints[Index].PendingLocActorTag = PendingLocActorTag;
    }
    else
    {
        AddNewConditionHint(ConditionObject,
        NewOwner,
        NewProgressPct,
        NewLocActor,
        NewDataString,
        NewComplete,
		PendingLocActorTag);
    }
}


simulated function AddNewConditionHint(
KF_ObjectiveCondition NewCondition,
name NewOwner,
float NewProgressPct,
Actor NewLocActor,
string NewDataString,
bool bComplete,
name PendingLocActorTag)
{
    ConditionHints.length                                               = ConditionHints.length + 1;
    ConditionHints[ConditionHints.length - 1].ConditionName             = NewCondition.name;
    ConditionHints[ConditionHints.length - 1].ObjOwner                  = NewOwner;
    ConditionHints[ConditionHints.length - 1].ConditionLoc              = NewLocActor;
    ConditionHints[ConditionHints.length - 1].World_Texture_Scale       = NewCondition.HUD_World.World_Texture_Scale;
    ConditionHints[ConditionHints.length - 1].DataString                = NewDataString;
    ConditionHints[ConditionHints.length - 1].WorldHint                 = NewCondition.HUD_World.World_Hint;
    ConditionHints[ConditionHints.length - 1].bHideWorld                = NewCondition.HUD_World.bHide;
    ConditionHints[ConditionHints.length - 1].HintStyle                 = NewCondition.HUD_Screen.Screen_ProgressStyle;
    ConditionHints[ConditionHints.length - 1].DisplayStyle              = NewCondition.HUD_Screen.Screen_CountStyle;
    ConditionHints[ConditionHints.length - 1].bShowCheckBox             = NewCondition.HUD_Screen.bShowCheckBox;
    ConditionHints[ConditionHints.length - 1].bShowStrikeThrough        = NewCondition.HUD_Screen.bShowStrikethrough;
    ConditionHints[ConditionHints.length - 1].bIgnoreWorldHidden        = NewCondition.HUD_World.bIgnoreWorldLocHidden;
    ConditionHints[ConditionHints.length - 1].bComplete                 = int(bComplete);
    ConditionHints[ConditionHints.length - 1].PendingLocActorTag 		= PendingLocActorTag;

    FindConditionTypeFor(ConditionHints[ConditionHints.length -1],ConditionHints[ConditionHints.length -1].ConditionType);

    /* Style Settings */
    if(!ApplyConditionStylePresets(Conditionhints.length - 1))
    {
        Conditionhints[Conditionhints.length - 1].World_Clr                 = NewCondition.HUD_World.World_Clr;
        Conditionhints[Conditionhints.length - 1].ProgressBar_Clr           = NewCondition.HUD_Screen.Screen_Clr;
        ConditionHints[ConditionHints.length - 1].WorldTex                  = NewCondition.HUD_World.World_Texture;
        ConditionHints[ConditionHints.length - 1].Material_ProgressBarBG    = NewCondition.HUD_Screen.Screen_ProgressBarBG;
        ConditionHints[ConditionHints.length - 1].Material_ProgressBarFill  = NewCondition.HUD_Screen.Screen_ProgressBarFill;
        ConditionHints[ConditionHints.length - 1].FontScale                 = NewCondition.HUD_Screen.FontScale;
    }
}


simulated function bool ApplyConditionStylePresets(int Index)
{
    if(HUDStylemanager == none ||
    !HUDStylemanager.StylePreset.Objectives.bOverride)
    {
        return false;
    }

    ConditionHints[Index].World_Clr                 = HUDStyleManager.StylePreset.Objectives.Conditions.Style_ObjCondition_World.World_Clr;
    Conditionhints[Index].ProgressBar_Clr           = HUDStyleManager.StylePreset.Objectives.Conditions.Style_ObjCondition_Screen.Screen_Clr;
    ConditionHints[Index].WorldTex                  = HUDStyleManager.StylePreset.Objectives.Conditions.Style_ObjCondition_World.World_Texture;
    ConditionHints[Index].Material_ProgressBarBG    = HUDStyleManager.StylePreset.Objectives.Conditions.Style_ObjCondition_Screen.Screen_ProgressBarBG;
    ConditionHints[Index].Material_ProgressBarFill  = HUDStyleManager.StylePreset.Objectives.Conditions.Style_ObjCondition_Screen.Screen_ProgressBarFill;
    ConditionHints[Index].FontScale                 = HUDStyleManager.StylePreset.Objectives.Conditions.Style_ObjCondition_Screen.FontScale;

    return true;
}



simulated function RemoveConditionHint(name HintToRemove)
{
    local int Index;

    if(FindExistingCondition(HintToRemove,Index))
    {
        ConditionHints.Remove(Index,1);
    }
}

simulated function float ResolveFontResolution(Canvas C, optional int FontScale)
{
    local float FontSize;
    local float SizeModifier;

    Sizemodifier = 1.f;
    if(FontScale != 1)
    {
        switch(FontScale)
        {
            case -1:  SizeModifier = 3.f;  break;     // tiny
            case 0 :  SizeModifier = 2.f;  break;     // small
            case 2 :  SizeModifier = -1.f; break;     // large
            case 3 :  SizeModifier = -2.f; break;     // very large.
        }
    }

    if ( C.ClipX <= 640 )
	 FontSize = 7;
	else if ( C.ClipX <= 800 )
	 FontSize = 6;
	else if ( C.ClipX <= 1024 )
	 FontSize = 5;
	else if ( C.ClipX <= 1280 )
	 FontSize = 4;
	else
	 FontSize = 3;

	FontSize += SizeModifier;
    return FontSize;
}

exec simulated function SetObjX(float X)
{
    if(CurrentObj != none)
    {
        CurrentObj.HUD_ScreenPosition.Horizontal = X ;
    }
}

exec simulated function SetObjY(float Y)
{
    if(CurrentObj != none)
    {
        CurrentObj.HUD_ScreenPosition.Vertical = Y ;
    }
}

/* Determines which elements of the ConditionHints array should be rendered at any given time
primarily used to ensure that hidden conditions, or those belonging to an objective other than the
one on the HUD are not drawn. */

simulated function bool     ShouldRenderCondition(SConditionHint  TestCondition, string Type)
{
    switch(Type)
    {
        case "World" :

        if(TestCondition.bHideWorld ||
           TestCondition.bComplete == 1 ||
           (TestCondition.WorldHint == "" && TestCondition.WorldTex == none) ||
           TestCondition.Conditionloc == none ||
           TestCondition.ConditionLoc.Location == Vect(0,0,0) ||
           (TestCondition.ConditionLoc.bHidden && !TestCondition.bIgnoreWorldHidden) ||
           (KF_BreakerBoxNPC(TestCondition.ConditionLoc) != none &&
           !KF_BreakerBoxNPC(TestCondition.ConditionLoc).bActive) )
        {
            return false;
        }
        break;

        case "Screen" :

        if(TestCondition.HintStyle == 0)
        {
            return false;
        }
        break;
    }

    return TestCondition.ObjOwner == RenderObj.name;
}


/* Renders Objective Hints on the Players HUD telling him what his objective is and where to find it */

simulated final function DrawObjectiveHints(Canvas C, KF_StoryObjective CurrentObj, float FadeValue)
{
	local float     SuccessWidth, SuccessHeight;
	local string	ObjectiveHint;
	local float     PosX,PosY;
	local float     BGWidth,BGHeight;
	local float     ScalingValue;
	local int       i;
	local float     WidestX;
    local float     HeaderX,HeaderY;
    local float     DataX,DataY;
    local int       FontSize;
    local int       DataFontSize;
    local float     BarWidth;
    local float     WidestDataX;
    local int       NumScreenConditions;
    local float     HighlightWidth;
    local float     HighlightFadeVal;
    local float     SecondaryBarWidth;
    local float     HighlightFadeTime;
    local float     OpacityModifier;
    local float     WidestStringX;
    local float     BarHeightMultiplier;
    local string    HintString;
    local Material  BGTex;
    local float     HeaderScale;
    local Material  CheckboxMat;
    local float     CheckboxScale;
    local float     CheckBoxSize;
    local float     OldXPos;
    local float     BarBGWidth,StrikeThroughWidth;

    BarHeightMultiplier = 1.25f;
    HeaderScale = 0.5;
    CheckBoxScale = 1.5;

    if ( PlayerOwner != none && KFGRI != none && FadeValue > 0 && !bShowScoreboard)
    {
        if ( CurrentObj != none && ConditionHints.length > 0 )
	   	{
			/* Coordinates to start drawing at */

            PosX = C.SizeX * CurrentObj.HUD_ScreenPosition.Horizontal;  //(C.SizeX / 64.f) ;
			PosY = C.SizeY * CurrentObj.HUD_ScreenPosition.Vertical;  //0  ;

		    /*================ SIZING PASS ====================================================================
		    ==================================================================================================*/

            ScalingValue = CurrentObj.HUD_Background.Background_Scale ;
            FontSize = ResolveFontResolution(C,CurrentObj.HUD_Header.Header_Scale);
    	    DataFontSize = FontSize;

		    /* Size up the Objective Header text */

            /* Header uses a massive font, gotta scale things */

		    C.FontScaleX      = HeaderScale;
		    C.FontScaleY      = HeaderScale;
		    C.Font            = GetWaitingFontSizeIndex(C,CurrentObj.HUD_Header.Header_Scale);//LoadFont(FontSize);
		    C.TextSize(CurrentObj.HUD_Header.Header_Text,HeaderX,HeaderY);
		    WidestX           = FMax(WidestX,HeaderX);
		    WidestStringX     = WidestX;

            HeaderY *= BarHeightMultiplier;

            C.FontScaleX = 1.f;
		    C.FontScaleY = 1.f;

		    /* Now Size up the conditions */

            for(i = 0 ; i < ConditionHints.Length ; i ++)
            {
                if(!ShouldRenderCondition(ConditionHints[i],"Screen"))
	            {
	                ConditionHints[i].ProgBarheight = 0 ;
	                continue;
	            }

                /* Check that there's actually something to display */
                if(ConditionHints[i].HintStyle > 0 && ConditionHints[i].DataString != "")
                {
	               NumScreenConditions ++ ;
                }

                FontSize      = ResolveFontResolution(C,ConditionHints[i].FontScale);
			    DataFontSize  = FontSize;
			    C.Font        = LoadFont(FontSize);

                ObjectiveHint    = ConditionHints[i].DataString ;
		        C.StrLen(ObjectiveHint, SuccessWidth, SuccessHeight);

		        SuccessWidth  *= ScalingValue;
		        SuccessHeight *= ScalingValue;

                CheckBoxSize = (FMax(SuccessHeight,DataY)* BarHeightMultiplier) * CheckboxScale;
                if(Conditionhints[i].bShowCheckBox)
                {
                    SuccessWidth += CheckBoxSize;
                }

		        ConditionHints[i].HintWidth = SuccessWidth;
                ConditionHints[i].HintHeight = SuccessHeight;

		        WidestX       = FMax(WidestX,SuccessWidth);
		        WidestStringX = WidestX;

                // retrieve scaling for data string

			    C.StrLen(ConditionHints[i].DataString,DataX,DataY);
			    DataX *= ScalingValue;
			    DataY *= ScalingValue;

                /* Since the Data string size affects the background scale we never want it to shrink */

 			    if(DataX > ConditionHints[i].WidestDataX)
			    {
                    ConditionHints[i].WidestDataX = DataX;
			    }
			    WidestDataX = FMax(DataX,WidestDataX);
                ConditionHints[i].ProgBarHeight = FMax(SuccessHeight,DataY)* BarHeightMultiplier ;

            }

		    WidestX += C.ClipX / 64.f;

            if(NumScreenConditions == 0 && CurrentObj.HUD_Header.Header_Text == "")
            {
                return;
            }

            if(CurrentObj.HUD_Background.Background_AspectRatio < 2)
            {
                BGHeight += HeaderY;
            }

            for(i = 0 ; i < ConditionHints.length ; i ++)
            {
                if(CurrentObj.HUD_Background.Background_AspectRatio < 2)
                {
                    BGHeight += ConditionHints[i].ProgBarheight ;
                }
            }

            BGHeight += CurrentObj.HUD_Background.Background_Padding ;
            if(CurrentObj.HUD_Background.Background_AspectRatio < 2)
            {
                BGWidth  =  WidestX + (CurrentObj.HUD_Background.Background_Padding );
            }
            else
            {
                BGWidth += CurrentObj.HUD_Background.Background_Padding ;
            }

            if(CurrentObj.HUD_BackGround.Background_Texture != none)
            {
		        if(CurrentObj.HUD_Background.Background_AspectRatio == Aspect_FromTexture)
                {
                    BGWidth  =  CurrentObj.HUD_Background.Background_Texture.MaterialUSize() ;
                    BGHeight =  CurrentObj.HUD_Background.Background_Texture.MaterialVSize() ;
                }
            }

            BGHeight *= ScalingValue  ;
            BGWidth *= ScalingValue  ;

            // clamp - make sure it's not travelling off screen
            PosX = FClamp(PosX-BGWidth/2,0,C.SizeX - BGWidth);
            PosY = FClamp(PosY,0,C.SizeY - BGHeight);

            /*================ DRAWING PASS ====================================================================
            ==================================================================================================*/

            /* Draw Background first */

            OpacityModifier = 1.f;

            C.Style = ERenderStyle.STY_Alpha;
            C.DrawColor = WhiteColor;
            C.DrawColor.A = (KFHUDAlpha * FadeValue) * OpacityModifier ;

            if(!bCollapseConditions)
            {
                BGTex = CurrentObj.HUD_Background.Background_Texture;
            }
            else
            {
                BGTex = CurrentObj.HUD_Background.Background_Texture_Collapsed ;
            }

            if(BGTex != none)
            {
                C.SetPos(PosX ,PosY);
                if(CurrentObj.HUD_Background.Background_AspectRatio > 0)
                {
                    C.DrawTileScaled(BGTex, BGWidth/BGTex.MaterialUSize() ,BGHeight / BGTex.MaterialVSize());
                }
                else
                {
                    C.DrawTileStretched(BGTex, BGWidth ,BGHeight);
                }
            }

            if(bCollapseConditions)
            {
                return;
            }

            /* Try to center justify everything */

            PosX = FClamp(PosX+CurrentObj.HUD_Background.BackGround_Offset.Horizontal,0,C.SizeX - BGWidth);
            PosY += CurrentObj.HUD_BackGround.BackGround_Offset.Vertical ;

            /* Now draw objective header title */

            C.FontScaleX = HeaderScale;
		    C.FontScaleY = HeaderScale;
            C.Font = GetWaitingFontSizeIndex(C,CurrentObj.HUD_Header.Header_Scale);//
            C.DrawColor = CurrentObj.HUD_Header.Header_Color;
            C.DrawColor.A = (KFHUDAlpha * FadeValue) * OpacityModifier ;
            C.SetPos(PosX - (HeaderX * 0.5) + (0.5*BGWidth),PosY );
            C.DrawText(CurrentObj.HUD_Header.Header_Text,true);
            C.FontScaleX = 1.f;
		    C.FontScaleY = 1.f;


            /* Shift everything down so it fits under the header */
            PosY += HeaderY * 1.1;

            /* Next draw the conditions - starting with the backplates and ending with the strings */

            for(i = 0 ; i < ConditionHints.length ; i ++)
            {
                if(!ShouldRenderCondition(ConditionHints[i],"Screen"))
                {
                    continue;
                }

                OpacityModifier = 1.f ;

                /* Shift it to the right abit if we're rendering a Checkbox */
                OldXPos = PosX;
                if(Conditionhints[i].bShowCheckBox)
                {
                    PosX += Conditionhints[i].ProgBarheight * CheckboxScale;
                }

                FontSize           = ResolveFontResolution(C,ConditionHints[i].FontScale);
                C.Font             = LoadFont(FontSize);

                /* Draw progreess bar & backdrop */

                C.StrLen(ConditionHints[i].DataString,SuccessWidth,SuccessHeight);
		        SuccessWidth  *= ScalingValue;
		        SuccessHeight *= ScalingValue;

                C.DrawColor = ConditionHints[i].ProgressBar_Clr ;
                StrikeThroughWidth = WidestX;
                BarBGWidth = WidestX;

                if(Conditionhints[i].bShowCheckBox)
                {
                    CheckBoxSize = ConditionHints[i].ProgBarheight * CheckboxScale;
                    BarBGWidth -= CheckboxSize;
                    StrikeThroughWidth -= CheckBoxSize;
                }

                // Render a strike-through if requested.  (defaults to on)
                if(ConditionHints[i].bShowStrikeThrough &&
                GetCheckBoxMaterialFor(ConditionHints[i]) != none )
                {
                    OpacityModifier = 0.5f ;
                    C.DrawColor.A = (KFHUDAlpha * FadeValue) * OpacityModifier  ;
                    C.SetPos(PosX + (BGWidth-WidestX)/2 , PosY);
                    C.DrawTileStretched(Texture'KFStoryGame_Tex.HUD.Objective_Strikethrough', StrikeThroughWidth,ConditionHints[i].ProgbarHeight );
                }

                /* Progess Bar Background Material */

                if((ConditionHints[i].HintStyle == 1 ||
                ConditionHints[i].HintStyle >= 3) &&
                ConditionHints[i].Material_ProgressBarBG != none)
                {
                    C.DrawColor.A = ((ConditionHints[i].ProgressBar_Clr.A * FadeValue) * (KFHUDAlpha/255.f)) * OpacityModifier ;
                    C.SetPos(PosX + (BGWidth-WidestX)/2, PosY );
                    C.DrawTileStretched(ConditionHints[i].Material_ProgressBarBG, BarBGWidth,ConditionHints[i].ProgBarheight );
		        }

                /* Progess Bar Fill Material */

                if((ConditionHints[i].HintStyle == 1 ||
                ConditionHints[i].HintStyle >= 3) &&
                ConditionHints[i].Material_ProgressBarFill != none)
                {
                    if(ConditionHints[i].DisplayStyle == 0)
                    {
                        BarWidth = WidestX * ConditionHints[i].ProgressPct ;
		            }
                    else
		            {
                        BarWidth = WidestX * (1.f-ConditionHints[i].ProgressPct) ;
                    }

                    if(Conditionhints[i].bShowCheckBox)
                    {
                        CheckBoxSize = ConditionHints[i].ProgBarheight * CheckboxScale;
                        BarWidth = FMax(BarWidth - CheckBoxSize,0.f);
                    }

                    C.DrawColor.A = ((ConditionHints[i].ProgressBar_Clr.A * FadeValue) * (KFHUDAlpha/255.f)) * OpacityModifier ;
                    C.SetPos(PosX + (BGWidth-WidestX)/2, PosY );
		            C.DrawTileStretched(ConditionHints[i].Material_ProgressBarFill, BarWidth,ConditionHints[i].ProgBarheight);

    		        /* Fill Highlight */

                    if(ConditionHints[i].DisplayStyle == 1)
                    {
                        SecondaryBarWidth = FMax(1.f-(WidestX * (ConditionHints[i].ProgressPct - ConditionHints[i].LastProgressVal)),0.f) ;
                        HighlightFadeTime = FClamp((ConditionHints[i].ProgressPct - ConditionHints[i].LastProgressVal) * 3.f,0.5f,2.f);
                        HighlightFadeVal = FClamp( 1.f - ((Level.TimeSeconds - ConditionHints[i].LastProgressUpdateTime ) / HighlightFadeTime), 0.f,1.f);

		                C.DrawColor.A    = (255 * (KFHudAlpha/255.f) * FadeValue * HighlightFadeVal) * OpacityModifier ;

                        C.SetPos(PosX + BarWidth - (SecondaryBarWidth), PosY);
		                C.DrawTileStretched(ConditionHints[i].Material_ProgressBarFill, SecondaryBarWidth,ConditionHints[i].ProgBarHeight);


                        C.DrawColor      = WhiteColor;
		                C.DrawColor.A    = (255 * (KFHudAlpha/255.f) * FadeValue * HighlightFadeVal) * OpacityModifier ;
                        HighlightWidth   = BarWidth * 0.1;
                        C.SetPos(PosX - (HighlightWidth) + (BGWidth-WidestX)/2 + BarWidth , PosY);
		                C.DrawTileStretched(Texture 'KFStoryGame_Tex.HUD.ProgressBar_Higlight', HighlightWidth,ConditionHints[i].ProgBarheight);

                        C.DrawColor      = ConditionHints[i].ProgressBar_Clr ;
		                C.DrawColor.A    = ((ConditionHints[i].ProgressBar_Clr.A * FadeValue) * (KFHUDAlpha/255.f)) * OpacityModifier ;
                    }
                }

                /* Finally, Draw the condition Hint strings & DataStrings.. */

                if(ConditionHints[i].HintStyle > 1 )
                {
                    /* Make sure the hint string isn't so large it overlaps the data string

                    while( ConditionHints[i].DataString != "" &&
                    PosX + (WidestX * 0.05) + ConditionHints[i].HintWidth >= (ClampedX-(DataX/2)) &&
                    FontSize < arraycount(FontArrayFonts)-1)
                    {
                        FontSize ++ ;
                        C.Font = LoadFont(FontSize);
                        C.StrLen(ConditionHints[i].DataString,SuccessWidth,SuccessHeight);
                        SuccessWidth  *= ScalingValue;
                        SuccessHeight *= ScalingValue;
                        ConditionHints[i].HintWidth = SuccessWidth;
                    }   */

                    if(ConditionHints[i].DataString != "")
                    {
                        C.Font = LoadFont(FontSize);
                        HintString = ConditionHints[i].DataString;
                        C.DrawColor = ConditionHints[i].ProgressBar_Clr;
                        C.DrawColor.A = ((ConditionHints[i].ProgressBar_Clr.A * FadeValue) * (KFHUDAlpha/255.f)) * OpacityModifier ;
			            C.SetPos(PosX + BGWidth/2 - (ConditionHints[i].HintWidth/2), PosY + ConditionHints[i].HintHeight/4 );
			            C.DrawText(HintString,true);
                    }

                    if(ConditionHints[i].bShowCheckBox)
                    {
                        C.SetPos(PosX + (BGWidth-WidestX)/2 - (ConditionHints[i].ProgBarheight * 1.1) , PosY );
                        C.DrawColor.A = ((ConditionHints[i].ProgressBar_Clr.A * FadeValue) * (KFHUDAlpha/255.f)) ;

                        /* Show a Tick next to success conditions that were completed , or failure conditions that were *not* completed
                        at the time the objective was successfully finished */

                        C.DrawTileScaled( Texture 'KFStoryGame_Tex.HUD.ObjInComplete_Ico', ConditionHints[i].ProgBarheight/64.f,ConditionHints[i].ProgBarHeight/64.f);

                        CheckBoxMat = GetCheckBoxMaterialFor(ConditionHints[i]);
                        if(CheckBoxMat != none)
                        {
                            C.SetPos(PosX + (BGWidth-WidestX)/2 - (ConditionHints[i].ProgBarheight * 1.1) , PosY - ((ConditionHints[i].ProgBarHeight*CheckBoxScale)/4) );
                            C.DrawTileScaled(CheckBoxMat, (ConditionHints[i].ProgBarheight*1.5)/64.f,(ConditionHints[i].ProgBarHeight*CheckboxScale)/64.f);
                        }
                    }
                }

                if(ConditionHints[i].HintStyle <= 3)
                {
                    PosY += ConditionHints[i].ProgBarheight * 1.25;
                }

                PosX = OldXPos;
            }
	    }
    }
}

simulated function Material  GetCheckBoxMaterialFor(SConditionHint  Testcondition)
{
    /* Failure conditions */
    if(TestCondition.ConditionType == 0)
    {
        if(Testcondition.bComplete == 1)
        {
            return Texture 'KFStoryGame_Tex.HUD.ObjFailed' ;
        }
        else
        {
            if(RenderObj.bCompleted)
            {
                return Texture 'KFStoryGame_Tex.HUD.ObjComplete_Ico';
            }
        }
    }
    else     // Success , optional
    {
        if(Testcondition.bComplete == 1)
        {
            return Texture 'KFStoryGame_Tex.HUD.ObjComplete_Ico' ;
        }
        else
        {
            if(RenderObj.bCompleted)
            {
                return Texture 'KFStoryGame_Tex.HUD.ObjFailed' ;
            }
        }
    }

    return none;
}

/* Dialogue rendering ====================================================================================
=========================================================================================================*/

simulated function ApplyDialogueStylePresets(KF_DialogueSpot NewDlg, int DlgIndex)
{
    if(HUDStylemanager != none && HUDStylemanager.StylePreset.Dialogue.bOverride)
    {
        NewDlg.Dialogues[DlgIndex].Display.Screen_BGMaterial    =  HUDStyleManager.StylePreset.Dialogue.Dialogue_Box.Screen_BGMaterial;
        NewDlg.Dialogues[DlgIndex].Display.Screen_Position      =  HUDStyleManager.StylePreset.Dialogue.Dialogue_Box.Screen_Position;
        NewDlg.Dialogues[DlgIndex].Display.Screen_Scaling       =  HUDStyleManager.StylePreset.Dialogue.Dialogue_Box.Screen_Scaling;
    }
}

/* adds a new dialogue entry to the cue -  Dialogue is rendered in order of addition */
simulated function AddDialogue(name DialogueActorName, int DlgIndex, float DisplayDuration)
{
    local int Index,ExistingIndex;
    local KF_DialogueSpot NewDlg;

    NewDlg = FindDialogueActor(DialogueActorName);
    if(NewDlg == none)
    {
        log("Warning - could not find Dialogue Actor of name : "@DialogueActorName@" Aborting HUD Render. ");
        return;
    }

    if(!FindExistingDialogue(NewDlg.Dialogues[DlgIndex].Display.Dialogue_text,ExistingIndex))
    {
        Index = Dialogues.length ;
        Dialogues.length = Index + 1;
        CurrentDlgIndex = Index;

        ApplyDialogueStylePresets(NewDlg,DlgIndex);

        Dialogues[Index].Message    = NewDlg.Dialogues[DlgIndex].Display.Dialogue_Text;
        Dialogues[Index].Speaker    = NewDlg.Dialogues[DlgIndex].Display.Dialogue_Header;
        Dialogues[Index].Portrait   = NewDlg.Dialogues[DlgIndex].Display.Portrait_Material;
        Dialogues[Index].Opacity    = KFHUDAlpha;
        Dialogues[Index].Duration   = DisplayDuration;
        Dialogues[Index].ScreenPos  = NewDlg.Dialogues[DlgIndex].Display.Screen_Position;
        Dialogues[Index].ScreenAlignment = NewDlg.Dialogues[DlgIndex].Display.ScreenAlignment;
        Dialogues[Index].ScreenScaleStyle = NewDlg.Dialogues[DlgIndex].Display.Screen_Scaling;
        Dialogues[Index].BackGroundMat = NewDlg.Dialogues[DlgIndex].Display.Screen_BGMaterial;

        Dialogues[Index].BinkPortrait                       = new class'LobbyMenuAd';
        Dialogues[Index].BinkPortrait.MenuMovie             = new class'Movie';
        Dialogues[Index].BinkPortrait.MenuMovie.Callbacks   = Dialogues[Index].BinkPortrait;
      	Dialogues[Index].BinkPortrait.MenuMovie.Open("../Movies/"$NewDlg.Dialogues[DlgIndex].Display.Portrait_BinkMovie$".bik");
        Dialogues[Index].BinkPortrait.MenuMovie.Play(true);
    }
    else
    {
        CurrentDlgIndex = ExistingIndex;
        Dialogues[ExistingIndex].Duration = DisplayDuration;
    }
}


/* Main drawing pump for Story mode Dialogue system */
simulated function DrawDialogue(Canvas C)
{
    local float HeaderX,HeaderY;
    local float BackgroundOffset;
    local int i;
    local AbsoluteCoordsInfo coords;
    local float DisplayTimeRemaining;
    local float XL,YL;
    local float OpacityVal;
    local float BGSizeX,BGSizeY;
    local float PortraitSizeX,PortraitSizeY;
    local float PortraitAspect;
    local Material DlgPortrait;
    local Material DlgBackGroundMaterial;

    if(CurrentDlgIndex >= Dialogues.length)
    {
        return;
    }

    DisplayTimeRemaining = Dialogues[CurrentDlgIndex].Duration - (Level.TimeSeconds - Dialogues[CurrentDlgIndex].FirstDisplayedTime) ;

    if(DisplayTimeRemaining <= 0)
    {
        Dialogues[CurrentDlgIndex].Opacity = Max(Dialogues[CurrentDlgIndex].Opacity - 2,0);
        if(Dialogues[CurrentDlgIndex].Opacity == 0)
        {
            // remove me from the cue and move on to the next!
            Dialogues.Remove(CurrentDlgIndex,1);
        }
    }

    if(CurrentDlgIndex >= Dialogues.length)
    {
        return;
    }

    OpacityVal = Dialogues[CurrentDlgIndex].Opacity ;
    if(OpacityVal > 0)
    {
        if(!Dialogues[CurrentDlgIndex].bFirstDisplay)
        {
            Dialogues[CurrentDlgIndex].bFirstDisplay = true;
            Dialogues[CurrentDlgIndex].FirstDisplayedTime = Level.TimeSeconds;
        }

        if(!Dialogues[CurrentDlgIndex].bWrapped)
        {
            CalculateDialogueWrappingData(C);
        }

        C.Font = GetFontSizeIndex(C,-2);
        C.StrLen(Dialogues[CurrentDlgIndex].Speaker,HeaderX,HeaderY);
		C.SetPos(0, 0);

		// Set proper rendering style
		C.Style = ERenderStyle.STY_Alpha;

		// Calculate background offset in relation to text
		backgroundOffset = DialogueBackground.PosY * C.ClipY;

		// Calculate absolute drawing coordinates (mostly for text widget)

		DialogueCoords.X = Dialogues[CurrentDlgIndex].ScreenPos.Horizontal;
		DialogueCoords.Y = Dialogues[CurrentDlgIndex].ScreenPos.Vertical;


		coords.PosX = DialogueCoords.X * C.ClipX;
		coords.PosY = DialogueCoords.Y * C.ClipY;
		coords.height = DialogueCoords.YL * C.ClipY;

		BGSizeY = coords.height + backgroundOffset * 2;

        DlgPortrait = Dialogues[CurrentDlgIndex].Portrait;
		if(DlgPortrait != none)
		{
		    PortraitAspect = float(DlgPortrait.MaterialUSize()) /  float(DlgPortrait.MaterialVsize())   ;
            // make sure the protrait is scaled to the background widget
            PortraitSizeY = FMin(DlgPortrait.MaterialVsize(),BGSizeY);
            PortraitSizeX = PortraitSizeY * PortraitAspect;
		}

		coords.width = (DialogueCoords.XL * C.ClipX)  ;

		// Draw the background
        C.DrawColor = WhiteColor;
        C.DrawColor.A = OpacityVal * (255.f/KFHUDAlpha) ;

		BGSizeX = (coords.width + backgroundOffset * 2) ;

        coords.PosX -= PortraitSizeX/2;
        coords.PosY -= BGSizeY/2;

		/* Clamp drawing positions so the dialogue never goes offscreen */

		coords.PosX = FClamp(coords.PosX,FMax(coords.PosX,PortraitSizeX),C.ClipX - BGSizeX);
		coords.PosY = FClamp(coords.PosY,0,C.ClipY - BGSizeY);

		C.SetPos(coords.PosX , coords.PosY );

        DlgBackGroundMaterial = Dialogues[CurrentDlgIndex].BackGroundMat;
        if(DlgBackGroundMaterial == none)
        {
            DlgBackGroundMaterial = Texture'KillingFloorHUD.HUD.Hud_Box_128x64';
        }

        if(Dialogues[CurrentDlgIndex].ScreenScaleStyle == Stretched)
        {
            C.DrawTileStretched(DlgBackGroundMaterial, BGSizeX,BGSizeY);
        }
        else
        {
            C.DrawTileScaled(DlgBackGroundMaterial, BGSizeX/DlgBackGroundMaterial.MaterialUSize(),BGSizeY/DlgBackGroundMaterial.MaterialVSize());
        }

		// Draw title
		C.Font = GetFontSizeIndex(C,-2);
		DialogueTitleWidget.text = Dialogues[CurrentDlgIndex].Speaker ;
		DialogueTitleWidget.Tints[0].A = OpacityVal;
		DialogueTitleWidget.Tints[1].A = OpacityVal;
		DrawTextWidgetClipped(C, DialogueTitleWidget, coords, XL, YL);

		// Draw each line individually
		DialogueTextWidget.OffsetY = (YL * 1.5) + DialogueTitleWidget.OffsetY ;
//		C.Font = getSmallMenuFont(C);
		YL = 0;
		for (i = 0; i < WrappedDialogue.Length; i++)
		{
			DialogueTextWidget.text = WrappedDialogue[i];
		    DialogueTextWidget.Tints[0].A = OpacityVal;
		    DialogueTextWidget.Tints[1].A = OpacityVal;

			if (WrappedDialogue[i] != "")
				DrawTextWidgetClipped(C, DialogueTextWidget, coords, XL, YL);
			else
				YL /= 2;
			DialogueTextWidget.OffsetY += YL;
		}

	    //Draw Portraits . (Bink if available, or else just regular material)

        if(Dialogues[CurrentDlgIndex].BinkPortrait != none &&
        Dialogues[CurrentDlgIndex].BinkPortrait.MenuMovie != none &&
        Dialogues[CurrentDlgIndex].BinkPortrait.MenuMovie.IsPlaying())
        {
		    C.Style = ERenderStyle.STY_Normal;
            C.DrawColor = WhiteColor;
            C.DrawColor.A = OpacityVal * (255.f/KFHUDAlpha) ;
            C.SetPos(coords.PosX - PortraitSizeX, coords.PosY );
            C.DrawTileScaled(Dialogues[CurrentDlgIndex].BinkPortrait.MenuMovie, PortraitSizeX/Dialogues[CurrentDlgIndex].BinkPortrait.MenuMovie.GetWidth(),PortraitSizeY/Dialogues[CurrentDlgIndex].BinkPortrait.MenuMovie.GetHeight());
        }
        else
        {
            DlgPortrait = Dialogues[CurrentDlgIndex].Portrait;
            if(DlgPortrait != none)
            {
		        C.Style = ERenderStyle.STY_Alpha;
                C.DrawColor = WhiteColor;
                C.DrawColor.A = OpacityVal * (255.f/KFHUDAlpha) ;
                C.SetPos(coords.PosX - PortraitSizeX, coords.PosY );
                C.DrawTileScaled(DlgPortrait,PortraitSizeX / DlgPortrait.MaterialUSize(), PortraitSizeY / DlgPortrait.MaterialVSize());
            }
        }
    }
}


simulated function bool FindExistingDialogue(string text, optional out int Index)
{
    local int i;

    for(i = 0 ; i < Dialogues.length ; i ++)
    {
        if(Dialogues[i].Message == text)
        {
            Index = i;
            return true;
        }
    }

    return false;
}

// This function is used to calculate how the Dialogue data should
// be wrapped.
function CalculateDialogueWrappingData(Canvas Canvas)
{
	local float XL, YL, XL2, YL2;
	local float minWidth, wrapWidth, totalYL;
	local int i, count;

	// First calculate minimum message width (e.g. the width of the
	// title string)
	Canvas.Font = GetFontSizeIndex(Canvas,-2);
	Canvas.SetPos(0, 0);
	Canvas.TextSize(Dialogues[CurrentDlgIndex].Speaker, minWidth, totalYL);
	if (minWidth < 10.0)
		minWidth = 10.0;
	totalYL += totalYL / 2;

	// Calculate max width of text string (or perhaps we should just use full screen width?)
	Canvas.Font = getSmallMenuFont(Canvas);
	Canvas.TextSize(Dialogues[CurrentDlgIndex].Message, XL, YL);

	// Starting with full string width, progressively reduce width until the ratio of the height
	// to the width is smaller than HintDesiredAspectRatio
	wrapWidth = XL;
	for (count = 0; count < 25; count++) // max 25 iterations
	{
		// Wrap text
		WrappedDialogue.Length = 0;
		Canvas.WrapStringToArray(Dialogues[CurrentDlgIndex].Message, WrappedDialogue, wrapWidth, "|");

		// Calculate current width & height
		XL = 0; YL = 0;
		XL2 = 0; YL2 = 0;
		for (i = 0; i < WrappedDialogue.Length; i++)
		{
			if (WrappedDialogue[i] != "")
				Canvas.TextSize(WrappedDialogue[i], XL, YL);
			else
				YL /= 2;
			if (XL > XL2)
				XL2 = XL;
			YL2 += YL;
		}

		// Check if current width is too small
		if (XL2 < minWidth)
		{
			wrapWidth = minWidth;
			break;
		}

		// Calculate ratio
		if (YL2 < 1)
			YL = 1;
		else
			YL = XL2 / YL2;

		// Check if we should accept this wrap width
		if (YL < HintDesiredAspectRatio)
		{
			wrapWidth = XL2;
			break;
		}

		// Else, reduce currentWidth and try again.
		wrapWidth *= 0.80;
	}

	// Wrap text to array
	WrappedDialogue.Length = 0;
	Canvas.SetPos(0, 0);
	Canvas.WrapStringToArray(Dialogues[CurrentDlgIndex].Message, WrappedDialogue, wrapWidth, "|");

	// Calculate total width and height
	wrapWidth = minWidth;
	XL = 0; YL = 0;
	for (i = 0; i < WrappedDialogue.Length; i++)
	{
		Canvas.SetPos(0, 0);
		if (WrappedDialogue[i] != "")
			Canvas.TextSize(WrappedDialogue[i], XL, YL);
		else
			YL /= 2;
		if (XL > wrapWidth)
			wrapWidth = XL;
		totalYL += YL;
		//log("Wrapped line #" $ i $ ": '" $ WrappedDialogue[i] $"'");
	}

	// for safety
	if (wrapWidth < 10)
		wrapWidth = 10;
	if (totalYL < 10)
		totalYL = 10;

	// Calculate target relative coordinates
	DialogueCoords.XL = wrapWidth / Canvas.ClipX;
	DialogueCoords.YL = totalYL / Canvas.ClipY;
	DialogueCoords.X = default.DialogueCoords.X + DialogueCoords.XL * default.DialogueCoords.XL;
	DialogueCoords.Y = default.DialogueCoords.Y + DialogueCoords.YL * default.DialogueCoords.YL;

	Dialogues[CurrentDlgIndex].bWrapped = true;
}



/* Modified to allow story HUDs to render different end game screens */

simulated function DrawEndGameHUD(Canvas C, bool bVictory)
{
	local float Scalar;
	local Material VictoryMaterial,DefeatMaterial;

	C.DrawColor.A = 255;
	C.DrawColor.R = 255;
	C.DrawColor.G = 255;
	C.DrawColor.B = 255;
	Scalar = FClamp(C.ClipY, 320, 1024);
	C.CurX = C.ClipX / 2 - Scalar / 2;
	C.CurY = C.ClipY / 2 - Scalar / 2;
	C.Style = ERenderStyle.STY_Alpha;

	Victorymaterial = SGRI.GetVictorySplashMaterial();
	DefeatMaterial  = SGRI.GetDefeatSplashMaterial();

	if ( bVictory )
	{
		MyColorMod.Material = VictoryMaterial;
	}
	else
	{
		MyColorMod.Material = DefeatMaterial;
	}

	if ( EndGameHUDTime >= 1 )
	{
		MyColorMod.Color.A = 255;
	}
	else
	{
		MyColorMod.Color.A = EndGameHUDTime * 255.f;
	}

	C.DrawTile(MyColorMod, Scalar, Scalar, 0, 0, 1024, 1024);

	if ( bShowScoreBoard && ScoreBoard != None )
	{
		ScoreBoard.DrawScoreboard(C);
	}
}

simulated function Message(PlayerReplicationInfo PRI, coerce string Msg, name MsgType)
{
	local Class<LocalMessage> LocalMessageClass;

	if(MsgType == 'Msg_CashReward')
	{
	    LocalMessageClass = class'Msg_CashReward';
	    AddTextMessage(Msg, LocalMessageClass, PRI);
	}
	else
	{
        Super.Message(PRI,Msg,MsgType);
	}
}


defaultproperties
{
	DialogueCoords=(X=0.5,Y=0.6,XL=-1.0,YL=0)
   	DialogueBackground=(WidgetTexture=Texture'KF_InterfaceArt_tex.Menu.Med_border_SlightTransparent',PosX=0,PosY=0.02,TextureCoords=(X1=0,Y1=0,X2=0,Y2=0),TextureScale=0.0,DrawPivot=DP_MiddleMiddle,OffsetX=0,OffsetY=0,ScaleMode=SM_Left,Scale=1.0,RenderStyle=STY_Alpha,Tints[0]=(R=255,G=255,B=255,A=192),Tints[1]=(R=255,G=255,B=255,A=192))
   	DialogueTitleWidget=(Text="",PosX=0,PosY=0,WrapWidth=0,WrapHeight=1,OffsetX=15,OffsetY=15,DrawPivot=DP_UpperLeft,RenderStyle=STY_Alpha,Tints[0]=(R=255,G=255,B=255,A=255),Tints[1]=(R=255,G=255,B=255,A=255),bDrawShadow=true)
   	DialogueTextWidget=(Text="",PosX=0,PosY=0,WrapWidth=0,WrapHeight=1,OffsetX=15,OffsetY=45,DrawPivot=DP_UpperLeft,RenderStyle=STY_Alpha,Tints[0]=(R=255,G=255,B=255,A=255),Tints[1]=(R=255,G=255,B=255,A=255),bDrawShadow=false)

    ObjFadeOutTime = 0.5f
	ObjFadeInTime = 0.5f
	ObjFadeOutDelay = 1.f

	StoryIconOffsetX=20;
	StoryIconOffsetY=50;

	bShowObjectives = true
}
