Compare commits
No commits in common. "09148f5b13c335e055c7a2a57616122d2b6a3594" and "5860f2a247af18fdcad49a2e469b73a8da88063d" have entirely different histories.
09148f5b13
...
5860f2a247
|
|
@ -6,5 +6,5 @@ type BoopPartTable = readonly {
|
|||
|
||||
export type BoopPart = BoopPartTable[number];
|
||||
|
||||
declare function getData(): BoopPartTable;
|
||||
export default getData;
|
||||
declare const data: BoopPartTable;
|
||||
export default data;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import getParts from './boopPart.csv';
|
||||
import parts from './boopPart.csv';
|
||||
import {createRegion, moveToRegion, Region} from "@/core/region";
|
||||
import {createPartsFromTable} from "@/core/part-factory";
|
||||
import {BoopPart} from "@/samples/boop/types";
|
||||
|
|
@ -6,7 +6,6 @@ import {BOARD_SIZE} from "@/samples/boop/constants";
|
|||
import {PlayerType, PieceType, RegionType} from "@/samples/boop/types";
|
||||
|
||||
export function createInitialState() {
|
||||
const parts = getParts();
|
||||
const pieces = createPartsFromTable(
|
||||
parts,
|
||||
(item, index) => `${item.player}-${item.type}-${index + 1}`,
|
||||
|
|
|
|||
|
|
@ -7,5 +7,5 @@ type CardsTable = readonly {
|
|||
|
||||
export type Cards = CardsTable[number];
|
||||
|
||||
declare function getData(): CardsTable;
|
||||
export default getData;
|
||||
declare const data: CardsTable;
|
||||
export default data;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import getCards from './cards.csv';
|
||||
import cards from './cards.csv';
|
||||
import {Part} from "@/core/part";
|
||||
import {createRegion, createRegionAxis, Region} from "@/core/region";
|
||||
import {createPromptDef, IGameContext} from "@/core/game";
|
||||
|
|
@ -38,7 +38,6 @@ export function createGameInfo(){
|
|||
}
|
||||
|
||||
export function createCards(){
|
||||
const cards = getCards();
|
||||
const dataMap = {} as Record<string, CardData>;
|
||||
|
||||
for(const entry of cards){
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
id, name, description
|
||||
string, string, string
|
||||
attack, 攻击, 对对手造成伤害
|
||||
defend, 防御, 抵消下次行动前受到的伤害
|
||||
spike, 尖刺, 对攻击者造成X点伤害
|
||||
venom, 蛇毒, 弃掉超过1张蛇毒时受到6伤害
|
||||
curse, 诅咒, 受攻击时物品攻击-1,直到弃掉一张该物品的牌
|
||||
aim, 瞄准, 造成双倍伤害,受伤时失去等量瞄准
|
||||
roll, 滚动, 攻击时每消耗10点滚动造成等量伤害
|
||||
rollDamage, 滚动攻击, 消耗滚动层数造成的伤害
|
||||
vultureEye, 秃鹫之眼, 受到伤害时自动从手牌打出秃鹫的攻击
|
||||
tailSting, 尾刺, 回合结束时对玩家造成X点攻击
|
||||
energyDrain, 能量吸取, 每回合首次受伤时玩家失去1能量
|
||||
molt, 脱皮, 达到生命上限时怪物逃跑
|
||||
discard, 弃牌, 弃掉物品的牌
|
||||
storm, 风暴, 攻击时给玩家塞入1张静电
|
||||
static, 静电, 在手里时受电击伤害+1
|
||||
charge, 冲锋, 受到或造成的伤害翻倍并消耗等量冲锋
|
||||
summonMummy, 召唤木乃伊, 召唤1个木乃伊
|
||||
summonSandwormLarva, 召唤幼沙虫, 召唤1个幼沙虫
|
||||
reviveMummy, 复活木乃伊, 复活1个已死亡的木乃伊
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
type EffectDesertTable = readonly {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly description: string;
|
||||
}[];
|
||||
|
||||
export type EffectDesert = EffectDesertTable[number];
|
||||
|
||||
declare function getData(): EffectDesertTable;
|
||||
export default getData;
|
||||
|
|
@ -4,32 +4,32 @@
|
|||
# shop (2): merchant who sells different stuff
|
||||
# camp (2): consumable restock and heal
|
||||
# curio (8): random pickup of treasure or resources
|
||||
type,name,description,enemies,dialogue
|
||||
'minion'|'elite'|'event'|'shop'|'camp'|'curio',string,string,[enemy: @enemyDesert;bonusHp: number][],string
|
||||
minion,仙人掌怪,概念:防+强化。【尖刺X】:对攻击者造成X点伤害。,[仙人掌怪;0];[仙人掌怪;0],
|
||||
minion,蛇,概念:攻+强化。给玩家塞入蛇毒牌(消耗。一回合弃掉超过1张蛇毒时,受到6伤害)。,[蛇;0];[蛇;0],
|
||||
minion,木乃伊,概念:攻+防。【诅咒】:受攻击时物品【攻击】-1,直到弃掉一张该物品的牌。,[木乃伊;0];[仙人掌怪;0],
|
||||
minion,枪手,概念:单回高攻。【瞄准X】:造成双倍伤害。受伤时失去等量【瞄准】,[枪手;0],
|
||||
minion,风卷草,概念:防+强化。【滚动X】:攻击时,每消耗10点【滚动】,造成等量伤害。,[风卷草;0];[风卷草;0],
|
||||
minion,秃鹫,概念:攻+防。造成伤害后玩家获得秃鹫之眼(当你受到伤害时自动从手牌打出受到秃鹫的攻击)。,[秃鹫;0];[仙人掌怪;0],
|
||||
minion,沙蝎,概念:攻+强化。【尾刺X】:玩家回合结束时受到沙蝎的X点攻击。受伤时失去等量【尾刺】。,[沙蝎;0];[蛇;0],
|
||||
minion,幼沙虫,概念:防+强化。每回合第一次受伤时,玩家失去1点能量。,[幼沙虫;0],
|
||||
minion,蜥蜴,概念:攻+防+逃跑。【脱皮】:若脱皮达到生命上限,则怪物逃跑,玩家不能获得战斗奖励。,[蜥蜴;0];[蜥蜴;0],
|
||||
minion,沙匪,概念:攻特化。洗牌时,将一个随机物品的牌全部弃掉。,[沙匪;0];[枪手;0],
|
||||
elite,风暴之灵,【风暴X】:攻击时,玩家获得1张静电。受伤时失去等量【风暴】。(静电:在手里时受【电击】伤害+1),[风暴之灵;0],
|
||||
elite,骑马枪手,【冲锋X】:受到或造成的伤害翻倍并消耗等量的冲锋。,[骑马枪手;0];[枪手;4],
|
||||
elite,沙虫王,召唤幼体沙虫;每当玩家弃掉一张牌,恢复1生命。,[沙虫王;0],
|
||||
elite,沙漠守卫,召唤木乃伊;会复活木乃伊2次。,[沙漠守卫;0];[木乃伊;2],
|
||||
shop,沙漠商人,商店:可以恢复生命、出售装备、附魔物品。,,
|
||||
shop,游牧商队,商队:出售稀有物品、移除牌组中一张牌。,,
|
||||
camp,绿洲篝火,篝火:可以恢复生命、补充药水使用次数、获得下次战斗Buff。,,
|
||||
camp,岩洞庇护所,篝火:可以恢复生命、升级一张牌。,,
|
||||
curio,沙中遗物,随机获得一件遗物或受到3点伤害。,,desert_relic_in_sand
|
||||
curio,枯井,投入1能量:可能获得药水或什么也没有。,,desert_dry_well
|
||||
curio,古代石碑,阅读碑文:获得随机Buff直到下次战斗结束。,,desert_ancient_stele
|
||||
curio,沙暴残骸,搜索残骸:随机获得一张物品牌或受到2点伤害。,,desert_storm_wreckage
|
||||
curio,蜃景宝箱,打开宝箱:50%获得宝藏,50%为蜃景什么也没有。,,desert_mirage_chest
|
||||
curio,埋藏陶罐,挖掘:获得随机资源(金币、药水或遗物碎片)。,,desert_buried_pot
|
||||
curio,风化雕像,献祭1生命:获得一件随机遗物。,,desert_weathered_statue
|
||||
curio,绿洲碎片,小型绿洲:恢复3生命并获得1张随机消耗品。,,desert_oasis_fragment
|
||||
event,海市蜃楼,随机遭遇:可能获得宝藏或遭遇陷阱,使用d6双阶段结构结算。,,desert_mirage_event
|
||||
type,name,description
|
||||
'minion'|'elite'|'event'|'shop'|'camp'|'curio',string,string
|
||||
minion,仙人掌怪,概念:防+强化。【尖刺X】:对攻击者造成X点伤害。
|
||||
minion,蛇,概念:攻+强化。给玩家塞入蛇毒牌(消耗。一回合弃掉超过1张蛇毒时,受到6伤害)。
|
||||
minion,木乃伊,概念:攻+防。【诅咒】:受攻击时物品【攻击】-1,直到弃掉一张该物品的牌。
|
||||
minion,枪手,概念:单回高攻。【瞄准X】:造成双倍伤害。受伤时失去等量【瞄准】。
|
||||
minion,风卷草,概念:防+强化。【滚动X】:攻击时,每消耗10点【滚动】,造成等量伤害。
|
||||
minion,秃鹫,概念:攻+防。造成伤害后玩家获得秃鹫之眼(当你受到伤害时自动从手牌打出受到秃鹫的攻击)。
|
||||
minion,沙蝎,概念:攻+强化。【尾刺X】:玩家回合结束时受到沙蝎的X点攻击。受伤时失去等量【尾刺】。
|
||||
minion,幼沙虫,概念:防+强化。每回合第一次受伤时,玩家失去1点能量。
|
||||
minion,蜥蜴,概念:攻+防+逃跑。【脱皮】:若脱皮达到生命上限,则怪物逃跑,玩家不能获得战斗奖励。
|
||||
minion,沙匪,概念:攻特化。洗牌时,将一个随机物品的牌全部弃掉。
|
||||
elite,风暴之灵,【风暴X】:攻击时,玩家获得1张静电。受伤时失去等量【风暴】。(静电:在手里时受【电击】伤害+1)
|
||||
elite,骑马枪手,【冲锋X】:受到或造成的伤害翻倍并消耗等量的冲锋。
|
||||
elite,沙虫王,召唤幼体沙虫;每当玩家弃掉一张牌,恢复1生命。
|
||||
elite,沙漠守卫,召唤木乃伊;会复活木乃伊2次。
|
||||
shop,沙漠商人,商店:可以恢复生命、出售装备、附魔物品。
|
||||
shop,游牧商队,商队:出售稀有物品、移除牌组中一张牌。
|
||||
camp,绿洲篝火,篝火:可以恢复生命、补充药水使用次数、获得下次战斗Buff。
|
||||
camp,岩洞庇护所,篝火:可以恢复生命、升级一张牌。
|
||||
curio,沙中遗物,随机获得一件遗物或受到3点伤害。
|
||||
curio,枯井,投入1能量:可能获得药水或什么也没有。
|
||||
curio,古代石碑,阅读碑文:获得随机Buff直到下次战斗结束。
|
||||
curio,沙暴残骸,搜索残骸:随机获得一张物品牌或受到2点伤害。
|
||||
curio,蜃景宝箱,打开宝箱:50%获得宝藏,50%为蜃景什么也没有。
|
||||
curio,埋藏陶罐,挖掘:获得随机资源(金币、药水或遗物碎片)。
|
||||
curio,风化雕像,献祭1生命:获得一件随机遗物。
|
||||
curio,绿洲碎片,小型绿洲:恢复3生命并获得1张随机消耗品。
|
||||
event,海市蜃楼,随机遭遇:可能获得宝藏或遭遇陷阱,使用d6双阶段结构结算。
|
||||
|
|
|
|||
|
|
|
@ -1,14 +1,10 @@
|
|||
import type { EnemyDesert } from './enemyDesert.csv';
|
||||
|
||||
type EncounterDesertTable = readonly {
|
||||
readonly type: "minion" | "elite" | "event" | "shop" | "camp" | "curio";
|
||||
readonly name: string;
|
||||
readonly description: string;
|
||||
readonly enemies: readonly [readonly enemy: EnemyDesert, readonly bonusHp: number];
|
||||
readonly dialogue: string;
|
||||
}[];
|
||||
|
||||
export type EncounterDesert = EncounterDesertTable[number];
|
||||
|
||||
declare function getData(): EncounterDesertTable;
|
||||
export default getData;
|
||||
declare const data: EncounterDesertTable;
|
||||
export default data;
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
id,initHp,initBuffs,initialIntent
|
||||
string, int, [effect: @effectDesert; stacks: int][], string
|
||||
仙人掌怪,20,[spike;1],boost
|
||||
蛇,14,,poison
|
||||
木乃伊,18,,attack
|
||||
枪手,16,,aim
|
||||
风卷草,22,,boost
|
||||
秃鹫,16,,attack
|
||||
沙蝎,14,[tailSting;1],boost
|
||||
幼沙虫,24,[energyDrain;1],defend
|
||||
蜥蜴,20,,attack
|
||||
沙匪,16,[discard;1],attack
|
||||
风暴之灵,44,,storm
|
||||
骑马枪手,50,,charge
|
||||
沙虫王,55,,summon
|
||||
沙漠守卫,48,,summon
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
import type { EffectDesert } from './effectDesert.csv';
|
||||
|
||||
type EnemyDesertTable = readonly {
|
||||
readonly id: string;
|
||||
readonly initHp: number;
|
||||
readonly initBuffs: readonly [readonly effect: EffectDesert, readonly stacks: number];
|
||||
readonly initialIntent: string;
|
||||
}[];
|
||||
|
||||
export type EnemyDesert = EnemyDesertTable[number];
|
||||
|
||||
declare function getData(): EnemyDesertTable;
|
||||
export default getData;
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
# 敌人行动状态机,敌人行动时会执行当前的意图,然后将意图变为下一个意图
|
||||
# 若敌人防御值被打空,则将意图变为brokenIntent,除非brokenIntent为空
|
||||
|
||||
# enemy: 敌人id
|
||||
# intent: 意图id
|
||||
# nextIntents: 下一个意图id, 多个意图则从中随机
|
||||
# brokenIntent: 防御被打空后改变的意图id,多个意图则从中随机
|
||||
# effects:技能效果,目标+buff/debuff/攻击/防御+数值/层数
|
||||
enemy,intent,nextIntents,brokenIntent,effects
|
||||
@enemyDesert,string,string[],string[],['self' | 'opponent';@effectDesert;number][]
|
||||
|
||||
仙人掌怪,boost,boost;defend;defend,,[self;spike;1];[self;defend;4]
|
||||
仙人掌怪,defend,attack,,[self;defend;8]
|
||||
仙人掌怪,attack,boost,,[opponent;attack;5]
|
||||
蛇,poison,attack;attack,,[opponent;venom;1];[opponent;attack;4]
|
||||
蛇,attack,poison;boost,,[opponent;attack;6]
|
||||
蛇,boost,poison;attack,,[self;defend;3];[opponent;venom;1]
|
||||
木乃伊,attack,defend;curse,,[opponent;attack;6]
|
||||
木乃伊,defend,attack,,[self;defend;6]
|
||||
木乃伊,curse,defend;attack,attack,[opponent;curse;1]
|
||||
枪手,aim,attack,,[self;aim;2]
|
||||
枪手,attack,aim;defend,aim,[opponent;attack;8]
|
||||
枪手,defend,aim,aim,[self;defend;5]
|
||||
风卷草,boost,defend;defend;boost,,[self;roll;5];[self;defend;4]
|
||||
风卷草,defend,boost;attack,,[self;defend;8]
|
||||
风卷草,attack,boost,,[opponent;rollDamage;0]
|
||||
秃鹫,attack,defend;defend,,[opponent;attack;6];[opponent;vultureEye;1]
|
||||
秃鹫,defend,attack;attack,,[self;defend;5]
|
||||
沙蝎,boost,attack;attack,,[self;tailSting;2]
|
||||
沙蝎,attack,boost;attack,,[opponent;attack;6]
|
||||
幼沙虫,defend,defend;boost,,[self;defend;6]
|
||||
幼沙虫,boost,attack;defend,,[self;energyDrain;1];[self;defend;4]
|
||||
幼沙虫,attack,defend;defend,,[opponent;attack;5]
|
||||
蜥蜴,attack,defend;molt,,[opponent;attack;5]
|
||||
蜥蜴,defend,attack;attack,,[self;defend;6]
|
||||
蜥蜴,molt,defend;attack,,[self;molt;3]
|
||||
沙匪,attack,attack;heavyAttack,,[opponent;attack;6]
|
||||
沙匪,heavyAttack,attack;attack;debuff,,[opponent;attack;10]
|
||||
沙匪,debuff,attack;attack,,[opponent;discard;1]
|
||||
风暴之灵,storm,attack;storm,,[self;storm;2];[self;defend;3]
|
||||
风暴之灵,attack,storm;defend,,[opponent;attack;8];[opponent;static;1]
|
||||
风暴之灵,defend,storm;attack,,[self;defend;8]
|
||||
骑马枪手,charge,attack,,[self;charge;2]
|
||||
骑马枪手,attack,charge;defend,charge,[opponent;attack;6]
|
||||
骑马枪手,defend,charge;attack,charge,[self;defend;5]
|
||||
沙虫王,summon,attack;defend,,[self;summonSandwormLarva;1]
|
||||
沙虫王,attack,summon;defend,,[opponent;attack;9]
|
||||
沙虫王,defend,attack;summon,,[self;defend;6]
|
||||
沙漠守卫,summon,attack;defend,,[self;summonMummy;1]
|
||||
沙漠守卫,attack,defend;summon,,[opponent;attack;8]
|
||||
沙漠守卫,defend,attack;revive,,[self;defend;8]
|
||||
沙漠守卫,revive,attack;summon,,[self;reviveMummy;1]
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
import type { EnemyDesert } from './enemyDesert.csv';
|
||||
import type { EffectDesert } from './effectDesert.csv';
|
||||
|
||||
type EnemyIntentDesertTable = readonly {
|
||||
readonly enemy: EnemyDesert;
|
||||
readonly intent: string;
|
||||
readonly nextIntents: readonly string[];
|
||||
readonly brokenIntent: readonly string[];
|
||||
readonly effects: readonly ["self" | "opponent", EffectDesert, number];
|
||||
}[];
|
||||
|
||||
export type EnemyIntentDesert = EnemyIntentDesertTable[number];
|
||||
|
||||
declare function getData(): EnemyIntentDesertTable;
|
||||
export default getData;
|
||||
|
|
@ -11,5 +11,5 @@ type HeroItemFighter1Table = readonly {
|
|||
|
||||
export type HeroItemFighter1 = HeroItemFighter1Table[number];
|
||||
|
||||
declare function getData(): HeroItemFighter1Table;
|
||||
export default getData;
|
||||
declare const data: HeroItemFighter1Table;
|
||||
export default data;
|
||||
|
|
|
|||
|
|
@ -1,16 +1,6 @@
|
|||
import heroItemFighter1Csv from './heroItemFighter1.csv';
|
||||
import encounterDesertCsv from './encounterDesert.csv';
|
||||
import enemyDesertCsv from './enemyDesert.csv';
|
||||
import enemyIntentDesertCsv from './enemyIntentDesert.csv';
|
||||
import effectDesertCsv from './effectDesert.csv';
|
||||
|
||||
export const heroItemFighter1Data = heroItemFighter1Csv();
|
||||
export const encounterDesertData = encounterDesertCsv();
|
||||
export const enemyDesertData = enemyDesertCsv();
|
||||
export const enemyIntentDesertData = enemyIntentDesertCsv();
|
||||
export const effectDesertData = effectDesertCsv();
|
||||
|
||||
export const heroItemFighter1Data = heroItemFighter1Csv;
|
||||
export const encounterDesertData = encounterDesertCsv;
|
||||
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';
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
import { Mulberry32RNG, type RNG } from '@/utils/rng';
|
||||
import encounterDesertCsvAccessor, { type EncounterDesert } from '../data/encounterDesert.csv';
|
||||
import encounterDesertCsv, { type EncounterDesert } from '../data/encounterDesert.csv';
|
||||
import { MapNodeType, MapLayerType } from './types';
|
||||
import type { MapLayer, MapNode, PointCrawlMap, MapGenerationConfig } from './types';
|
||||
|
||||
const encounterDesertCsv = encounterDesertCsvAccessor();
|
||||
|
||||
/** Pre-indexed encounters by type */
|
||||
const encountersByType = buildEncounterIndex();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,5 @@
|
|||
import { describe, it, expect } from 'vitest';
|
||||
import {
|
||||
heroItemFighter1Data,
|
||||
encounterDesertData,
|
||||
enemyDesertData,
|
||||
enemyIntentDesertData,
|
||||
effectDesertData,
|
||||
} from '@/samples/slay-the-spire-like/data';
|
||||
import { heroItemFighter1Data } from '@/samples/slay-the-spire-like/data';
|
||||
|
||||
describe('heroItemFighter1.csv import', () => {
|
||||
it('should import data as an array', () => {
|
||||
|
|
@ -14,6 +8,7 @@ describe('heroItemFighter1.csv import', () => {
|
|||
});
|
||||
|
||||
it('should have expected number of items', () => {
|
||||
// CSV has 24 data rows (excluding header and type rows)
|
||||
expect(heroItemFighter1Data.length).toBe(24);
|
||||
});
|
||||
|
||||
|
|
@ -76,162 +71,3 @@ describe('heroItemFighter1.csv import', () => {
|
|||
expect(typeCounts['tool']).toBe(6);
|
||||
});
|
||||
});
|
||||
|
||||
describe('encounterDesert.csv import', () => {
|
||||
it('should import data as an array', () => {
|
||||
expect(Array.isArray(encounterDesertData)).toBe(true);
|
||||
expect(encounterDesertData.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should have correct encounter type counts', () => {
|
||||
const typeCounts = encounterDesertData.reduce((acc, e) => {
|
||||
acc[e.type] = (acc[e.type] || 0) + 1;
|
||||
return acc;
|
||||
}, {} as Record<string, number>);
|
||||
|
||||
expect(typeCounts['minion']).toBe(10);
|
||||
expect(typeCounts['elite']).toBe(4);
|
||||
expect(typeCounts['shop']).toBe(2);
|
||||
expect(typeCounts['camp']).toBe(2);
|
||||
expect(typeCounts['curio']).toBe(8);
|
||||
expect(typeCounts['event']).toBe(1);
|
||||
});
|
||||
|
||||
it('should have enemies for combat encounters', () => {
|
||||
for (const e of encounterDesertData) {
|
||||
if (e.type === 'minion' || e.type === 'elite') {
|
||||
expect(Array.isArray(e.enemies)).toBe(true);
|
||||
expect(e.enemies.length).toBeGreaterThan(0);
|
||||
for (const [enemy, bonusHp] of e.enemies) {
|
||||
expect(typeof enemy === 'string' || typeof enemy === 'object').toBe(true);
|
||||
expect(typeof bonusHp).toBe('number');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('should have empty enemies for non-combat encounters', () => {
|
||||
for (const e of encounterDesertData) {
|
||||
if (e.type === 'shop' || e.type === 'camp') {
|
||||
expect(e.enemies.length).toBe(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('should have dialogue for curio and event encounters', () => {
|
||||
for (const e of encounterDesertData) {
|
||||
if (e.type === 'curio' || e.type === 'event') {
|
||||
expect(e.dialogue).toBeTruthy();
|
||||
expect(e.dialogue.startsWith('desert_')).toBe(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('effectDesert.csv import', () => {
|
||||
it('should import data as an array', () => {
|
||||
expect(Array.isArray(effectDesertData)).toBe(true);
|
||||
expect(effectDesertData.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should have expected number of effects', () => {
|
||||
expect(effectDesertData.length).toBe(19);
|
||||
});
|
||||
|
||||
it('should have correct fields for each effect', () => {
|
||||
for (const effect of effectDesertData) {
|
||||
expect(effect).toHaveProperty('id');
|
||||
expect(effect).toHaveProperty('name');
|
||||
expect(effect).toHaveProperty('description');
|
||||
}
|
||||
});
|
||||
|
||||
it('should contain core effect types', () => {
|
||||
const ids = effectDesertData.map(e => e.id);
|
||||
expect(ids).toContain('attack');
|
||||
expect(ids).toContain('defend');
|
||||
expect(ids).toContain('spike');
|
||||
expect(ids).toContain('venom');
|
||||
});
|
||||
});
|
||||
|
||||
describe('enemyDesert.csv import', () => {
|
||||
it('should import data as an array', () => {
|
||||
expect(Array.isArray(enemyDesertData)).toBe(true);
|
||||
expect(enemyDesertData.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should have expected number of enemies', () => {
|
||||
expect(enemyDesertData.length).toBe(14);
|
||||
});
|
||||
|
||||
it('should have correct fields for each enemy', () => {
|
||||
for (const enemy of enemyDesertData) {
|
||||
expect(enemy).toHaveProperty('id');
|
||||
expect(enemy).toHaveProperty('initHp');
|
||||
expect(enemy).toHaveProperty('initBuffs');
|
||||
expect(enemy).toHaveProperty('initialIntent');
|
||||
expect(typeof enemy.initHp).toBe('number');
|
||||
expect(typeof enemy.initialIntent).toBe('string');
|
||||
}
|
||||
});
|
||||
|
||||
it('should have valid HP ranges', () => {
|
||||
for (const enemy of enemyDesertData) {
|
||||
expect(enemy.initHp).toBeGreaterThan(0);
|
||||
}
|
||||
});
|
||||
|
||||
it('should have minions with lower HP than elites', () => {
|
||||
const minionIds = ['仙人掌怪', '蛇', '木乃伊', '枪手', '风卷草', '秃鹫', '沙蝎', '幼沙虫', '蜥蜴', '沙匪'];
|
||||
const eliteIds = ['风暴之灵', '骑马枪手', '沙虫王', '沙漠守卫'];
|
||||
const byId = Object.fromEntries(enemyDesertData.map(e => [e.id, e]));
|
||||
|
||||
for (const id of minionIds) {
|
||||
expect(byId[id].initHp).toBeLessThan(40);
|
||||
}
|
||||
for (const id of eliteIds) {
|
||||
expect(byId[id].initHp).toBeGreaterThanOrEqual(40);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('enemyIntentDesert.csv import', () => {
|
||||
it('should import data as an array', () => {
|
||||
expect(Array.isArray(enemyIntentDesertData)).toBe(true);
|
||||
expect(enemyIntentDesertData.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should have expected number of intent rows', () => {
|
||||
expect(enemyIntentDesertData.length).toBe(41);
|
||||
});
|
||||
|
||||
it('should have correct fields for each intent', () => {
|
||||
for (const intent of enemyIntentDesertData) {
|
||||
expect(intent).toHaveProperty('enemy');
|
||||
expect(intent).toHaveProperty('intent');
|
||||
expect(intent).toHaveProperty('nextIntents');
|
||||
expect(intent).toHaveProperty('brokenIntent');
|
||||
expect(intent).toHaveProperty('effects');
|
||||
expect(intent.enemy).toHaveProperty('id');
|
||||
expect(Array.isArray(intent.nextIntents)).toBe(true);
|
||||
expect(Array.isArray(intent.brokenIntent)).toBe(true);
|
||||
expect(Array.isArray(intent.effects)).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
it('should have effects with target, effect ref, and value', () => {
|
||||
for (const intent of enemyIntentDesertData) {
|
||||
for (const [target, effect, value] of intent.effects) {
|
||||
expect(target === 'self' || target === 'opponent').toBe(true);
|
||||
expect(typeof effect === 'string' || typeof effect === 'object').toBe(true);
|
||||
expect(typeof value).toBe('number');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('should cover all 14 enemies', () => {
|
||||
const enemyIds = new Set(enemyIntentDesertData.map(i => typeof i.enemy === 'string' ? i.enemy : i.enemy.id));
|
||||
expect(enemyIds.size).toBe(14);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue