From b449ea455a268049b4cc3a56d29814ced5c6fb6d Mon Sep 17 00:00:00 2001 From: wancat Date: Fri, 8 Mar 2024 01:05:26 +0800 Subject: [PATCH] feat: add Session and stage active check --- packages/game/src/Session.ts | 17 +++++++++++++++++ packages/game/src/Stage.ts | 24 ++++++++++++++++++++---- packages/game/src/types.ts | 7 ++++++- packages/game/test/stage.test.ts | 19 +++++++++++++++++++ 4 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 packages/game/src/Session.ts diff --git a/packages/game/src/Session.ts b/packages/game/src/Session.ts new file mode 100644 index 0000000..e8b5417 --- /dev/null +++ b/packages/game/src/Session.ts @@ -0,0 +1,17 @@ +import { ISession } from './types' + +export class Session implements ISession { + private completed: string[] = [] + + constructor({ completedStages }: ISession) { + this.completed = completedStages + } + + get completedStages() { + return this.completed + } + + complete(stageId: string) { + this.completed.push(stageId) + } +} diff --git a/packages/game/src/Stage.ts b/packages/game/src/Stage.ts index 579bb74..e440488 100644 --- a/packages/game/src/Stage.ts +++ b/packages/game/src/Stage.ts @@ -1,15 +1,17 @@ -import { IStage, ICommand, IFileExistenceChecker } from './types' +import { IStage, ICommand, IFileExistenceChecker, ISession } from './types' import { Condition } from './condition/Condition' export class Stage { id: string name: string - condition: Condition + private condition: Condition + private requirements: string[] - constructor({ id, name, condition }: IStage) { + constructor({ id, name, condition, requirements }: IStage) { this.id = id - this.name = name + this.name = name || '' this.condition = new Condition(condition) + this.requirements = requirements || [] } async satisfies( @@ -18,4 +20,18 @@ export class Stage { ): Promise { return this.condition.match(command) && this.condition.check(checker) } + + private checkRequirements(session: ISession): boolean { + return this.requirements.every((req) => + session.completedStages.includes(req) + ) + } + + private finished(session: ISession): boolean { + return session.completedStages.includes(this.id) + } + + active(session: ISession): boolean { + return !this.finished(session) && this.checkRequirements(session) + } } diff --git a/packages/game/src/types.ts b/packages/game/src/types.ts index 52fff79..972200f 100644 --- a/packages/game/src/types.ts +++ b/packages/game/src/types.ts @@ -35,6 +35,11 @@ export interface IFileExistenceChecker { export interface IStage { id: string - name: string + name?: string + requirements?: string[] condition: ICondition } + +export interface ISession { + completedStages: string[] +} diff --git a/packages/game/test/stage.test.ts b/packages/game/test/stage.test.ts index 5631e21..de81ade 100644 --- a/packages/game/test/stage.test.ts +++ b/packages/game/test/stage.test.ts @@ -2,6 +2,7 @@ import { test, expect } from '@jest/globals' import { Stage } from '../src/Stage' import { FileType } from '../src/types' import { MockFileChecker } from './asyncCondition.test' +import { Session } from '../src/Session' const checker = new MockFileChecker() @@ -67,3 +68,21 @@ test('check stage with async condition falsy', async () => { }) expect(await stage.satisfies({}, checker)).toBe(false) }) + +test('stage active checks', () => { + const session = new Session({ + completedStages: ['stage1'], + }) + const stage3 = new Stage({ + id: 'stage3', + requirements: ['stage1', 'stage2'], + condition: {}, + }) + expect(stage3.active(session)).toBe(false) + + session.complete('stage2') + expect(stage3.active(session)).toBe(true) + + session.complete('stage3') + expect(stage3.active(session)).toBe(false) +})