+
+ 최근 설정:
+ {{ lastSetters.policy.setter ?? "-없음-" }}
+ ({{ lastSetters.policy.date ?? "설정 기록 없음" }})
+
+
+
+
+
+ 이보다 많으면 포상, 적으면 몰수/헌납합니다.(긴급포상 제외)
+
+
+
+
+ 이보다 많으면 포상, 적으면 몰수/헌납합니다.(긴급포상 제외)
+
+
+
+
+ 유저장긴급포상시 이보다 금이 적은 장수에게 포상합니다.
+
+ 0이면 보병 6회 징병({{ (defaultStatMax * 100).toLocaleString() }} * 6) 가능한 금을 기준으로 하며, 그 수치는
+ 현재 {{ zeroPolicy.reqHumanWarUrgentGold.toLocaleString() }}입니다.
+
+
+
+
+ 유저장긴급포상시 이보다 쌀이 적은 장수에게 포상합니다.
+
+ 0이면 기본 병종으로 {{ (defaultStatMax * 100).toLocaleString() }} * 6명 사살 가능한 쌀을 기준으로 하며, 그
+ 수치는 현재 {{ zeroPolicy.reqHumanWarUrgentRice.toLocaleString() }}입니다.
+
+
+
+
+ 유저전투장에게 주는 금입니다. 이보다 적으면 포상합니다.
+
+ 0이면 유저전투장 긴급포상 금의 2배를 기준으로 하며, 그 수치는 현재
+ {{ (calcPolicyValue("reqHumanWarUrgentGold") * 2).toLocaleString() }}입니다.
+
+
+
+
+ 유저전투장에게 주는 쌀입니다. 이보다 적으면 포상합니다.
+
+ 0이면 유저전투장 긴급포상 쌀의 2배를 기준으로 하며, 그 수치는 현재
+ {{ (calcPolicyValue("reqHumanWarUrgentRice") * 2).toLocaleString() }}입니다.
+
+
+
+
+ 유저내정장에게 주는 금입니다. 이보다 적으면 포상합니다.
+
+
+
+
+ 유저내정장에게 주는 쌀입니다. 이보다 적으면 포상합니다.
+
+
+
+
+ NPC전투장에게 주는 금입니다. 이보다 적으면 포상합니다.
+
+ 0이면 기본 병종 4회({{ (defaultStatNPCMax * 100).toLocaleString() }}
+ * 4) 징병비를 기준으로 하며, 그 수치는 현재
+ {{ zeroPolicy.reqNPCWarGold.toLocaleString() }}입니다.
+
+
+
+
+ NPC전투장에게 주는 쌀입니다. 이보다 적으면 포상합니다.
+
+ 0이면 기본 병종으로
+ {{ (defaultStatNPCMax * 100).toLocaleString() }} * 4명 사살 가능한 쌀을 기준으로 하며, 그 수치는 현재
+ {{ zeroPolicy.reqNPCWarRice.toLocaleString() }}입니다.
+
+
+
+
+ NPC내정장에게 주는 금입니다. 이보다 5배 더 많다면 헌납합니다.
+
+ 0이면 30턴 내정 가능한 금을 기준으로 하며, 그 수치는 현재
+ {{ zeroPolicy.reqNPCDevelGold }}입니다.
+
+
+
+
+ NPC내정장에게 주는 쌀입니다. 이보다 5배 더 많다면 헌납합니다.
+
+
+
+
+ 연산결과가 이 단위보다 적다면 수행하지 않습니다.
+
+
+
+
+ 연산결과가 이 단위보다 크다면, 이 값에 맞춥니다.
+
+
+
+
+ 이보다 적을 때에는 징병을 시도합니다.
+
+
+
+
+ 도시의 인구가 이보다 낮으면 NPC는 도시에서 징병하지 않고 후방 워프합니다.
+ NPC의 최대 병력수보다 낮게 설정하면 제자리에서 정착장려를 합니다.
+
+
+
+
+ 전쟁 시 후방 발령, 후방 워프의 기준 인구입니다. 이보다 많다면 '충분하다'고 판단합니다.
+ NPC의 최대 병력수보다 낮게 설정하면 제자리에서 정착장려를 합니다.
+
+
+
+
+ 이 수치보다 같거나 높으면 NPC전투장으로 분류됩니다.
+
+
+
+
+ 훈련/사기진작 기준치입니다. 이보다 같거나 높으면 출병합니다.
+
+
+
+
+
+
+ 전투 부대는 작업중입니다(json양식: {부대번호:[시작도시번호(아국),도착도시번호(적군)],...})
+ 후방 징병 부대는 작업중입니다(json양식: [부대번호,...]) 내정 부대는 작업중입니다(json양식:
+ [부대번호,...])
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
NPC 사령턴 우선순위
+
+
+ 최근 설정:
+ {{ lastSetters.nation.setter ?? "-없음-" }}
+ ({{ lastSetters.nation.date ?? "설정 기록 없음" }})
+
+
+
+
+ 예턴이 없거나, 지정되어 있더라도 실패하면
+ 아래 순위에 따라 사령턴을 시도합니다.
+
+
+
+
+
+
비활성
+
+
+
<비활성화 항목들>
+
+
+
+
+ {{ element.id }}
+
+
+
+
+
+
+
활성
+
+
+
+
+ {{ element.id }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
NPC 일반턴 우선순위
+
+
+ 최근 설정:
+ {{ lastSetters.general.setter ?? "-없음-" }}
+ ({{ lastSetters.general.date ?? "설정 기록 없음" }})
+
+
+
+
+ 순위가 높은 것부터 시도합니다.
+ 아무것도 실행할 수 없으면 물자조달이나 인재탐색을 합니다.
+
+
+ {{ colValue.iActionMap[params.data[colValue.target] as string ].name }}
+
+
+ {{ colValue.converter(params.data)[0] }}
+
+
+ {{params.data[colValue.target] as string}}
+
+
+
+
+
+
+
+
diff --git a/hwe/ts/gridCellRenderer/SimpleTooltipCell.vue b/hwe/ts/gridCellRenderer/SimpleTooltipCell.vue
new file mode 100644
index 0000000..c3e6d4d
--- /dev/null
+++ b/hwe/ts/gridCellRenderer/SimpleTooltipCell.vue
@@ -0,0 +1,45 @@
+
+ ?
+ {{
+ params.iActionMap[props.params.value].name
+ }}
+ {{ displayValue }}
+ {{ displayValue }}
+
+
+
diff --git a/hwe/ts/hallOfFame.ts b/hwe/ts/hallOfFame.ts
new file mode 100644
index 0000000..b6a4f0e
--- /dev/null
+++ b/hwe/ts/hallOfFame.ts
@@ -0,0 +1,29 @@
+import "@scss/hallOfFame.scss";
+
+import $ from 'jquery';
+import { stringifyUrl } from 'query-string';
+import { initTooltip } from "@/legacy/initTooltip";
+import { auto500px } from './util/auto500px';
+import { htmlReady } from './util/htmlReady';
+import { insertCustomCSS } from './util/customCSS';
+
+auto500px();
+
+htmlReady(() => {
+ $('#by_scenario').on('change', function (e) {
+ e.preventDefault();
+
+ const $this = $(this);
+ const scenarioIdx = $this.val();
+ const seasonIdx = $(this).find('option:selected').data('season');
+
+ document.location.href = stringifyUrl({
+ url: 'a_hallOfFame.php',
+ query: {
+ scenarioIdx: scenarioIdx, seasonIdx: seasonIdx
+ }
+ });
+ })
+ initTooltip();
+ insertCustomCSS();
+});
\ No newline at end of file
diff --git a/hwe/ts/helpTexts.ts b/hwe/ts/helpTexts.ts
new file mode 100644
index 0000000..290434d
--- /dev/null
+++ b/hwe/ts/helpTexts.ts
@@ -0,0 +1,70 @@
+import type { NPCChiefActions, NPCGeneralActions } from "@/defs";
+
+export const NPCPriorityBtnHelpMessage: {
+ [v in NPCChiefActions | NPCGeneralActions]: string;
+} &
+ Record = {
+ 불가침제의:
+ "군주가 NPC이고, 타국에서 원조를 받았을 때, 세입(금/쌀) 대비 원조량에 따라 불가침제의를 합니다.",
+ 선전포고:
+ "군주가 NPC이고, 전쟁중이 아닐 때, 주변국중 하나를 골라 선포합니다.
선포 시점은 다음을 참고합니다. - 인구율 - 도시내정률 - NPC전투장권장 금 충족률 - NPC전투장권장 쌀 충족률
국력이 낮은 국가를 조금 더 선호합니다.",
+ 천도: "인구가 많은 곳을 찾아 천도를 시도합니다. 영토의 가운데를 선호합니다.
도시 인구가 충분하다면, 굳이 천도하지는 않습니다.",
+ 유저장긴급포상:
+ "금/쌀이 부족한 유저전투장에게 긴급하게 포상합니다. 국고가 권장량보다 적어지더라도 시도합니다.",
+ 부대전방발령:
+ "(작동하지 않음) 전투 부대를 접경으로 발령합니다. 수도->시작점->도착점 경로를 따릅니다.",
+ 유저장구출발령:
+ "아군 영토에 있지 않은 유저장을 아군 영토로 발령합니다. 곧 집합하는 부대에 탑승한 경우는 제외합니다.",
+ 유저장후방발령:
+ "유저전투장 중에 - 병력이 충분하지 않고, - 도시의 인구가 제자리 징병할 수 있을 정도로 충분하지 않고, - 부대에 탑승하지 않았다면, 인구가 충분한 후방도시로 발령합니다.",
+ 부대유저장후방발령:
+ "접경에 위치한 부대에 탑승한 유저전투장 중에, - 병력이 충분하지 않고, - 첫 턴이 징병턴이며, - 부대장 집합 턴 사이라면, 인구가 충분한 후방도시로 발령합니다.
부대장의 위치와 유저장의 위치가 다르다면 발령하지 않습니다.",
+ 유저장전방발령:
+ "후방에 있는 유저장이 - 병력을 가지고 있으며, - 곧 훈련/사기진작이 완료될 것 같으면, 전방으로 발령합니다.
도시 관직이 많이 임명된 도시를 선호합니다.",
+ 유저장포상:
+ "금/쌀이 부족한 유저장에게 포상합니다. 유저전투장과 유저내정장은 각각 기준을 따릅니다. 국고 권장량을 가급적 지킵니다.",
+ 부대후방발령:
+ "(작동하지 않음) 후방 부대가 위치한 도시의 인구가 충분하지 않을 경우, 인구가 충분한 도시로 발령합니다.",
+ 부대구출발령:
+ "전투 부대, 후방 부대가 아닌 부대가 아군 영토에 있지 않을 때, 전방 도시 중 하나를 골라 발령합니다.",
+ NPC긴급포상:
+ "금/쌀이 부족한 NPC전투장에게 긴급하게 포상합니다. 국고가 권장량보다 '약간' 적어지더라도 시도합니다.",
+ NPC구출발령: "아군 영토에 있지 않은 NPC장을 아군 영토로 발령합니다.",
+ NPC후방발령:
+ "NPC전투장 중에 - 병력이 충분하지 않고, - 도시의 인구가 제자리 징병할 수 있을 정도로 충분하지 않고, - 부대에 탑승하지 않았다면, 인구가 충분한 후방도시로 발령합니다.",
+ NPC포상:
+ "금/쌀이 부족한 NPC에게 포상합니다. NPC전투장과 NPC내정장은 각각 기준을 따릅니다. 국고 권장량을 가급적 지킵니다.",
+ NPC전방발령:
+ "후방에 있는 유저장이 - 병력을 가지고 있으며, - 곧 훈련/사기진작이 완료될 것 같으면, 전방으로 발령합니다.
도시 관직이 많이 임명된 도시를 선호합니다.",
+ 유저장내정발령:
+ "내정중인 유저장이 위치한 도시의 내정률이 95% 이상이면 개발되지 않은 도시로 발령합니다.",
+ NPC내정발령:
+ "내정중인 NPC장이 위치한 도시의 내정률이 95% 이상이면 개발되지 않은 도시로 발령합니다.",
+ NPC몰수:
+ "국고가 부족하다면 NPC에게서 몰수합니다. 내정NPC장은 국고가 부족하지 않아도 몰수합니다.",
+
+ NPC사망대비:
+ "NPC의 사망까지 5턴 이내인 경우, 헌납합니다. 헌납할 금쌀이 없다면 물자조달을 수행합니다.",
+ 귀환: "아국 도시에 있지 않다면 귀환합니다.",
+ 금쌀구매:
+ "전쟁 중에 금쌀의 비율이 크게 차이난다면 금쌀을 거래하여 비슷하게 맞춥니다. 금쌀 비율이 적절하는지 판단하는데 살상률을 포함합니다. NPC는 상인이 없어도 금쌀을 구매할 수 있습니다.
또는 금쌀 한쪽이 지나치게 적은 경우에는 내정 중에도 금쌀을 거래합니다.",
+ 출병: "충분한 병력과 충분한 훈련/사기를 가지고 있는 경우 출병합니다. 접경이 여럿인 경우 무작위로 선택합니다.
타국과 전쟁중인 경우 공백지로는 출병하지 않습니다.",
+ 긴급내정:
+ "전쟁중에 민심이 70 미만이거나, 인구가 제자리 징병이 가능하지 않을 정도로 적을 경우, 일정확률로 주민선정과 정착장려를 수행합니다.
통솔이 높을 수록 수행할 확률이 높습니다.",
+ 전투준비:
+ "충분한 병력을 가지고 있지만 훈련과 사기가 부족한 경우 훈련과 사기진작을 수행합니다.",
+ 전방워프: "전투장이 충분한 병력을 가지고 있다면 전방으로 이동합니다.",
+ NPC헌납:
+ "국고가 부족한데 NPC전쟁장이 충분한 금쌀(권장량 대비 1.5배)을 가지고 있다면 일부를 헌납합니다. NPC내정장은 국고가 넉넉하더라도 충분한 금쌀을 가지고 있다면 권장량만 유지하고 헌납합니다.",
+ 징병: "전쟁 중 병력을 소진하였다면 재 징병합니다.
기존에 사용한 병종군 중에서 사용가능한 병종을 랜덤하게 선택합니다. 고급 병종을 선택할 확률이 조금 더 높습니다.
NPC의 경우 도시의 인구가 충분하지 않다면 징병을 할 확률이 감소합니다.
유저장은 최대한 고급병종을 유지하며, 유저장 모병이 허용되는 경우 모병을 3회할 수 있다면 모병합니다.",
+ 후방워프:
+ "전쟁 중 병력을 소진하였는데 도시의 인구가 충분하지 않다면, 인구가 많은 도시로 이동합니다.",
+ 전쟁내정:
+ "전쟁 중 수행하는 내정입니다. 정착장려, 기술연구의 확률이 좀 더 높고, 치안강화, 농지개간, 상업투자의 확률이 낮습니다.
내정이 가능하다 하더라도 전시임을 고려해, 30% 확률로 다른 턴을 수행합니다.",
+ 소집해제:
+ "전쟁 중이 아닌 데 병력이 남아있는 경우, 3/4 확률로 소집해제합니다.",
+ 일반내정:
+ "도시에서 내정을 수행합니다. 낮은 내정일 수록 수행할 확률이 높습니다. 기술 연구는 1등급 이상 뒤쳐지지 않도록 노력합니다.",
+ 내정워프:
+ "도시에서 더이상 내정을 수행할 수 없는 경우, 일정확률로 내정이 부족한 다른 도시로 이동합니다.",
+};
diff --git a/hwe/ts/history.ts b/hwe/ts/history.ts
new file mode 100644
index 0000000..2e2a84a
--- /dev/null
+++ b/hwe/ts/history.ts
@@ -0,0 +1,44 @@
+import $ from 'jquery';
+import '@/map';
+import { joinYearMonth } from './util/joinYearMonth';
+import { parseYearMonth } from './util/parseYearMonth';
+
+declare const staticValues: {
+ startYear:number;
+ startMonth:number;
+
+ lastYear: number;
+ lastMonth: number;
+
+ selectYear: number;
+ selectMonth: number;
+}
+
+
+
+$(function ($) {
+ let currYear = staticValues.startYear;
+ let currMonth = staticValues.startMonth;
+ const selectDate = joinYearMonth(staticValues.selectYear, staticValues.selectMonth);
+
+ const $yearMonth = $('#yearmonth');
+ let $elements = $();
+
+ const endDate = joinYearMonth(staticValues.lastYear, staticValues.lastMonth) + 1;//연감 마지막 + 1(현재)
+ let currDate = joinYearMonth(currYear, currMonth);
+ while (currDate <= endDate) {
+ let sel = '';
+ if (currDate == selectDate) {
+ sel = 'selected="selected"';
+ }
+
+ const more = currDate == endDate? ' (현재)':'';
+ const option = $(``);
+ $elements = $elements.add(option);
+
+ currDate += 1;
+ [currYear, currMonth] = parseYearMonth(currDate);
+ }
+ $yearMonth.empty();
+ $yearMonth.append($elements);
+});
\ No newline at end of file
diff --git a/hwe/ts/install.ts b/hwe/ts/install.ts
new file mode 100644
index 0000000..c5a1617
--- /dev/null
+++ b/hwe/ts/install.ts
@@ -0,0 +1,269 @@
+import $ from "jquery";
+import 'bootstrap';
+import { setAxiosXMLHttpRequest } from "@util/setAxiosXMLHttpRequest";
+import axios from "axios";
+import type { InvalidResponse } from "@/defs";
+import { JQValidateForm, type NamedRules } from "@util/jqValidateForm";
+import { convertFormData } from "@util/convertFormData";
+import { exportWindow } from "@util/exportWindow";
+
+type ResponseScenarioItem = {
+ year?: number,
+ title: string,
+ npc_cnt: number,
+ npcEx_cnt: number,
+ npcNeutral_cnt: number,
+ nation: Record
+}
+
+type ExpandedScenarioItem = ResponseScenarioItem & {
+ category: string,
+ idx: string,
+ year: number,
+}
+
+type ResponseScenario = {
+ result: true,
+ scenario: Record
+}
+
+async function loadScenarios() {
+
+ let result: InvalidResponse | ResponseScenario;
+ try {
+ const response = await axios({
+ url: 'j_load_scenarios.php',
+ method: 'post',
+ responseType: 'json'
+ });
+ result = response.data;
+ }
+ catch (e) {
+ alert(`시나리오를 불러오는데 실패했습니다.: ${e}`);
+ return;
+ }
+
+ if (!result.result) {
+ alert(result.reason);
+ return;
+ }
+
+ const list: Record> = {};
+ const pat = /【(.*?)[0-9\-_.a-zA-Z]*】/;
+
+ for (const [idx, value] of Object.entries(result.scenario)) {
+ const title = value.title || "-";
+ const categoryRes = pat.exec(title);
+ const category = categoryRes ? categoryRes[1] : '-';
+
+ if (!(category in list)) {
+ list[category] = {};
+ }
+
+ list[category][idx] = {
+ ...value,
+ title: title,
+ category: category,
+ idx: idx,
+ year: value.year ?? 180
+ };
+ }
+
+
+ const $select = $('#scenario_sel');
+ for (const [category, items] of Object.entries(list)) {
+ const $optgroup = $('