diff --git a/src/samples/slay-the-spire-like/data/index.ts b/src/samples/slay-the-spire-like/data/index.ts index b9747b4..a7d5255 100644 --- a/src/samples/slay-the-spire-like/data/index.ts +++ b/src/samples/slay-the-spire-like/data/index.ts @@ -3,14 +3,17 @@ import encounterDesertCsv from './encounterDesert.csv'; import enemyDesertCsv from './enemyDesert.csv'; import enemyIntentDesertCsv from './enemyIntentDesert.csv'; import effectDesertCsv from './effectDesert.csv'; +import statusCardDesertCsv from './statusCardDesert.csv'; export const heroItemFighter1Data = heroItemFighter1Csv(); export const encounterDesertData = encounterDesertCsv(); export const enemyDesertData = enemyDesertCsv(); export const enemyIntentDesertData = enemyIntentDesertCsv(); export const effectDesertData = effectDesertCsv(); +export const statusCardDesertData = statusCardDesertCsv(); export { default as encounterDesertCsv, type EncounterDesert } from './encounterDesert.csv'; export { default as enemyDesertCsv, type EnemyDesert } from './enemyDesert.csv'; export { default as enemyIntentDesertCsv, type EnemyIntentDesert } from './enemyIntentDesert.csv'; export { default as effectDesertCsv, type EffectDesert } from './effectDesert.csv'; +export { default as statusCardDesertCsv, type StatusCardDesert } from './statusCardDesert.csv'; diff --git a/src/samples/slay-the-spire-like/data/rules.md b/src/samples/slay-the-spire-like/data/rules.md new file mode 100644 index 0000000..5c244f5 --- /dev/null +++ b/src/samples/slay-the-spire-like/data/rules.md @@ -0,0 +1,72 @@ +# 战斗规则 + +## 战斗状态 + +角色表: +- 敌方角色表 +- 我方角色表 + +角色状态: +- hp/最大hp +- buff表(名称->层数,同名buff合并叠加) + +战利品表: +- 类型 +- 数量 + +触发规则表: +- 类型 +- 层数 + +## 战斗开始 + +战斗开始时,首先进行敌方角色的战斗开始结算,然后进行玩家的战斗开始结算。 + +战斗开始会让玩家抓5张起始手牌,让敌方角色初始化意图。 + +## 回合 + +进行一个玩家回合,然后进行一个敌方回合,以此重复,直到不存在玩家或敌方角色存活。 + +玩家回合包含以下阶段: + +- buff更新:temporary buff清空,lingering buff层数-1。 +- 回合开始:触发回合开始结算的效果。 +- 玩家行动:玩家可以花费能量打出手牌。点结束来结束行动。 +- 回合结束:触发回合结束结算的效果。 +- 重置手牌:弃掉剩余的手牌,重新抓5张手牌。 +- 重置能量:重置到3点能量。 + +敌人回合包含以下阶段: + +- buff更新:每个敌人的temporary buff清空,lingering buff层数-1。 +- 回合开始:触发回合开始结算的效果。 +- 敌人行动:每个敌人的意图依次生效,然后更新下一个意图。 +- 回合结束:触发回合结束结算的效果。 + +## 效果 + +效果结算时,具有以下上下文: +- name: 效果名称 +- stacks: 层数 +- target: 目标角色,如果有 +- source:来源角色,如果有 +- card:来源卡牌,如果有 + +效果有以下类型: + +- Instant: 立即结算。 + +- Buff: 施加为buff,持续整场战斗。 +- BuffTemporary: 施加为buff,下次buff更新时清空。 +- BuffLingering: 施加为buff,下次buff更新时层数-1。 +- BuffPosture: 施加为buff,受到伤害时扣除等量层数。 + +- Item: 施加为物品buff,对来源卡牌对应的物品周围的物品生效。 +- ItemUntilPlayed:施加为物品buff,对来源卡牌对应的物品生效,生效一次后失效。 +- ItemTemporary: 施加为物品buff,对来源卡牌对应的物品生效,下次buff更新时清空。 +- ItemPermanent: 施加为物品buff,对来源卡牌对应的物品生效,战斗结束后依然有效。 + +- Card: 施加为状态卡牌,洗入玩家弃牌堆。对敌人无效。 +- CardDraw:洗入抓牌堆。 +- CardHand:加入手牌。 \ No newline at end of file diff --git a/src/samples/slay-the-spire-like/data/statusCardDesert.csv b/src/samples/slay-the-spire-like/data/statusCardDesert.csv new file mode 100644 index 0000000..c03a258 --- /dev/null +++ b/src/samples/slay-the-spire-like/data/statusCardDesert.csv @@ -0,0 +1,10 @@ +# 状态牌:由某些效果创建,洗入玩家牌堆或手牌 +# unplayable: 是否不可打出 +# effects: 状态牌在场时施加的效果 + +id, name, desc, unplayable, effects +string, string, string, boolean, ['self'; @effectDesert; number][] +wound, 伤口, 无效果,占用手牌和牌堆, true, +venom, 蛇毒, 弃掉超过1张蛇毒时受到6伤害, true, [self; venom; 1] +curse, 诅咒, 受攻击时物品攻击-1,直到弃掉一张该物品的牌, true, [self; curse; 1] +static, 静电, 在手里时受电击伤害+1, true, [self; static; 1] diff --git a/src/samples/slay-the-spire-like/data/statusCardDesert.csv.d.ts b/src/samples/slay-the-spire-like/data/statusCardDesert.csv.d.ts new file mode 100644 index 0000000..45134c5 --- /dev/null +++ b/src/samples/slay-the-spire-like/data/statusCardDesert.csv.d.ts @@ -0,0 +1,14 @@ +import type { EffectDesert } from './effectDesert.csv'; + +type StatusCardDesertTable = readonly { + readonly id: string; + readonly name: string; + readonly desc: string; + readonly unplayable: boolean; + readonly effects: readonly ["self", EffectDesert, number]; +}[]; + +export type StatusCardDesert = StatusCardDesertTable[number]; + +declare function getData(): StatusCardDesertTable; +export default getData; diff --git a/tests/samples/slay-the-spire-like/data/index.test.ts b/tests/samples/slay-the-spire-like/data/index.test.ts index 30e77ef..19a66de 100644 --- a/tests/samples/slay-the-spire-like/data/index.test.ts +++ b/tests/samples/slay-the-spire-like/data/index.test.ts @@ -5,6 +5,7 @@ import { enemyDesertData, enemyIntentDesertData, effectDesertData, + statusCardDesertData, } from '@/samples/slay-the-spire-like/data'; describe('heroItemFighter1.csv import', () => { @@ -258,3 +259,53 @@ describe('enemyIntentDesert.csv import', () => { expect(enemyIds.size).toBe(14); }); }); + +describe('statusCardDesert.csv import', () => { + it('should import data as an array', () => { + expect(Array.isArray(statusCardDesertData)).toBe(true); + expect(statusCardDesertData.length).toBeGreaterThan(0); + }); + + it('should have expected number of status cards', () => { + expect(statusCardDesertData.length).toBe(4); + }); + + it('should have correct fields for each status card', () => { + for (const card of statusCardDesertData) { + expect(card).toHaveProperty('id'); + expect(card).toHaveProperty('name'); + expect(card).toHaveProperty('desc'); + expect(card).toHaveProperty('unplayable'); + expect(card).toHaveProperty('effects'); + expect(typeof card.id).toBe('string'); + expect(typeof card.name).toBe('string'); + expect(typeof card.desc).toBe('string'); + expect(typeof card.unplayable).toBe('boolean'); + } + }); + + it('should have all cards unplayable', () => { + for (const card of statusCardDesertData) { + expect(card.unplayable).toBe(true); + } + }); + + it('should have effects with target, effect ref, and value', () => { + for (const card of statusCardDesertData) { + expect(Array.isArray(card.effects)).toBe(true); + for (const [target, effect, value] of card.effects) { + expect(target).toBe('self'); + expect(typeof effect === 'string' || typeof effect === 'object').toBe(true); + expect(typeof value).toBe('number'); + } + } + }); + + it('should contain expected status cards by id', () => { + const ids = statusCardDesertData.map(c => c.id); + expect(ids).toContain('wound'); + expect(ids).toContain('venom'); + expect(ids).toContain('curse'); + expect(ids).toContain('static'); + }); +});