Skip to content

Commit bbc7671

Browse files
committed
[+] Ongeki的谱面IR
1 parent 2fe8e8e commit bbc7671

6 files changed

Lines changed: 223 additions & 2 deletions

File tree

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,3 +356,12 @@ public sealed class FooGenerator : IGenerator<MaiChart>
356356
- 详见上文[多语言(i18n)相关](#多语言i18n相关)部分的说明。
357357
7. **CLI**(可选):如需实现CLI,可在`Program.cs`中增加相应的功能。
358358
359+
### Ongeki谱面的数据结构实现
360+
- 关于ogkr格式中各种指令的含义,可参考[ogkr格式详解](https://github.com/MikiraSora/OngekiFumianDescription/blob/master/ongeki.md)和[补充说明](https://github.com/MikiraSora/OngekiFumianDescription/blob/master/description.md)。
361+
- ogkr格式当中,大量的采用列举Pallete列表+使用ID引用具体的数据的实现方式。
362+
- 例如子弹`BLT`指令,要通过`strId`引用`[B_PALLETE]`中内容,Tap、Hold等音符需要通过`groupId`引用`Lane`相关的内容,等
363+
- 而我们的数据结构实现的过程中,采用直接的对象引用而不是ID字符串引用;同时对于`BulletPallete``Lane`,谱面数据结构中**不会保存其ID**
364+
- 因此,推荐的做法是:
365+
- 在Parser的解析过程中,在Parser内部缓存ID和具体对象的映射关系,每次解析完一个`[B_PALLETE]``Lane`对象时就把它放进该内部缓存中,以供后面解析具体的`BLT`、Tap时直接找到对象。
366+
- 在Generator的生成过程中,也是在内部建立缓存、为每个相关对象编好ID,然后生成引用这些对象的具体音符的时候直接写入编号。
367+
- 也就是说,**ID相关的逻辑应当由Parser和Generator内部自行实现,谱面数据结构中不会保存ID。**

chart/ogk/Bullet.cs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using MuConvert.utils;
2+
using Rationals;
3+
4+
namespace MuConvert.ogk;
5+
6+
7+
public interface IBullet
8+
{
9+
public BulletPallete? Detail { get; set; }
10+
}
11+
12+
/**
13+
* 声明子弹的细节行为,包括运动路径、速度、类型、尺寸等。
14+
*
15+
* record各项声明的默认值,对应于一种子弹静止不动的最简单情况,也就是官谱中的A0(绿谱当中能见到的子弹基本都是这种情况)
16+
*/
17+
public record BulletPallete(
18+
BulletShooter Shooter = BulletShooter.UPS, // 发出子弹的位置
19+
20+
bool TargetToPlayer = false, // true表示该子弹射向玩家(取子弹生成时玩家的当前位置为目标位置);false表示子弹射向基于TargetOffset所算出的目标位置。
21+
int TargetOffset = 0, // 子弹的终点位置相对于起点位置的偏移量 (仅当TargetToPlayer为false时有效)。多数情况下,此值设置为0,得到的是不动的子弹,反之就是动的子弹。
22+
int RandomOffsetDist = 0, // 如果不为0,则是随机子弹,结束位置会在正常位置的基础上随机偏移至多N格。
23+
24+
decimal Speed = 1.0m,
25+
BulletSize Size = BulletSize.N,
26+
BulletType Type = BulletType.CIR
27+
);
28+
29+
public class Bullet: IBullet
30+
{
31+
public BulletPallete Detail { get; set; } = new();
32+
33+
public Rational Time;
34+
public int Pos;
35+
36+
public BulletDamage Damage = BulletDamage.NML;
37+
}
38+
39+
public record BeamPoint(Rational Time, int Pos, int Width) : OgkLanePoint(Time, Pos);
40+
public class Beam: OgkBaseLane<BeamPoint>, IBullet
41+
{
42+
public int ObliqueOffset = 0; // 如果不为0,表示这个激光是倾斜激光(OBS),否则是一般的激光(BMS)
43+
44+
// 激光是没有IBullet的Detail属性的。但接口还必须实现一个,所以实现成返回null、不准设置。
45+
public BulletPallete? Detail { get => null; set => throw new InvalidOperationException("Beam has no bullet detail!"); }
46+
}

chart/ogk/Lane.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using MuConvert.utils;
2+
using Rationals;
3+
4+
namespace MuConvert.ogk;
5+
6+
public record OgkLanePoint(Rational Time, int Pos);
7+
8+
public abstract class OgkBaseLane<T> where T: OgkLanePoint
9+
{
10+
public List<T> Points;
11+
12+
public Rational Time => Points.First().Time;
13+
}
14+
15+
public class Lane(LaneType type): OgkBaseLane<OgkLanePoint>
16+
{
17+
public LaneType Type = type;
18+
public bool IsTransparent = false;
19+
}
20+
21+
public class ColorfulLane() : Lane(LaneType.Colorful)
22+
{
23+
public ColorfulLaneColor Color;
24+
public int Brightness;
25+
}
26+
27+
// 这里的Start,是相对于Wall的开始时刻的!不是绝对时间!
28+
public record Block(Rational Start, Rational Duration, int Pos, int EndPos);
29+
30+
public class Wall() : Lane(LaneType.Wall)
31+
{
32+
public Direction Direction;
33+
34+
/**
35+
* 给墙壁上添加“阻挡区域”。
36+
* 注意!Block中的Start是相对于Wall的起始时刻(Time)的,而不是绝对时间。
37+
*
38+
* 默认的“墙壁”描述的仅仅是场地的有效范围,显然玩家摇杆是可以穿越出去的;
39+
* 但如果这里加上Blocks的话,玩家摇杆就无法穿越出去了。
40+
*/
41+
public List<Block> Blocks = [];
42+
}
43+
44+
public class EnemyMovement: OgkBaseLane<OgkLanePoint>;

chart/ogk/OgkChart.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,38 @@
11
using MuConvert.chart;
2+
using MuConvert.utils;
3+
using Rationals;
24

35
namespace MuConvert.ogk;
46

57
public class OgkChart: BaseChart<OgkNote>
68
{
9+
public string Designer { get; set; } = ""; // 谱师
10+
11+
// 全局声明的“子弹伤害类型与伤害数值的映射关系”。绝大多数情况下都是这个默认值,不需要做改动。
12+
public Dictionary<BulletDamage, decimal> BulletDamages = new() {
13+
[BulletDamage.NML] = 1.0m, [BulletDamage.STR] = 2.0m, [BulletDamage.DNG] = 4.0m,
14+
};
15+
16+
/**
17+
* 游戏中所有的轨道。
18+
* 包括:红绿蓝线、左墙右墙(也就是侧键的轨道)、以及部分谱面中会出现的彩色线
19+
*/
20+
public List<Lane> Lanes = [];
21+
22+
/**
23+
* 游戏中所有的伤害弹。
24+
* 目前包括子弹(Bullet)和激光(Beam)两种子类型。
25+
*/
26+
public List<IBullet> Bullets = [];
27+
28+
/**
29+
* 指定切换敌人的时刻。
30+
*
31+
* 可以不显式指定(保持默认的空数组状态),此时游戏会自动应用“前半小怪、后半boss”的机制。
32+
* Type是一个字符串,游戏内的典型取值:"WAVE1" "WAVE2" "BOSS"
33+
*/
34+
public List<(Rational Time, string Type)> EnemyList = [];
735

36+
// 部分谱面中会有,上方的敌人在上面进行水平方向的移动的情况。
37+
public List<EnemyMovement> EnemyMovements = [];
838
}

chart/ogk/OgkNote.cs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,41 @@
11
using MuConvert.chart;
2+
using MuConvert.utils;
3+
using Rationals;
24

35
namespace MuConvert.ogk;
46

5-
public class OgkNote: BaseNote
7+
/**
8+
* 所有游戏内的参与计分的基础音符所构成的基类。
9+
* 包括:Tap(普通键和侧键)、Hold(普通键和侧键)、Flick、Bell四种子类。
10+
*/
11+
public abstract class OgkNote: BaseNote
612
{
13+
/**
14+
* 音符的水平位置。取值范围[-24,24]。
15+
* 之所以用Rational,是因为极个别TAP/HOLD音符可能不在整数位置上;但绝大多数音符,包括所有的bell、flick,都一定是在整数位置上的。
16+
*/
17+
public Rational Pos { get; set; }
718

8-
}
19+
public bool IsEx;
20+
}
21+
22+
public class Tap: OgkNote
23+
{
24+
public required Lane Lane;
25+
}
26+
27+
public class Hold : Tap
28+
{
29+
public override Rational EndTime { get; set; }
30+
public Rational EndPos;
31+
}
32+
33+
public class Flick : OgkNote
34+
{
35+
public Direction Direction;
36+
}
37+
38+
public class Bell: OgkNote, IBullet
39+
{
40+
public BulletPallete? Detail { get; set; } = null; // 大多数的bell默认是原地不动的。如果指定此属性,则可以实现和子弹类似的可动bell的效果。
41+
}

utils/OgkEnums.cs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
namespace MuConvert.utils;
2+
3+
public enum LaneType
4+
{
5+
Red,
6+
Green,
7+
Blue,
8+
Colorful, // 多彩线,颜色详见ColorfulLaneColor枚举
9+
Wall, // 墙壁,即场地的左右边界
10+
}
11+
12+
public enum ColorfulLaneColor
13+
{
14+
Akari = 0,
15+
Yuzu = 1,
16+
Rio = 2,
17+
Riku = 3,
18+
Tsubaki = 4,
19+
Ayaka = 5,
20+
Kaede = 6,
21+
Saki = 7,
22+
Koboshi = 8,
23+
Arisu = 9,
24+
Mia = 10,
25+
Chinatsu = 11,
26+
Tsumugi = 12,
27+
Setsuna = 13,
28+
Brown = 14,
29+
Haruna = 15,
30+
Black = 16,
31+
Akane = 17,
32+
G = 18,
33+
Aoi = 19,
34+
}
35+
36+
public enum Direction { L, R }
37+
38+
public enum BulletSize { N, L } // Normal or Large
39+
40+
public enum BulletType
41+
{
42+
CIR, // 圆形子弹
43+
NDL, // 针状子弹
44+
SQR, // 圆柱形(方状)子弹
45+
}
46+
47+
public enum BulletShooter
48+
{
49+
UPS, // 从具体BLT命令行中所指定的那个Pos位置
50+
ENE, // 从敌人位置
51+
CEN, // 从谱面中心即Pos=0的位置
52+
}
53+
54+
public enum BulletDamage
55+
{
56+
NML, // Normal 使用BULLET_DAMAGE伤害
57+
STR, // Hard 使用HARDBULLET_DAMAGE伤害
58+
DNG, // Danger 使用DANGERBULLET_DAMAGE伤害
59+
}

0 commit comments

Comments
 (0)