@@ -11,6 +11,11 @@ import {
|
||||
resolveInventoryItemUseEffect,
|
||||
} from '../../bridges/legacyInventoryRuntimeBridge.js';
|
||||
import { conflict } from '../../errors.js';
|
||||
import {
|
||||
buildExperienceGrantResultText,
|
||||
grantPlayerExperience,
|
||||
} from '../progression/playerProgressionService.js';
|
||||
import { resolveHostileBattleProfile } from '../progression/hostileProgressionService.js';
|
||||
import {
|
||||
appendBuildBuffs,
|
||||
resolvePlayerOutgoingDamageResult,
|
||||
@@ -41,7 +46,9 @@ type CombatActionConfig = {
|
||||
}>;
|
||||
consumedItemId?: string | null;
|
||||
usedItem?: RuntimeCombatInventoryItem | null;
|
||||
itemEffect?: NonNullable<ReturnType<typeof resolveInventoryItemUseEffect>> | null;
|
||||
itemEffect?: NonNullable<
|
||||
ReturnType<typeof resolveInventoryItemUseEffect>
|
||||
> | null;
|
||||
};
|
||||
|
||||
export type CombatResolution = {
|
||||
@@ -87,6 +94,15 @@ function getAliveTarget(session: RuntimeSession) {
|
||||
return session.sceneHostileNpcs.find((npc) => npc.hp > 0) ?? null;
|
||||
}
|
||||
|
||||
function getVictoryResolvedTargets(
|
||||
session: RuntimeSession,
|
||||
primaryTargetId: string,
|
||||
) {
|
||||
return session.sceneHostileNpcs.filter(
|
||||
(npc) => npc.id === primaryTargetId || npc.hp > 0,
|
||||
);
|
||||
}
|
||||
|
||||
function getCombatInventoryItem(
|
||||
session: RuntimeSession,
|
||||
itemId: string,
|
||||
@@ -147,13 +163,64 @@ function applySparAffinityReward(session: RuntimeSession) {
|
||||
}
|
||||
|
||||
function clampPlayerVitals(session: RuntimeSession) {
|
||||
session.playerHp = Math.max(0, Math.min(session.playerHp, session.playerMaxHp));
|
||||
session.playerHp = Math.max(
|
||||
0,
|
||||
Math.min(session.playerHp, session.playerMaxHp),
|
||||
);
|
||||
session.playerMana = Math.max(
|
||||
0,
|
||||
Math.min(session.playerMana, session.playerMaxMana),
|
||||
);
|
||||
}
|
||||
|
||||
function applyHostileVictoryRewards(
|
||||
session: RuntimeSession,
|
||||
resolvedTargets: RuntimeSession['sceneHostileNpcs'],
|
||||
) {
|
||||
if (resolvedTargets.length <= 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const grantedXp = resolvedTargets.reduce((sum, hostileNpc) => {
|
||||
const battleProfile = resolveHostileBattleProfile({
|
||||
playerProgression: session.rawGameState.playerProgression,
|
||||
encounter: {
|
||||
hostile: true,
|
||||
monsterPresetId: hostileNpc.id,
|
||||
levelProfile: hostileNpc.levelProfile,
|
||||
experienceReward: hostileNpc.experienceReward,
|
||||
},
|
||||
battleMode: 'fight',
|
||||
});
|
||||
|
||||
return sum + battleProfile.experienceReward;
|
||||
}, 0);
|
||||
const experienceGrant = grantPlayerExperience(
|
||||
session.rawGameState.playerProgression,
|
||||
grantedXp,
|
||||
{
|
||||
source: 'hostile_npc',
|
||||
},
|
||||
);
|
||||
|
||||
session.rawGameState.playerProgression = experienceGrant.state;
|
||||
session.rawGameState.runtimeStats = incrementGameRuntimeStats(
|
||||
(isObject(session.rawGameState.runtimeStats)
|
||||
? session.rawGameState.runtimeStats
|
||||
: {
|
||||
hostileNpcsDefeated: 0,
|
||||
questsAccepted: 0,
|
||||
itemsUsed: 0,
|
||||
scenesTraveled: 0,
|
||||
}) as Parameters<typeof incrementGameRuntimeStats>[0],
|
||||
{
|
||||
hostileNpcsDefeated: resolvedTargets.length,
|
||||
},
|
||||
);
|
||||
|
||||
return buildExperienceGrantResultText(experienceGrant);
|
||||
}
|
||||
|
||||
function finishBattle(
|
||||
session: RuntimeSession,
|
||||
outcome: RuntimeBattlePresentation['outcome'],
|
||||
@@ -194,10 +261,7 @@ function buildBasicAttackBaseDamage(session: RuntimeSession) {
|
||||
);
|
||||
}
|
||||
|
||||
function tickCooldownMap(
|
||||
cooldowns: Record<string, number>,
|
||||
turns: number,
|
||||
) {
|
||||
function tickCooldownMap(cooldowns: Record<string, number>, turns: number) {
|
||||
let nextCooldowns = cooldowns;
|
||||
|
||||
for (let index = 0; index < Math.max(0, Math.floor(turns)); index += 1) {
|
||||
@@ -232,7 +296,10 @@ function resolveCombatActionConfig(params: {
|
||||
} satisfies CombatActionConfig;
|
||||
}
|
||||
|
||||
if (functionId === 'battle_attack_basic' || LEGACY_ATTACK_FUNCTION_IDS.has(functionId)) {
|
||||
if (
|
||||
functionId === 'battle_attack_basic' ||
|
||||
LEGACY_ATTACK_FUNCTION_IDS.has(functionId)
|
||||
) {
|
||||
return {
|
||||
actionText: '普通攻击',
|
||||
manaCost: 0,
|
||||
@@ -253,7 +320,9 @@ function resolveCombatActionConfig(params: {
|
||||
throw conflict('battle_use_skill 缺少 skillId');
|
||||
}
|
||||
|
||||
const skill = character.skills.find((candidate) => candidate.id === skillId);
|
||||
const skill = character.skills.find(
|
||||
(candidate) => candidate.id === skillId,
|
||||
);
|
||||
if (!skill) {
|
||||
throw conflict(`未找到技能:${skillId}`);
|
||||
}
|
||||
@@ -386,7 +455,9 @@ export function resolveCombatAction(
|
||||
const damageResult =
|
||||
action.baseDamage > 0
|
||||
? resolvePlayerOutgoingDamageResult(
|
||||
session.rawGameState as Parameters<typeof resolvePlayerOutgoingDamageResult>[0],
|
||||
session.rawGameState as Parameters<
|
||||
typeof resolvePlayerOutgoingDamageResult
|
||||
>[0],
|
||||
character,
|
||||
action.baseDamage,
|
||||
1,
|
||||
@@ -397,7 +468,7 @@ export function resolveCombatAction(
|
||||
? action.baseDamage > 0
|
||||
? 1
|
||||
: 0
|
||||
: damageResult?.damage ?? 0;
|
||||
: (damageResult?.damage ?? 0);
|
||||
|
||||
session.playerMana -= action.manaCost;
|
||||
session.playerHp += action.heal ?? 0;
|
||||
@@ -417,7 +488,9 @@ export function resolveCombatAction(
|
||||
|
||||
if (action.consumedItemId) {
|
||||
session.rawGameState.playerInventory = removeInventoryItem(
|
||||
session.rawGameState.playerInventory as Parameters<typeof removeInventoryItem>[0],
|
||||
session.rawGameState.playerInventory as Parameters<
|
||||
typeof removeInventoryItem
|
||||
>[0],
|
||||
action.consumedItemId,
|
||||
1,
|
||||
);
|
||||
@@ -436,8 +509,9 @@ export function resolveCombatAction(
|
||||
|
||||
if (action.buildBuffs?.length) {
|
||||
session.rawGameState.activeBuildBuffs = appendBuildBuffs(
|
||||
(session.rawGameState.activeBuildBuffs as Parameters<typeof appendBuildBuffs>[0]) ??
|
||||
[],
|
||||
(session.rawGameState.activeBuildBuffs as Parameters<
|
||||
typeof appendBuildBuffs
|
||||
>[0]) ?? [],
|
||||
action.buildBuffs as Parameters<typeof appendBuildBuffs>[1],
|
||||
);
|
||||
}
|
||||
@@ -463,17 +537,21 @@ export function resolveCombatAction(
|
||||
outcome = 'spar_complete';
|
||||
resultText = `你和${target.name}这轮过招已经分出高下,对方也承认了你的身手。`;
|
||||
} else {
|
||||
const resolvedTargets = getVictoryResolvedTargets(session, target.id);
|
||||
const experienceText = applyHostileVictoryRewards(
|
||||
session,
|
||||
resolvedTargets,
|
||||
);
|
||||
finishBattle(session, 'victory');
|
||||
outcome = 'victory';
|
||||
resultText = `你这一手彻底压垮了${target.name},眼前战斗已经正式结束。`;
|
||||
resultText = experienceText
|
||||
? `你这一手彻底压垮了${target.name},眼前战斗已经正式结束。 ${experienceText}`
|
||||
: `你这一手彻底压垮了${target.name},眼前战斗已经正式结束。`;
|
||||
}
|
||||
} else {
|
||||
const baseCounter = isSpar
|
||||
? 1
|
||||
: Math.max(
|
||||
4,
|
||||
Math.round(target.maxHp * 0.14 * action.counterMultiplier),
|
||||
);
|
||||
: Math.max(4, Math.round(target.maxHp * 0.14 * action.counterMultiplier));
|
||||
damageTaken = baseCounter;
|
||||
session.playerHp = Math.max(isSpar ? 1 : 0, session.playerHp - damageTaken);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user