파일 이식(0.31.1)
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<div v-for="(cityList, distance) in distanceList" :key="distance">
|
||||
{{ distance }}칸 떨어진 도시:
|
||||
<template v-for="(cityID, key) in cityList" :key="key">
|
||||
<template v-if="key !== 0"> , </template>
|
||||
<a
|
||||
:style="{ color: colorMap[distance as keyof typeof colorMap] ?? undefined, textDecoration:'underline' }"
|
||||
@click="$emit('selected', cityID)"
|
||||
>{{ citiesMap.get(cityID)?.name }}</a
|
||||
>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, type PropType } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
distanceList: {
|
||||
type: Object as PropType<Record<number, number[]>>,
|
||||
required: true,
|
||||
},
|
||||
citiesMap: {
|
||||
type: Object as PropType<Map<number, { name: string }>>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: ["selected"],
|
||||
data() {
|
||||
return {
|
||||
colorMap: {
|
||||
1: "magenta",
|
||||
2: "orange",
|
||||
3: "yellow",
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<div class="crewTypeItem crewTypeSubGrid text-center s-border-b">
|
||||
<div
|
||||
class="crewTypeImg"
|
||||
:style="{
|
||||
background: '#222222 no-repeat center',
|
||||
backgroundImage: `url('${crewType.img}')`,
|
||||
backgroundSize: '64px',
|
||||
outline: 'solid 1px gray',
|
||||
}"
|
||||
/>
|
||||
<div
|
||||
:style="{
|
||||
backgroundColor: crewType.notAvailable ? 'red' : crewType.reqTech == 0 ? 'green' : 'limegreen',
|
||||
height: '100%',
|
||||
}"
|
||||
class="d-grid crewTypeName"
|
||||
>
|
||||
<div style="margin: auto">{{ crewType.name }}</div>
|
||||
</div>
|
||||
<div>{{ crewType.attack }}</div>
|
||||
<div>{{ crewType.defence }}</div>
|
||||
<div>{{ crewType.speed }}</div>
|
||||
<div>{{ crewType.avoid }}</div>
|
||||
<div>{{ crewType.baseCost.toFixed(1) }}</div>
|
||||
<div>{{ crewType.baseRice.toFixed(1) }}</div>
|
||||
<div class="crewTypePanel">
|
||||
<b-button-group>
|
||||
<b-button class="py-1" variant="dark" @click="beHalf">절반</b-button>
|
||||
<b-button class="py-1" variant="dark" @click="beFilled">채우기</b-button>
|
||||
<b-button class="py-1" variant="dark" @click="beFull">가득</b-button>
|
||||
</b-button-group>
|
||||
<div class="row">
|
||||
<div class="col mx-2">
|
||||
<div class="input-group my-0">
|
||||
<span class="input-group-text py-1">병력</span>
|
||||
<input v-model.number="amount" type="number" class="form-control py-1 f_tnum px-0 text-end" min="1" />
|
||||
<span class="input-group-text py-1 f_tnum">00명</span>
|
||||
<span
|
||||
class="input-group-text py-1 f_tnum"
|
||||
style="text-align: right; min-width: 10ch; color: #303030; background-color: #ddd"
|
||||
>
|
||||
<div style="margin-left: auto">
|
||||
{{ Math.ceil(amount * crewType.baseCost * goldCoeff).toLocaleString() }}금
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="crewTypeBtn d-grid">
|
||||
<b-button variant="primary" @click="doSubmit">{{ commandName }}</b-button>
|
||||
</div>
|
||||
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
<div class="crewTypeInfo text-start" v-html="crewType.info.join('<br>')" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from "vue";
|
||||
import VueTypes from "vue-types";
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
crewType: VueTypes.object.isRequired,
|
||||
leadership: VueTypes.number.isRequired,
|
||||
commandName: VueTypes.string.isRequired,
|
||||
currentCrewType: VueTypes.number.def(-1),
|
||||
crew: VueTypes.number.def(0),
|
||||
goldCoeff: VueTypes.number.isRequired,
|
||||
},
|
||||
emits: ["submitOutput", "update:amount"],
|
||||
setup(props, { emit }) {
|
||||
const amount = ref(0);
|
||||
|
||||
function beHalf() {
|
||||
amount.value = Math.ceil(props.leadership * 0.5);
|
||||
}
|
||||
|
||||
function beFilled() {
|
||||
if (props.crewType.id == props.currentCrewType) {
|
||||
amount.value = Math.max(1, props.leadership - Math.floor(props.crew / 100));
|
||||
} else {
|
||||
amount.value = props.leadership;
|
||||
}
|
||||
}
|
||||
|
||||
function beFull() {
|
||||
amount.value = Math.floor(props.leadership * 1.2);
|
||||
}
|
||||
|
||||
function doSubmit(e: Event) {
|
||||
emit("submitOutput", e, amount.value, props.crewType.id);
|
||||
}
|
||||
|
||||
beFilled();
|
||||
|
||||
return {
|
||||
amount,
|
||||
beHalf,
|
||||
beFilled,
|
||||
beFull,
|
||||
doSubmit,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
amount(val: number) {
|
||||
this.$emit("update:amount", val);
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<TopBackBar :title="commandName" />
|
||||
<div v-if="!available건국" class="bg0">더 이상 건국은 불가능합니다.</div>
|
||||
<div v-else class="bg0">
|
||||
<div>현재 도시에서 나라를 세웁니다. 중, 소도시에서만 가능합니다.</div>
|
||||
<ul>
|
||||
<li v-for="nationType in nationTypes" :key="nationType.type" class="row">
|
||||
<div class="col-2 col-md-1">- {{ nationType.name }}</div>
|
||||
<div class="col-4 col-md-2">
|
||||
: <span style="color: cyan">{{ nationType.pros }}</span
|
||||
>,
|
||||
</div>
|
||||
<div class="col-4 col-md-2">
|
||||
<span style="color: magenta">{{ nationType.cons }}</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="row">
|
||||
<div class="col-4 col-md-2">국명 : <b-form-input v-model="destNationName" maxlength="18" /></div>
|
||||
<div class="col-3 col-md-2">색상 : <ColorSelect v-model="selectedColorID" :colors="colors" /></div>
|
||||
<div class="col-3 col-md-2">
|
||||
<label>성향 :</label>
|
||||
<b-form-select v-model="selectedNationType" :options="nationTypesOption" />
|
||||
</div>
|
||||
|
||||
<div class="col-2 col-md-2 d-grid">
|
||||
<b-button @click="submit">
|
||||
{{ commandName }}
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<BottomBar :title="commandName" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import ColorSelect from "@/processing/SelectColor.vue";
|
||||
import { defineComponent, ref } from "vue";
|
||||
import { unwrap } from "@/util/unwrap";
|
||||
import type { Args } from "@/processing/args";
|
||||
import TopBackBar from "@/components/TopBackBar.vue";
|
||||
import BottomBar from "@/components/BottomBar.vue";
|
||||
import type { procNationTypeList } from "../processingRes";
|
||||
|
||||
declare const commandName: string;
|
||||
|
||||
declare const procRes: {
|
||||
available건국: boolean;
|
||||
colors: string[];
|
||||
nationTypes: procNationTypeList;
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
ColorSelect,
|
||||
TopBackBar,
|
||||
BottomBar,
|
||||
},
|
||||
setup() {
|
||||
const destNationName = ref("");
|
||||
const selectedColorID = ref(0);
|
||||
|
||||
const selectedNationType = ref(Object.values(procRes.nationTypes)[0].type);
|
||||
|
||||
async function submit(e: Event) {
|
||||
const event = new CustomEvent<Args>("customSubmit", {
|
||||
detail: {
|
||||
colorType: selectedColorID.value,
|
||||
nationName: destNationName.value,
|
||||
nationType: selectedNationType.value,
|
||||
},
|
||||
});
|
||||
unwrap(e.target).dispatchEvent(event);
|
||||
}
|
||||
|
||||
const nationTypesOption: { html: string; value: string }[] = [];
|
||||
for (const nationType of Object.values(procRes.nationTypes)) {
|
||||
nationTypesOption.push({
|
||||
html: nationType.name,
|
||||
value: nationType.type,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
available건국: procRes.available건국,
|
||||
selectedColorID,
|
||||
selectedNationType,
|
||||
colors: procRes.colors,
|
||||
nationTypes: procRes.nationTypes,
|
||||
nationTypesOption,
|
||||
destNationName,
|
||||
commandName,
|
||||
submit,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<TopBackBar :title="commandName" />
|
||||
<div class="bg0">
|
||||
<div>자신의 군량을 사거나 팝니다.<br /></div>
|
||||
<div class="row">
|
||||
<div class="col-2 col-md-1">
|
||||
군량을 :
|
||||
<b-button-group>
|
||||
<b-button :pressed="buyRice" @click="buyRice = true"> 삼 </b-button>
|
||||
<b-button :pressed="!buyRice" @click="buyRice = false"> 팜 </b-button>
|
||||
</b-button-group>
|
||||
</div>
|
||||
<div class="col-7 col-md-4">
|
||||
금액 :
|
||||
<SelectAmount v-model="amount" :amountGuide="amountGuide" :maxAmount="maxAmount" :minAmount="minAmount" />
|
||||
</div>
|
||||
<div class="col-3 col-md-2 d-grid">
|
||||
<b-button variant="primary" @click="submit">
|
||||
{{ commandName }}
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<BottomBar :title="commandName" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import SelectAmount from "@/processing/SelectAmount.vue";
|
||||
import { defineComponent, ref } from "vue";
|
||||
import { unwrap } from "@/util/unwrap";
|
||||
import type { Args } from "@/processing/args";
|
||||
import TopBackBar from "@/components/TopBackBar.vue";
|
||||
import BottomBar from "@/components/BottomBar.vue";
|
||||
declare const commandName: string;
|
||||
|
||||
declare const procRes: {
|
||||
minAmount: number;
|
||||
maxAmount: number;
|
||||
amountGuide: number[];
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
SelectAmount,
|
||||
TopBackBar,
|
||||
BottomBar,
|
||||
},
|
||||
setup() {
|
||||
const amount = ref(1000);
|
||||
const buyRice = ref(true);
|
||||
|
||||
async function submit(e: Event) {
|
||||
const event = new CustomEvent<Args>("customSubmit", {
|
||||
detail: {
|
||||
amount: amount.value,
|
||||
buyRice: buyRice.value,
|
||||
},
|
||||
});
|
||||
unwrap(e.target).dispatchEvent(event);
|
||||
}
|
||||
|
||||
return {
|
||||
buyRice,
|
||||
amount,
|
||||
minAmount: ref(procRes.minAmount),
|
||||
maxAmount: ref(procRes.maxAmount),
|
||||
amountGuide: procRes.amountGuide,
|
||||
commandName,
|
||||
submit,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<TopBackBar v-model:searchable="searchable" :title="commandName" />
|
||||
<div class="bg0">
|
||||
<div>
|
||||
재야나 타국의 장수를 등용합니다.<br />
|
||||
서신은 개인 메세지로 전달됩니다.<br />
|
||||
등용할 장수를 목록에서 선택하세요.<br />
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-6">
|
||||
장수 :
|
||||
<SelectGeneral
|
||||
v-model="selectedGeneralID"
|
||||
:generals="generalList"
|
||||
:groupByNation="nationList"
|
||||
:textHelper="textHelpGeneral"
|
||||
:searchable="searchable"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-4 col-md-2 d-grid">
|
||||
<b-button variant="primary" @click="submit">
|
||||
{{ commandName }}
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<BottomBar :title="commandName" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import SelectGeneral from "@/processing/SelectGeneral.vue";
|
||||
import { defineComponent, ref } from "vue";
|
||||
import { unwrap } from "@/util/unwrap";
|
||||
import type { Args } from "@/processing/args";
|
||||
import TopBackBar from "@/components/TopBackBar.vue";
|
||||
import BottomBar from "@/components/BottomBar.vue";
|
||||
import {
|
||||
convertGeneralList,
|
||||
getProcSearchable,
|
||||
type procGeneralItem,
|
||||
type procGeneralKey,
|
||||
type procGeneralRawItemList,
|
||||
type procNationItem,
|
||||
type procNationList,
|
||||
} from "../processingRes";
|
||||
import { getNpcColor } from "@/common_legacy";
|
||||
declare const commandName: string;
|
||||
|
||||
declare const procRes: {
|
||||
generals: procGeneralRawItemList;
|
||||
generalsKey: procGeneralKey[];
|
||||
nationList: procNationList;
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
SelectGeneral,
|
||||
TopBackBar,
|
||||
BottomBar,
|
||||
},
|
||||
setup() {
|
||||
const generalList = convertGeneralList(procRes.generalsKey, procRes.generals);
|
||||
|
||||
const selectedGeneralID = ref(generalList[0].no);
|
||||
|
||||
function textHelpGeneral(gen: procGeneralItem): string {
|
||||
const nameColor = getNpcColor(gen.npc);
|
||||
const name = nameColor ? `<span style="color:${nameColor}">${gen.name}</span>` : gen.name;
|
||||
return name;
|
||||
}
|
||||
|
||||
async function submit(e: Event) {
|
||||
const event = new CustomEvent<Args>("customSubmit", {
|
||||
detail: {
|
||||
destGeneralID: selectedGeneralID.value,
|
||||
},
|
||||
});
|
||||
unwrap(e.target).dispatchEvent(event);
|
||||
}
|
||||
|
||||
const nationList = new Map<number, procNationItem>();
|
||||
for (const nationItem of procRes.nationList) {
|
||||
nationList.set(nationItem.id, nationItem);
|
||||
}
|
||||
|
||||
return {
|
||||
searchable: getProcSearchable(),
|
||||
selectedGeneralID,
|
||||
generalList,
|
||||
nationList,
|
||||
commandName,
|
||||
textHelpGeneral,
|
||||
submit,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<TopBackBar v-model:searchable="searchable" :title="commandName" />
|
||||
<div class="bg0">
|
||||
<div v-if="commandName == '등용'">
|
||||
재야나 타국의 장수를 등용합니다.<br />
|
||||
서신은 개인 메세지로 전달됩니다.<br />
|
||||
등용할 장수를 목록에서 선택하세요.<br />
|
||||
</div>
|
||||
<div v-if="commandName == '선양'">
|
||||
군주의 자리를 다른 장수에게 물려줍니다.<br />
|
||||
장수를 선택하세요.<br />
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-9 col-md-4">
|
||||
장수 :
|
||||
<SelectGeneral
|
||||
v-model="selectedGeneralID"
|
||||
:generals="generalList"
|
||||
:textHelper="textHelpGeneral"
|
||||
:searchable="searchable"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-3 col-md-2 d-grid">
|
||||
<b-button variant="primary" @click="submit">
|
||||
{{ commandName }}
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<BottomBar :title="commandName" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import SelectGeneral from "@/processing/SelectGeneral.vue";
|
||||
import { defineComponent, ref } from "vue";
|
||||
import { unwrap } from "@/util/unwrap";
|
||||
import type { Args } from "@/processing/args";
|
||||
import TopBackBar from "@/components/TopBackBar.vue";
|
||||
import BottomBar from "@/components/BottomBar.vue";
|
||||
import {
|
||||
convertGeneralList,
|
||||
getProcSearchable,
|
||||
type procGeneralItem,
|
||||
type procGeneralKey,
|
||||
type procGeneralRawItemList,
|
||||
} from "../processingRes";
|
||||
import { getNpcColor } from "@/common_legacy";
|
||||
declare const commandName: string;
|
||||
|
||||
declare const procRes: {
|
||||
generals: procGeneralRawItemList;
|
||||
generalsKey: procGeneralKey[];
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
SelectGeneral,
|
||||
TopBackBar,
|
||||
BottomBar,
|
||||
},
|
||||
setup() {
|
||||
const generalList = convertGeneralList(procRes.generalsKey, procRes.generals);
|
||||
|
||||
const selectedGeneralID = ref(generalList[0].no);
|
||||
|
||||
function textHelpGeneral(gen: procGeneralItem): string {
|
||||
const nameColor = getNpcColor(gen.npc);
|
||||
const name = nameColor ? `<span style="color:${nameColor}">${gen.name}</span>` : gen.name;
|
||||
return `${name} (${gen.leadership}/${gen.strength}/${gen.intel})`;
|
||||
}
|
||||
|
||||
async function submit(e: Event) {
|
||||
const event = new CustomEvent<Args>("customSubmit", {
|
||||
detail: {
|
||||
destGeneralID: selectedGeneralID.value,
|
||||
},
|
||||
});
|
||||
unwrap(e.target).dispatchEvent(event);
|
||||
}
|
||||
|
||||
return {
|
||||
searchable: getProcSearchable(),
|
||||
selectedGeneralID,
|
||||
generalList,
|
||||
commandName,
|
||||
textHelpGeneral,
|
||||
submit,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,234 @@
|
||||
<template>
|
||||
<TopBackBar :title="commandName" />
|
||||
<div class="bg0">
|
||||
<div>본인의 특정 병종 숙련을 40% 줄이고, 줄어든 숙련 중 9/10(90%p)를 다른 병종 숙련으로 전환합니다.</div>
|
||||
<div class="row">
|
||||
<div class="col-4 col-md-2">
|
||||
감소 대상 숙련 :
|
||||
<b-form-select v-model="srcArmTypeID">
|
||||
<b-form-select-option v-for="[armType, dexInfo] in dexFullInfo" :key="armType" :value="armType">
|
||||
{{ dexInfo.name }} (<span :style="{ color: dexInfo.currentInfo.color }">{{ dexInfo.currentInfo.name }}</span
|
||||
>)
|
||||
</b-form-select-option>
|
||||
</b-form-select>
|
||||
</div>
|
||||
<div class="col-4 col-md-2">
|
||||
전환 대상 숙련 :
|
||||
<b-form-select v-model="destArmTypeID">
|
||||
<b-form-select-option v-for="[armType, dexInfo] in dexFullInfo" :key="armType" :value="armType">
|
||||
{{ dexInfo.name }} (<span :style="{ color: dexInfo.currentInfo.color }">{{ dexInfo.currentInfo.name }}</span
|
||||
>)
|
||||
</b-form-select-option>
|
||||
</b-form-select>
|
||||
</div>
|
||||
<div class="col-4 col-md-2 d-grid">
|
||||
<b-button @click="submit">
|
||||
{{ commandName }}
|
||||
</b-button>
|
||||
</div>
|
||||
<div
|
||||
:style="{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: '3ch 1ch 2ch 10ch 1ch 3ch 1ch 2ch 10ch 1ch',
|
||||
}"
|
||||
>
|
||||
<div>{{ unwrap(dexFullInfo.get(srcArmTypeID)).name }}</div>
|
||||
<div class="text-end">[</div>
|
||||
<div :style="`color:${unwrap(dexFullInfo.get(srcArmTypeID)).currentInfo.color}`">
|
||||
{{ unwrap(dexFullInfo.get(srcArmTypeID)).currentInfo.name }}
|
||||
</div>
|
||||
<div class="f_tnum text-end">
|
||||
{{ convNumberFormat(unwrap(dexFullInfo.get(srcArmTypeID)).currentInfo.amount) }}
|
||||
</div>
|
||||
<div>]</div>
|
||||
<div class="text-center">→</div>
|
||||
<div class="text-end">[</div>
|
||||
<div :style="`color:${unwrap(dexFullInfo.get(srcArmTypeID)).decreasedInfo.color}`">
|
||||
{{ unwrap(dexFullInfo.get(srcArmTypeID)).decreasedInfo.name }}
|
||||
</div>
|
||||
<div class="f_tnum text-end">
|
||||
{{ convNumberFormat(unwrap(dexFullInfo.get(srcArmTypeID)).decreasedInfo.amount) }}
|
||||
</div>
|
||||
<div>]</div>
|
||||
</div>
|
||||
<div
|
||||
:style="{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: '3ch 1ch 2ch 10ch 1ch 3ch 1ch 2ch 10ch 1ch',
|
||||
}"
|
||||
>
|
||||
<div>{{ unwrap(dexFullInfo.get(destArmTypeID)).name }}</div>
|
||||
<div class="text-end">[</div>
|
||||
<template v-if="srcArmTypeID == destArmTypeID">
|
||||
<div :style="`color:${unwrap(dexFullInfo.get(destArmTypeID)).decreasedInfo.color}`">
|
||||
{{ unwrap(dexFullInfo.get(destArmTypeID)).decreasedInfo.name }}
|
||||
</div>
|
||||
<div class="f_tnum text-end">
|
||||
{{ convNumberFormat(unwrap(dexFullInfo.get(destArmTypeID)).decreasedInfo.amount) }}
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div :style="`color:${unwrap(dexFullInfo.get(destArmTypeID)).currentInfo.color}`">
|
||||
{{ unwrap(dexFullInfo.get(destArmTypeID)).currentInfo.name }}
|
||||
</div>
|
||||
<div class="f_tnum text-end">
|
||||
{{ convNumberFormat(unwrap(dexFullInfo.get(destArmTypeID)).currentInfo.amount) }}
|
||||
</div>
|
||||
</template>
|
||||
<div>]</div>
|
||||
<div class="text-center">→</div>
|
||||
<div class="text-end">[</div>
|
||||
<div :style="`color:${unwrap(unwrap(dexFullInfo.get(destArmTypeID)).afterInfo.get(srcArmTypeID)).color}`">
|
||||
{{ unwrap(unwrap(dexFullInfo.get(destArmTypeID)).afterInfo.get(srcArmTypeID)).name }}
|
||||
</div>
|
||||
<div class="f_tnum text-end">
|
||||
{{ convNumberFormat(unwrap(unwrap(dexFullInfo.get(destArmTypeID)).afterInfo.get(srcArmTypeID)).amount) }}
|
||||
</div>
|
||||
<div>]</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<BottomBar :title="commandName" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from "vue";
|
||||
import { unwrap } from "@/util/unwrap";
|
||||
import type { Args } from "@/processing/args";
|
||||
import TopBackBar from "@/components/TopBackBar.vue";
|
||||
import BottomBar from "@/components/BottomBar.vue";
|
||||
|
||||
declare const commandName: string;
|
||||
|
||||
type dexInfo = {
|
||||
amount: number;
|
||||
color: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
declare const procRes: {
|
||||
ownDexList: {
|
||||
armType: number;
|
||||
name: string;
|
||||
amount: number;
|
||||
}[];
|
||||
dexLevelList: dexInfo[];
|
||||
decreaseCoeff: number;
|
||||
convertCoeff: number;
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
TopBackBar,
|
||||
BottomBar,
|
||||
},
|
||||
setup() {
|
||||
const srcArmTypeID = ref(procRes.ownDexList[0].armType);
|
||||
const destArmTypeID = ref(procRes.ownDexList[0].armType);
|
||||
|
||||
async function submit(e: Event) {
|
||||
const event = new CustomEvent<Args>("customSubmit", {
|
||||
detail: {
|
||||
srcArmType: srcArmTypeID.value,
|
||||
destArmType: destArmTypeID.value,
|
||||
},
|
||||
});
|
||||
unwrap(e.target).dispatchEvent(event);
|
||||
}
|
||||
|
||||
function getDexCall(dex: number): { color: string; name: string } {
|
||||
if (dex < 0) {
|
||||
throw `올바르지 않은 수치: ${dex}`;
|
||||
}
|
||||
|
||||
let color = "";
|
||||
let name = "";
|
||||
for (const nextDexLevel of procRes.dexLevelList) {
|
||||
if (dex < nextDexLevel.amount) {
|
||||
break;
|
||||
}
|
||||
color = nextDexLevel.color;
|
||||
name = nextDexLevel.name;
|
||||
}
|
||||
|
||||
return {
|
||||
color,
|
||||
name,
|
||||
};
|
||||
}
|
||||
|
||||
const dexFullInfo = new Map<
|
||||
number,
|
||||
{
|
||||
armType: number;
|
||||
name: string;
|
||||
amount: number;
|
||||
decresedAmount: number;
|
||||
currentInfo: dexInfo;
|
||||
decreasedInfo: dexInfo;
|
||||
afterInfo: Map<number, dexInfo>;
|
||||
}
|
||||
>();
|
||||
|
||||
for (const dexItem of procRes.ownDexList) {
|
||||
const amount = dexItem.amount;
|
||||
const currentInfo = { ...getDexCall(amount), amount };
|
||||
|
||||
const decresedAmount = amount * procRes.decreaseCoeff;
|
||||
const decresedAfterAmount = amount - decresedAmount;
|
||||
const decreasedInfo = {
|
||||
...getDexCall(decresedAfterAmount),
|
||||
amount: decresedAfterAmount,
|
||||
};
|
||||
|
||||
dexFullInfo.set(dexItem.armType, {
|
||||
...dexItem,
|
||||
decresedAmount,
|
||||
currentInfo,
|
||||
decreasedInfo,
|
||||
afterInfo: new Map(),
|
||||
});
|
||||
}
|
||||
|
||||
for (const [armType, dexItem] of dexFullInfo.entries()) {
|
||||
for (const [fromArmType, fromDexItem] of dexFullInfo.entries()) {
|
||||
let afterAmount = fromDexItem.decresedAmount * procRes.convertCoeff;
|
||||
if (armType != fromArmType) {
|
||||
afterAmount += dexItem.amount;
|
||||
} else {
|
||||
afterAmount += dexItem.decresedAmount;
|
||||
}
|
||||
|
||||
dexItem.afterInfo.set(fromArmType, {
|
||||
amount: afterAmount,
|
||||
...getDexCall(afterAmount),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function convDexFormat(value: dexInfo): string {
|
||||
const amount = convNumberFormat(value.amount);
|
||||
return `<span class="f_tnum" style="color:${value.color}">${value.name}</span>,${"\xa0".repeat(
|
||||
Math.max(0, 3 - value.name.length)
|
||||
)} ${amount}`;
|
||||
}
|
||||
|
||||
function convNumberFormat(value: number): string {
|
||||
return Math.floor(value).toLocaleString();
|
||||
}
|
||||
|
||||
return {
|
||||
unwrap,
|
||||
...procRes,
|
||||
srcArmTypeID,
|
||||
destArmTypeID,
|
||||
dexFullInfo,
|
||||
getDexCall,
|
||||
commandName,
|
||||
submit,
|
||||
convDexFormat,
|
||||
convNumberFormat,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,184 @@
|
||||
<template>
|
||||
<TopBackBar v-model:searchable="searchable" :title="commandName" />
|
||||
<div class="bg0">
|
||||
<div>
|
||||
국가에 임관합니다.
|
||||
<br>
|
||||
이미 임관/등용되었던 국가는 다시 임관할 수 없습니다.
|
||||
<br>
|
||||
바로 군주의 위치로 이동합니다.
|
||||
<br>
|
||||
임관할 국가를 목록에서 선택하세요.
|
||||
<br>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 col-md-3">
|
||||
국가 :
|
||||
<SelectNation v-model="selectedNationID" :nations="nationList" :searchable="searchable" />
|
||||
</div>
|
||||
<div class="col-4 col-md-2 d-grid">
|
||||
<b-button @click="submit">
|
||||
{{ commandName }}
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="nation-list">
|
||||
<div class="nation-header nation-row bg1 center">
|
||||
<div>국가명</div>
|
||||
<div>임관권유문</div>
|
||||
<div class="zoom-toggle d-grid">
|
||||
<b-button
|
||||
v-model="toggleZoom"
|
||||
:pressed="toggleZoom"
|
||||
:variant="toggleZoom ? 'info' : 'secondary'"
|
||||
@click="toggleZoom = !toggleZoom"
|
||||
>
|
||||
{{ toggleZoom ? "작게 보기" : "크게 보기" }}
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-for="[, nation] in nationList"
|
||||
:key="nation.id"
|
||||
:class="['nation-row', 's-border-b', toggleZoom ? 'on-zoom' : 'on-fit']"
|
||||
@click="selectedNationID = nation.id"
|
||||
>
|
||||
<div
|
||||
:style="{
|
||||
backgroundColor: nation.color,
|
||||
color: isBrightColor(nation.color) ? 'black' : 'white',
|
||||
fontSize: '1.3em',
|
||||
}"
|
||||
class="d-grid"
|
||||
>
|
||||
<div class="align-self-center center">{{ nation.name }}</div>
|
||||
</div>
|
||||
<div class="nation-scout-plate align-self-center">
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
<div class="nation-scout-msg" v-html="nation.scoutMsg" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<BottomBar :title="commandName" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import SelectNation from "@/processing/SelectNation.vue";
|
||||
import { defineComponent, ref } from "vue";
|
||||
import { unwrap } from "@/util/unwrap";
|
||||
import type { Args } from "@/processing/args";
|
||||
import TopBackBar from "@/components/TopBackBar.vue";
|
||||
import BottomBar from "@/components/BottomBar.vue";
|
||||
import { type procNationItem, type procNationList, getProcSearchable } from "../processingRes";
|
||||
import { isBrightColor } from "@/util/isBrightColor";
|
||||
declare const commandName: string;
|
||||
|
||||
declare const procRes: {
|
||||
nationList: procNationList;
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
SelectNation,
|
||||
TopBackBar,
|
||||
BottomBar,
|
||||
},
|
||||
setup() {
|
||||
const nationList = new Map<number, procNationItem>();
|
||||
for (const nationItem of procRes.nationList) {
|
||||
nationList.set(nationItem.id, nationItem);
|
||||
}
|
||||
|
||||
const toggleZoom = ref(true);
|
||||
const selectedNationID = ref(procRes.nationList[0].id);
|
||||
|
||||
function selectedNation(nationID: number) {
|
||||
selectedNationID.value = nationID;
|
||||
}
|
||||
|
||||
async function submit(e: Event) {
|
||||
const event = new CustomEvent<Args>("customSubmit", {
|
||||
detail: {
|
||||
destNationID: selectedNationID.value,
|
||||
},
|
||||
});
|
||||
unwrap(e.target).dispatchEvent(event);
|
||||
}
|
||||
|
||||
return {
|
||||
searchable: getProcSearchable(),
|
||||
nationList: ref(nationList),
|
||||
selectedNationID,
|
||||
commandName,
|
||||
toggleZoom,
|
||||
isBrightColor,
|
||||
selectedNation,
|
||||
submit,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "@scss/common/break_500px.scss";
|
||||
|
||||
@include media-1000px {
|
||||
.nation-list .nation-row {
|
||||
display: grid;
|
||||
grid-template-columns: 130px 870px;
|
||||
}
|
||||
|
||||
.zoom-toggle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.zoom-toggle > * {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-500px {
|
||||
.nation-list .nation-row.nation-header {
|
||||
display: grid;
|
||||
grid-template-columns: 3fr 1fr;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
|
||||
.zoom-toggle {
|
||||
grid-column: 2/3;
|
||||
grid-row: 1/3;
|
||||
}
|
||||
}
|
||||
|
||||
.nation-list .nation-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr minmax(1fr, calc(200px * 500 / 870));
|
||||
}
|
||||
|
||||
.on-fit {
|
||||
.nation-scout-plate {
|
||||
max-height: calc(200px * 500 / 870);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.nation-scout-msg {
|
||||
width: 870px;
|
||||
transform-origin: 0px 0px;
|
||||
transform: scale(calc(500 / 870));
|
||||
}
|
||||
}
|
||||
|
||||
.on-zoom {
|
||||
.nation-scout-plate {
|
||||
max-height: 200px;
|
||||
overflow-y: hidden;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.nation-scout-msg {
|
||||
max-width: 870px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,205 @@
|
||||
<template>
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<TopBackBar v-model:searchable="searchable" :title="commandName" />
|
||||
<div class="bg0">
|
||||
<div>
|
||||
장비를 구입하거나 매각합니다.
|
||||
<br>
|
||||
현재 구입 불가능한 것은 <span style="color: red">붉은색</span>으로
|
||||
표시됩니다.
|
||||
<br>
|
||||
현재 도시 치안 : {{ citySecu.toLocaleString() }} 현재
|
||||
자금 : {{ gold.toLocaleString() }}
|
||||
<br>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-8 col-md-4">
|
||||
장비:
|
||||
<v-multiselect
|
||||
v-model="selectedItemObj"
|
||||
class="selectedItemObj"
|
||||
:allow-empty="false"
|
||||
:options="forFind"
|
||||
:group-select="false"
|
||||
group-values="values"
|
||||
group-label="category"
|
||||
label="searchText"
|
||||
track-by="simpleName"
|
||||
:show-labels="false"
|
||||
select-label="선택(엔터)"
|
||||
select-group-label
|
||||
selected-label="선택됨"
|
||||
deselect-label="해제(엔터)"
|
||||
deselect-group-label
|
||||
placeholder="아이템 선택"
|
||||
:max-height="400"
|
||||
:searchable="searchable"
|
||||
>
|
||||
<template #option="props">
|
||||
<div
|
||||
v-if="props.option.html"
|
||||
:style="{
|
||||
color: props.option.notAvailable ? 'red' : undefined,
|
||||
}"
|
||||
v-html="
|
||||
`${props.option.html} ${
|
||||
props.option.notAvailable ? '(불가)' : ''
|
||||
}`
|
||||
"
|
||||
/>
|
||||
<div
|
||||
v-else-if="props.option.simpleName"
|
||||
:style="{
|
||||
color: props.option.notAvailable ? 'red' : undefined,
|
||||
}"
|
||||
>
|
||||
{{ props.option.simpleName }}
|
||||
{{ props.option.notAvailable ? "(불가)" : undefined }}
|
||||
</div>
|
||||
</template>
|
||||
<template #singleLabel="props">
|
||||
[{{ ItemTypeNameMap[props.option.type as keyof typeof ItemTypeNameMap] }}]
|
||||
{{ props.option.simpleName }}
|
||||
</template>
|
||||
</v-multiselect>
|
||||
</div>
|
||||
<div class="col-4 col-md-2 d-grid">
|
||||
<b-button @click="submit">
|
||||
{{ commandName }}
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="selectedItemObj.obj.id != NoneValue" class="row">
|
||||
<div class="col-4 col-md-2 align-self-center text-center">
|
||||
{{ selectedItemObj.obj.name }}
|
||||
</div>
|
||||
<div class="col" v-html="selectedItemObj.obj.info" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<BottomBar :title="commandName" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from "vue";
|
||||
import { unwrap } from "@/util/unwrap";
|
||||
import { entriesWithType } from "@util/entriesWithType";
|
||||
import type { Args } from "@/processing/args";
|
||||
import TopBackBar from "@/components/TopBackBar.vue";
|
||||
import BottomBar from "@/components/BottomBar.vue";
|
||||
import {
|
||||
getProcSearchable,
|
||||
type procItemList,
|
||||
type procItemType,
|
||||
} from "../processingRes";
|
||||
import {
|
||||
type ItemTypeKey,
|
||||
ItemTypeNameMap,
|
||||
NoneValue,
|
||||
type ValuesOf,
|
||||
} from "@/defs";
|
||||
import { convertSearch초성 } from "@/util/convertSearch초성";
|
||||
|
||||
declare const commandName: string;
|
||||
|
||||
declare const procRes: {
|
||||
citySecu: number;
|
||||
gold: number;
|
||||
itemList: procItemList;
|
||||
ownItem: Record<ItemTypeKey, procItemType>;
|
||||
};
|
||||
|
||||
type selectItemKey = {
|
||||
type: ItemTypeKey;
|
||||
id: string;
|
||||
html: string;
|
||||
simpleName: string;
|
||||
searchText: string;
|
||||
notAvailable?: boolean;
|
||||
obj: procItemType;
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
TopBackBar,
|
||||
BottomBar,
|
||||
},
|
||||
setup() {
|
||||
const forFind: {
|
||||
category: ValuesOf<typeof ItemTypeNameMap> | "판매";
|
||||
values: selectItemKey[];
|
||||
}[] = [];
|
||||
|
||||
//판매 처리
|
||||
const forSell: typeof forFind[0] = {
|
||||
category: "소유 물품 판매",
|
||||
values: [],
|
||||
};
|
||||
for (const [type, ownItem] of entriesWithType(procRes.ownItem)) {
|
||||
const typeName = ItemTypeNameMap[type];
|
||||
const itemNameHelp =
|
||||
ownItem.id == NoneValue
|
||||
? ""
|
||||
: ` [${ownItem.name}, ${(ownItem.cost / 2).toLocaleString()}금]`;
|
||||
forSell.values.push({
|
||||
type,
|
||||
id: NoneValue,
|
||||
html: `${typeName} 판매${itemNameHelp}`,
|
||||
simpleName: `${ownItem.id == NoneValue ? typeName : ownItem.name} 판매`,
|
||||
searchText: convertSearch초성(typeName).join("|"),
|
||||
notAvailable: ownItem.id == NoneValue,
|
||||
obj: ownItem,
|
||||
});
|
||||
}
|
||||
forFind.push(forSell);
|
||||
|
||||
const selectedItemObj = ref<selectItemKey>(forSell.values[0]);
|
||||
|
||||
for (const [type, itemSubList] of entriesWithType(procRes.itemList)) {
|
||||
const values: selectItemKey[] = [];
|
||||
const forBuy: typeof forFind[0] = {
|
||||
category: `${ItemTypeNameMap[type]} 구매`,
|
||||
values,
|
||||
};
|
||||
|
||||
for (const itemObj of itemSubList.values) {
|
||||
values.push({
|
||||
type,
|
||||
id: itemObj.id,
|
||||
html: `${
|
||||
itemObj.name
|
||||
} 구매 [${itemObj.cost.toLocaleString()}금, 필요 치안 ${itemObj.reqSecu.toLocaleString()}]`,
|
||||
simpleName: `${itemObj.name} 구매`,
|
||||
searchText: convertSearch초성(itemObj.name).join("|"),
|
||||
notAvailable:
|
||||
itemObj.reqSecu > procRes.citySecu || procRes.gold < itemObj.cost,
|
||||
obj: itemObj,
|
||||
});
|
||||
}
|
||||
|
||||
forFind.push(forBuy);
|
||||
}
|
||||
|
||||
async function submit(e: Event) {
|
||||
const event = new CustomEvent<Args>("customSubmit", {
|
||||
detail: {
|
||||
itemType: selectedItemObj.value.type,
|
||||
itemCode: selectedItemObj.value.id,
|
||||
},
|
||||
});
|
||||
unwrap(e.target).dispatchEvent(event);
|
||||
}
|
||||
|
||||
return {
|
||||
...procRes,
|
||||
searchable: getProcSearchable(),
|
||||
forFind,
|
||||
NoneValue,
|
||||
ItemTypeNameMap,
|
||||
selectedItemObj,
|
||||
commandName,
|
||||
submit,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,214 @@
|
||||
<template>
|
||||
<TopBackBar
|
||||
v-model:searchable="searchable"
|
||||
:title="commandName"
|
||||
/>
|
||||
<div class="bg0">
|
||||
<div>
|
||||
장수를 따라 임관합니다.<br>
|
||||
이미 임관/등용되었던 국가는 다시 임관할 수 없습니다.<br>
|
||||
바로 군주의 위치로 이동합니다.<br>
|
||||
임관할 국가를 목록에서 선택하세요.<br>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-8 col-md-4">
|
||||
장수 :
|
||||
<SelectGeneral
|
||||
v-model="selectedGeneralID"
|
||||
:generals="generalList"
|
||||
:groupByNation="nationList"
|
||||
:textHelper="textHelpGeneral"
|
||||
:searchable="searchable"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-4 col-md-2 d-grid">
|
||||
<b-button
|
||||
variant="primary"
|
||||
@click="submit"
|
||||
>
|
||||
{{ commandName }}
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="nation-list">
|
||||
<div class="nation-header nation-row bg1 center">
|
||||
<div>국가명</div>
|
||||
<div>임관권유문</div>
|
||||
<div class="zoom-toggle d-grid">
|
||||
<b-button
|
||||
v-model="toggleZoom"
|
||||
:pressed="toggleZoom"
|
||||
:variant="toggleZoom ? 'info' : 'secondary'"
|
||||
@click="toggleZoom = !toggleZoom"
|
||||
>
|
||||
{{ toggleZoom ? "작게 보기" : "크게 보기" }}
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-for="[, nation] in nationList"
|
||||
:key="nation.id"
|
||||
:class="['nation-row', 's-border-b', toggleZoom ? 'on-zoom' : 'on-fit']"
|
||||
>
|
||||
<div
|
||||
:style="{
|
||||
backgroundColor: nation.color,
|
||||
color: isBrightColor(nation.color) ? 'black' : 'white',
|
||||
fontSize: '1.3em',
|
||||
}"
|
||||
class="d-grid"
|
||||
>
|
||||
<div class="align-self-center center">
|
||||
{{ nation.name }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="nation-scout-plate align-self-center">
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
<div class="nation-scout-msg" v-html="nation.scoutMsg" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<BottomBar :title="commandName" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import SelectGeneral from "@/processing/SelectGeneral.vue";
|
||||
import { defineComponent, ref } from "vue";
|
||||
import { unwrap } from "@/util/unwrap";
|
||||
import type { Args } from "@/processing/args";
|
||||
import TopBackBar from "@/components/TopBackBar.vue";
|
||||
import BottomBar from "@/components/BottomBar.vue";
|
||||
import {
|
||||
convertGeneralList,
|
||||
getProcSearchable,
|
||||
type procGeneralItem,
|
||||
type procGeneralKey,
|
||||
type procGeneralRawItemList,
|
||||
type procNationItem,
|
||||
type procNationList,
|
||||
} from "../processingRes";
|
||||
import { getNpcColor } from "@/common_legacy";
|
||||
import { isBrightColor } from "@/util/isBrightColor";
|
||||
declare const commandName: string;
|
||||
|
||||
declare const procRes: {
|
||||
generals: procGeneralRawItemList;
|
||||
generalsKey: procGeneralKey[];
|
||||
nationList: procNationList;
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
SelectGeneral,
|
||||
TopBackBar,
|
||||
BottomBar,
|
||||
},
|
||||
setup() {
|
||||
const generalList = convertGeneralList(
|
||||
procRes.generalsKey,
|
||||
procRes.generals
|
||||
);
|
||||
|
||||
const toggleZoom = ref(true);
|
||||
const selectedGeneralID = ref(generalList[0].no);
|
||||
|
||||
function textHelpGeneral(gen: procGeneralItem): string {
|
||||
const nameColor = getNpcColor(gen.npc);
|
||||
const name = nameColor
|
||||
? `<span style="color:${nameColor}">${gen.name}</span>`
|
||||
: gen.name;
|
||||
return name;
|
||||
}
|
||||
|
||||
const nationList = new Map<number, procNationItem>();
|
||||
for (const nationItem of procRes.nationList) {
|
||||
nationList.set(nationItem.id, nationItem);
|
||||
}
|
||||
|
||||
async function submit(e: Event) {
|
||||
const event = new CustomEvent<Args>("customSubmit", {
|
||||
detail: {
|
||||
destGeneralID: selectedGeneralID.value,
|
||||
},
|
||||
});
|
||||
unwrap(e.target).dispatchEvent(event);
|
||||
}
|
||||
|
||||
return {
|
||||
searchable: getProcSearchable(),
|
||||
nationList: ref(nationList),
|
||||
selectedGeneralID,
|
||||
generalList,
|
||||
commandName,
|
||||
toggleZoom,
|
||||
isBrightColor,
|
||||
textHelpGeneral,
|
||||
submit,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "@scss/common/break_500px.scss";
|
||||
|
||||
@include media-1000px {
|
||||
.nation-list .nation-row {
|
||||
display: grid;
|
||||
grid-template-columns: 130px 870px;
|
||||
}
|
||||
|
||||
.zoom-toggle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.zoom-toggle > * {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-500px {
|
||||
.nation-list .nation-row.nation-header {
|
||||
display: grid;
|
||||
grid-template-columns: 3fr 1fr;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
|
||||
.zoom-toggle {
|
||||
grid-column: 2/3;
|
||||
grid-row: 1/3;
|
||||
}
|
||||
}
|
||||
|
||||
.nation-list .nation-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr minmax(1fr, calc(200px * 500 / 870));
|
||||
}
|
||||
|
||||
.on-fit {
|
||||
.nation-scout-plate {
|
||||
max-height: calc(200px * 500 / 870);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.nation-scout-msg {
|
||||
width: 870px;
|
||||
transform-origin: 0px 0px;
|
||||
transform: scale(calc(500 / 870));
|
||||
}
|
||||
}
|
||||
|
||||
.on-zoom {
|
||||
.nation-scout-plate {
|
||||
max-height: 200px;
|
||||
overflow-y: hidden;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.nation-scout-msg {
|
||||
max-width: 870px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,419 @@
|
||||
<template>
|
||||
<TopBackBar :title="commandName" />
|
||||
<div class="bg0">
|
||||
<div>
|
||||
병사를 모집합니다.
|
||||
<template v-if="commandName == '징병'">
|
||||
훈련과 사기치는 낮지만 가격이 저렴합니다.<br>
|
||||
</template>
|
||||
<template v-else-if="commandName == '모병'">
|
||||
훈련과 사기치는 높지만 자금이 많이 듭니다.<br>
|
||||
</template>
|
||||
가능한 수보다 많게 입력하면 가능한 최대 병사를 모집합니다.<br>
|
||||
이미 병사가 있는 경우 추가 {{ commandName }}되며, 병종이 다를경우는 기존의
|
||||
병사는 소집해제됩니다. <br>
|
||||
현재 {{ commandName }} 가능한 병종은
|
||||
<span style="color: green">녹색</span>으로 표시되며, 현재
|
||||
{{ commandName }} 가능한 특수병종은
|
||||
<span style="color: limegreen">초록색</span>으로 표시됩니다.
|
||||
</div>
|
||||
<div
|
||||
ref="defaultTarget"
|
||||
class="crewTypeList"
|
||||
>
|
||||
<div class="listFront">
|
||||
<div class="row gx-0 bg0">
|
||||
<div class="col-12 col-md-12 d-flex align-items-center">
|
||||
<div
|
||||
v-if="commandName == '모병'"
|
||||
class="text-center w-100"
|
||||
>
|
||||
모병은 가격 2배의 자금이 소요됩니다.<br>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row text-center bg2 gx-0">
|
||||
<div class="col-4 col-md-2">
|
||||
현재 기술력 : {{ techLevel }}등급
|
||||
</div>
|
||||
<div class="col-4 col-md-2">
|
||||
현재 통솔 :
|
||||
<span
|
||||
:style="{
|
||||
color: leadership < fullLeadership ? 'red' : undefined,
|
||||
}"
|
||||
>{{ leadership }}</span>
|
||||
</div>
|
||||
<div class="col-4 col-md-2">
|
||||
최대 통솔 : {{ fullLeadership }}
|
||||
</div>
|
||||
<div class="col-4 col-md-2">
|
||||
현재 병종 : {{ crewTypeMap?.get(currentCrewType)?.name }}
|
||||
</div>
|
||||
<div class="col-4 col-md-2">
|
||||
현재 병사 : {{ crew.toLocaleString() }}
|
||||
</div>
|
||||
<div class="col-4 col-md-2">
|
||||
현재 자금 : {{ gold.toLocaleString() }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="miniCrewPanel center bg0">
|
||||
<div
|
||||
class="crewTypeImg"
|
||||
:style="{
|
||||
background: '#222222 no-repeat center',
|
||||
backgroundImage: `url('${destCrewType.img}')`,
|
||||
backgroundSize: '64px',
|
||||
outline: 'solid 1px gray',
|
||||
height: '64px',
|
||||
}"
|
||||
/>
|
||||
<div
|
||||
:style="{
|
||||
backgroundColor:
|
||||
(destCrewType.notAvailable
|
||||
? 'red'
|
||||
: destCrewType.reqTech == 0
|
||||
? 'green'
|
||||
: 'limegreen') + ' !important',
|
||||
height: '100%',
|
||||
}"
|
||||
class="d-grid"
|
||||
>
|
||||
<div style="margin: auto">
|
||||
{{ destCrewType.name }}
|
||||
</div>
|
||||
</div>
|
||||
<div />
|
||||
<div class="crewTypePanel">
|
||||
<b-button-group>
|
||||
<b-button
|
||||
class="py-1"
|
||||
variant="dark"
|
||||
@click="beHalf"
|
||||
>
|
||||
절반
|
||||
</b-button><b-button
|
||||
class="py-1"
|
||||
variant="dark"
|
||||
@click="beFilled"
|
||||
>
|
||||
채우기
|
||||
</b-button><b-button
|
||||
class="py-1"
|
||||
variant="dark"
|
||||
@click="beFull"
|
||||
>
|
||||
가득
|
||||
</b-button>
|
||||
</b-button-group>
|
||||
<div class="row">
|
||||
<div class="col mx-2">
|
||||
<div class="input-group my-0">
|
||||
<span class="input-group-text py-1">병력</span>
|
||||
<input
|
||||
v-model.number="amount"
|
||||
type="number"
|
||||
class="form-control py-1 f_tnum px-0 text-end"
|
||||
min="1"
|
||||
>
|
||||
<span class="input-group-text py-1 f_tnum">00명</span>
|
||||
<span
|
||||
class="input-group-text py-1 f_tnum"
|
||||
style="
|
||||
text-align: right;
|
||||
min-width: 10ch;
|
||||
color: #303030;
|
||||
background-color: #ddd;
|
||||
"
|
||||
><div style="margin-left: auto">
|
||||
{{
|
||||
Math.ceil(
|
||||
amount * destCrewType.baseCost * goldCoeff
|
||||
).toLocaleString()
|
||||
}}금
|
||||
</div></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div />
|
||||
<b-button
|
||||
variant="primary"
|
||||
@click="submit"
|
||||
>
|
||||
{{
|
||||
commandName
|
||||
}}
|
||||
</b-button>
|
||||
</div>
|
||||
<div class="listHeader crewTypeSubGrid text-center bg1">
|
||||
<div class="crewTypeImg">
|
||||
사진
|
||||
</div>
|
||||
<div class="crewTypeName">
|
||||
병종
|
||||
</div>
|
||||
<div>공격</div>
|
||||
<div>방어</div>
|
||||
<div>기동</div>
|
||||
<div>회피</div>
|
||||
<div>가격</div>
|
||||
<div>군량</div>
|
||||
<div class="crewTypePanel">
|
||||
병사 수
|
||||
</div>
|
||||
<div class="crewTypeBtn">
|
||||
행동
|
||||
</div>
|
||||
<div class="crewTypeInfo">
|
||||
특징
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="listMain">
|
||||
<template
|
||||
v-for="armCrewType in armCrewTypes"
|
||||
:key="armCrewType.armType"
|
||||
>
|
||||
<div class="s-border-b row gx-0">
|
||||
<div
|
||||
class="col-7 col-md-10 align-self-center px-3"
|
||||
style="font-size: 1.3em"
|
||||
>
|
||||
{{ armCrewType.armName }} 계열
|
||||
</div>
|
||||
<div class="col-5 col-md-2 d-grid">
|
||||
<b-button
|
||||
:variant="
|
||||
showNotAvailable.get(armCrewType.armType) ? 'warning' : 'dark'
|
||||
"
|
||||
:pressed="showNotAvailable.get(armCrewType.armType)"
|
||||
class="btn-sm"
|
||||
@click="toggleShowNotAvailable(armCrewType.armType)"
|
||||
>
|
||||
{{
|
||||
showNotAvailable.get(armCrewType.armType)
|
||||
? "선택 할 수 있는 병종만 보기"
|
||||
: "선택 할 수 없는 병종도 보기"
|
||||
}}
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
<template
|
||||
v-for="crewType in armCrewType.values"
|
||||
:key="crewType.id"
|
||||
>
|
||||
<CrewTypeItem
|
||||
v-if="
|
||||
showNotAvailable.get(armCrewType.armType) ||
|
||||
!crewType.notAvailable
|
||||
"
|
||||
:crewType="crewType"
|
||||
:leadership="fullLeadership"
|
||||
:commandName="commandName"
|
||||
:currentCrewType="currentCrewType"
|
||||
:crew="crew"
|
||||
:goldCoeff="goldCoeff"
|
||||
@submitOutput="trySubmit"
|
||||
@click="
|
||||
destCrewType = unwrap(crewTypeMap.get(crewType.id));
|
||||
beFilled();
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<BottomBar :title="commandName" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import CrewTypeItem from "@/processing/CrewTypeItem.vue";
|
||||
import { defineComponent, ref } from "vue";
|
||||
import { unwrap } from "@/util/unwrap";
|
||||
import type { Args } from "@/processing/args";
|
||||
import TopBackBar from "@/components/TopBackBar.vue";
|
||||
import BottomBar from "@/components/BottomBar.vue";
|
||||
import type { procArmTypeItem, procCrewTypeItem } from "../processingRes";
|
||||
declare const commandName: string;
|
||||
|
||||
declare const procRes: {
|
||||
relYear: number;
|
||||
year: number;
|
||||
tech: number;
|
||||
techLevel: number;
|
||||
startYear: number;
|
||||
goldCoeff: number;
|
||||
leadership: number;
|
||||
fullLeadership: number;
|
||||
armCrewTypes: procArmTypeItem[];
|
||||
currentCrewType: number;
|
||||
crew: number;
|
||||
gold: number;
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
CrewTypeItem,
|
||||
TopBackBar,
|
||||
BottomBar,
|
||||
},
|
||||
setup() {
|
||||
const amount = ref(procRes.fullLeadership - Math.floor(procRes.crew / 100));
|
||||
|
||||
async function submit(e: Event) {
|
||||
const event = new CustomEvent<Args>("customSubmit", {
|
||||
detail: {
|
||||
amount: amount.value * 100,
|
||||
crewType: destCrewType.value.id,
|
||||
},
|
||||
});
|
||||
unwrap(e.target).dispatchEvent(event);
|
||||
}
|
||||
|
||||
const showNotAvailable = ref(new Map<number, boolean>());
|
||||
|
||||
const crewTypeMap = new Map<number, procCrewTypeItem>();
|
||||
for (const armType of procRes.armCrewTypes) {
|
||||
showNotAvailable.value.set(armType.armType, false);
|
||||
for (const crewType of armType.values) {
|
||||
crewTypeMap.set(crewType.id, crewType);
|
||||
}
|
||||
}
|
||||
|
||||
const destCrewType = ref(unwrap(crewTypeMap.get(procRes.currentCrewType)));
|
||||
|
||||
function beHalf() {
|
||||
amount.value = Math.ceil(procRes.fullLeadership * 0.5);
|
||||
}
|
||||
|
||||
function beFilled() {
|
||||
if (destCrewType.value.id == procRes.currentCrewType) {
|
||||
amount.value = Math.max(
|
||||
1,
|
||||
procRes.fullLeadership - Math.floor(procRes.crew / 100)
|
||||
);
|
||||
} else {
|
||||
amount.value = procRes.fullLeadership;
|
||||
}
|
||||
}
|
||||
|
||||
function beFull() {
|
||||
amount.value = Math.floor(procRes.fullLeadership * 1.2);
|
||||
}
|
||||
|
||||
function trySubmit(e: Event, inAmount: number, inCrewType: number) {
|
||||
e.preventDefault();
|
||||
amount.value = inAmount;
|
||||
destCrewType.value = unwrap(crewTypeMap.get(inCrewType));
|
||||
void submit(e);
|
||||
}
|
||||
|
||||
function toggleShowNotAvailable(armType: number) {
|
||||
showNotAvailable.value.set(
|
||||
armType,
|
||||
!(showNotAvailable.value.get(armType) ?? 0)
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
destCrewType,
|
||||
amount,
|
||||
showNotAvailable,
|
||||
...procRes,
|
||||
crewTypeMap,
|
||||
commandName,
|
||||
beHalf,
|
||||
beFilled,
|
||||
beFull,
|
||||
submit,
|
||||
toggleShowNotAvailable,
|
||||
trySubmit,
|
||||
unwrap,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "@scss/common/break_500px.scss";
|
||||
|
||||
.crewTypeSubGrid {
|
||||
display: grid;
|
||||
align-items: center;
|
||||
}
|
||||
.crewTypeItem .crewTypeImg {
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
.crewTypeInfo {
|
||||
padding-left: 0.5em;
|
||||
padding-right: 0.5em;
|
||||
}
|
||||
|
||||
@include media-1000px {
|
||||
.crewTypeSubGrid {
|
||||
grid-template-columns: 64px 1.5fr 1fr 1fr 1fr 1fr 1fr 1fr 250px 1.5fr 270px;
|
||||
}
|
||||
|
||||
.miniCrewPanel {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.only500pxMode {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-500px {
|
||||
.only1000pxMode {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.listFront {
|
||||
position: sticky;
|
||||
top: 0px;
|
||||
}
|
||||
.crewTypeSubGrid {
|
||||
grid-template-columns: 64px 1.5fr 1fr 1fr 1fr 270px;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
}
|
||||
.crewTypeImg {
|
||||
grid-column: 1 / 2;
|
||||
grid-row: 1 / 3;
|
||||
}
|
||||
.crewTypeName {
|
||||
grid-row: 1/ 3;
|
||||
}
|
||||
|
||||
.crewTypeInfo {
|
||||
grid-column: -2 / -1;
|
||||
grid-row: 1 / 3;
|
||||
}
|
||||
|
||||
.crewTypePanel {
|
||||
display: none;
|
||||
}
|
||||
.crewTypeBtn {
|
||||
display: none;
|
||||
}
|
||||
.crewTypeBtn > button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.miniCrewPanel .crewTypePanel {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.miniCrewPanel {
|
||||
display: grid;
|
||||
grid-template-columns: 64px 1.5fr 0.5fr 270px 0.5fr 2fr;
|
||||
grid-template-rows: 64px;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<TopBackBar :title="commandName" />
|
||||
<div class="bg0">
|
||||
<div>
|
||||
자신의 자금이나 군량을 국가 재산으로 헌납합니다.
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-2 col-md-1">
|
||||
자원 :
|
||||
<b-button-group>
|
||||
<b-button
|
||||
:pressed="isGold"
|
||||
@click="isGold = true"
|
||||
>
|
||||
금
|
||||
</b-button>
|
||||
<b-button
|
||||
:pressed="!isGold"
|
||||
@click="isGold = false"
|
||||
>
|
||||
쌀
|
||||
</b-button>
|
||||
</b-button-group>
|
||||
</div>
|
||||
<div class="col-7 col-md-4">
|
||||
금액 :
|
||||
<SelectAmount
|
||||
v-model="amount"
|
||||
:amountGuide="amountGuide"
|
||||
:maxAmount="maxAmount"
|
||||
:minAmount="minAmount"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-3 col-md-2 d-grid">
|
||||
<b-button
|
||||
variant="primary"
|
||||
@click="submit"
|
||||
>
|
||||
{{ commandName }}
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<BottomBar :title="commandName" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import SelectAmount from "@/processing/SelectAmount.vue";
|
||||
import { defineComponent, ref } from "vue";
|
||||
import { unwrap } from "@/util/unwrap";
|
||||
import type { Args } from "@/processing/args";
|
||||
import TopBackBar from "@/components/TopBackBar.vue";
|
||||
import BottomBar from "@/components/BottomBar.vue";
|
||||
declare const commandName: string;
|
||||
|
||||
declare const procRes: {
|
||||
minAmount: number;
|
||||
maxAmount: number;
|
||||
amountGuide: number[];
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
SelectAmount,
|
||||
TopBackBar,
|
||||
BottomBar,
|
||||
},
|
||||
setup() {
|
||||
const amount = ref(1000);
|
||||
const isGold = ref(true);
|
||||
|
||||
|
||||
async function submit(e: Event) {
|
||||
const event = new CustomEvent<Args>("customSubmit", {
|
||||
detail: {
|
||||
amount: amount.value,
|
||||
isGold: isGold.value,
|
||||
},
|
||||
});
|
||||
unwrap(e.target).dispatchEvent(event);
|
||||
}
|
||||
|
||||
return {
|
||||
amount,
|
||||
isGold,
|
||||
minAmount: ref(procRes.minAmount),
|
||||
maxAmount: ref(procRes.maxAmount),
|
||||
amountGuide: procRes.amountGuide,
|
||||
commandName,
|
||||
submit,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,44 @@
|
||||
import { default as che_건국 } from "./che_건국.vue";
|
||||
import { default as che_군량매매 } from "./che_군량매매.vue";
|
||||
import { default as che_등용 } from "./che_등용.vue";
|
||||
import { default as che_선양 } from "./che_선양.vue";
|
||||
import { default as che_숙련전환 } from "./che_숙련전환.vue";
|
||||
import { default as che_임관 } from "./che_임관.vue";
|
||||
import { default as che_장비매매 } from "./che_장비매매.vue";
|
||||
import { default as che_장수대상임관 } from "./che_장수대상임관.vue";
|
||||
import { default as che_징병 } from "./che_징병.vue";
|
||||
import { default as che_헌납 } from "./che_헌납.vue";
|
||||
|
||||
import { default as ProcessCity } from "../ProcessCity.vue";
|
||||
import { default as ProcessGeneralAmount } from "../ProcessGeneralAmount.vue";
|
||||
|
||||
|
||||
//TODO: 자주 쓰는 녀석들은 Slot으로 변경
|
||||
|
||||
export const commandMap: Record<string, typeof ProcessCity> = {
|
||||
che_강행: ProcessCity,
|
||||
che_군량매매,
|
||||
che_건국,
|
||||
che_등용,
|
||||
che_모병: che_징병,
|
||||
che_선동: ProcessCity,
|
||||
che_선양,
|
||||
che_숙련전환,
|
||||
che_이동: ProcessCity,
|
||||
che_임관,
|
||||
che_장비매매,
|
||||
che_장수대상임관,
|
||||
che_징병,
|
||||
che_증여: ProcessGeneralAmount,
|
||||
che_첩보: ProcessCity,
|
||||
che_출병: ProcessCity,
|
||||
che_탈취: ProcessCity,
|
||||
che_파괴: ProcessCity,
|
||||
che_화계: ProcessCity,
|
||||
che_헌납,
|
||||
}
|
||||
|
||||
/*
|
||||
- 항목들
|
||||
고유 양식 - 장비매매
|
||||
*/
|
||||
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<TopBackBar
|
||||
:title="commandName"
|
||||
type="chief"
|
||||
/>
|
||||
<div class="bg0">
|
||||
<div>
|
||||
국기를 변경합니다. 단 1회 가능합니다.<br>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 col-md-3">
|
||||
색상 :
|
||||
<ColorSelect
|
||||
v-model="selectedColorID"
|
||||
:colors="colors"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-4 col-md-2 d-grid">
|
||||
<b-button @click="submit">
|
||||
{{ commandName }}
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<BottomBar
|
||||
:title="commandName"
|
||||
type="chief"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import ColorSelect from "@/processing/SelectColor.vue";
|
||||
import { defineComponent, ref } from "vue";
|
||||
import { unwrap } from "@/util/unwrap";
|
||||
import type { Args } from "@/processing/args";
|
||||
import TopBackBar from "@/components/TopBackBar.vue";
|
||||
import BottomBar from "@/components/BottomBar.vue";
|
||||
|
||||
declare const commandName: string;
|
||||
|
||||
declare const procRes: {
|
||||
colors: string[],
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
ColorSelect,
|
||||
TopBackBar,
|
||||
BottomBar,
|
||||
},
|
||||
setup() {
|
||||
|
||||
const selectedColorID = ref(0);
|
||||
|
||||
async function submit(e: Event) {
|
||||
const event = new CustomEvent<Args>("customSubmit", {
|
||||
detail: {
|
||||
colorType: selectedColorID.value,
|
||||
},
|
||||
});
|
||||
unwrap(e.target).dispatchEvent(event);
|
||||
}
|
||||
|
||||
return {
|
||||
selectedColorID,
|
||||
colors: procRes.colors,
|
||||
commandName,
|
||||
submit,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<TopBackBar
|
||||
:title="commandName"
|
||||
type="chief"
|
||||
/>
|
||||
<div class="bg0">
|
||||
<div>
|
||||
나라의 이름을 바꿉니다. 황제가 된 후 1회 가능합니다.<br>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 col-md-3">
|
||||
국명 :
|
||||
<b-form-input
|
||||
v-model="destNationName"
|
||||
maxlength="18"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-4 col-md-2 d-grid">
|
||||
<b-button @click="submit">
|
||||
{{ commandName }}
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<BottomBar
|
||||
:title="commandName"
|
||||
type="chief"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from "vue";
|
||||
import { unwrap } from "@/util/unwrap";
|
||||
import type { Args } from "@/processing/args";
|
||||
import TopBackBar from "@/components/TopBackBar.vue";
|
||||
import BottomBar from "@/components/BottomBar.vue";
|
||||
|
||||
declare const commandName: string;
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
TopBackBar,
|
||||
BottomBar,
|
||||
},
|
||||
setup() {
|
||||
const destNationName = ref("");
|
||||
|
||||
async function submit(e: Event) {
|
||||
const event = new CustomEvent<Args>("customSubmit", {
|
||||
detail: {
|
||||
nationName: destNationName.value,
|
||||
},
|
||||
});
|
||||
unwrap(e.target).dispatchEvent(event);
|
||||
}
|
||||
|
||||
return {
|
||||
destNationName,
|
||||
commandName,
|
||||
submit,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,186 @@
|
||||
<template>
|
||||
<TopBackBar v-model:searchable="searchable" :title="commandName" type="chief" />
|
||||
<div v-if="asyncReady" class="bg0">
|
||||
<MapViewer
|
||||
v-if="map"
|
||||
v-model="selectedCityObj"
|
||||
:server-nick="serverNick"
|
||||
:serverID="serverID"
|
||||
:map-name="unwrap(gameConstStore?.gameConst.mapName)"
|
||||
:mapData="map"
|
||||
:isDetailMap="false"
|
||||
:cityPosition="cityPosition"
|
||||
:formatCityInfo="formatCityInfoText"
|
||||
:image-path="imagePath"
|
||||
/>
|
||||
<div>
|
||||
타국에게 원조합니다.<br />
|
||||
작위별로 금액 제한이 있습니다.<br /><br />
|
||||
<ul>
|
||||
<template v-for="({ text, amount }, level) in levelInfo" :key="level">
|
||||
<li>
|
||||
<span
|
||||
:style="{
|
||||
width: '4em',
|
||||
display: 'inline-block',
|
||||
...(level != currentNationLevel
|
||||
? {}
|
||||
: {
|
||||
textDecoration: 'underline',
|
||||
fontWeight: 'bold',
|
||||
}),
|
||||
}"
|
||||
>{{ text }}</span
|
||||
>: {{ amount.toLocaleString() }}
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
<br />
|
||||
원조할 국가를 목록에서 선택하세요.<br /><br />
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 col-md-3">
|
||||
국가 :
|
||||
<SelectNation v-model="selectedNationID" :nations="nationList" :searchable="searchable" />
|
||||
</div>
|
||||
<div class="col-6 col-md-0" />
|
||||
<div class="col-8 col-md-4">
|
||||
금 :
|
||||
<SelectAmount
|
||||
v-model="goldAmount"
|
||||
:amountGuide="amountGuide"
|
||||
:step="10"
|
||||
:maxAmount="maxAmount"
|
||||
:minAmount="minAmount"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-8 col-md-4">
|
||||
쌀 :
|
||||
<SelectAmount
|
||||
v-model="riceAmount"
|
||||
:amountGuide="amountGuide"
|
||||
:step="10"
|
||||
:maxAmount="maxAmount"
|
||||
:minAmount="minAmount"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-4 col-md-2 d-grid">
|
||||
<b-button variant="primary" @click="submit">
|
||||
{{ commandName }}
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<BottomBar :title="commandName" type="chief" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
declare const staticValues: {
|
||||
serverNick: string;
|
||||
serverID: string;
|
||||
mapName: string;
|
||||
commandName: string;
|
||||
};
|
||||
|
||||
declare const procRes: {
|
||||
nationList: procNationList;
|
||||
currentNationLevel: number;
|
||||
levelInfo: Record<
|
||||
number,
|
||||
{
|
||||
text: string;
|
||||
amount: number;
|
||||
}
|
||||
>;
|
||||
minAmount: number;
|
||||
maxAmount: number;
|
||||
amountGuide: number[];
|
||||
};
|
||||
|
||||
declare const getCityPosition: () => CityPositionMap;
|
||||
declare const formatCityInfo: (city: MapCityParsedRaw) => MapCityParsed;
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import MapViewer, { type CityPositionMap, type MapCityParsed, type MapCityParsedRaw } from "@/components/MapViewer.vue";
|
||||
import SelectNation from "@/processing/SelectNation.vue";
|
||||
import SelectAmount from "@/processing/SelectAmount.vue";
|
||||
import { ref, watch, onMounted, provide } from "vue";
|
||||
import { unwrap } from "@/util/unwrap";
|
||||
import type { Args } from "@/processing/args";
|
||||
import TopBackBar from "@/components/TopBackBar.vue";
|
||||
import BottomBar from "@/components/BottomBar.vue";
|
||||
import { getProcSearchable, type procNationItem, type procNationList } from "../processingRes";
|
||||
import type { MapResult } from "@/defs";
|
||||
import { SammoAPI } from "@/SammoAPI";
|
||||
import { getGameConstStore, type GameConstStore } from "@/GameConstStore";
|
||||
|
||||
const serverNick = staticValues.serverNick;
|
||||
const serverID = staticValues.serverID;
|
||||
|
||||
const cityPosition = getCityPosition();
|
||||
const formatCityInfoText = formatCityInfo;
|
||||
const imagePath = window.pathConfig.gameImage;
|
||||
|
||||
const asyncReady = ref<boolean>(false);
|
||||
const gameConstStore = ref<GameConstStore>();
|
||||
provide("gameConstStore", gameConstStore);
|
||||
const storeP = getGameConstStore().then((store) => {
|
||||
gameConstStore.value = store;
|
||||
});
|
||||
|
||||
void Promise.all([storeP]).then(() => {
|
||||
asyncReady.value = true;
|
||||
});
|
||||
|
||||
const nationList = new Map<number, procNationItem>();
|
||||
for (const nationItem of procRes.nationList) {
|
||||
nationList.set(nationItem.id, nationItem);
|
||||
}
|
||||
|
||||
const goldAmount = ref(procRes.minAmount);
|
||||
const riceAmount = ref(procRes.minAmount);
|
||||
|
||||
const currentNationLevel = procRes.currentNationLevel;
|
||||
const levelInfo = procRes.levelInfo;
|
||||
const minAmount = ref(procRes.minAmount);
|
||||
const maxAmount = ref(procRes.maxAmount);
|
||||
const amountGuide = procRes.amountGuide;
|
||||
|
||||
const selectedNationID = ref(procRes.nationList[0]?.id);
|
||||
|
||||
const map = ref<MapResult>();
|
||||
|
||||
async function submit(e: Event) {
|
||||
const event = new CustomEvent<Args>("customSubmit", {
|
||||
detail: {
|
||||
amountList: [goldAmount.value, riceAmount.value],
|
||||
destNationID: selectedNationID.value,
|
||||
},
|
||||
});
|
||||
unwrap(e.target).dispatchEvent(event);
|
||||
}
|
||||
|
||||
const searchable = getProcSearchable();
|
||||
|
||||
const selectedCityObj = ref<MapCityParsed>();
|
||||
const commandName = ref(staticValues.commandName);
|
||||
|
||||
watch(selectedCityObj, (city?: MapCityParsed) => {
|
||||
if (city === undefined) {
|
||||
return;
|
||||
}
|
||||
if (city.nationID === undefined) {
|
||||
return;
|
||||
}
|
||||
selectedNationID.value = city.nationID;
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
map.value = await SammoAPI.Global.GetMap({ neutralView: 0, showMe: 1 });
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,184 @@
|
||||
<template>
|
||||
<TopBackBar v-model:searchable="searchable" :title="commandName" type="chief" />
|
||||
<div v-if="asyncReady" class="bg0">
|
||||
<MapViewer
|
||||
v-if="map"
|
||||
v-model="selectedCityObj"
|
||||
:server-nick="serverNick"
|
||||
:serverID="serverID"
|
||||
:map-name="unwrap(gameConstStore?.gameConst.mapName)"
|
||||
:mapData="map"
|
||||
:isDetailMap="false"
|
||||
:cityPosition="cityPosition"
|
||||
:formatCityInfo="formatCityInfoText"
|
||||
:image-path="imagePath"
|
||||
/>
|
||||
|
||||
<div>
|
||||
선택된 도시로 아국 장수를 발령합니다.<br />
|
||||
아국 도시로만 발령이 가능합니다.<br />
|
||||
목록을 선택하거나 도시를 클릭하세요.<br />
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-6">
|
||||
장수 :
|
||||
<SelectGeneral
|
||||
v-model="selectedGeneralID"
|
||||
:cities="citiesMap"
|
||||
:generals="generalList"
|
||||
:troops="troops"
|
||||
:textHelper="textHelpGeneral"
|
||||
:searchable="searchable"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-6 col-md-4">
|
||||
도시 :
|
||||
<SelectCity v-model="selectedCityID" :cities="citiesMap" :searchable="searchable" />
|
||||
</div>
|
||||
<div class="col-4 col-md-2 d-grid">
|
||||
<b-button variant="primary" @click="submit">
|
||||
{{ commandName }}
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<BottomBar :title="commandName" type="chief" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
declare const staticValues: {
|
||||
serverNick: string;
|
||||
serverID: string;
|
||||
currentCity: number;
|
||||
commandName: string;
|
||||
};
|
||||
|
||||
declare const procRes: {
|
||||
troops: procTroopList;
|
||||
generals: procGeneralRawItemList;
|
||||
generalsKey: procGeneralKey[];
|
||||
};
|
||||
|
||||
declare const getCityPosition: () => CityPositionMap;
|
||||
declare const formatCityInfo: (city: MapCityParsedRaw) => MapCityParsed;
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import MapViewer, { type CityPositionMap, type MapCityParsed, type MapCityParsedRaw } from "@/components/MapViewer.vue";
|
||||
import SelectCity from "@/processing/SelectCity.vue";
|
||||
import SelectGeneral from "@/processing/SelectGeneral.vue";
|
||||
import { ref, watch, onMounted, provide } from "vue";
|
||||
import { unwrap } from "@/util/unwrap";
|
||||
import type { Args } from "@/processing/args";
|
||||
import TopBackBar from "@/components/TopBackBar.vue";
|
||||
import BottomBar from "@/components/BottomBar.vue";
|
||||
import {
|
||||
convertGeneralList,
|
||||
getProcSearchable,
|
||||
type procGeneralItem,
|
||||
type procGeneralKey,
|
||||
type procGeneralRawItemList,
|
||||
type procTroopList,
|
||||
} from "../processingRes";
|
||||
import { getNpcColor } from "@/common_legacy";
|
||||
import type { MapResult } from '@/defs';
|
||||
import { SammoAPI } from '@/SammoAPI';
|
||||
import { getGameConstStore, type GameConstStore } from '@/GameConstStore';
|
||||
|
||||
const serverNick = staticValues.serverNick;
|
||||
const serverID = staticValues.serverID;
|
||||
|
||||
const cityPosition = getCityPosition();
|
||||
const formatCityInfoText = formatCityInfo;
|
||||
const imagePath = window.pathConfig.gameImage;
|
||||
|
||||
const asyncReady = ref<boolean>(false);
|
||||
const gameConstStore = ref<GameConstStore>();
|
||||
provide("gameConstStore", gameConstStore);
|
||||
const storeP = getGameConstStore().then((store) => {
|
||||
gameConstStore.value = store;
|
||||
});
|
||||
|
||||
void Promise.all([storeP]).then(() => {
|
||||
asyncReady.value = true;
|
||||
});
|
||||
|
||||
const selectedCityID = ref(staticValues.currentCity);
|
||||
|
||||
const map = ref<MapResult>();
|
||||
const citiesMap = ref(
|
||||
new Map<
|
||||
number,
|
||||
{
|
||||
name: string;
|
||||
info?: string;
|
||||
}
|
||||
>()
|
||||
);
|
||||
watch(gameConstStore, (store)=>{
|
||||
if(!store){
|
||||
return;
|
||||
}
|
||||
const tmpCitiesMap = new Map<
|
||||
number,
|
||||
{
|
||||
name: string;
|
||||
info?: string;
|
||||
}
|
||||
>();
|
||||
|
||||
for(const city of Object.values(store.cityConst)){
|
||||
tmpCitiesMap.set(city.id, {
|
||||
name: city.name,
|
||||
});
|
||||
}
|
||||
citiesMap.value = tmpCitiesMap;
|
||||
})
|
||||
|
||||
//TODO: onMount로 이전하고 장수 목록은 실시간으로 받아와야함
|
||||
const generalList = convertGeneralList(procRes.generalsKey, procRes.generals);
|
||||
const troops = procRes.troops;
|
||||
|
||||
const selectedGeneralID = ref(generalList[0].no);
|
||||
|
||||
function textHelpGeneral(gen: procGeneralItem): string {
|
||||
const troops = !gen.troopID ? "" : `,${procRes.troops[gen.troopID].name}`;
|
||||
const nameColor = getNpcColor(gen.npc);
|
||||
const name = nameColor ? `<span style="color:${nameColor}">${gen.name}</span>` : gen.name;
|
||||
return `${name} [${citiesMap.value.get(unwrap(gen.cityID))?.name}${troops}] (${gen.leadership}/${gen.strength}/${
|
||||
gen.intel
|
||||
}) <병${unwrap(gen.crew).toLocaleString()}/훈${gen.train}/사${gen.atmos}>`;
|
||||
}
|
||||
|
||||
async function submit(e: Event) {
|
||||
const event = new CustomEvent<Args>("customSubmit", {
|
||||
detail: {
|
||||
destCityID: selectedCityID.value,
|
||||
destGeneralID: selectedGeneralID.value,
|
||||
},
|
||||
});
|
||||
unwrap(e.target).dispatchEvent(event);
|
||||
}
|
||||
|
||||
|
||||
const searchable = getProcSearchable();
|
||||
|
||||
const selectedCityObj = ref<MapCityParsed>();
|
||||
const commandName = ref(staticValues.commandName);
|
||||
|
||||
watch(selectedCityObj, (city?: MapCityParsed) => {
|
||||
if (city === undefined) {
|
||||
return;
|
||||
}
|
||||
selectedCityID.value = city.id;
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
try{
|
||||
map.value = await SammoAPI.Global.GetMap({neutralView:0, showMe: 1});
|
||||
}
|
||||
catch(e){
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,161 @@
|
||||
<template>
|
||||
<TopBackBar v-model:searchable="searchable" :title="commandName" type="chief" />
|
||||
<div v-if="asyncReady" class="bg0">
|
||||
<MapViewer
|
||||
v-if="map"
|
||||
v-model="selectedCityObj"
|
||||
:server-nick="serverNick"
|
||||
:serverID="serverID"
|
||||
:map-name="unwrap(gameConstStore?.gameConst.mapName)"
|
||||
:mapData="map"
|
||||
:isDetailMap="false"
|
||||
:cityPosition="cityPosition"
|
||||
:formatCityInfo="formatCityInfoText"
|
||||
:image-path="imagePath"
|
||||
/>
|
||||
|
||||
<div>
|
||||
타국에게 불가침을 제의합니다.<br />
|
||||
제의할 국가를 목록에서 선택하세요.<br />
|
||||
불가침 기한 다음 달부터 선포 가능합니다.<br />
|
||||
현재 제의가 불가능한 국가는
|
||||
<span style="color: red">붉은색</span>으로 표시됩니다.<br />
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 col-md-3">
|
||||
국가 :
|
||||
<SelectNation v-model="selectedNationID" :nations="nationList" :searchable="searchable" />
|
||||
</div>
|
||||
<div class="col-5 col-md-3">
|
||||
기간 :
|
||||
<div class="input-group">
|
||||
<b-form-select v-model="selectedYear" class="text-end selectedYear">
|
||||
<b-form-select-option v-for="yearP in maxYear - minYear + 1" :key="yearP" :value="yearP + minYear - 1">
|
||||
{{ yearP + minYear - 1 }}
|
||||
</b-form-select-option>
|
||||
</b-form-select>
|
||||
<span class="input-group-text px-2">년</span>
|
||||
<b-form-select v-model="selectedMonth" class="text-center">
|
||||
<b-form-select-option v-for="month in 12" :key="month" :value="month">
|
||||
{{ month }}
|
||||
</b-form-select-option>
|
||||
</b-form-select>
|
||||
<span class="input-group-text px-2">월</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-3 col-md-2 d-grid">
|
||||
<b-button @click="submit">
|
||||
{{ commandName }}
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<BottomBar :title="commandName" type="chief" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
declare const staticValues: {
|
||||
serverNick: string;
|
||||
serverID: string;
|
||||
mapName: string;
|
||||
commandName: string;
|
||||
};
|
||||
|
||||
declare const procRes: {
|
||||
nationList: procNationList;
|
||||
startYear: number;
|
||||
minYear: number;
|
||||
maxYear: number;
|
||||
month: number;
|
||||
};
|
||||
|
||||
declare const getCityPosition: () => CityPositionMap;
|
||||
declare const formatCityInfo: (city: MapCityParsedRaw) => MapCityParsed;
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import MapViewer, { type CityPositionMap, type MapCityParsed, type MapCityParsedRaw } from "@/components/MapViewer.vue";
|
||||
import SelectNation from "@/processing/SelectNation.vue";
|
||||
import { ref, watch, onMounted, provide } from "vue";
|
||||
import { unwrap } from "@/util/unwrap";
|
||||
import type { Args } from "@/processing/args";
|
||||
import TopBackBar from "@/components/TopBackBar.vue";
|
||||
import BottomBar from "@/components/BottomBar.vue";
|
||||
import { getProcSearchable, type procNationItem, type procNationList } from "../processingRes";
|
||||
import type { MapResult } from "@/defs";
|
||||
import { SammoAPI } from "@/SammoAPI";
|
||||
import { getGameConstStore, type GameConstStore } from "@/GameConstStore";
|
||||
|
||||
|
||||
const serverNick = staticValues.serverNick;
|
||||
const serverID = staticValues.serverID;
|
||||
|
||||
const cityPosition = getCityPosition();
|
||||
const formatCityInfoText = formatCityInfo;
|
||||
const imagePath = window.pathConfig.gameImage;
|
||||
|
||||
const asyncReady = ref<boolean>(false);
|
||||
const gameConstStore = ref<GameConstStore>();
|
||||
provide("gameConstStore", gameConstStore);
|
||||
const storeP = getGameConstStore().then((store) => {
|
||||
gameConstStore.value = store;
|
||||
});
|
||||
|
||||
void Promise.all([storeP]).then(() => {
|
||||
asyncReady.value = true;
|
||||
});
|
||||
|
||||
const nationList = new Map<number, procNationItem>();
|
||||
for (const nationItem of procRes.nationList) {
|
||||
nationList.set(nationItem.id, nationItem);
|
||||
}
|
||||
|
||||
const selectedNationID = ref(procRes.nationList[0].id);
|
||||
const map = ref<MapResult>();
|
||||
|
||||
const minYear = procRes.minYear;
|
||||
const maxYear = procRes.maxYear;
|
||||
|
||||
const selectedYear = ref(procRes.minYear);
|
||||
const selectedMonth = ref(procRes.month);
|
||||
|
||||
async function submit(e: Event) {
|
||||
const event = new CustomEvent<Args>("customSubmit", {
|
||||
detail: {
|
||||
destNationID: selectedNationID.value,
|
||||
year: selectedYear.value,
|
||||
month: selectedMonth.value,
|
||||
},
|
||||
});
|
||||
unwrap(e.target).dispatchEvent(event);
|
||||
}
|
||||
|
||||
const searchable = getProcSearchable();
|
||||
|
||||
const selectedCityObj = ref<MapCityParsed>();
|
||||
const commandName = ref(staticValues.commandName);
|
||||
|
||||
watch(selectedCityObj, (city?: MapCityParsed) => {
|
||||
if (city === undefined) {
|
||||
return;
|
||||
}
|
||||
if (city.nationID === undefined) {
|
||||
return;
|
||||
}
|
||||
selectedNationID.value = city.nationID;
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
map.value = await SammoAPI.Global.GetMap({ neutralView: 0, showMe: 1 });
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.selectedYear {
|
||||
width: 32%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,165 @@
|
||||
<template>
|
||||
<TopBackBar v-model:searchable="searchable" :title="commandName" type="chief" />
|
||||
<div v-if="asyncReady" class="bg0">
|
||||
<MapViewer
|
||||
v-if="map"
|
||||
v-model="selectedCityObj"
|
||||
:server-nick="serverNick"
|
||||
:serverID="serverID"
|
||||
:map-name="unwrap(gameConstStore?.gameConst.mapName)"
|
||||
:mapData="map"
|
||||
:isDetailMap="false"
|
||||
:cityPosition="cityPosition"
|
||||
:formatCityInfo="formatCityInfoText"
|
||||
:image-path="imagePath"
|
||||
/>
|
||||
<div>
|
||||
선택된 국가에 피장파장을 발동합니다.<br />
|
||||
지정한 전략을 상대국이
|
||||
{{ delayCnt }}턴 동안 사용할 수 없게됩니다.<br />
|
||||
대신 아국은 지정한 전략을 {{ postReqTurn }}턴 동안 사용할 수 없습니다.<br />
|
||||
선포, 전쟁중인 상대국에만 가능합니다.<br />
|
||||
상대 국가를 목록에서 선택하세요.<br />
|
||||
현재 피장파장이 불가능한 국가는
|
||||
<span style="color: red">붉은색</span>으로 표시됩니다.<br />
|
||||
</div>
|
||||
<div class="row gx-3">
|
||||
<div class="col-5 col-md-3">
|
||||
국가 :
|
||||
<SelectNation v-model="selectedNationID" :nations="nationList" :searchable="searchable" />
|
||||
</div>
|
||||
<div class="col-4 col-md-2">
|
||||
<label>전략 :</label>
|
||||
<select
|
||||
v-model="selectedCommandID"
|
||||
class="form-control"
|
||||
:style="{
|
||||
color: availableCommandTypeList[selectedCommandID].remainTurn > 0 ? 'red' : undefined,
|
||||
}"
|
||||
>
|
||||
<option
|
||||
v-for="(command, commandRawName) in availableCommandTypeList"
|
||||
:key="commandRawName"
|
||||
:value="commandRawName"
|
||||
:style="{
|
||||
color: command.remainTurn > 0 ? 'red' : 'black',
|
||||
}"
|
||||
>
|
||||
{{ command.name }} {{ command.remainTurn > 0 ? `(불가, ${command.remainTurn}턴)` : "" }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-3 col-md-2 d-grid">
|
||||
<b-button @click="submit">
|
||||
{{ commandName }}
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<BottomBar :title="commandName" type="chief" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
declare const staticValues: {
|
||||
serverNick: string;
|
||||
serverID: string;
|
||||
mapName: string;
|
||||
commandName: string;
|
||||
};
|
||||
|
||||
declare const procRes: {
|
||||
nationList: procNationList;
|
||||
startYear: number;
|
||||
delayCnt: number;
|
||||
postReqTurn: number;
|
||||
availableCommandTypeList: Record<
|
||||
string,
|
||||
{
|
||||
name: string;
|
||||
remainTurn: number;
|
||||
}
|
||||
>;
|
||||
};
|
||||
|
||||
declare const getCityPosition: () => CityPositionMap;
|
||||
declare const formatCityInfo: (city: MapCityParsedRaw) => MapCityParsed;
|
||||
</script>
|
||||
<script lang="ts" setup>
|
||||
import MapViewer, { type CityPositionMap, type MapCityParsed, type MapCityParsedRaw } from "@/components/MapViewer.vue";
|
||||
import SelectNation from "@/processing/SelectNation.vue";
|
||||
import { ref, watch, onMounted, provide } from "vue";
|
||||
import { unwrap } from "@/util/unwrap";
|
||||
import type { Args } from "@/processing/args";
|
||||
import TopBackBar from "@/components/TopBackBar.vue";
|
||||
import BottomBar from "@/components/BottomBar.vue";
|
||||
import { getProcSearchable, type procNationItem, type procNationList } from "../processingRes";
|
||||
import type { MapResult } from "@/defs";
|
||||
import { SammoAPI } from "@/SammoAPI";
|
||||
import { getGameConstStore, type GameConstStore } from "@/GameConstStore";
|
||||
|
||||
const serverNick = staticValues.serverNick;
|
||||
const serverID = staticValues.serverID;
|
||||
|
||||
const cityPosition = getCityPosition();
|
||||
const formatCityInfoText = formatCityInfo;
|
||||
const imagePath = window.pathConfig.gameImage;
|
||||
|
||||
const asyncReady = ref<boolean>(false);
|
||||
const gameConstStore = ref<GameConstStore>();
|
||||
provide("gameConstStore", gameConstStore);
|
||||
const storeP = getGameConstStore().then((store) => {
|
||||
gameConstStore.value = store;
|
||||
});
|
||||
|
||||
void Promise.all([storeP]).then(() => {
|
||||
asyncReady.value = true;
|
||||
});
|
||||
|
||||
const nationList = new Map<number, procNationItem>();
|
||||
for (const nationItem of procRes.nationList) {
|
||||
nationList.set(nationItem.id, nationItem);
|
||||
}
|
||||
|
||||
const selectedNationID = ref(procRes.nationList[0].id);
|
||||
|
||||
const map = ref<MapResult>();
|
||||
|
||||
const delayCnt = procRes.delayCnt;
|
||||
const postReqTurn = procRes.postReqTurn;
|
||||
|
||||
const availableCommandTypeList = procRes.availableCommandTypeList;
|
||||
const selectedCommandID = ref(Object.keys(availableCommandTypeList)[0]);
|
||||
|
||||
async function submit(e: Event) {
|
||||
const event = new CustomEvent<Args>("customSubmit", {
|
||||
detail: {
|
||||
destNationID: selectedNationID.value,
|
||||
commandType: selectedCommandID.value,
|
||||
},
|
||||
});
|
||||
unwrap(e.target).dispatchEvent(event);
|
||||
}
|
||||
|
||||
const searchable = getProcSearchable();
|
||||
|
||||
const selectedCityObj = ref<MapCityParsed>();
|
||||
const commandName = ref(staticValues.commandName);
|
||||
|
||||
watch(selectedCityObj, (city?: MapCityParsed) => {
|
||||
if (city === undefined) {
|
||||
return;
|
||||
}
|
||||
if (city.nationID === undefined) {
|
||||
return;
|
||||
}
|
||||
selectedNationID.value = city.nationID;
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
map.value = await SammoAPI.Global.GetMap({ neutralView: 0, showMe: 1 });
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,36 @@
|
||||
import { default as che_국기변경 } from "./che_국기변경.vue";
|
||||
import { default as che_국호변경 } from "./che_국호변경.vue";
|
||||
import { default as che_물자원조 } from "./che_물자원조.vue";
|
||||
import { default as che_불가침제의 } from "./che_불가침제의.vue";
|
||||
import { default as che_피장파장 } from "./che_피장파장.vue";
|
||||
|
||||
import { default as ProcessNation } from "../ProcessNation.vue";
|
||||
import { default as ProcessGeneralAmount } from "../ProcessGeneralAmount.vue";
|
||||
import { default as ProcessGeneralCity } from "./che_발령.vue";
|
||||
import { default as ProcessCity } from "../ProcessCity.vue";
|
||||
|
||||
export const commandMap: Record<string, typeof ProcessNation | typeof ProcessCity> = {
|
||||
che_국기변경,
|
||||
che_국호변경,
|
||||
che_급습: ProcessNation,
|
||||
che_몰수: ProcessGeneralAmount,
|
||||
che_물자원조,
|
||||
che_발령: ProcessGeneralCity,
|
||||
che_백성동원: ProcessCity,
|
||||
che_불가침제의,
|
||||
che_불가침파기제의: ProcessNation,
|
||||
che_선전포고: ProcessNation,
|
||||
che_수몰: ProcessCity,
|
||||
che_이호경식: ProcessNation,
|
||||
che_종전제의: ProcessNation,
|
||||
che_천도: ProcessCity,
|
||||
che_초토화: ProcessCity,
|
||||
che_포상: ProcessGeneralAmount,
|
||||
che_피장파장,
|
||||
che_허보: ProcessCity,
|
||||
}
|
||||
|
||||
/*
|
||||
- 항목들
|
||||
고유 양식 - 불가침제의
|
||||
*/
|
||||
@@ -0,0 +1,201 @@
|
||||
<template>
|
||||
<TopBackBar v-model:searchable="searchable" :title="commandName" :type="procEntryMode" />
|
||||
<div v-if="asyncReady" class="bg0">
|
||||
<MapViewer
|
||||
v-if="map"
|
||||
v-model="selectedCityObj"
|
||||
:server-nick="serverNick"
|
||||
:serverID="serverID"
|
||||
:map-name="unwrap(gameConstStore?.gameConst.mapName)"
|
||||
:mapData="map"
|
||||
:isDetailMap="false"
|
||||
:cityPosition="cityPosition"
|
||||
:formatCityInfo="formatCityInfoText"
|
||||
:image-path="imagePath"
|
||||
/>
|
||||
|
||||
<div v-if="commandName == '강행'">
|
||||
선택된 도시로 강행합니다.<br />
|
||||
최대 3칸내 도시로만 강행이 가능합니다.<br />
|
||||
목록을 선택하거나 도시를 클릭하세요.<br />
|
||||
</div>
|
||||
<div v-else-if="commandName == '이동'">
|
||||
선택된 도시로 이동합니다.<br />
|
||||
인접 도시로만 이동이 가능합니다.<br />
|
||||
목록을 선택하거나 도시를 클릭하세요.<br />
|
||||
</div>
|
||||
<div v-else-if="commandName == '출병'">
|
||||
선택된 도시를 향해 침공을 합니다.<br />
|
||||
침공 경로에 적군의 도시가 있다면 전투를 벌입니다.<br />
|
||||
목록을 선택하거나 도시를 클릭하세요.<br />
|
||||
</div>
|
||||
<div v-else-if="commandName == '첩보'">
|
||||
선택된 도시에 첩보를 실행합니다.<br />
|
||||
인접도시일 경우 많은 정보를 얻을 수 있습니다.<br />
|
||||
목록을 선택하거나 도시를 클릭하세요.<br />
|
||||
</div>
|
||||
<div v-else-if="commandName in { 화계: 1, 탈취: 1, 파괴: 1, 선동: 1 }">
|
||||
선택된 도시에 {{ commandName }}{{ JosaPick(commandName, "을") }} 실행합니다.<br />
|
||||
목록을 선택하거나 도시를 클릭하세요.<br />
|
||||
</div>
|
||||
<div v-else-if="commandName == '수몰'">
|
||||
선택된 도시에 수몰을 발동합니다.<br />
|
||||
전쟁중인 상대국 도시만 가능합니다.<br />
|
||||
목록을 선택하거나 도시를 클릭하세요.<br />
|
||||
</div>
|
||||
<div v-else-if="commandName == '백성동원'">
|
||||
선택된 도시에 백성을 동원해 성벽을 쌓습니다.<br />
|
||||
아국 도시만 가능합니다.<br />
|
||||
목록을 선택하거나 도시를 클릭하세요.<br />
|
||||
</div>
|
||||
<div v-else-if="commandName == '천도'">
|
||||
선택된 도시로 천도합니다.<br />
|
||||
현재 수도에서 연결된 도시만 가능하며, 1+2×거리만큼의 턴이 필요합니다.<br />
|
||||
목록을 선택하거나 도시를 클릭하세요.<br />
|
||||
</div>
|
||||
<div v-else-if="commandName == '허보'">
|
||||
선택된 도시에 허보를 발동합니다.<br />
|
||||
전쟁중인 상대국 도시만 가능합니다.<br />
|
||||
목록을 선택하거나 도시를 클릭하세요.<br />
|
||||
</div>
|
||||
<div v-else-if="commandName == '초토화'">
|
||||
선택된 도시를 초토화 시킵니다.<br />
|
||||
도시가 공백지가 되며, 도시의 인구, 내정 상태에 따라 상당량의 국고가 확보됩니다.<br />
|
||||
국가의 수뇌들은 명성을 잃고, 모든 장수들은 배신 수치가 1 증가합니다.<br />
|
||||
목록을 선택하거나 도시를 클릭하세요.<br />
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-4 col-md-2">
|
||||
도시:
|
||||
<SelectCity v-model="selectedCityID" :cities="citiesMap" :searchable="searchable" />
|
||||
</div>
|
||||
<div class="col-4 col-md-2 d-grid">
|
||||
<b-button @click="submit">
|
||||
{{ commandName }}
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
<CityBasedOnDistance :citiesMap="citiesMap" :distanceList="distanceList" @selected="selected" />
|
||||
</div>
|
||||
<BottomBar :title="commandName" :type="procEntryMode" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
declare const staticValues: {
|
||||
serverNick: string,
|
||||
serverID: string,
|
||||
currentCity: number;
|
||||
commandName: string;
|
||||
entryInfo: ["General" | "Nation", unknown];
|
||||
};
|
||||
declare const procRes: {
|
||||
distanceList: Record<number, number[]>;
|
||||
};
|
||||
|
||||
declare const getCityPosition: () => CityPositionMap;
|
||||
declare const formatCityInfo: (city: MapCityParsedRaw) => MapCityParsed;
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import MapViewer, { type CityPositionMap, type MapCityParsed, type MapCityParsedRaw} from '@/components/MapViewer.vue';
|
||||
import SelectCity from "@/processing/SelectCity.vue";
|
||||
import CityBasedOnDistance from "@/processing/CitiesBasedOnDistance.vue";
|
||||
import { ref, type Ref, watch, onMounted, provide } from "vue";
|
||||
import { unwrap } from "@/util/unwrap";
|
||||
import type { Args } from "@/processing/args";
|
||||
import TopBackBar from "@/components/TopBackBar.vue";
|
||||
import BottomBar from "@/components/BottomBar.vue";
|
||||
import { pick as JosaPick } from "@util/JosaUtil";
|
||||
import { getProcSearchable } from "./processingRes";
|
||||
import type { MapResult } from '@/defs';
|
||||
import { SammoAPI } from '@/SammoAPI';
|
||||
import { getGameConstStore, type GameConstStore } from '@/GameConstStore';
|
||||
|
||||
const serverNick = staticValues.serverNick;
|
||||
const serverID = staticValues.serverID;
|
||||
|
||||
const cityPosition = getCityPosition();
|
||||
const formatCityInfoText = formatCityInfo;
|
||||
const imagePath = window.pathConfig.gameImage;
|
||||
|
||||
const asyncReady = ref<boolean>(false);
|
||||
const gameConstStore = ref<GameConstStore>();
|
||||
provide("gameConstStore", gameConstStore);
|
||||
const storeP = getGameConstStore().then((store) => {
|
||||
gameConstStore.value = store;
|
||||
});
|
||||
|
||||
void Promise.all([storeP]).then(() => {
|
||||
asyncReady.value = true;
|
||||
});
|
||||
|
||||
const { distanceList } = procRes;
|
||||
|
||||
const selectedCityID = ref(staticValues.currentCity);
|
||||
|
||||
const map = ref<MapResult>();
|
||||
const citiesMap = ref(
|
||||
new Map<
|
||||
number,
|
||||
{
|
||||
name: string;
|
||||
info?: string;
|
||||
}
|
||||
>()
|
||||
);
|
||||
watch(gameConstStore, (store)=>{
|
||||
if(!store){
|
||||
return;
|
||||
}
|
||||
const tmpCitiesMap = new Map<
|
||||
number,
|
||||
{
|
||||
name: string;
|
||||
info?: string;
|
||||
}
|
||||
>();
|
||||
|
||||
for(const city of Object.values(store.cityConst)){
|
||||
tmpCitiesMap.set(city.id, {
|
||||
name: city.name,
|
||||
});
|
||||
}
|
||||
citiesMap.value = tmpCitiesMap;
|
||||
})
|
||||
|
||||
|
||||
function selected(cityID: number) {
|
||||
selectedCityID.value = cityID;
|
||||
}
|
||||
|
||||
async function submit(e: Event) {
|
||||
const event = new CustomEvent<Args>("customSubmit", {
|
||||
detail: {
|
||||
destCityID: selectedCityID.value,
|
||||
},
|
||||
});
|
||||
unwrap(e.target).dispatchEvent(event);
|
||||
}
|
||||
|
||||
const searchable = getProcSearchable();
|
||||
|
||||
const procEntryMode: Ref<"chief" | "normal"> = ref(staticValues.entryInfo[0] == "Nation" ? "chief" : "normal");
|
||||
const selectedCityObj = ref<MapCityParsed>();
|
||||
const commandName = ref(staticValues.commandName);
|
||||
|
||||
watch(selectedCityObj, (city?: MapCityParsed) => {
|
||||
if (city === undefined) {
|
||||
return;
|
||||
}
|
||||
selectedCityID.value = city.id;
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
try{
|
||||
map.value = await SammoAPI.Global.GetMap({neutralView:0, showMe: 1});
|
||||
}
|
||||
catch(e){
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,167 @@
|
||||
<template>
|
||||
<TopBackBar
|
||||
v-model:searchable="searchable"
|
||||
:title="commandName"
|
||||
:type="procEntryMode"
|
||||
/>
|
||||
<div class="bg0">
|
||||
<div v-if="commandName == '몰수'">
|
||||
장수의 자금이나 군량을 몰수합니다.<br>
|
||||
몰수한것은 국가재산으로 귀속됩니다.<br>
|
||||
</div>
|
||||
<div v-else-if="commandName == '포상'">
|
||||
국고로 장수에게 자금이나 군량을 지급합니다.<br>
|
||||
</div>
|
||||
<div v-else-if="commandName == '증여'">
|
||||
자신의 자금이나 군량을 다른 장수에게 증여합니다.<br>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-5">
|
||||
장수 :
|
||||
<SelectGeneral
|
||||
v-model="selectedGeneralID"
|
||||
:cities="citiesMap"
|
||||
:generals="generalList"
|
||||
:textHelper="textHelpGeneral"
|
||||
:searchable="searchable"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-2 col-md-1">
|
||||
자원 :
|
||||
<b-button-group>
|
||||
<b-button
|
||||
:pressed="isGold"
|
||||
@click="isGold=true"
|
||||
>
|
||||
금
|
||||
</b-button>
|
||||
<b-button
|
||||
:pressed="!isGold"
|
||||
@click="isGold=false"
|
||||
>
|
||||
쌀
|
||||
</b-button>
|
||||
</b-button-group>
|
||||
</div>
|
||||
<div class="col-7 col-md-4">
|
||||
금액 :
|
||||
<SelectAmount
|
||||
v-model="amount"
|
||||
:amountGuide="amountGuide"
|
||||
:maxAmount="maxAmount"
|
||||
:minAmount="minAmount"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-3 col-md-2 d-grid">
|
||||
<b-button
|
||||
variant="primary"
|
||||
@click="submit"
|
||||
>
|
||||
{{ commandName }}
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<BottomBar
|
||||
:title="commandName"
|
||||
:type="procEntryMode"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import SelectGeneral from "@/processing/SelectGeneral.vue";
|
||||
import SelectAmount from "@/processing/SelectAmount.vue";
|
||||
import { defineComponent, ref } from "vue";
|
||||
import { unwrap } from "@/util/unwrap";
|
||||
import type { Args } from "@/processing/args";
|
||||
import TopBackBar from "@/components/TopBackBar.vue";
|
||||
import BottomBar from "@/components/BottomBar.vue";
|
||||
import {
|
||||
convertGeneralList,
|
||||
getProcSearchable,
|
||||
type procGeneralItem,
|
||||
type procGeneralKey,
|
||||
type procGeneralRawItemList,
|
||||
} from "./processingRes";
|
||||
import { getNpcColor } from "@/common_legacy";
|
||||
declare const commandName: string;
|
||||
declare const entryInfo: ['General'|'Nation', unknown];
|
||||
|
||||
declare const procRes: {
|
||||
distanceList: Record<number, number[]>;
|
||||
cities: [number, string][];
|
||||
generals: procGeneralRawItemList;
|
||||
generalsKey: procGeneralKey[];
|
||||
minAmount: number;
|
||||
maxAmount: number;
|
||||
amountGuide: number[];
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
SelectGeneral,
|
||||
SelectAmount,
|
||||
TopBackBar,
|
||||
BottomBar,
|
||||
},
|
||||
setup() {
|
||||
const citiesMap = new Map<
|
||||
number,
|
||||
{
|
||||
name: string;
|
||||
info?: string;
|
||||
}
|
||||
>();
|
||||
for (const [id, name] of procRes.cities) {
|
||||
citiesMap.set(id, { name });
|
||||
}
|
||||
|
||||
const generalList = convertGeneralList(
|
||||
procRes.generalsKey,
|
||||
procRes.generals
|
||||
);
|
||||
const amount = ref(1000);
|
||||
const isGold = ref(true);
|
||||
|
||||
const selectedGeneralID = ref(generalList[0].no);
|
||||
|
||||
function textHelpGeneral(gen: procGeneralItem): string {
|
||||
const nameColor = getNpcColor(gen.npc);
|
||||
const name = nameColor
|
||||
? `<span style="color:${nameColor}">${gen.name}</span>`
|
||||
: gen.name;
|
||||
return `${name} (금${unwrap(gen.gold).toLocaleString()}/쌀${unwrap(gen.rice).toLocaleString()}) (${
|
||||
gen.leadership
|
||||
}/${gen.strength}/${gen.intel})`;
|
||||
}
|
||||
|
||||
async function submit(e: Event) {
|
||||
const event = new CustomEvent<Args>("customSubmit", {
|
||||
detail: {
|
||||
amount: amount.value,
|
||||
isGold: isGold.value,
|
||||
destGeneralID: selectedGeneralID.value,
|
||||
},
|
||||
});
|
||||
unwrap(e.target).dispatchEvent(event);
|
||||
}
|
||||
|
||||
return {
|
||||
procEntryMode: <'chief'|'normal'>(entryInfo[0] == 'Nation'?'chief':'normal'),
|
||||
searchable: getProcSearchable(),
|
||||
amount,
|
||||
isGold,
|
||||
selectedGeneralID,
|
||||
citiesMap: ref(citiesMap),
|
||||
distanceList: procRes.distanceList,
|
||||
minAmount: ref(procRes.minAmount),
|
||||
maxAmount: ref(procRes.maxAmount),
|
||||
amountGuide: procRes.amountGuide,
|
||||
generalList,
|
||||
commandName,
|
||||
textHelpGeneral,
|
||||
submit,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,171 @@
|
||||
<template>
|
||||
<TopBackBar v-model:searchable="searchable" :title="commandName" :type="procEntryMode" />
|
||||
<div v-if="asyncReady" class="bg0">
|
||||
<MapViewer
|
||||
v-if="map"
|
||||
v-model="selectedCityObj"
|
||||
:server-nick="serverNick"
|
||||
:serverID="serverID"
|
||||
:map-name="unwrap(gameConstStore?.gameConst.mapName)"
|
||||
:mapData="map"
|
||||
:isDetailMap="false"
|
||||
:cityPosition="cityPosition"
|
||||
:formatCityInfo="formatCityInfoText"
|
||||
:image-path="imagePath"
|
||||
/>
|
||||
|
||||
<div v-if="commandName == '선전포고'">
|
||||
타국에게 선전 포고합니다.<br />
|
||||
선전 포고할 국가를 목록에서 선택하세요.<br />
|
||||
고립되지 않은 아국 도시에서 인접한 국가에 선포 가능합니다.<br />
|
||||
초반제한 해제 2년전부터 선포가 가능합니다. ({{ startYear + 1 }}년 1월부터 가능)<br />
|
||||
현재 선포가 불가능한 국가는 배경색이
|
||||
<span style="color: red">붉은색</span>으로 표시됩니다.<br />
|
||||
</div>
|
||||
<div v-else-if="commandName == '급습'">
|
||||
선택된 국가에 급습을 발동합니다.<br />
|
||||
선포, 전쟁중인 상대국에만 가능합니다.<br />
|
||||
상대 국가를 목록에서 선택하세요.<br />
|
||||
현재 급습이 불가능한 국가는
|
||||
<span style="color: red">붉은색</span>으로 표시됩니다.<br />
|
||||
</div>
|
||||
<div v-else-if="commandName == '불가침 파기 제의'">
|
||||
불가침중인 국가에 조약 파기를 제의합니다.<br />
|
||||
제의할 국가를 목록에서 선택하세요.<br />
|
||||
현재 제의가 불가능한 국가는
|
||||
<span style="color: red">붉은색</span>으로 표시됩니다.<br />
|
||||
</div>
|
||||
<div v-else-if="commandName == '이호경식'">
|
||||
선택된 국가에 이호경식을 발동합니다.<br />
|
||||
선포, 전쟁중인 상대국에만 가능합니다.<br />
|
||||
상대 국가를 목록에서 선택하세요.<br />
|
||||
현재 이호경식이 불가능한 국가는
|
||||
<span style="color: red">붉은색</span>으로 표시됩니다.<br />
|
||||
</div>
|
||||
<div v-else-if="commandName == '종전 제의'">
|
||||
전쟁중인 국가에 종전을 제의합니다.<br />
|
||||
제의할 국가를 목록에서 선택하세요.<br />
|
||||
현재 제의가 불가능한 국가는
|
||||
<span style="color: red">붉은색</span>으로 표시됩니다.<br />
|
||||
</div>
|
||||
<div v-else-if="commandName == '허보'">
|
||||
전쟁중인 국가에 종전을 제의합니다.<br />
|
||||
제의할 국가를 목록에서 선택하세요.<br />
|
||||
현재 제의가 불가능한 국가는
|
||||
<span style="color: red">붉은색</span>으로 표시됩니다.<br />
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 col-md-3">
|
||||
국가 :
|
||||
<SelectNation v-model="selectedNationID" :nations="nationList" :searchable="searchable" />
|
||||
</div>
|
||||
<div class="col-4 col-md-2 d-grid">
|
||||
<b-button @click="submit">
|
||||
{{ commandName }}
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<BottomBar :title="commandName" :type="procEntryMode" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
declare const staticValues: {
|
||||
serverNick: string;
|
||||
serverID: string;
|
||||
commandName: string;
|
||||
entryInfo: ["General" | "Nation", unknown];
|
||||
};
|
||||
declare const procRes: {
|
||||
nationList: procNationList;
|
||||
startYear: number;
|
||||
};
|
||||
|
||||
declare const getCityPosition: () => CityPositionMap;
|
||||
declare const formatCityInfo: (city: MapCityParsedRaw) => MapCityParsed;
|
||||
</script>
|
||||
<script lang="ts" setup>
|
||||
import MapViewer, { type CityPositionMap, type MapCityParsed, type MapCityParsedRaw } from "@/components/MapViewer.vue";
|
||||
import SelectNation from "@/processing/SelectNation.vue";
|
||||
import { ref, type Ref, watch, onMounted, provide } from "vue";
|
||||
import { unwrap } from "@/util/unwrap";
|
||||
import type { Args } from "@/processing/args";
|
||||
import TopBackBar from "@/components/TopBackBar.vue";
|
||||
import BottomBar from "@/components/BottomBar.vue";
|
||||
import { getProcSearchable, type procNationItem, type procNationList } from "./processingRes";
|
||||
import { getGameConstStore, type GameConstStore } from "@/GameConstStore";
|
||||
import { SammoAPI } from "@/SammoAPI";
|
||||
import type { MapResult } from "@/defs";
|
||||
|
||||
const serverNick = staticValues.serverNick;
|
||||
const serverID = staticValues.serverID;
|
||||
const startYear = procRes.startYear;
|
||||
|
||||
const cityPosition = getCityPosition();
|
||||
const formatCityInfoText = formatCityInfo;
|
||||
const imagePath = window.pathConfig.gameImage;
|
||||
|
||||
const asyncReady = ref<boolean>(false);
|
||||
const gameConstStore = ref<GameConstStore>();
|
||||
provide("gameConstStore", gameConstStore);
|
||||
const storeP = getGameConstStore().then((store) => {
|
||||
gameConstStore.value = store;
|
||||
});
|
||||
|
||||
void Promise.all([storeP]).then(() => {
|
||||
asyncReady.value = true;
|
||||
});
|
||||
|
||||
const nationList = ref(new Map<number, procNationItem>());
|
||||
|
||||
watch(
|
||||
() => procRes.nationList,
|
||||
(newNationList) => {
|
||||
const tmpNationList = new Map<number, procNationItem>();
|
||||
for (const nationItem of newNationList) {
|
||||
tmpNationList.set(nationItem.id, nationItem);
|
||||
}
|
||||
nationList.value = tmpNationList;
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
const selectedNationID = ref(procRes.nationList[0].id);
|
||||
|
||||
const map = ref<MapResult>();
|
||||
|
||||
async function submit(e: Event) {
|
||||
const event = new CustomEvent<Args>("customSubmit", {
|
||||
detail: {
|
||||
destNationID: selectedNationID.value,
|
||||
},
|
||||
});
|
||||
unwrap(e.target).dispatchEvent(event);
|
||||
}
|
||||
|
||||
const searchable = getProcSearchable();
|
||||
|
||||
const procEntryMode: Ref<"chief" | "normal"> = ref(staticValues.entryInfo[0] == "Nation" ? "chief" : "normal");
|
||||
const selectedCityObj = ref<MapCityParsed>();
|
||||
const commandName = ref(staticValues.commandName);
|
||||
|
||||
watch(selectedCityObj, (city?: MapCityParsed) => {
|
||||
if (city === undefined) {
|
||||
return;
|
||||
}
|
||||
if(city.nationID === undefined){
|
||||
return;
|
||||
}
|
||||
|
||||
selectedNationID.value = city.nationID;
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
try{
|
||||
map.value = await SammoAPI.Global.GetMap({neutralView:0, showMe: 1});
|
||||
}
|
||||
catch(e){
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<div class="input-group">
|
||||
<b-button v-if="maxAmount > 20000" class="btn-sm" @click="amount = Math.max(amount - 10000, minAmount)">
|
||||
-만
|
||||
</b-button>
|
||||
<b-button v-if="maxAmount > 2000" class="btn-sm" @click="amount = Math.max(amount - 1000, minAmount)">
|
||||
-천
|
||||
</b-button>
|
||||
<b-button v-if="maxAmount > 200" class="btn-sm" @click="amount = Math.max(amount - 100, minAmount)"> -백 </b-button>
|
||||
<input
|
||||
v-model.number="amount"
|
||||
type="number"
|
||||
class="form-control text-end"
|
||||
:max="maxAmount"
|
||||
:min="minAmount"
|
||||
:step="step"
|
||||
placeholder="금액"
|
||||
/>
|
||||
<b-dropdown v-if="amountGuide" right text="" class="amount-dropdown">
|
||||
<b-dropdown-item v-for="guide in amountGuide" :key="guide" @click="amount = guide">
|
||||
<div class="text-end">
|
||||
{{ guide.toLocaleString() }}
|
||||
</div>
|
||||
</b-dropdown-item>
|
||||
</b-dropdown>
|
||||
<b-button v-if="maxAmount > 200" class="btn-sm" @click="amount = Math.min(amount + 100, maxAmount)"> +백 </b-button>
|
||||
<b-button v-if="maxAmount > 2000" class="btn-sm" @click="amount = Math.min(amount + 1000, maxAmount)">
|
||||
+천
|
||||
</b-button>
|
||||
<b-button v-if="maxAmount >= 10000" class="btn-sm" @click="amount = Math.min(amount + 10000, maxAmount)">
|
||||
+만
|
||||
</b-button>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "vue";
|
||||
import VueTypes from "vue-types";
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
modelValue: VueTypes.number.isRequired,
|
||||
minAmount: VueTypes.number.isRequired,
|
||||
maxAmount: VueTypes.number.isRequired,
|
||||
amountGuide: VueTypes.arrayOf(Number).def([1000, 2000, 5000, 10000]),
|
||||
step: VueTypes.number.def(1),
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
data() {
|
||||
return {
|
||||
amount: this.modelValue,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
amount(val: number) {
|
||||
this.$emit("update:modelValue", val);
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.btn-group.amount-dropdown > .btn {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.btn-group.amount-dropdown .dropdown-menu.show {
|
||||
min-width: 6rem;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<v-multiselect
|
||||
v-model="selectedCity"
|
||||
:allow-empty="false"
|
||||
:options="citiesForFind"
|
||||
:group-select="false"
|
||||
label="searchText"
|
||||
track-by="value"
|
||||
:show-labels="false"
|
||||
selectLabel="선택(엔터)"
|
||||
selectGroupLabel=""
|
||||
selectedLabel="선택됨"
|
||||
deselectLabel="해제(엔터)"
|
||||
deselectGroupLabel=""
|
||||
placeholder="도시 선택"
|
||||
:maxHeight="400"
|
||||
:searchable="searchable"
|
||||
>
|
||||
<template #option="props"
|
||||
><span
|
||||
:style="{
|
||||
color: props.option.notAvailable ? 'red' : undefined,
|
||||
}"
|
||||
>
|
||||
{{ props.option.title }}
|
||||
<span v-if="props.option.info">({{ props.option.info }})</span>
|
||||
{{ props.option.notAvailable ? "(불가)" : undefined }}</span
|
||||
>
|
||||
</template>
|
||||
<template #singleLabel="props">
|
||||
<span
|
||||
:style="{
|
||||
color: props.option.notAvailable ? 'red' : undefined,
|
||||
}"
|
||||
>{{ props.option.simpleName }} {{ props.option.notAvailable ? "(불가)" : undefined }}</span
|
||||
>
|
||||
</template>
|
||||
</v-multiselect>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { convertSearch초성 } from "@/util/convertSearch초성";
|
||||
import { defineComponent, type PropType } from "vue";
|
||||
|
||||
type SelectedCity = {
|
||||
value: number;
|
||||
searchText: string;
|
||||
title: string;
|
||||
simpleName: string;
|
||||
info?: string;
|
||||
notAvailable?: boolean;
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
cities: {
|
||||
type: Map as PropType<Map<number, { name: string; info?: string }>>,
|
||||
required: true,
|
||||
},
|
||||
searchable: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
data() {
|
||||
const citiesForFind = [];
|
||||
const targets = new Map<number, SelectedCity>();
|
||||
let selectedCity;
|
||||
for (const [value, { name, info }] of this.cities.entries()) {
|
||||
const obj: SelectedCity = {
|
||||
value,
|
||||
title: name,
|
||||
info: info,
|
||||
simpleName: name,
|
||||
searchText: convertSearch초성(name).join("|"),
|
||||
};
|
||||
if (value == this.modelValue) {
|
||||
selectedCity = obj;
|
||||
}
|
||||
citiesForFind.push(obj);
|
||||
targets.set(value, obj);
|
||||
}
|
||||
return {
|
||||
selectedCity,
|
||||
citiesForFind,
|
||||
targets,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
modelValue(val: number) {
|
||||
const target = this.targets.get(val);
|
||||
this.selectedCity = target;
|
||||
},
|
||||
selectedCity(val: SelectedCity) {
|
||||
this.$emit("update:modelValue", val.value);
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,107 @@
|
||||
<template>
|
||||
<v-multiselect
|
||||
v-model="selectedColor"
|
||||
:allow-empty="false"
|
||||
:options="forFind"
|
||||
:group-select="false"
|
||||
label="searchText"
|
||||
track-by="value"
|
||||
:show-labels="false"
|
||||
selectLabel="선택(엔터)"
|
||||
selectGroupLabel=""
|
||||
selectedLabel="선택됨"
|
||||
deselectLabel="해제(엔터)"
|
||||
deselectGroupLabel=""
|
||||
placeholder="색상 선택"
|
||||
:maxHeight="400"
|
||||
:searchable="false"
|
||||
>
|
||||
<template #option="props">
|
||||
<div
|
||||
:class="`sam-color-${props.option.title.slice(1)}`"
|
||||
:style="{
|
||||
margin: '-0.375rem -0.75rem',
|
||||
}"
|
||||
>
|
||||
<div
|
||||
class="sam-nation-own-bgcolor"
|
||||
:style="{
|
||||
padding: '0.545rem 0.75rem',
|
||||
}"
|
||||
>
|
||||
{{ props.option.title }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #singleLabel="props">
|
||||
<div
|
||||
:class="`sam-color-${props.option.title.slice(1)}`"
|
||||
:style="{
|
||||
margin: '-0.25rem -0.75rem',
|
||||
}"
|
||||
>
|
||||
<div
|
||||
class="sam-nation-own-bgcolor"
|
||||
:style="{
|
||||
padding: '0.30rem 0.75rem',
|
||||
borderRadius: '0.25rem',
|
||||
}"
|
||||
>
|
||||
{{ props.option.title }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</v-multiselect>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { unwrap } from "@/util/unwrap";
|
||||
import { defineComponent, type PropType } from "vue";
|
||||
|
||||
type SelectedColor = {
|
||||
value: number;
|
||||
title: string;
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
colors: {
|
||||
type: Array as PropType<string[]>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
data() {
|
||||
const forFind = [];
|
||||
const targets = new Map<number, SelectedColor>();
|
||||
for (const [value, title] of this.colors.entries()) {
|
||||
const obj: SelectedColor = {
|
||||
value,
|
||||
title,
|
||||
};
|
||||
forFind.push(obj);
|
||||
targets.set(value, obj);
|
||||
}
|
||||
let selectedColor = forFind[0];
|
||||
|
||||
return {
|
||||
selectedColor,
|
||||
searchMode: false,
|
||||
forFind,
|
||||
targets,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
modelValue(val: number) {
|
||||
const target = unwrap(this.targets.get(val));
|
||||
this.selectedColor = target;
|
||||
},
|
||||
selectedColor(val: SelectedColor) {
|
||||
this.$emit("update:modelValue", val.value);
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,177 @@
|
||||
<template>
|
||||
<v-multiselect
|
||||
v-model="selectedGeneral"
|
||||
class="selectedGeneral"
|
||||
:allow-empty="false"
|
||||
:options="forFind"
|
||||
:group-select="false"
|
||||
:group-values="groupByNation ? 'values' : undefined"
|
||||
group-label="nationID"
|
||||
label="searchText"
|
||||
track-by="value"
|
||||
:show-labels="false"
|
||||
selectLabel="선택(엔터)"
|
||||
selectGroupLabel=""
|
||||
selectedLabel="선택됨"
|
||||
deselectLabel="해제(엔터)"
|
||||
deselectGroupLabel=""
|
||||
placeholder="장수 선택"
|
||||
:maxHeight="400"
|
||||
:searchable="searchable"
|
||||
>
|
||||
<template #option="props">
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
<div v-if="props.option.title" v-html="props.option.title" />
|
||||
<div
|
||||
v-if="props.option.$groupLabel !== undefined"
|
||||
class="margin-filler"
|
||||
:style="{
|
||||
backgroundColor: groupByNation?.get(props.option.$groupLabel)?.color,
|
||||
color: isBrightColor(groupByNation?.get(props.option.$groupLabel)?.color ?? '#ffffff') ? 'black' : 'white',
|
||||
}"
|
||||
>
|
||||
{{ groupByNation?.get(props.option.$groupLabel)?.name }}
|
||||
</div>
|
||||
</template>
|
||||
<template #singleLabel="props">
|
||||
{{ props.option.simpleName }}
|
||||
{{ groupByNation ? `[${groupByNation.get(props.option.obj.nationID)?.name}]` : undefined }}
|
||||
</template>
|
||||
</v-multiselect>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { getNpcColor } from "@/common_legacy";
|
||||
import { convertSearch초성 } from "@/util/convertSearch초성";
|
||||
import { isBrightColor } from "@/util/isBrightColor";
|
||||
import { unwrap } from "@/util/unwrap";
|
||||
import { defineComponent, type PropType } from "vue";
|
||||
import VueTypes from "vue-types";
|
||||
import type { procGeneralItem, procGeneralList, procNationItem } from "./processingRes";
|
||||
|
||||
type SelectedGeneral = {
|
||||
value: number;
|
||||
searchText: string;
|
||||
title: string;
|
||||
simpleName: string;
|
||||
obj: procGeneralItem;
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
modelValue: VueTypes.number.isRequired,
|
||||
generals: {
|
||||
type: Array as PropType<procGeneralList>,
|
||||
required: true,
|
||||
},
|
||||
textHelper: {
|
||||
type: Function as PropType<(item: procGeneralItem) => string>,
|
||||
required: false,
|
||||
default: undefined,
|
||||
},
|
||||
groupByNation: {
|
||||
type: Map as PropType<Map<number, procNationItem>>,
|
||||
required: false,
|
||||
default: undefined,
|
||||
},
|
||||
searchable: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
data() {
|
||||
const forFind: (
|
||||
| SelectedGeneral
|
||||
| {
|
||||
values: SelectedGeneral[];
|
||||
nationID: number;
|
||||
}
|
||||
)[] = [];
|
||||
const forFindGroup = new Map<number, SelectedGeneral[]>();
|
||||
const targets = new Map<number, SelectedGeneral>();
|
||||
|
||||
let selectedGeneral;
|
||||
|
||||
for (const gen of this.generals) {
|
||||
let groupArray = forFind;
|
||||
if (this.groupByNation) {
|
||||
const nationID = gen.nationID ?? 0;
|
||||
if (!forFindGroup.has(nationID)) {
|
||||
const nationItem = unwrap(this.groupByNation.get(nationID));
|
||||
let tmpArr: SelectedGeneral[] = [];
|
||||
forFindGroup.set(nationID, tmpArr);
|
||||
groupArray = tmpArr;
|
||||
|
||||
forFind.push({
|
||||
nationID: nationItem.id,
|
||||
values: tmpArr,
|
||||
});
|
||||
} else {
|
||||
groupArray = unwrap(forFindGroup.get(nationID));
|
||||
}
|
||||
}
|
||||
|
||||
const nameColor = getNpcColor(gen.npc);
|
||||
const name = nameColor ? `<span style="color:${nameColor}">${gen.name}</span>` : gen.name;
|
||||
|
||||
const obj: SelectedGeneral = {
|
||||
value: gen.no,
|
||||
title: this.textHelper ? this.textHelper(gen) : `${name} (${gen.leadership}/${gen.strength}/${gen.intel})`,
|
||||
simpleName: gen.name,
|
||||
searchText: convertSearch초성(gen.name).join("|"),
|
||||
obj: gen,
|
||||
};
|
||||
if (gen.no == this.modelValue) {
|
||||
selectedGeneral = obj;
|
||||
}
|
||||
groupArray.push(obj);
|
||||
targets.set(gen.no, obj);
|
||||
}
|
||||
return {
|
||||
selectedGeneral,
|
||||
forFind,
|
||||
targets,
|
||||
isBrightColor,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
modelValue(val: number) {
|
||||
const target = this.targets.get(val);
|
||||
this.selectedGeneral = target;
|
||||
},
|
||||
selectedGeneral(val: SelectedGeneral) {
|
||||
this.$emit("update:modelValue", val.value);
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="scss">
|
||||
@import "@scss/common/break_500px.scss";
|
||||
@import "@scss/common/variables.scss";
|
||||
@import "@scss/common/bootswatch_custom_variables.scss";
|
||||
@import "bootstrap/scss/bootstrap-utilities.scss";
|
||||
|
||||
.selectedGeneral {
|
||||
$vue-multiselect-bg: $gray-700;
|
||||
$vue-multiselect-color: $gray-100;
|
||||
$form-select-color: $gray-100;
|
||||
$text-muted: $gray-400;
|
||||
$dark: $gray-100;
|
||||
$light: $gray-700;
|
||||
$vue-multiselect-option-selected-bg: $gray-600;
|
||||
@import "@scss/common/vue-multiselect.scss";
|
||||
color: $vue-multiselect-color;
|
||||
|
||||
input {
|
||||
color: $vue-multiselect-color;
|
||||
}
|
||||
|
||||
.multiselect__option--group {
|
||||
padding: 0;
|
||||
}
|
||||
.margin-filler {
|
||||
padding: calc($vue-multiselect-padding-y + 0.17em) $vue-multiselect-padding-x;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<v-multiselect
|
||||
v-model="selectedNation"
|
||||
:allow-empty="false"
|
||||
:options="forFind"
|
||||
:group-select="false"
|
||||
label="searchText"
|
||||
track-by="value"
|
||||
:show-labels="false"
|
||||
selectLabel="선택(엔터)"
|
||||
selectGroupLabel=""
|
||||
selectedLabel="선택됨"
|
||||
deselectLabel="해제(엔터)"
|
||||
deselectGroupLabel=""
|
||||
placeholder="국가 선택"
|
||||
:maxHeight="400"
|
||||
:searchable="searchable"
|
||||
>
|
||||
<template #option="props">
|
||||
<span
|
||||
:style="{
|
||||
color: props.option.notAvailable ? 'red' : undefined,
|
||||
}"
|
||||
>
|
||||
{{ props.option.title }}
|
||||
<span v-if="props.option.info">({{ props.option.info }})</span>
|
||||
{{ props.option.notAvailable ? "(불가)" : undefined }}
|
||||
</span>
|
||||
</template>
|
||||
<template #singleLabel="props">
|
||||
<span
|
||||
:style="{
|
||||
color: props.option.notAvailable ? 'red' : undefined,
|
||||
}"
|
||||
>
|
||||
{{ props.option.simpleName }}
|
||||
{{ props.option.notAvailable ? "(불가)" : undefined }}</span
|
||||
>
|
||||
</template>
|
||||
</v-multiselect>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { convertSearch초성 } from "@/util/convertSearch초성";
|
||||
import { defineComponent, type PropType } from "vue";
|
||||
import type { procNationItem } from "./processingRes";
|
||||
|
||||
type SelectedNation = {
|
||||
value: number;
|
||||
searchText: string;
|
||||
title: string;
|
||||
simpleName: string;
|
||||
info?: string;
|
||||
notAvailable?: boolean;
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
nations: {
|
||||
type: Map as PropType<Map<number, procNationItem>>,
|
||||
required: true,
|
||||
},
|
||||
searchable: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
data() {
|
||||
const forFind = [];
|
||||
const targets = new Map<number, SelectedNation>();
|
||||
let selectedNation;
|
||||
for (const nationItem of this.nations.values()) {
|
||||
const obj: SelectedNation = {
|
||||
value: nationItem.id,
|
||||
title: nationItem.name,
|
||||
info: nationItem.info,
|
||||
simpleName: nationItem.name,
|
||||
notAvailable: nationItem.notAvailable,
|
||||
searchText: convertSearch초성(nationItem.name).join("|"),
|
||||
};
|
||||
if (nationItem.id == this.modelValue) {
|
||||
selectedNation = obj;
|
||||
}
|
||||
forFind.push(obj);
|
||||
targets.set(nationItem.id, obj);
|
||||
}
|
||||
return {
|
||||
selectedNation,
|
||||
forFind,
|
||||
targets,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
modelValue(val: number) {
|
||||
const target = this.targets.get(val);
|
||||
this.selectedNation = target;
|
||||
},
|
||||
selectedNation(val: SelectedNation) {
|
||||
this.$emit("update:modelValue", val.value);
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,87 @@
|
||||
import { mb_strwidth } from "@/util/mb_strwidth";
|
||||
import { isArray, isBoolean, isInteger, isString } from "lodash";
|
||||
|
||||
|
||||
const stringArgs = [
|
||||
'nationName', 'optionText', 'itemType', 'nationType', 'itemCode', 'commandType',
|
||||
] as const;
|
||||
const intArgs = [
|
||||
'crewType', 'destGeneralID', 'destCityID', 'destNationID',
|
||||
'amount', 'colorType',
|
||||
'year', 'month',
|
||||
'srcArmType', 'destArmType', //숙련전환 전용
|
||||
] as const;
|
||||
|
||||
const booleanArgs = [
|
||||
'isGold', 'buyRice',
|
||||
] as const;
|
||||
|
||||
const integerArrayArgs = [
|
||||
'destNationIDList', 'destGeneralIDList', 'amountList'
|
||||
] as const;
|
||||
|
||||
type StringKeys = typeof stringArgs[number];
|
||||
type IntKeys = typeof intArgs[number];
|
||||
type BooleanKeys = typeof booleanArgs[number];
|
||||
type IntegerArrayKeys = typeof integerArrayArgs[number];
|
||||
|
||||
|
||||
export type Args = {
|
||||
[key in StringKeys]?: string;
|
||||
} & {
|
||||
[key in IntKeys]?: number;
|
||||
} & {
|
||||
[key in BooleanKeys]?: boolean;
|
||||
} & {
|
||||
[key in IntegerArrayKeys]?: number[]
|
||||
};
|
||||
|
||||
|
||||
|
||||
export function testSubmitArgs(args: Args): true | ['int' | 'string' | 'boolean' | 'int[]', keyof Args, number | string | boolean | number[]] {
|
||||
for (const intKey of intArgs) {
|
||||
const testVal = args[intKey];
|
||||
if (testVal === undefined) {
|
||||
continue;
|
||||
}
|
||||
if (!isInteger(testVal)) {
|
||||
return ['int', intKey, testVal];
|
||||
}
|
||||
}
|
||||
for (const stringKey of stringArgs) {
|
||||
const testVal = args[stringKey];
|
||||
if (testVal === undefined) {
|
||||
continue;
|
||||
}
|
||||
if (!isString(testVal)) {
|
||||
return ['string', stringKey, testVal];
|
||||
}
|
||||
if(stringKey == 'nationName' && mb_strwidth(testVal) > 18){
|
||||
throw `길이가 반각 18자 분량을 넘었습니다.`;
|
||||
}
|
||||
}
|
||||
for (const booleanKey of booleanArgs) {
|
||||
const testVal = args[booleanKey];
|
||||
if (testVal === undefined) {
|
||||
continue;
|
||||
}
|
||||
if (!isBoolean(testVal)) {
|
||||
return ['boolean', booleanKey, testVal];
|
||||
}
|
||||
}
|
||||
for (const integerArrayKey of integerArrayArgs) {
|
||||
const testVal = args[integerArrayKey];
|
||||
if (testVal === undefined) {
|
||||
continue;
|
||||
}
|
||||
if (!isArray(args[integerArrayKey])) {
|
||||
return ['int[]', integerArrayKey, testVal];
|
||||
}
|
||||
for (const value of testVal) {
|
||||
if (!isInteger(value)) {
|
||||
return ['int[]', integerArrayKey, testVal];
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
import type { ItemTypeKey } from "@/defs";
|
||||
import { combineArray } from "@/util/combineArray";
|
||||
import { type Ref, ref, watch } from "vue";
|
||||
|
||||
export type procGeneralItem = {
|
||||
no: number,
|
||||
name: string,
|
||||
nationID?: number,
|
||||
officerLevel: number,
|
||||
npc: number,
|
||||
gold?: number,
|
||||
rice?: number,
|
||||
leadership: number,
|
||||
strength: number,
|
||||
intel: number,
|
||||
cityID?: number,
|
||||
crew?: number,
|
||||
train?: number,
|
||||
atmos?: number,
|
||||
troopID?: number,
|
||||
}
|
||||
|
||||
export type procGeneralList = procGeneralItem[];
|
||||
|
||||
export type procGeneralKey = 'no' | 'name' | 'nationID' | 'officerLevel' | 'npc' | 'gold' | 'rice' | 'leadership' | 'strength' | 'intel' | 'cityID' | 'crew' | 'train' | 'atmos' | 'troopID';
|
||||
|
||||
export type procGeneralRawItem = procGeneralItem[procGeneralKey][];
|
||||
|
||||
export type procTroopItem = {
|
||||
troop_leader: number,
|
||||
nation: number,
|
||||
name: string
|
||||
};
|
||||
export type procTroopList = Record<number, procTroopItem>;
|
||||
|
||||
export type procGeneralRawItemList = procGeneralRawItem[];
|
||||
|
||||
export function convertGeneralList(keys: procGeneralKey[], rawList: procGeneralRawItemList): procGeneralList {
|
||||
return combineArray(rawList, keys) as procGeneralList;
|
||||
}
|
||||
|
||||
|
||||
export type procNationItem = {
|
||||
id: number,
|
||||
name: string,
|
||||
color: string,
|
||||
power: number,
|
||||
scoutMsg?: string,
|
||||
info?: string,
|
||||
notAvailable?: boolean,
|
||||
};
|
||||
|
||||
export type procNationList = procNationItem[];
|
||||
|
||||
export type procNationTypeItem = {
|
||||
type: string,
|
||||
name: string,
|
||||
pros: string,
|
||||
cons: string,
|
||||
}
|
||||
|
||||
export type procNationTypeList = Record<string, procNationTypeItem>;
|
||||
|
||||
|
||||
export type procArmTypeItem = {
|
||||
armType: number,
|
||||
armName: string,
|
||||
values: procCrewTypeItem[],
|
||||
}
|
||||
|
||||
export type procCrewTypeItem = {
|
||||
id: number,
|
||||
reqTech: number,
|
||||
reqYear: number,
|
||||
notAvailable?: boolean,
|
||||
baseRice: number,
|
||||
baseCost: number,
|
||||
name: string,
|
||||
attack: number,
|
||||
defence: number,
|
||||
speed: number,
|
||||
avoid: number,
|
||||
img: string,
|
||||
info: string[],
|
||||
}
|
||||
|
||||
export type procItemType = {
|
||||
id: string,
|
||||
name: string,
|
||||
reqSecu: number,
|
||||
cost: number,
|
||||
info: string,//<br>
|
||||
isBuyable: boolean,
|
||||
}
|
||||
|
||||
export type procItemList = Record<ItemTypeKey, {
|
||||
typeName: string,
|
||||
values: procItemType[],
|
||||
}>
|
||||
|
||||
|
||||
//XXX: vuex 쓰기 전까지...
|
||||
export const searchableProcessingMode = 'sam.processing.searchable';
|
||||
const searchable = ref((localStorage.getItem(searchableProcessingMode) ?? "0") != "0");
|
||||
watch(searchable, (val) => {
|
||||
localStorage.setItem(searchableProcessingMode, val ? "1" : "0");
|
||||
});
|
||||
|
||||
export function getProcSearchable():Ref<boolean>{
|
||||
return searchable;
|
||||
}
|
||||
Reference in New Issue
Block a user