// Should be called once the physics system has settled down
    void Init()
    {
        ChartEditor editor = ChartEditor.Instance;

        Debug.Assert(!hitWindowFeeder.enabled);
        hitWindowFeeder.enabled = true;

        if (botEnabled)
        {
            // We want the bot to automatically hit any sustains that are currently active in the view, but for which the notes are already past the strikeline
            Song  song        = editor.currentSong;
            float currentTime = editor.currentVisibleTime;
            uint  currentTick = song.TimeToTick(currentTime, song.resolution);
            int   index       = SongObjectHelper.FindClosestPositionRoundedDown(currentTick, editor.currentChart.notes);
            if (index != SongObjectHelper.NOTFOUND)
            {
                Note        note         = editor.currentChart.notes[index];
                List <Note> sustainNotes = new List <Note>();
                NoteFunctions.GetPreviousOfSustains(sustainNotes, note, GameSettings.extendedSustainsEnabled);

                foreach (Note chordNote in note.chord)
                {
                    sustainNotes.Add(chordNote);
                }
                foreach (Note sustainNote in sustainNotes)
                {
                    if (sustainNote.controller != null)
                    {
                        hitWindowFeeder.TryAddNote(sustainNote.controller);
                    }
                }
            }
        }
    }
Example #2
0
    static void AddNote(Note note, IList <BaseAction> subActions, bool extendedSustainsEnabled)
    {
        ChartEditor editor = ChartEditor.Instance;
        Chart       chart  = editor.currentChart;
        Song        song   = editor.currentSong;

        NoteFunctions.PerformPreChartInsertCorrections(note, chart, subActions, extendedSustainsEnabled);
        AddAndInvokeSubAction(new AddAction(note), subActions);

        int arrayPos = SongObjectHelper.FindObjectPosition(note, chart.chartObjects);

        if (arrayPos != SongObjectHelper.NOTFOUND)
        {
            Note justAdded = chart.chartObjects[arrayPos] as Note;
            if (justAdded == null)
            {
                UnityEngine.Debug.LogError("Object just added was not a note");
            }
            else
            {
                NoteFunctions.PerformPostChartInsertCorrections(justAdded, subActions, extendedSustainsEnabled);
            }
        }
        else
        {
            UnityEngine.Debug.LogError("Unable to find note that was just added");
        }
    }
    static void CapNoteCheck(Chart chart, Note noteToAdd, IList <BaseAction> subActions, Song song, bool extendedSustainsEnabled)
    {
        Note[] previousNotes = NoteFunctions.GetPreviousOfSustains(noteToAdd, extendedSustainsEnabled);
        if (!Globals.gameSettings.extendedSustainsEnabled)
        {
            // Cap all the notes
            foreach (Note prevNote in previousNotes)
            {
                uint newLength = prevNote.GetCappedLength(noteToAdd, song);
                if (prevNote.length != newLength)
                {
                    Note newNote = new Note(prevNote.tick, prevNote.rawNote, newLength, prevNote.flags);
                    SongEditCommand.AddAndInvokeSubAction(new DeleteAction(prevNote), subActions);
                    SongEditCommand.AddAndInvokeSubAction(new AddAction(newNote), subActions);
                }
            }

            foreach (Note chordNote in noteToAdd.chord)
            {
                uint newLength = noteToAdd.length;
                if (chordNote.length != newLength)
                {
                    Note newNote = new Note(chordNote.tick, chordNote.rawNote, newLength, chordNote.flags);
                    SongEditCommand.AddAndInvokeSubAction(new DeleteAction(chordNote), subActions);
                    SongEditCommand.AddAndInvokeSubAction(new AddAction(newNote), subActions);
                }
            }
        }
        else
        {
            // Cap only the sustain of the same fret type and open notes
            foreach (Note prevNote in previousNotes)
            {
                if (
#if OPEN_NOTES_BLOCK_EXTENDED_SUSTAINS
                    noteToAdd.IsOpenNote() ||
#endif
                    prevNote.guitarFret == noteToAdd.guitarFret
                    )
                {
                    uint newLength = prevNote.GetCappedLength(noteToAdd, song);
                    if (prevNote.length != newLength)
                    {
                        Note newNote = new Note(prevNote.tick, prevNote.rawNote, newLength, prevNote.flags);
                        SongEditCommand.AddAndInvokeSubAction(new DeleteAction(prevNote), subActions);
                        SongEditCommand.AddAndInvokeSubAction(new AddAction(newNote), subActions);
                    }
                }
            }
        }
    }
Example #4
0
    static void CapNoteCheck(Chart chart, Note noteToAdd, IList <SongObject> overwrittenList, IList <SongObject> replacementNotes, Song song, bool extendedSustainsEnabled)
    {
        Note[] previousNotes = NoteFunctions.GetPreviousOfSustains(noteToAdd, extendedSustainsEnabled);
        if (!GameSettings.extendedSustainsEnabled)
        {
            // Cap all the notes
            foreach (Note prevNote in previousNotes)
            {
                uint newLength = prevNote.GetCappedLength(noteToAdd, song);
                if (prevNote.length != newLength)
                {
                    Note newNote = new Note(prevNote.tick, prevNote.rawNote, newLength, prevNote.flags);
                    AddOrReplaceNote(chart, prevNote, newNote, overwrittenList, replacementNotes);
                }
            }

            foreach (Note chordNote in noteToAdd.chord)
            {
                uint newLength = noteToAdd.length;
                if (chordNote.length != newLength)
                {
                    if (noteToAdd == chordNote)
                    {
                        noteToAdd.length = newLength;
                    }
                    else
                    {
                        Note newNote = new Note(chordNote.tick, chordNote.rawNote, newLength, chordNote.flags);
                        AddOrReplaceNote(chart, chordNote, newNote, overwrittenList, replacementNotes);
                    }
                }
            }
        }
        else
        {
            // Cap only the sustain of the same fret type and open notes
            foreach (Note prevNote in previousNotes)
            {
                if (noteToAdd.IsOpenNote() || prevNote.guitarFret == noteToAdd.guitarFret)
                {
                    uint newLength = prevNote.GetCappedLength(noteToAdd, song);
                    if (prevNote.length != newLength)
                    {
                        overwrittenList.Add(prevNote);
                        chart.Add(new Note(prevNote.tick, prevNote.rawNote, newLength, prevNote.flags));
                    }
                }
            }
        }
    }
    void UpdateTogglesInteractable()
    {
        // Prevent users from forcing notes when they shouldn't be forcable but retain the previous user-set forced property when using the note tool
        bool drumsMode    = Globals.drumMode;
        bool proDrumsMode = drumsMode && Globals.gameSettings.drumsModeOptions == GameSettings.DrumModeOptions.ProDrums;

        forcedToggle.gameObject.SetActive(!drumsMode);
        tapToggle.gameObject.SetActive(!drumsMode);
        cymbalToggle.gameObject.SetActive(proDrumsMode);
        doubleKickToggle.gameObject.SetActive(proDrumsMode);

        if (!drumsMode)
        {
            if (IsInNoteTool() && (noteToolObject.activeSelf || Globals.gameSettings.keysModeEnabled))
            {
                forcedToggle.interactable = noteToolController.forcedInteractable;
                tapToggle.interactable    = noteToolController.tapInteractable;
            }
            else if (!IsInNoteTool())
            {
                forcedToggle.interactable = !(currentNote.cannotBeForced && !Globals.gameSettings.keysModeEnabled);
                tapToggle.interactable    = !currentNote.IsOpenNote();
            }
            else
            {
                forcedToggle.interactable = true;
                tapToggle.interactable    = true;
            }
        }
        else
        {
            if (IsInNoteTool() && noteToolObject.activeSelf)
            {
                cymbalToggle.interactable     = noteToolController.cymbalInteractable;
                doubleKickToggle.interactable = noteToolController.doubleKickInteractable;
            }
            else if (!IsInNoteTool())
            {
                cymbalToggle.interactable     = NoteFunctions.AllowedToBeCymbal(currentNote);
                doubleKickToggle.interactable = NoteFunctions.AllowedToBeDoubleKick(currentNote, editor.currentDifficulty);
            }
            else
            {
                cymbalToggle.interactable     = true;
                doubleKickToggle.interactable = true;
            }
        }
    }
    protected static ActionHistory.Action[] CapNoteCheck(Note noteToAdd)
    {
        List <ActionHistory.Action> actionRecord = new List <ActionHistory.Action>();

        Note[] previousNotes = NoteFunctions.GetPreviousOfSustains(noteToAdd);
        if (!GameSettings.extendedSustainsEnabled)
        {
            // Cap all the notes
            foreach (Note prevNote in previousNotes)
            {
                if (prevNote.controller != null)
                {
                    ActionHistory.Action action = prevNote.CapSustain(noteToAdd);
                    if (action != null)
                    {
                        actionRecord.Add(action);
                    }
                }
            }

            foreach (Note chordNote in noteToAdd.chord)
            {
                if (chordNote.controller != null)
                {
                    chordNote.controller.note.length = noteToAdd.length;
                }
            }
        }
        else
        {
            // Cap only the sustain of the same fret type and open notes
            foreach (Note prevNote in previousNotes)
            {
                if (prevNote.controller != null && (noteToAdd.IsOpenNote() || prevNote.guitarFret == noteToAdd.guitarFret))
                {
                    ActionHistory.Action action = prevNote.CapSustain(noteToAdd);
                    if (action != null)
                    {
                        actionRecord.Add(action);
                    }
                }
            }
        }

        return(actionRecord.ToArray());
    }
Example #7
0
    static void AddNote(Note note, IList <SongObject> overwrittenList, bool extendedSustainsEnabled, List <SongObject> validatedNotes)
    {
        ChartEditor editor = ChartEditor.Instance;
        Chart       chart  = editor.currentChart;
        Song        song   = editor.currentSong;

        Note noteToAdd = new Note(note);

        NoteFunctions.PerformPreChartInsertCorrections(noteToAdd, chart, validatedNotes, overwrittenList, extendedSustainsEnabled);
        chart.Add(noteToAdd, false);
        NoteFunctions.PerformPostChartInsertCorrections(noteToAdd, validatedNotes, overwrittenList, extendedSustainsEnabled);

        // Queue visual refresh
        {
            foreach (Note chordNote in noteToAdd.chord)
            {
                if (chordNote.controller)
                {
                    chordNote.controller.SetDirty();
                }
            }

            Note next = noteToAdd.nextSeperateNote;
            if (next != null)
            {
                foreach (Note chordNote in next.chord)
                {
                    if (chordNote.controller)
                    {
                        chordNote.controller.SetDirty();
                    }
                }
            }
        }

        if (!validatedNotes.Contains(noteToAdd))
        {
            validatedNotes.Add(noteToAdd);
        }
    }
    private static void ReadNotes(IList <MidiEvent> track, Song song, Song.Instrument instrument)
    {
        List <NoteOnEvent> forceNotesList    = new List <NoteOnEvent>();
        List <NoteOnEvent> proDrumsNotesList = new List <NoteOnEvent>();
        List <SysexEvent>  tapAndOpenEvents  = new List <SysexEvent>();

        Chart unrecognised = new Chart(song, Song.Instrument.Unrecognised);

        Chart.GameMode gameMode = Song.InstumentToChartGameMode(instrument);

        if (instrument == Song.Instrument.Unrecognised)
        {
            song.unrecognisedCharts.Add(unrecognised);
        }

        int rbSustainFixLength = (int)(64 * song.resolution / SongConfig.STANDARD_BEAT_RESOLUTION);

        // Load all the notes
        for (int i = 0; i < track.Count; i++)
        {
            var text = track[i] as TextEvent;
            if (text != null)
            {
                if (i == 0)
                {
                    if (instrument == Song.Instrument.Unrecognised)
                    {
                        unrecognised.name = text.Text;
                    }
                    continue;           // We don't want the first event because that is the name of the track
                }

                var        tick       = (uint)text.AbsoluteTime;
                var        eventName  = text.Text.Trim(new char[] { '[', ']' });
                ChartEvent chartEvent = new ChartEvent(tick, eventName);

                if (instrument == Song.Instrument.Unrecognised)
                {
                    unrecognised.Add(chartEvent);
                }
                else
                {
                    song.GetChart(instrument, Song.Difficulty.Expert).Add(chartEvent);
                }
            }

            var note = track[i] as NoteOnEvent;
            if (note != null && note.OffEvent != null)
            {
                Song.Difficulty difficulty;

                var tick = (uint)note.AbsoluteTime;
                var sus  = (uint)(note.OffEvent.AbsoluteTime - tick);

                if (instrument == Song.Instrument.Unrecognised)
                {
                    int  rawNote = SelectRawNoteValue(note.NoteNumber);
                    Note newNote = new Note(tick, rawNote, sus);
                    //difficulty = SelectRawNoteDifficulty(note.NoteNumber);
                    unrecognised.Add(newNote);
                    continue;
                }

                // Check if starpower event
                if (note.NoteNumber == MidIOHelper.STARPOWER_NOTE)
                {
                    foreach (Song.Difficulty diff in EnumX <Song.Difficulty> .Values)
                    {
                        song.GetChart(instrument, diff).Add(new Starpower(tick, sus), false);
                    }

                    continue;
                }

                if (note.NoteNumber == MidIOHelper.SOLO_NOTE)
                {
                    foreach (Song.Difficulty diff in EnumX <Song.Difficulty> .Values)
                    {
                        Chart chart = song.GetChart(instrument, diff);
                        chart.Add(new ChartEvent(tick, MidIOHelper.SoloEventText));
                        chart.Add(new ChartEvent(tick + sus, MidIOHelper.SoloEndEventText));
                    }

                    continue;
                }

                if (gameMode == Chart.GameMode.Drums)
                {
                    Note.DrumPad dummy;
                    if (MidIOHelper.CYMBAL_TO_PAD_LOOKUP.TryGetValue(note.NoteNumber, out dummy))
                    {
                        // Cymbals toggles
                        proDrumsNotesList.Add(note);
                        continue;
                    }
                }

                // Determine which difficulty we are manipulating
                try
                {
                    if (gameMode == Chart.GameMode.GHLGuitar)
                    {
                        difficulty = SelectGHLNoteDifficulty(note.NoteNumber);
                    }
                    else
                    {
                        difficulty = SelectNoteDifficulty(note.NoteNumber);
                    }
                }
                catch
                {
                    continue;
                }

                // Check if we're reading a forcing event instead of a regular note
                if (gameMode != Chart.GameMode.Drums)
                {
                    switch (note.NoteNumber)
                    {
                    case 65:
                    case 66:
                    case 77:
                    case 78:
                    case 89:
                    case 90:
                    case 101:
                    case 102:
                        forceNotesList.Add(note);           // Store the event for later processing and continue
                        continue;

                    default:
                        break;
                    }
                }

                int fret;

                if (sus <= rbSustainFixLength)
                {
                    sus = 0;
                }

                Note.Flags flags = Note.Flags.None;

                if (gameMode == Chart.GameMode.Drums)
                {
                    fret = (int)GetDrumFretType(note.NoteNumber);

                    int cymbalToggleId;
                    if (MidIOHelper.PAD_TO_CYMBAL_LOOKUP.TryGetValue((Note.DrumPad)fret, out cymbalToggleId))
                    {
                        flags |= Note.Flags.ProDrums_Cymbal;
                    }
                }
                else if (gameMode == Chart.GameMode.GHLGuitar)
                {
                    fret = (int)GetGHLFretType(note.NoteNumber);
                }
                else
                {
                    fret = (int)GetStandardFretType(note.NoteNumber);
                }

                {
                    // Add the note to the correct chart
                    Note newNote = new Note(tick, fret, sus, flags);
                    song.GetChart(instrument, difficulty).Add(newNote, false);
                }
            }

            var sysexEvent = track[i] as SysexEvent;
            if (sysexEvent != null)
            {
                tapAndOpenEvents.Add(sysexEvent);
            }
        }

        // Update all chart arrays
        if (instrument != Song.Instrument.Unrecognised)
        {
            foreach (Song.Difficulty diff in EnumX <Song.Difficulty> .Values)
            {
                song.GetChart(instrument, diff).UpdateCache();
            }
        }
        else
        {
            unrecognised.UpdateCache();
        }

        // Apply tap and open note events
        Chart[] chartsOfInstrument;

        if (instrument == Song.Instrument.Unrecognised)
        {
            chartsOfInstrument = new Chart[] { unrecognised };
        }
        else
        {
            chartsOfInstrument = new Chart[EnumX <Song.Difficulty> .Count];

            int difficultyCount = 0;
            foreach (Song.Difficulty difficulty in EnumX <Song.Difficulty> .Values)
            {
                chartsOfInstrument[difficultyCount++] = song.GetChart(instrument, difficulty);
            }
        }

        for (int i = 0; i < tapAndOpenEvents.Count; ++i)
        {
            var    se1   = tapAndOpenEvents[i];
            byte[] bytes = se1.GetData();

            // Check for tap event
            if (bytes.Length == 8 && bytes[5] == 255 && bytes[7] == 1)
            {
                // Identified a tap section
                // 8 total bytes, 5th byte is FF, 7th is 1 to start, 0 to end
                uint tick   = (uint)se1.AbsoluteTime;
                uint endPos = 0;

                // Find the end of the tap section
                for (int j = i; j < tapAndOpenEvents.Count; ++j)
                {
                    var se2    = tapAndOpenEvents[j];
                    var bytes2 = se2.GetData();
                    /// Check for tap section end
                    if (bytes2.Length == 8 && bytes2[5] == 255 && bytes2[7] == 0)
                    {
                        endPos = (uint)(se2.AbsoluteTime - tick);

                        if (endPos > 0)
                        {
                            --endPos;
                        }

                        break;
                    }
                }

                // Apply tap property
                foreach (Chart chart in chartsOfInstrument)
                {
                    int index, length;
                    SongObjectHelper.GetRange(chart.notes, tick, tick + endPos, out index, out length);
                    for (int k = index; k < index + length; ++k)
                    {
                        chart.notes[k].SetType(Note.NoteType.Tap);
                    }
                }
            }

            // Check for open notes
            // 5th byte determines the difficulty to apply to
            else if (bytes.Length == 8 && bytes[5] >= 0 && bytes[5] < 4 && bytes[7] == 1)
            {
                uint            tick = (uint)se1.AbsoluteTime;
                Song.Difficulty difficulty;
                switch (bytes[5])
                {
                case 0: difficulty = Song.Difficulty.Easy; break;

                case 1: difficulty = Song.Difficulty.Medium; break;

                case 2: difficulty = Song.Difficulty.Hard; break;

                case 3: difficulty = Song.Difficulty.Expert; break;

                default: continue;
                }

                uint endPos = 0;
                for (int j = i; j < tapAndOpenEvents.Count; ++j)
                {
                    var se2 = tapAndOpenEvents[j] as SysexEvent;
                    if (se2 != null)
                    {
                        var b2 = se2.GetData();
                        if (b2.Length == 8 && b2[5] == bytes[5] && b2[7] == 0)
                        {
                            endPos = (uint)(se2.AbsoluteTime - tick);

                            if (endPos > 0)
                            {
                                --endPos;
                            }

                            break;
                        }
                    }
                }

                int index, length;
                SongObjectCache <Note> notes;
                if (instrument == Song.Instrument.Unrecognised)
                {
                    notes = unrecognised.notes;
                }
                else
                {
                    notes = song.GetChart(instrument, difficulty).notes;
                }
                SongObjectHelper.GetRange(notes, tick, tick + endPos, out index, out length);
                for (int k = index; k < index + length; ++k)
                {
                    notes[k].guitarFret = Note.GuitarFret.Open;

                    if (gameMode == Chart.GameMode.Drums)
                    {
                        notes[k].guitarFret = NoteFunctions.LoadDrumNoteToGuitarNote(notes[k].guitarFret);
                    }
                }
            }
        }

        // Apply forcing events
        foreach (NoteOnEvent flagEvent in forceNotesList)
        {
            uint tick   = (uint)flagEvent.AbsoluteTime;
            uint endPos = (uint)(flagEvent.OffEvent.AbsoluteTime - tick);

            Song.Difficulty difficulty;

            // Determine which difficulty we are manipulating
            try
            {
                difficulty = SelectNoteDifficulty(flagEvent.NoteNumber);
            }
            catch
            {
                continue;
            }

            Chart chart;
            if (instrument != Song.Instrument.Unrecognised)
            {
                chart = song.GetChart(instrument, difficulty);
            }
            else
            {
                chart = unrecognised;
            }

            int index, length;
            SongObjectHelper.GetRange(chart.notes, tick, tick + endPos, out index, out length);

            for (int i = index; i < index + length; ++i)
            {
                if ((chart.notes[i].flags & Note.Flags.Tap) != 0)
                {
                    continue;
                }

                // if NoteNumber is odd force hopo, if even force strum
                if (flagEvent.NoteNumber % 2 != 0)
                {
                    chart.notes[i].SetType(Note.NoteType.Hopo);
                }
                else
                {
                    chart.notes[i].SetType(Note.NoteType.Strum);
                }
            }
        }

        foreach (var flagEvent in proDrumsNotesList)
        {
            uint tick   = (uint)flagEvent.AbsoluteTime;
            uint endPos = (uint)(flagEvent.OffEvent.AbsoluteTime - tick);
            if (endPos > 0)
            {
                --endPos;
            }

            Debug.Assert(instrument == Song.Instrument.Drums);

            foreach (Song.Difficulty difficulty in EnumX <Song.Difficulty> .Values)
            {
                Chart chart = song.GetChart(instrument, difficulty);

                int index, length;
                SongObjectHelper.GetRange(chart.notes, tick, tick + endPos, out index, out length);

                Note.DrumPad drumPadForFlag;
                if (!MidIOHelper.CYMBAL_TO_PAD_LOOKUP.TryGetValue(flagEvent.NoteNumber, out drumPadForFlag))
                {
                    Debug.Assert(false, "Unknown note number flag " + flagEvent.NoteNumber);
                    continue;
                }

                for (int i = index; i < index + length; ++i)
                {
                    Note note = chart.notes[i];

                    if (note.drumPad == drumPadForFlag)
                    {
                        // Reverse cymbal flag
                        note.flags ^= Note.Flags.ProDrums_Cymbal;
                    }
                }
            }
        }
    }
Example #9
0
    void CollectNotesInViewRange(IList <Note> notes)
    {
        bool extendedSustainsEnabled = GameSettings.extendedSustainsEnabled;

        uint min_pos = editor.minPos;

        if (noteVisibilityRangeYPosOverride.HasValue)
        {
            uint gameplayPos = editor.currentSong.WorldYPositionToTick(noteVisibilityRangeYPosOverride.Value, editor.currentSong.resolution);
            if (min_pos < gameplayPos)
            {
                min_pos = gameplayPos;
            }
        }

        collectedNotesInRange.Clear();
        int index, length;

        SongObjectHelper.GetRange(notes, min_pos, editor.maxPos, out index, out length);
        for (int i = index; i < index + length; ++i)
        {
            collectedNotesInRange.Add(notes[i]);
        }

        if (min_pos == editor.minPos)
        {
            if (collectedNotesInRange.Count > 0)
            {
                NoteFunctions.GetPreviousOfSustains(prevSustainCache, collectedNotesInRange[0], extendedSustainsEnabled);
                // Find the last known note of each fret type to find any sustains that might overlap into the camera view
                foreach (Note prevNote in prevSustainCache)
                {
                    if (prevNote.tick + prevNote.length > editor.minPos)
                    {
                        collectedNotesInRange.Add(prevNote);
                    }
                }
            }
            else
            {
                int minArrayPos = SongObjectHelper.FindClosestPosition(editor.minPos, editor.currentChart.notes);

                if (minArrayPos != SongObjectHelper.NOTFOUND)
                {
                    while (minArrayPos > 0 && editor.currentChart.notes[minArrayPos].tick == editor.currentChart.notes[minArrayPos - 1].tick)
                    {
                        --minArrayPos;
                    }

                    Note minNote = editor.currentChart.notes[minArrayPos];

                    if (minNote.tick + minNote.length > editor.minPos && minNote.tick < editor.maxPos)
                    {
                        foreach (Note note in minNote.chord)
                        {
                            if (note.tick + note.length > editor.minPos)
                            {
                                collectedNotesInRange.Add(note);
                            }
                        }
                    }

                    NoteFunctions.GetPreviousOfSustains(prevSustainCache, minNote, extendedSustainsEnabled);
                    foreach (Note prevNote in prevSustainCache)
                    {
                        if (prevNote.tick + prevNote.length > editor.minPos)
                        {
                            collectedNotesInRange.Add(prevNote);
                        }
                    }
                }
            }
        }

        // Make sure the notes are within the allowable lanes
        for (int i = collectedNotesInRange.Count - 1; i >= 0; --i)
        {
            Note note = collectedNotesInRange[i];
            if (!note.IsOpenNote() && ((1 << note.rawNote) & editor.laneInfo.laneMask) == 0)
            {
                collectedNotesInRange.RemoveAt(i);
            }
        }
    }
    public static Note.Flags GetFlagsToSetType(this Note note, Note.NoteType type)
    {
        Note.Flags flags = Note.Flags.None;
        switch (type)
        {
        case (Note.NoteType.Strum):
            if (note.isChord)
            {
                flags &= ~Note.Flags.Forced;
            }
            else
            {
                if (note.isNaturalHopo)
                {
                    flags |= Note.Flags.Forced;
                }
                else
                {
                    flags &= ~Note.Flags.Forced;
                }
            }

            break;

        case (Note.NoteType.Hopo):
            if (!note.cannotBeForced)
            {
                if (note.isChord)
                {
                    flags |= Note.Flags.Forced;
                }
                else
                {
                    if (!note.isNaturalHopo)
                    {
                        flags |= Note.Flags.Forced;
                    }
                    else
                    {
                        flags &= ~Note.Flags.Forced;
                    }
                }
            }
            break;

        case (Note.NoteType.Tap):
            if (!note.IsOpenNote())
            {
                flags |= Note.Flags.Tap;
            }
            break;

        case (Note.NoteType.Cymbal):
            if (NoteFunctions.AllowedToBeCymbal(note))
            {
                flags |= Note.Flags.ProDrums_Cymbal;
            }
            break;

        default:
            break;
        }

        if (type != Note.NoteType.Natural && ((note.flags & Note.Flags.DoubleKick) != 0) && AllowedToBeDoubleKickIgnoreDifficulty(note))
        {
            flags |= Note.Flags.DoubleKick;
        }

        return(flags);
    }
    public static void LoadChart(Chart chart, string[] data, Song.Instrument instrument = Song.Instrument.Guitar)
    {
#if TIMING_DEBUG
        float time = Time.realtimeSinceStartup;
#endif
        List <NoteFlag> flags = new List <NoteFlag>();

        chart.SetCapacity(data.Length);

        const int SPLIT_POSITION = 0;
        const int SPLIT_EQUALITY = 1;
        const int SPLIT_TYPE     = 2;
        const int SPLIT_VALUE    = 3;
        const int SPLIT_LENGTH   = 4;

        try
        {
            // Load notes, collect flags
            foreach (string line in data)
            {
                try
                {
                    string[] splitString = line.Split(' ');
                    uint     tick        = uint.Parse(splitString[SPLIT_POSITION]);
                    string   type        = splitString[SPLIT_TYPE].ToLower();

                    switch (type)
                    {
                    case ("n"):
                        // Split string to get note information
                        string[] digits = splitString;

                        int  fret_type = int.Parse(digits[SPLIT_VALUE]);
                        uint length    = uint.Parse(digits[SPLIT_LENGTH]);

                        if (instrument == Song.Instrument.Unrecognised)
                        {
                            Note newNote = new Note(tick, fret_type, length);
                            chart.Add(newNote, false);
                        }
                        else if (instrument == Song.Instrument.Drums)
                        {
                            LoadDrumNote(chart, tick, fret_type, length);
                        }
                        else if (instrument == Song.Instrument.GHLiveGuitar || instrument == Song.Instrument.GHLiveBass)
                        {
                            LoadGHLiveNote(chart, tick, fret_type, length, flags);
                        }
                        else
                        {
                            LoadStandardNote(chart, tick, fret_type, length, flags);
                        }
                        break;

                    case ("s"):
                        fret_type = int.Parse(splitString[SPLIT_VALUE]);

                        if (fret_type != 2)
                        {
                            continue;
                        }

                        length = uint.Parse(splitString[SPLIT_LENGTH]);

                        chart.Add(new Starpower(tick, length), false);
                        break;

                    case ("e"):
                        string[] strings   = splitString;
                        string   eventName = strings[SPLIT_VALUE];
                        chart.Add(new ChartEvent(tick, eventName), false);
                        break;

                    default:
                        break;
                    }
                }
                catch (System.Exception e)
                {
                    Logger.LogException(e, "Error parsing chart reader line \"" + line);
                }
            }
            chart.UpdateCache();

            // Load flags
            foreach (NoteFlag flag in flags)
            {
                int index, length;
                SongObjectHelper.FindObjectsAtPosition(flag.tick, chart.notes, out index, out length);
                if (length > 0)
                {
                    NoteFunctions.GroupAddFlags(chart.notes, flag.flag, index, length);
                }
            }
#if TIMING_DEBUG
            Debug.Log("Chart load time: " + (Time.realtimeSinceStartup - time));
#endif
        }
        catch (System.Exception e)
        {
            // Bad load, most likely a parsing error
            Logger.LogException(e, "Error parsing chart reader chart data");
            chart.Clear();
        }
    }
    public static void LoadChart(Chart chart, string[] data, Song.Instrument instrument = Song.Instrument.Guitar)
    {
#if TIMING_DEBUG
        float time = Time.realtimeSinceStartup;
#endif
        List <NoteFlag> flags = new List <NoteFlag>();

        chart.SetCapacity(data.Length);

        const int SPLIT_POSITION = 0;
        const int SPLIT_EQUALITY = 1;
        const int SPLIT_TYPE     = 2;
        const int SPLIT_VALUE    = 3;
        const int SPLIT_LENGTH   = 4;

        try
        {
            // Load notes, collect flags
            foreach (string line in data)
            {
                try
                {
                    string[] splitString = line.Split(' ');
                    uint     tick        = uint.Parse(splitString[SPLIT_POSITION]);
                    string   type        = splitString[SPLIT_TYPE].ToLower();

                    switch (type)
                    {
                    case ("n"):
                        // Split string to get note information
                        string[] digits = splitString;

                        int  fret_type = int.Parse(digits[SPLIT_VALUE]);
                        uint length    = uint.Parse(digits[SPLIT_LENGTH]);

                        if (instrument == Song.Instrument.Unrecognised)
                        {
                            Note newNote = new Note(tick, fret_type, length);
                            chart.Add(newNote, false);
                        }
                        else if (instrument == Song.Instrument.Drums)
                        {
                            LoadDrumNote(chart, tick, fret_type, length, flags);
                        }
                        else if (instrument == Song.Instrument.GHLiveGuitar || instrument == Song.Instrument.GHLiveBass)
                        {
                            LoadGHLiveNote(chart, tick, fret_type, length, flags);
                        }
                        else
                        {
                            LoadStandardNote(chart, tick, fret_type, length, flags);
                        }
                        break;

                    case ("s"):
                        fret_type = int.Parse(splitString[SPLIT_VALUE]);

                        if (fret_type != 2)
                        {
                            continue;
                        }

                        length = uint.Parse(splitString[SPLIT_LENGTH]);

                        chart.Add(new Starpower(tick, length), false);
                        break;

                    case ("e"):
                        string[] strings   = splitString;
                        string   eventName = strings[SPLIT_VALUE];
                        chart.Add(new ChartEvent(tick, eventName), false);
                        break;

                    default:
                        break;
                    }
                }
                catch (System.Exception e)
                {
                    Logger.LogException(e, "Error parsing chart reader line \"" + line);
                }
            }
            chart.UpdateCache();

            // Load flags
            foreach (NoteFlag flag in flags)
            {
                if (flag.flag == Note.Flags.ProDrums_Cymbal)
                {
                    // The note number indicates which note it should attach to
                    int noteNumber = flag.noteNumber - ChartIOHelper.c_proDrumsOffset;
                    Debug.Assert(noteNumber >= 0, "Incorrectly parsed a note flag as a pro-drums flag. Note number was " + flag.noteNumber);

                    int index, length;
                    SongObjectHelper.FindObjectsAtPosition(flag.tick, chart.notes, out index, out length);
                    if (length > 0)
                    {
                        for (int i = index; i < index + length; ++i)
                        {
                            Note note = chart.notes[i];

                            int saveNoteNum;
                            if (!ChartIOHelper.c_drumNoteToSaveNumberLookup.TryGetValue(note.rawNote, out saveNoteNum))
                            {
                                continue;
                            }

                            if (noteNumber == saveNoteNum)
                            {
                                // Reverse cymbal flag
                                note.flags ^= Note.Flags.ProDrums_Cymbal;
                            }
                        }
                    }
                }
                else
                {
                    int index, length;
                    SongObjectHelper.FindObjectsAtPosition(flag.tick, chart.notes, out index, out length);
                    if (length > 0)
                    {
                        NoteFunctions.GroupAddFlags(chart.notes, flag.flag, index, length);
                    }
                }
            }
#if TIMING_DEBUG
            Debug.Log("Chart load time: " + (Time.realtimeSinceStartup - time));
#endif
        }
        catch (System.Exception e)
        {
            // Bad load, most likely a parsing error
            Logger.LogException(e, "Error parsing chart reader chart data");
            chart.Clear();
        }
    }