feat(game-ui): 세력일람에 총 병사를 표시

This commit is contained in:
2026-08-29 03:30:55 +00:00
parent 26952fcdc7
commit 20a2812d65
4 changed files with 26 additions and 1 deletions
@@ -96,6 +96,7 @@ export const getNationDirectory = authedProcedure.query(async ({ ctx }) => {
npcState: true, npcState: true,
nationId: true, nationId: true,
cityId: true, cityId: true,
crew: true,
dedication: true, dedication: true,
officerLevel: true, officerLevel: true,
meta: true, meta: true,
@@ -182,6 +183,7 @@ export const getNationDirectory = authedProcedure.query(async ({ ctx }) => {
capitalCityId: nation.capitalCityId ?? 0, capitalCityId: nation.capitalCityId ?? 0,
rulerCityName: ruler ? (cityNameById.get(ruler.cityId) ?? null) : null, rulerCityName: ruler ? (cityNameById.get(ruler.cityId) ?? null) : null,
generalCount: readMetaNumber(nation.meta, 'gennum', nationGenerals.length), generalCount: readMetaNumber(nation.meta, 'gennum', nationGenerals.length),
totalCrew: nationGenerals.reduce((sum, general) => sum + general.crew, 0),
cityCount: nationCities.length, cityCount: nationCities.length,
officers, officers,
ambassadorNames: secretPermissions ambassadorNames: secretPermissions
+3 -1
View File
@@ -112,6 +112,7 @@ const directoryGeneral = (overrides: Record<string, unknown> = {}) => ({
leadership: 70, leadership: 70,
strength: 60, strength: 60,
intel: 50, intel: 50,
crew: 100,
experience: 1_000, experience: 1_000,
meta: { killturn: 3 }, meta: { killturn: 3 },
penalty: {}, penalty: {},
@@ -303,6 +304,7 @@ describe('legacy global nation/general directories', () => {
expect(result[1]).toMatchObject({ expect(result[1]).toMatchObject({
id: 1, id: 1,
generalCount: 4, generalCount: 4,
totalCrew: 400,
cityCount: 1, cityCount: 1,
ambassadorNames: ['군주', '외교관'], ambassadorNames: ['군주', '외교관'],
auditorCount: 1, auditorCount: 1,
@@ -312,7 +314,7 @@ describe('legacy global nation/general directories', () => {
officerLevel: 12, officerLevel: 12,
general: { id: 10, name: '군주' }, general: { id: 10, name: '군주' },
}); });
expect(result[2]).toMatchObject({ name: '재 야', generalCount: 1, cityCount: 1 }); expect(result[2]).toMatchObject({ name: '재 야', generalCount: 1, totalCrew: 100, cityCount: 1 });
}); });
it('projects a level-zero nation location from its ruler city even when that city belongs to another nation', async () => { it('projects a level-zero nation location from its ruler city even when that city belongs to another nation', async () => {
@@ -19,6 +19,7 @@ const nationDirectory = [
power: 2000, power: 2000,
capitalCityId: 2, capitalCityId: 2,
generalCount: 1, generalCount: 1,
totalCrew: 1_200,
cityCount: 1, cityCount: 1,
officers: Array.from({ length: 8 }, (_, index) => ({ officers: Array.from({ length: 8 }, (_, index) => ({
officerLevel: 12 - index, officerLevel: 12 - index,
@@ -38,6 +39,7 @@ const nationDirectory = [
power: 1000, power: 1000,
capitalCityId: 1, capitalCityId: 1,
generalCount: 2, generalCount: 2,
totalCrew: 2_500,
cityCount: 1, cityCount: 1,
officers: Array.from({ length: 8 }, (_, index) => ({ officers: Array.from({ length: 8 }, (_, index) => ({
officerLevel: 12 - index, officerLevel: 12 - index,
@@ -60,6 +62,7 @@ const nationDirectory = [
power: 0, power: 0,
capitalCityId: 0, capitalCityId: 0,
generalCount: 1, generalCount: 1,
totalCrew: 0,
cityCount: 1, cityCount: 1,
officers: Array.from({ length: 8 }, (_, index) => ({ officerLevel: 12 - index, general: null })), officers: Array.from({ length: 8 }, (_, index) => ({ officerLevel: 12 - index, general: null })),
ambassadorNames: [], ambassadorNames: [],
@@ -697,6 +700,8 @@ test('nation directory reuses only the public general-directory row on hover and
await page.waitForLoadState('networkidle'); await page.waitForLoadState('networkidle');
await expect(page.getByRole('button', { name: '장수 일람 연동' })).toHaveCount(0); await expect(page.getByRole('button', { name: '장수 일람 연동' })).toHaveCount(0);
await expect(page.locator('[data-general-preview-trigger]')).toHaveCount(4); await expect(page.locator('[data-general-preview-trigger]')).toHaveCount(4);
await expect(page.locator('[data-nation-id="1"]')).toContainText('총 병사');
await expect(page.locator('[data-nation-id="1"]')).toContainText('2,500');
expect(requestedOperations.filter((operation) => operation === 'world.getGeneralDirectory')).toHaveLength( expect(requestedOperations.filter((operation) => operation === 'world.getGeneralDirectory')).toHaveLength(
viewport.name === 'desktop' ? 0 : 1 viewport.name === 'desktop' ? 0 : 1
); );
@@ -203,6 +203,16 @@ onBeforeUnmount(() => {
<td class="label-cell">장수 / 속령</td> <td class="label-cell">장수 / 속령</td>
<td class="value-wide">{{ nation.generalCount }} / {{ nation.cityCount }}</td> <td class="value-wide">{{ nation.generalCount }} / {{ nation.cityCount }}</td>
</tr> </tr>
<tr class="desktop-only">
<td class="label-cell"> 병사</td>
<td class="value-wide">{{ nation.totalCrew.toLocaleString('ko-KR') }}</td>
<td colspan="6"></td>
</tr>
<tr class="mobile-only">
<td class="label-cell"> 병사</td>
<td class="value-wide">{{ nation.totalCrew.toLocaleString('ko-KR') }}</td>
<td colspan="2"></td>
</tr>
<tr v-for="row in 2" :key="`desktop-officers-${row}`" class="desktop-only"> <tr v-for="row in 2" :key="`desktop-officers-${row}`" class="desktop-only">
<template v-for="column in 4" :key="column"> <template v-for="column in 4" :key="column">
<td class="label-cell"> <td class="label-cell">
@@ -316,6 +326,12 @@ onBeforeUnmount(() => {
<td class="neutral-label"> </td> <td class="neutral-label"> </td>
<td class="neutral-value">{{ nation.cityCount }}</td> <td class="neutral-value">{{ nation.cityCount }}</td>
</tr> </tr>
<tr>
<td class="neutral-spacer">&nbsp;</td>
<td class="neutral-label"> 병사</td>
<td class="neutral-value">{{ nation.totalCrew.toLocaleString('ko-KR') }}</td>
<td colspan="2"></td>
</tr>
<tr> <tr>
<td colspan="5"> <td colspan="5">
속령 일람 : 속령 일람 :