public async UniTask <GameStateMap> SaveGameAsync(string slotId)
        {
            if (rollbackStack.Count == 0 || rollbackStack.Peek() is null)
            {
                PushRollbackSnapshot(false);
            }

            var quick = slotId.StartsWithFast(Configuration.QuickSaveSlotMask.GetBefore("{"));

            OnGameSaveStarted?.Invoke(new GameSaveLoadArgs(slotId, quick));

            var state = new GameStateMap(rollbackStack.Peek());

            state.SaveDateTime = DateTime.Now;
            state.Thumbnail    = Engine.GetService <ICameraManager>().CaptureThumbnail();

            var lastZero = rollbackStack.FirstOrDefault(s => s.PlaybackSpot.InlineIndex == 0); // Required when changing locale.

            bool filter(GameStateMap s) => (Configuration.EnableStateRollback && Configuration.SavedRollbackSteps > 0 && s.PlayerRollbackAllowed) || s == lastZero;

            state.RollbackStackJson = rollbackStack.ToJson(Configuration.SavedRollbackSteps, filter);

            await GameStateSlotManager.SaveAsync(slotId, state);

            // Also save global state on every game save.
            await SaveGlobalStateAsync();

            OnGameSaveFinished?.Invoke(new GameSaveLoadArgs(slotId, quick));

            return(state);
        }
        public async UniTask ResetStateAsync(string[] exclude = null, params Func <UniTask>[] tasks)
        {
            OnResetStarted?.Invoke();

            if (Configuration.LoadStartDelay > 0)
            {
                await UniTask.Delay(TimeSpan.FromSeconds(Configuration.LoadStartDelay));
            }

            Engine.GetService <IScriptPlayer>()?.Playlist?.ReleaseResources();

            if (exclude != null && exclude.Length > 0)
            {
                Engine.Reset(exclude.Select(typeName => Type.GetType($"Naninovel.{typeName}", true, true)).ToArray());
            }
            else
            {
                Engine.Reset();
            }

            await Resources.UnloadUnusedAssets();

            if (tasks != null)
            {
                foreach (var task in tasks)
                {
                    await task?.Invoke();
                }
            }

            OnResetFinished?.Invoke();
        }
Example #3
0
        protected override void Awake()
        {
            base.Awake();

            this.AssertRequiredObjects(revealableText);
            printerManager = Engine.GetService <ITextPrinterManager>();
        }
Example #4
0
        protected override void Awake()
        {
            base.Awake();
            this.AssertRequiredObjects(thumbnailImage, lockedTexture);

            unlockableManager = Engine.GetService <UnlockableManager>();
        }
Example #5
0
        public virtual UniTask InitializeServiceAsync()
        {
            scriptPlayer  = Engine.GetService <IScriptPlayer>();
            cameraManager = Engine.GetService <ICameraManager>();

            return(UniTask.CompletedTask);
        }
Example #6
0
        private async void PlayScriptAsync()
        {
            if (!string.IsNullOrEmpty(scriptName))
            {
                var player = Engine.GetService <IScriptPlayer>();
                if (player is null)
                {
                    Debug.LogError($"Failed to play a script via `{nameof(PlayScript)}` component attached to `{gameObject.name}` game object: script player service is not available.");
                    return;
                }
                await player.PreloadAndPlayAsync(scriptName);

                return;
            }

            if (!string.IsNullOrWhiteSpace(scriptText))
            {
                var text     = string.IsNullOrEmpty(argument) ? scriptText : scriptText.Replace("{arg}", argument);
                var script   = Script.FromScriptText($"`{name}` generated script", text);
                var playlist = new ScriptPlaylist(script);
                foreach (var command in playlist)
                {
                    if (command.ShouldExecute)
                    {
                        await command.ExecuteAsync();
                    }
                }
            }
        }
        private void OnDisable()
        {
            var localeManager = Engine.GetService <LocalizationManager>();

            if (localeManager != null)
            {
                localeManager.OnLocaleChanged -= HandleLocaleChanged;
            }
        }
        public static void ToggleScriptNavigator()
        {
            var scriptManager = Engine.GetService <IScriptManager>();

            if (scriptManager.ScriptNavigator)
            {
                scriptManager.ScriptNavigator.ToggleVisibility();
            }
        }
Example #9
0
 public CharacterLipSyncer(string authorId, Action <bool> setIsSpeaking)
 {
     this.authorId      = authorId;
     this.setIsSpeaking = setIsSpeaking;
     audioManager       = Engine.GetService <IAudioManager>();
     textPrinterManager = Engine.GetService <ITextPrinterManager>();
     textPrinterManager.OnPrintTextStarted += HandlePrintTextStarted;
     setIsSpeaking.Invoke(false);
 }
        private void HandleEngineInitialized()
        {
            Engine.OnInitializationFinished -= HandleEngineInitialized;

            textManager         = Engine.GetService <ITextManager>();
            localizationManager = Engine.GetService <ILocalizationManager>();
            localizationManager.OnLocaleChanged += HandleLocalizationChanged;

            InvokeValueChanged();
        }
Example #11
0
        /// <summary>
        /// Loads the provided script, preloads the script's commands and starts playing at the provided line and inline indexes or a label;
        /// when <paramref name="label"/> is provided, will ignore line and inline indexes.
        /// </summary>
        /// <param name="scriptName">Name (resource path) of the script to load and play.</param>
        /// <param name="startLineIndex">Line index to start playback from.</param>
        /// <param name="startInlineIndex">Command inline index to start playback from.</param>
        /// <param name="label">Name of a label within the script to start playback from.</param>
        public static async UniTask PreloadAndPlayAsync(this IScriptPlayer scriptPlayer, string scriptName, int startLineIndex = 0, int startInlineIndex = 0, string label = null)
        {
            var script = await Engine.GetService <IScriptManager>().LoadScriptAsync(scriptName);

            if (script is null)
            {
                throw new Exception($"Script player failed to start: script with name `{scriptName}` wasn't able to load.");
            }
            await scriptPlayer.PreloadAndPlayAsync(script, startLineIndex, startInlineIndex, label);
        }
Example #12
0
 public void DestroyService()
 {
     if (config.StateRollbackMode != StateRollbackMode.Disabled)
     {
         Engine.GetService <ScriptPlayer>()?.RemovePreExecutionTask(PushRollbackSnapshotAsync);
         if (inputManager?.Rollback != null)
         {
             inputManager.Rollback.OnStart -= HandleRollbackInputStart;
         }
     }
 }
Example #13
0
        public static async void Rewind(int line)
        {
            line = Mathf.Clamp(line, 1, int.MaxValue);
            var player = Engine.GetService <ScriptPlayer>();
            var ok     = await player.RewindAsync(line - 1);

            if (!ok)
            {
                Debug.LogWarning($"Failed to rewind to line #{line} of script `{player.PlayedScript?.Name}`. Make sure the line exists in the script and it's playable (either a command or a generic text line). When rewinding forward, `@stop` commands can prevent reaching the target line. When rewinding backward the target line should've been previously played and be kept in the rollback stack (capacity controlled by `{nameof(StateConfiguration.StateRollbackSteps)}` property in state configuration).");
            }
        }
        public void DestroyService()
        {
            Engine.GetService <IScriptPlayer>()?.RemovePreExecutionTask(HandleCommandPreExecution);

            if (rollbackInput != null)
            {
                rollbackInput.OnStart -= HandleRollbackInputStart;
            }

            Engine.RemovePostInitializationTask(InitializeStateAsync);
        }
            public static float CalculateProgress()
            {
                var player = Engine.GetService <ScriptPlayer>();

                if (player.TotalCommandsCount == 0)
                {
                    Debug.LogWarning("`CalculateProgress` script expression function were used, while to total number of script commands is zero. You've most likely disabled `UpdateActionCountOnInit` in the script player configuration menu or didn't add any naninovel sctipts to the project resources.");
                    return(0);
                }
                return(player.PlayedCommandsCount / (float)player.TotalCommandsCount);
            }
Example #16
0
 /// <summary>
 /// Creates the matcher for a background actor with the provided metadata and renderer.
 /// Will return null in case matcher is not required based on the actor configuration.
 /// </summary>
 public static BackgroundMatcher CreateFor(BackgroundMetadata metadata, TransitionalRenderer renderer)
 {
     if (renderer is TransitionalSpriteRenderer spriteRenderer && metadata.MatchMode != CameraMatchMode.Disable)
     {
         var cameraManager = Engine.GetService <ICameraManager>();
         var matcher       = new BackgroundMatcher(cameraManager, spriteRenderer, metadata);
         matcher.Start();
         return(matcher);
     }
     return(null);
 }
Example #17
0
        protected override void OnDisable()
        {
            base.OnDisable();

            var localeManager = Engine.GetService <LocalizationManager>();

            if (localeManager != null)
            {
                localeManager.OnLocaleChanged -= HandleLocaleChanged;
            }
        }
Example #18
0
        public override void RemoveActor(string actorId)
        {
            base.RemoveActor(actorId);

            // Unload generic character's resources.
            var meta = GetActorMetadata(actorId);

            if (!string.IsNullOrEmpty(meta.MessageSound))
            {
                Engine.GetService <AudioManager>().ReleaseAudioResources(this, meta.MessageSound);
            }
        }
Example #19
0
        protected override void Awake()
        {
            base.Awake();
            this.AssertRequiredObjects(ThumbnailImage, LockedTexture);

            unlockableManager      = Engine.GetService <IUnlockableManager>();
            localizationManager    = Engine.GetService <ILocalizationManager>();
            ThumbnailImage.texture = LoadingTexture;

            unlockableManager.OnItemUpdated     += HandleItemUpdated;
            localizationManager.OnLocaleChanged += HandleLocaleChanged;
        }
Example #20
0
        public override async Task InitializeServiceAsync()
        {
            await base.InitializeServiceAsync();

            var providerMngr = Engine.GetService <ResourceProviderManager>();

            avatarTextureLoader = new LocalizableResourceLoader <Texture2D>(Configuration.AvatarLoader, providerMngr, localizationManager);

            textPrinterManager.OnPrintTextStarted += HandlePrintTextStarted;

            // TODO: Load only the required avatar textures.
            await avatarTextureLoader.LoadAllAsync();
        }
        public UniTask InitializeServiceAsync()
        {
            Engine.GetService <IScriptPlayer>().AddPreExecutionTask(HandleCommandPreExecution);

            rollbackInput = Engine.GetService <IInputManager>().GetRollback();

            if (rollbackInput != null)
            {
                rollbackInput.OnStart += HandleRollbackInputStart;
            }

            return(UniTask.CompletedTask);
        }
Example #22
0
        public async Task InitializeServiceAsync()
        {
            SettingsState = await LoadSettingsAsync();

            GlobalState = await LoadGlobalStateAsync();

            Engine.GetService <ScriptPlayer>()?.AddPreExecutionTask(PushRollbackSnapshotAsync);

            if (inputManager?.Rollback != null)
            {
                inputManager.Rollback.OnStart += HandleRollbackInputStart;
            }
        }
Example #23
0
        public static bool HasPlayed()
        {
            var player = Engine.GetService <IScriptPlayer>();

            if (player?.PlayedScript is null)
            {
                return(false);
            }

            var playedScriptName = player.PlayedScript.Name;
            var playedIndex      = player.PlayedIndex;

            return(player.HasPlayed(playedScriptName, playedIndex));
        }
Example #24
0
        public override async UniTask InitializeServiceAsync()
        {
            await base.InitializeServiceAsync();

            var providerManager = Engine.GetService <IResourceProviderManager>();

            avatarTextureLoader = Configuration.AvatarLoader.CreateFor <Texture2D>(providerManager);

            textPrinterManager.OnPrintTextStarted += HandleAuthorHighlighting;

            // Loading only the required avatar resources is not possible, as we can't use async to provide them later.
            // In case of heavy usage of the avatar resources, consider using `render character to texture` feature instead.
            await avatarTextureLoader.LoadAndHoldAllAsync(this);
        }
Example #25
0
        private void ContextMenu(ContextualMenuPopulateEvent evt)
        {
            var worldPos          = evt.mousePosition;
            var localPos          = linesContainer.WorldToLocal(worldPos);
            var nearLine          = FindLineNearPosition(worldPos);
            var nearLineViewIndex = linesContainer.IndexOf(nearLine);
            var insertViewIndex   = nearLine is null ? 0 : nearLine.layout.center.y > localPos.y ? nearLineViewIndex : nearLineViewIndex + 1;
            var insertIndex       = ViewToGlobalIndex(insertViewIndex);
            var hoveringLine      = nearLine != null && nearLine.ContainsPoint(new Vector2(nearLine.transform.position.x, nearLine.WorldToLocal(evt.mousePosition).y));

            if (config.HotReloadScripts || !EditorApplication.isPlayingOrWillChangePlaymode)
            {
                evt.menu.AppendAction("Insert...", _ => ShowSearcher(worldPos, insertIndex, insertViewIndex));
                evt.menu.AppendAction("Copy", _ => copiedLineText = nearLine?.GenerateLineText(), hoveringLine ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Disabled);
                evt.menu.AppendAction("Paste", _ => InsertLine(CreateLineView(insertIndex, copiedLineText), insertIndex, insertViewIndex), !string.IsNullOrEmpty(copiedLineText) ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Disabled);
                evt.menu.AppendAction("Remove", _ => {
                    RemoveLine(nearLine);
                    focusable = true;
                    Focus();
                    focusable = false;
                }, hoveringLine ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Disabled);
            }
            if (EditorApplication.isPlayingOrWillChangePlaymode && hoveringLine && (nearLine is CommandLineView || nearLine is GenericTextLineView))
            {
                var player       = Engine.GetService <IScriptPlayer>();
                var stateManager = Engine.GetService <IStateManager>();
                if (stateManager != null && player != null && player.PlayedScript != null && player.PlayedScript.Name == scriptAsset.name)
                {
                    var rewindIndex = ViewToGlobalIndex(nearLineViewIndex);
                    var status      = (rewindIndex > player.PlaybackSpot.LineIndex ||
                                       stateManager.CanRollbackTo(s => s.PlaybackSpot.ScriptName == player.PlayedScript.Name && s.PlaybackSpot.LineIndex == rewindIndex))
                        ? DropdownMenuAction.Status.Normal
                        : DropdownMenuAction.Status.Disabled;
                    evt.menu.AppendAction("Rewind", _ => player.RewindAsync(rewindIndex).Forget(), status);
                }
            }
            if (hoveringLine)
            {
                if (nearLine is CommandLineView cmdLine && (cmdLine.CommandId.EqualsFastIgnoreCase("goto") || cmdLine.CommandId.EqualsFastIgnoreCase("gosub")))
                {
                    var path = cmdLine.Q <LineTextField>().value;
                    evt.menu.AppendAction($"Open `{path}`", _ => HandleGoto(path));
                }
                else if (nearLine.Q("Content")?.Children().FirstOrDefault(c => c is LineTextField field && field.label.EqualsFastIgnoreCase("goto")) is LineTextField gotoField)
                {
                    evt.menu.AppendAction($"Open `{gotoField.value}`", _ => HandleGoto(gotoField.value));
                }

                evt.menu.AppendAction("Help", _ => OpenHelpFor(nearLine));
            }
Example #26
0
        public static void PurgeCache()
        {
            var manager = Engine.GetService <ResourceProviderManager>();

            if (manager is null)
            {
                Debug.LogError("Failed to retrieve provider manager."); return;
            }
            var googleDriveProvider = manager.GetProvider(ResourceProviderType.GoogleDrive) as UnityCommon.GoogleDriveResourceProvider;

            if (googleDriveProvider is null)
            {
                Debug.LogError("Failed to retrieve google drive provider."); return;
            }
            googleDriveProvider.PurgeCache();
        }
Example #27
0
        protected override async Task <ICharacterActor> ConstructActorAsync(string actorId)
        {
            var actor = await base.ConstructActorAsync(actorId);

            // When adding new character place it at the scene origin by default.
            actor.Position = GlobalSceneOrigin;

            // Preload generic character's resources.
            var meta = GetActorMetadata(actorId);

            if (!string.IsNullOrEmpty(meta.MessageSound))
            {
                await Engine.GetService <AudioManager>().HoldAudioResourcesAsync(this, meta.MessageSound);
            }

            return(actor);
        }
Example #28
0
        private void HandleEngineInitialized()
        {
            if (!(Engine.Behaviour is RuntimeBehaviour))
            {
                return;
            }

            var player = Engine.GetService <ScriptPlayer>();

            player.OnCommandExecutionStart += HighlightPlayedCommand;
            var stateManager = Engine.GetService <StateManager>();

            stateManager.OnRollbackFinished += () => HighlightPlayedCommand(player.PlayedCommand);
            if (player.PlayedCommand != null)
            {
                HighlightPlayedCommand(player.PlayedCommand);
            }
        }
        public override async UniTask InitializeServiceAsync()
        {
            await base.InitializeServiceAsync();

            var providerMngr = Engine.GetService <IResourceProviderManager>();

            avatarTextureLoader = Configuration.AvatarLoader.CreateLocalizableFor <Texture2D>(providerMngr, localizationManager);

            textPrinterManager.OnPrintTextStarted += HandleAuthorHighlighting;

            // TODO: Load only the required avatar textures.
            var avatarResources = await avatarTextureLoader.LoadAllAsync();

            foreach (var resource in avatarResources)
            {
                resource.Hold(this);
            }
        }
Example #30
0
        private async UniTask PerformPostEngineInitializationTasks()
        {
            await LoadSettingsAsync();

            if (!Engine.Initializing)
            {
                return;
            }
            await LoadGlobalAsync();

            if (!Engine.Initializing)
            {
                return;
            }

            if (Configuration.EnableStateRollback)
            {
                InitializeRollback();
            }

            async UniTask LoadSettingsAsync()
            {
                SettingsState = await SettingsSlotManager.LoadOrDefaultAsync(Configuration.DefaultSettingsSlotId);
                await LoadAllServicesFromStateAsync <IStatefulService <SettingsStateMap>, SettingsStateMap>(SettingsState);
            }

            async UniTask LoadGlobalAsync()
            {
                GlobalState = await GlobalSlotManager.LoadOrDefaultAsync(Configuration.DefaultGlobalSlotId);
                await LoadAllServicesFromStateAsync <IStatefulService <GlobalStateMap>, GlobalStateMap>(GlobalState);
            }

            void InitializeRollback()
            {
                scriptPlayer.AddPreExecutionTask(HandleCommandPreExecution);

                rollbackInput = Engine.GetService <IInputManager>().GetRollback();
                if (rollbackInput != null)
                {
                    rollbackInput.OnStart += HandleRollbackInputStart;
                }
            }
        }