Split custom world generation into staged lightweight batches
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
2026-04-05 22:20:30 +08:00
parent 89cecda7da
commit fcd8d727b0
57 changed files with 7646 additions and 1425 deletions

View File

@@ -19,6 +19,7 @@ import {
StoryOption,
WorldType,
} from '../types';
import { resolveRoleCombatStats } from './attributeCombat';
import {
buildRelationState,
resolveAttributeSchema,
@@ -26,6 +27,7 @@ import {
} from './attributeResolver';
import {
getCharacterById,
getCharacterCombatStats,
getCharacterEquipment,
getCharacterMaxHp,
getInventoryItems,
@@ -1572,29 +1574,50 @@ export function checkTradeItem(
};
}
export function getNpcSparMaxHp(character: Character | null) {
export function getNpcSparMaxHp(
character: Character | null,
worldType: WorldType | null = null,
customWorldProfile: GameState['customWorldProfile'] = getRuntimeCustomWorldProfile(),
) {
if (!character) return 8;
const values = character.attributeProfile?.values ?? {};
const sparScore =
((values.axis_a ?? 0) + (values.axis_b ?? 0) + (values.axis_f ?? 0)) / 10;
return Math.max(7, Math.min(12, Math.round(sparScore / 3)));
const sparStats = getCharacterCombatStats(
character,
worldType,
customWorldProfile,
);
return Math.max(7, Math.min(12, Math.round(sparStats.maxHpBonus / 4)));
}
export function createNpcBattleMonster(
encounter: Encounter,
npcState: NpcPersistentState,
mode: NpcBattleMode = 'fight',
options: {
worldType?: WorldType | null;
customWorldProfile?: GameState['customWorldProfile'];
} = {},
) {
const monsterPreset = getMonsterPresetForEncounter(encounter);
const recruitCharacter = resolveEncounterRecruitCharacter(encounter);
const resolvedWorldType = options.worldType ?? null;
const resolvedCustomWorldProfile =
options.customWorldProfile ?? getRuntimeCustomWorldProfile();
if (monsterPreset) {
const monsterCombatStats = resolveRoleCombatStats(
monsterPreset.attributeProfile,
{
baseSpeed: monsterPreset.baseStats.speed,
},
);
const resolvedMonsterMaxHp =
monsterPreset.baseStats.maxHp + monsterCombatStats.maxHpBonus;
const hostileMaxHp =
mode === 'spar'
? Math.max(
8,
Math.min(14, Math.round(monsterPreset.baseStats.maxHp / 18)),
Math.min(14, Math.round(resolvedMonsterMaxHp / 18)),
)
: monsterPreset.baseStats.maxHp;
: resolvedMonsterMaxHp;
return {
id: monsterPreset.id,
@@ -1609,7 +1632,7 @@ export function createNpcBattleMonster(
yOffset: 0,
facing: 'left' as const,
attackRange: monsterPreset.baseStats.attackRange,
speed: monsterPreset.baseStats.speed,
speed: monsterCombatStats.turnSpeed,
hp: hostileMaxHp,
maxHp: hostileMaxHp,
renderKind: 'npc' as const,
@@ -1624,18 +1647,33 @@ export function createNpcBattleMonster(
} satisfies SceneMonster;
}
const baseHp = recruitCharacter ? getCharacterMaxHp(recruitCharacter) : 120;
const recruitCombatStats = recruitCharacter
? getCharacterCombatStats(
recruitCharacter,
resolvedWorldType,
resolvedCustomWorldProfile,
)
: null;
const baseHp = recruitCharacter
? getCharacterMaxHp(
recruitCharacter,
resolvedWorldType,
resolvedCustomWorldProfile,
)
: 120;
const baseSpeed = recruitCharacter
? Math.max(
6,
Math.round(
(recruitCharacter.attributeProfile?.values.axis_b ?? 48) / 12 + 1,
),
5,
Math.round((recruitCombatStats?.turnSpeed ?? 4.5) + 1.5),
)
: 7;
const maxHp =
mode === 'spar'
? getNpcSparMaxHp(recruitCharacter)
? getNpcSparMaxHp(
recruitCharacter,
resolvedWorldType,
resolvedCustomWorldProfile,
)
: Math.max(baseHp, 80 + npcState.affinity);
if (mode === 'spar') {
@@ -1930,16 +1968,17 @@ export function buildNpcChatResultText(
}
export function buildNpcSparResultText(
npcName: string,
affinityGain: number,
nextAffinity: number,
) {
const sparEncounter = {
npcName: '对方',
npcName,
npcDescription: '',
npcAvatar: '',
context: '',
} satisfies Encounter;
return `点到为止地切磋了一场,彼此都更认可对方的身手。${describeAffinityShift(affinityGain)}${describeNpcAffinityInWords(sparEncounter, nextAffinity)}`;
return `${npcName}点到为止地切磋了一场,彼此都更认可对方的身手。${describeAffinityShift(affinityGain)}${describeNpcAffinityInWords(sparEncounter, nextAffinity)}`;
}
export function buildNpcGiftResultText(
@@ -2019,6 +2058,22 @@ export function buildNpcTradeTransactionActionText({
return `${encounter.npcName}手里买下${quantityText}`;
}
export function buildNpcHelpCommitActionText(
encounter: Encounter,
reward: NpcHelpReward,
) {
const goals: string[] = [];
if ((reward.hp ?? 0) > 0) goals.push('疗伤');
if ((reward.mana ?? 0) > 0) goals.push('回气');
if ((reward.cooldownBonus ?? 0) > 0) goals.push('调整招式节奏');
if (reward.items.length > 0) goals.push('补给');
return goals.length > 0
? `${encounter.npcName}请求${goals.join('、')}`
: `${encounter.npcName}寻求支援`;
}
export function buildNpcHelpResultText(
encounter: Encounter,
reward: NpcHelpReward,