From 9cfeaed3fe811f02b3cd368fc2ab947cc92e1bb3 Mon Sep 17 00:00:00 2001 From: hided62 Date: Tue, 4 Aug 2026 05:29:12 +0000 Subject: [PATCH 1/2] fix(api): align scenario 2601 reference data --- app/game-api/src/router/lobby/index.ts | 4 + .../nation/endpoints/getBattleCenter.ts | 91 ++++++++++++++----- .../router/nation/endpoints/getGeneralLog.ts | 11 ++- .../router/nation/endpoints/getNationInfo.ts | 2 +- .../nation/endpoints/getSecretGeneralList.ts | 11 ++- .../router/nation/endpoints/getStratFinan.ts | 51 ++++++----- app/game-api/src/router/world/directory.ts | 13 ++- app/game-api/src/router/yearbook/index.ts | 34 ++++--- .../test/inGameMenuPermissions.test.ts | 35 +++++-- .../test/yearbookArchiveRouter.test.ts | 38 +++++++- 10 files changed, 212 insertions(+), 78 deletions(-) diff --git a/app/game-api/src/router/lobby/index.ts b/app/game-api/src/router/lobby/index.ts index e6653e7..abd5ba3 100644 --- a/app/game-api/src/router/lobby/index.ts +++ b/app/game-api/src/router/lobby/index.ts @@ -1,5 +1,7 @@ import { TRPCError } from '@trpc/server'; +import { asRecord } from '@sammo-ts/common'; + import { zWorldStateConfig, zWorldStateMeta } from '../../context.js'; import { isSelectionPoolWorld, resolveSelectionMaxGeneral } from '../../services/selectPool.js'; import { procedure, router } from '../../trpc.js'; @@ -23,6 +25,7 @@ export const lobbyRouter = router({ const userCnt = await ctx.db.general.count({ where: { npcState: { lt: 2 } } }); const npcCnt = await ctx.db.general.count({ where: { npcState: { gte: 2 } } }); const nationCnt = await ctx.db.nation.count({ where: { level: { gt: 0 } } }); + const scenarioTitle = asRecord(asRecord(rawWorldState.meta).scenarioMeta).title; let myGeneral = null; if (ctx.auth?.user.id) { @@ -55,6 +58,7 @@ export const lobbyRouter = router({ isUnited: worldState.meta.isunited ?? worldState.meta.isUnited ?? 0, selectionPoolEnabled: isSelectionPoolWorld(rawWorldState), npcPossessionEnabled: worldState.config.npcMode === 1, + scenarioTitle: typeof scenarioTitle === 'string' ? scenarioTitle : '', myGeneral, }; }), diff --git a/app/game-api/src/router/nation/endpoints/getBattleCenter.ts b/app/game-api/src/router/nation/endpoints/getBattleCenter.ts index 2628fa1..63525a1 100644 --- a/app/game-api/src/router/nation/endpoints/getBattleCenter.ts +++ b/app/game-api/src/router/nation/endpoints/getBattleCenter.ts @@ -27,6 +27,8 @@ export const getBattleCenter = accessAuthedProcedure.query(async ({ ctx }) => { select: { id: true, name: true, + picture: true, + imageServer: true, npcState: true, officerLevel: true, cityId: true, @@ -43,6 +45,16 @@ export const getBattleCenter = accessAuthedProcedure.query(async ({ ctx }) => { crew: true, train: true, atmos: true, + age: true, + crewTypeId: true, + weaponCode: true, + bookCode: true, + horseCode: true, + itemCode: true, + personalCode: true, + specialCode: true, + special2Code: true, + meta: true, }, orderBy: { id: 'asc' }, }), @@ -79,29 +91,62 @@ export const getBattleCenter = accessAuthedProcedure.query(async ({ ctx }) => { } } - const generals = generalRows.map((general) => ({ - id: general.id, - name: general.name, - npcState: general.npcState, - officerLevel: general.officerLevel, - cityId: general.cityId, - turnTime: formatDateTime(general.turnTime), - recentWar: formatDateTime(general.recentWarTime), - warnum: battleCountMap.get(general.id) ?? 0, - stats: { - leadership: general.leadership, - strength: general.strength, - intelligence: general.intel, - }, - experience: general.experience, - dedication: general.dedication, - injury: general.injury, - gold: general.gold, - rice: general.rice, - crew: general.crew, - train: general.train, - atmos: general.atmos, - })); + const generals = generalRows.map((general) => { + const meta = + general.meta && typeof general.meta === 'object' && !Array.isArray(general.meta) + ? (general.meta as Record) + : {}; + const metaNumber = (key: string): number => { + const value = meta[key]; + return typeof value === 'number' && Number.isFinite(value) ? value : 0; + }; + return { + id: general.id, + name: general.name, + picture: general.picture, + imageServer: general.imageServer, + npcState: general.npcState, + officerLevel: general.officerLevel, + cityId: general.cityId, + turnTime: formatDateTime(general.turnTime), + recentWar: formatDateTime(general.recentWarTime), + warnum: battleCountMap.get(general.id) ?? 0, + stats: { + leadership: general.leadership, + strength: general.strength, + intelligence: general.intel, + }, + experience: general.experience, + dedication: general.dedication, + injury: general.injury, + gold: general.gold, + rice: general.rice, + crew: general.crew, + train: general.train, + atmos: general.atmos, + age: general.age, + crewTypeId: general.crewTypeId, + equipment: { + weapon: general.weaponCode, + book: general.bookCode, + horse: general.horseCode, + item: general.itemCode, + }, + traits: { + personal: general.personalCode, + specialDomestic: general.specialCode, + specialWar: general.special2Code, + }, + battleStats: { + kills: metaNumber('rank_killnum') || metaNumber('killnum'), + deaths: metaNumber('deathnum'), + fire: metaNumber('firenum'), + killCrew: metaNumber('killcrew'), + deathCrew: metaNumber('deathcrew'), + dex: [1, 2, 3, 4, 5].map((index) => metaNumber(`dex${index}`)), + }, + }; + }); return { me: { diff --git a/app/game-api/src/router/nation/endpoints/getGeneralLog.ts b/app/game-api/src/router/nation/endpoints/getGeneralLog.ts index a9c612d..d280e5c 100644 --- a/app/game-api/src/router/nation/endpoints/getGeneralLog.ts +++ b/app/game-api/src/router/nation/endpoints/getGeneralLog.ts @@ -5,7 +5,13 @@ import { LogCategory, LogScope } from '@sammo-ts/infra'; import { authedProcedure } from '../../../trpc.js'; import { getMyGeneral } from '../../shared/general.js'; -import { assertNationAccess, resolveNationPermission, zGeneralLogType, type GeneralLogType } from '../shared.js'; +import { + assertNationAccess, + formatDateTime, + resolveNationPermission, + zGeneralLogType, + type GeneralLogType, +} from '../shared.js'; export const getGeneralLog = authedProcedure .input( @@ -75,6 +81,9 @@ export const getGeneralLog = authedProcedure logs: logs.map((entry) => ({ id: entry.id, text: entry.text, + year: entry.year, + month: entry.month, + createdAt: formatDateTime(entry.createdAt), })), }; }); diff --git a/app/game-api/src/router/nation/endpoints/getNationInfo.ts b/app/game-api/src/router/nation/endpoints/getNationInfo.ts index bc3aa93..5da19c3 100644 --- a/app/game-api/src/router/nation/endpoints/getNationInfo.ts +++ b/app/game-api/src/router/nation/endpoints/getNationInfo.ts @@ -30,7 +30,7 @@ export const getNationInfo = authedProcedure.query(async ({ ctx }) => { nationId: me.nationId, }, select: { id: true, year: true, month: true, text: true }, - orderBy: { id: 'asc' }, + orderBy: { id: 'desc' }, }), ]); if (!nation) { diff --git a/app/game-api/src/router/nation/endpoints/getSecretGeneralList.ts b/app/game-api/src/router/nation/endpoints/getSecretGeneralList.ts index 09e9cc2..c4fede1 100644 --- a/app/game-api/src/router/nation/endpoints/getSecretGeneralList.ts +++ b/app/game-api/src/router/nation/endpoints/getSecretGeneralList.ts @@ -2,6 +2,7 @@ import { TRPCError } from '@trpc/server'; import { asRecord } from '@sammo-ts/common'; +import { loadUnitSetDefinitionByName } from '../../../battleSim/unitSetLoader.js'; import { accessAuthedProcedure } from '../../../trpc.js'; import { getMyGeneral } from '../../shared/general.js'; import { assertNationAccess, resolveNationPermission } from '../shared.js'; @@ -41,7 +42,7 @@ export const getSecretGeneralList = accessAuthedProcedure.query(async ({ ctx }) }); } - const [cities, troops, generalRows] = await Promise.all([ + const [cities, troops, generalRows, worldState] = await Promise.all([ ctx.db.city.findMany({ select: { id: true, name: true } }), ctx.db.troop.findMany({ where: { nationId: me.nationId }, @@ -51,7 +52,14 @@ export const getSecretGeneralList = accessAuthedProcedure.query(async ({ ctx }) where: { nationId: me.nationId }, orderBy: [{ turnTime: 'asc' }, { id: 'asc' }], }), + ctx.db.worldState.findFirst({ select: { config: true } }), ]); + const worldConfig = asRecord(worldState?.config); + const environment = asRecord(worldConfig.environment ?? worldConfig.map); + const unitSetName = + typeof environment.unitSet === 'string' && environment.unitSet.trim() ? environment.unitSet : ctx.profile.id; + const unitSet = await loadUnitSetDefinitionByName(unitSetName); + const crewTypeNames = new Map((unitSet.crewTypes ?? []).map((crewType) => [crewType.id, crewType.name])); const generalIds = generalRows.map((general) => general.id); const turns = generalIds.length ? await ctx.db.generalTurn.findMany({ @@ -92,6 +100,7 @@ export const getSecretGeneralList = accessAuthedProcedure.query(async ({ ctx }) defenceTrain, defenceTrainText: defenceTrainText(defenceTrain), crewTypeId: general.crewTypeId, + crewTypeName: crewTypeNames.get(general.crewTypeId) ?? '-', crew: general.crew, train: general.train, atmos: general.atmos, diff --git a/app/game-api/src/router/nation/endpoints/getStratFinan.ts b/app/game-api/src/router/nation/endpoints/getStratFinan.ts index 8f79b55..c2e81cc 100644 --- a/app/game-api/src/router/nation/endpoints/getStratFinan.ts +++ b/app/game-api/src/router/nation/endpoints/getStratFinan.ts @@ -1,7 +1,14 @@ import { TRPCError } from '@trpc/server'; import { asRecord } from '@sammo-ts/common'; -import { getGoldIncome, getOutcome, getRiceIncome, getWallIncome, getWarGoldIncome, type NationIncomeContext } from '@sammo-ts/logic'; +import { + getGoldIncome, + getOutcome, + getRiceIncome, + getWallIncome, + getWarGoldIncome, + type NationIncomeContext, +} from '@sammo-ts/logic'; import { accessAuthedProcedure } from '../../../trpc.js'; import { getMyGeneral } from '../../shared/general.js'; @@ -171,13 +178,7 @@ export const getStratFinan = accessAuthedProcedure.query(async ({ ctx }) => { const cityStatsByNation = new Map(); for (const city of cityRows) { const entry = cityStatsByNation.get(city.nationId) ?? { popSum: 0, valueSum: 0, maxSum: 0 }; - const valueSum = - city.population + - city.agriculture + - city.commerce + - city.security + - city.wall + - city.defence; + const valueSum = city.population + city.agriculture + city.commerce + city.security + city.wall + city.defence; const maxSum = city.populationMax + city.agricultureMax + @@ -222,22 +223,24 @@ export const getStratFinan = accessAuthedProcedure.query(async ({ ctx }) => { ); } - const nationsList = nationRows.map((nationItem) => { - const diplomacy = - nationItem.id === nation.id - ? { state: 7, term: null } - : diplomacyMap.get(nationItem.id) ?? { state: 2, term: 0 }; - return { - id: nationItem.id, - name: nationItem.name, - color: nationItem.color, - level: nationItem.level, - power: powerByNation.get(nationItem.id) ?? 0, - generalCount: generalCountMap.get(nationItem.id) ?? 0, - cityCount: cityCountMap.get(nationItem.id) ?? 0, - diplomacy, - }; - }); + const nationsList = nationRows + .filter((nationItem) => nationItem.id > 0) + .map((nationItem) => { + const diplomacy = + nationItem.id === nation.id + ? { state: 7, term: null } + : (diplomacyMap.get(nationItem.id) ?? { state: 2, term: 0 }); + return { + id: nationItem.id, + name: nationItem.name, + color: nationItem.color, + level: nationItem.level, + power: powerByNation.get(nationItem.id) ?? 0, + generalCount: generalCountMap.get(nationItem.id) ?? 0, + cityCount: cityCountMap.get(nationItem.id) ?? 0, + diplomacy, + }; + }); const nationCities = cityRows.filter((city) => city.nationId === nation.id); const nationGenerals = generalRows.filter((general) => general.nationId === nation.id); diff --git a/app/game-api/src/router/world/directory.ts b/app/game-api/src/router/world/directory.ts index 38d94cc..47b646e 100644 --- a/app/game-api/src/router/world/directory.ts +++ b/app/game-api/src/router/world/directory.ts @@ -143,9 +143,7 @@ export const getNationDirectory = authedProcedure.query(async ({ ctx }) => { const nationGenerals = generalsByNation.get(nation.id) ?? []; const nationCities = citiesByNation.get(nation.id) ?? []; const officers = Array.from({ length: 8 }, (_, index) => 12 - index).map((officerLevel) => { - const general = nationGenerals - .filter((candidate) => candidate.officerLevel === officerLevel) - .at(-1); + const general = nationGenerals.filter((candidate) => candidate.officerLevel === officerLevel).at(-1); return { officerLevel, general: general @@ -178,7 +176,7 @@ export const getNationDirectory = authedProcedure.query(async ({ ctx }) => { }, power: readMetaNumber(nation.meta, 'power'), capitalCityId: nation.capitalCityId ?? 0, - generalCount: nationGenerals.length, + generalCount: readMetaNumber(nation.meta, 'gennum', nationGenerals.length), cityCount: nationCities.length, officers, ambassadorNames: secretPermissions @@ -204,8 +202,8 @@ export const getNationDirectory = authedProcedure.query(async ({ ctx }) => { }); }); -export const getGeneralDirectory = accessAuthedInputProcedure(z.object({ sort: zDirectorySort }).optional()) - .query(async ({ ctx, input }) => { +export const getGeneralDirectory = accessAuthedInputProcedure(z.object({ sort: zDirectorySort }).optional()).query( + async ({ ctx, input }) => { await getMyGeneral(ctx); const sort = input?.sort ?? 9; const [generals, nations, accessLogs, worldState] = await Promise.all([ @@ -365,4 +363,5 @@ export const getGeneralDirectory = accessAuthedInputProcedure(z.object({ sort: z }); return { sort, generals: rows }; - }); + } +); diff --git a/app/game-api/src/router/yearbook/index.ts b/app/game-api/src/router/yearbook/index.ts index da38770..6eb9d09 100644 --- a/app/game-api/src/router/yearbook/index.ts +++ b/app/game-api/src/router/yearbook/index.ts @@ -7,10 +7,7 @@ import { LogCategory, LogScope } from '@sammo-ts/infra'; import type { GameApiContext } from '../../context.js'; import { loadPublicMap, type BaseMapResult } from '../../maps/worldMap.js'; -import { - generalAccessEndpointWeights, - recordGeneralAccessWeight, -} from '../../services/generalAccess.js'; +import { generalAccessEndpointWeights, recordGeneralAccessWeight } from '../../services/generalAccess.js'; import { authedProcedure, router } from '../../trpc.js'; import { getMyGeneral } from '../shared/general.js'; @@ -72,6 +69,7 @@ const parseYearbookNations = (value: unknown): YearbookNation[] => { const resolveArchiveTarget = ( worldMeta: unknown, + profileId: string, profileName: string, requestedServerId?: string ): { archiveKey: string; legacyAlias: string | null; isCurrentProfile: boolean } => { @@ -81,7 +79,12 @@ const resolveArchiveTarget = ( const isCurrentProfile = requested === profileName || requested === canonicalServerId; return { archiveKey: isCurrentProfile ? canonicalServerId : requested, - legacyAlias: isCurrentProfile && canonicalServerId !== profileName ? profileName : null, + legacyAlias: + isCurrentProfile && canonicalServerId !== profileName + ? profileName + : isCurrentProfile && profileId !== canonicalServerId + ? profileId + : null, isCurrentProfile, }; }; @@ -263,7 +266,7 @@ export const yearbookRouter = router({ message: 'World state is not initialized.', }); } - const target = resolveArchiveTarget(worldState.meta, ctx.profile.name, input?.serverID); + const target = resolveArchiveTarget(worldState.meta, ctx.profile.id, ctx.profile.name, input?.serverID); const findRange = async (profileName: string) => Promise.all([ @@ -278,10 +281,19 @@ export const yearbookRouter = router({ orderBy: [{ year: 'desc' as const }, { month: 'desc' as const }], }), ]); - let [firstRow, lastRow] = await findRange(target.archiveKey); - if ((!firstRow || !lastRow) && target.legacyAlias) { - [firstRow, lastRow] = await findRange(target.legacyAlias); - } + const ranges = await Promise.all( + [target.archiveKey, target.legacyAlias] + .filter((value): value is string => Boolean(value)) + .map(findRange) + ); + const firstRow = ranges + .map(([first]) => first) + .filter((row): row is NonNullable => Boolean(row)) + .sort((a, b) => joinYearMonth(a.year, a.month) - joinYearMonth(b.year, b.month))[0]; + const lastRow = ranges + .map(([, last]) => last) + .filter((row): row is NonNullable => Boolean(row)) + .sort((a, b) => joinYearMonth(b.year, b.month) - joinYearMonth(a.year, a.month))[0]; if (!target.isCurrentProfile && (!firstRow || !lastRow)) { throw new TRPCError({ code: 'NOT_FOUND', message: '연감 범위를 찾을 수 없습니다.' }); @@ -314,7 +326,7 @@ export const yearbookRouter = router({ if (!worldState) { throw new TRPCError({ code: 'PRECONDITION_FAILED', message: 'World state is not initialized.' }); } - const target = resolveArchiveTarget(worldState.meta, ctx.profile.name, input.serverID); + const target = resolveArchiveTarget(worldState.meta, ctx.profile.id, ctx.profile.name, input.serverID); const shouldRecordAfterHashCheck = target.isCurrentProfile && Boolean(input.hash); if (target.isCurrentProfile && !shouldRecordAfterHashCheck) { await recordHistoryAccess(ctx); diff --git a/app/game-api/test/inGameMenuPermissions.test.ts b/app/game-api/test/inGameMenuPermissions.test.ts index 6480029..a1bb7e3 100644 --- a/app/game-api/test/inGameMenuPermissions.test.ts +++ b/app/game-api/test/inGameMenuPermissions.test.ts @@ -79,7 +79,7 @@ const createContext = (options: { nationMeta?: Record; requestCommand?: ReturnType; accessToken?: string; - logs?: Array<{ id: number; text: string }>; + logs?: Array<{ id: number; text: string; year?: number; month?: number; createdAt?: Date }>; }) => { const me = options.me === undefined ? buildGeneral() : options.me; const targets = options.targets ?? (me ? [me] : []); @@ -119,12 +119,24 @@ const createContext = (options: { }, logEntry: { groupBy: vi.fn(async () => []), - findMany: vi.fn(async (query?: { where?: { id?: { lt?: number } }; take?: number }) => { - const source = options.logs ?? [{ id: 1, text: '기록' }]; - const beforeId = query?.where?.id?.lt; - const filtered = beforeId ? source.filter((entry) => entry.id < beforeId) : source; - return query?.take ? filtered.slice(0, query.take) : filtered; - }), + findMany: vi.fn( + async (query?: { + where?: { id?: { lt?: number } }; + take?: number; + select?: { id?: boolean; text?: boolean }; + }) => { + const source = (options.logs ?? [{ id: 1, text: '기록' }]).map((entry) => ({ + year: 185, + month: 1, + createdAt: now, + ...entry, + })); + const beforeId = query?.where?.id?.lt; + const filtered = beforeId ? source.filter((entry) => entry.id < beforeId) : source; + const selected = query?.select ? filtered.map(({ id, text }) => ({ id, text })) : filtered; + return query?.take ? selected.slice(0, query.take) : selected; + } + ), }, }; const redisClient = { get: async () => null, set: async () => null }; @@ -405,6 +417,14 @@ describe('battle-center general and user permissions', () => { }); await expect(appRouter.createCaller(tenured.context).nation.getBattleCenter()).resolves.toMatchObject({ me: { id: 7, permissionLevel: 1 }, + generals: [ + { + id: 7, + picture: 'default.jpg', + imageServer: 0, + battleStats: { kills: 0, deaths: 0, fire: 0, killCrew: 0, deathCrew: 0, dex: [0, 0, 0, 0, 0] }, + }, + ], }); const auditor = createContext({ @@ -430,6 +450,7 @@ describe('battle-center general and user permissions', () => { await expect(member.nation.getGeneralLog({ generalId: me.id, type: 'generalAction' })).resolves.toMatchObject({ generalId: me.id, + logs: [{ id: 1, year: 185, month: 1, createdAt: '2026-01-01 00:00:00' }], }); await expect( member.nation.getGeneralLog({ generalId: otherUser.id, type: 'generalAction' }) diff --git a/app/game-api/test/yearbookArchiveRouter.test.ts b/app/game-api/test/yearbookArchiveRouter.test.ts index 24e8076..b75bd22 100644 --- a/app/game-api/test/yearbookArchiveRouter.test.ts +++ b/app/game-api/test/yearbookArchiveRouter.test.ts @@ -52,12 +52,27 @@ const archiveRows = [ year: 219, month: 12, map: { year: 219, month: 12, startYear: 190, cityList: [], nationList: [] }, - nations: [{ id: 1, name: '현재기수국', color: '#00FF00', level: 7, power: 1300, generalCount: 10, cities: ['낙양'] }], + nations: [ + { id: 1, name: '현재기수국', color: '#00FF00', level: 7, power: 1300, generalCount: 10, cities: ['낙양'] }, + ], globalHistory: ['저장된 현재 기수 과거 기록'], globalAction: ['저장된 현재 기수 과거 행동'], hash: 'current-archive', createdAt: new Date('2026-07-31T00:00:00.000Z'), }, + { + id: 4, + profileName: profile.id, + sourceId: 201, + year: 219, + month: 11, + map: { year: 219, month: 11, startYear: 190, cityList: [], nationList: [] }, + nations: [], + globalHistory: ['레거시 프로필 별칭 기록'], + globalAction: [], + hash: 'legacy-profile-alias', + createdAt: new Date('2026-07-30T00:00:00.000Z'), + }, ]; const authFor = (userId: string): GameSessionTokenPayload => ({ @@ -75,14 +90,21 @@ const authFor = (userId: string): GameSessionTokenPayload => ({ sanctions: {}, }); -const buildContext = (auth: GameSessionTokenPayload | null, options: { hasGeneral?: boolean } = {}): GameApiContext => { +const buildContext = ( + auth: GameSessionTokenPayload | null, + options: { hasGeneral?: boolean; worldMeta?: unknown } = {} +): GameApiContext => { const db = { general: { findFirst: async ({ where }: { where: { userId: string } }) => options.hasGeneral === false ? null : { id: where.userId === 'owner-a' ? 1 : 2, userId: where.userId }, }, worldState: { - findFirst: async () => ({ currentYear: 220, currentMonth: 1, meta: { serverId: currentServerId } }), + findFirst: async () => ({ + currentYear: 220, + currentMonth: 1, + meta: options.worldMeta ?? { serverId: currentServerId }, + }), }, yearbookHistory: { findFirst: async (args: { @@ -198,6 +220,16 @@ describe('historical yearbook access from dynasty', () => { }); }); + it('reads imported history under the short profile ID when world metadata has no server ID', async () => { + const caller = appRouter.createCaller(buildContext(authFor('owner-a'), { worldMeta: {} })); + + await expect(caller.yearbook.getRange()).resolves.toEqual({ + firstYearMonth: 219 * 12 + 10, + lastYearMonth: 219 * 12 + 10, + currentYearMonth: 220 * 12, + }); + }); + it('uses stored logs for a past month of the current generation', async () => { const caller = appRouter.createCaller(buildContext(authFor('owner-a'))); From 4113d81ba21f3c3c6922d5035c32ce35d6d1b268 Mon Sep 17 00:00:00 2001 From: hided62 Date: Tue, 4 Aug 2026 05:29:18 +0000 Subject: [PATCH 2/2] fix(frontend): converge scenario 2601 legacy UI --- .../src/components/chief/ChiefTurnCard.vue | 8 +- .../components/main/MainMobileBottomBar.vue | 6 +- .../src/components/main/MessagePanel.vue | 1 + .../tournament/TournamentBracket.vue | 38 +- app/game-frontend/src/views/AuctionView.vue | 57 ++- .../src/views/BattleCenterView.vue | 102 ++++- .../src/views/BattleSimulatorView.vue | 20 +- .../src/views/BestGeneralView.vue | 26 +- app/game-frontend/src/views/BettingView.vue | 92 ++++- .../src/views/ChiefCenterView.vue | 142 ++++++- .../src/views/CurrentCityView.vue | 47 ++- app/game-frontend/src/views/DiplomacyView.vue | 364 +++++++++++++----- .../src/views/GeneralListView.vue | 12 +- .../src/views/GlobalInfoView.vue | 71 +++- .../src/views/HallOfFameView.vue | 8 +- app/game-frontend/src/views/InheritView.vue | 22 +- app/game-frontend/src/views/MainView.vue | 46 ++- app/game-frontend/src/views/MyPageView.vue | 107 +++-- .../src/views/NationBettingView.vue | 4 +- .../src/views/NationCitiesView.vue | 230 +++++++++-- .../src/views/NationGeneralsView.vue | 243 +++++++++--- .../src/views/NationInfoView.vue | 10 +- .../src/views/NationListView.vue | 86 ++++- .../src/views/NationPersonnelView.vue | 39 +- .../src/views/NationSecretView.vue | 215 +++++++++-- .../src/views/NationStratFinanView.vue | 40 +- .../src/views/NpcControlView.vue | 12 + app/game-frontend/src/views/NpcListView.vue | 10 +- app/game-frontend/src/views/SurveyView.vue | 10 +- .../src/views/TournamentView.vue | 109 +++++- app/game-frontend/src/views/TrafficView.vue | 176 ++++++--- app/game-frontend/src/views/TroopView.vue | 18 +- app/game-frontend/src/views/YearbookView.vue | 65 +++- 33 files changed, 1944 insertions(+), 492 deletions(-) diff --git a/app/game-frontend/src/components/chief/ChiefTurnCard.vue b/app/game-frontend/src/components/chief/ChiefTurnCard.vue index cf72796..d0c63f9 100644 --- a/app/game-frontend/src/components/chief/ChiefTurnCard.vue +++ b/app/game-frontend/src/components/chief/ChiefTurnCard.vue @@ -26,6 +26,10 @@ const emit = defineEmits<{ }>(); const nameColor = computed(() => (props.npcState !== null ? getNpcColor(props.npcState) : undefined)); +const displayName = computed(() => { + const name = props.name ?? '-'; + return (props.npcState ?? 0) > 0 && !/^[ⓜⓝ]/u.test(name) ? `ⓝ${name}` : name; +}); const handleClick = () => { if (props.clickable) { @@ -45,7 +49,7 @@ const handleClick = () => { {{ props.name ?? '-' }}{{ displayName }} {{ props.officerLevelText }} {
{{ props.officerLevelText }} - {{ props.name ?? '-' }} + {{ displayName }}
ME diff --git a/app/game-frontend/src/components/main/MainMobileBottomBar.vue b/app/game-frontend/src/components/main/MainMobileBottomBar.vue index f49eea4..e607497 100644 --- a/app/game-frontend/src/components/main/MainMobileBottomBar.vue +++ b/app/game-frontend/src/components/main/MainMobileBottomBar.vue @@ -55,7 +55,7 @@ const onQuick = (item: QuickNavigationItem) => { 외부 메뉴 -