/// <summary>
    /// Handle selecting the row when clicked
    /// </summary>
    /// <param name="row">UI row that was clicked on</param>
    private void OnClick(DifficultyRow row)
    {
        if (!diffs.ContainsKey(row.Name))
        {
            return;
        }

        DeselectDiff();

        // Select a difficulty
        selected = row;
        if (!loading)
        {
            selectedMemory[currentCharacteristic.beatmapCharacteristicName] = selected.Name;
        }
        var selImage = selected.Background;

        selImage.color = new Color(selImage.color.r, selImage.color.g, selImage.color.b, 1.0f);

        var diff = diffs[row.Name];

        BeatSaberSongContainer.Instance.difficultyData = diff.DifficultyBeatmap;

        njsField.text            = diff.NoteJumpMovementSpeed.ToString();
        songBeatOffsetField.text = diff.NoteJumpStartBeatOffset.ToString();
        envRemoval.UpdateFromDiff(diff.envRemoval);
    }
    /// <summary>
    /// Load song data and set up listeners on UI elements
    /// </summary>
    public void Start()
    {
        if (Song?.difficultyBeatmapSets != null)
        {
            Characteristics = Song.difficultyBeatmapSets.GroupBy(it => it.beatmapCharacteristicName).ToDictionary(
                characteristic => characteristic.Key,
                characteristic => characteristic.SelectMany(i => i.difficultyBeatmaps).GroupBy(map => map.difficulty).ToDictionary(
                    grouped => grouped.Key,
                    grouped => new DifficultySettings(grouped.First())
                    ),
                StringComparer.OrdinalIgnoreCase
                );
        }
        else
        {
            Characteristics = new Dictionary <string, Dictionary <string, DifficultySettings> >();
        }

        foreach (Transform child in transform)
        {
            // Add event listeners, it's hard to do this staticly as the rows are prefabs
            // so they can't access the parent object with this script
            var row = new DifficultyRow(child);
            rows.Add(row);

            row.Toggle.onValueChanged.AddListener((val) => OnChange(row, val));
            row.Button.onClick.AddListener(() => OnClick(row));
            row.NameInput.onValueChanged.AddListener((name) => OnValueChanged(row, name));
            row.Copy.onClick.AddListener(() => SetCopySource(row));
            row.Save.onClick.AddListener(() => SaveDiff(row));
            row.Revert.onClick.AddListener(() => Revertdiff(row));
            row.Paste.onClick.AddListener(() => DoPaste(row));
        }
    }
    /// <summary>
    /// Save the diff
    /// </summary>
    /// <param name="row">UI row that was clicked on</param>
    private void SaveDiff(DifficultyRow row)
    {
        var localDiff = diffs[row.Name];
        var firstSave = localDiff.ForceDirty;

        localDiff.Commit();
        row.ShowDirtyObjects(false, true);

        var Song = BeatSaberSongContainer.Instance.song;
        var diff = localDiff.DifficultyBeatmap;

        if (!Song.difficultyBeatmapSets.Contains(currentCharacteristic))
        {
            Song.difficultyBeatmapSets.Add(currentCharacteristic);
        }
        if (!currentCharacteristic.difficultyBeatmaps.Contains(diff))
        {
            currentCharacteristic.difficultyBeatmaps.Add(diff);
        }

        BeatSaberMap map = TryGetExistingMapFromDiff(diff) ?? new BeatSaberMap
        {
            mainNode = new JSONObject()
        };
        string oldPath = map?.directoryAndFile;

        diff.UpdateName();
        map.directoryAndFile = Path.Combine(Song.directory, diff.beatmapFilename);
        if (File.Exists(oldPath) && oldPath != map.directoryAndFile && !File.Exists(map.directoryAndFile))
        {
            if (firstSave)
            {
                File.Copy(oldPath, map.directoryAndFile);
            }
            else
            {
                File.Move(oldPath, map.directoryAndFile); //This should properly "convert" difficulties just fine
            }
        }
        else
        {
            map.Save();
        }

        diff.RefreshRequirementsAndWarnings(map);

        Song.SaveSong();
        characteristicSelect.Recalculate();

        Debug.Log("Saved " + row.Name);
    }
    /// <summary>
    /// Helper to deselect the currently selected row
    /// </summary>
    private void DeselectDiff()
    {
        if (selected != null)
        {
            var selImage = selected.Background;
            selImage.color = new Color(selImage.color.r, selImage.color.g, selImage.color.b, 0.0f);

            // Clean the UI, if we're selecting a new item they'll be repopulated
            BeatSaberSongContainer.Instance.difficultyData = null;
            njsField.text            = "";
            songBeatOffsetField.text = "";
            envRemoval.ClearList();
        }

        selected = null;
    }
    /// <summary>
    /// Revert the diff to the saved version
    /// </summary>
    /// <param name="obj">UI row that was clicked on</param>
    private void Revertdiff(DifficultyRow row)
    {
        var localDiff = diffs[row.Name];

        localDiff.Revert();

        row.NameInput.text = localDiff.CustomName;

        if (row == selected)
        {
            njsField.text            = localDiff.NoteJumpMovementSpeed.ToString();
            songBeatOffsetField.text = localDiff.NoteJumpStartBeatOffset.ToString();
            envRemoval.UpdateFromDiff(localDiff.envRemoval);
        }

        row.ShowDirtyObjects(localDiff);
    }
    /// <summary>
    /// Set the row as the source for a copy-paste operation
    /// </summary>
    /// <param name="row">UI row that was clicked on</param>
    private void SetCopySource(DifficultyRow row)
    {
        // If we copied from the current characteristic remove the highlight
        if (copySource != null && currentCharacteristic == copySource.Characteristic)
        {
            copySource.Obj.CopyImage.color = Color.white;
        }

        // Clicking twice on the same source removes it
        if (copySource != null && copySource.Obj == row && currentCharacteristic == copySource.Characteristic)
        {
            CancelCopy();
            return;
        }

        copySource = new CopySource(diffs[row.Name], currentCharacteristic, row);
        SetPasteMode(true);
        row.CopyImage.color = copyColor;
    }
    /// <summary>
    /// Handle deleting a difficulty that was previously saved
    /// </summary>
    /// <param name="row">UI row that was clicked on</param>
    /// <param name="r">Confirmation from the user</param>
    private void HandleDeleteDifficulty(DifficultyRow row, int r)
    {
        if (r == 1) // User canceled out
        {
            row.Toggle.isOn = true;
            return;
        }

        var diff = diffs[row.Name].DifficultyBeatmap;

        string fileToDelete = Song.GetMapFromDifficultyBeatmap(diff)?.directoryAndFile;

        if (File.Exists(fileToDelete))
        {
            FileOperationAPIWrapper.MoveToRecycleBin(fileToDelete);
        }

        // Remove status effects if present
        if (copySource != null && row == copySource.Obj && currentCharacteristic == copySource.Characteristic)
        {
            CancelCopy();
        }
        if (row == selected)
        {
            DeselectDiff();
        }

        currentCharacteristic.difficultyBeatmaps.Remove(diffs[row.Name].DifficultyBeatmap);
        if (currentCharacteristic.difficultyBeatmaps.Count == 0)
        {
            Song.difficultyBeatmapSets.Remove(currentCharacteristic);
        }

        diffs.Remove(row.Name);
        Song.SaveSong();

        row.SetInteractable(false);
        row.NameInput.text = "";
        row.ShowDirtyObjects(false, false);
        characteristicSelect.Recalculate();
    }
    /// <summary>
    /// Handle changes to the difficulty label
    /// </summary>
    /// <param name="row">UI row that was updated</param>
    /// <param name="difficultyLabel">New label value</param>
    private void OnValueChanged(DifficultyRow row, string difficultyLabel)
    {
        if (!diffs.ContainsKey(row.Name))
        {
            return;
        }

        var diff = diffs[row.Name];

        // Expert+ is special as the only difficulty that is different in JSON
        string defaultName = row.Name == "ExpertPlus" ? "Expert+" : row.Name;

        if (difficultyLabel != "" && difficultyLabel != defaultName)
        {
            diff.CustomName = difficultyLabel;
        }
        else
        {
            diff.CustomName = null;
        }

        row.ShowDirtyObjects(diff);
    }
Exemple #9
0
 public BeatmapDetails()
 {
     Children = new Drawable[]
     {
         new Box
         {
             RelativeSizeAxes = Axes.Both,
             Colour           = Color4.Black,
             Alpha            = 0.5f,
         },
         new FillFlowContainer <MetadataSegment>()
         {
             Anchor           = Anchor.TopRight,
             Origin           = Anchor.TopRight,
             RelativeSizeAxes = Axes.X,
             AutoSizeAxes     = Axes.Y,
             Width            = 0.4f,
             Direction        = FillDirection.Vertical,
             LayoutDuration   = 200,
             LayoutEasing     = EasingTypes.OutQuint,
             Children         = new[]
             {
                 description = new MetadataSegment("Description"),
                 source      = new MetadataSegment("Source"),
                 tags        = new MetadataSegment("Tags")
             },
         },
         new FillFlowContainer
         {
             RelativeSizeAxes = Axes.X,
             AutoSizeAxes     = Axes.Y,
             Width            = 0.6f,
             Direction        = FillDirection.Vertical,
             Spacing          = new Vector2(0, 15),
             Padding          = new MarginPadding(10)
             {
                 Top = 0
             },
             Children = new Drawable[]
             {
                 new Container
                 {
                     RelativeSizeAxes = Axes.X,
                     AutoSizeAxes     = Axes.Y,
                     Children         = new Drawable[]
                     {
                         new Box
                         {
                             RelativeSizeAxes = Axes.Both,
                             Colour           = Color4.Black,
                             Alpha            = 0.5f,
                         },
                         new FillFlowContainer
                         {
                             RelativeSizeAxes = Axes.X,
                             AutoSizeAxes     = Axes.Y,
                             Direction        = FillDirection.Vertical,
                             Spacing          = new Vector2(0, 5),
                             Padding          = new MarginPadding(10),
                             Children         = new[]
                             {
                                 circleSize        = new DifficultyRow("Circle Size", 7),
                                 drainRate         = new DifficultyRow("HP Drain"),
                                 overallDifficulty = new DifficultyRow("Accuracy"),
                                 approachRate      = new DifficultyRow("Approach Rate"),
                                 stars             = new DifficultyRow("Star Difficulty"),
                             },
                         },
                     },
                 },
                 ratingsContainer = new Container
                 {
                     RelativeSizeAxes = Axes.X,
                     AutoSizeAxes     = Axes.Y,
                     Alpha            = 0,
                     AlwaysPresent    = true,
                     Children         = new Drawable[]
                     {
                         new Box
                         {
                             RelativeSizeAxes = Axes.Both,
                             Colour           = Color4.Black,
                             Alpha            = 0.5f,
                         },
                         new FillFlowContainer
                         {
                             RelativeSizeAxes = Axes.X,
                             AutoSizeAxes     = Axes.Y,
                             Direction        = FillDirection.Vertical,
                             Padding          = new MarginPadding
                             {
                                 Top   = 25,
                                 Left  = 15,
                                 Right = 15,
                             },
                             Children = new Drawable[]
                             {
                                 new OsuSpriteText
                                 {
                                     Text   = "User Rating",
                                     Font   = @"Exo2.0-Medium",
                                     Anchor = Anchor.TopCentre,
                                     Origin = Anchor.TopCentre,
                                 },
                                 ratingsBar = new Bar
                                 {
                                     RelativeSizeAxes = Axes.X,
                                     Height           = 5,
                                 },
                                 new Container
                                 {
                                     RelativeSizeAxes = Axes.X,
                                     AutoSizeAxes     = Axes.Y,
                                     Children         = new[]
                                     {
                                         negativeRatings = new OsuSpriteText
                                         {
                                             Font = @"Exo2.0-Regular",
                                             Text = "0",
                                         },
                                         positiveRatings = new OsuSpriteText
                                         {
                                             Font   = @"Exo2.0-Regular",
                                             Text   = "0",
                                             Anchor = Anchor.TopRight,
                                             Origin = Anchor.TopRight,
                                         },
                                     },
                                 },
                                 new OsuSpriteText
                                 {
                                     Text     = "Rating Spread",
                                     TextSize = 14,
                                     Font     = @"Exo2.0-Regular",
                                     Anchor   = Anchor.TopCentre,
                                     Origin   = Anchor.TopCentre,
                                 },
                                 ratingsGraph = new BarGraph
                                 {
                                     RelativeSizeAxes = Axes.X,
                                     Height           = 50,
                                 },
                             },
                         },
                     },
                 },
                 retryFailContainer = new FillFlowContainer
                 {
                     RelativeSizeAxes = Axes.X,
                     AutoSizeAxes     = Axes.Y,
                     Alpha            = 0,
                     Children         = new Drawable[]
                     {
                         new OsuSpriteText
                         {
                             Text = "Points of Failure",
                             Font = @"Exo2.0-Regular",
                         },
                         new Container <BarGraph>
                         {
                             RelativeSizeAxes = Axes.X,
                             Size             = new Vector2(1 / 0.6f, 50),
                             Children         = new[]
                             {
                                 retryGraph = new BarGraph
                                 {
                                     RelativeSizeAxes = Axes.Both,
                                 },
                                 failGraph = new BarGraph
                                 {
                                     RelativeSizeAxes = Axes.Both,
                                 },
                             },
                         },
                     }
                 },
             },
         },
         loading = new LoadingAnimation()
     };
 }
Exemple #10
0
 public CopySource(DifficultySettings difficultySettings, DifficultyBeatmapSet characteristic, DifficultyRow obj)
 {
     DifficultySettings = difficultySettings;
     Characteristic     = characteristic;
     Obj = obj;
 }
    /// <summary>
    /// Handle adding and deleting difficulties, they aren't added to the
    /// song being edited until they are saved so this method stages them
    /// </summary>
    /// <param name="row">UI row that was clicked on</param>
    /// <param name="val">True if the diff is being added</param>
    private void OnChange(DifficultyRow row, bool val)
    {
        if (!val && diffs.ContainsKey(row.Name)) // Delete if exists
        {
            // ForceDirty = has never been saved, don't ask for permission
            if (diffs[row.Name].ForceDirty)
            {
                if (row == selected)
                {
                    DeselectDiff();
                }

                diffs.Remove(row.Name);
                row.SetInteractable(false);
                row.NameInput.text = "";
                row.ShowDirtyObjects(false, false);
                return;
            }

            // This diff has previously been saved, confirm deletion
            PersistentUI.Instance.ShowDialogBox("SongEditMenu", "deletediff.dialog",
                                                (r) => HandleDeleteDifficulty(row, r), PersistentUI.DialogBoxPresetType.YesNo, new object[] { diffs[row.Name].DifficultyBeatmap.difficulty });
        }
        else if (val && !diffs.ContainsKey(row.Name)) // Create if does not exist
        {
            DifficultyBeatmap map = new DifficultyBeatmap(currentCharacteristic)
            {
                difficulty     = row.Name,
                difficultyRank = diffRankLookup[row.Name]
            };

            map.UpdateName();

            if (copySource != null)
            {
                var fromDiff = copySource.DifficultySettings;

                CancelCopy();

                if (fromDiff != null)
                {
                    map.noteJumpMovementSpeed   = fromDiff.DifficultyBeatmap.noteJumpMovementSpeed;
                    map.noteJumpStartBeatOffset = fromDiff.DifficultyBeatmap.noteJumpStartBeatOffset;

                    map.customData = fromDiff.DifficultyBeatmap.customData?.Clone();

                    // This sets the current filename as the filename for another diff and will trigger the copy on save
                    map.UpdateName(fromDiff.DifficultyBeatmap.beatmapFilename);
                }
            }

            diffs[row.Name] = new DifficultySettings(map, true);

            row.ShowDirtyObjects(diffs[row.Name]);
            row.SetInteractable(true);
            OnClick(row);
        }
        else if (val) // Create, but already exists
        {
            // I don't know how this would happen anymore
            row.ShowDirtyObjects(diffs[row.Name]);
            row.SetInteractable(true);
            if (!loading)
            {
                OnClick(row);
            }
        }
    }
 /// <summary>
 /// Paste from another difficulty to this one
 /// As the toggles are hidden in this mode and replaced with paste icons
 /// we just forward the click to the toggle below
 /// </summary>
 /// <param name="row">UI row that was clicked on</param>
 private void DoPaste(DifficultyRow row)
 {
     // This will trigger the code in OnChange below
     row.Toggle.isOn = true;
 }