merge: 예약 명령 인자 요약을 사령부 호환에 통합
# Conflicts: # app/game-frontend/src/components/command/ReservedCommandEditor.vue
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
import { JosaUtil } from '@sammo-ts/common';
|
||||
|
||||
import type { CommandOption, CommandTable } from './types';
|
||||
|
||||
type CommandScope = 'general' | 'nation';
|
||||
type CommandArgs = Record<string, unknown>;
|
||||
|
||||
const CITY_TO_COMMANDS = new Set(['che_이동', 'che_강행', 'che_출병', 'che_천도']);
|
||||
const CITY_AT_COMMANDS = new Set([
|
||||
'che_첩보',
|
||||
'che_화계',
|
||||
'che_선동',
|
||||
'che_탈취',
|
||||
'che_파괴',
|
||||
'che_백성동원',
|
||||
'che_허보',
|
||||
]);
|
||||
const CITY_OBJECT_COMMANDS = new Set(['che_수몰', 'che_초토화']);
|
||||
const NATION_AT_COMMANDS = new Set(['che_선전포고', 'che_급습', 'che_이호경식']);
|
||||
const NATION_PROPOSAL_COMMANDS = new Set(['che_불가침파기제의', 'che_종전제의']);
|
||||
|
||||
const ITEM_TYPE_NAMES: Record<string, string> = {
|
||||
horse: '명마',
|
||||
weapon: '무기',
|
||||
book: '서적',
|
||||
item: '도구',
|
||||
};
|
||||
|
||||
const asArgs = (value: unknown): CommandArgs =>
|
||||
value && typeof value === 'object' && !Array.isArray(value) ? (value as CommandArgs) : {};
|
||||
|
||||
const firstValue = (args: CommandArgs, keys: readonly string[]): unknown => {
|
||||
for (const key of keys) {
|
||||
if (args[key] !== undefined) return args[key];
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const optionLabel = (options: readonly CommandOption[], value: unknown): string | null => {
|
||||
const option = options.find((entry) => entry.value === value || String(entry.value) === String(value));
|
||||
if (!option) return null;
|
||||
// 도시·장수 option에는 선택 보조용 소속 정보가 괄호로 붙지만 Ref brief에는 이름만 들어간다.
|
||||
return option.label.replace(/\s+\([^)]*\)$/u, '');
|
||||
};
|
||||
|
||||
const commandNameMap = (table: CommandTable | null, scope: CommandScope): Map<string, string> => {
|
||||
const result = new Map<string, string>([['휴식', '휴식']]);
|
||||
for (const group of table?.[scope] ?? []) {
|
||||
for (const command of group.values) result.set(command.key, command.name);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
const defaultCommandName = (action: string): string => action.replace(/^(?:che_|cr_|event_)/u, '');
|
||||
|
||||
const numberText = (value: unknown, grouped = false): string => {
|
||||
if (typeof value !== 'number' || !Number.isFinite(value)) return String(value ?? '');
|
||||
return grouped ? value.toLocaleString('en-US') : String(value);
|
||||
};
|
||||
|
||||
const wrap = (value: string): string => `【${value}】`;
|
||||
const withParticle = (value: string, particle: '을' | '으로'): string => `${value}${JosaUtil.pick(value, particle)}`;
|
||||
const wrappedWithParticle = (value: string, particle: '을' | '으로'): string =>
|
||||
`${wrap(value)}${JosaUtil.pick(value, particle)}`;
|
||||
|
||||
export const formatReservedCommandBrief = (
|
||||
scope: CommandScope,
|
||||
action: string,
|
||||
rawArgs: unknown,
|
||||
table: CommandTable | null
|
||||
): string => {
|
||||
const args = asArgs(rawArgs);
|
||||
const input = table?.inputOptions;
|
||||
const commandNames = commandNameMap(table, scope);
|
||||
const commandName = commandNames.get(action) ?? defaultCommandName(action);
|
||||
const cityName = optionLabel(input?.cities ?? [], firstValue(args, ['destCityId', 'destCityID']));
|
||||
const nationName = optionLabel(input?.nations ?? [], firstValue(args, ['destNationId', 'destNationID']));
|
||||
const generalName = optionLabel(
|
||||
input?.generalTargets?.[action] ?? input?.generals ?? [],
|
||||
firstValue(args, ['destGeneralId', 'destGeneralID'])
|
||||
);
|
||||
|
||||
if (CITY_TO_COMMANDS.has(action) && cityName) {
|
||||
return `${wrappedWithParticle(cityName, '으로')} ${commandName}`;
|
||||
}
|
||||
if (action === 'cr_인구이동' && cityName) {
|
||||
return `${wrappedWithParticle(cityName, '으로')} ${numberText(args.amount, true)}명 ${commandName}`;
|
||||
}
|
||||
if (CITY_AT_COMMANDS.has(action) && cityName) {
|
||||
const suffix =
|
||||
scope === 'nation' ? commandName : action === 'che_첩보' ? `${commandName} 실행` : `${commandName}실행`;
|
||||
return `${wrap(cityName)}에 ${suffix}`;
|
||||
}
|
||||
if (CITY_OBJECT_COMMANDS.has(action) && cityName) {
|
||||
return `${wrappedWithParticle(cityName, '을')} ${commandName}`;
|
||||
}
|
||||
if (action === 'che_임관' && nationName) {
|
||||
return `${wrappedWithParticle(nationName, '으로')} ${commandName}`;
|
||||
}
|
||||
if (NATION_AT_COMMANDS.has(action) && nationName) {
|
||||
return `${wrap(nationName)}에 ${commandName}`;
|
||||
}
|
||||
if (NATION_PROPOSAL_COMMANDS.has(action) && nationName) {
|
||||
return `${wrap(nationName)}에게 ${commandName}`;
|
||||
}
|
||||
if (action === 'che_불가침제의' && nationName) {
|
||||
return `${wrap(nationName)}에게 ${numberText(args.year)}년 ${numberText(args.month)}월까지 ${commandName}`;
|
||||
}
|
||||
if (action === 'che_등용' && generalName) {
|
||||
return `${wrappedWithParticle(generalName, '을')} ${commandName}`;
|
||||
}
|
||||
if (action === 'che_장수대상임관' && generalName) {
|
||||
return `${wrappedWithParticle(generalName, '을')} 따라 임관`;
|
||||
}
|
||||
if (action === 'che_선양' && generalName) {
|
||||
return `${wrap(generalName)}에게 ${commandName}`;
|
||||
}
|
||||
if (action === 'che_랜덤임관') {
|
||||
return '무작위 국가로 임관';
|
||||
}
|
||||
if ((action === 'che_건국' || action === 'cr_건국') && typeof args.nationName === 'string') {
|
||||
return `${wrappedWithParticle(args.nationName, '을')} 건국`;
|
||||
}
|
||||
if (action === 'che_무작위건국' && typeof args.nationName === 'string') {
|
||||
return `${wrappedWithParticle(args.nationName, '을')} 무작위 도시에 건국`;
|
||||
}
|
||||
if (action === 'che_군량매매') {
|
||||
return `군량 ${numberText(args.amount)}을 ${args.buyRice ? '구입' : '판매'}`;
|
||||
}
|
||||
if (action === 'che_헌납') {
|
||||
return `${args.isGold ? '금' : '쌀'} ${numberText(args.amount)}을 ${commandName}`;
|
||||
}
|
||||
if (action === 'che_증여' && generalName) {
|
||||
return `${wrap(generalName)}에게 ${args.isGold ? '금' : '쌀'} ${numberText(args.amount)}을 ${commandName}`;
|
||||
}
|
||||
if (action === 'che_징병' || action === 'che_모병') {
|
||||
const crewType = optionLabel(input?.crewTypes ?? [], args.crewType);
|
||||
if (crewType) return `${wrap(crewType)} ${numberText(args.amount)}명 ${commandName}`;
|
||||
}
|
||||
if (action === 'che_숙련전환') {
|
||||
const srcArmType = optionLabel(input?.armTypes ?? [], args.srcArmType);
|
||||
const destArmType = optionLabel(input?.armTypes ?? [], args.destArmType);
|
||||
if (srcArmType && destArmType) return `${wrap(srcArmType)}숙련을 ${wrap(destArmType)}숙련으로 전환`;
|
||||
}
|
||||
if (action === 'che_장비매매') {
|
||||
const itemType = typeof args.itemType === 'string' ? args.itemType : '';
|
||||
if (args.itemCode === 'None') {
|
||||
const itemTypeName = ITEM_TYPE_NAMES[itemType];
|
||||
if (itemTypeName) return `${withParticle(itemTypeName, '을')} 판매`;
|
||||
}
|
||||
const itemName = optionLabel(input?.items[itemType] ?? [], args.itemCode);
|
||||
if (itemName) {
|
||||
const itemRawName = itemName.replace(/\([^)]*\)$/u, '');
|
||||
return `${wrap(itemName)}${JosaUtil.pick(itemRawName, '을')} 구입`;
|
||||
}
|
||||
}
|
||||
if (action === 'che_발령' && generalName && cityName) {
|
||||
return `${wrap(generalName)}${wrappedWithParticle(cityName, '으로')} ${commandName}`;
|
||||
}
|
||||
if (action === 'che_부대탈퇴지시' && generalName) {
|
||||
return `${wrap(generalName)}${commandName}`;
|
||||
}
|
||||
if ((action === 'che_포상' || action === 'che_몰수') && generalName) {
|
||||
return `${wrap(generalName)} ${args.isGold ? '금' : '쌀'} ${numberText(args.amount, true)} ${commandName}`;
|
||||
}
|
||||
if (action === 'che_물자원조' && nationName && Array.isArray(args.amountList)) {
|
||||
return `${wrap(nationName)}에게 국고 ${numberText(args.amountList[0], true)} 병량 ${numberText(
|
||||
args.amountList[1],
|
||||
true
|
||||
)} ${commandName}`;
|
||||
}
|
||||
if (action === 'che_증축' || action === 'che_감축') {
|
||||
return `수도를 ${commandName}`;
|
||||
}
|
||||
if (action === 'che_국기변경') {
|
||||
return '【국기】를 변경';
|
||||
}
|
||||
if (action === 'che_국호변경' && typeof args.nationName === 'string') {
|
||||
return `국호를 ${wrappedWithParticle(args.nationName, '으로')} 변경`;
|
||||
}
|
||||
if (action === 'che_피장파장' && nationName) {
|
||||
const targetAction = typeof args.commandType === 'string' ? args.commandType : '';
|
||||
const targetName = commandNames.get(targetAction) ?? defaultCommandName(targetAction);
|
||||
return `${wrap(nationName)}에 ${wrap(targetName)} ${commandName}`;
|
||||
}
|
||||
|
||||
return commandName;
|
||||
};
|
||||
Reference in New Issue
Block a user