Split custom world generation into staged lightweight batches
Some checks failed
CI / verify (push) Has been cancelled
Some checks failed
CI / verify (push) Has been cancelled
This commit is contained in:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user