/*
	--------------------------------------------------------------
	KF_DialogueSpot
	--------------------------------------------------------------

    Displays dialogue on the screen with a header and image.
    Optionally plays voicover audio.

	Author :  Alex Quick

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


class KF_DialogueSpot	extends Info
dependson(KFStoryGameInfo)
placeable;

var				int                                             CurrentMsgIdx;

var				Controller                                      DialogueInstigator;

var()			array<KFStoryGameInfo.SDialogueEntry>           Dialogues;

/* true if this dialogue spot should display its text when a player touches the dialogue actor's collision cylinder */
var			    bool						                    bTouchTriggered;


/* true if this dialogue can be 'skipped'  by moving out of range of the speaker*/
var			    bool						                    bCanSkipDlg;

/* the range at which dialogue will be skipped if bCanSkipDlg*/
var 			float						                    SkipDlgRange;

/* if true, the dialogue will play randomly each time it is triggered rather than in order */
var()			bool						                    bRandomize;

// if true, this dialogue can be played multiple times.  If not, only allow one play of each dialogue
var()           bool                                            bAllowRepeatDialogue;

var				bool						                    bDebugDialogue;

var				bool						                    bTraversing;

var				bool						                    bFinished;

var             bool                                            bSingleTouchOnly;

var             bool                                            bTouched;


function Reset()
{
	local int i, SavedIdx;
	local array<byte>	SavedTriggerStates;

	Super.Reset();

	/* Partial reset using saved positions*/

	bTraversing = false;

	if(!bFinished)
	{
		SavedIdx = KFStoryGameInfo(Level.Game).CurrentCheckPoint.GetSavedDialogueIndexFor(self,SavedTriggerStates) ;
		CurrentMsgIdx =  SavedIdx;

		if(bDebugDialogue)
		{
			log("----- DIALOGUE DEBUG ------   Resetting "@self@". Dialogue will play back from Index : "@SavedIdx, 'Story_Debug');
		}

		for(i = 0 ; i < Dialogues.length ; i ++)
		{
			Dialogues[i].bWasTriggered = bool(SavedTriggerStates[i]);
		}
	}
	else	/* Full reset from the start. */
	{
 		for(i = 0 ; i < Dialogues.length ; i ++)
		{
			Dialogues[i].bWasTriggered = false;
		}

		CurrentMsgIdx = 0;
		bFinished = false;
	}
}

function Destroyed()
{
	local int i;

	/* Perform cleanup on any event listeners */
	for(i = 0 ; i < Dialogues.length ; i ++)
	{
		if(Dialogues[i].EventListener != none)
		{
			Dialogues[i].EventListener.Destroy();
		}
	}
}

/* Dialogue callback - timing is handled in the event listener */
function OnDialogueDisplayComplete(int Index)
{
//    log("Dialogue - Triggering Displayed Event : "@Dialogues[Index].Events.DisplayedEvent);
    if(Dialogues[Index].Events.DisplayedEvent != '')
    {
	   TriggerEvent(Dialogues[Index].Events.DisplayedEvent,self,DialogueInstigator.Pawn);
    }

    Dialogues[Index].bWasTriggered = false;
}

function PostBeginPlay()
{
	local int i;
	local Dialogue_EventListener		NewListener;

    /* No KFO Gametype, no Initialization */
	if(KFStoryGameinfo(Level.Game) == none)
	{
        return;
	}

	// Spawn Event listeners for Dialogue entries which require them.
	for(i = 0 ; i < Dialogues.length ; i ++)
	{
		NewListener = Spawn(class 'Dialogue_EventListener',self,Dialogues[i].Events.RequiredEvent);
		NewListener.AssociatedIndex = i ;
		Dialogues[i].EventListener = NewListener;
	}
}

function Trigger( actor Other, pawn EventInstigator )
{
	if(EventInstigator != none &&
	EventInstigator.Controller != none)
	{
		DialogueInstigator = EventInstigator.Controller;
	}

	TraverseDialogue();
}

function bool SelfTriggered()
{
	return bTouchTriggered;
}

function Touch( Actor Other )
{
	if ( Pawn(Other) != None &&
    PlayerController(Pawn(Other).Controller) != none &&
     SelfTriggered() &&
     (!bSingleTouchOnly || !bTouched))
	{
		bTouched = true;
		Trigger(self,Pawn(Other));
	}
}

function 	int		GetIndexFor(KFstoryGameInfo.SDialogueEntry  TestDlg)
{
	local int i;

	for(i = 0 ; i < Dialogues.length ; i ++)
	{
		if(Dialogues[i] == TestDlg)
		{
			return i;
		}
	}

	return - 1 ;
}


/* 	Retrieves the index of the next dialogue entry in a DialogueSpot actor with a 'required event'.
	Necessary in cases where the game is restarting from an objective in a non-linear fashion and
	certain intermediate dialogue needs to be skipped
*/

function		int			GetNextDlgRequiredEventIdx(int 	TestIndex)
{
	local int i;

	for( i = TestIndex + 1 ; i < Dialogues.length ; i ++)
	{
		if(Dialogues[i].Events.RequiredEvent != '')
		{
			return i ;
		}
	}

	return -1;
}


function TraverseDialogue()
{
	local bool bWaitingForEvent ;

	/* Hit the end. early out */
	if( Dialogues.length <= CurrentMsgIdx  || bTraversing || (bFinished && !bAllowRepeatDialogue))
	{
		return;
	}

	if(bRandomize)
	{
		CurrentMsgIdx = RandRange(0,Dialogues.length) ;
	}

	/* If the next index after this one is event-triggered,  don't set the timer */
	if(Dialogues[CurrentMsgIdx].Events.RequiredEvent != '' &&
	!Dialogues[CurrentMsgIdx].bLooping)
	{
		bWaitingForEvent = !Dialogues[CurrentMsgIdx].bWasTriggered;
	}

	if(!bWaitingForEvent)
	{
		if(bDebugDialogue)
		{
			log("----- DIALOGUE DEBUG ------  Showing Dialogue at index .. "@CurrentMsgIdx, 'Story_Debug');
		}

		ShowDialogue(CurrentMsgIdx);
		if(CurrentMsgIdx + 1 < Dialogues.length )
		{
			bTraversing = true;

			if(GetCurrentDisplayDur() > 0)
			{
                SetTimer(GetCurrentDisplayDur(),false);
            }
            else
            {
                Timer();
            }
        }
		else
		{
			/* we're finished. Reset the actor */
			OnDialogueCompleted();
		}
	}
	else
	{
		if(bDebugDialogue)
		{
			log("----- DIALOGUE DEBUG ------  Freezing dialogue at index :"@CurrentMsgIdx@"for event : "@Dialogues[CurrentMsgIdx].Events.RequiredEvent, 'Story_Debug');
		}
	}
}

function OnDialogueCompleted()
{
	bFinished = true;
}

function Timer()
{
	if(bTraversing)
	{
		bTraversing = false;

		if(CurrentMsgIdx + 1 < Dialogues.length && !bRandomize)
		{
			if(!Dialogues[CurrentMsgIdx].bLooping)
			{
				CurrentMsgIdx ++ ;
				if(bDebugDialogue)
				{
					log("----- DIALOGUE DEBUG ------  Incrementing Dialogue Index .. ", 'Story_Debug');
				}

				TraverseDialogue();
			}
		}
	}
}

/* Helper function - calculates the amount of time a string should
be displayed for based on its word length  */

function float CalcDisplayTime(string InString)
{
    local float TimePerWord;
    local array<string> Words;

    TimePerWord = 0.5f;
    Split(InString," ",Words);

    return FMax(Words.length * TimePerWord,3.f);
}

function ShowDialogue(int DlgIndex)
{
	local Controller C;
	local KFPlayerController_Story 	StoryPC;
	local string SpeakerName;
	local actor VoiceOverSource;
	local Material Portrait;
    local Pawn MyInstigator;

	/* nothing to show, nothing to play ... early out */
	if(Dialogues[DlgIndex].Display.Dialogue_Text == "" &&
	Dialogues[DlgIndex].VoiceOver.VoiceOverSound == none)
	{
		return;
	}

	for (C = Level.ControllerList; C != None; C = C.NextController)
	{
		StoryPC = KFPlayerController_Story(C);
		if(StoryPC != none &&
		(Dialogues[DlgIndex].BroadcastScope != InstigatorOnly ||
        (DialogueInstigator != none && DialogueInstigator == C) ))
		{
			SpeakerName  = Dialogues[DlgIndex].Display.Dialogue_Header ;
			Portrait     = Dialogues[DlgIndex].Display.Portrait_Material ;

			if(SpeakerName == "self")
			{
				SpeakerName = StoryPC.PlayerReplicationinfo.PlayerName ;
				Portrait    = xPlayerReplicationInfo(StoryPC.PlayerReplicationInfo).Rec.Portrait;
			}

            if(GetCurrentDisplayDur() > 0)
            {
                Dialogues[DlgIndex].EventListener.SetTimer(GetCurrentDisplayDur(),false);
            }
            else
            {
                OnDialogueDisplayComplete(DlgIndex);
            }

            if(!SkipDialogue())
            {
                StoryPC.ClientShowStoryDialogue(name ,DlgIndex,GetCurrentDisplayDur());
            }

			if ( !SkipDialogue() && Dialogues[DlgIndex].VoiceOver.VoiceOverSound != None )
			{
				/* attempt to play a spatialised sound from the source actor (if there is one) */
			    VoiceOverSource = Dialogues[DlgIndex].VoiceOver.SourceActor ;
				if(VoiceOverSource != none)
				{
					VoiceOverSource.PlaySound(Dialogues[DlgIndex].VoiceOver.VoiceOverSound,SLOT_Talk,VoiceOverSource.SoundVolume,,VoiceOverSource.SoundRadius,VoiceOverSource.SoundPitch,VoiceOverSource.bFullVolume);
				}
				else	/*otherwise just play a ClientSound */
				{
					StoryPC.ClientPlayStorySound(Dialogues[DlgIndex].VoiceOver.VoiceOverSound, 2.0, false );
				}
			}

            if(Dialogues[DlgIndex].Events.DisplayingEvent != '')
            {
                MyInstigator = DialogueInstigator.Pawn;
                TriggerEvent(Dialogues[DlgIndex].Events.DisplayingEvent,self,MyInstigator);
            }
        }
	}
}

function bool SkipDialogue()
{
    return KFStoryGameInfo(Level.Game) != none && KFStoryGameInfo(Level.Game).bSkipDialogue;
}

function float GetCurrentDisplayDur()
{
    local float Duration,VODuration;

    if( SkipDialogue())
    {
        return 0.f;
    }

    Duration = CalcDisplayTime(Dialogues[CurrentMsgIdx].Display.Dialogue_Text);

    /* Always make sure the text sticks around for at least as long as the VO */
    if(Dialogues[CurrentMsgIdx].VoiceOver.VoiceOverSound != none)
    {
        VODuration = Dialogues[CurrentMsgIdx].VoiceOver.VoiceOverSound.Duration;
        Duration   = VODuration * 1.15; // Gamespeed is 1.1 so you always have to multiplay sound durations by 1.1. Adding a bit more buffer just in case - Ramm
    }

    return Duration;
}

function GetRequiredEvents( out array<Name> AllRequiredEvents)
{
    local int i;

    for(i = 0 ; i < Dialogues.length ; i ++)
    {
        if(Dialogues[i].Events.RequiredEvent != '')
        {
            AllRequiredEvents[AllRequiredEvents.length] = Dialogues[i].Events.RequiredEvent;
        }
    }
}



function GetDisplayEvents( out array<Name> AllDisplayEvents)
{
    local int i;

    for(i = 0 ; i < Dialogues.length ; i ++)
    {
        if(Dialogues[i].Events.DisplayedEvent != '')
        {
            AllDisplayEvents[AllDisplayEvents.length] = Dialogues[i].Events.DisplayedEvent;
        }

        if(Dialogues[i].Events.DisplayingEvent != '')
        {
            AllDisplayEvents[AllDisplayEvents.length] = Dialogues[i].Events.DisplayingEvent;
        }
    }
}

function GetEvents(out array<name> TriggeredEvents,  out array<name>  ReceivedEvents)
{
    local int i;

    Super.GetEvents(TriggeredEvents,ReceivedEvents);

    for(i = 0 ; i < Dialogues.length ; i ++)
    {
        /* Things I trigger */
        if(Dialogues[i].Events.DisplayedEvent != '')
        {
            TriggeredEvents[TriggeredEvents.length] = Dialogues[i].Events.DisplayedEvent;
        }
        if(Dialogues[i].Events.DisplayingEvent != '')
        {
            TriggeredEvents[TriggeredEvents.length] = Dialogues[i].Events.DisplayingEvent;
        }
        /* Things that trigger me*/
        if(Dialogues[i].Events.RequiredEvent != '')
        {
            ReceivedEvents[ReceivedEvents.length] = Dialogues[i].Events.RequiredEvent;
        }
    }
}

defaultproperties
{
    DrawScale = 0.5

    bDebugDialogue = true
    bNoDelete = true
    bCollideActors = true
	SkipDlgRange = 250

	Texture = Texture 'KFStoryGame_Tex.Editor.KF_Dlgspot_Ico'
}

