/// <summary>
        /// Start a new full ADV scene with fade in, full control over camera and map, multiple characters and such. Can be used in roaming mode and some other cases.
        /// Best to use this for custom events where player talks to other characters, or characters other than player talk (if player talks with himself consider using <see cref="StartMonologueEvent"/> instead).
        /// Do not use in TalkScenes, use <see cref="StartTextSceneEvent"/> instead.
        /// You can get variables set in the commands through actScene.AdvScene.Scenario.Vars.
        /// You can specify heroines you want to use in this script and use the CharaChange command to load their parameters. You can also use Program.SetParam to hard code the parameters in the list.
        /// Warning: By default you need to have ScreenFadeRegulate and SceneFade commands at the start of your script, check fadeIn parameter description for more info.
        /// Warning: You have to have a Close command or the scene will softlock after reaching the end!
        /// </summary>
        /// <param name="commands">List of commands for the event</param>
        /// <param name="fadeIn">Should the scene fade in and out (otherwise it instantly pops in and out of existence, in that case consider using <see cref="StartMonologueEvent"/> instead).
        /// Important: With this turned on you have to have `ScreenFadeRegulate "false"` as first command or your script will never start (it's automatically added by <see cref="CreateNewEvent"/>).
        /// You also need to add a `SceneFade "out"` command at the start to fade out the effect and show the dialog (spawn characters and do all initialization before this step so it's not visible).
        /// At the end of the scene if you want to fade out then add a `SceneFade "in"` command at the end of your script (after closing it will fade back to the game automatically).</param>
        /// <param name="extraData">Event called when running the scene and fade, also fade type. Can be safely ignored in most cases.</param>
        /// <param name="camera">Initial position and rotation of the camera. By default the camera starts facing the center character. Can be safely ignored in most cases.</param>
        /// <param name="heroines">List of heroines to set in event context. They can be accessed from commands like <see cref="Command.CharaCreate"/> and <see cref="Command.CharaChange"/>. If null, current Scenario.heroineList is used.</param>
        /// <param name="position">This is the position of the center character, NOT the camera. Camera is controlled separately and by default looks at the character from the front.
        /// You can use <see cref="Command.CameraPositionSet"/> and <see cref="Command.CharaPositionSet"/> to change this from your script instead.</param>
        /// <param name="rotation">This is the rotation of the center character, NOT the camera. Camera is controlled separately and by default looks at the character from the front. Should only change Y rotation and keep XZ = 0.
        /// You can use <see cref="Command.CameraRotationSet"/> to change the camera rotation from your script.</param>
        public static IEnumerator StartAdvEvent(IEnumerable <Program.Transfer> commands, Vector3 position, Quaternion rotation, bool fadeIn = true,
                                                Program.OpenDataProc extraData = null, OpenData.CameraData camera = null, List <SaveData.Heroine> heroines = null)
        {
            if (commands == null)
            {
                throw new ArgumentNullException(nameof(commands));
            }

            var list = commands.ToList();

            if (list.All(x => x.param.Command != Command.Close))
            {
                list.Add(Program.Transfer.Close());
                UnityEngine.Debug.LogWarning("No Close command in the transfer list! Adding close at end to prevent lockup. Add Program.Transfer.Close() at the end of list to fix this.");
            }

            // todo handle this properly? Used when going back to title screen from F1 menu and possibly other places. Possibly not a problem since we run as a coroutine?
            var token = CancellationToken.None;

            var actScene = ActionScene.instance;

            // Stop player input and some game functions
            actScene.Player.isActionNow = true;

            var prevBGM    = string.Empty;
            var prevVolume = 1f;

            yield return(TuplelessGetBgmAndVolume((x, y) => { prevBGM = x; prevVolume = y; }));

            if (fadeIn)
            {
                // Sanity checks
                if (list.Take(4).All(x => x.param.Command != Command.SceneFadeRegulate))
                {
                    list.Insert(0, Program.Transfer.Create(true, Command.SceneFadeRegulate, "false"));
                    UnityEngine.Debug.LogWarning("No 'SceneFadeRegulate false' command at the start of the transfer list when fadeIn=true! Adding SceneFadeRegulate at start to prevent lockup. Add 'SceneFadeRegulate false' at the start of list to fix this.");
                }
                if (list.All(x => !(x.param.Command == Command.SceneFade && x.param.Args.FirstOrDefault()?.ToLower() == "out")))
                {
                    list.Insert(1, Program.Transfer.Create(true, Command.SceneFade, "out"));
                    UnityEngine.Debug.LogWarning("No 'SceneFade out' command in the transfer list! Adding 'SceneFade out' at start to prevent lockup. Add 'SceneFade out' after you are done with initializing the scene and before first text to fix this. Make sure it's set to multi=true and that there are no multi=false items before it or the game will lock up.");
                }
                if (list.All(x => !(x.param.Command == Command.SceneFade && x.param.Args.FirstOrDefault()?.ToLower() == "in")))
                {
                    UnityEngine.Debug.LogWarning("No 'SceneFade in' command in the transfer list! The scene won't fade out back to gameplay, instead it will instantly jump at the end until you add a 'SceneFade in' command before the Close command. Warning: Make sure that 'SceneFadeRegulate false' command has been ran before you run SceneFade or it will lock up.");
                }

                // Fade out of the gameplay into a loading screen
                //Scene.sceneFadeCanvas.SetColor(Color.black);
                yield return(Scene.sceneFadeCanvas.StartFadeAysnc(FadeCanvas.Fade.In).ToCoroutine(UnityEngine.Debug.LogException));
            }

            list.Insert(1, Program.Transfer.Create(true, Command.CameraSetFov, Program.BASE_FOV));

            // Disable all characters to make sure nothing weird happens while the scene runs
            actScene.Player.SetActive(false);
            actScene.npcList.ForEach(p => p.SetActive(false));
            actScene.npcList.ForEach(p => p.Pause(true));
            if (actScene.fixChara != null)
            {
                actScene.fixChara.SetActive(false);
            }

            bool isOpenADV = false;

            if (extraData == null)
            {
                extraData = new Program.OpenDataProc();
            }
            extraData.onLoad += () => isOpenADV = true;

            // Run the actual scene
            yield return(Program.Open(new Data
            {
                fadeInTime = 0f,
                position = position,
                rotation = rotation,
                camera = camera,
                heroineList = heroines ?? actScene.AdvScene.Scenario.heroineList,
                scene = SingletonInitializer <ActionScene> .instance,
                transferList = list
            }, token, extraData).ToCoroutine(UnityEngine.Debug.LogException));

            // Wait until the scene is done
            yield return(UniTask.WaitUntil(() => isOpenADV, PlayerLoopTiming.Update, token).ToCoroutine(UnityEngine.Debug.LogException));

            yield return(Program.Wait(string.Empty, token).ToCoroutine(UnityEngine.Debug.LogException));

            // Reenable all characters
            actScene.Player.SetActive(true);
            actScene.npcList.ForEach(p => p.SetActive(p.mapNo == actScene.Map.no));
            actScene.npcList.ForEach(p =>
            {
                p.Pause(false);
                // No longer needed in KKS?
                //p.isPopOK = true;
                //p.AI.FirstAction();
                //p.ReStart();
            });
            if (actScene.fixChara != null)
            {
                actScene.fixChara.SetActive(actScene.fixChara.mapNo == actScene.Map.no);
            }

            // Fade back to gameplay, assuming the script faded to loading screen, does nothing otherwise
            yield return(Scene.sceneFadeCanvas.StartFadeAysnc(FadeCanvas.Fade.Out).ToCoroutine(UnityEngine.Debug.LogException));

            Scene.sceneFadeCanvas.DefaultColor();
            yield return(Illusion.Game.Utils.Sound.GetFadePlayerWhileNull(prevBGM, prevVolume).ToCoroutine(UnityEngine.Debug.LogException));

            //actScene.Cycle.AddTimer(0.25f);

            // Reenable player controls
            actScene.Player.isActionNow = false;
        }
示例#2
0
        /// <summary>
        /// Start a new ADV event. Can be used in roaming mode and some other cases. Do not use in TalkScenes, use <see cref="StartTextSceneEvent"/> instead.
        /// Can get variables set in the commands through actScene.AdvScene.Scenario.Vars
        /// You can use Program.SetParam(player, list) to add all parameters related to player/heroine. Alternatively use the CharaChange command.
        /// </summary>
        /// <param name="list">List of commands for the event</param>
        /// <param name="dialogOnly">If true, only open a dialog box with everything else still running. If false, open a full ADV scene with other NPCs paused and such.</param>
        /// <param name="extraData">Event called when running the scene and fade, also fade type</param>
        /// <param name="camera">Initial position and rotation of the camera</param>
        /// <param name="heroines">List of heroines to set in event context. They can be accessed from commands like CreateCharacter. If null, current Scenario.heroineList is used.</param>
        /// <param name="position">This is the position of the center character, NOT the camera. Camera is controlled separately and by default looks at the character from the front.</param>
        /// <param name="rotation">This is the rotation of the center character, NOT the camera. Camera is controlled separately and by default looks at the character from the front. Should only change Y rotation and keep XZ = 0.</param>
        public static IEnumerator StartAdvEvent(List <Program.Transfer> list, bool dialogOnly, Vector3 position, Quaternion rotation,
                                                Program.OpenDataProc extraData   = null, OpenData.CameraData camera = null,
                                                List <SaveData.Heroine> heroines = null)
        {
            if (list == null)
            {
                throw new ArgumentNullException(nameof(list));
            }

            if (KoikatuAPI.EnableDebugLogging && list.All(x => x.param.Command != Command.Close))
            {
                list.Add(Program.Transfer.Close());
                UnityEngine.Debug.LogWarning("No Close command in the transfer list! Adding close at end to prevent lockup. Add Program.Transfer.Close() at the end of list to fix this.");
            }

            var game     = Singleton <Game> .Instance;
            var actScene = game.actScene;

            // has to be at start of list
            //var player = game.saveData.player;
            //Program.SetParam(player, list);

            var prevBgm    = string.Empty;
            var prevVolume = 1f;

            yield return(new WaitWhile(() => Singleton <Scene> .Instance.IsNowLoadingFade));

            yield return(null);

            // Set up full adv scene with disabled NPCs, custom bgm and such
            if (!dialogOnly)
            {
                actScene.SetPropertyValue("_isInChargeBGM", true);
                actScene.Player.isActionNow = true;
                actScene.SetPropertyValue("isEventNow", true);
                actScene.Player.isLesMotionPlay = false;
                actScene.SetPropertyValue("shortcutKey", false);
                yield return(Illusion.Game.Utils.Sound.GetBGMandVolume(delegate(string bgm, float volume)
                {
                    prevBgm = bgm;
                    prevVolume = volume;
                }));

                yield return(null);

                yield return(new WaitUntil(() => Singleton <Scene> .Instance.AddSceneName.IsNullOrEmpty()));

                yield return(Scene.Instance.Fade(SimpleFade.Fade.In));

                actScene.Player.SetActive(false);
                actScene.npcList.ForEach(delegate(NPC p) { p.SetActive(false); });
                actScene.npcList.ForEach(delegate(NPC p) { p.Pause(true); });
            }

            if (dialogOnly)
            {
                if (camera == null)
                {
                    camera = new OpenData.CameraData
                    {
                        position = actScene.cameraTransform.position,
                        rotation = actScene.cameraTransform.rotation
                    };
                }
            }
            else
            {
                list.Insert(0, Program.Transfer.Create(false, Command.CameraSetFov, "23"));
            }

            if (extraData == null)
            {
                extraData = new Program.OpenDataProc();
                //if (!dialogOnly) extraData.fadeType = Scene.Data.FadeType.InOut; // does nothing by itself
            }
            var isOpenAdv = false;

            extraData.onLoad += () =>
            {
                isOpenAdv = true;
                // Hack to fade back in
                if (!dialogOnly)
                {
                    KoikatuAPI.Instance.StartCoroutine(Scene.Instance.Fade(SimpleFade.Fade.Out));
                }
            };
            var prevNowScene = actScene.AdvScene.nowScene;

            actScene.AdvScene.nowScene = KoikatuAPI.Instance;
            //var position = actScene.Player.position;
            //var rotation = actScene.Player.rotation;
            yield return(Program.Open(new Data
            {
                fadeInTime = 0f, // setting to other than 0 breaks everything
                position = position,
                rotation = rotation,
                camera = camera,
                heroineList = heroines ?? actScene.AdvScene.Scenario.heroineList,
                scene = actScene,
                transferList = list
            }, extraData).PreventFromCrashing());

            yield return(new WaitUntil(() => isOpenAdv));

            yield return(Program.Wait(string.Empty));

            actScene.AdvScene.nowScene = prevNowScene;

            // Restore the game to normal after the full ADV scene
            if (!dialogOnly)
            {
                actScene.npcList.ForEach(delegate(NPC p) { p.SetActive(p.mapNo == actScene.Map.no); });
                actScene.npcList.ForEach(delegate(NPC p)
                {
                    p.Pause(false);
                    p.isPopOK = true;
                    p.AI.FirstAction();
                    p.ReStart();
                });
                yield return(new WaitUntil(() => actScene.MiniMapAndCameraActive));

                if (Illusion.Game.Utils.Scene.IsFadeOutOK)
                {
                    yield return(Singleton <Scene> .Instance.Fade(SimpleFade.Fade.Out)); //todo not necessary?
                }
                yield return(Illusion.Game.Utils.Sound.GetFadePlayerWhileNull(prevBgm, prevVolume));

                actScene.SetPropertyValue("shortcutKey", true);
                actScene.SetPropertyValue("_isInChargeBGM", false);
                actScene.SetPropertyValue("isEventNow", false);
                actScene.Player.isActionNow = false;
                actScene.Player.move.isStop = false;
                actScene.Player.isPopOK     = true;
                actScene.Player.SetActive(true);
            }
        }