Beispiel #1
0
void Screen::DoContext(ContextMenu *pNewCtx, Element *pAtElement, int32_t iX, int32_t iY)
	{
	assert(pNewCtx); assert(pNewCtx != pContext);
	// close previous context menu
	AbortContext(false);
	// element offset
	if (pAtElement) pAtElement->ClientPos2ScreenPos(iX, iY);
	// usually open bottom right
	// check bottom bounds
	if (iY + pNewCtx->GetBounds().Hgt >= GetBounds().Hgt)
		{
		// bottom too narrow: open to top, if height is sufficient
		// otherwise, open to top from bottom screen pos
		if (iY < pNewCtx->GetBounds().Hgt) iY = GetBounds().Hgt;
		iY -= pNewCtx->GetBounds().Hgt;
		}
	// check right bounds likewise
	if (iX + pNewCtx->GetBounds().Wdt >= GetBounds().Wdt)
		{
		// bottom too narrow: open to top, if height is sufficient
		// otherwise, open to top from bottom screen pos
		if (iX < pNewCtx->GetBounds().Wdt) iX = GetBounds().Wdt;
		iX -= pNewCtx->GetBounds().Wdt;
		}
	// open new
	(pContext = pNewCtx)->Open(pAtElement, iX, iY);
	}
Beispiel #2
0
Screen::~Screen()
	{
	// dtor: Close context menu
	AbortContext(false);
	// clear singleton
	if (this == pScreen) pScreen = NULL;
	// GamePad
	if (pGamePadOpener) delete pGamePadOpener;
	}
// IEntityEventListener
void CDialogActorContext::OnEntityEvent( IEntity *pEntity,SEntityEvent &event )
{
	switch (event.event)
	{
		case ENTITY_EVENT_RESET:
		case ENTITY_EVENT_DONE:
			AbortContext(true, CDialogSession::eAR_EntityDestroyed);
			break;
		default:
			break;
	}
}
Beispiel #4
0
void Screen::ActivateDialog(Dialog *pDlg)
	{
	// no change?
	if (pActiveDlg == pDlg) return;
	// in single-mode: release any MouseOver/Drag of previous dlg
	if (IsExclusive())
		Mouse.ReleaseElements();
	// close any context menu
	AbortContext(false);
	// set as active dlg
	pActiveDlg = pDlg;
	// ensure it's last in the list, if it's not a specially ordered dlg
	if (!pDlg->GetZOrdering() && pDlg->GetNext())
		MakeLastElement(pDlg);
	}
Beispiel #5
0
void Screen::CloseDialog(Dialog *pDlg, bool fFade)
	{
	// hide dlg
	if (!fFade) pDlg->fShow = false;
	// kill from active
	if (pActiveDlg == pDlg)
		{
		// release any MouseOver/Drag of previous dlg
		Mouse.ReleaseElements();
		// close context menu: probably belonging to closed dlg anyway
		AbortContext(false);
		// set new active dlg
		pActiveDlg = GetTopDialog();
		// do not set yet if it's fading
		if (pActiveDlg && pActiveDlg->IsFading()) pActiveDlg = NULL;
		}
	// redraw background; clip update
	Game.GraphicsSystem.InvalidateBg(); UpdateMouseFocus();
	}
bool CDialogActorContext::Update(float dt)
{
	if (IsAborted())
		return true;

	// FIXME: this should never happen, as we should get a notification before.
	//        temp leave in for tracking down a bug [AlexL: 23/11/2006]
	IEntity* pEntity = gEnv->pEntitySystem->GetEntity(m_entityID);
	if (pEntity == 0)
	{
		m_pIActor = 0;
		GameWarning("[DIALOG] CDialogActorContext::Update: %s Actor=%d (EntityId=%d) no longer existent.",
			m_pSession->GetDebugName(), m_actorID, m_entityID);
		AbortContext(true, CDialogSession::eAR_EntityDestroyed);
		return true;
	}

	CDialogSession::AlertnessInterruptMode alertnessInterruptMode = m_pSession->GetAlertnessInterruptMode();
	if(alertnessInterruptMode != CDialogSession::None)
	{
		if(IAIObject* aiObject = pEntity->GetAI())
		{
			if(IAIActorProxy* aiActorProxy = aiObject->GetProxy())
			{
				if(aiActorProxy->GetAlertnessState() >= alertnessInterruptMode)
				{
					AbortContext(true, CDialogSession::eAR_AIAborted);
					return true;
				}
			}
		}
	}

	float now = m_pSession->GetCurTime();

	if (!CheckActorFlags(CDialogSession::eDACF_NoActorDeadAbort) && m_pIActor && m_pIActor->IsDead())
	{
		if (!IsAborted())
		{
			DiaLOG::Log(DiaLOG::eAlways, "[DIALOG] CDialogActorContext::Update: %s Actor=%d (EntityId=%d) has died. Aborting.",
				m_pSession->GetDebugName(), m_actorID, m_entityID);
			AbortContext(true, CDialogSession::eAR_ActorDead);
			return true;
		}
	}

	if (m_bIsLocalPlayer)
	{
		// when the local player is involved in the conversation do some special checks
		if (DoLocalPlayerChecks(dt) == false)
		{
			DiaLOG::Log(DiaLOG::eAlways, "[DIALOG] CDialogActorContext::Update: %s Abort from LocalPlayer.", m_pSession->GetDebugName());
			AbortContext(true, m_bIsAwareInRange == false ? CDialogSession::eAR_PlayerOutOfRange : CDialogSession::eAR_PlayerOutOfView);
			return true;
		}
	}

	if (m_bAbortFromAI)
	{
		m_bAbortFromAI = false;
		if (!IsAborted())
		{
			DiaLOG::Log(DiaLOG::eAlways, "[DIALOG] CDialogActorContext::Update: %s Abort from AI.", m_pSession->GetDebugName());
			AbortContext(true, CDialogSession::eAR_AIAborted);
			return true;
		}
	}

	if (SessionAllowsLookAt())
	{
		DoStickyLookAt();
	}

	int loop = 0;
	do 
	{
		// DiaLOG::Log("[DIALOG] DiaLOG::eAlways"CDialogActorContext::Update: %s now=%f actorId=%d loop=%d", m_pSession->GetDebugName(), now, m_actorID, loop);

		bool bAdvance = false;
		switch (m_phase)
		{
		case eDAC_Idle:
			break;

		case eDAC_NewLine:
			{
				m_bHasScheduled = false;
				m_lookAtTimeOut = LOOKAT_TIMEOUT;
				m_animTimeOut   = ANIM_TIMEOUT;
				m_soundTimeOut  = SOUND_TIMEOUT; // wait one second until sound is timed out
				m_bAnimScheduled = false;
				m_bAnimStarted   = false;
				m_bSoundScheduled = false;
				m_bSoundStarted   = false;
				m_soundLength   = 0.0f;

				if (m_pCurLine->m_flagResetLookAt)
				{
					m_stickyLookAtActorID = CDialogScript::NO_ACTOR_ID;
					m_lookAtActorID = CDialogScript::NO_ACTOR_ID;
				}
				// check if look-at sticky is set
				else if (m_pCurLine->m_lookatActor != CDialogScript::NO_ACTOR_ID)
				{
					if (m_pCurLine->m_flagLookAtSticky == false)
					{
						m_lookAtActorID = m_pCurLine->m_lookatActor;
						m_stickyLookAtActorID = CDialogScript::NO_ACTOR_ID;
					}
					else
					{
						m_stickyLookAtActorID = m_pCurLine->m_lookatActor;
						m_lookAtActorID = CDialogScript::NO_ACTOR_ID;
					}
				}

				bAdvance = true;

				// handle the first sticky look-at here
				if (SessionAllowsLookAt())
				{
					DoStickyLookAt();
				}









			}
			break;

		case eDAC_LookAt:
			{
				bool bWaitForLookAtFinish = true;
				bAdvance = true;
	
				if (SessionAllowsLookAt() == false)
				{
					break;
				}

				// Question: maybe do this although EP is requested
				if (bWaitForLookAtFinish && m_lookAtActorID != CDialogScript::NO_ACTOR_ID && m_pCurLine->m_flagAGEP == false) 
				{
					IEntity* pActorEntity = m_pSession->GetActorEntity(m_actorID);
					IEntity* pLookAtEntity = m_pSession->GetActorEntity(m_lookAtActorID);
					if (pActorEntity != 0)
					{
						m_lookAtTimeOut -= dt;
						bool bTargetReached;
						//bool bSuccess = DoLookAt(pActorEntity, pLookAtEntity, bTargetReached);
						DoLookAt(pActorEntity, pLookAtEntity, bTargetReached);
						//DiaLOG::Log(DiaLOG::eDebugA, "[DIALOG] CDialogActorContext::Update: %s now=%f actorID=%d phase=eDAC_LookAt %d",
						//	m_pSession->GetDebugName(), now, m_actorID, bTargetReached);

						if (/* bSuccess == false || */ bTargetReached || m_lookAtTimeOut<=0.0f)
						{
							DiaLOG::Log(DiaLOG::eAlways, "[DIALOG] CDialogActorContext::Update: %s now=%f actorID=%d phase=eDAC_LookAt %s",
								m_pSession->GetDebugName(), now, m_actorID, bTargetReached ? "Target Reached" : "Timed Out");
							m_lookAtActorID = CDialogScript::NO_ACTOR_ID;
						}
						else
							bAdvance = false;
					}
				}
			}
			break;

		case eDAC_Anim:
			{
				bAdvance = true;
				if (SessionAllowsLookAt() && m_lookAtActorID != CDialogScript::NO_ACTOR_ID) // maybe: don't do this when EP is requested [&& m_pCurLine->m_flagAGEP == false]
				{
					IEntity* pActorEntity = m_pSession->GetActorEntity(m_actorID);
					IEntity* pLookAtEntity = m_pSession->GetActorEntity(m_lookAtActorID);
					if (pActorEntity != 0)
					{
						m_lookAtTimeOut -= dt;
						bool bTargetReached;
						//bool bSuccess = DoLookAt(pActorEntity, pLookAtEntity, bTargetReached);
						DoLookAt(pActorEntity, pLookAtEntity, bTargetReached);
						if (/* bSuccess == false || */ bTargetReached || m_lookAtTimeOut<=0.0f)
						{
							DiaLOG::Log(DiaLOG::eAlways, "[DIALOG] CDialogActorContext::Update: %s now=%f actorID=%d phase=eDAC_Anim %s",
								m_pSession->GetDebugName(), now, m_actorID, bTargetReached ? "Target Reached" : "Timed Out");
							m_lookAtActorID = CDialogScript::NO_ACTOR_ID;
						}
						else
							bAdvance = false;
					}
				}
				const bool bHasAnim = !m_pCurLine->m_anim.empty();
				if (SessionAllowsAnim() && bHasAnim)
				{
					bAdvance = false;
					if (!m_bAnimScheduled)
					{
						m_bSoundStopsAnim = false; // whenever there is a new animation, no need for the still playing sound
						                           // to stop the animation
						m_bAnimStarted = false;
						m_bAnimScheduled = true;
						// schedule animation
						IEntity* pActorEntity = m_pSession->GetActorEntity(m_actorID);
						if (pActorEntity)
							DoAnimAction(pActorEntity, m_pCurLine->m_anim, m_pCurLine->m_flagAGSignal, m_pCurLine->m_flagAGEP);
						else
							bAdvance = true;
					}
					else
					{
						// we scheduled it already
						// wait until it starts or timeout
						m_animTimeOut-=dt;
						bAdvance = m_animTimeOut <= 0.0f || m_bAnimStarted;
						if (bAdvance)
						{
							DiaLOG::Log(DiaLOG::eAlways, "[DIALOG] CDialogActorContext::Update: %s Now=%f actorID=%d phase=eDAC_Anim %s",
								m_pSession->GetDebugName(), now, m_actorID, m_bAnimStarted ? "Anim Started" : "Anim Timed Out");
						}
					}
				}
			}
			break;

		case eDAC_ScheduleSoundPlay:
			{
				bAdvance = true;
				const bool bHasSound  = !m_pCurLine->m_sound.empty();
				if (bHasSound)
				{
					if (m_bSoundScheduled == false)
					{
						IEntity* pActorEntity = m_pSession->GetActorEntity(m_actorID);
						if (pActorEntity == 0)
							break;

						IEntityAudioProxy* pActorAudioProxy = m_pSession->GetEntityAudioProxy(pActorEntity);
						if (pActorAudioProxy == 0)
							break;

						REINST("play voice line");
						// start a new sound
						//int sFlags = CDialogActorContext::SOUND_FLAGS;
						//if (CDialogSystem::sLoadSoundSynchronously != 0)
						//	sFlags |= FLAG_SOUND_LOAD_SYNCHRONOUSLY;
						//
						//// if it's the local player make the sound relative to the entity
						//if (m_bIsLocalPlayer)
						//	sFlags |= FLAG_SOUND_RELATIVE;

						//const char* soundKey = GetSoundKey(m_pCurLine->m_sound);
						//ISound* pSound = 0;

						//pSound = gEnv->pAudioSystem->CreateSound(soundKey, sFlags);

						//DiaLOG::Log(DiaLOG::eDebugA, "[DIALOG] CDialogActorContex::Update: %s Now=%f actorID=%d phase=eDAC_ScheduleSoundPlay: Starting '%s' [key=%s]",
						//	m_pSession->GetDebugName(), now, m_actorID, m_pCurLine->m_sound.c_str(), soundKey);

						//if (!pSound) 
						//{
						//	GameWarning("[DIALOG] CDialogActorContext::Update: %s ActorID=%d: Cannot play sound '%s'",
						//		m_pSession->GetDebugName(), m_actorID, m_pCurLine->m_sound.c_str());
						//}

						//if (pSound)
						//{
						//	if (m_bSoundStopsAnim)
						//	{
						//		m_bSoundStopsAnim = false;
						//		if (m_pAGState != 0)
						//		{
						//			ResetAGState();
						//			DiaLOG::Log(DiaLOG::eDebugA, "[DIALOG] CDialogActorContext::Update: %s Now=%f actorID=%d phase=eDAC_ScheduleSoundPlay: Stopping old animation",
						//				m_pSession->GetDebugName(), now, m_actorID);
						//		}
						//	}
						//	StopSound(); // apparently, if we dont explicitely stop the sound, in rare cases it never stops. Im pretty sure that is an engine/fmod bug.
						//	             // stopping it this way may lead to some subtitles staying for an extra couple seconds on screen (this comes from another engine bug/problem related to an extra fade out time added to cover the dummy NAX soudns) 
						//	m_soundID = pSound->GetId();
						//	m_bSoundStarted = false;
						//	m_bSoundScheduled = true;
						//	// tell the animation whether to stop on sound stop
						//	m_bSoundStopsAnim = m_pCurLine->m_flagSoundStopsAnim;

						//	DiaLOG::Log(DiaLOG::eDebugA, "[DIALOG] CDialogActorContext::Update: %s Now=%f actorID=%d phase=eDAC_ScheduleSoundPlay: Reg as listener on 0x%p '%s'",
						//		m_pSession->GetDebugName(), now, m_actorID, pSound, pSound->GetName());

						//	pSound->SetSemantic(eSoundSemantic_Dialog);
						//	pSound->AddEventListener(this, "DialogActor"); // event listener will set m_soundLength
						//	// sound proxy uses head pos on dialog sounds
						//	const bool bResult = pActorAudioProxy->PlaySound(pSound, Vec3(ZERO), FORWARD_DIRECTION, 1.0f);

						//	if (bResult)
						//	{
						//		// Fetch some localization info for the sound
						//		ILocalizationManager* pLocMgr = gEnv->pSystem->GetLocalizationManager();
						//		if (pLocMgr && CDialogSystem::sWarnOnMissingLoc != 0)
						//		{
						//			SLocalizedInfoGame GameInfo;

						//			const bool bFound = pLocMgr->GetLocalizedInfoByKey(soundKey, GameInfo);
						//			if (!bFound)
						//			{
						//				GameWarning("[DIALOG] CDialogActorContext::Update: '%s' DialogScript '%s': Localized info for '%s' not found!", 
						//					m_pSession->GetDebugName(), m_pSession->GetScript()->GetID().c_str(), m_pCurLine->m_sound.c_str());
						//			}
						//		}

						//		bAdvance = false; // don't advance
						//	}
						//	else
						//	{
						//		GameWarning("[DIALOG] CDialogActorContext::Update: %s ActorID=%d: Cannot play sound '%s' [AudioProxy ignored playback]",
						//			m_pSession->GetDebugName(), m_actorID, m_pCurLine->m_sound.c_str());

						//		pSound->Stop(ESoundStopMode_AtOnce);
						//		m_soundID = INVALID_SOUNDID;
						//		pSound->RemoveEventListener(this);
						//		pSound = NULL;
						//	}
						//}
						//else
						//{
						//	GameWarning("[DIALOG] CDialogActorContext::Update: %s ActorID=%d: Cannot play sound '%s' [AudioProxy returns invalid sound]",
						//		m_pSession->GetDebugName(), m_actorID, m_pCurLine->m_sound.c_str());
						//}
					}
					else
					{
						// sound has been scheduled
						// wait for sound start or timeout
						m_soundTimeOut-=dt;
						const bool bTimedOut = m_soundTimeOut <= 0.0f;
						bAdvance = bTimedOut || m_bSoundStarted;
						if (bAdvance)
						{
							if (bTimedOut)
								StopSound(true); // unregister from sound as listener and free resource
							DiaLOG::Log(DiaLOG::eAlways, "[DIALOG] CDialogActorContext::Update: %s Now=%f actorID=%d phase=eDAC_ScheduleSoundPlay %s",
								m_pSession->GetDebugName(), now, m_actorID, m_bSoundStarted ? "Sound Started" : "Sound Timed Out");
						}
					}
				}
			}
			break;

		case eDAC_SoundFacial:
			{
				bAdvance = true;
				const bool bHasSound  = !m_pCurLine->m_sound.empty();
				const bool bHasFacial = !m_pCurLine->m_facial.empty() || m_pCurLine->m_flagResetFacial;
				if (bHasFacial)
				{
					IEntity* pActorEntity = m_pSession->GetActorEntity(m_actorID);
					if (pActorEntity)
						DoFacialExpression(pActorEntity, m_pCurLine->m_facial, m_pCurLine->m_facialWeight, m_pCurLine->m_facialFadeTime);
				}

				float delay = m_pCurLine->m_delay;
				if (bHasSound && m_bSoundStarted)
				{
					if (m_soundLength < 0.0f)
					{
						m_soundLength = Random(2.0f,6.0f);
						DiaLOG::Log(DiaLOG::eAlways, "[DIALOG] CDialogActorContext::Update: %s Now=%f actorID=%d phase=eDAC_SoundFacial Faking SoundTime to %f",
							m_pSession->GetDebugName(), now, m_actorID, m_soundLength);
					}

					DiaLOG::Log(DiaLOG::eAlways, "[DIALOG] CDialogActorContext::Update: %s Now=%f actorID=%d phase=eDAC_SoundFacial Delay=%f SoundTime=%f",
						m_pSession->GetDebugName(), now , m_actorID, delay, m_soundLength);
					delay += m_soundLength;
				}

				// schedule END
				if (delay < 0.0f)
					delay = 0.0f;
				m_pSession->ScheduleNextLine(delay+0.05f); // ~1 frame at 20 fps. we now stop the current line sound before going for next line. Sometimes, this stop call is right before the
																									 // sound has actually stopped in engine side. When that happens, the engine is adding ~2 seconds extra to the sound duration (because fade problems related to NAX header)
																									 // and that looks bad if subtitles are enabled. So we add this small delay here to try to minimize that situation.
																									 // this is part of the workaround for some dialogs apparently never finishing in the engine side (rare).
				m_bHasScheduled = true;
			}
			break;

		case eDAC_EndLine:
			bAdvance = true;
			if (m_bHasScheduled == false)
			{
				m_bHasScheduled = true;
				m_pSession->ScheduleNextLine(m_pCurLine->m_delay);
			}
			break;
		}

		if (bAdvance)
		{
			AdvancePhase();
			dt = 0.0f;
			++loop;
			assert (loop <= eDAC_EndLine+1);
			if (loop > eDAC_EndLine+1)
			{
				DiaLOG::Log(DiaLOG::eAlways, "[DIALOG] CDialogActorContext::Update: %s Actor=%d InternalLoopCount=%d. Final Exit!",
					m_pSession->GetDebugName(), m_actorID, loop);
			}
		}
		else
			break;
	}
	while (true);

	return true;
}
bool CDialogActorContext::Update(float dt)
{
    if (IsAborted())
        return true;

    // FIXME: this should never happen, as we should get a notification before.
    //        temp leave in for tracking down a bug [AlexL: 23/11/2006]
    IEntity* pEntity = gEnv->pEntitySystem->GetEntity(m_entityID);
    if (pEntity == 0)
    {
        m_pIActor = 0;
        GameWarning("[DIALOG] CDialogActorContext::Update: %s Actor=%d (EntityId=%d) no longer existent.",
                    m_pSession->GetDebugName(), m_actorID, m_entityID);
        AbortContext(true, CDialogSession::eAR_EntityDestroyed);
        return true;
    }

    CDialogSession::AlertnessInterruptMode alertnessInterruptMode = m_pSession->GetAlertnessInterruptMode();
    if(alertnessInterruptMode != CDialogSession::None)
    {
        if(IAIObject* aiObject = pEntity->GetAI())
        {
            if(IAIActorProxy* aiActorProxy = aiObject->GetProxy())
            {
                if(aiActorProxy->GetAlertnessState() >= alertnessInterruptMode)
                {
                    AbortContext(true, CDialogSession::eAR_AIAborted);
                    return true;
                }
            }
        }
    }

    float now = m_pSession->GetCurTime();

    if (!CheckActorFlags(CDialogSession::eDACF_NoActorDeadAbort) && m_pIActor && m_pIActor->IsDead())
    {
        if (!IsAborted())
        {
            DiaLOG::Log(DiaLOG::eAlways, "[DIALOG] CDialogActorContext::Update: %s Actor=%d (EntityId=%d) has died. Aborting.",
                        m_pSession->GetDebugName(), m_actorID, m_entityID);
            AbortContext(true, CDialogSession::eAR_ActorDead);
            return true;
        }
    }

    if (m_bIsLocalPlayer)
    {
        // when the local player is involved in the conversation do some special checks
        if (DoLocalPlayerChecks(dt) == false)
        {
            DiaLOG::Log(DiaLOG::eAlways, "[DIALOG] CDialogActorContext::Update: %s Abort from LocalPlayer.", m_pSession->GetDebugName());
            AbortContext(true, m_bIsAwareInRange == false ? CDialogSession::eAR_PlayerOutOfRange : CDialogSession::eAR_PlayerOutOfView);
            return true;
        }
    }

    if (m_bAbortFromAI)
    {
        m_bAbortFromAI = false;
        if (!IsAborted())
        {
            DiaLOG::Log(DiaLOG::eAlways, "[DIALOG] CDialogActorContext::Update: %s Abort from AI.", m_pSession->GetDebugName());
            AbortContext(true, CDialogSession::eAR_AIAborted);
            return true;
        }
    }

    if (SessionAllowsLookAt())
    {
        DoStickyLookAt();
    }

    int loop = 0;
    do
    {
        // DiaLOG::Log("[DIALOG] DiaLOG::eAlways"CDialogActorContext::Update: %s now=%f actorId=%d loop=%d", m_pSession->GetDebugName(), now, m_actorID, loop);

        bool bAdvance = false;
        switch (m_phase)
        {
        case eDAC_Idle:
            break;

        case eDAC_NewLine:
        {
            m_bHasScheduled = false;
            m_lookAtTimeOut = LOOKAT_TIMEOUT;
            m_animTimeOut   = ANIM_TIMEOUT;
            m_soundTimeOut  = SOUND_TIMEOUT; // wait one second until sound is timed out
            m_bAnimScheduled = false;
            m_bAnimStarted   = false;
            m_bSoundScheduled = false;
            m_bSoundStarted   = false;
            m_soundLength   = 0.0f;

            if (m_pCurLine->m_flagResetLookAt)
            {
                m_stickyLookAtActorID = CDialogScript::NO_ACTOR_ID;
                m_lookAtActorID = CDialogScript::NO_ACTOR_ID;
            }
            // check if look-at sticky is set
            else if (m_pCurLine->m_lookatActor != CDialogScript::NO_ACTOR_ID)
            {
                if (m_pCurLine->m_flagLookAtSticky == false)
                {
                    m_lookAtActorID = m_pCurLine->m_lookatActor;
                    m_stickyLookAtActorID = CDialogScript::NO_ACTOR_ID;
                }
                else
                {
                    m_stickyLookAtActorID = m_pCurLine->m_lookatActor;
                    m_lookAtActorID = CDialogScript::NO_ACTOR_ID;
                }
            }

            bAdvance = true;

            // handle the first sticky look-at here
            if (SessionAllowsLookAt())
            {
                DoStickyLookAt();
            }

#if 0   // test for immediate scheduling of next line while the current line is still playing
            // maybe we're going to use some special delay TAGS like FORCE_NEXT, WAIT
            if (m_pCurLine->m_delay <= -999.0f)
            {
                m_bHasScheduled = true;
                m_pSession->ScheduleNextLine(0.0f);
            }
#endif
        }
        break;

        case eDAC_LookAt:
        {
            bool bWaitForLookAtFinish = true;
            bAdvance = true;

            if (SessionAllowsLookAt() == false)
            {
                break;
            }

            // Question: maybe do this although EP is requested
            if (bWaitForLookAtFinish && m_lookAtActorID != CDialogScript::NO_ACTOR_ID && m_pCurLine->m_flagAGEP == false)
            {
                IEntity* pActorEntity = m_pSession->GetActorEntity(m_actorID);
                IEntity* pLookAtEntity = m_pSession->GetActorEntity(m_lookAtActorID);
                if (pActorEntity != 0)
                {
                    m_lookAtTimeOut -= dt;
                    bool bTargetReached;
                    //bool bSuccess = DoLookAt(pActorEntity, pLookAtEntity, bTargetReached);
                    DoLookAt(pActorEntity, pLookAtEntity, bTargetReached);
                    //DiaLOG::Log(DiaLOG::eDebugA, "[DIALOG] CDialogActorContext::Update: %s now=%f actorID=%d phase=eDAC_LookAt %d",
                    //	m_pSession->GetDebugName(), now, m_actorID, bTargetReached);

                    if (/* bSuccess == false || */ bTargetReached || m_lookAtTimeOut<=0.0f)
                    {
                        DiaLOG::Log(DiaLOG::eAlways, "[DIALOG] CDialogActorContext::Update: %s now=%f actorID=%d phase=eDAC_LookAt %s",
                                    m_pSession->GetDebugName(), now, m_actorID, bTargetReached ? "Target Reached" : "Timed Out");
                        m_lookAtActorID = CDialogScript::NO_ACTOR_ID;
                    }
                    else
                        bAdvance = false;
                }
            }
        }
        break;

        case eDAC_Anim:
        {
            bAdvance = true;
            if (SessionAllowsLookAt() && m_lookAtActorID != CDialogScript::NO_ACTOR_ID) // maybe: don't do this when EP is requested [&& m_pCurLine->m_flagAGEP == false]
            {
                IEntity* pActorEntity = m_pSession->GetActorEntity(m_actorID);
                IEntity* pLookAtEntity = m_pSession->GetActorEntity(m_lookAtActorID);
                if (pActorEntity != 0)
                {
                    m_lookAtTimeOut -= dt;
                    bool bTargetReached;
                    //bool bSuccess = DoLookAt(pActorEntity, pLookAtEntity, bTargetReached);
                    DoLookAt(pActorEntity, pLookAtEntity, bTargetReached);
                    if (/* bSuccess == false || */ bTargetReached || m_lookAtTimeOut<=0.0f)
                    {
                        DiaLOG::Log(DiaLOG::eAlways, "[DIALOG] CDialogActorContext::Update: %s now=%f actorID=%d phase=eDAC_Anim %s",
                                    m_pSession->GetDebugName(), now, m_actorID, bTargetReached ? "Target Reached" : "Timed Out");
                        m_lookAtActorID = CDialogScript::NO_ACTOR_ID;
                    }
                    else
                        bAdvance = false;
                }
            }
            const bool bHasAnim = !m_pCurLine->m_anim.empty();
            if (SessionAllowsAnim() && bHasAnim)
            {
                bAdvance = false;
                if (!m_bAnimScheduled)
                {
                    m_bSoundStopsAnim = false; // whenever there is a new animation, no need for the still playing sound
                    // to stop the animation
                    m_bAnimStarted = false;
                    m_bAnimScheduled = true;
                    // schedule animation
                    IEntity* pActorEntity = m_pSession->GetActorEntity(m_actorID);
                    if (pActorEntity)
                        DoAnimAction(pActorEntity, m_pCurLine->m_anim, m_pCurLine->m_flagAGSignal, m_pCurLine->m_flagAGEP);
                    else
                        bAdvance = true;
                }
                else
                {
                    // we scheduled it already
                    // wait until it starts or timeout
                    m_animTimeOut-=dt;
                    bAdvance = m_animTimeOut <= 0.0f || m_bAnimStarted;
                    if (bAdvance)
                    {
                        DiaLOG::Log(DiaLOG::eAlways, "[DIALOG] CDialogActorContext::Update: %s Now=%f actorID=%d phase=eDAC_Anim %s",
                                    m_pSession->GetDebugName(), now, m_actorID, m_bAnimStarted ? "Anim Started" : "Anim Timed Out");
                    }
                }
            }
        }
        break;

        case eDAC_ScheduleSoundPlay:
        {
            bAdvance = true;
            const bool bHasSound  = m_pCurLine->m_audioID != INVALID_AUDIO_CONTROL_ID;
            if (bHasSound)
            {
                if (m_bSoundScheduled == false)
                {
                    IEntity* pActorEntity = m_pSession->GetActorEntity(m_actorID);
                    if (pActorEntity == 0)
                        break;

                    IEntityAudioProxy* pActorAudioProxy = m_pSession->GetEntityAudioProxy(pActorEntity);

                    if (pActorAudioProxy == 0)
                        break;

                    if(m_SpeechAuxProxy == INVALID_AUDIO_PROXY_ID)
                    {
                        m_SpeechAuxProxy = pActorAudioProxy->CreateAuxAudioProxy();
                        pActorAudioProxy->AddAsListenerToAuxAudioProxy(m_SpeechAuxProxy, &CDialogActorContext::OnAudioTriggerFinished, eART_AUDIO_CALLBACK_MANAGER_REQUEST, eACMRT_REPORT_FINISHED_TRIGGER_INSTANCE);
                    }
                    UpdateAuxProxyPosition();

                    SAudioCallBackInfos callbackInfos(0, (void*)CDialogActorContext::GetClassIdentifier(), reinterpret_cast<void*>(static_cast<intptr_t>(m_ContextID)), eARF_PRIORITY_NORMAL | eARF_SYNC_FINISHED_CALLBACK);
                    if(!pActorAudioProxy->ExecuteTrigger(m_pCurLine->m_audioID, eLSM_None, m_SpeechAuxProxy, callbackInfos))
                    {
                        m_bSoundStarted = false;
                        m_bHasScheduled = false;
                        m_pSession->ScheduleNextLine(2.0f);
                    }
                    else
                    {
                        m_bSoundStarted = true;
                        m_bHasScheduled = true;
                        //todo: get the length of the sound to schedule the next line (set m_soundLength). Currently we schedule the next line as soon as the current one is finished, ignoring the specified "delay" parameter. its done in: DialogTriggerFinishedCallback

                        const char* triggerName = gEnv->pAudioSystem->GetAudioControlName(eACT_TRIGGER, m_pCurLine->m_audioID);
                        if (triggerName == NULL)
                            triggerName =  "dialog trigger";
                        DiaLOG::Log(DiaLOG::eDebugA, "[DIALOG] CDialogActorContex::Update: %s Now=%f actorID=%d phase=eDAC_ScheduleSoundPlay: Starting '%s'",
                                    m_pSession->GetDebugName(), now, m_actorID, triggerName);
                    }
                }
                else
                {
                    // sound has been scheduled
                    // wait for sound start or timeout
                    m_soundTimeOut-=dt;
                    const bool bTimedOut = m_soundTimeOut <= 0.0f;
                    bAdvance = bTimedOut || m_bSoundStarted;
                    if (bAdvance)
                    {
                        if (bTimedOut)
                            StopSound(true); // unregister from sound as listener and free resource
                        DiaLOG::Log(DiaLOG::eAlways, "[DIALOG] CDialogActorContext::Update: %s Now=%f actorID=%d phase=eDAC_ScheduleSoundPlay %s",
                                    m_pSession->GetDebugName(), now, m_actorID, m_bSoundStarted ? "Sound Started" : "Sound Timed Out");
                    }
                }
            }
        }
        break;

        case eDAC_SoundFacial:
        {
            bAdvance = true;
            const bool bHasSound  = m_pCurLine->m_audioID != INVALID_AUDIO_CONTROL_ID;
            const bool bHasFacial = !m_pCurLine->m_facial.empty() || m_pCurLine->m_flagResetFacial;
            if (bHasFacial)
            {
                IEntity* pActorEntity = m_pSession->GetActorEntity(m_actorID);
                if (pActorEntity)
                    DoFacialExpression(pActorEntity, m_pCurLine->m_facial, m_pCurLine->m_facialWeight, m_pCurLine->m_facialFadeTime);
            }

            //hd-todo: re-add me, when we have a way to query the length of the audio-lines
            //float delay = m_pCurLine->m_delay;
            //if (bHasSound && m_bSoundStarted)
            //{
            //	if (m_soundLength <= 0.0f)
            //	{
            //		m_soundLength = cry_random(2.0f, 6.0f);
            //		DiaLOG::Log(DiaLOG::eAlways, "[DIALOG] CDialogActorContext::Update: %s Now=%f actorID=%d phase=eDAC_SoundFacial Faking SoundTime to %f",
            //			m_pSession->GetDebugName(), now, m_actorID, m_soundLength);
            //	}

            //	DiaLOG::Log(DiaLOG::eAlways, "[DIALOG] CDialogActorContext::Update: %s Now=%f actorID=%d phase=eDAC_SoundFacial Delay=%f SoundTime=%f",
            //		m_pSession->GetDebugName(), now , m_actorID, delay, m_soundLength);
            //	delay += m_soundLength;
            //}

            //// schedule END
            //if (delay < 0.0f)
            //	delay = 0.0f;
            //m_pSession->ScheduleNextLine(delay+0.05f); // ~1 frame at 20 fps. we now stop the current line sound before going for next line. Sometimes, this stop call is right before the
            //																					 // sound has actually stopped in engine side. When that happens, the engine is adding ~2 seconds extra to the sound duration (because fade problems related to NAX header)
            //																					 // and that looks bad if subtitles are enabled. So we add this small delay here to try to minimize that situation.
            //																					 // this is part of the workaround for some dialogs apparently never finishing in the engine side (rare).
            //m_bHasScheduled = true;
        }
        break;

        case eDAC_EndLine:
            bAdvance = true;
            if (m_bHasScheduled == false)
            {
                m_bHasScheduled = true;
                m_pSession->ScheduleNextLine(m_pCurLine->m_delay);
            }
            break;
        }

        if (bAdvance)
        {
            AdvancePhase();
            dt = 0.0f;
            ++loop;
            assert (loop <= eDAC_EndLine+1);
            if (loop > eDAC_EndLine+1)
            {
                DiaLOG::Log(DiaLOG::eAlways, "[DIALOG] CDialogActorContext::Update: %s Actor=%d InternalLoopCount=%d. Final Exit!",
                            m_pSession->GetDebugName(), m_actorID, loop);
            }
        }
        else
        {
            if (IsStillPlaying())
            {
                UpdateAuxProxyPosition();
            }

            break;
        }
    }
    while (true);

    return true;
}
Beispiel #8
0
bool Screen::MouseInput(int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam, Dialog *pForDlg, class C4Viewport *pForVP)
	{
	// help mode and button pressed: Abort help and discard button
	if (Game.MouseControl.IsHelp())
		{
		switch (iButton)
			{
			case C4MC_Button_None:
				// just movement
				break;
			case C4MC_Button_LeftDown:
			case C4MC_Button_RightDown:
				// special for left/right down: Just ignore them, but don't stop help yet
				// help should be stopped on button-up, so these won't be processed
				iButton = C4MC_Button_None;
				break;
			default:
				// buttons stop help
				Game.MouseControl.AbortHelp();
				iButton = C4MC_Button_None;
				break;
			}
		}
	// forward to mouse
	Mouse.Input(iButton, iX, iY, dwKeyParam);
	// dragging
	if (Mouse.pDragElement)
		{
		int32_t iX2=iX, iY2=iY;
		Mouse.pDragElement->ScreenPos2ClientPos(iX2, iY2);
		if (!Mouse.IsLDown())
			{
			// stop dragging
			Mouse.pDragElement->StopDragging(Mouse, iX2, iY2, dwKeyParam);
			Mouse.pDragElement = NULL;
			}
		else
			{
			// continue dragging
			Mouse.pDragElement->DoDragging(Mouse, iX2, iY2, dwKeyParam);
			}
		}
	// backup previous MouseOver-element
	Mouse.pPrevMouseOverElement = Mouse.pMouseOverElement;
	Mouse.pMouseOverElement = NULL;
	bool fProcessed = false;
	// active context menu?
	if (!pForVP && pContext && pContext->CtxMouseInput(Mouse, iButton, iX, iY, dwKeyParam))
		{
		// processed by context menu: OK!
		}
	// otherwise: active dlg and inside screen? (or direct forward to specific dlg/viewport dlg)
	else if (rcBounds.Contains(iX, iY) || pForDlg || pForVP)
		{
		// context menu open but mouse down command issued? close context then
		if (pContext && (iButton == C4MC_Button_LeftDown || iButton == C4MC_Button_RightDown))
			AbortContext(true);
		// get client pos
		if (!pForDlg && !pForVP)
			{
			C4Rect &rcClientArea = GetClientRect();
			iX -= rcClientArea.x; iY -= rcClientArea.y;
			}
		// exclusive mode: process active dialog only
		if (IsExclusive() && !pForDlg && !pForVP)
			{
			if (pActiveDlg && pActiveDlg->IsVisible() && !pActiveDlg->IsFading())
				{
				// bounds check to dlg: only if not dragging
				C4Rect &rcDlgBounds = pActiveDlg->GetBounds();
				if (Mouse.IsLDown() || rcDlgBounds.Contains(iX, iY))
					// forward to active dialog
					pActiveDlg->MouseInput(Mouse, iButton, iX - rcDlgBounds.x, iY - rcDlgBounds.y, dwKeyParam);
				else
					Mouse.pMouseOverElement = NULL;
				}
			else
				// outside dialog: own handling (for screen context menu)
				Window::MouseInput(Mouse, iButton, iX, iY, dwKeyParam);
			}
		else
			{
			// non-exclusive mode: process all dialogs; make them active on left-click
			Dialog *pDlg;
			for (Element *pEl = pLast; pEl; pEl = pEl->GetPrev())
				if (pDlg = pEl->GetDlg())
					if (pDlg->IsShown())
						{
						// if specified: process specified dlg only
						if (pForDlg && pDlg != pForDlg) continue;
						// if specified: process specified viewport only
						bool fIsExternalDrawDialog = pDlg->IsExternalDrawDialog();
						C4Viewport *pVP = fIsExternalDrawDialog ? pDlg->GetViewport() : NULL;
						if (pForVP && pForVP != pVP) continue;
						// calc offset
						C4Rect &rcDlgBounds = pDlg->GetBounds();
						int32_t iOffX=0, iOffY=0;
						// special handling for viewport dialogs
						if (fIsExternalDrawDialog)
							{
							// ignore external drawing dialogs without a viepwort assigned
							if (!pVP) continue;
							// always clip to viewport bounds
							C4Rect rcOut(pVP->GetOutputRect());
							if (!rcOut.Contains(iX + rcBounds.x, iY + rcBounds.y)) continue;
							// viewport dialogs: Offset determined by viewport position
							iOffX = rcOut.x; iOffY = rcOut.y;
							}
						// hit test; or special: dragging possible outside active dialog
						if (rcDlgBounds.Contains(iX-iOffX, iY-iOffY) || (pDlg == pActiveDlg && Mouse.pDragElement))
							{
							// Okay; do input
							pDlg->MouseInput(Mouse, iButton, iX - rcDlgBounds.x - iOffX, iY - rcDlgBounds.y - iOffY, dwKeyParam);
							// dlgs may destroy GUI
							if (!IsGUIValid()) return false;
							// CAUTION: pDlg may be invalid now!
							// set processed-flag manually
							fProcessed = true;
							// inactive dialogs get activated by clicks
							if (Mouse.IsLDown() && pDlg != pActiveDlg)
								// but not viewport dialogs!
								if (!pDlg->IsExternalDrawDialog())
									ActivateDialog(pDlg);
							// one dlg only; break loop here
							break;
							}
						}
			}
		// check valid GUI; might be destroyed by mouse input
		if (!IsGUIValid()) return false;
		}

	// check if MouseOver has changed
	if (Mouse.pPrevMouseOverElement != Mouse.pMouseOverElement)
		{
		// send events
		if (Mouse.pPrevMouseOverElement) Mouse.pPrevMouseOverElement->MouseLeave(Mouse);
		if (Mouse.pMouseOverElement) Mouse.pMouseOverElement->MouseEnter(Mouse);
		}
	// return whether anything processed it
	return fProcessed || Mouse.pDragElement || (Mouse.pMouseOverElement && Mouse.pMouseOverElement!=this) || pContext;
	}