feat: execute MyPage immediate actions in daemon
This commit is contained in:
@@ -70,8 +70,8 @@ export type TurnDaemonCommand =
|
||||
}
|
||||
| { type: 'troopRename'; requestId?: string; generalId: number; troopId: number; troopName: string }
|
||||
| { type: 'dieOnPrestart'; requestId?: string; generalId: number }
|
||||
| { type: 'buildNationCandidate'; requestId?: string; generalId: number }
|
||||
| { type: 'instantRetreat'; requestId?: string; generalId: number }
|
||||
| { type: 'buildNationCandidate'; requestId?: string; userId: string; generalId: number }
|
||||
| { type: 'instantRetreat'; requestId?: string; userId: string; generalId: number }
|
||||
| { type: 'vacation'; requestId?: string; generalId: number }
|
||||
| {
|
||||
type: 'setMySetting';
|
||||
|
||||
@@ -30,6 +30,9 @@ export interface UprisingContext extends ActionContextBase {
|
||||
}
|
||||
|
||||
const ACTION_NAME = '거병';
|
||||
const formatHourMinute = (date: Date): string =>
|
||||
`${String(date.getUTCHours()).padStart(2, '0')}:${String(date.getUTCMinutes()).padStart(2, '0')}`;
|
||||
|
||||
const truncateLegacyWidth = (value: string, maxWidth: number): string => {
|
||||
let result = '';
|
||||
let width = 0;
|
||||
@@ -119,7 +122,7 @@ export class ActionDefinition<
|
||||
|
||||
const cityName = context.city?.name ?? '??';
|
||||
|
||||
context.addLog(`거병에 성공하였습니다.`, {
|
||||
context.addLog(`거병에 성공하였습니다. <1>${formatHourMinute(uprisingCtx.general.turnTime)}</>`, {
|
||||
category: LogCategory.ACTION,
|
||||
format: LogFormat.MONTH,
|
||||
});
|
||||
@@ -128,18 +131,21 @@ export class ActionDefinition<
|
||||
category: LogCategory.SUMMARY,
|
||||
format: LogFormat.MONTH,
|
||||
});
|
||||
context.addLog(
|
||||
`<Y><b>【거병】</b></><D><b>${general.name}</b></>${josaYi} 세력을 결성하였습니다.`,
|
||||
{
|
||||
scope: LogScope.SYSTEM,
|
||||
category: LogCategory.HISTORY,
|
||||
format: LogFormat.YEAR_MONTH,
|
||||
}
|
||||
);
|
||||
context.addLog(`<Y><b>【거병】</b></><D><b>${general.name}</b></>${josaYi} 세력을 결성하였습니다.`, {
|
||||
scope: LogScope.SYSTEM,
|
||||
category: LogCategory.HISTORY,
|
||||
format: LogFormat.YEAR_MONTH,
|
||||
});
|
||||
context.addLog(`<G><b>${cityName}</b></>에서 거병`, {
|
||||
category: LogCategory.HISTORY,
|
||||
format: LogFormat.YEAR_MONTH,
|
||||
});
|
||||
context.addLog(`<Y>${general.name}</>${josaYi} <G><b>${cityName}</b></>에서 거병`, {
|
||||
scope: LogScope.NATION,
|
||||
nationId: newNationId,
|
||||
category: LogCategory.HISTORY,
|
||||
format: LogFormat.YEAR_MONTH,
|
||||
});
|
||||
|
||||
tryApplyUniqueLottery(context, {
|
||||
acquireType: '아이템',
|
||||
|
||||
@@ -8,7 +8,6 @@ import { LogCategory, LogFormat, LogScope } from '@sammo-ts/logic/logging/types.
|
||||
import { JosaUtil } from '@sammo-ts/common';
|
||||
import type { ActionContextBuilder } from '@sammo-ts/logic/actions/turn/actionContext.js';
|
||||
import type { TurnCommandEnv } from '@sammo-ts/logic/actions/turn/commandEnv.js';
|
||||
import { searchDistance } from '@sammo-ts/logic/world/distance.js';
|
||||
import type { MapDefinition } from '@sammo-ts/logic/world/types.js';
|
||||
import type { GeneralTurnCommandSpec } from './index.js';
|
||||
|
||||
@@ -23,9 +22,7 @@ type InclusiveRandomGenerator = GeneralActionResolveContext['rng'] & {
|
||||
|
||||
const legacyChoiceIndex = (rng: GeneralActionResolveContext['rng'], length: number): number => {
|
||||
const inclusive = rng as InclusiveRandomGenerator;
|
||||
return inclusive.nextIntInclusive
|
||||
? inclusive.nextIntInclusive(length - 1)
|
||||
: rng.nextInt(0, length);
|
||||
return inclusive.nextIntInclusive ? inclusive.nextIntInclusive(length - 1) : rng.nextInt(0, length);
|
||||
};
|
||||
|
||||
export interface BorderReturnResolveContext<
|
||||
@@ -66,26 +63,32 @@ export class ActionDefinition<
|
||||
return { effects: [] };
|
||||
}
|
||||
|
||||
const distanceByCity = searchDistance(map, general.cityId, 3);
|
||||
const candidates = allCities
|
||||
.filter((city) => city.nationId === nation.id && city.supplyState === 1)
|
||||
.map((city) => ({ city, distance: distanceByCity[city.id] }))
|
||||
.filter((entry) => typeof entry.distance === 'number' && Number.isFinite(entry.distance));
|
||||
|
||||
if (candidates.length === 0) {
|
||||
context.addLog('3칸 이내에 아국 도시가 없습니다.', {
|
||||
scope: LogScope.GENERAL,
|
||||
category: LogCategory.ACTION,
|
||||
format: LogFormat.MONTH,
|
||||
const cityById = new Map(allCities.map((city) => [city.id, city]));
|
||||
const mapCityById = new Map(map.cities.map((city) => [city.id, city]));
|
||||
const visited = new Set([general.cityId]);
|
||||
let frontier = [general.cityId];
|
||||
let nearestCities: City[] = [];
|
||||
for (let distance = 1; distance <= 3 && frontier.length > 0; distance += 1) {
|
||||
const nextFrontier: number[] = [];
|
||||
for (const cityId of frontier) {
|
||||
for (const connectedCityId of mapCityById.get(cityId)?.connections ?? []) {
|
||||
if (visited.has(connectedCityId)) {
|
||||
continue;
|
||||
}
|
||||
visited.add(connectedCityId);
|
||||
nextFrontier.push(connectedCityId);
|
||||
}
|
||||
}
|
||||
nearestCities = nextFrontier.flatMap((cityId) => {
|
||||
const city = cityById.get(cityId);
|
||||
return city?.nationId === nation.id && city.supplyState === 1 ? [city] : [];
|
||||
});
|
||||
return { effects: [] };
|
||||
if (nearestCities.length > 0) {
|
||||
break;
|
||||
}
|
||||
frontier = nextFrontier;
|
||||
}
|
||||
|
||||
const minDistance = Math.min(...candidates.map((entry) => entry.distance as number));
|
||||
const nearestCities = candidates
|
||||
.filter((entry) => entry.distance === minDistance)
|
||||
.map((entry) => entry.city);
|
||||
|
||||
if (nearestCities.length === 0) {
|
||||
context.addLog('3칸 이내에 아국 도시가 없습니다.', {
|
||||
scope: LogScope.GENERAL,
|
||||
|
||||
Reference in New Issue
Block a user