1
This commit is contained in:
@@ -17,6 +17,7 @@ import {
|
||||
NPC_PREVIEW_TALK_FUNCTION,
|
||||
shouldNpcRecruitOpenModal,
|
||||
} from './index';
|
||||
import { RPG_FUNCTION_RUNTIME_OVERVIEW } from './runtimeIndex';
|
||||
import type { Encounter, GameState, InventoryItem } from '../../types';
|
||||
|
||||
function createEncounter(overrides: Partial<Encounter> = {}): Encounter {
|
||||
@@ -87,6 +88,12 @@ describe('functionCatalog', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('keeps runtime overview aligned with the main function documentation list', () => {
|
||||
expect(RPG_FUNCTION_RUNTIME_OVERVIEW.allDocumentation).toEqual(
|
||||
ALL_FUNCTION_DOCUMENTATION,
|
||||
);
|
||||
});
|
||||
|
||||
it('builds flow helper options with the expected function ids', () => {
|
||||
const continueOption = buildContinueAdventureOption();
|
||||
const campTravelOption = buildCampTravelHomeOption('竹林古道');
|
||||
@@ -110,16 +117,12 @@ describe('functionCatalog', () => {
|
||||
const state = createModalState();
|
||||
const encounter = createEncounter();
|
||||
const tradeModal = buildNpcTradeModalState(
|
||||
state,
|
||||
encounter,
|
||||
'先看看货',
|
||||
[
|
||||
createInventoryItem('npc-herb', '止血草'),
|
||||
createInventoryItem('npc-ore', '陨铁碎片'),
|
||||
],
|
||||
'npc-herb',
|
||||
'player-potion',
|
||||
);
|
||||
const giftModal = buildNpcGiftModalState(
|
||||
state,
|
||||
encounter,
|
||||
'送你一样东西',
|
||||
'player-charm',
|
||||
@@ -138,18 +141,13 @@ describe('functionCatalog', () => {
|
||||
expect(shouldNpcRecruitOpenModal(1, 2)).toBe(false);
|
||||
});
|
||||
|
||||
it('prefers the first tradable player item when zero-quantity items exist', () => {
|
||||
it('keeps server-selected trade item ids when opening the trade modal', () => {
|
||||
const encounter = createEncounter();
|
||||
const tradeModal = buildNpcTradeModalState(
|
||||
createModalState({
|
||||
playerInventory: [
|
||||
createInventoryItem('empty-slot', '空槽位', { quantity: 0 }),
|
||||
createInventoryItem('usable-item', '可售草药', { quantity: 2 }),
|
||||
],
|
||||
}),
|
||||
encounter,
|
||||
'交易',
|
||||
[createInventoryItem('npc-herb', '止血草')],
|
||||
'npc-herb',
|
||||
'usable-item',
|
||||
);
|
||||
|
||||
expect(tradeModal.selectedPlayerItemId).toBe('usable-item');
|
||||
|
||||
@@ -31,6 +31,7 @@ export * from './panel/forgeCraft';
|
||||
export * from './panel/forgeDismantle';
|
||||
export * from './panel/forgeReforge';
|
||||
export * from './panel/inventoryUse';
|
||||
export * from './runtimeIndex';
|
||||
export * from './state';
|
||||
export * from './treasure/treasureInspect';
|
||||
export * from './treasure/treasureLeave';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { GiftModalState } from '../../../hooks/rpg-runtime-story/uiTypes';
|
||||
import type { Encounter, GameState } from '../../../types';
|
||||
import type { Encounter } from '../../../types';
|
||||
import type { FunctionDocumentationEntry } from '../types';
|
||||
|
||||
/**
|
||||
@@ -16,10 +16,9 @@ export function buildNpcGiftModalIntroText(encounter: Encounter) {
|
||||
}
|
||||
|
||||
export function buildNpcGiftModalState(
|
||||
state: GameState,
|
||||
encounter: Encounter,
|
||||
actionText: string,
|
||||
selectedItemId: string | null = state.playerInventory[0]?.id ?? null,
|
||||
selectedItemId: string | null,
|
||||
): GiftModalState {
|
||||
return {
|
||||
encounter,
|
||||
@@ -34,13 +33,13 @@ export const NPC_GIFT_FUNCTION: FunctionDocumentationEntry = {
|
||||
domain: 'npc',
|
||||
title: '向该角色送礼',
|
||||
source: 'src/data/functionCatalog/npc/npcGift.ts',
|
||||
summary: '打开送礼面板并根据礼物质量结算 affinity 变化。',
|
||||
summary: '打开送礼面板并由后端结算 affinity 变化。',
|
||||
detailedDescription:
|
||||
'它会把当前互动引到礼物选择 modal,通过本地规则估算礼物对该 NPC 的吸引力和好感增益,避免送礼结果漂移。',
|
||||
trigger: '玩家背包里存在可送出的物品时出现在 NPC 交互菜单里。',
|
||||
'它会把当前互动引到礼物选择 modal,礼物列表、好感增益和不可选原因都读取后端 runtimeNpcInteraction view。',
|
||||
trigger: '后端判断当前 NPC 可接收礼物时出现在 NPC 交互菜单里。',
|
||||
execution:
|
||||
'首次点击只打开 gift modal,确认礼物后再调用 commitGeneratedState 把送礼结果写回主流程。',
|
||||
result: '玩家可立即看到好感变化与送礼反馈,并影响后续交易、聊天和招募阈值。',
|
||||
'首次点击只打开 gift modal,确认礼物后只提交 itemId 给后端结算。',
|
||||
result: '玩家可立即看到后端结算后的好感变化与送礼反馈,并影响后续交易、聊天和招募阈值。',
|
||||
active: true,
|
||||
runtime: {
|
||||
storyMode: 'modal_then_generate',
|
||||
@@ -50,7 +49,7 @@ export const NPC_GIFT_FUNCTION: FunctionDocumentationEntry = {
|
||||
animationNote: '第一次点击不驱动额外演出,重点是切到礼物面板。',
|
||||
storyNote:
|
||||
'真正的剧情推进发生在 confirmGift 之后,届时才会写入好感变化与结果文本。',
|
||||
uiNote: '会先打开 gift modal,并默认选中当前最适合作为礼物的物品。',
|
||||
uiNote: '会先打开 gift modal,并默认选中后端 view 中第一件可提交的礼物。',
|
||||
compactDetailText: '送礼提升好感',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { TradeModalState } from '../../../hooks/rpg-runtime-story/uiTypes';
|
||||
import type { Encounter, GameState, InventoryItem } from '../../../types';
|
||||
import type { Encounter } from '../../../types';
|
||||
import type { FunctionDocumentationEntry } from '../types';
|
||||
|
||||
/**
|
||||
@@ -16,21 +16,17 @@ export function buildNpcTradeModalIntroText(encounter: Encounter) {
|
||||
}
|
||||
|
||||
export function buildNpcTradeModalState(
|
||||
state: GameState,
|
||||
encounter: Encounter,
|
||||
actionText: string,
|
||||
npcInventory: InventoryItem[],
|
||||
selectedNpcItemId: string | null,
|
||||
selectedPlayerItemId: string | null,
|
||||
mode: 'buy' | 'sell' = selectedNpcItemId ? 'buy' : 'sell',
|
||||
): TradeModalState {
|
||||
const selectedNpcItemId =
|
||||
npcInventory.find((item) => item.quantity > 0)?.id ?? null;
|
||||
const selectedPlayerItemId =
|
||||
state.playerInventory.find((item) => item.quantity > 0)?.id ?? null;
|
||||
|
||||
return {
|
||||
encounter,
|
||||
actionText,
|
||||
introText: buildNpcTradeModalIntroText(encounter),
|
||||
mode: 'buy',
|
||||
mode,
|
||||
selectedNpcItemId,
|
||||
selectedPlayerItemId,
|
||||
selectedQuantity: 1,
|
||||
@@ -44,11 +40,11 @@ export const NPC_TRADE_FUNCTION: FunctionDocumentationEntry = {
|
||||
source: 'src/data/functionCatalog/npc/npcTrade.ts',
|
||||
summary: '打开 NPC 交易流程并结算买卖或交换。',
|
||||
detailedDescription:
|
||||
'它负责把当前交互引到交易面板,展示 NPC 库存、折扣和可交换物。第一次点击通常只打开 modal,真正确认后才继续推进剧情。',
|
||||
'它负责把当前交互引到交易面板,库存、价格、折扣和不可选原因都读取后端 runtimeNpcInteraction view。第一次点击通常只打开 modal,真正确认后才继续推进剧情。',
|
||||
trigger: '当 NPC 允许交易且自身库存非空时出现在 NPC 交互菜单里。',
|
||||
execution:
|
||||
'首次点击进入 trade modal,确认后再通过 commitGeneratedState 把结果写回主流程。',
|
||||
result: '玩家可以买入、以物易物,或在失败时得到明确的价值差提示。',
|
||||
'首次点击进入 trade modal,确认后只提交 mode、itemId、quantity 给后端结算。',
|
||||
result: '玩家可以买入、出售物品,或在后端拒绝时得到明确的失败原因。',
|
||||
active: true,
|
||||
runtime: {
|
||||
storyMode: 'modal_then_generate',
|
||||
@@ -58,7 +54,7 @@ export const NPC_TRADE_FUNCTION: FunctionDocumentationEntry = {
|
||||
animationNote: '第一次点击不播额外战斗或位移动画,重点是切到交易窗口。',
|
||||
storyNote:
|
||||
'真正的剧情推进发生在 confirmTrade 之后,而不是打开 modal 的瞬间。',
|
||||
uiNote: '会先打开交易 modal,并预选 NPC 第一件商品与玩家第一件可卖物品。',
|
||||
uiNote: '会先打开交易 modal,并预选后端 view 中第一件可提交的买入 / 卖出物品。',
|
||||
compactDetailText: '查看库存与价格',
|
||||
},
|
||||
};
|
||||
|
||||
35
src/data/functionCatalog/runtimeIndex.ts
Normal file
35
src/data/functionCatalog/runtimeIndex.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { FLOW_FUNCTION_DOCUMENTATION } from './flow';
|
||||
import { NPC_FUNCTION_DOCUMENTATION } from './npc';
|
||||
import { PANEL_FUNCTION_DOCUMENTATION } from './panel';
|
||||
import {
|
||||
STATE_FUNCTION_DOCUMENTATION,
|
||||
STATE_FUNCTION_RUNTIME_SOURCES,
|
||||
} from './state';
|
||||
import { TREASURE_FUNCTION_DOCUMENTATION } from './treasure';
|
||||
import type { FunctionDocumentationEntry } from './types';
|
||||
|
||||
export const RPG_FUNCTION_RUNTIME_ALL_DOCUMENTATION: FunctionDocumentationEntry[] =
|
||||
[
|
||||
...STATE_FUNCTION_DOCUMENTATION,
|
||||
...NPC_FUNCTION_DOCUMENTATION,
|
||||
...TREASURE_FUNCTION_DOCUMENTATION,
|
||||
...FLOW_FUNCTION_DOCUMENTATION,
|
||||
...PANEL_FUNCTION_DOCUMENTATION,
|
||||
];
|
||||
|
||||
/**
|
||||
* RPG function 运行时总览入口。
|
||||
*
|
||||
* 目的:
|
||||
* 1. 在同一个脚本里集中看到当前所有 function 的注册入口。
|
||||
* 2. 先看总表,再跳到各自独立文件维护实现,避免重新回到巨型 switch。
|
||||
*/
|
||||
export const RPG_FUNCTION_RUNTIME_OVERVIEW = {
|
||||
allDocumentation: RPG_FUNCTION_RUNTIME_ALL_DOCUMENTATION,
|
||||
stateDocumentation: STATE_FUNCTION_DOCUMENTATION,
|
||||
npcDocumentation: NPC_FUNCTION_DOCUMENTATION,
|
||||
treasureDocumentation: TREASURE_FUNCTION_DOCUMENTATION,
|
||||
flowDocumentation: FLOW_FUNCTION_DOCUMENTATION,
|
||||
panelDocumentation: PANEL_FUNCTION_DOCUMENTATION,
|
||||
stateRuntimeSources: STATE_FUNCTION_RUNTIME_SOURCES,
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AnimationState } from '../../../types';
|
||||
import type { StateFunctionSource } from '../types';
|
||||
import type { StateFunctionRuntimeSource } from '../types';
|
||||
|
||||
/**
|
||||
* battle_all_in_crush
|
||||
@@ -7,7 +7,7 @@ import type { StateFunctionSource } from '../types';
|
||||
* 战斗中的正面爆发动作。它要求主角不绕、不拖,直接把当前回合的叙事、
|
||||
* 技能权重和视觉表现都推向“强压正面敌人”的方向。
|
||||
*/
|
||||
export const BATTLE_ALL_IN_CRUSH_FUNCTION_SOURCE: StateFunctionSource = {
|
||||
export const BATTLE_ALL_IN_CRUSH_FUNCTION_SOURCE: StateFunctionRuntimeSource = {
|
||||
definition: {
|
||||
id: 'battle_all_in_crush',
|
||||
state: 'battle',
|
||||
@@ -56,5 +56,23 @@ export const BATTLE_ALL_IN_CRUSH_FUNCTION_SOURCE: StateFunctionSource = {
|
||||
category: 'battle',
|
||||
active: true,
|
||||
},
|
||||
runtime: {
|
||||
buildSuggestedActionText({ metrics, environment }) {
|
||||
if (metrics.monsterHpRatio <= 0.25) {
|
||||
return `压上去收掉${environment.monsterName}最后一口气`;
|
||||
}
|
||||
if (metrics.playerHpRatio <= 0.35) {
|
||||
return `顶着伤势强压${environment.monsterName}赌一波强杀`;
|
||||
}
|
||||
return `正面强压${environment.monsterName}不给喘息`;
|
||||
},
|
||||
getPriority({ metrics }) {
|
||||
return metrics.monsterHpRatio <= 0.25
|
||||
? 8
|
||||
: metrics.playerHpRatio <= 0.35
|
||||
? 2
|
||||
: 4;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AnimationState } from '../../../types';
|
||||
import type { StateFunctionSource } from '../types';
|
||||
import type { StateFunctionRuntimeSource } from '../types';
|
||||
|
||||
/**
|
||||
* battle_escape_breakout
|
||||
@@ -7,7 +7,7 @@ import type { StateFunctionSource } from '../types';
|
||||
* 战斗中的脱离动作。它不是继续换血,而是明确让主角放弃当前缠斗,
|
||||
* 把叙事重心切到“拉开距离、甩开追击、离开战场”。
|
||||
*/
|
||||
export const BATTLE_ESCAPE_BREAKOUT_FUNCTION_SOURCE: StateFunctionSource = {
|
||||
export const BATTLE_ESCAPE_BREAKOUT_FUNCTION_SOURCE: StateFunctionRuntimeSource = {
|
||||
definition: {
|
||||
id: 'battle_escape_breakout',
|
||||
state: 'battle',
|
||||
@@ -51,5 +51,20 @@ export const BATTLE_ESCAPE_BREAKOUT_FUNCTION_SOURCE: StateFunctionSource = {
|
||||
category: 'escape',
|
||||
active: true,
|
||||
},
|
||||
runtime: {
|
||||
buildSuggestedActionText({ metrics, environment }) {
|
||||
if (metrics.playerHpRatio <= 0.35) {
|
||||
return `撑着伤势先脱离${environment.monsterName}的追杀`;
|
||||
}
|
||||
return `转身拉开距离,甩开${environment.monsterName}`;
|
||||
},
|
||||
getPriority({ metrics }) {
|
||||
return metrics.playerHpRatio <= 0.2
|
||||
? 9
|
||||
: metrics.playerHpRatio <= 0.35
|
||||
? 5
|
||||
: 1;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AnimationState } from '../../../types';
|
||||
import type { StateFunctionSource } from '../types';
|
||||
import type { StateFunctionRuntimeSource } from '../types';
|
||||
|
||||
/**
|
||||
* battle_feint_step
|
||||
@@ -7,7 +7,7 @@ import type { StateFunctionSource } from '../types';
|
||||
* 战斗中的机动切入动作。它把重点放在虚晃、变线与抢身位,
|
||||
* 让战斗叙事更偏向灵活切入而不是硬扛伤害。
|
||||
*/
|
||||
export const BATTLE_FEINT_STEP_FUNCTION_SOURCE: StateFunctionSource = {
|
||||
export const BATTLE_FEINT_STEP_FUNCTION_SOURCE: StateFunctionRuntimeSource = {
|
||||
definition: {
|
||||
id: 'battle_feint_step',
|
||||
state: 'battle',
|
||||
@@ -56,5 +56,16 @@ export const BATTLE_FEINT_STEP_FUNCTION_SOURCE: StateFunctionSource = {
|
||||
category: 'battle',
|
||||
active: true,
|
||||
},
|
||||
runtime: {
|
||||
buildSuggestedActionText({ metrics, environment }) {
|
||||
if (metrics.monsterHpRatio <= 0.35) {
|
||||
return `虚晃切进去收掉${environment.monsterName}`;
|
||||
}
|
||||
return `借假动作切进${environment.monsterName}身前`;
|
||||
},
|
||||
getPriority({ metrics }) {
|
||||
return metrics.monsterHpRatio <= 0.5 ? 5 : 3;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AnimationState } from '../../../types';
|
||||
import type { StateFunctionSource } from '../types';
|
||||
import type { StateFunctionRuntimeSource } from '../types';
|
||||
|
||||
/**
|
||||
* battle_finisher_window
|
||||
@@ -7,7 +7,7 @@ import type { StateFunctionSource } from '../types';
|
||||
* 战斗中的终结窗口动作。它要求系统把这一回合理解为“敌人已经露出空档”,
|
||||
* 因而优先演出收割、补刀和终结技。
|
||||
*/
|
||||
export const BATTLE_FINISHER_WINDOW_FUNCTION_SOURCE: StateFunctionSource = {
|
||||
export const BATTLE_FINISHER_WINDOW_FUNCTION_SOURCE: StateFunctionRuntimeSource = {
|
||||
definition: {
|
||||
id: 'battle_finisher_window',
|
||||
state: 'battle',
|
||||
@@ -55,5 +55,23 @@ export const BATTLE_FINISHER_WINDOW_FUNCTION_SOURCE: StateFunctionSource = {
|
||||
category: 'battle',
|
||||
active: true,
|
||||
},
|
||||
runtime: {
|
||||
buildSuggestedActionText({ metrics, environment }) {
|
||||
if (metrics.monsterHpRatio <= 0.25) {
|
||||
return `完成对${environment.monsterName}的残血收割`;
|
||||
}
|
||||
if (metrics.monsterHpRatio <= 0.45) {
|
||||
return `抓住${environment.monsterName}露出的破绽补上重击`;
|
||||
}
|
||||
return `盯住${environment.monsterName}的空当准备终结一击`;
|
||||
},
|
||||
getPriority({ metrics }) {
|
||||
return metrics.monsterHpRatio <= 0.25
|
||||
? 10
|
||||
: metrics.monsterHpRatio <= 0.45
|
||||
? 6
|
||||
: 1;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AnimationState } from '../../../types';
|
||||
import type { StateFunctionSource } from '../types';
|
||||
import type { StateFunctionRuntimeSource } from '../types';
|
||||
|
||||
/**
|
||||
* battle_guard_break
|
||||
@@ -7,7 +7,7 @@ import type { StateFunctionSource } from '../types';
|
||||
* 战斗中的破架重击动作。它强调“针对敌人当前动作强拆架势”,
|
||||
* 比纯换血更讲究把敌人的节奏打断。
|
||||
*/
|
||||
export const BATTLE_GUARD_BREAK_FUNCTION_SOURCE: StateFunctionSource = {
|
||||
export const BATTLE_GUARD_BREAK_FUNCTION_SOURCE: StateFunctionRuntimeSource = {
|
||||
definition: {
|
||||
id: 'battle_guard_break',
|
||||
state: 'battle',
|
||||
@@ -54,5 +54,16 @@ export const BATTLE_GUARD_BREAK_FUNCTION_SOURCE: StateFunctionSource = {
|
||||
category: 'battle',
|
||||
active: true,
|
||||
},
|
||||
runtime: {
|
||||
buildSuggestedActionText({ metrics, environment }) {
|
||||
if (metrics.monsterHpRatio <= 0.35) {
|
||||
return `砸开${environment.monsterName}的架势直接斩落`;
|
||||
}
|
||||
return `重击破开${environment.monsterName}的招架`;
|
||||
},
|
||||
getPriority({ metrics }) {
|
||||
return metrics.monsterHpRatio <= 0.4 ? 6 : 3;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AnimationState } from '../../../types';
|
||||
import type { StateFunctionSource } from '../types';
|
||||
import type { StateFunctionRuntimeSource } from '../types';
|
||||
|
||||
/**
|
||||
* battle_probe_pressure
|
||||
@@ -7,7 +7,7 @@ import type { StateFunctionSource } from '../types';
|
||||
* 战斗中的稳扎试探动作。适合在局势未明、资源需要保留时,
|
||||
* 先用安全且持续的压制把信息和节奏摸出来。
|
||||
*/
|
||||
export const BATTLE_PROBE_PRESSURE_FUNCTION_SOURCE: StateFunctionSource = {
|
||||
export const BATTLE_PROBE_PRESSURE_FUNCTION_SOURCE: StateFunctionRuntimeSource = {
|
||||
definition: {
|
||||
id: 'battle_probe_pressure',
|
||||
state: 'battle',
|
||||
@@ -54,5 +54,19 @@ export const BATTLE_PROBE_PRESSURE_FUNCTION_SOURCE: StateFunctionSource = {
|
||||
category: 'battle',
|
||||
active: true,
|
||||
},
|
||||
runtime: {
|
||||
buildSuggestedActionText({ metrics, environment }) {
|
||||
if (metrics.playerManaRatio <= 0.3) {
|
||||
return `稳住节奏试探${environment.monsterName},先省下灵力`;
|
||||
}
|
||||
if (metrics.monsterHpRatio <= 0.3) {
|
||||
return `稳步逼近,补掉${environment.monsterName}残余血量`;
|
||||
}
|
||||
return `稳扎稳打继续试探${environment.monsterName}`;
|
||||
},
|
||||
getPriority({ metrics }) {
|
||||
return metrics.playerManaRatio <= 0.3 ? 8 : 4;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AnimationState } from '../../../types';
|
||||
import type { StateFunctionSource } from '../types';
|
||||
import type { StateFunctionRuntimeSource } from '../types';
|
||||
|
||||
/**
|
||||
* battle_recover_breath
|
||||
@@ -7,7 +7,7 @@ import type { StateFunctionSource } from '../types';
|
||||
* 战斗中的恢复动作。它会把当前回合塑造成“先稳住伤势与灵力”,
|
||||
* 让数值、冷却和叙事都朝回气与整顿节奏的方向靠拢。
|
||||
*/
|
||||
export const BATTLE_RECOVER_BREATH_FUNCTION_SOURCE: StateFunctionSource = {
|
||||
export const BATTLE_RECOVER_BREATH_FUNCTION_SOURCE: StateFunctionRuntimeSource = {
|
||||
definition: {
|
||||
id: 'battle_recover_breath',
|
||||
state: 'battle',
|
||||
@@ -57,5 +57,22 @@ export const BATTLE_RECOVER_BREATH_FUNCTION_SOURCE: StateFunctionSource = {
|
||||
category: 'recovery',
|
||||
active: true,
|
||||
},
|
||||
runtime: {
|
||||
buildSuggestedActionText({ metrics }) {
|
||||
if (metrics.playerHpRatio <= 0.35) {
|
||||
return '原地打坐恢复血量';
|
||||
}
|
||||
if (metrics.playerManaRatio <= 0.3) {
|
||||
return '收势调息回一口灵力';
|
||||
}
|
||||
return '边守边调息稳住节奏';
|
||||
},
|
||||
getPriority({ metrics }) {
|
||||
return (
|
||||
(metrics.playerHpRatio <= 0.35 ? 10 : 0) +
|
||||
(metrics.playerManaRatio <= 0.3 ? 6 : 0)
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AnimationState } from '../../../types';
|
||||
import type { StateFunctionSource } from '../types';
|
||||
import type { StateFunctionRuntimeSource } from '../types';
|
||||
|
||||
/**
|
||||
* idle_call_out
|
||||
@@ -7,7 +7,7 @@ import type { StateFunctionSource } from '../types';
|
||||
* 空闲状态下的主动喊话动作。它会把探索从“静悄悄地摸过去”
|
||||
* 转成“先出声试探,看谁先回应”的节奏。
|
||||
*/
|
||||
export const IDLE_CALL_OUT_FUNCTION_SOURCE: StateFunctionSource = {
|
||||
export const IDLE_CALL_OUT_FUNCTION_SOURCE: StateFunctionRuntimeSource = {
|
||||
definition: {
|
||||
id: 'idle_call_out',
|
||||
state: 'idle',
|
||||
@@ -44,5 +44,24 @@ export const IDLE_CALL_OUT_FUNCTION_SOURCE: StateFunctionSource = {
|
||||
category: 'idle',
|
||||
active: true,
|
||||
},
|
||||
runtime: {
|
||||
applyDefinitionAdjustments(definition) {
|
||||
return {
|
||||
...definition,
|
||||
text: '主动出声试探',
|
||||
description:
|
||||
'主动朝前方喊话试探,可能把附近潜着的角色或怪物直接从远处引出来。',
|
||||
};
|
||||
},
|
||||
buildSuggestedActionText({ environment }) {
|
||||
return `冲着${environment.sceneName}前方扬声试探,看是谁先被逼出来`;
|
||||
},
|
||||
buildDetailText() {
|
||||
return '主动打破寂静,把附近潜着的角色或怪物从屏幕外直接引到眼前。';
|
||||
},
|
||||
getPriority() {
|
||||
return 5;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AnimationState } from '../../../types';
|
||||
import type { StateFunctionSource } from '../types';
|
||||
import type { StateFunctionRuntimeSource } from '../types';
|
||||
|
||||
/**
|
||||
* idle_explore_forward
|
||||
@@ -7,7 +7,7 @@ import type { StateFunctionSource } from '../types';
|
||||
* 空闲状态下最核心的推进动作。它负责把“继续往前探”从一句泛化文案,
|
||||
* 落成真正会引出下一幕遭遇的运行时 function。
|
||||
*/
|
||||
export const IDLE_EXPLORE_FORWARD_FUNCTION_SOURCE: StateFunctionSource = {
|
||||
export const IDLE_EXPLORE_FORWARD_FUNCTION_SOURCE: StateFunctionRuntimeSource = {
|
||||
definition: {
|
||||
id: 'idle_explore_forward',
|
||||
state: 'idle',
|
||||
@@ -44,5 +44,32 @@ export const IDLE_EXPLORE_FORWARD_FUNCTION_SOURCE: StateFunctionSource = {
|
||||
category: 'idle',
|
||||
active: true,
|
||||
},
|
||||
runtime: {
|
||||
applyDefinitionAdjustments(definition) {
|
||||
return {
|
||||
...definition,
|
||||
text: '继续向前探索',
|
||||
description:
|
||||
'沿着当前场景继续深入,把前路真正探出来,下一刻就可能撞上新的危险或际遇。',
|
||||
};
|
||||
},
|
||||
buildSuggestedActionText({ metrics, environment }) {
|
||||
if (metrics.playerHpRatio <= 0.35) {
|
||||
return `按着伤口,沿着${environment.sceneName}继续往深处摸去`;
|
||||
}
|
||||
if (environment.hasForwardScene) {
|
||||
return `顺着${environment.sceneName}的路势,继续朝前方深处探去`;
|
||||
}
|
||||
return `拨开${environment.sceneName}前的遮挡,继续朝更深处探去`;
|
||||
},
|
||||
buildDetailText({ environment }) {
|
||||
return environment.hasForwardScene
|
||||
? `沿着${environment.sceneName}继续往前压过去,真正把前方会遇到的人影、怪物或宝藏探出来。`
|
||||
: `继续深入${environment.sceneName}前方未探明的地带,下一刻就可能撞见新的动静。`;
|
||||
},
|
||||
getPriority({ metrics }) {
|
||||
return metrics.playerHpRatio > 0.45 ? 6 : 2;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AnimationState } from '../../../types';
|
||||
import type { StateFunctionSource } from '../types';
|
||||
import type { StateFunctionRuntimeSource } from '../types';
|
||||
|
||||
/**
|
||||
* idle_follow_clue
|
||||
@@ -7,7 +7,7 @@ import type { StateFunctionSource } from '../types';
|
||||
* 空闲状态下的循线推进动作。它在源码定义层仍然存在,
|
||||
* 但当前运行时会在聚合阶段被过滤,因此属于保留中的停用 function。
|
||||
*/
|
||||
export const IDLE_FOLLOW_CLUE_FUNCTION_SOURCE: StateFunctionSource = {
|
||||
export const IDLE_FOLLOW_CLUE_FUNCTION_SOURCE: StateFunctionRuntimeSource = {
|
||||
definition: {
|
||||
id: 'idle_follow_clue',
|
||||
state: 'idle',
|
||||
@@ -44,5 +44,16 @@ export const IDLE_FOLLOW_CLUE_FUNCTION_SOURCE: StateFunctionSource = {
|
||||
category: 'idle',
|
||||
active: false,
|
||||
},
|
||||
runtime: {
|
||||
buildSuggestedActionText() {
|
||||
return '顺着可疑痕迹继续靠近';
|
||||
},
|
||||
buildDetailText() {
|
||||
return '沿着声音、脚印或灵气痕迹继续摸过去,可能更快接近前方目标。';
|
||||
},
|
||||
getPriority() {
|
||||
return 5;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AnimationState } from '../../../types';
|
||||
import type { StateFunctionSource } from '../types';
|
||||
import type { StateFunctionRuntimeSource } from '../types';
|
||||
|
||||
/**
|
||||
* idle_observe_signs
|
||||
@@ -7,7 +7,7 @@ import type { StateFunctionSource } from '../types';
|
||||
* 空闲状态下的侦察动作。它把当前回合定义成“停下来观察”,
|
||||
* 重点不是立刻推进,而是为后续选择生成可引用的观察结果。
|
||||
*/
|
||||
export const IDLE_OBSERVE_SIGNS_FUNCTION_SOURCE: StateFunctionSource = {
|
||||
export const IDLE_OBSERVE_SIGNS_FUNCTION_SOURCE: StateFunctionRuntimeSource = {
|
||||
definition: {
|
||||
id: 'idle_observe_signs',
|
||||
state: 'idle',
|
||||
@@ -44,5 +44,16 @@ export const IDLE_OBSERVE_SIGNS_FUNCTION_SOURCE: StateFunctionSource = {
|
||||
category: 'idle',
|
||||
active: true,
|
||||
},
|
||||
runtime: {
|
||||
buildSuggestedActionText() {
|
||||
return '停步观察附近的风吹草动';
|
||||
},
|
||||
buildDetailText() {
|
||||
return '先确认附近是否潜伏着人影、怪物或其他值得靠近的东西。';
|
||||
},
|
||||
getPriority() {
|
||||
return 4;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AnimationState } from '../../../types';
|
||||
import type { StateFunctionSource } from '../types';
|
||||
import type { StateFunctionRuntimeSource } from '../types';
|
||||
|
||||
/**
|
||||
* idle_rest_focus
|
||||
@@ -7,7 +7,7 @@ import type { StateFunctionSource } from '../types';
|
||||
* 空闲状态下的原地恢复动作。它不会推进遭遇,而是给玩家一个
|
||||
* 在非战斗场景里回收少量血蓝的缓冲回合。
|
||||
*/
|
||||
export const IDLE_REST_FOCUS_FUNCTION_SOURCE: StateFunctionSource = {
|
||||
export const IDLE_REST_FOCUS_FUNCTION_SOURCE: StateFunctionRuntimeSource = {
|
||||
definition: {
|
||||
id: 'idle_rest_focus',
|
||||
state: 'idle',
|
||||
@@ -46,5 +46,21 @@ export const IDLE_REST_FOCUS_FUNCTION_SOURCE: StateFunctionSource = {
|
||||
category: 'recovery',
|
||||
active: true,
|
||||
},
|
||||
runtime: {
|
||||
buildSuggestedActionText({ metrics }) {
|
||||
if (metrics.playerHpRatio <= 0.35) {
|
||||
return '原地打坐恢复气血';
|
||||
}
|
||||
if (metrics.playerManaRatio <= 0.35) {
|
||||
return '盘坐调息恢复灵力';
|
||||
}
|
||||
return '原地调息整理状态';
|
||||
},
|
||||
getPriority({ metrics }) {
|
||||
return metrics.playerHpRatio <= 0.35 || metrics.playerManaRatio <= 0.35
|
||||
? 8
|
||||
: 2;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AnimationState } from '../../../types';
|
||||
import type { StateFunctionSource } from '../types';
|
||||
import type { StateFunctionRuntimeSource } from '../types';
|
||||
|
||||
/**
|
||||
* idle_travel_next_scene
|
||||
@@ -7,7 +7,7 @@ import type { StateFunctionSource } from '../types';
|
||||
* 空闲状态下的切场景动作。它代表玩家主动离开当前地点,
|
||||
* 进入相邻场景重新开启新的遭遇周期。
|
||||
*/
|
||||
export const IDLE_TRAVEL_NEXT_SCENE_FUNCTION_SOURCE: StateFunctionSource = {
|
||||
export const IDLE_TRAVEL_NEXT_SCENE_FUNCTION_SOURCE: StateFunctionRuntimeSource = {
|
||||
definition: {
|
||||
id: 'idle_travel_next_scene',
|
||||
state: 'idle',
|
||||
@@ -44,5 +44,21 @@ export const IDLE_TRAVEL_NEXT_SCENE_FUNCTION_SOURCE: StateFunctionSource = {
|
||||
category: 'idle',
|
||||
active: true,
|
||||
},
|
||||
runtime: {
|
||||
buildSuggestedActionText({ environment }) {
|
||||
return environment.travelSceneName
|
||||
? `前往${environment.travelSceneName}`
|
||||
: '前往其他场景';
|
||||
},
|
||||
buildDetailText({ environment }) {
|
||||
return (
|
||||
environment.travelSceneDescription ??
|
||||
'离开当前区域,前往相邻场景继续冒险。'
|
||||
);
|
||||
},
|
||||
getPriority({ metrics }) {
|
||||
return metrics.playerHpRatio > 0.45 ? 5 : 3;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { StateFunctionSource } from '../types';
|
||||
import type { StateFunctionRuntimeSource } from '../types';
|
||||
import { BATTLE_ALL_IN_CRUSH_FUNCTION_SOURCE } from './battleAllInCrush';
|
||||
import { BATTLE_ATTACK_BASIC_FUNCTION } from './battleAttackBasic';
|
||||
import { BATTLE_ESCAPE_BREAKOUT_FUNCTION_SOURCE } from './battleEscapeBreakout';
|
||||
@@ -15,7 +15,7 @@ import { IDLE_OBSERVE_SIGNS_FUNCTION_SOURCE } from './idleObserveSigns';
|
||||
import { IDLE_REST_FOCUS_FUNCTION_SOURCE } from './idleRestFocus';
|
||||
import { IDLE_TRAVEL_NEXT_SCENE_FUNCTION_SOURCE } from './idleTravelNextScene';
|
||||
|
||||
export const STATE_FUNCTION_SOURCES: StateFunctionSource[] = [
|
||||
export const STATE_FUNCTION_SOURCES: StateFunctionRuntimeSource[] = [
|
||||
BATTLE_ALL_IN_CRUSH_FUNCTION_SOURCE,
|
||||
BATTLE_GUARD_BREAK_FUNCTION_SOURCE,
|
||||
BATTLE_PROBE_PRESSURE_FUNCTION_SOURCE,
|
||||
@@ -31,6 +31,10 @@ export const STATE_FUNCTION_SOURCES: StateFunctionSource[] = [
|
||||
IDLE_CALL_OUT_FUNCTION_SOURCE,
|
||||
];
|
||||
|
||||
export const STATE_FUNCTION_RUNTIME_SOURCES = STATE_FUNCTION_SOURCES.filter(
|
||||
(source) => source.runtime,
|
||||
);
|
||||
|
||||
export const STATE_FUNCTION_DEFINITIONS = STATE_FUNCTION_SOURCES.map(
|
||||
(source) => source.definition,
|
||||
);
|
||||
@@ -47,4 +51,3 @@ export const STATE_FUNCTION_DOCUMENTATION = [
|
||||
BATTLE_USE_SKILL_FUNCTION,
|
||||
...STATE_FUNCTION_SOURCES.map((source) => source.documentation),
|
||||
];
|
||||
|
||||
|
||||
@@ -56,3 +56,40 @@ export interface StateFunctionSource {
|
||||
documentation: FunctionDocumentationEntry;
|
||||
promptDescription: string;
|
||||
}
|
||||
|
||||
export interface StateFunctionRuntimeMetrics {
|
||||
playerHpRatio: number;
|
||||
playerManaRatio: number;
|
||||
monsterHpRatio: number;
|
||||
}
|
||||
|
||||
export interface StateFunctionRuntimeEnvironment {
|
||||
sceneName: string;
|
||||
monsterName: string;
|
||||
hasForwardScene: boolean;
|
||||
travelSceneName?: string | null;
|
||||
travelSceneDescription?: string | null;
|
||||
}
|
||||
|
||||
export interface StateFunctionRuntimeHandler {
|
||||
applyDefinitionAdjustments?: (
|
||||
definition: StateFunctionDefinition,
|
||||
) => StateFunctionDefinition;
|
||||
buildSuggestedActionText?: (params: {
|
||||
definition: StateFunctionDefinition;
|
||||
metrics: StateFunctionRuntimeMetrics;
|
||||
environment: StateFunctionRuntimeEnvironment;
|
||||
}) => string;
|
||||
buildDetailText?: (params: {
|
||||
definition: StateFunctionDefinition;
|
||||
environment: StateFunctionRuntimeEnvironment;
|
||||
}) => string | undefined;
|
||||
getPriority?: (params: {
|
||||
definition: StateFunctionDefinition;
|
||||
metrics: StateFunctionRuntimeMetrics;
|
||||
}) => number;
|
||||
}
|
||||
|
||||
export interface StateFunctionRuntimeSource extends StateFunctionSource {
|
||||
runtime?: StateFunctionRuntimeHandler;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
NPC_RECRUIT_FUNCTION,
|
||||
STATE_FUNCTION_DEFINITIONS as SPLIT_STATE_FUNCTION_DEFINITIONS,
|
||||
STATE_FUNCTION_PROMPT_DESCRIPTIONS as SPLIT_STATE_FUNCTION_PROMPT_DESCRIPTIONS,
|
||||
STATE_FUNCTION_RUNTIME_SOURCES,
|
||||
} from './functionCatalog';
|
||||
import {
|
||||
getForwardScenePreset,
|
||||
@@ -103,6 +104,12 @@ export function getFunctionPromptDescription(
|
||||
const STATE_FUNCTION_OVERRIDES =
|
||||
stateFunctionOverridesJson as StateFunctionOverrideMap;
|
||||
const BASE_FUNCTIONS = [...SPLIT_STATE_FUNCTION_DEFINITIONS];
|
||||
const STATE_FUNCTION_RUNTIME_SOURCE_MAP = new Map(
|
||||
STATE_FUNCTION_RUNTIME_SOURCES.map((source) => [
|
||||
source.definition.id,
|
||||
source,
|
||||
]),
|
||||
);
|
||||
|
||||
function mergeStateFunctionDefinition(
|
||||
definition: StateFunctionDefinition,
|
||||
@@ -151,25 +158,9 @@ function applyRuntimeFunctionAdjustments(
|
||||
return definitions
|
||||
.filter((definition) => definition.id !== 'idle_follow_clue')
|
||||
.map((definition) => {
|
||||
if (definition.id === 'idle_explore_forward') {
|
||||
return {
|
||||
...definition,
|
||||
text: '继续向前探索',
|
||||
description:
|
||||
'沿着当前场景继续深入,把前路真正探出来,下一刻就可能撞上新的危险或际遇。',
|
||||
};
|
||||
}
|
||||
|
||||
if (definition.id === 'idle_call_out') {
|
||||
return {
|
||||
...definition,
|
||||
text: '主动出声试探',
|
||||
description:
|
||||
'主动朝前方喊话试探,可能把附近潜着的角色或怪物直接从远处引出来。',
|
||||
};
|
||||
}
|
||||
|
||||
return definition;
|
||||
const runtime =
|
||||
STATE_FUNCTION_RUNTIME_SOURCE_MAP.get(definition.id)?.runtime;
|
||||
return runtime?.applyDefinitionAdjustments?.(definition) ?? definition;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -211,15 +202,16 @@ function getMonsterHpRatio(context: FunctionAvailabilityContext) {
|
||||
return monster.hp / Math.max(monster.maxHp, 1);
|
||||
}
|
||||
|
||||
function buildSuggestedActionText(
|
||||
definition: StateFunctionDefinition,
|
||||
context: FunctionAvailabilityContext,
|
||||
) {
|
||||
function buildRuntimeMetrics(context: FunctionAvailabilityContext) {
|
||||
return {
|
||||
playerHpRatio: getPlayerHpRatio(context),
|
||||
playerManaRatio: getPlayerManaRatio(context),
|
||||
monsterHpRatio: getMonsterHpRatio(context),
|
||||
};
|
||||
}
|
||||
|
||||
function buildRuntimeEnvironment(context: FunctionAvailabilityContext) {
|
||||
const monster = getPrimaryMonster(context);
|
||||
const monsterName = monster?.name ?? '前方怪物';
|
||||
const playerHpRatio = getPlayerHpRatio(context);
|
||||
const playerManaRatio = getPlayerManaRatio(context);
|
||||
const monsterHpRatio = getMonsterHpRatio(context);
|
||||
const forwardScene = getForwardScenePreset(
|
||||
context.worldType,
|
||||
context.currentSceneId,
|
||||
@@ -229,153 +221,51 @@ function buildSuggestedActionText(
|
||||
context.currentSceneId,
|
||||
);
|
||||
|
||||
const sceneName = context.currentSceneName ?? '前路';
|
||||
return {
|
||||
sceneName: context.currentSceneName ?? '前路',
|
||||
monsterName: monster?.name ?? '前方怪物',
|
||||
hasForwardScene: Boolean(forwardScene),
|
||||
travelSceneName: travelScene?.name ?? null,
|
||||
travelSceneDescription: travelScene?.description ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
if (definition.id === 'idle_explore_forward') {
|
||||
if (playerHpRatio <= 0.35)
|
||||
return `按着伤口,沿着${sceneName}继续往深处摸去`;
|
||||
if (forwardScene) return `顺着${sceneName}的路势,继续朝前方深处探去`;
|
||||
return `拨开${sceneName}前的遮挡,继续朝更深处探去`;
|
||||
}
|
||||
|
||||
if (definition.id === 'idle_call_out') {
|
||||
return `冲着${sceneName}前方扬声试探,看是谁先被逼出来`;
|
||||
}
|
||||
|
||||
switch (definition.id) {
|
||||
case 'battle_finisher_window':
|
||||
if (monsterHpRatio <= 0.25) return `完成对${monsterName}的残血收割`;
|
||||
if (monsterHpRatio <= 0.45) return `抓住${monsterName}露出的破绽补上重击`;
|
||||
return `盯住${monsterName}的空当准备终结一击`;
|
||||
case 'battle_all_in_crush':
|
||||
if (monsterHpRatio <= 0.25) return `压上去收掉${monsterName}最后一口气`;
|
||||
if (playerHpRatio <= 0.35) return `顶着伤势强压${monsterName}赌一波强杀`;
|
||||
return `正面强压${monsterName}不给喘息`;
|
||||
case 'battle_guard_break':
|
||||
if (monsterHpRatio <= 0.35) return `砸开${monsterName}的架势直接斩落`;
|
||||
return `重击破开${monsterName}的招架`;
|
||||
case 'battle_probe_pressure':
|
||||
if (playerManaRatio <= 0.3)
|
||||
return `稳住节奏试探${monsterName},先省下灵力`;
|
||||
if (monsterHpRatio <= 0.3) return `稳步逼近,补掉${monsterName}残余血量`;
|
||||
return `稳扎稳打继续试探${monsterName}`;
|
||||
case 'battle_feint_step':
|
||||
if (monsterHpRatio <= 0.35) return `虚晃切进去收掉${monsterName}`;
|
||||
return `借假动作切进${monsterName}身前`;
|
||||
case 'battle_recover_breath':
|
||||
if (playerHpRatio <= 0.35) return '原地打坐恢复血量';
|
||||
if (playerManaRatio <= 0.3) return '收势调息回一口灵力';
|
||||
return '边守边调息稳住节奏';
|
||||
case 'battle_escape_breakout':
|
||||
if (playerHpRatio <= 0.35) return `撑着伤势先脱离${monsterName}的追杀`;
|
||||
return `转身拉开距离,甩开${monsterName}`;
|
||||
case 'idle_explore_forward':
|
||||
if (forwardScene) return `继续向前探路`;
|
||||
if (playerHpRatio <= 0.35) return '拖着伤势继续向前摸索';
|
||||
return '继续向前探索前路';
|
||||
case 'idle_travel_next_scene':
|
||||
return travelScene ? `前往${travelScene.name}` : '前往其他场景';
|
||||
case 'idle_rest_focus':
|
||||
if (playerHpRatio <= 0.35) return '原地打坐恢复气血';
|
||||
if (playerManaRatio <= 0.35) return '盘坐调息恢复灵力';
|
||||
return '原地调息整理状态';
|
||||
case 'idle_observe_signs':
|
||||
return '停步观察附近的风吹草动';
|
||||
case 'idle_follow_clue':
|
||||
return '顺着可疑痕迹继续靠近';
|
||||
case 'idle_call_out':
|
||||
return '朝前方主动出声试探';
|
||||
default:
|
||||
return definition.text;
|
||||
}
|
||||
function buildSuggestedActionText(
|
||||
definition: StateFunctionDefinition,
|
||||
context: FunctionAvailabilityContext,
|
||||
) {
|
||||
const runtime = STATE_FUNCTION_RUNTIME_SOURCE_MAP.get(definition.id)?.runtime;
|
||||
return (
|
||||
runtime?.buildSuggestedActionText?.({
|
||||
definition,
|
||||
metrics: buildRuntimeMetrics(context),
|
||||
environment: buildRuntimeEnvironment(context),
|
||||
}) ?? definition.text
|
||||
);
|
||||
}
|
||||
|
||||
function buildOptionDetailText(
|
||||
definition: StateFunctionDefinition,
|
||||
context: FunctionAvailabilityContext,
|
||||
) {
|
||||
const forwardScene = getForwardScenePreset(
|
||||
context.worldType,
|
||||
context.currentSceneId,
|
||||
);
|
||||
const travelScene = getTravelScenePreset(
|
||||
context.worldType,
|
||||
context.currentSceneId,
|
||||
);
|
||||
const sceneName = context.currentSceneName ?? '当前区域';
|
||||
|
||||
if (definition.id === 'idle_explore_forward') {
|
||||
return forwardScene
|
||||
? `沿着${sceneName}继续往前压过去,真正把前方会遇到的人影、怪物或宝藏探出来。`
|
||||
: `继续深入${sceneName}前方未探明的地带,下一刻就可能撞见新的动静。`;
|
||||
}
|
||||
|
||||
if (definition.id === 'idle_call_out') {
|
||||
return '主动打破寂静,把附近潜着的角色或怪物从屏幕外直接引到眼前。';
|
||||
}
|
||||
|
||||
switch (definition.id) {
|
||||
case 'idle_explore_forward':
|
||||
return forwardScene
|
||||
? `沿当前路径继续深入,可能会遇到角色、怪物、宝藏……`
|
||||
: '继续向前试探这片区域,可能会遇到角色、怪物、宝藏……';
|
||||
case 'idle_travel_next_scene':
|
||||
return travelScene?.description ?? '离开当前区域,前往相邻场景继续冒险。';
|
||||
case 'idle_observe_signs':
|
||||
return '先确认附近是否潜伏着人影、怪物或其他值得靠近的东西。';
|
||||
case 'idle_follow_clue':
|
||||
return '沿着声音、脚印或灵气痕迹继续摸过去,可能更快接近前方目标。';
|
||||
case 'idle_call_out':
|
||||
return '主动打破寂静,看看附近是谁或什么东西先有反应。';
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
const runtime = STATE_FUNCTION_RUNTIME_SOURCE_MAP.get(definition.id)?.runtime;
|
||||
return runtime?.buildDetailText?.({
|
||||
definition,
|
||||
environment: buildRuntimeEnvironment(context),
|
||||
});
|
||||
}
|
||||
|
||||
function getFunctionPriority(
|
||||
definition: StateFunctionDefinition,
|
||||
context: FunctionAvailabilityContext,
|
||||
) {
|
||||
const playerHpRatio = getPlayerHpRatio(context);
|
||||
const playerManaRatio = getPlayerManaRatio(context);
|
||||
const monsterHpRatio = getMonsterHpRatio(context);
|
||||
|
||||
if (definition.id === 'idle_call_out') {
|
||||
return 5;
|
||||
}
|
||||
|
||||
switch (definition.id) {
|
||||
case 'battle_recover_breath':
|
||||
return (
|
||||
(playerHpRatio <= 0.35 ? 10 : 0) + (playerManaRatio <= 0.3 ? 6 : 0)
|
||||
);
|
||||
case 'battle_finisher_window':
|
||||
return monsterHpRatio <= 0.25 ? 10 : monsterHpRatio <= 0.45 ? 6 : 1;
|
||||
case 'battle_all_in_crush':
|
||||
return monsterHpRatio <= 0.25 ? 8 : playerHpRatio <= 0.35 ? 2 : 4;
|
||||
case 'battle_guard_break':
|
||||
return monsterHpRatio <= 0.4 ? 6 : 3;
|
||||
case 'battle_probe_pressure':
|
||||
return playerManaRatio <= 0.3 ? 8 : 4;
|
||||
case 'battle_feint_step':
|
||||
return monsterHpRatio <= 0.5 ? 5 : 3;
|
||||
case 'battle_escape_breakout':
|
||||
return playerHpRatio <= 0.2 ? 9 : playerHpRatio <= 0.35 ? 5 : 1;
|
||||
case 'idle_rest_focus':
|
||||
return playerHpRatio <= 0.35 || playerManaRatio <= 0.35 ? 8 : 2;
|
||||
case 'idle_explore_forward':
|
||||
return playerHpRatio > 0.45 ? 6 : 2;
|
||||
case 'idle_travel_next_scene':
|
||||
return playerHpRatio > 0.45 ? 5 : 3;
|
||||
case 'idle_observe_signs':
|
||||
return 4;
|
||||
case 'idle_follow_clue':
|
||||
return 5;
|
||||
case 'idle_call_out':
|
||||
return 3;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
const runtime = STATE_FUNCTION_RUNTIME_SOURCE_MAP.get(definition.id)?.runtime;
|
||||
return (
|
||||
runtime?.getPriority?.({
|
||||
definition,
|
||||
metrics: buildRuntimeMetrics(context),
|
||||
}) ?? 0
|
||||
);
|
||||
}
|
||||
|
||||
function matchesCategory(
|
||||
|
||||
Reference in New Issue
Block a user