diff --git a/packages/common/src/controlPoints/ControlPointInfo.ts b/packages/common/src/controlPoints/ControlPointInfo.ts index a2906932..d2a08aca 100644 --- a/packages/common/src/controlPoints/ControlPointInfo.ts +++ b/packages/common/src/controlPoints/ControlPointInfo.ts @@ -231,7 +231,11 @@ export class ControlPointInfo extends AbstractCrdt { return this.controlPointLists.flatMap(list => list.filter(it => almostEquals(it.time, time, 1))); } - override get childObjects(): AbstractCrdt[] { + override get childObjects(): AbstractCrdt[] { + return this.allControlPoints; + } + + get allControlPoints(): ControlPoint[] { return this.controlPointLists.flatMap(it => it.items); } } diff --git a/packages/common/src/verifier/checks/timing/CheckFirstLine.ts b/packages/common/src/verifier/checks/timing/CheckFirstLine.ts new file mode 100644 index 00000000..95499b61 --- /dev/null +++ b/packages/common/src/verifier/checks/timing/CheckFirstLine.ts @@ -0,0 +1,39 @@ +import type { CheckMetadata } from '../../BeatmapCheck'; +import type { Issue } from '../../Issue'; +import type { VerifierBeatmap } from '../../VerifierBeatmap'; +import { TimingPoint } from '../../../controlPoints/TimingPoint'; +import { minBy } from '../../../utils/arrayUtils'; +import { BeatmapCheck } from '../../BeatmapCheck'; +import { IssueTemplate } from '../../template/IssueTemplate'; + +export class CheckFirstLine extends BeatmapCheck { + override get metadata(): CheckMetadata { + return { + category: 'Timing', + message: 'First line toggles kiai or is inherited.', + author: 'Naxess', + }; + } + + override templates = { + inherited: new IssueTemplate('problem', '{0:timestamp} First timing line is inherited.', 'timestamp - ').withCause('The first timing line of a beatmap is inherited.'), + togglesKiai: new IssueTemplate('problem', '{0:timestamp} First timing line toggles kiai.', 'timestamp - ').withCause('The first timing line of a beatmap has kiai enabled.'), + noLines: new IssueTemplate('problem', 'There are no timing lines.').withCause('A beatmap has no timing lines.'), + }; + + override async * getIssues(beatmap: VerifierBeatmap): AsyncGenerator { + const firstTimingPoint = beatmap.controlPoints.timingPoints.first; + if (!firstTimingPoint) { + yield this.createIssue(this.templates.noLines, beatmap); + return; + } + + const firstControlPoint = minBy(beatmap.controlPoints.allControlPoints, it => it.time); + + if (!(firstControlPoint instanceof TimingPoint) && firstControlPoint.time < firstTimingPoint.time) + yield this.createIssue(this.templates.inherited, beatmap, firstControlPoint.time); + + if (beatmap.controlPoints.effectPointAt(firstTimingPoint.time).kiaiMode) + yield this.createIssue(this.templates.togglesKiai, beatmap, firstControlPoint.time); + } +}