public void Run(IScorePluginArgs args) { var score = args.GetCurrentScore(); var range = args.GetSelectedRange(); int startTick = range.Duration < 0 ? range.StartTick + range.Duration : range.StartTick; int endTick = range.Duration < 0 ? range.StartTick : range.StartTick + range.Duration; var endStepDic = score.Notes.Slides.ToDictionary(p => p, p => p.StepNotes.OrderByDescending(q => q.TickOffset).First()); var airStepDic = score.Notes.Airs .Where(p => endStepDic.Values.Contains(p.ParentNote)) .ToDictionary(p => p.ParentNote as Slide.StepTap, p => p); var airActionStepDic = score.Notes.AirActions .Where(p => endStepDic.Values.Contains(p.ParentNote)) .ToDictionary(p => p.ParentNote as Slide.StepTap, p => p); var targets = score.Notes.Slides .Where(p => p.StartTick >= startTick && p.StartTick + p.GetDuration() <= endTick) .Where(p => p.StartLaneIndex >= range.StartLaneIndex && p.StartLaneIndex + p.StartWidth <= range.StartLaneIndex + range.SelectedLanesCount) .Where(p => p.StepNotes.All(q => q.LaneIndex >= range.StartLaneIndex && q.LaneIndex + q.Width <= range.StartLaneIndex + range.SelectedLanesCount)) .Where(p => !airStepDic.ContainsKey(endStepDic[p]) && !airActionStepDic.ContainsKey(endStepDic[p])) .ToList(); if (targets.Count == 0) { return; } var results = targets.Select(p => { var ordered = p.StepNotes.OrderByDescending(q => q.TickOffset).ToList(); var res = new Slide() { StartTick = startTick + (endTick - ordered[0].Tick) }; res.SetPosition(ordered[0].LaneIndex, ordered[0].Width); var trailing = new Slide.StepTap(res) { IsVisible = true, TickOffset = startTick + (endTick - p.StartTick) - res.StartTick }; trailing.SetPosition(p.StartLaneIndex - res.StartLaneIndex, p.StartWidth - res.StartWidth); var steps = ordered.Skip(1).Select(q => { var step = new Slide.StepTap(res) { IsVisible = q.IsVisible, TickOffset = startTick + (endTick - q.Tick) - res.StartTick }; step.SetPosition(q.LaneIndex - res.StartLaneIndex, q.Width - res.StartWidth); return(step); }) .Concat(new[] { trailing }); res.StepNotes.AddRange(steps); return(res); }); foreach (var slide in targets) { score.Notes.Slides.Remove(slide); } score.Notes.Slides.AddRange(results); args.UpdateScore(score); }
public RemoveSlideStepNoteOperation(Slide parent, Slide.StepTap stepNote) : base(parent, stepNote) { }
public InsertSlideStepNoteOperation(Slide parent, Slide.StepTap stepNote) : base(parent, stepNote) { }
public SlideStepNoteCollectionOperation(Slide parent, Slide.StepTap stepNote) { ParentNote = parent; StepNote = stepNote; }
public MoveSlideStepNoteOperation(Slide.StepTap note, NotePosition before, NotePosition after) { StepNote = note; BeforePosition = before; AfterPosition = after; }
public void Run(IScorePluginArgs args) { var score = args.GetCurrentScore(); var range = args.GetSelectedRange(); bool modified = false; var targets = score.Notes.Slides.Where(p => p.StartTick <range.StartTick && p.StepNotes.OrderByDescending(q => q.TickOffset).First().Tick> range.StartTick); var endStepDic = score.Notes.Slides.ToDictionary(p => p, p => p.StepNotes.OrderByDescending(q => q.TickOffset).First()); var airStepDic = score.Notes.Airs .Where(p => endStepDic.Values.Contains(p.ParentNote)) .ToDictionary(p => p.ParentNote as Slide.StepTap, p => p); var airActionStepDic = score.Notes.AirActions .Where(p => endStepDic.Values.Contains(p.ParentNote)) .ToDictionary(p => p.ParentNote as Slide.StepTap, p => p); foreach (var slide in targets.ToList()) { // カーソル位置に中継点が存在しなければ処理しない int offset = range.StartTick - slide.StartTick; if (slide.StepNotes.All(p => p.TickOffset != offset)) { continue; } var first = new Slide() { StartTick = slide.StartTick }; first.SetPosition(slide.StartLaneIndex, slide.StartWidth); first.StepNotes.AddRange(slide.StepNotes.OrderBy(p => p.TickOffset).TakeWhile(p => p.TickOffset <= offset).Select(p => { var step = new Slide.StepTap(first) { TickOffset = p.TickOffset, IsVisible = p.IsVisible }; step.SetPosition(p.LaneIndexOffset, p.WidthChange); return(step); })); first.StepNotes[first.StepNotes.Count - 1].IsVisible = true; var second = new Slide() { StartTick = range.StartTick }; var trailing = slide.StepNotes.OrderBy(p => p.TickOffset).SkipWhile(p => p.TickOffset < offset).ToList(); second.SetPosition(trailing[0].LaneIndex, trailing[0].Width); second.StepNotes.AddRange(trailing.Skip(1).Select(p => { var step = new Slide.StepTap(second) { TickOffset = p.TickOffset - offset, IsVisible = p.IsVisible }; step.SetPosition(p.LaneIndex - second.StartLaneIndex, p.Width - second.StartWidth); return(step); })); // 終点AIRをsecondに挿入 if (airStepDic.ContainsKey(endStepDic[slide])) { var origAir = airStepDic[endStepDic[slide]]; var air = new Air(second.StepNotes[second.StepNotes.Count - 1]) { VerticalDirection = origAir.VerticalDirection, HorizontalDirection = origAir.HorizontalDirection }; score.Notes.Airs.Remove(origAir); score.Notes.Airs.Add(air); } if (airActionStepDic.ContainsKey(endStepDic[slide])) { var origAirAction = airActionStepDic[endStepDic[slide]]; var airAction = new AirAction(second.StepNotes[second.StepNotes.Count - 1]); airAction.ActionNotes.AddRange(origAirAction.ActionNotes.Select(p => new AirAction.ActionNote(airAction) { Offset = p.Offset })); score.Notes.AirActions.Remove(origAirAction); score.Notes.AirActions.Add(airAction); } score.Notes.Slides.Add(first); score.Notes.Slides.Add(second); score.Notes.Slides.Remove(slide); modified = true; } if (modified) { args.UpdateScore(score); } }
public void Run(IScorePluginArgs args) { var score = args.GetCurrentScore(); var range = args.GetSelectedRange(); bool modified = false; int startTick = range.Duration < 0 ? range.StartTick + range.Duration : range.StartTick; int endTick = range.Duration < 0 ? range.StartTick : range.StartTick + range.Duration; var endStepDic = score.Notes.Slides.ToDictionary(p => p, p => p.StepNotes.OrderByDescending(q => q.TickOffset).First()); var airStepDic = score.Notes.Airs .Where(p => endStepDic.Values.Contains(p.ParentNote)) .ToDictionary(p => p.ParentNote as Slide.StepTap, p => p); var airActionStepDic = score.Notes.AirActions .Where(p => endStepDic.Values.Contains(p.ParentNote)) .ToDictionary(p => p.ParentNote as Slide.StepTap, p => p); var startDic = score.Notes.Slides.Where(p => p.StartTick >= startTick && p.StartTick <= endTick) .Select(p => new { Position = Tuple.Create(p.StartTick, p.StartLaneIndex, p.StartWidth), Note = p }) .GroupBy(p => p.Position) .ToDictionary(p => p.Key, p => p.Select(q => q.Note).ToList()); var endDic = score.Notes.Slides.Where(p => endStepDic[p].Tick >= startTick && endStepDic[p].Tick <= endTick) .Select(p => new { Position = Tuple.Create(endStepDic[p].Tick, endStepDic[p].LaneIndex, endStepDic[p].Width), Note = p }) .GroupBy(p => p.Position) .ToDictionary(p => p.Key, p => p.Select(q => q.Note).ToList()); while (endDic.Count > 0) { var pos = endDic.First().Key; if (endDic[pos].Count == 0 || !startDic.ContainsKey(pos) || startDic[pos].Count == 0) { endDic.Remove(pos); continue; } if (startDic[pos].Count > 0) { // 終点AIR付きスライドはheadingの対象外 while (endDic[pos].Count > 0 && (airStepDic.ContainsKey(endStepDic[endDic[pos][0]]) || airActionStepDic.ContainsKey(endStepDic[endDic[pos][0]]))) { endDic[pos].RemoveAt(0); } if (endDic[pos].Count == 0) { endDic.Remove(pos); continue; } var heading = endDic[pos][0]; var trailing = startDic[pos][0]; var trailingOldSteps = trailing.StepNotes.OrderBy(p => p.TickOffset).ToList(); heading.StepNotes.AddRange(trailingOldSteps.Select(p => { var step = new Slide.StepTap(heading) { TickOffset = p.Tick - heading.StartTick, IsVisible = p.IsVisible }; step.SetPosition(p.LaneIndex - heading.StartLaneIndex, p.Width - heading.StartWidth); return(step); })); // trailingにAIRが追加されていれば反映 var trailingOldEndStep = trailingOldSteps[trailingOldSteps.Count - 1]; var trailingNewEndStep = heading.StepNotes[heading.StepNotes.Count - 1]; if (airStepDic.ContainsKey(trailingOldEndStep)) { var air = new Air(trailingNewEndStep) { VerticalDirection = airStepDic[trailingOldEndStep].VerticalDirection, HorizontalDirection = airStepDic[trailingOldEndStep].HorizontalDirection }; score.Notes.Airs.Add(air); score.Notes.Airs.Remove(airStepDic[trailingOldEndStep]); airStepDic.Add(trailingNewEndStep, air); airStepDic.Remove(trailingOldEndStep); } if (airActionStepDic.ContainsKey(trailingOldEndStep)) { var airAction = new AirAction(trailingNewEndStep); airAction.ActionNotes.AddRange(airActionStepDic[trailingOldEndStep].ActionNotes.Select(p => new AirAction.ActionNote(airAction) { Offset = p.Offset })); score.Notes.AirActions.Add(airAction); score.Notes.AirActions.Remove(airActionStepDic[trailingOldEndStep]); airActionStepDic.Add(trailingNewEndStep, airAction); airActionStepDic.Remove(trailingOldEndStep); } endStepDic[heading] = trailingNewEndStep; score.Notes.Slides.Remove(trailing); startDic[pos].Remove(trailing); var trailingEndPos = Tuple.Create(endStepDic[trailing].Tick, endStepDic[trailing].LaneIndex, endStepDic[trailing].Width); if (endDic.ContainsKey(trailingEndPos)) { endDic[trailingEndPos].Remove(trailing); } endDic[pos].Remove(heading); if (!endDic.ContainsKey(trailingEndPos)) { endDic.Add(trailingEndPos, new[] { heading }.ToList()); } else { endDic[trailingEndPos].Add(heading); } modified = true; } } if (modified) { args.UpdateScore(score); } }
private Score ConvertScore(IScoreBookImportPluginArgs args, SusScoreData raw) { var res = new Score() { TicksPerBeat = raw.TicksPerBeat }; res.Events.BpmChangeEvents = raw.BpmDefinitions.Select(p => new BpmChangeEvent() { Tick = p.Key, Bpm = p.Value }).ToList(); res.Events.TimeSignatureChangeEvents = raw.TimeSignatures.Select(p => { int factor = 1; for (int i = 2; i < 10; i++) { if (factor * p.Value % 1 == 0) { return new TimeSignatureChangeEvent() { Tick = p.Key, Numerator = (int)(factor * p.Value), DenominatorExponent = i } } ; factor *= 2; } throw new ArgumentException("Invalid time signature"); }).ToList(); foreach (var item in raw.ShortNotes['1']) { switch (item.Type) { case '1': res.Notes.Taps.Add(SetNotePosition(new Tap(), item.Position)); break; case '2': res.Notes.ExTaps.Add(SetNotePosition(new ExTap(), item.Position)); break; case '5': case '6': args.ReportDiagnostic(new Diagnostic(DiagnosticSeverity.Warning, $"やべーExTAPは通常ExTAPとしてインポートされます。(行: {item.LineIndex + 1})")); res.Notes.ExTaps.Add(SetNotePosition(new ExTap(), item.Position)); break; case '3': res.Notes.Flicks.Add(SetNotePosition(new Flick(), item.Position)); break; case '4': res.Notes.Damages.Add(SetNotePosition(new Damage(), item.Position)); break; } } foreach (var hold in raw.LongNotes['2']) { if (hold.Count != 2) { args.ReportDiagnostic(new Diagnostic(DiagnosticSeverity.Warning, $"始点と終点以外を含むホールド定義です。このホールドは無視されます。(行: {string.Join(", ", hold.Select(p => p.LineIndex + 1))})")); continue; } if (hold[0].Position.LaneIndex != hold[1].Position.LaneIndex || hold[0].Position.Width != hold[1].Position.Width) { args.ReportDiagnostic(new Diagnostic(DiagnosticSeverity.Warning, $"始点と終点のレーン位置が対応していないホールド定義です。このホールドは無視されます。(行: {string.Join(", ", hold.Select(p => p.LineIndex + 1))})")); continue; } res.Notes.Holds.Add(new Hold() { StartTick = hold[0].Position.Tick, Duration = hold[1].Position.Tick - hold[0].Position.Tick, LaneIndex = hold[0].Position.LaneIndex, Width = hold[0].Position.Width }); } foreach (var steps in raw.LongNotes['3']) { var slide = new Slide() { StartTick = steps[0].Position.Tick, StartLaneIndex = steps[0].Position.LaneIndex, StartWidth = steps[0].Position.Width }; foreach (var step in steps.Skip(1)) { if (step.Type == '4') { args.ReportDiagnostic(new Diagnostic(DiagnosticSeverity.Warning, $"スライド変曲点は中継点としてインポートされます。(行: {step.LineIndex + 1})")); } var stepTap = new Slide.StepTap(slide) { IsVisible = step.Type == '3' || step.Type == '2', TickOffset = step.Position.Tick - slide.StartTick }; stepTap.SetPosition(step.Position.LaneIndex - slide.StartLaneIndex, step.Position.Width - slide.StartWidth); slide.StepNotes.Add(stepTap); } res.Notes.Slides.Add(slide); } var airables = res.Notes.GetShortNotes().Cast <IAirable>() .Concat(res.Notes.Holds.Select(p => p.EndNote)) .Concat(res.Notes.Slides.Select(p => p.StepNotes.OrderByDescending(q => q.Tick).First())); var airablesDic = airables.Select(p => new { Note = p, Position = new NotePosition() { Tick = p.Tick, LaneIndex = p.LaneIndex, Width = p.Width } }) .GroupBy(p => p.Position) .ToDictionary(p => p.Key, p => p.Select(q => q.Note).ToList()); var usedAirs = new HashSet <IAirable>(); foreach (var item in raw.ShortNotes['5']) { if (!airablesDic.ContainsKey(item.Position)) { continue; } foreach (var airable in airablesDic[item.Position]) { if (usedAirs.Contains(airable)) { continue; } var air = new Air(airable); switch (item.Type) { case '1': case '2': air.HorizontalDirection = HorizontalAirDirection.Center; break; case '3': case '5': air.HorizontalDirection = HorizontalAirDirection.Left; break; case '4': case '6': air.HorizontalDirection = HorizontalAirDirection.Right; break; } switch (item.Type) { case '1': case '3': case '4': air.VerticalDirection = VerticalAirDirection.Up; break; case '2': case '5': case '6': air.VerticalDirection = VerticalAirDirection.Down; break; } res.Notes.Airs.Add(air); usedAirs.Add(airable); break; } } var usedAirActions = new HashSet <IAirable>(); foreach (var item in raw.LongNotes['4']) { if (!airablesDic.ContainsKey(item[0].Position)) { continue; } foreach (var airable in airablesDic[item[0].Position]) { if (usedAirActions.Contains(airable)) { continue; } var airAction = new AirAction(airable); foreach (var note in item.Skip(1)) { if (note.Position.LaneIndex != item[0].Position.LaneIndex || note.Position.Width != item[0].Position.Width) { args.ReportDiagnostic(new Diagnostic(DiagnosticSeverity.Warning, $"レーン位置が対応していないAir Action定義です。このAir Actionは無視されます。(行: {note.LineIndex + 1})")); continue; } var actionNote = new AirAction.ActionNote(airAction) { Offset = note.Position.Tick - item[0].Position.Tick }; airAction.ActionNotes.Add(actionNote); } res.Notes.AirActions.Add(airAction); usedAirActions.Add(airable); break; } } return(res); }