using Invector.vCamera; using PixelCrushers.DialogueSystem; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using PixelCrushers.QuestMachine; using UnityEngine; using UnityEngine.Events; using UnityEngine.Playables; using Sirenix.OdinInspector; using QuestState = PixelCrushers.QuestMachine.QuestState; using PixelCrushers; namespace Beyond { [Serializable] public class CutsceneSaveData { public bool wasPlayed; } [RequireComponent(typeof(PlayableDirector))] public class CutScene : Saver { public static Action CutSceneEnable; [Header("Conditions"), Tooltip("Leave it empty if you want this to play every time")] public Condition m_condition; [Header("Start Cutscene")] public GameObject[] toEnableOnStart; public GameObject[] toDisableOnStart; public UnityEvent m_onStartEvent; [Header("End Cutscene")] public GameObject[] toEnableOnEnd; public GameObject[] toDisableOnEnd; public UnityEvent m_onEndEvent; [Space] [Tooltip("Depricated! don't use. Left to make sure some settings won't be lost (before copping)")] public FadeSettings fadeSettings; public bool m_completeCurrentQuest = false; private float m_fadeTime = 1f; public TeleportObject teleport; public bool playOnStart = false; //public bool playOnce = true; public bool playMultipleTimes = false; [TitleGroup("Skip Fade")] public bool skipFadeOutIn = false; [TitleGroup("Skip Fade")] public bool skipFadeIn = false; private bool timelineStarted; private PlayableDirector m_playableDirector; private Camera[] cameras; private bool timeLineStoped; private PlayState timeLineState = PlayState.Paused; [Header("Debug"), SerializeField, Min(0.001f)] private float m_speed = 1f; private AudioListener[] audioListeners; private CutSceneLerpCamera lerpCamera; private StandardBarkUI m_barkUI; private Camera playerCamera; private bool cancelAlphaWait; private Coroutine cancelAlpha; private bool followTarget; private Camera lastUseCamera; private float cutSceneTimer; private float nearestStopTime; private CutSceneDialogueBreakpoint currentDialogueBreakpoint; private int currentDialogueBreakpointIndex = 0; private bool wasPlayed; private bool wasLoaded; public bool WasPlayed { set { wasPlayed = value; } get { return wasPlayed; } } [SerializeField] private CutsceneLevelLoader m_cutsceneLevelLoader = new CutsceneLevelLoader(); [Space] [Header("Breakpoints")] [SerializeField] private List cutSceneBreakpoints = new List(); [SerializeField] private List cutSceneDialogueBreakpoints = new List(); private bool CanStart { get { if (wasPlayed && !playMultipleTimes) return false; return m_condition.IsTrue(Player.Instance.transform); } } public bool TimelineStarted { get => timelineStarted; set => timelineStarted = value; } public override void Awake() { base.Awake(); //fadeSettings.CreateInverseEndFadeCurve(); m_playableDirector = GetComponent(); m_playableDirector.playOnAwake = false; cameras = GetComponentsInChildren(true); audioListeners = GetComponentsInChildren(true); SetActiveCameras(false); SetActiveAudioListener(false); var tpCam = FindObjectOfType(); if (tpCam) playerCamera = tpCam.GetComponent(); wasPlayed = false; } private IEnumerator Start() { //lil hack to prevent starting cutscenes while loading, person to blame -> Seba //yield return new WaitForSeconds(0.1f); yield return null; yield return null; m_barkUI = GameObject.FindObjectOfType(); if (CanStart) { if (playOnStart) PlayTimeline(); } /* else { this.gameObject.SetActive(false); } */ } public override void OnEnable() { base.OnEnable(); m_playableDirector.played += OnPlayed; m_playableDirector.stopped += OnStoped; m_playableDirector.paused += OnPaused; cutSceneDialogueBreakpoints = cutSceneDialogueBreakpoints.OrderBy(x => x.PauseTime).ToList(); currentDialogueBreakpoint = GetCutsceneDialogueBreakpoint(currentDialogueBreakpointIndex); } public override void OnDisable() { base.OnDisable(); m_playableDirector.played -= OnPlayed; m_playableDirector.stopped -= OnStoped; m_playableDirector.paused -= OnPaused; } public override void OnDestroy() { base.OnDestroy(); m_playableDirector.played -= OnPlayed; m_playableDirector.stopped -= OnStoped; m_playableDirector.paused -= OnPaused; StopAllCoroutines(); } private void OnPaused() { if (m_playableDirector.state == PlayState.Playing) { m_playableDirector.Pause(); } else { m_playableDirector.Play(); } } private void OnSkip() { //StopCoroutine(cancelAlpha); //StopAllCoroutines(); //StartCoroutine(OnExitDelay()); OnStoped(m_playableDirector); } private void OnPaused(PlayableDirector director) { if (timelineStarted == false) { PlayTimeline(); } } private void SetActive(GameObject[] list, bool isActive) { var length = list.Length; for (int i = 0; i < length; i++) { if (list[i] != null) { list[i].gameObject.SetActive(isActive); } } } private CutSceneDialogueBreakpoint GetCutsceneDialogueBreakpoint(int index) { return cutSceneDialogueBreakpoints.Count <= 0 || index >= cutSceneDialogueBreakpoints.Count ? null : cutSceneDialogueBreakpoints[index]; } private CutSceneBreakpoint GetCutsceneBreakpoint(int index) { return cutSceneBreakpoints.Count <= 0 || index >= cutSceneDialogueBreakpoints.Count ? null : cutSceneBreakpoints[index]; } private void OnPlayed(PlayableDirector director) { if (timelineStarted == false) { director.Pause(); } } private void Update() { if (timelineStarted) { if (currentDialogueBreakpoint == null) { return; } if (m_playableDirector.time >= currentDialogueBreakpoint.PauseTime) { m_playableDirector.Pause(); DialogueManager.instance.StartConversation(currentDialogueBreakpoint.DialogueTitle); currentDialogueBreakpointIndex++; currentDialogueBreakpoint = GetCutsceneDialogueBreakpoint(currentDialogueBreakpointIndex); } } } private void LateUpdate() { if (lerpCamera != null || timelineStarted == false || timeLineStoped == true) return; if (playerCamera && followTarget) { var length = cameras.Length; for (int i = 0; i < length; i++) { if (cameras[i].gameObject.activeInHierarchy) { lastUseCamera = cameras[i]; playerCamera.transform.position = cameras[i].transform.position; playerCamera.transform.rotation = cameras[i].transform.rotation; break; } } //No active cutscene camera! Keep old transform. if (lastUseCamera) { playerCamera.transform.position = lastUseCamera.transform.position; playerCamera.transform.rotation = lastUseCamera.transform.rotation; } } } private void OnStoped(PlayableDirector director) { //FadeCanvasGroup.Instance.FadeIn(1f); if (!timeLineStoped) { timeLineStoped = true; timeLineState = PlayState.Paused; timelineStarted = false; director.Stop(); director.timeUpdateMode = DirectorUpdateMode.Manual; director.time = director.duration; director.Evaluate(); if (lerpCamera == null) { teleport.Teleport(vThirdPersonCamera.instance.transform); vThirdPersonCamera.instance.ResetAngle(); vThirdPersonCamera.instance.ResetTarget(); } try { m_onEndEvent?.Invoke(); } catch (Exception e) { Debug.LogError("CutScene: error on OnStopped, while executing event. Message: " + e.Message); } SetActive(toEnableOnEnd, true); SetActive(toDisableOnEnd, false); SkipCanvas.Instance.m_OnSkip -= OnSkip; SkipCanvas.Instance.m_OnPause -= OnPaused; SkipCanvas.Instance.gameObject.SetActive(false); } Player.Instance.ResetAnimator(); if (lerpCamera == null) { //after player is enabled on set active, so everything works just fine EnableInputAndUI(); } var dc = Player.Instance.GetComponent(); if (dc != null) { dc.HideWeapons(true); } if (m_cutsceneLevelLoader.Enabled) { m_cutsceneLevelLoader.LoadLevel(); } StopAllCoroutines(); //FadeCanvasGroup.Instance.FadeIn(1f); } /* private void OnStoped(PlayableDirector director) { StopAllCoroutines(); StartCoroutine(OnStopedCoroutine(director)); } private IEnumerator OnStopedCoroutine(PlayableDirector director) { if (!timeLineStoped) { director.time = director.duration; //give one frame to move director to the end yield return null; director.Stop(); if (lerpCamera == null) { EnableInputAndUI(); } timeLineStoped = true; timeLineState = PlayState.Paused; timelineStarted = false; if (lerpCamera == null) { teleport.Teleport(vThirdPersonCamera.instance.transform); } m_onEndEvent?.Invoke(); SetActive(toEnableOnEnd, true); SetActive(toDisableOnEnd, false); SkipCanvas.Instance.m_OnSkip -= OnSkip; SkipCanvas.Instance.m_OnPause -= OnPaused; SkipCanvas.Instance.gameObject.SetActive(false); } var dc = Player.Instance.GetComponent(); if (dc != null) { dc.HideWeapons(true); } yield return null; } */ private IEnumerator OnPlayDelay() { wasPlayed = true; Player.Instance.PlayerInput.SetLockAllInput(true); if (lerpCamera == null && FadeCanvasGroup.Instance != null) { if (playOnStart || skipFadeIn) { FadeCanvasGroup.Instance.FadeIn(m_fadeTime); //yield return new WaitForSeconds(m_fadeTime); followTarget = true; } else { FadeCanvasGroup.Instance.FadeOutIn(m_fadeTime); yield return new WaitForSeconds(m_fadeTime); followTarget = true; //yield return new WaitForSeconds(m_fadeTime * 0.5f); } ///FadeCanvasGroup.InvokeFade(fadeSettings.Startfade, fadeSettings.speed * m_speed); // /* FadeCanvasGroup.Instance.FadeOutIn(m_fadeTime); cancelAlpha = StartCoroutine(CancelAlphaWait()); yield return new WaitUntil(() => Alpha1()); yield return new WaitForSeconds(m_fadeTime * 0.5f); followTarget = true; yield return new WaitForSeconds(m_fadeTime * 0.5f); yield return new WaitUntil(() => Alpha0()); if (cancelAlpha != null) { StopCoroutine(cancelAlpha); } */ } else { yield return null; } HideUI.InvokeSetActiveUI(false); SkipCanvas.Instance.gameObject.SetActive(true); SkipCanvas.Instance.m_OnSkip += OnSkip; SkipCanvas.Instance.m_OnPause += OnPaused; if (m_playableDirector && timeLineState != PlayState.Playing) { timeLineState = PlayState.Playing; m_playableDirector.initialTime = 0f; m_playableDirector.Play(); m_playableDirector.playableGraph.GetRootPlayable(0).SetSpeed(m_speed); CutSceneEnable?.Invoke(true); m_onStartEvent?.Invoke(); SetActive(toEnableOnStart, true); SetActive(toDisableOnStart, false); timeLineStoped = false; StartCoroutine(OnExitDelay()); } yield return null; } private IEnumerator OnExitDelay() { //var waitTime = (float)m_playableDirector.playableAsset.duration - (fadeSettings.GetInverseEndFadeCurveValue(1f) / fadeSettings.speed); var waitTime = (float)m_playableDirector.playableAsset.duration - m_fadeTime - .2f; // * 0.5f; //0.5, b robimy fade in/out while (m_playableDirector.time /*+ Time.deltaTime * 2f*/ < waitTime) // hak, chcę być o jedną 2 klatkę do przodu. { yield return null; } if (lerpCamera == null) { if (!skipFadeOutIn)// && m_cutsceneLevelLoader.Enabled == false) { FadeCanvasGroup.Instance.FadeOutIn(m_fadeTime); } } //cancelAlpha = StartCoroutine(CancelAlphaWait()); //yield return new WaitUntil(() => Alpha1()); //yield return new WaitUntil(() => Alpha0()); //yield return new WaitForSeconds(m_fadeTime * 0.5f); // if (m_cutsceneLevelLoader.Enabled) // { // FadeCanvasGroup.Instance.FadeOut(m_fadeTime); // yield return new WaitForSeconds(m_fadeTime); // m_cutsceneLevelLoader.LoadLevel(); // yield break; // } yield return new WaitForSeconds(m_fadeTime); followTarget = false; //if (cancelAlpha != null) //{ // StopCoroutine(cancelAlpha); //} OnStoped(m_playableDirector); yield return null; } private IEnumerator CancelAlphaWait() { cancelAlphaWait = false; float maxWaitTime = m_fadeTime; //if (curve != null && curve.keys.Length > 0) //{ //maxWaitTime = curve.keys[curve.length - 1].time / fadeSettings.speed; //} yield return new WaitForSeconds(maxWaitTime); Debug.Log("Fade timeout, cancel wait"); cancelAlphaWait = true; cancelAlpha = null; } private bool Alpha1() { return FadeCanvasGroup.Instance.canvasGroup.alpha >= 1f || cancelAlphaWait; } private bool Alpha0() { return FadeCanvasGroup.Instance.canvasGroup.alpha < 1f || cancelAlphaWait; } public void EnableInputAndUI() { HideUI.InvokeSetActiveUI(true); CutSceneEnable?.Invoke(false); Player.Instance.PlayerInput.SetLockAllInput(false); Debug.Log("Disabled cutscene"); } public void ResumeCutScene() { m_playableDirector.Resume(); } [Button] private void CompleteQuest() { var journal = QuestMachine.GetQuestJournal(); Quest currentQuest = null; for (int i = 0; i < journal.questList.Count; i++) { var quest = journal.questList[i]; if (!quest.isTrackable || quest.GetState() == QuestState.Successful) { continue; } currentQuest = quest; break; } if (currentQuest) { for (int i = 0; i < currentQuest.nodeList.Count; i++) { if (currentQuest.nodeList[i].GetState() == QuestNodeState.True) { continue; } if (currentQuest.nodeList[i].GetState() == QuestNodeState.Active) { currentQuest.nodeList[i].SetState(QuestNodeState.True); journal.OnQuestNodeStateChanged(currentQuest.nodeList[i]); } } //journal.(); } } [Button] public void PlayTimeline() { if (CanStart) { timelineStarted = true; lastUseCamera = null; lerpCamera = GetComponent(); if (lerpCamera != null && !lerpCamera.enabled) { lerpCamera = null; } if (m_completeCurrentQuest) { CompleteQuest(); } StartCoroutine(OnPlayDelay()); } } private void SetActiveCameras(bool isActive) { foreach (var item in cameras) { item.enabled = isActive; } } private void SetActiveAudioListener(bool isActive) { foreach (var item in audioListeners) { item.enabled = isActive; } } private void OnValidate() { m_playableDirector = GetComponent(); m_playableDirector.playOnAwake = false; } public override string RecordData() { CutsceneSaveData data = new(); data.wasPlayed = WasPlayed; return SaveSystem.Serialize(data); } public override void ApplyData(string s) { var sd = SaveSystem.Deserialize(s); if (sd != null) { WasPlayed = sd.wasPlayed; } if (wasPlayed) { gameObject.SetActive(false); } } } [System.Serializable] public class CutSceneBreakpoint { public float PauseTime; public float Duration; public UnityEvent OnPauseStart; public UnityEvent OnPauseEnd; } [System.Serializable] public class CutSceneDialogueBreakpoint { public float PauseTime; public string DialogueTitle; [Tooltip("if 0 pause will be stay until player pick choice")] public float TimeForAutomaticChoice; } }