Skip to content

Commit 4930403

Browse files
committed
[F&R] Shift谱面时,星星头的Time没有随之移动。解决方法是在BaseNote里创建Children字段,星星头保存在Children里面,这样通用的BaseChart.Shift函数就能找到这些星星头了。
1 parent b0e85a0 commit 4930403

5 files changed

Lines changed: 53 additions & 5 deletions

File tree

chart/BaseChart.cs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,20 @@ public virtual void Shift(Rational offset, decimal? bpm = null)
105105
MetList = MetList.Select(x => x with { Time = x.Time + offset }) // 同上
106106
.Skip(MetList.Count(x => x.Time <= 0) - 1)
107107
.Select((x, i) => i == 0 ? x with { Time = 0 } : x).ToList();
108-
// Notes,直接丢弃所有负数项即可
109-
Notes = Notes.Select(x => { x.Time += offset; return x; }).Where(x => x.Time >= 0).ToList();
108+
109+
// Notes,时间加上offset,但注意需要对children进行递归操作
110+
HashSet<BaseNote> processed = [];
111+
Notes = Notes.Where(x => addOffset(x).Time >= 0).ToList();
112+
113+
BaseNote addOffset(BaseNote note)
114+
{
115+
if (!processed.Contains(note))
116+
{
117+
note.Time += offset;
118+
processed.Add(note);
119+
foreach (var child in note.Children) addOffset(child);
120+
}
121+
return note;
122+
}
110123
}
111124
}

chart/BaseNote.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,10 @@ public abstract class BaseNote
1515
* 默认实现中实现为等于开始时刻(瞬间音符,没有持续时间)。有持续时间的音符应当重写此属性。
1616
*/
1717
public virtual Rational EndTime => Time;
18+
19+
/**
20+
* 如果有某个音符,包含另一个/一些音符作为子音符,这些子音符本身不会出现在Chart的Notes列表内:
21+
* 则应将这些子音符放在这里,以便Chart还能“通过某种方式索引到它们”(目前只有BaseChart.Shift函数需要用到这一特性),而不是根本找不到。
22+
*/
23+
public List<BaseNote> Children = [];
1824
}

chart/mai/Slide.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ public Star(Tap inTake): base(inTake) {} // 拷贝构造函数
1818
[DebuggerDisplay("{DebuggerDisplay(),nq}")]
1919
public class Slide : Note
2020
{
21-
public Tap? OwnHead; // 属于自己的星星头。只有在SharedHeadWith=null的那根星星上应该设置此项,否则直接通过SharedHeadWith链过去即可
21+
public Tap? OwnHead // 属于自己的星星头。同头星星的情况下,只有在SharedHeadWith=null的那一根星星上应该设置此项,其他的都直接通过SharedHeadWith链过去即可。
22+
{
23+
get => Children.FirstOrDefault() as Tap;
24+
set => Children.SetFirst(value);
25+
}
2226
// PS: 根据simai语法,星星头既可以是普通的星星形状(1-5),也可以是Tap形状的(1@-5),也可以没有(1?-5或1!-5)
2327
public Slide? SharedHeadWith;
2428
public List<SlideSegment> segments = new();

tests/mai/ChartShift测试.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,19 @@ public class ChartShift测试
1212
{
1313
private static readonly Rational QuarterBar = new(1, 4);
1414

15-
private static List<(Rational Time, int FalseEachIdx)> NotesInStableOrder(MaiChart c) =>
16-
c.Notes.OrderBy(n => n.Time).ThenBy(n => n.FalseEachIdx).Select(n => (n.Time, n.FalseEachIdx)).ToList();
15+
private static List<(Rational Time, int FalseEachIdx)> NotesInStableOrder(MaiChart c)
16+
{
17+
List<(Rational Time, int FalseEachIdx)> result = [];
18+
foreach (var n in c.Notes.OrderBy(n => n.Time).ThenBy(n => n.FalseEachIdx))
19+
{
20+
result.Add((n.Time, n.FalseEachIdx));
21+
if (n is Slide slide && slide.OwnHead != null)
22+
{
23+
result.Add((slide.OwnHead.Time, slide.OwnHead.FalseEachIdx));
24+
}
25+
}
26+
return result;
27+
}
1728

1829
private static List<BPM> BpmInOrder(MaiChart c) => c.BpmList.OrderBy(x => x.Time).ToList();
1930

utils/Utils.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,18 @@ internal static Dictionary<K, V> Concat<K, V>(this Dictionary<K, V> dict, Dictio
7979
foreach (var (k, v) in dict2) dict[k] = v;
8080
return dict;
8181
}
82+
83+
internal static void SetFirst<T>(this List<T> list, T? item)
84+
{
85+
if (item != null)
86+
{
87+
if (list.Count == 0) list.Add(item);
88+
else list[0] = item;
89+
}
90+
else
91+
{
92+
if (list.Count == 1) list.RemoveAt(0);
93+
else if (list.Count > 1) list[0] = item!; // item是null,所以直接赋值进去就是null了
94+
}
95+
}
8296
}

0 commit comments

Comments
 (0)