From e094246963561fbf8e6592bb4e381fbc1144c963 Mon Sep 17 00:00:00 2001 From: hided62 Date: Tue, 15 Sep 2026 15:45:16 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=EA=B2=80=EC=A6=9D=EB=90=9C=20=EC=9D=B8?= =?UTF-8?q?=EA=B2=8C=EC=9E=84=20=EA=B8=80=EC=9E=90=20=ED=81=AC=EA=B8=B0?= =?UTF-8?q?=EB=A5=BC=20=EB=84=A4=20=EB=8B=A8=EA=B3=84=EB=A1=9C=20=ED=86=B5?= =?UTF-8?q?=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../e2e/fixtures/typography.json | 2914 +++++++++++++++++ app/game-frontend/e2e/playwright.config.mjs | 1 + .../e2e/typographyPolicy.spec.ts | 194 ++ app/game-frontend/src/assets/main.css | 2 +- .../src/assets/styles/game-shell.css | 8 +- .../src/assets/styles/legacy-controls.css | 2 +- .../src/assets/styles/ref-shell.css | 4 +- .../src/assets/styles/tokens.css | 5 + .../components/battle/BattleGeneralCard.vue | 8 +- .../src/components/chief/ChiefTurnCard.vue | 8 +- .../command/RecruitmentCommandForm.vue | 21 +- .../command/ReservedCommandEditor.vue | 13 +- .../components/directory/DirectoryTooltip.vue | 4 +- .../directory/GeneralDirectoryTable.vue | 15 +- .../components/main/CommandArgumentForm.vue | 13 +- .../src/components/main/CommandSelectForm.vue | 13 +- .../src/components/main/GeneralBasicCard.vue | 4 +- .../components/main/GeneralRecordPanels.vue | 2 +- .../src/components/main/MainAutorunStatus.vue | 2 +- .../src/components/main/MainFrontStatus.vue | 4 +- .../components/main/MainMobileBottomBar.vue | 12 +- .../components/main/MainNavigationLink.vue | 2 +- .../src/components/main/MainTurnControls.vue | 2 +- .../src/components/main/MapCityBasic.vue | 2 +- .../src/components/main/MapCityDetail.vue | 4 +- .../src/components/main/MapViewer.vue | 12 +- .../src/components/main/MessagePanel.vue | 4 +- .../src/components/main/MessagePlate.vue | 8 +- .../src/components/main/RecentLogList.vue | 2 +- .../src/components/main/RecordPanel.vue | 2 +- .../src/components/main/SelectedCityPanel.vue | 4 +- .../personnel/PermissionMultiSelect.vue | 4 +- .../personnel/PersonnelSelectionDialog.vue | 21 +- .../tournament/TournamentBracket.vue | 10 +- .../tournament/TournamentGroupCard.vue | 6 +- .../tournament/TournamentPageHeader.vue | 6 +- .../src/components/ui/GameFeedbackLayer.vue | 6 +- .../ui/GameServerConnectionNotice.vue | 2 +- .../components/ui/LegacyGeneralProgress.vue | 2 +- .../src/components/ui/LegacyHtmlEditor.vue | 4 +- .../src/components/ui/PanelCard.vue | 6 +- .../src/components/ui/RichTooltip.vue | 4 +- app/game-frontend/src/views/AuctionView.vue | 4 +- .../src/views/BattleCenterView.vue | 4 +- .../src/views/BattleSimulatorView.vue | 22 +- .../src/views/BestGeneralView.vue | 13 +- app/game-frontend/src/views/BettingView.vue | 13 +- app/game-frontend/src/views/BoardView.vue | 8 +- .../src/views/ChiefCenterView.vue | 26 +- .../src/views/CurrentCityView.vue | 10 +- app/game-frontend/src/views/DiplomacyView.vue | 8 +- .../src/views/DynastyDetailView.vue | 8 +- .../src/views/DynastyListView.vue | 8 +- .../src/views/GeneralListView.vue | 11 +- .../src/views/GlobalInfoView.vue | 6 +- .../src/views/HallOfFameView.vue | 13 +- app/game-frontend/src/views/InheritView.vue | 11 +- app/game-frontend/src/views/JoinView.vue | 67 +- app/game-frontend/src/views/LoginView.vue | 2 +- app/game-frontend/src/views/MainView.vue | 18 +- app/game-frontend/src/views/MyPageView.vue | 6 +- .../src/views/MySettingsView.vue | 19 +- .../src/views/NationBettingView.vue | 4 +- .../src/views/NationCitiesView.vue | 4 +- .../src/views/NationGeneralsView.vue | 23 +- .../src/views/NationInfoView.vue | 2 +- .../src/views/NationListView.vue | 11 +- .../src/views/NationPersonnelView.vue | 51 +- .../src/views/NationSecretView.vue | 18 +- .../src/views/NationStratFinanView.vue | 4 +- .../src/views/NpcControlView.vue | 23 +- app/game-frontend/src/views/NpcListView.vue | 6 +- app/game-frontend/src/views/PastPlaysView.vue | 8 +- app/game-frontend/src/views/PublicView.vue | 14 +- app/game-frontend/src/views/SurveyView.vue | 6 +- .../src/views/TournamentView.vue | 29 +- app/game-frontend/src/views/TrafficView.vue | 6 +- app/game-frontend/src/views/TroopView.vue | 8 +- app/game-frontend/src/views/YearbookView.vue | 6 +- docs/frontend-css-architecture.md | 62 + 80 files changed, 3595 insertions(+), 329 deletions(-) create mode 100644 app/game-frontend/e2e/fixtures/typography.json create mode 100644 app/game-frontend/e2e/typographyPolicy.spec.ts diff --git a/app/game-frontend/e2e/fixtures/typography.json b/app/game-frontend/e2e/fixtures/typography.json new file mode 100644 index 00000000..9e55a1ca --- /dev/null +++ b/app/game-frontend/e2e/fixtures/typography.json @@ -0,0 +1,2914 @@ +{ + "main": { + "lobby.info": { + "result": { + "data": { + "myGeneral": { + "id": 7, + "name": "가나다라마바사아자" + }, + "serverId": "che_fixture_season", + "profile": "che", + "gameIdx": 101, + "year": 185, + "month": 1, + "turnTerm": 10, + "serverTime": "2026-08-13T00:00:00.000Z", + "serverWallTime": "2026-08-13T00:00:00.000Z", + "clockMode": "realtime", + "clockRunning": true, + "clockStartsAt": null, + "clockRecovery": null, + "turnEngineRunning": true, + "scenarioTitle": "", + "autorunUser": null + } + } + }, + "auth.status": { + "result": { + "data": { + "ok": true + } + } + }, + "dashboard.getContextBundleDelta": { + "result": { + "data": { + "context": { + "kind": "snapshot", + "revision": "AAAAAAAAAAAAAAAAAAAAAA", + "data": { + "general": { + "id": 7, + "name": "가나다라마바사아자", + "nationId": 1, + "cityId": 1, + "troopId": 7, + "npcState": 0, + "officerLevel": 12, + "officerLevelText": "군주", + "officerCityName": null, + "generalType": "용장", + "leadershipBonus": 6, + "picture": null, + "imageServer": 0, + "stats": { + "leadership": 70, + "strength": 60, + "intelligence": 50 + }, + "gold": 1000, + "rice": 2000, + "crew": 300, + "train": 80, + "atmos": 90, + "injury": 0, + "experience": 100, + "dedication": 200, + "age": 25, + "retirementYear": 70, + "defenceTrain": 80, + "killTurn": 5, + "remainingMinutes": 7, + "troop": { + "name": "백마대", + "status": "present", + "leaderCityName": "업" + }, + "refreshScore": { + "current": 3, + "total": 120, + "text": "보통" + }, + "progression": { + "experienceLevel": 1, + "dedicationLevel": 2, + "statExperience": { + "leadership": 5, + "strength": 10, + "intelligence": 15 + }, + "statUpgradeLimit": 20, + "dex": [350, 100000, 500000, 1000000, 1275975] + }, + "items": { + "horse": null, + "weapon": null, + "book": null, + "item": null + }, + "itemNames": { + "horse": "적토마", + "weapon": "청룡언월도", + "book": "육도", + "item": "옥벽" + }, + "crewTypeId": 1100, + "crewTypeName": "보병", + "traits": { + "personal": "대담", + "specialDomestic": "상재", + "specialWar": "무쌍" + }, + "turnTime": "0185-01-01T00:00:00.000Z", + "nextTurnMonthOffset": 0 + }, + "city": { + "id": 1, + "name": "업", + "level": 8, + "levelName": "특", + "nationId": 1, + "nationName": "대한민국천하통일국", + "nationColor": "#008000", + "region": 1, + "regionName": "중원", + "population": 100000, + "populationMax": 200000, + "agriculture": 1000, + "agricultureMax": 2000, + "commerce": 1000, + "commerceMax": 2000, + "security": 1000, + "securityMax": 2000, + "trust": 80, + "trade": 100, + "defence": 1000, + "defenceMax": 2000, + "wall": 1000, + "wallMax": 2000, + "supplyState": 1, + "frontState": 0, + "state": 0, + "officers": { + "2": { + "id": 10, + "name": "가나다라마바사아자", + "npcState": 6 + }, + "3": { + "id": 9, + "name": "가나다라마바사아자", + "npcState": 2 + }, + "4": { + "id": 8, + "name": "가나다라마바사아자", + "npcState": 0 + } + } + }, + "nation": { + "id": 1, + "name": "가나다라마바사아자", + "color": "#008000", + "level": 3, + "gold": 10000, + "rice": 20000, + "tech": 100, + "rate": 20, + "bill": 100, + "capitalCityId": 1, + "typeCode": "che_유가", + "typeName": "유가", + "typePros": "농상↑ 민심↑", + "typeCons": "쌀수입↓", + "population": { + "cityCount": 2, + "current": 150000, + "max": 620500 + }, + "crew": { + "generalCount": 2, + "current": 500, + "max": 7000 + }, + "power": 1234, + "taxRate": 20, + "strategicCommandLimit": 2, + "diplomaticLimit": 0, + "prohibitScout": false, + "prohibitWar": true, + "techLevel": 0, + "techLimited": false, + "topChiefs": { + "11": { + "id": 2, + "name": "가나다라마바사아자", + "npcState": 1 + }, + "12": { + "id": 1, + "name": "가나다라마바사아자", + "npcState": 0 + } + }, + "impossibleStrategicCommands": [ + { + "name": "수몰", + "remainingTurns": 2, + "availableYear": 190, + "availableMonth": 5 + } + ] + }, + "settings": {}, + "penalties": {} + }, + "sourceRevision": "AAAAAAAAAAAAAAAAAAAAAA" + }, + "commandTable": { + "kind": "snapshot", + "revision": "CCCCCCCCCCCCCCCCCCCCCC", + "sourceRevision": "CCCCCCCCCCCCCCCCCCCCCC", + "data": { + "general": [], + "nation": [], + "inputOptions": { + "cities": [ + { + "value": 1, + "label": "도시 1" + }, + { + "value": 2, + "label": "도시 2" + }, + { + "value": 3, + "label": "도시 3" + }, + { + "value": 4, + "label": "도시 4" + }, + { + "value": 5, + "label": "도시 5" + }, + { + "value": 6, + "label": "도시 6" + }, + { + "value": 7, + "label": "도시 7" + }, + { + "value": 8, + "label": "도시 8" + }, + { + "value": 9, + "label": "도시 9" + }, + { + "value": 10, + "label": "도시 10" + }, + { + "value": 11, + "label": "도시 11" + }, + { + "value": 12, + "label": "도시 12" + }, + { + "value": 13, + "label": "도시 13" + }, + { + "value": 14, + "label": "도시 14" + }, + { + "value": 15, + "label": "도시 15" + }, + { + "value": 16, + "label": "도시 16" + }, + { + "value": 17, + "label": "도시 17" + }, + { + "value": 18, + "label": "도시 18" + }, + { + "value": 19, + "label": "도시 19" + }, + { + "value": 20, + "label": "도시 20" + } + ], + "nations": [], + "generals": [], + "crewTypes": [], + "armTypes": [], + "nationTypes": [], + "colors": [], + "items": {} + } + } + }, + "boardAccess": { + "kind": "snapshot", + "revision": "DDDDDDDDDDDDDDDDDDDDDD", + "sourceRevision": "DDDDDDDDDDDDDDDDDDDDDD", + "data": { + "permission": 4, + "canMeeting": true, + "canSecret": true + } + } + } + } + }, + "world.getState": { + "result": { + "data": { + "currentYear": 185, + "currentMonth": 1, + "tickSeconds": 600, + "config": { + "npcMode": 1, + "const": {}, + "environment": {} + }, + "meta": {} + } + } + }, + "world.getMapLayout": { + "result": { + "data": { + "mapName": "che", + "cityList": [ + { + "id": 1, + "name": "업", + "level": 8, + "region": 1, + "x": 200, + "y": 120, + "path": [] + } + ], + "regionMap": { + "1": "하북" + }, + "levelMap": { + "8": "특" + } + } + } + }, + "turns.reserved.getGeneral": { + "result": { + "data": { + "turns": [], + "revision": 0, + "autorunLimit": null + } + } + }, + "general.getRecentRecords": { + "result": { + "data": { + "global": [ + { + "id": 3, + "text": "장수 동향 기록" + } + ], + "general": [ + { + "id": 2, + "text": "개인 기록" + } + ], + "history": [ + { + "id": 1, + "text": "중원 정세 기록" + } + ] + } + } + }, + "general.getFrontStatus": { + "result": { + "data": { + "serverId": "che_fixture_season", + "onlineUserCount": 1, + "onlineNations": "위(1)", + "onlineGenerals": "메뉴검증장수", + "nationNotice": "

국가 방침

", + "lastExecuted": "2026-08-13T00:05:06.000Z", + "latestVote": { + "id": 9, + "title": "메뉴 설문", + "hasVoted": false + } + } + } + }, + "tournament.getState": { + "result": { + "data": { + "stage": 5, + "type": 0 + } + } + }, + "world.getMap": { + "result": { + "data": { + "result": true, + "version": 0, + "startYear": 180, + "year": 185, + "month": 1, + "cityList": [[1, 8, 0, 1, 1, 1]], + "nationList": [[1, "대한민국천하통일국", "#008000", 1]], + "spyList": {}, + "shownByGeneralList": [], + "myCity": 1, + "myNation": 1 + } + } + }, + "messages.getRecent": { + "result": { + "data": { + "private": [], + "national": [], + "public": [], + "diplomacy": [], + "permission": 4, + "sequence": -1, + "hasMore": { + "private": false, + "national": false, + "public": false, + "diplomacy": false + }, + "latestRead": { + "private": 0, + "national": 0, + "public": 0, + "diplomacy": 0 + }, + "canRespondDiplomacy": false + } + } + }, + "messages.getContacts": { + "result": { + "data": { + "nation": [] + } + } + } + }, + "chief": { + "lobby.info": { + "result": { + "data": { + "myGeneral": { + "id": 1, + "name": "가나다라마바사아자" + }, + "year": 200, + "month": 1, + "turnTerm": 10, + "userCnt": 1, + "maxUserCnt": 100, + "npcCnt": 0, + "nationCnt": 2 + } + } + }, + "auth.status": { + "result": { + "data": { + "ok": true + } + } + }, + "nation.getChiefCenter": { + "result": { + "data": { + "me": { + "id": 1, + "officerLevel": 5, + "nationId": 1 + }, + "nation": { + "id": 1, + "name": "대한민국천하통일국", + "level": 1 + }, + "currentYear": 200, + "currentMonth": 1, + "turnTermMinutes": 10, + "maxTurns": 12, + "chiefs": [ + { + "officerLevel": 12, + "name": "가나다라마바사아자", + "npcState": 0, + "turnTime": "2026-08-30T01:46:47.590Z", + "revision": 0, + "turns": [ + { + "index": 0, + "action": "che_포상", + "args": { + "destGeneralId": 2, + "isGold": false, + "amount": 300 + } + }, + { + "index": 1, + "action": "휴식", + "args": {} + }, + { + "index": 2, + "action": "휴식", + "args": {} + }, + { + "index": 3, + "action": "휴식", + "args": {} + }, + { + "index": 4, + "action": "휴식", + "args": {} + }, + { + "index": 5, + "action": "휴식", + "args": {} + }, + { + "index": 6, + "action": "휴식", + "args": {} + }, + { + "index": 7, + "action": "휴식", + "args": {} + }, + { + "index": 8, + "action": "휴식", + "args": {} + }, + { + "index": 9, + "action": "휴식", + "args": {} + }, + { + "index": 10, + "action": "휴식", + "args": {} + }, + { + "index": 11, + "action": "휴식", + "args": {} + } + ] + }, + { + "officerLevel": 10, + "name": "가나다라마바사아자", + "npcState": 0, + "turnTime": "2026-08-30T01:46:47.590Z", + "revision": 0, + "turns": [ + { + "index": 0, + "action": "휴식", + "args": {} + }, + { + "index": 1, + "action": "휴식", + "args": {} + }, + { + "index": 2, + "action": "휴식", + "args": {} + }, + { + "index": 3, + "action": "휴식", + "args": {} + }, + { + "index": 4, + "action": "휴식", + "args": {} + }, + { + "index": 5, + "action": "휴식", + "args": {} + }, + { + "index": 6, + "action": "휴식", + "args": {} + }, + { + "index": 7, + "action": "휴식", + "args": {} + }, + { + "index": 8, + "action": "휴식", + "args": {} + }, + { + "index": 9, + "action": "휴식", + "args": {} + }, + { + "index": 10, + "action": "휴식", + "args": {} + }, + { + "index": 11, + "action": "휴식", + "args": {} + } + ] + }, + { + "officerLevel": 8, + "name": "가나다라마바사아자", + "npcState": 2, + "turnTime": "2026-08-30T01:46:47.590Z", + "revision": 0, + "turns": [ + { + "index": 0, + "action": "휴식", + "args": {} + }, + { + "index": 1, + "action": "휴식", + "args": {} + }, + { + "index": 2, + "action": "휴식", + "args": {} + }, + { + "index": 3, + "action": "휴식", + "args": {} + }, + { + "index": 4, + "action": "휴식", + "args": {} + }, + { + "index": 5, + "action": "휴식", + "args": {} + }, + { + "index": 6, + "action": "휴식", + "args": {} + }, + { + "index": 7, + "action": "휴식", + "args": {} + }, + { + "index": 8, + "action": "휴식", + "args": {} + }, + { + "index": 9, + "action": "휴식", + "args": {} + }, + { + "index": 10, + "action": "휴식", + "args": {} + }, + { + "index": 11, + "action": "휴식", + "args": {} + } + ] + }, + { + "officerLevel": 6, + "name": "가나다라마바사아자", + "npcState": 0, + "turnTime": "2026-08-30T01:46:47.590Z", + "revision": 0, + "turns": [ + { + "index": 0, + "action": "휴식", + "args": {} + }, + { + "index": 1, + "action": "휴식", + "args": {} + }, + { + "index": 2, + "action": "휴식", + "args": {} + }, + { + "index": 3, + "action": "휴식", + "args": {} + }, + { + "index": 4, + "action": "휴식", + "args": {} + }, + { + "index": 5, + "action": "휴식", + "args": {} + }, + { + "index": 6, + "action": "휴식", + "args": {} + }, + { + "index": 7, + "action": "휴식", + "args": {} + }, + { + "index": 8, + "action": "휴식", + "args": {} + }, + { + "index": 9, + "action": "휴식", + "args": {} + }, + { + "index": 10, + "action": "휴식", + "args": {} + }, + { + "index": 11, + "action": "휴식", + "args": {} + } + ] + }, + { + "officerLevel": 11, + "name": "가나다라마바사아자", + "npcState": 0, + "turnTime": "2026-08-30T01:46:47.590Z", + "revision": 0, + "turns": [ + { + "index": 0, + "action": "휴식", + "args": {} + }, + { + "index": 1, + "action": "휴식", + "args": {} + }, + { + "index": 2, + "action": "휴식", + "args": {} + }, + { + "index": 3, + "action": "휴식", + "args": {} + }, + { + "index": 4, + "action": "휴식", + "args": {} + }, + { + "index": 5, + "action": "휴식", + "args": {} + }, + { + "index": 6, + "action": "휴식", + "args": {} + }, + { + "index": 7, + "action": "휴식", + "args": {} + }, + { + "index": 8, + "action": "휴식", + "args": {} + }, + { + "index": 9, + "action": "휴식", + "args": {} + }, + { + "index": 10, + "action": "휴식", + "args": {} + }, + { + "index": 11, + "action": "휴식", + "args": {} + } + ] + }, + { + "officerLevel": 9, + "name": "가나다라마바사아자", + "npcState": 0, + "turnTime": "2026-08-30T01:46:47.590Z", + "revision": 0, + "turns": [ + { + "index": 0, + "action": "휴식", + "args": {} + }, + { + "index": 1, + "action": "휴식", + "args": {} + }, + { + "index": 2, + "action": "휴식", + "args": {} + }, + { + "index": 3, + "action": "휴식", + "args": {} + }, + { + "index": 4, + "action": "휴식", + "args": {} + }, + { + "index": 5, + "action": "휴식", + "args": {} + }, + { + "index": 6, + "action": "휴식", + "args": {} + }, + { + "index": 7, + "action": "휴식", + "args": {} + }, + { + "index": 8, + "action": "휴식", + "args": {} + }, + { + "index": 9, + "action": "휴식", + "args": {} + }, + { + "index": 10, + "action": "휴식", + "args": {} + }, + { + "index": 11, + "action": "휴식", + "args": {} + } + ] + }, + { + "officerLevel": 7, + "name": "가나다라마바사아자", + "npcState": 0, + "turnTime": "2026-08-30T01:46:47.590Z", + "revision": 0, + "turns": [ + { + "index": 0, + "action": "휴식", + "args": {} + }, + { + "index": 1, + "action": "휴식", + "args": {} + }, + { + "index": 2, + "action": "휴식", + "args": {} + }, + { + "index": 3, + "action": "휴식", + "args": {} + }, + { + "index": 4, + "action": "휴식", + "args": {} + }, + { + "index": 5, + "action": "휴식", + "args": {} + }, + { + "index": 6, + "action": "휴식", + "args": {} + }, + { + "index": 7, + "action": "휴식", + "args": {} + }, + { + "index": 8, + "action": "휴식", + "args": {} + }, + { + "index": 9, + "action": "휴식", + "args": {} + }, + { + "index": 10, + "action": "휴식", + "args": {} + }, + { + "index": 11, + "action": "휴식", + "args": {} + } + ] + }, + { + "officerLevel": 5, + "name": "가나다라마바사아자", + "npcState": 0, + "turnTime": "2026-08-30T01:46:47.590Z", + "revision": 0, + "turns": [ + { + "index": 0, + "action": "휴식", + "args": {} + }, + { + "index": 1, + "action": "휴식", + "args": {} + }, + { + "index": 2, + "action": "휴식", + "args": {} + }, + { + "index": 3, + "action": "휴식", + "args": {} + }, + { + "index": 4, + "action": "휴식", + "args": {} + }, + { + "index": 5, + "action": "휴식", + "args": {} + }, + { + "index": 6, + "action": "휴식", + "args": {} + }, + { + "index": 7, + "action": "휴식", + "args": {} + }, + { + "index": 8, + "action": "휴식", + "args": {} + }, + { + "index": 9, + "action": "휴식", + "args": {} + }, + { + "index": 10, + "action": "휴식", + "args": {} + }, + { + "index": 11, + "action": "휴식", + "args": {} + } + ] + } + ] + } + } + }, + "turns.getCommandTable": { + "result": { + "data": { + "general": [ + { + "category": "계략", + "values": [ + { + "key": "che_화계", + "name": "화계", + "reqArg": true, + "possible": false, + "status": "blocked", + "reason": "현재 조건에서는 실행할 수 없습니다.", + "inputFields": [ + { + "key": "destCityId", + "label": "대상 도시", + "kind": "select", + "required": true, + "optionSource": "cities" + } + ] + }, + { + "key": "che_선동", + "name": "선동", + "reqArg": true, + "possible": true, + "status": "needsInput", + "inputFields": [ + { + "key": "destCityId", + "label": "대상 도시", + "kind": "select", + "required": true, + "optionSource": "cities" + } + ] + }, + { + "key": "che_탈취", + "name": "탈취", + "reqArg": true, + "possible": true, + "status": "needsInput", + "inputFields": [ + { + "key": "destCityId", + "label": "대상 도시", + "kind": "select", + "required": true, + "optionSource": "cities" + } + ] + }, + { + "key": "che_파괴", + "name": "파괴", + "reqArg": true, + "possible": true, + "status": "needsInput", + "inputFields": [ + { + "key": "destCityId", + "label": "대상 도시", + "kind": "select", + "required": true, + "optionSource": "cities" + } + ] + } + ] + }, + { + "category": "내정", + "values": [ + { + "key": "che_징병", + "name": "징병", + "reqArg": true, + "possible": true, + "status": "needsInput", + "inputFields": [ + { + "key": "crewType", + "label": "병종", + "kind": "select", + "required": true, + "optionSource": "crewTypes" + }, + { + "key": "amount", + "label": "수량", + "kind": "number", + "required": true, + "min": 0, + "step": 1 + } + ] + }, + { + "key": "che_모병", + "name": "모병", + "reqArg": true, + "possible": true, + "status": "needsInput", + "inputFields": [ + { + "key": "crewType", + "label": "병종", + "kind": "select", + "required": true, + "optionSource": "crewTypes" + }, + { + "key": "amount", + "label": "수량", + "kind": "number", + "required": true, + "min": 0, + "step": 1 + } + ] + } + ] + }, + { + "category": "군사", + "values": [ + { + "key": "che_출병", + "name": "출병", + "reqArg": true, + "possible": true, + "status": "needsInput", + "inputFields": [ + { + "key": "destCityId", + "label": "대상 도시", + "kind": "select", + "required": true, + "optionSource": "cities" + } + ] + }, + { + "key": "che_첩보", + "name": "첩보", + "reqArg": true, + "possible": true, + "status": "needsInput", + "inputFields": [ + { + "key": "destCityId", + "label": "대상 도시", + "kind": "select", + "required": true, + "optionSource": "cities" + } + ] + } + ] + } + ], + "nation": [ + { + "category": "인사", + "values": [ + { + "key": "che_발령", + "name": "발령", + "reqArg": true, + "possible": true, + "status": "needsInput", + "inputFields": [ + { + "key": "destGeneralId", + "label": "대상 장수", + "kind": "select", + "required": true, + "optionSource": "generals" + }, + { + "key": "destCityId", + "label": "대상 도시", + "kind": "select", + "required": true, + "optionSource": "cities" + } + ] + }, + { + "key": "che_포상", + "name": "포상", + "reqArg": true, + "possible": true, + "status": "needsInput", + "inputFields": [ + { + "key": "isGold", + "label": "물자", + "kind": "boolean", + "required": true + }, + { + "key": "amount", + "label": "수량", + "kind": "number", + "required": true, + "min": 0, + "step": 1 + }, + { + "key": "destGeneralId", + "label": "대상 장수", + "kind": "select", + "required": true, + "optionSource": "generals" + } + ] + }, + { + "key": "che_몰수", + "name": "몰수", + "reqArg": true, + "possible": true, + "status": "needsInput", + "inputFields": [ + { + "key": "isGold", + "label": "물자", + "kind": "boolean", + "required": true + }, + { + "key": "amount", + "label": "수량", + "kind": "number", + "required": true, + "min": 0, + "step": 1 + }, + { + "key": "destGeneralId", + "label": "대상 장수", + "kind": "select", + "required": true, + "optionSource": "generals" + } + ] + }, + { + "key": "che_부대탈퇴지시", + "name": "부대 탈퇴 지시", + "reqArg": true, + "possible": true, + "status": "needsInput", + "inputFields": [ + { + "key": "destGeneralId", + "label": "대상 장수", + "kind": "select", + "required": true, + "optionSource": "generals" + } + ] + } + ] + }, + { + "category": "외교", + "values": [ + { + "key": "che_물자원조", + "name": "원조", + "reqArg": true, + "possible": true, + "status": "needsInput", + "inputFields": [ + { + "key": "destNationId", + "label": "대상 국가", + "kind": "select", + "required": true, + "optionSource": "nations" + }, + { + "key": "amountList", + "label": "지원 물자", + "kind": "numberTuple", + "required": true, + "min": 0, + "step": 1, + "tupleLabels": ["금", "쌀"] + } + ] + }, + { + "key": "che_불가침제의", + "name": "불가침 제의", + "reqArg": true, + "possible": true, + "status": "needsInput", + "inputFields": [ + { + "key": "destNationId", + "label": "대상 국가", + "kind": "select", + "required": true, + "optionSource": "nations" + }, + { + "key": "year", + "label": "기간(년)", + "kind": "number", + "required": true, + "min": 191, + "max": 210, + "step": 1, + "defaultValue": 191 + }, + { + "key": "month", + "label": "기간(월)", + "kind": "number", + "required": true, + "min": 1, + "max": 12, + "step": 1, + "defaultValue": 1 + } + ] + }, + { + "key": "che_선전포고", + "name": "선전포고", + "reqArg": true, + "possible": true, + "status": "needsInput", + "inputFields": [ + { + "key": "destNationId", + "label": "대상 국가", + "kind": "select", + "required": true, + "optionSource": "nations" + } + ] + }, + { + "key": "che_종전제의", + "name": "종전 제의", + "reqArg": true, + "possible": true, + "status": "needsInput", + "inputFields": [ + { + "key": "destNationId", + "label": "대상 국가", + "kind": "select", + "required": true, + "optionSource": "nations" + } + ] + }, + { + "key": "che_불가침파기제의", + "name": "불가침 파기 제의", + "reqArg": true, + "possible": true, + "status": "needsInput", + "inputFields": [ + { + "key": "destNationId", + "label": "대상 국가", + "kind": "select", + "required": true, + "optionSource": "nations" + } + ] + } + ] + }, + { + "category": "특수", + "values": [ + { + "key": "che_초토화", + "name": "초토화", + "reqArg": true, + "possible": true, + "status": "needsInput", + "inputFields": [ + { + "key": "destCityId", + "label": "대상 도시", + "kind": "select", + "required": true, + "optionSource": "cities" + } + ] + }, + { + "key": "che_천도", + "name": "천도", + "reqArg": true, + "possible": true, + "status": "needsInput", + "inputFields": [ + { + "key": "destCityId", + "label": "대상 도시", + "kind": "select", + "required": true, + "optionSource": "cities" + } + ] + }, + { + "key": "che_증축", + "name": "증축", + "reqArg": false, + "possible": true, + "status": "available", + "inputFields": [] + }, + { + "key": "che_감축", + "name": "감축", + "reqArg": false, + "possible": true, + "status": "available", + "inputFields": [] + } + ] + }, + { + "category": "전략", + "values": [ + { + "key": "che_백성동원", + "name": "백성동원", + "reqArg": true, + "possible": true, + "status": "needsInput", + "inputFields": [ + { + "key": "destCityId", + "label": "대상 도시", + "kind": "select", + "required": true, + "optionSource": "cities" + } + ] + }, + { + "key": "che_수몰", + "name": "수몰", + "reqArg": true, + "possible": true, + "status": "needsInput", + "inputFields": [ + { + "key": "destCityId", + "label": "대상 도시", + "kind": "select", + "required": true, + "optionSource": "cities" + } + ] + }, + { + "key": "che_허보", + "name": "허보", + "reqArg": true, + "possible": true, + "status": "needsInput", + "inputFields": [ + { + "key": "destCityId", + "label": "대상 도시", + "kind": "select", + "required": true, + "optionSource": "cities" + } + ] + }, + { + "key": "che_이호경식", + "name": "이호경식", + "reqArg": true, + "possible": true, + "status": "needsInput", + "inputFields": [ + { + "key": "destNationId", + "label": "대상 국가", + "kind": "select", + "required": true, + "optionSource": "nations" + } + ] + }, + { + "key": "che_급습", + "name": "급습", + "reqArg": true, + "possible": true, + "status": "needsInput", + "inputFields": [ + { + "key": "destNationId", + "label": "대상 국가", + "kind": "select", + "required": true, + "optionSource": "nations" + } + ] + }, + { + "key": "che_피장파장", + "name": "피장파장", + "reqArg": true, + "possible": true, + "status": "needsInput", + "inputFields": [ + { + "key": "destNationId", + "label": "대상 국가", + "kind": "select", + "required": true, + "optionSource": "nations" + }, + { + "key": "commandType", + "label": "대응 명령", + "kind": "select", + "required": true, + "options": [ + { + "value": "che_수몰", + "label": "수몰" + } + ] + } + ] + } + ] + } + ], + "inputOptions": { + "cities": [ + { + "value": 1, + "label": "업 (아국)" + }, + { + "value": 2, + "label": "허창 (적국)", + "description": "적국 · 예주 · 대도시" + }, + { + "value": 3, + "label": "단양 (오)" + } + ], + "nations": [ + { + "value": 1, + "label": "아국", + "color": "#008000" + }, + { + "value": 2, + "label": "적국", + "color": "#800000", + "description": "수도 허창" + }, + { + "value": 3, + "label": "불가침국", + "color": "#000080", + "description": "수도 단양" + } + ], + "nationTargets": { + "che_물자원조": [ + { + "value": 2, + "label": "적국", + "color": "#800000", + "availableNow": true, + "description": "현재 원조 대상 · 교역" + }, + { + "value": 3, + "label": "불가침국", + "color": "#000080", + "availableNow": true, + "description": "현재 원조 대상 · 불가침 12턴" + }, + { + "value": 1, + "label": "아국", + "color": "#008000", + "availableNow": false, + "description": "아국은 대상이 아닙니다." + } + ], + "che_불가침제의": [ + { + "value": 2, + "label": "적국", + "color": "#800000", + "availableNow": true, + "description": "현재 제의 가능 · 교역" + }, + { + "value": 3, + "label": "불가침국", + "color": "#000080", + "availableNow": true, + "description": "현재 제의 가능 · 불가침 12턴" + }, + { + "value": 1, + "label": "아국", + "color": "#008000", + "availableNow": false, + "description": "아국은 대상이 아닙니다." + } + ], + "che_선전포고": [ + { + "value": 2, + "label": "적국", + "color": "#800000", + "availableNow": true, + "description": "현재 선전포고 가능 · 교역" + }, + { + "value": 3, + "label": "불가침국", + "color": "#000080", + "availableNow": false, + "description": "현재 외교 관계에서는 선전포고 불가" + }, + { + "value": 1, + "label": "아국", + "color": "#008000", + "availableNow": false, + "description": "아국은 대상이 아닙니다." + } + ], + "che_종전제의": [ + { + "value": 2, + "label": "적국", + "color": "#800000", + "availableNow": true, + "description": "현재 종전 제의 가능 · 전쟁 6턴" + }, + { + "value": 3, + "label": "불가침국", + "color": "#000080", + "availableNow": false, + "description": "전쟁·선포 중인 국가가 아닙니다." + }, + { + "value": 1, + "label": "아국", + "color": "#008000", + "availableNow": false, + "description": "아국은 대상이 아닙니다." + } + ], + "che_불가침파기제의": [ + { + "value": 3, + "label": "불가침국", + "color": "#000080", + "availableNow": true, + "description": "현재 불가침 파기 제의 가능 · 불가침 12턴" + }, + { + "value": 2, + "label": "적국", + "color": "#800000", + "availableNow": false, + "description": "불가침 중인 국가가 아닙니다." + }, + { + "value": 1, + "label": "아국", + "color": "#008000", + "availableNow": false, + "description": "아국은 대상이 아닙니다." + } + ] + }, + "generals": [ + { + "value": 1, + "label": "장수 (아국 · 업)" + }, + { + "value": 2, + "label": "관우 (아국 · 업)" + } + ], + "generalTargets": { + "che_포상": [ + { + "value": 1, + "label": "장수 (업)", + "gold": 5000, + "rice": 400, + "crew": 500, + "description": "금 5,000 · 쌀 400 · 병력 500" + }, + { + "value": 2, + "label": "관우 (업)", + "gold": 100, + "rice": 4000, + "crew": 1200, + "troopId": 2, + "description": "금 100 · 쌀 4,000 · 병력 1,200" + }, + { + "value": 3, + "label": "여포NPC (업)", + "npcState": 2, + "gold": 3000, + "rice": 500, + "crew": 1500, + "troopId": 2, + "description": "금 3,000 · 쌀 500 · 병력 1,500" + } + ], + "che_몰수": [ + { + "value": 1, + "label": "장수 (업)", + "gold": 5000, + "rice": 400, + "crew": 500, + "description": "금 5,000 · 쌀 400 · 병력 500" + }, + { + "value": 2, + "label": "관우 (업)", + "gold": 100, + "rice": 4000, + "crew": 1200, + "troopId": 2, + "description": "금 100 · 쌀 4,000 · 병력 1,200" + }, + { + "value": 3, + "label": "여포NPC (업)", + "npcState": 2, + "gold": 3000, + "rice": 500, + "crew": 1500, + "troopId": 2, + "description": "금 3,000 · 쌀 500 · 병력 1,500" + } + ], + "che_발령": [ + { + "value": 1, + "label": "장수 (업)", + "targetNames": { + "name": "장수", + "nationName": "대한민국천하통일국", + "cityName": "업", + "troopName": null + }, + "description": "통솔 70 · 무력 80 · 지력 90\n병력 500 · 훈련 0 · 사기 0\n금 5,000 · 쌀 400", + "gold": 5000, + "rice": 400, + "crew": 500, + "troopId": 0, + "npcState": 0 + }, + { + "value": 2, + "label": "관우 (업)", + "targetNames": { + "name": "관우", + "nationName": "대한민국천하통일국", + "cityName": "업", + "troopName": "청룡대" + }, + "description": "통솔 100 · 무력 95 · 지력 70\n병력 1,200 · 훈련 80 · 사기 70\n금 100 · 쌀 4,000", + "gold": 100, + "rice": 4000, + "crew": 1200, + "troopId": 2, + "npcState": 0 + }, + { + "value": 3, + "label": "여포NPC (업)", + "targetNames": { + "name": "여포NPC", + "nationName": "대한민국천하통일국", + "cityName": "업", + "troopName": "청룡대" + }, + "description": "통솔 90 · 무력 100 · 지력 0\n병력 1,500 · 훈련 100 · 사기 100\n금 3,000 · 쌀 500", + "gold": 3000, + "rice": 500, + "crew": 1500, + "troopId": 2, + "npcState": 2 + } + ], + "che_부대탈퇴지시": [ + { + "value": 3, + "label": "여포NPC (아국 · 업)", + "npcState": 2, + "availableNow": true, + "crew": 1500, + "troopId": 2, + "description": "현재 탈퇴 지시 가능 · 병력 1,500 · 탑승 부대 청룡대" + }, + { + "value": 1, + "label": "장수 (아국 · 업)", + "availableNow": false, + "crew": 500, + "description": "현재 탈퇴 지시 불가 · 탑승 부대 없음" + }, + { + "value": 2, + "label": "관우 (아국 · 업)", + "availableNow": false, + "crew": 1200, + "troopId": 2, + "description": "현재 탈퇴 지시 불가 · 탑승 부대 청룡대 (부대장)" + } + ] + }, + "crewTypes": [ + { + "value": 1100, + "label": "보병" + } + ], + "armTypes": [ + { + "value": 1, + "label": "보병" + } + ], + "nationTypes": [ + { + "value": "che_도적", + "label": "도적", + "description": "금 수입 증가, 쌀 수입 감소" + } + ], + "colors": [ + { + "value": 0, + "label": "색상 1", + "color": "#ff0000" + } + ], + "items": { + "horse": [ + { + "value": "None", + "label": "판매/해제" + } + ] + }, + "recruitment": { + "techLevel": 1, + "leadership": 68, + "fullLeadership": 70, + "currentCrewTypeId": 1100, + "currentCrewTypeName": "보병", + "crew": 500, + "gold": 12345, + "groups": [ + { + "armType": 1, + "armName": "보병", + "values": [ + { + "id": 1100, + "armType": 1, + "name": "보병", + "available": true, + "special": false, + "attack": 125, + "defence": 175, + "speed": 7, + "avoid": 10, + "baseCost": 10.35, + "baseRice": 10.35, + "info": ["표준적인 보병입니다.", "보병은 방어특화입니다."] + }, + { + "id": 1101, + "armType": 1, + "name": "정예병", + "available": false, + "special": true, + "attack": 175, + "defence": 225, + "speed": 8, + "avoid": 20, + "baseCost": 13.8, + "baseRice": 11.5, + "info": [ + "강력하지만 기술이 필요합니다.", + "기술력 2000 이상 필요", + "중원 지역 소유시 가능", + "저 소유시 가능" + ] + } + ] + } + ] + }, + "amountPresets": { + "che_포상": { + "values": [100, 500, 1000, 5000, 10000], + "defaultValue": 1000, + "min": 100, + "max": 10000, + "step": 1 + }, + "che_몰수": { + "values": [100, 500, 1000, 5000, 10000], + "defaultValue": 1000, + "min": 100, + "max": 10000, + "step": 1 + }, + "che_물자원조": { + "values": [10000, 20000, 30000], + "defaultValue": 1000, + "min": 1000, + "max": 30000, + "step": 10 + } + }, + "context": { + "actorGold": 1000, + "actorRice": 1000, + "citySecurity": 500, + "nationGold": 5000, + "nationRice": 6000, + "nationLevel": 1 + } + } + } + } + }, + "world.getMap": { + "result": { + "data": { + "result": true, + "version": 0, + "startYear": 180, + "year": 200, + "month": 1, + "cityList": [ + [1, 8, 0, 1, 1, 1], + [2, 7, 41, 2, 2, 1] + ], + "nationList": [ + [1, "대한민국천하통일국", "#008000", 1], + [2, "대한민국천하통일국", "#800000", 2] + ], + "spyList": {}, + "shownByGeneralList": [], + "myCity": 1, + "myNation": 1 + } + } + }, + "world.getMapLayout": { + "result": { + "data": { + "mapName": "che", + "cityList": [ + { + "id": 1, + "name": "업", + "level": 8, + "region": 1, + "x": 100, + "y": 100, + "path": [2] + }, + { + "id": 2, + "name": "허창", + "level": 7, + "region": 2, + "x": 240, + "y": 180, + "path": [1] + } + ], + "regionMap": { + "1": "하북", + "2": "예주" + }, + "levelMap": { + "7": "대", + "8": "특" + } + } + } + } + }, + "personnel": { + "lobby.info": { + "result": { + "data": { + "myGeneral": { + "id": 1, + "name": "가나다라마바사아자" + } + } + } + }, + "auth.status": { + "result": { + "data": { + "ok": true + } + } + }, + "nation.getPersonnelInfo": { + "result": { + "data": { + "me": { + "id": 1, + "officerLevel": 12, + "canManage": true, + "canChangePermissions": true, + "canKick": true + }, + "nation": { + "id": 1, + "name": "대한민국천하통일국", + "color": "#777777", + "level": 3, + "typeCode": "che_법가", + "capitalCityId": 1, + "chiefSet": 0 + }, + "chiefStatMin": 65, + "generals": [ + { + "id": 1, + "name": "가나다라마바사아자", + "npcState": 0, + "officerLevel": 12, + "cityId": 1, + "cityName": "허창", + "troopId": 0, + "troopName": null, + "picture": "default.jpg", + "imageServer": 0, + "officerCity": 0, + "officerCityName": null, + "stats": { + "leadership": 70, + "strength": 70, + "intelligence": 70 + }, + "experience": 100, + "dedication": 200, + "injury": 0, + "gold": 1000, + "rice": 1000, + "crew": 100, + "personality": null, + "specialDomestic": null, + "specialWar": null, + "belong": 10, + "permission": "normal" + }, + { + "id": 2, + "name": "가나다라마바사아자", + "npcState": 0, + "officerLevel": 11, + "cityId": 1, + "cityName": "허창", + "troopId": 0, + "troopName": null, + "picture": "default.jpg", + "imageServer": 0, + "officerCity": 0, + "officerCityName": null, + "stats": { + "leadership": 70, + "strength": 70, + "intelligence": 70 + }, + "experience": 100, + "dedication": 200, + "injury": 0, + "gold": 1000, + "rice": 1000, + "crew": 100, + "personality": null, + "specialDomestic": null, + "specialWar": null, + "belong": 10, + "permission": "normal" + }, + { + "id": 3, + "name": "가나다라마바사아자", + "npcState": 0, + "officerLevel": 4, + "cityId": 1, + "cityName": "허창", + "troopId": 0, + "troopName": null, + "picture": "default.jpg", + "imageServer": 0, + "officerCity": 1, + "officerCityName": "허창", + "stats": { + "leadership": 70, + "strength": 70, + "intelligence": 70 + }, + "experience": 100, + "dedication": 200, + "injury": 0, + "gold": 1000, + "rice": 1000, + "crew": 100, + "personality": null, + "specialDomestic": null, + "specialWar": null, + "belong": 10, + "permission": "normal" + }, + { + "id": 4, + "name": "가나다라마바사아자", + "npcState": 0, + "officerLevel": 3, + "cityId": 1, + "cityName": "허창", + "troopId": 0, + "troopName": null, + "picture": "default.jpg", + "imageServer": 0, + "officerCity": 1, + "officerCityName": "허창", + "stats": { + "leadership": 70, + "strength": 70, + "intelligence": 70 + }, + "experience": 100, + "dedication": 200, + "injury": 0, + "gold": 1000, + "rice": 1000, + "crew": 100, + "personality": null, + "specialDomestic": null, + "specialWar": null, + "belong": 10, + "permission": "normal" + }, + { + "id": 5, + "name": "가나다라마바사아자", + "npcState": 0, + "officerLevel": 2, + "cityId": 1, + "cityName": "허창", + "troopId": 0, + "troopName": null, + "picture": "default.jpg", + "imageServer": 0, + "officerCity": 1, + "officerCityName": "허창", + "stats": { + "leadership": 70, + "strength": 70, + "intelligence": 70 + }, + "experience": 100, + "dedication": 200, + "injury": 0, + "gold": 1000, + "rice": 1000, + "crew": 100, + "personality": null, + "specialDomestic": null, + "specialWar": null, + "belong": 10, + "permission": "normal" + }, + { + "id": 6, + "name": "가나다라마바사아자", + "npcState": 2, + "officerLevel": 1, + "cityId": 1, + "cityName": "허창", + "troopId": 0, + "troopName": null, + "picture": "default.jpg", + "imageServer": 0, + "officerCity": 0, + "officerCityName": null, + "stats": { + "leadership": 70, + "strength": 70, + "intelligence": 70 + }, + "experience": 100, + "dedication": 200, + "injury": 0, + "gold": 1000, + "rice": 1000, + "crew": 100, + "personality": null, + "specialDomestic": null, + "specialWar": null, + "belong": 10, + "permission": "normal" + }, + { + "id": 7, + "name": "가나다라마바사아자", + "npcState": 0, + "officerLevel": 1, + "cityId": 1, + "cityName": "허창", + "troopId": 0, + "troopName": null, + "picture": "default.jpg", + "imageServer": 0, + "officerCity": 0, + "officerCityName": null, + "stats": { + "leadership": 70, + "strength": 70, + "intelligence": 70 + }, + "experience": 100, + "dedication": 200, + "injury": 0, + "gold": 1000, + "rice": 1000, + "crew": 100, + "personality": null, + "specialDomestic": null, + "specialWar": null, + "belong": 10, + "permission": "ambassador" + }, + { + "id": 8, + "name": "가나다라마바사아자", + "npcState": 0, + "officerLevel": 1, + "cityId": 1, + "cityName": "허창", + "troopId": 0, + "troopName": null, + "picture": "default.jpg", + "imageServer": 0, + "officerCity": 0, + "officerCityName": null, + "stats": { + "leadership": 70, + "strength": 70, + "intelligence": 70 + }, + "experience": 100, + "dedication": 200, + "injury": 0, + "gold": 1000, + "rice": 1000, + "crew": 100, + "personality": null, + "specialDomestic": null, + "specialWar": null, + "belong": 10, + "permission": "auditor" + }, + { + "id": 9, + "name": "가나다라마바사아자", + "npcState": 0, + "officerLevel": 1, + "cityId": 1, + "cityName": "허창", + "troopId": 0, + "troopName": null, + "picture": "default.jpg", + "imageServer": 0, + "officerCity": 0, + "officerCityName": null, + "stats": { + "leadership": 70, + "strength": 70, + "intelligence": 70 + }, + "experience": 100, + "dedication": 200, + "injury": 0, + "gold": 1000, + "rice": 1000, + "crew": 100, + "personality": null, + "specialDomestic": null, + "specialWar": null, + "belong": 10, + "permission": "normal" + } + ], + "chiefAssignments": { + "11": { + "id": 2, + "name": "가나다라마바사아자", + "npcState": 0, + "officerLevel": 11, + "cityId": 1, + "cityName": "허창", + "troopId": 0, + "troopName": null, + "picture": "default.jpg", + "imageServer": 0, + "officerCity": 0, + "officerCityName": null, + "stats": { + "leadership": 70, + "strength": 70, + "intelligence": 70 + }, + "experience": 100, + "dedication": 200, + "injury": 0, + "gold": 1000, + "rice": 1000, + "crew": 100, + "personality": null, + "specialDomestic": null, + "specialWar": null, + "belong": 10, + "permission": "normal" + }, + "12": { + "id": 1, + "name": "가나다라마바사아자", + "npcState": 0, + "officerLevel": 12, + "cityId": 1, + "cityName": "허창", + "troopId": 0, + "troopName": null, + "picture": "default.jpg", + "imageServer": 0, + "officerCity": 0, + "officerCityName": null, + "stats": { + "leadership": 70, + "strength": 70, + "intelligence": 70 + }, + "experience": 100, + "dedication": 200, + "injury": 0, + "gold": 1000, + "rice": 1000, + "crew": 100, + "personality": null, + "specialDomestic": null, + "specialWar": null, + "belong": 10, + "permission": "normal" + } + }, + "cityAssignments": [ + { + "id": 1, + "name": "허창", + "level": 7, + "region": 2, + "officerSet": 8, + "officers": { + "2": { + "id": 5, + "name": "가나다라마바사아자", + "npcState": 0, + "officerLevel": 2, + "cityId": 1, + "cityName": "허창", + "troopId": 0, + "troopName": null, + "picture": "default.jpg", + "imageServer": 0, + "officerCity": 1, + "officerCityName": "허창", + "stats": { + "leadership": 70, + "strength": 70, + "intelligence": 70 + }, + "experience": 100, + "dedication": 200, + "injury": 0, + "gold": 1000, + "rice": 1000, + "crew": 100, + "personality": null, + "specialDomestic": null, + "specialWar": null, + "belong": 10, + "permission": "normal" + }, + "3": { + "id": 4, + "name": "가나다라마바사아자", + "npcState": 0, + "officerLevel": 3, + "cityId": 1, + "cityName": "허창", + "troopId": 0, + "troopName": null, + "picture": "default.jpg", + "imageServer": 0, + "officerCity": 1, + "officerCityName": "허창", + "stats": { + "leadership": 70, + "strength": 70, + "intelligence": 70 + }, + "experience": 100, + "dedication": 200, + "injury": 0, + "gold": 1000, + "rice": 1000, + "crew": 100, + "personality": null, + "specialDomestic": null, + "specialWar": null, + "belong": 10, + "permission": "normal" + }, + "4": { + "id": 3, + "name": "가나다라마바사아자", + "npcState": 0, + "officerLevel": 4, + "cityId": 1, + "cityName": "허창", + "troopId": 0, + "troopName": null, + "picture": "default.jpg", + "imageServer": 0, + "officerCity": 1, + "officerCityName": "허창", + "stats": { + "leadership": 70, + "strength": 70, + "intelligence": 70 + }, + "experience": 100, + "dedication": 200, + "injury": 0, + "gold": 1000, + "rice": 1000, + "crew": 100, + "personality": null, + "specialDomestic": null, + "specialWar": null, + "belong": 10, + "permission": "normal" + } + } + }, + { + "id": 2, + "name": "낙양", + "level": 6, + "region": 2, + "officerSet": 0, + "officers": { + "2": null, + "3": null, + "4": null + } + } + ], + "awards": { + "tigers": [ + { + "id": 3, + "name": "하후돈", + "value": 12 + } + ], + "eagles": [ + { + "id": 4, + "name": "곽가", + "value": 9 + } + ] + }, + "permissionCandidates": { + "ambassadors": [ + { + "id": 6, + "name": "가나다라마바사아자", + "npcState": 0, + "permission": "normal", + "maxPermission": 4 + }, + { + "id": 7, + "name": "가나다라마바사아자", + "npcState": 0, + "permission": "ambassador", + "maxPermission": 4 + }, + { + "id": 9, + "name": "가나다라마바사아자", + "npcState": 0, + "permission": "normal", + "maxPermission": 4 + } + ], + "auditors": [ + { + "id": 6, + "name": "가나다라마바사아자", + "npcState": 0, + "permission": "normal", + "maxPermission": 4 + }, + { + "id": 8, + "name": "가나다라마바사아자", + "npcState": 0, + "permission": "auditor", + "maxPermission": 4 + }, + { + "id": 9, + "name": "가나다라마바사아자", + "npcState": 0, + "permission": "normal", + "maxPermission": 4 + } + ] + } + } + } + } + }, + "possession": { + "lobby.info": { + "result": { + "data": { + "myGeneral": null, + "year": 180, + "month": 1, + "turnTerm": 5 + } + } + }, + "auth.status": { + "result": { + "data": { + "ok": true + } + } + }, + "join.getConfig": { + "result": { + "data": { + "rules": { + "stat": { + "total": 165, + "min": 15, + "max": 80, + "bonusMin": 3, + "bonusMax": 5 + }, + "allowDirectCreation": false, + "allowCustomName": true + }, + "user": { + "id": "npc-user", + "displayName": "빙의사용자", + "canCreateGeneral": true, + "icons": [], + "preferredPicture": null + }, + "personalities": [ + { + "key": "Random", + "name": "???", + "info": "" + } + ], + "warSpecials": [], + "nations": [], + "serverInfo": { + "currentYear": 180, + "currentMonth": 1, + "tickMinutes": 5, + "maxGeneral": 500, + "userGeneralCount": 0, + "npcGeneralCount": 12 + }, + "inherit": { + "totalPoint": 0, + "costs": { + "inheritBornSpecialPoint": 0, + "inheritBornTurntimePoint": 0, + "inheritBornCityPoint": 0, + "inheritBornStatPoint": 0 + }, + "availableCities": [], + "turnTimeZones": [], + "availableSpecialWar": [] + }, + "selectionPool": { + "enabled": false + }, + "npcPossession": { + "enabled": true + } + } + } + }, + "join.listPossessCandidates": { + "result": { + "data": { + "tokenNonce": 101, + "validUntil": "2026-09-15T15:10:08.460Z", + "pickMoreFrom": "2026-09-15T15:08:37.460Z", + "pickMoreSeconds": 0, + "candidates": [ + { + "id": 1, + "name": "가나다라마바사아자", + "nation": { + "id": 0, + "name": "대한민국천하통일국", + "color": "#aaaaaa" + }, + "stats": { + "leadership": 40, + "strength": 50, + "intelligence": 60 + }, + "picture": "default.jpg", + "imageServer": 1, + "personality": { + "code": "che_안전", + "name": "안전", + "info": "안전을 중시합니다." + }, + "specialDomestic": { + "code": "che_인덕", + "name": "인덕", + "info": "인덕 설명" + }, + "specialWar": { + "code": "che_무쌍", + "name": "무쌍", + "info": "무쌍 설명" + }, + "keepCount": 3 + }, + { + "id": 2, + "name": "가나다라마바사아자", + "nation": { + "id": 0, + "name": "대한민국천하통일국", + "color": "#aaaaaa" + }, + "stats": { + "leadership": 41, + "strength": 51, + "intelligence": 61 + }, + "picture": "default.jpg", + "imageServer": 0, + "personality": { + "code": "che_안전", + "name": "안전", + "info": "안전을 중시합니다." + }, + "specialDomestic": { + "code": "che_인덕", + "name": "인덕", + "info": "인덕 설명" + }, + "specialWar": { + "code": "che_무쌍", + "name": "무쌍", + "info": "무쌍 설명" + }, + "keepCount": 3 + }, + { + "id": 3, + "name": "가나다라마바사아자", + "nation": { + "id": 0, + "name": "대한민국천하통일국", + "color": "#aaaaaa" + }, + "stats": { + "leadership": 42, + "strength": 52, + "intelligence": 62 + }, + "picture": "default.jpg", + "imageServer": 0, + "personality": { + "code": "che_안전", + "name": "안전", + "info": "안전을 중시합니다." + }, + "specialDomestic": { + "code": "che_인덕", + "name": "인덕", + "info": "인덕 설명" + }, + "specialWar": { + "code": "che_무쌍", + "name": "무쌍", + "info": "무쌍 설명" + }, + "keepCount": 3 + }, + { + "id": 4, + "name": "가나다라마바사아자", + "nation": { + "id": 0, + "name": "대한민국천하통일국", + "color": "#aaaaaa" + }, + "stats": { + "leadership": 43, + "strength": 53, + "intelligence": 63 + }, + "picture": "default.jpg", + "imageServer": 0, + "personality": { + "code": "che_안전", + "name": "안전", + "info": "안전을 중시합니다." + }, + "specialDomestic": { + "code": "che_인덕", + "name": "인덕", + "info": "인덕 설명" + }, + "specialWar": { + "code": "che_무쌍", + "name": "무쌍", + "info": "무쌍 설명" + }, + "keepCount": 3 + }, + { + "id": 5, + "name": "가나다라마바사아자", + "nation": { + "id": 0, + "name": "대한민국천하통일국", + "color": "#aaaaaa" + }, + "stats": { + "leadership": 44, + "strength": 54, + "intelligence": 64 + }, + "picture": "default.jpg", + "imageServer": 0, + "personality": { + "code": "che_안전", + "name": "안전", + "info": "안전을 중시합니다." + }, + "specialDomestic": { + "code": "che_인덕", + "name": "인덕", + "info": "인덕 설명" + }, + "specialWar": { + "code": "che_무쌍", + "name": "무쌍", + "info": "무쌍 설명" + }, + "keepCount": 3 + } + ] + } + } + } + } +} diff --git a/app/game-frontend/e2e/playwright.config.mjs b/app/game-frontend/e2e/playwright.config.mjs index 29319c5d..4e309933 100644 --- a/app/game-frontend/e2e/playwright.config.mjs +++ b/app/game-frontend/e2e/playwright.config.mjs @@ -20,6 +20,7 @@ export default defineConfig({ testDir: '.', testMatch: [ 'troop.spec.ts', + 'typographyPolicy.spec.ts', 'board.spec.ts', 'inGameInfo.spec.ts', 'infoMobileLayout.spec.ts', diff --git a/app/game-frontend/e2e/typographyPolicy.spec.ts b/app/game-frontend/e2e/typographyPolicy.spec.ts new file mode 100644 index 00000000..7c7e79b7 --- /dev/null +++ b/app/game-frontend/e2e/typographyPolicy.spec.ts @@ -0,0 +1,194 @@ +import { readFile } from 'node:fs/promises'; +import { resolve } from 'node:path'; +import { expect, test, type Page } from '@playwright/test'; +import { gameProfile, gameTrpcRoute } from './gameTestPaths.js'; + +// 기존 화면 fixture의 읽기 응답을 고정했다. 실제 계정이나 게임 서버에는 접근하지 않는다. +const fixtureText = await readFile(new URL('./fixtures/typography.json', import.meta.url), 'utf8'); +const imageRoot = process.env.FRONTEND_PARITY_IMAGE_ROOT ?? resolve(import.meta.dirname, '../../../../image'); +const fontRoot = process.env.TYPOGRAPHY_FONT_ROOT; +const names = [ + { label: '전각9자', general: '가나다라마바사아자', nation: '대한민국천하통일국' }, + { label: '반각18자', general: 'WWWWWWWWWWWWWWWWWW', nation: 'MMMMMMMMMMMMMMMMMM' }, +] as const; + +const installAssets = async (page: Page): Promise => { + await page.route(/(?:\/image\/|sam-image\.hided\.net\/)/u, async (route) => { + const url = new URL(route.request().url()); + const relative = decodeURIComponent(url.pathname.replace(/^\/image\//u, '').replace(/^\//u, '')); + if (relative.split('/').includes('..')) throw new Error('Invalid typography image path'); + await route.fulfill({ + body: await readFile(resolve(imageRoot, relative)), + contentType: relative.endsWith('.png') + ? 'image/png' + : relative.endsWith('.gif') + ? 'image/gif' + : 'image/jpeg', + }); + }); + if (fontRoot) { + await page.route('**/pretendard.css', (route) => + route.fulfill({ path: resolve(fontRoot, 'source.css'), contentType: 'text/css' }) + ); + await page.route('**/*.woff2', (route) => + route.fulfill({ + path: resolve(fontRoot, new URL(route.request().url()).pathname.split('/').at(-1)!), + contentType: 'font/woff2', + }) + ); + } +}; + +const install = async (page: Page, scene: string, general: string, nation: string): Promise => { + const fixtures = JSON.parse( + fixtureText.replaceAll(names[0].general, general).replaceAll(names[0].nation, nation) + ) as Record>; + await page.addInitScript((profile) => { + localStorage.setItem('sammo-game-token', 'ga_typography_fixture'); + localStorage.setItem('sammo-game-profile', profile); + localStorage.removeItem('sam_customCSS'); + }, gameProfile); + await page.route('**/events**', (route) => route.abort()); + await page.route(gameTrpcRoute, async (route) => { + const operations = decodeURIComponent(new URL(route.request().url()).pathname.split('/trpc/')[1]!).split(','); + const responses = operations.map((operation) => { + const response = fixtures[scene]?.[operation]; + if (response !== undefined) return response; + if (operation === 'public.recordAccess') return { result: { data: { recorded: true } } }; + throw new Error(`Uncovered typography fixture: ${scene}/${operation}`); + }); + await route.fulfill({ contentType: 'application/json', body: JSON.stringify(responses) }); + }); + await installAssets(page); +}; + +const waitForFonts = async (page: Page): Promise => { + await page.evaluate(async () => { + await document.fonts.ready; + }); + expect( + await page.evaluate(() => + [...document.fonts].some((font) => font.family.includes('Pretendard') && font.status === 'loaded') + ) + ).toBe(true); +}; + +for (const width of [500, 1000]) { + for (const name of names) { + test(`메인 주요 이름과 제목을 보존한다 ${width}px ${name.label}`, async ({ page }, testInfo) => { + await page.setViewportSize({ width, height: 900 }); + await install(page, 'main', name.general, name.nation); + await page.goto('./'); + const general = page.locator('.battle-general-name:visible').first(); + await expect(general).toContainText(name.general); + const nation = page.locator('.city-nation:visible').first(); + await expect(nation).toContainText(name.nation); + await waitForFonts(page); + for (const label of [general, nation]) { + expect(await label.evaluate((element) => element.scrollWidth <= element.clientWidth + 1)).toBe(true); + } + await expect(page.locator('.game-shell__title')).toHaveCSS('font-size', '24px'); + await page.screenshot({ path: testInfo.outputPath('main-maximum-name.png'), fullPage: true }); + }); + + test(`네 단계와 밀집 화면 보류 계약 ${width}px ${name.label}`, async ({ page }, testInfo) => { + await page.setViewportSize({ width, height: 900 }); + await install(page, 'personnel', name.general, name.nation); + await page.goto('nation/personnel'); + await expect(page.locator('.nation-heading')).toContainText(name.nation); + await expect(page.locator('.chief-entry-copy strong').first()).toHaveText(name.general); + await waitForFonts(page); + await expect(page.locator('.nation-heading')).toHaveCSS('font-size', width === 500 ? '16px' : '24px'); + // 500px의 15px 이름/10px 잠금은 확대 시 표시 이름이 줄어들어 보류했다. + await expect(page.locator('.chief-entry-copy strong').first()).toHaveCSS( + 'font-size', + width === 500 ? '15px' : '16px' + ); + const geometry = await page.locator('#personnel-container').evaluate((element) => ({ + width: element.getBoundingClientRect().width, + scrollWidth: element.scrollWidth, + controls: [...element.querySelectorAll('.personnel-change-button')].map((button) => { + const rect = button.getBoundingClientRect(); + return { width: rect.width, height: rect.height, text: button.innerText }; + }), + })); + expect(geometry.scrollWidth).toBeLessThanOrEqual(width + 1); + expect(geometry.controls.every((control) => control.width > 0 && control.height >= 24)).toBe(true); + await testInfo.attach('geometry', { + body: JSON.stringify(geometry, null, 2), + contentType: 'application/json', + }); + await page.screenshot({ path: testInfo.outputPath('personnel-maximum-name.png'), fullPage: true }); + }); + } + + test(`사령부 12턴 요약의 기존 밀도를 유지한다 ${width}px`, async ({ page }, testInfo) => { + await page.setViewportSize({ width, height: 900 }); + await install(page, 'chief', names[0].general, names[0].nation); + await page.goto('chief-center'); + const row = page.locator('.chief-overview .chief-card.compact .chief-row').first(); + await expect(row).toBeVisible(); + await waitForFonts(page); + await expect(row).toHaveCSS('font-size', '8.8px'); + await expect(row).toHaveCSS('line-height', '11.25px'); + await expect(page.locator('.chief-overview .compact-name').first()).toHaveCSS('font-size', '10.4px'); + expect((await row.boundingBox())?.height).toBe(11.25); + await page.screenshot({ path: testInfo.outputPath('chief-deferred-density.png'), fullPage: true }); + }); + + for (const length of [4, 7, 9, 18]) { + test(`빙의 이름 길이별 축소를 유지한다 ${width}px ${length}자`, async ({ page }, testInfo) => { + const general = length === 18 ? names[1].general : names[0].general.slice(0, length); + await page.setViewportSize({ width, height: 900 }); + await install(page, 'possession', general, names[0].nation); + await page.goto('join'); + const title = page.locator('.npc-card-name').first(); + await expect(title).toHaveText(general); + await waitForFonts(page); + await expect(title).toHaveCSS('font-size', length >= 9 ? '12px' : '16px'); + await expect(title).toHaveCSS('white-space', 'nowrap'); + expect((await title.boundingBox())?.height).toBe(25); + await page.screenshot({ path: testInfo.outputPath('possession-name-length.png'), fullPage: true }); + }); + } + + test(`전투 로그의 장수명과 병력 축소를 유지한다 ${width}px`, async ({ page }, testInfo) => { + await page.setViewportSize({ width, height: 900 }); + await installAssets(page); + const log = + '◆217년 7월:
마귀 ' + + '【Hide_D】 0(-3714) ' + + ' 3955(-3545) ' + + '백이 【경국지색소교】 19:57
'; + await page.route(gameTrpcRoute, async (route) => { + const operations = decodeURIComponent(new URL(route.request().url()).pathname.split('/trpc/')[1]!).split( + ',' + ); + const data: Record = { + 'public.getMapLayout': { mapName: 'che', cityList: [] }, + 'public.getCachedMap': { + year: 217, + month: 7, + cityList: [], + nationList: [], + history: [{ id: 1, text: log }], + }, + 'public.getWorldTrend': { year: 217, month: 7, turnTerm: 10 }, + 'public.getNationList': [], + 'public.getGeneralList': [], + }; + await route.fulfill({ + contentType: 'application/json', + body: JSON.stringify(operations.map((operation) => ({ result: { data: data[operation] } }))), + }); + }); + await page.goto('public'); + const summary = page.locator('.small_war_log').first(); + await expect(summary).toContainText('【경국지색소교】'); + await expect(summary).toHaveCSS('font-size', '14px'); + await expect(summary.locator('.name_plate').first()).toHaveCSS('font-size', '10.5px'); + await expect(summary.locator('.crew_plate').first()).toHaveCSS('font-size', '12.6px'); + await waitForFonts(page); + await page.screenshot({ path: testInfo.outputPath('battle-log-exception.png'), fullPage: true }); + }); +} diff --git a/app/game-frontend/src/assets/main.css b/app/game-frontend/src/assets/main.css index d1665fb7..5bc644d9 100644 --- a/app/game-frontend/src/assets/main.css +++ b/app/game-frontend/src/assets/main.css @@ -27,7 +27,7 @@ body { background-repeat: repeat-y; color: #fff; font-family: var(--sammo-font-sans); - font-size: 14px; + font-size: var(--sammo-font-size-normal); line-height: 1.3; } diff --git a/app/game-frontend/src/assets/styles/game-shell.css b/app/game-frontend/src/assets/styles/game-shell.css index 9929c557..6b92bb25 100644 --- a/app/game-frontend/src/assets/styles/game-shell.css +++ b/app/game-frontend/src/assets/styles/game-shell.css @@ -23,12 +23,12 @@ } .game-shell__title { - font-size: 1.6rem; + font-size: var(--sammo-font-size-title); font-weight: 600; } .game-shell__subtitle { - font-size: 0.85rem; + font-size: var(--sammo-font-size-normal); color: var(--sammo-color-text-muted); } @@ -44,11 +44,11 @@ background: var(--sammo-color-action-bg); color: inherit; text-decoration: none; - font-size: 0.8rem; + font-size: var(--sammo-font-size-small); cursor: pointer; } .game-feedback--error { color: var(--sammo-color-error); - font-size: 0.85rem; + font-size: var(--sammo-font-size-normal); } diff --git a/app/game-frontend/src/assets/styles/legacy-controls.css b/app/game-frontend/src/assets/styles/legacy-controls.css index 2b2fcba0..db304b9a 100644 --- a/app/game-frontend/src/assets/styles/legacy-controls.css +++ b/app/game-frontend/src/assets/styles/legacy-controls.css @@ -133,7 +133,7 @@ .legacy-sort-indicator { margin-left: 2px; color: #9ee7ba; - font-size: 0.75em; + font-size: var(--sammo-font-size-small); opacity: 0.65; } diff --git a/app/game-frontend/src/assets/styles/ref-shell.css b/app/game-frontend/src/assets/styles/ref-shell.css index 335841d3..cb8105ad 100644 --- a/app/game-frontend/src/assets/styles/ref-shell.css +++ b/app/game-frontend/src/assets/styles/ref-shell.css @@ -16,7 +16,7 @@ background-image: var(--sammo-texture-walnut); color: var(--sammo-color-text); font-family: var(--sammo-font-sans); - font-size: 14px; + font-size: var(--sammo-font-size-normal); line-height: 1.5; } @@ -35,7 +35,7 @@ } .ref-shell__title { - font-size: 17px; + font-size: var(--sammo-font-size-emphasis); font-weight: 500; } diff --git a/app/game-frontend/src/assets/styles/tokens.css b/app/game-frontend/src/assets/styles/tokens.css index 894ccb92..5e6fbac4 100644 --- a/app/game-frontend/src/assets/styles/tokens.css +++ b/app/game-frontend/src/assets/styles/tokens.css @@ -1,4 +1,9 @@ :root { + /* 검증된 화면이 공유하는 네 단계. 콘텐츠 내부 축소/숨김은 별도 계약이다. */ + --sammo-font-size-small: 12px; + --sammo-font-size-normal: 14px; + --sammo-font-size-emphasis: 16px; + --sammo-font-size-title: 24px; --sammo-font-sans: Pretendard, 'Apple SD Gothic Neo', 'Noto Sans KR', 'Malgun Gothic', sans-serif; --sammo-color-text: #fff; --sammo-color-text-muted: rgba(232, 221, 196, 0.7); diff --git a/app/game-frontend/src/components/battle/BattleGeneralCard.vue b/app/game-frontend/src/components/battle/BattleGeneralCard.vue index 1f35164b..756379a2 100644 --- a/app/game-frontend/src/components/battle/BattleGeneralCard.vue +++ b/app/game-frontend/src/components/battle/BattleGeneralCard.vue @@ -352,7 +352,7 @@ const officerLevelOptions = [ .general-title { font-weight: 600; - font-size: 14px; + font-size: var(--sammo-font-size-normal); } .general-subtitle { @@ -371,7 +371,7 @@ const officerLevelOptions = [ background: #3498db; color: #fff; padding: 4px 9px; - font-size: 12px; + font-size: var(--sammo-font-size-small); font-weight: 700; cursor: pointer; } @@ -419,7 +419,7 @@ const officerLevelOptions = [ display: grid; grid-template-columns: auto minmax(0, 1fr); color: #ddd; - font-size: 12px; + font-size: var(--sammo-font-size-small); } .field span { @@ -438,7 +438,7 @@ const officerLevelOptions = [ background: #ddd; color: #303030; padding: 6px 10px; - font-size: 14px; + font-size: var(--sammo-font-size-normal); line-height: 21px; box-shadow: inset 0 2px 0 rgba(0, 0, 0, 0.075); } diff --git a/app/game-frontend/src/components/chief/ChiefTurnCard.vue b/app/game-frontend/src/components/chief/ChiefTurnCard.vue index 351791f9..ef968764 100644 --- a/app/game-frontend/src/components/chief/ChiefTurnCard.vue +++ b/app/game-frontend/src/components/chief/ChiefTurnCard.vue @@ -131,20 +131,20 @@ const handleClick = () => { } .chief-level { - font-size: 0.75rem; + font-size: var(--sammo-font-size-small); color: rgba(232, 221, 196, 0.7); } .chief-name { overflow: hidden; - font-size: 0.8rem; + font-size: var(--sammo-font-size-small); font-weight: 600; text-overflow: ellipsis; white-space: nowrap; } .chief-me { - font-size: 0.65rem; + font-size: var(--sammo-font-size-small); padding: 2px 6px; border-radius: 0; background: rgba(201, 164, 90, 0.2); @@ -166,7 +166,7 @@ const handleClick = () => { /* Ref draws no rule between reservation rows and keeps them left aligned. */ border: 0; text-align: left; - font-size: 0.75rem; + font-size: var(--sammo-font-size-small); } .chief-card.compact .chief-row { diff --git a/app/game-frontend/src/components/command/RecruitmentCommandForm.vue b/app/game-frontend/src/components/command/RecruitmentCommandForm.vue index d71555f1..b76ed8c2 100644 --- a/app/game-frontend/src/components/command/RecruitmentCommandForm.vue +++ b/app/game-frontend/src/components/command/RecruitmentCommandForm.vue @@ -270,13 +270,18 @@ watch( diff --git a/app/game-frontend/src/components/ui/RichTooltip.vue b/app/game-frontend/src/components/ui/RichTooltip.vue index c3d8901f..3669be41 100644 --- a/app/game-frontend/src/components/ui/RichTooltip.vue +++ b/app/game-frontend/src/components/ui/RichTooltip.vue @@ -112,7 +112,7 @@ watch( box-shadow: 0 3px 12px rgb(0 0 0 / 65%); color: #f5f5f5; font-family: var(--sammo-font-sans); - font-size: 12.5px; + font-size: var(--sammo-font-size-small); line-height: 1.45; text-align: left; } @@ -147,7 +147,7 @@ watch( .rich-tooltip-content__title { margin-bottom: 4px; color: #7fd4ff; - font-size: 13px; + font-size: var(--sammo-font-size-small); font-weight: 700; } diff --git a/app/game-frontend/src/views/AuctionView.vue b/app/game-frontend/src/views/AuctionView.vue index e5b2d63c..2f3f0f41 100644 --- a/app/game-frontend/src/views/AuctionView.vue +++ b/app/game-frontend/src/views/AuctionView.vue @@ -517,7 +517,7 @@ onMounted(() => { margin: 0 auto; color: #fff; font-family: var(--sammo-font-sans); - font-size: 14px; + font-size: var(--sammo-font-size-normal); line-height: 21px; } .legacy-auction-page.resource-page { @@ -549,7 +549,7 @@ onMounted(() => { } .top-back-bar h1 { margin: 0; - font-size: 24px; + font-size: var(--sammo-font-size-title); font-weight: 500; line-height: 32px; text-align: center; diff --git a/app/game-frontend/src/views/BattleCenterView.vue b/app/game-frontend/src/views/BattleCenterView.vue index e60ca0a8..669aff4b 100644 --- a/app/game-frontend/src/views/BattleCenterView.vue +++ b/app/game-frontend/src/views/BattleCenterView.vue @@ -405,7 +405,7 @@ onMounted(() => { } :deep(.panel-title) { color: skyblue; - font-size: 18px; + font-size: var(--sammo-font-size-emphasis); font-weight: 500; } :deep(.panel-header) { @@ -443,7 +443,7 @@ onMounted(() => { } .battle-top h1 { margin: 0; - font-size: 24px; + font-size: var(--sammo-font-size-title); font-weight: 400; line-height: 32px; text-align: center; diff --git a/app/game-frontend/src/views/BattleSimulatorView.vue b/app/game-frontend/src/views/BattleSimulatorView.vue index aeb44e14..3df8ba93 100644 --- a/app/game-frontend/src/views/BattleSimulatorView.vue +++ b/app/game-frontend/src/views/BattleSimulatorView.vue @@ -1368,7 +1368,7 @@ const shouldShowUI = computed(() => !loading.value && !!options.value); padding: 2px 6px; background: #444; color: #aaa; - font-size: 11px; + font-size: var(--sammo-font-size-small); } .independence-notice p { @@ -1403,7 +1403,7 @@ button { border-radius: 5.25px; padding: 5.25px 10.5px; color: #fff; - font-size: 14px; + font-size: var(--sammo-font-size-normal); font-weight: 700; line-height: 21px; vertical-align: middle; @@ -1465,12 +1465,12 @@ button:disabled { .error { color: #f5b7b1; - font-size: 0.85rem; + font-size: var(--sammo-font-size-normal); } .status { color: #f5d08a; - font-size: 0.85rem; + font-size: var(--sammo-font-size-normal); } .settings-grid { @@ -1484,7 +1484,7 @@ button:disabled { display: grid; grid-template-columns: auto minmax(0, 1fr); color: #ddd; - font-size: 14px; + font-size: var(--sammo-font-size-normal); } .unit-after { @@ -1507,7 +1507,7 @@ button:disabled { background: #ddd; color: #303030; padding: 5.25px 10.5px; - font-size: 14px; + font-size: var(--sammo-font-size-normal); line-height: 21px; box-shadow: inset 0 2px 0 rgba(0, 0, 0, 0.075); transition: @@ -1571,7 +1571,7 @@ button:disabled { border: 1px solid #111; background: #444; color: #ddd; - font-size: 14px; + font-size: var(--sammo-font-size-normal); white-space: nowrap; } @@ -1595,7 +1595,7 @@ button:disabled { padding: 6px 14px; border-radius: 5px; background: #444; - font-size: 14px; + font-size: var(--sammo-font-size-normal); } .defender-toolbar > div { @@ -1653,7 +1653,7 @@ button:disabled { .log-body { min-height: 0; line-height: 1.5; - font-size: 0.85rem; + font-size: var(--sammo-font-size-normal); } .modal-backdrop { @@ -1686,7 +1686,7 @@ button:disabled { .modal h3 { margin: 0; - font-size: 1rem; + font-size: var(--sammo-font-size-emphasis); } .modal-body { @@ -1696,7 +1696,7 @@ button:disabled { } .hint { - font-size: 0.75rem; + font-size: var(--sammo-font-size-small); color: rgba(232, 221, 196, 0.7); } diff --git a/app/game-frontend/src/views/BestGeneralView.vue b/app/game-frontend/src/views/BestGeneralView.vue index eda77607..ad5b0b4a 100644 --- a/app/game-frontend/src/views/BestGeneralView.vue +++ b/app/game-frontend/src/views/BestGeneralView.vue @@ -179,6 +179,11 @@ watch(viewMode, () => { diff --git a/app/game-frontend/src/views/BettingView.vue b/app/game-frontend/src/views/BettingView.vue index 19581030..aedb7ea8 100644 --- a/app/game-frontend/src/views/BettingView.vue +++ b/app/game-frontend/src/views/BettingView.vue @@ -281,11 +281,16 @@ const placeBet = async (target: TournamentBracketSlot) => { diff --git a/app/game-frontend/src/views/DynastyListView.vue b/app/game-frontend/src/views/DynastyListView.vue index 6c0b8a38..2505aeee 100644 --- a/app/game-frontend/src/views/DynastyListView.vue +++ b/app/game-frontend/src/views/DynastyListView.vue @@ -186,7 +186,7 @@ watch(selectedSource, loadDynasty); width: 1000px; background-color: transparent; border-collapse: collapse; - font-size: 14px; + font-size: var(--sammo-font-size-normal); word-break: break-all; } @@ -241,7 +241,7 @@ watch(selectedSource, loadDynasty); } .large-text { - font-size: x-large; + font-size: var(--sammo-font-size-title); } .label-column { @@ -261,7 +261,7 @@ watch(selectedSource, loadDynasty); background: buttonface; color: buttontext; font-family: var(--sammo-font-sans); - font-size: 13.3333px; + font-size: var(--sammo-font-size-normal); font-weight: 400; line-height: normal; cursor: default; @@ -284,6 +284,6 @@ watch(selectedSource, loadDynasty); } .banner { - font-size: 13px; + font-size: var(--sammo-font-size-small); } diff --git a/app/game-frontend/src/views/GeneralListView.vue b/app/game-frontend/src/views/GeneralListView.vue index 4bd4bad0..7403e59f 100644 --- a/app/game-frontend/src/views/GeneralListView.vue +++ b/app/game-frontend/src/views/GeneralListView.vue @@ -133,10 +133,15 @@ onMounted(() => { diff --git a/app/game-frontend/src/views/InheritView.vue b/app/game-frontend/src/views/InheritView.vue index 1e1cd2ca..09ae3beb 100644 --- a/app/game-frontend/src/views/InheritView.vue +++ b/app/game-frontend/src/views/InheritView.vue @@ -787,6 +787,11 @@ onMounted(() => {