Beispiel #1
0
 protected void OnGameLoaded()
 {
     if (game.State.Mode == GameMode.Tier)
     {
         tierState = Context.TierState;
     }
 }
Beispiel #2
0
 public override CriterionState Judge(TierState state)
 {
     if (state.Stages[StageIndex] == null || !state.Stages[StageIndex].IsCompleted)
     {
         return(CriterionState.Undetermined);
     }
     return(state.Stages[StageIndex].MaxCombo >= RequiredCombo ? CriterionState.Passed : CriterionState.Failed);
 }
 public override CriterionState Judge(TierState state)
 {
     if (!state.IsCompleted)
     {
         return(CriterionState.Undetermined);
     }
     return(state.AverageAccuracy >= RequiredAverageAccuracy ? CriterionState.Passed : CriterionState.Failed);
 }
Beispiel #4
0
 public override CriterionState Judge(TierState state)
 {
     if (!state.IsCompleted)
     {
         return(CriterionState.Undetermined);
     }
     return(state.Stages.Last().Health / state.Stages.Last().MaxHealth >= RequiredPercentage ? CriterionState.Passed : CriterionState.Failed);
 }
Beispiel #5
0
 public override CriterionState Judge(TierState state)
 {
     if (!state.IsCompleted)
     {
         return(CriterionState.Undetermined);
     }
     return(state.MaxCombo >= RequiredTotalCombo ? CriterionState.Passed : CriterionState.Failed);
 }
Beispiel #6
0
    public override CriterionState Judge(TierState state)
    {
        var count = state.GradeCounts[NoteGrade.Good];

        if (count > MaxCount)
        {
            return(CriterionState.Failed);
        }
        return(state.IsCompleted ? CriterionState.Passed : CriterionState.Undetermined);
    }
    public override CriterionState Judge(TierState state)
    {
        if (state.Stages[StageIndex] == null)
        {
            return(CriterionState.Undetermined);
        }
        var count = state.Stages[StageIndex].GradeCounts[NoteGrade.Bad] + state.Stages[StageIndex].GradeCounts[NoteGrade.Miss];

        if (count > MaxCount)
        {
            return(CriterionState.Failed);
        }
        return(state.Stages[StageIndex].IsCompleted ? CriterionState.Passed : CriterionState.Undetermined);
    }
Beispiel #8
0
 public abstract CriterionState Judge(TierState state);
Beispiel #9
0
    public async UniTask Initialize(bool startAutomatically = true)
    {
        ObjectPool = new ObjectPool(this);

        // Decide game mode
        var mode = Context.SelectedGameMode;

        if (mode == GameMode.Unspecified)
        {
            if (EditorGameMode != GameMode.Unspecified)
            {
                mode = EditorGameMode;
            }
            else
            {
                throw new Exception("Game mode not specified");
            }
        }

        if (mode == GameMode.Tier)
        {
            var tierState = Context.TierState;
            if (tierState == null)
            {
                await Context.LevelManager.LoadLevelsOfType(LevelType.Tier);

                tierState         = new TierState(MockData.Season.tiers[0]);
                Context.TierState = tierState;
            }

            if (tierState.IsFailed || tierState.IsCompleted)
            {
                // Reset tier state
                tierState         = new TierState(tierState.Tier);
                Context.TierState = tierState;
            }

            tierState.CurrentStageIndex++;

            Level      = tierState.Tier.Meta.stages[Context.TierState.CurrentStageIndex].ToLevel(LevelType.Tier);
            Difficulty = Difficulty.Parse(Level.Meta.charts.Last().type);
        }
        else if (mode == GameMode.GlobalCalibration)
        {
            // Load global calibration level
            Level = await Context.LevelManager.LoadOrInstallBuiltInLevel(BuiltInData.GlobalCalibrationModeLevelId,
                                                                         LevelType.Temp);

            Difficulty = Level.Meta.GetEasiestDifficulty();

            // Initialize global calibrator
            globalCalibrator = new GlobalCalibrator(this);
        }
        else
        {
            if (Context.SelectedLevel == null && Application.isEditor)
            {
                // Load test level
                await Context.LevelManager.LoadFromMetadataFiles(LevelType.User, new List <string>
                {
                    $"{Context.UserDataPath}/{EditorDefaultLevelDirectory}/level.json"
                });

                Context.SelectedLevel      = Context.LevelManager.LoadedLocalLevels.Values.First();
                Context.SelectedDifficulty = Context.SelectedLevel.Meta.GetHardestDifficulty();
            }

            Level      = Context.SelectedLevel;
            Difficulty = Context.SelectedDifficulty;
        }

        onGameReadyToLoad.Invoke(this);

        await Resources.UnloadUnusedAssets();

        // Load chart
        print("Loading chart");
        var    chartMeta = Level.Meta.GetChartSection(Difficulty.Id);
        var    chartPath = "file://" + Level.Path + chartMeta.path;
        string chartText;

        using (var request = UnityWebRequest.Get(chartPath))
        {
            await request.SendWebRequest();

            if (request.isNetworkError || request.isHttpError)
            {
                Debug.LogError(request.error);
                throw new Exception($"Failed to download chart from {chartPath}");
            }

            chartText = Encoding.UTF8.GetString(request.downloadHandler.data);
        }

        var mods = new HashSet <Mod>(Context.SelectedMods);

        if (Application.isEditor && EditorForceAutoMod)
        {
            mods.Add(Mod.Auto);
        }

        Chart = new Chart(
            chartText,
            mods.Contains(Mod.FlipX) || mods.Contains(Mod.FlipAll),
            mods.Contains(Mod.FlipY) || mods.Contains(Mod.FlipAll),
            true,
            Context.Player.Settings.UseExperimentalNoteAr,
            mods.Contains(Mod.Fast) ? 1.5f : (mods.Contains(Mod.Slow) ? 0.75f : 1),
            camera.orthographicSize
            );
        ChartLength = Chart.Model.note_list.Max(it => it.end_time);
        foreach (var type in (NoteType[])Enum.GetValues(typeof(NoteType)))
        {
            ObjectPool.UpdateNoteObjectCount(type, Chart.MaxSamePageNoteCountByType[type] * 3);
        }

        // Load audio
        print("Loading audio");
        AudioListener.pause = false;

        if (Context.AudioManager == null)
        {
            await UniTask.WaitUntil(() => Context.AudioManager != null);
        }
        Context.AudioManager.Initialize();
        var audioPath = "file://" + Level.Path + Level.Meta.GetMusicPath(Difficulty.Id);
        var loader    = new AudioClipLoader(audioPath);
        await loader.Load();

        if (loader.Error != null)
        {
            Debug.LogError(loader.Error);
            throw new Exception($"Failed to download audio from {audioPath}");
        }

        Music       = Context.AudioManager.Load("Level", loader.AudioClip, false, false, true);
        MusicLength = Music.Length;

        // Load storyboard
        StoryboardPath =
            Level.Path + (chartMeta.storyboard != null ? chartMeta.storyboard.path : "storyboard.json");

        if (File.Exists(StoryboardPath))
        {
            // Initialize storyboard
            // TODO: Why File.ReadAllText() works but not UnityWebRequest?
            // (UnityWebRequest downloaded text could not be parsed by Newtonsoft.Json)
            try
            {
                var storyboardText = File.ReadAllText(StoryboardPath);
                Storyboard = new Cytoid.Storyboard.Storyboard(this, storyboardText);
                Storyboard.Parse();
                await Storyboard.Initialize();

                print($"Loaded storyboard from {StoryboardPath}");
            }
            catch (Exception e)
            {
                Debug.LogError(e);
                Debug.LogError("Could not load storyboard.");
            }
        }

        // Load hit sound
        if (Context.Player.Settings.HitSound != "none")
        {
            var resource = await Resources.LoadAsync <AudioClip>("Audio/HitSounds/" + Context.Player.Settings.HitSound);

            Context.AudioManager.Load("HitSound", resource as AudioClip, isResource: true);
        }

        // State & config
        State             = new GameState(this, mode, mods);
        Context.GameState = State;

        if (Application.isEditor)
        {
            Debug.Log("Chart checksum: " + State.ChartChecksum);
        }

        Config = new GameConfig(this);

        // Touch handlers
        if (mode != GameMode.GlobalCalibration && !State.Mods.Contains(Mod.Auto))
        {
            inputController.EnableInput();
        }

        // System config
        Application.targetFrameRate = 120;
        Context.SetAutoRotation(false);

        // Update last played time
        Level.Record.LastPlayedDate = DateTimeOffset.UtcNow;
        Level.SaveRecord();

        // Initialize note pool
        ObjectPool.Initialize();

        IsLoaded = true;
        if (mode != GameMode.GlobalCalibration)
        {
            Context.ScreenManager.ChangeScreen(OverlayScreen.Id, ScreenTransition.None);
        }

        onGameLoaded.Invoke(this);

        levelInfoParent.transform.RebuildLayout();

        if (startAutomatically)
        {
            StartGame();
        }
    }
Beispiel #10
0
    public override async void OnScreenBecameActive()
    {
        base.OnScreenBecameActive();

        // TODO: Most code here is the same as the one in ResultScreen.cs. Refactor?

        gameState         = Context.GameState;
        Context.GameState = null;

        // Load translucent cover
        var path = "file://" + gameState.Level.Path + gameState.Level.Meta.background.path;

        TranslucentCover.Set(await Context.AssetMemory.LoadAsset <Sprite>(path, AssetTag.GameCover));

        // Update performance info
        scoreText.text    = Mathf.FloorToInt((float)gameState.Score).ToString("D6");
        accuracyText.text = "RESULT_X_ACCURACY".Get((Math.Floor(gameState.Accuracy * 100 * 100) / 100).ToString("0.00"));
        if (Mathf.Approximately((float)gameState.Accuracy, 1))
        {
            accuracyText.text = "RESULT_FULL_ACCURACY".Get();
        }
        maxComboText.text = "RESULT_X_COMBO".Get(gameState.MaxCombo);
        if (gameState.GradeCounts[NoteGrade.Bad] == 0 && gameState.GradeCounts[NoteGrade.Miss] == 0)
        {
            maxComboText.text = "RESULT_FULL_COMBO".Get();
        }

        var scoreGrade = ScoreGrades.From(gameState.Score);

        gradeText.text = scoreGrade.ToString();
        gradeText.GetComponent <GradientMeshEffect>().SetGradient(scoreGrade.GetGradient());
        if (scoreGrade == ScoreGrade.MAX || scoreGrade == ScoreGrade.SSS)
        {
            scoreText.GetComponent <GradientMeshEffect>().SetGradient(scoreGrade.GetGradient());
        }
        else
        {
            scoreText.GetComponent <GradientMeshEffect>().enabled = false;
        }

        standardMetricText.text = $"<b>Perfect</b> {gameState.GradeCounts[NoteGrade.Perfect]}    " +
                                  $"<b>Great</b> {gameState.GradeCounts[NoteGrade.Great]}    " +
                                  $"<b>Good</b> {gameState.GradeCounts[NoteGrade.Good]}    " +
                                  $"<b>Bad</b> {gameState.GradeCounts[NoteGrade.Bad]}    " +
                                  $"<b>Miss</b> {gameState.GradeCounts[NoteGrade.Miss]}";
        advancedMetricText.text = $"<b>Early</b> {gameState.EarlyCount}    " +
                                  $"<b>Late</b> {gameState.LateCount}    " +
                                  $"<b>{"RESULT_AVG_TIMING_ERR".Get()}</b> {gameState.AverageTimingError:0.000}s    " +
                                  $"<b>{"RESULT_STD_TIMING_ERR".Get()}</b> {gameState.StandardTimingError:0.000}s";
        if (!Context.Player.Settings.DisplayEarlyLateIndicators)
        {
            advancedMetricText.text = "";
        }

        tierState     = Context.TierState;
        modeText.text = tierState.Tier.Meta.name;

        var stage = tierState.CurrentStage;

        difficultyBall.SetModel(stage.Difficulty, stage.DifficultyLevel);
        stageTitleText.text = stage.Level.Meta.title;

        if (tierState.IsFailed)
        {
            retryButton.StartPulsing();
        }
        else
        {
            nextStageButton.StartPulsing();
        }

        nextStageButton.State = tierState.IsFailed ? CircleButtonState.GoBack : (tierState.IsCompleted ? CircleButtonState.Finish : CircleButtonState.NextStage);
        nextStageButton.interactableMonoBehavior.onPointerClick.SetListener(_ =>
        {
            Context.Haptic(HapticTypes.SoftImpact, true);
            nextStageButton.StopPulsing();
            if (tierState.IsFailed)
            {
                GoBack();
            }
            else
            {
                if (tierState.IsCompleted)
                {
                    Finish();
                }
                else
                {
                    NextStage();
                }
            }
        });
        retryButton.State = CircleButtonState.Retry;
        retryButton.interactableMonoBehavior.onPointerClick.SetListener(_ =>
        {
            Context.Haptic(HapticTypes.SoftImpact, true);
            retryButton.StopPulsing();
            Retry();
        });

        // Update criterion entries
        foreach (Transform child in criterionEntryHolder.transform)
        {
            Destroy(child.gameObject);
        }

        foreach (var criterion in Context.TierState.Criteria)
        {
            var entry = Instantiate(criterionEntryPrefab, criterionEntryHolder.transform)
                        .GetComponent <CriterionEntry>();
            entry.SetModel(criterion.Description, criterion.Judge(Context.TierState));
        }

        criterionEntryHolder.transform.RebuildLayout();

        NavigationBackdrop.Instance.Apply(it =>
        {
            it.IsBlurred = true;
            it.FadeBrightness(1, 0.8f);
        });
        ProfileWidget.Instance.Enter();

        if (tierState.IsFailed)
        {
            Toast.Next(Toast.Status.Failure, "TOAST_TIER_FAILED".Get());
        }

        if (!tierState.IsCompleted && !tierState.IsFailed)
        {
            proceedToNextStageTime     = DateTimeOffset.UtcNow.AddSeconds(IntermissionSeconds);
            nextBroadcastCountdownTime = DateTimeOffset.UtcNow;
        }
        else
        {
            proceedToNextStageTime     = DateTimeOffset.MaxValue;
            nextBroadcastCountdownTime = DateTimeOffset.MaxValue;
        }
    }
Beispiel #11
0
    public override async void OnScreenBecameActive()
    {
        base.OnScreenBecameActive();

        tierState = Context.TierState;
        if (tierState == null)
        {
            tierState = new TierState(MockData.Season.tiers[0])
            {
                Combo             = 1,
                CurrentStageIndex = 0,
                Health            = 1000.0,
                IsFailed          = false,
                MaxCombo          = 1,
                Stages            = new[]
                {
                    new GameState()
                }
            };
            tierState.OnComplete();
        }
        Context.TierState = null;

        nextButton.State = CircleButtonState.Next;
        nextButton.StartPulsing();
        nextButton.interactableMonoBehavior.onPointerClick.SetListener(_ =>
        {
            Context.Haptic(HapticTypes.SoftImpact, true);
            nextButton.StopPulsing();
            Done();
        });
        retryButton.State = CircleButtonState.Retry;
        retryButton.interactableMonoBehavior.onPointerClick.SetListener(_ =>
        {
            Context.Haptic(HapticTypes.SoftImpact, true);
            retryButton.StopPulsing();
            Retry();
        });

        // Update performance info
        scoreText.text    = (Mathf.Floor((float)tierState.Completion * 100 * 100) / 100).ToString("0.00") + "%";
        accuracyText.text = "RESULT_X_ACCURACY".Get((Math.Floor(tierState.AverageAccuracy * 100 * 100) / 100).ToString("0.00"));
        if (Mathf.Approximately((float)tierState.AverageAccuracy, 1))
        {
            accuracyText.text = "RESULT_FULL_ACCURACY".Get();
        }
        maxComboText.text = "RESULT_X_COMBO".Get(tierState.MaxCombo);
        if (tierState.GradeCounts[NoteGrade.Bad] == 0 && tierState.GradeCounts[NoteGrade.Miss] == 0)
        {
            maxComboText.text = "RESULT_FULL_COMBO".Get();
        }

        var scoreGrade = ScoreGrades.FromTierCompletion(tierState.Completion);

        gradeText.text = scoreGrade.ToString();
        gradeText.GetComponent <GradientMeshEffect>().SetGradient(scoreGrade.GetGradient());
        if (scoreGrade == ScoreGrade.MAX || scoreGrade == ScoreGrade.SSS)
        {
            scoreText.GetComponent <GradientMeshEffect>().SetGradient(scoreGrade.GetGradient());
        }
        else
        {
            scoreText.GetComponent <GradientMeshEffect>().enabled = false;
        }

        standardMetricText.text = $"<b>Perfect</b> {tierState.GradeCounts[NoteGrade.Perfect]}    " +
                                  $"<b>Great</b> {tierState.GradeCounts[NoteGrade.Great]}    " +
                                  $"<b>Good</b> {tierState.GradeCounts[NoteGrade.Good]}    " +
                                  $"<b>Bad</b> {tierState.GradeCounts[NoteGrade.Bad]}    " +
                                  $"<b>Miss</b> {tierState.GradeCounts[NoteGrade.Miss]}";
        advancedMetricText.text = $"<b>Early</b> {tierState.EarlyCount}    " +
                                  $"<b>Late</b> {tierState.LateCount}    " +
                                  $"<b>{"RESULT_AVG_TIMING_ERR".Get()}</b> {tierState.AverageTimingError:0.000}s    " +
                                  $"<b>{"RESULT_STD_TIMING_ERR".Get()}</b> {tierState.StandardTimingError:0.000}s";
        if (!Context.Player.Settings.DisplayEarlyLateIndicators)
        {
            advancedMetricText.text = "";
        }

        newBestText.text = "";
        if (tierState.Completion >= 1)
        {
            if (tierState.Tier.completion < 1)
            {
                newBestText.text = "TIER_CLEARED".Get();
            }
            else
            {
                var historicBest = tierState.Tier.completion;
                var newBest      = tierState.Completion;
                if (newBest > historicBest)
                {
                    tierState.Tier.completion = tierState.Completion; // Update cached tier json
                    newBestText.text          =
                        $"+{(Mathf.FloorToInt((float) (newBest - historicBest) * 100 * 100) / 100f):0.00}%";
                }
            }
        }

        shareButton.onPointerClick.SetListener(_ => StartCoroutine(Share()));

        gradientPane.SetModel(tierState.Tier);
        for (var index = 0; index < Math.Min(tierState.Tier.Meta.parsedStages.Count, 3); index++)
        {
            var widget      = stageResultWidgets[index];
            var level       = tierState.Tier.Meta.parsedStages[index];
            var stageResult = tierState.Stages[index];
            widget.difficultyBall.SetModel(Difficulty.Parse(level.Meta.charts.Last().type), level.Meta.charts.Last().difficulty);
            widget.titleText.text = level.Meta.title;
            widget.performanceWidget.SetModel(new LevelRecord.Performance
            {
                Score = (int)stageResult.Score, Accuracy = stageResult.Accuracy
            });
        }

        ProfileWidget.Instance.Enter();
        upperRightColumn.Enter();

        UploadRecord();
    }
Beispiel #12
0
 public override CriterionState Judge(TierState state)
 {
     if (!state.IsCompleted) return CriterionState.Undetermined;
     return state.Stages.Sum(it => it.Score) >= RequiredTotalScore ? CriterionState.Passed : CriterionState.Failed;
 }