/** * knife_hit.aigameplay — reducer 接口模板 * * Agent 生成实际 reducer 时,必须导出以下函数。 * 此文件是接口约束模板,不是可执行代码。 * * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ * ⚠️ 重要:渲染层(GameScene)不应直接调用 dispatch() * * 游戏的核心循环是「每帧旋转 + 玩家点击投刀」, * 渲染层必须每帧调用 updateRotation 更新木桩旋转角度, * 并根据 state.target.current_angle 旋转木桩精灵组。 * * 玩家点击时,先调用 checkCollision,再调用 throwKnife, * 在飞刀飞行动画播放期间保持逻辑状态不变,动画结束后再更新。 * * dispatch() 的用途:无头测试、AI 模拟、关卡回放等场景。 * ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */ // ─── 配置类型 ───────────────────────────────────────────────────────────────── export type KnifeHitConfig = { /** 木桩半径(像素),默认 120 */ targetRadius: number; /** 已插入飞刀之间最小角度差(度),默认 15 */ minSeparationAngle: number; /** 基础旋转速度(度/秒),默认 90 */ baseRotationSpeed: number; /** 旋转方向,1=顺时针,-1=逆时针 */ rotationDirection: 1 | -1; /** 苹果收集判定角度半径(度),默认 8 */ appleAngularRadius: number; /** 苹果额外得分,默认 50 */ appleBonusScore: number; /** 每刀成功插入得分,默认 10 */ knifeScore: number; /** 关卡需要投出的飞刀总数 */ knivesRequired: number; /** 旋转方向变化阶段 */ phaseChanges?: Array<{ atKnifeNumber: number; newDirection: 1 | -1; newSpeed?: number }>; }; // ─── 核心类型 ───────────────────────────────────────────────────────────────── export type Apple = { id: number; /** 苹果在木桩上的固定角度(相对于木桩坐标系,度) */ angle: number; collected: boolean; }; export type Target = { radius: number; rotationSpeed: number; rotationDirection: 1 | -1; /** 当前旋转角度 [0, 360) */ currentAngle: number; /** 已插入飞刀的角度列表(相对于世界坐标系,度) */ insertedKnives: number[]; }; // ─── 动作类型 ───────────────────────────────────────────────────────────────── export type KnifeHitAction = | { type: 'throw_knife' } // 玩家点击投刀(使用当前 currentAngle) | { type: 'update_rotation'; deltaMs: number } // 每帧旋转更新 | { type: 'reset' }; // ─── 状态类型 ───────────────────────────────────────────────────────────────── export type KnifeHitState = { target: Target; apples: Apple[]; knivesRemaining: number; // 剩余可投飞刀数 knivesThrown: number; // 已成功投出的飞刀数 score: number; gameOver: boolean; win: boolean; loseReason: 'knife_collision' | null; /** 上一次投刀是否收集到苹果 */ lastAppleCollected: boolean; lastAppleId: number | null; /** 上一次投刀的插入角度 */ lastInsertedAngle: number | null; }; // ─── 分步操作返回类型 ───────────────────────────────────────────────────────── /** 碰撞检测结果 */ export type CollisionCheckResult = { collision: boolean; /** 最近的已插入飞刀角度(若有) */ nearestKnifeAngle?: number; /** 最近飞刀的角距(度) */ nearestAngleDistance?: number; }; /** 投刀结果 */ export type ThrowResult = { success: boolean; insertedAngle: number; appleCollected: boolean; appleId?: number; bonusScore: number; levelComplete: boolean; }; // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 必须导出的函数 // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // ─── 初始化 ────────────────────────────────────────────────────────────────── /** * init:根据关卡配置初始化游戏状态 */ export declare function init( config: KnifeHitConfig, apples?: Apple[], ): KnifeHitState; // ─── 一体化接口(测试/AI 模拟用) ─────────────────────────────────────────── /** * dispatch:接收动作,返回新状态(纯函数) * - throw_knife:投出飞刀(使用当前 currentAngle) * - update_rotation:更新旋转(通常由渲染层每帧调用) * - reset:重置 * * ⚠️ 仅用于无头测试和 AI 模拟。渲染层请使用下方分步函数。 */ export declare function dispatch(state: KnifeHitState, action: KnifeHitAction): KnifeHitState; /** * getState:返回当前状态快照 */ export declare function getState(state: KnifeHitState): KnifeHitState; // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 分步函数(渲染层/场景层必须使用这些函数) // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ /** * updateRotation:每帧更新木桩旋转角度 * 公式:currentAngle = (currentAngle + rotationSpeed × direction × deltaMs/1000) % 360 * * 渲染层每帧调用,根据返回的 currentAngle 旋转木桩精灵组 */ export declare function updateRotation( state: KnifeHitState, deltaMs: number, ): KnifeHitState; /** * checkCollision:检测在给定角度插入是否会与已有飞刀碰撞 * 角距公式:angularDistance(a, b) = min(|a-b|, 360-|a-b|) * 碰撞条件:任意已有飞刀的角距 < minSeparationAngle * * 渲染层在玩家点击时立即调用,若碰撞则播放碰撞动画,否则继续飞行动画 */ export declare function checkCollision( state: KnifeHitState, newAngle: number, ): CollisionCheckResult; /** * collectApple:检测并收集指定角度处的苹果 * 收集条件:angularDistance(angle, apple.angle) < apple.angular_radius * * 内部被 throwKnife 调用,也可由渲染层单独调用以查询是否会命中苹果 */ export declare function collectApple( state: KnifeHitState, angle: number, ): { collected: boolean; appleId?: number; bonusScore: number; newState: KnifeHitState }; /** * throwKnife:执行投刀操作(使用 target.currentAngle 作为插入角度) * 1. 检测碰撞,若碰撞则 gameOver = true * 2. 检测苹果收集 * 3. 将当前角度加入 insertedKnives * 4. knivesRemaining--;score += knifeScore * 5. 处理旋转方向/速度变化(根据 phaseChanges) * * 渲染层在飞刀飞行动画结束后调用 */ export declare function throwKnife(state: KnifeHitState): KnifeHitState; /** * checkLevelComplete:检测当前关卡是否通关 * 通关条件:knivesRemaining == 0 且 gameOver == false */ export declare function checkLevelComplete( state: KnifeHitState, ): { complete: boolean; totalScore: number; applesCollected: number };