229 lines
8.1 KiB
TypeScript
229 lines
8.1 KiB
TypeScript
/**
|
|
* Data-driven configuration interfaces for《影之传说》MVP.
|
|
*
|
|
* Every numeric default in here traces directly back to a requirement in
|
|
* `.codebuddy/plan/kage_legend_mvp/requirements.md`. When you change a field,
|
|
* keep the inline `req` comment in sync so QA can rebuild the traceability
|
|
* matrix.
|
|
*
|
|
* NOTE: This module is platform-agnostic and MUST NOT depend on `cc`.
|
|
*/
|
|
|
|
import { PlayerColorState } from '../common/Constants';
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Enemies
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/** Enum covers all enemy types required by MVP Chapter 1 (req 6.1-6.7). */
|
|
export enum EnemyType {
|
|
/** Green ninja — shuriken + sword, 2s interval (req 6.1). */
|
|
QingRen = 'qing_ren',
|
|
/** Red ninja — 120px/s + smoke bomb, proactive intercept jump (req 6.2-6.3). */
|
|
ChiRen = 'chi_ren',
|
|
/** Black ninja — drops magic flute scroll on castle stages (req 6.5). */
|
|
HeiRen = 'hei_ren',
|
|
/** Monster priest — straight-line fireball, 3.0s interval (req 6.6). */
|
|
YaoFang = 'yao_fang',
|
|
}
|
|
|
|
/** Allowed damage types (req 3.7, 3.8, 10.4-10.5). */
|
|
export type AttackType = 'shuriken' | 'sword' | 'fireball' | 'smoke_bomb';
|
|
|
|
export interface IEnemyConfig {
|
|
id: EnemyType;
|
|
displayName: string;
|
|
/** Pixel sprite size (width x height). */
|
|
size: { w: number; h: number };
|
|
/** Horizontal movement speed (px/s). 0 means stationary. */
|
|
moveSpeed: number;
|
|
/** Attack interval in seconds. */
|
|
attackIntervalSec: number;
|
|
/** Possible attack types. */
|
|
attacks: AttackType[];
|
|
/** Hit points (1 means dies to any successful hit). */
|
|
hp: number;
|
|
/** How many enemies of this type must be killed for chapter objective (optional). */
|
|
killObjective?: number;
|
|
/** Drop rules specific to this enemy type (see `IItemDropRule`). */
|
|
drops?: IItemDropRule[];
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Items
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/** Item IDs used by MVP Chapter 1 (req 7.1-7.6, 5.1-5.6). */
|
|
export enum ItemType {
|
|
/** 水晶玉 — auto-upgrades red → green → yellow (req 5.1-5.2). */
|
|
CrystalJade = 'crystal_jade',
|
|
/** 点丸 — +50% attack power, 30s (req 7.3). */
|
|
DianWan = 'dian_wan',
|
|
/** 术丸 — +30% move speed, 20s (req 7.3). */
|
|
ShuWan = 'shu_wan',
|
|
/** 魔笛 — screen-wipe one-shot kill (req 7.4). */
|
|
MoDi = 'mo_di',
|
|
/** 增丸 — +1 permanent life (req 7.5). */
|
|
ZengWan = 'zeng_wan',
|
|
}
|
|
|
|
export interface IItemConfig {
|
|
id: ItemType;
|
|
displayName: string;
|
|
/** Icon asset path under `assets/resources/textures/items`. */
|
|
icon: string;
|
|
/** Duration in seconds for timed buffs (0 / omitted for instant items). */
|
|
durationSec?: number;
|
|
/**
|
|
* Relative strength of the effect (interpretation is per item type;
|
|
* see `IItemEffectApplier` in logic layer for the actual math).
|
|
*/
|
|
magnitude?: number;
|
|
/** Lifetime in seconds after spawning on the map (req 7.2). */
|
|
lifetimeSec: number;
|
|
}
|
|
|
|
/** Drop rule attached to an enemy type. Evaluated on enemy death. */
|
|
export interface IItemDropRule {
|
|
item: ItemType;
|
|
/** Required consecutive kills of this enemy type before drop can happen. */
|
|
afterKills?: number;
|
|
/** Probability 0~1 once `afterKills` condition is satisfied. */
|
|
probability: number;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Weapons
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export enum WeaponType {
|
|
Shuriken = 'shuriken',
|
|
NinjaSword = 'ninja_sword',
|
|
}
|
|
|
|
export interface IWeaponConfig {
|
|
id: WeaponType;
|
|
displayName: string;
|
|
/** Base attack interval (s). Yellow state may override for shuriken. */
|
|
baseIntervalSec: number;
|
|
/** Upgraded (green/yellow) interval (s). */
|
|
upgradedIntervalSec?: number;
|
|
/** Damage applied to standard enemies on hit. */
|
|
damage: number;
|
|
/** Supports parry (req 3.7 — only sword). */
|
|
canParry: boolean;
|
|
/** When long-pressed, max shots in a burst (req 3.5 — shuriken only). */
|
|
burstMax?: number;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Levels (Chapter 1 only)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/** Scroll direction for a level (req 8.1-8.5). */
|
|
export type ScrollDirection = 'horizontal' | 'horizontal_bi' | 'vertical';
|
|
|
|
export interface ILevelObjective {
|
|
/** e.g. 'kill_yao_fang', 'reach_top', 'boss_defeated'. */
|
|
kind: 'kill_count' | 'reach_top' | 'defeat_boss';
|
|
/** For kill_count objectives — which enemy type, and how many. */
|
|
enemy?: EnemyType;
|
|
/** Required count for kill_count objectives. */
|
|
count?: number;
|
|
/** For defeat_boss objectives — boss ID. */
|
|
bossId?: string;
|
|
}
|
|
|
|
export interface ILevelConfig {
|
|
id: string; // e.g. '1-1'
|
|
chapter: 1 | 2 | 3;
|
|
displayName: string;
|
|
/** One of 'forest' / 'cave' / 'castle_wall' / 'demon_castle'. */
|
|
sceneTheme: string;
|
|
scrollDirection: ScrollDirection;
|
|
/** Time limit in seconds (req 8.1-8.4). */
|
|
timeLimitSec: number;
|
|
objective: ILevelObjective;
|
|
/** Scene length in pixels (landscape 16:9 baseline, req 8.8). */
|
|
levelLengthPx: number;
|
|
/** BGM bundle key under `assets/resources/audio`. */
|
|
bgm: string;
|
|
/** Enemy spawn list evaluated by the LevelMgr. */
|
|
enemySpawns: Array<{ type: EnemyType; atPx: number; count?: number }>;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Bosses
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export interface IBossAttackPhase {
|
|
/** HP threshold at which this phase activates (1.0, 0.66, 0.33, ...). */
|
|
hpThreshold: number;
|
|
/** Human-readable mode id (e.g. 'pair_pincer', 'fireball_spread', 'clone_confuse'). */
|
|
mode: string;
|
|
/** Interval between actions in this phase (s). */
|
|
actionIntervalSec: number;
|
|
}
|
|
|
|
export interface IBossConfig {
|
|
id: string;
|
|
displayName: string;
|
|
hp: number;
|
|
/** A non-zero value means "butterfly appearance required before damage". */
|
|
butterflyReveal: boolean;
|
|
/** Ordered from highest hpThreshold to lowest. */
|
|
phases: IBossAttackPhase[];
|
|
/** Cutscene trigger (req 8.6 / 14.1): play short "princess taken" at hp<=X. */
|
|
princessCutsceneAtHpRatio?: number;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Story intro (req 19)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export interface IStoryPageConfig {
|
|
/** 1-based page index. */
|
|
index: number;
|
|
/** Texture path under `assets/resources/textures/story`. */
|
|
illustration: string;
|
|
/** Pixel typewriter text to display. */
|
|
text: string;
|
|
}
|
|
|
|
export interface IStorySceneConfig {
|
|
id: string; // e.g. 'chapter_1_intro'
|
|
bgm: string; // e.g. 'bgm_story'
|
|
/** Max total duration (seconds); UI should auto-advance if exceeded. */
|
|
maxDurationSec: number;
|
|
pages: IStoryPageConfig[];
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Aggregate configuration table
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export interface IChapter1ConfigBundle {
|
|
enemies: IEnemyConfig[];
|
|
items: IItemConfig[];
|
|
weapons: IWeaponConfig[];
|
|
levels: ILevelConfig[];
|
|
bosses: IBossConfig[];
|
|
stories: IStorySceneConfig[];
|
|
}
|
|
|
|
/**
|
|
* Describes which player-state the JSON config applies to. We explicitly
|
|
* leave **no room** for a 'casual' mode string so that future edits cannot
|
|
* silently reintroduce the removed difficulty (decision D-4, req 13.1-13.6).
|
|
*/
|
|
export type DifficultyProfile = 'hardcore';
|
|
|
|
export const ONLY_DIFFICULTY: DifficultyProfile = 'hardcore';
|
|
|
|
/** Convenience map for looking up the red/green/yellow state that unlocks each move-speed bucket. */
|
|
export const COLOR_STATE_MOVE_BUCKET: Record<PlayerColorState, number> = {
|
|
[PlayerColorState.Red]: 100,
|
|
[PlayerColorState.Green]: 100,
|
|
[PlayerColorState.Yellow]: 150,
|
|
};
|