Merge remote-tracking branch 'origin/main' into fix/command-map-detail-images-20260813
This commit is contained in:
@@ -0,0 +1,524 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, ref, watch } from 'vue';
|
||||
import { configuredGameAssetUrl } from '../../utils/imageAssets';
|
||||
import type { RecruitmentCrewType, RecruitmentInfo } from './types';
|
||||
|
||||
const props = defineProps<{
|
||||
commandKey: 'che_징병' | 'che_모병';
|
||||
info: RecruitmentInfo;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: 'update:args', args: Record<string, unknown>): void;
|
||||
(event: 'update:valid', valid: boolean): void;
|
||||
(event: 'submit'): void;
|
||||
}>();
|
||||
|
||||
const selectedCrewTypeId = ref(0);
|
||||
const amount = ref(1);
|
||||
const showUnavailable = ref<Record<number, boolean>>({});
|
||||
|
||||
const commandName = computed(() => (props.commandKey === 'che_모병' ? '모병' : '징병'));
|
||||
const goldCoefficient = computed(() => (props.commandKey === 'che_모병' ? 2 : 1));
|
||||
const crewTypes = computed(() => props.info.groups.flatMap((group) => group.values));
|
||||
const selectedCrewType = computed(
|
||||
() => crewTypes.value.find((crewType) => crewType.id === selectedCrewTypeId.value) ?? crewTypes.value[0] ?? null
|
||||
);
|
||||
const valid = computed(
|
||||
() => Boolean(selectedCrewType.value?.available) && Number.isFinite(amount.value) && amount.value >= 1
|
||||
);
|
||||
const estimatedGold = computed(() =>
|
||||
selectedCrewType.value ? Math.ceil(amount.value * selectedCrewType.value.baseCost * goldCoefficient.value) : 0
|
||||
);
|
||||
|
||||
const filledAmount = (crewType: RecruitmentCrewType | null): number => {
|
||||
if (crewType?.id === props.info.currentCrewTypeId) {
|
||||
return Math.max(1, props.info.fullLeadership - Math.floor(props.info.crew / 100));
|
||||
}
|
||||
return Math.max(1, props.info.fullLeadership);
|
||||
};
|
||||
|
||||
const initialize = () => {
|
||||
showUnavailable.value = Object.fromEntries(props.info.groups.map((group) => [group.armType, false]));
|
||||
const current = crewTypes.value.find((crewType) => crewType.id === props.info.currentCrewTypeId);
|
||||
selectedCrewTypeId.value =
|
||||
(current ?? crewTypes.value.find((crewType) => crewType.available) ?? crewTypes.value[0])?.id ?? 0;
|
||||
amount.value = filledAmount(selectedCrewType.value);
|
||||
};
|
||||
|
||||
const selectCrewType = (crewType: RecruitmentCrewType) => {
|
||||
selectedCrewTypeId.value = crewType.id;
|
||||
amount.value = filledAmount(crewType);
|
||||
};
|
||||
|
||||
const setHalf = () => {
|
||||
amount.value = Math.max(1, Math.ceil(props.info.fullLeadership * 0.5));
|
||||
};
|
||||
|
||||
const setFilled = () => {
|
||||
amount.value = filledAmount(selectedCrewType.value);
|
||||
};
|
||||
|
||||
const setFull = () => {
|
||||
amount.value = Math.max(1, Math.floor(props.info.fullLeadership * 1.2));
|
||||
};
|
||||
|
||||
const updateAmount = (event: Event) => {
|
||||
const value = Number((event.currentTarget as HTMLInputElement).value);
|
||||
amount.value = Number.isFinite(value) ? value : 0;
|
||||
};
|
||||
|
||||
const submit = async (crewType?: RecruitmentCrewType) => {
|
||||
if (crewType) selectCrewType(crewType);
|
||||
await nextTick();
|
||||
if (valid.value) emit('submit');
|
||||
};
|
||||
|
||||
const imageUrl = (crewTypeId: number): string => `${configuredGameAssetUrl()}/crewtype${crewTypeId}.png`;
|
||||
const availabilityClass = (crewType: RecruitmentCrewType): string =>
|
||||
crewType.available ? (crewType.special ? 'special' : 'available') : 'unavailable';
|
||||
const displayDecimal = (value: number): string => value.toFixed(1);
|
||||
|
||||
watch(() => [props.commandKey, props.info] as const, initialize, { immediate: true, deep: true });
|
||||
watch(
|
||||
[selectedCrewTypeId, amount, valid],
|
||||
() => {
|
||||
emit('update:args', {
|
||||
crewType: selectedCrewTypeId.value,
|
||||
amount: Math.max(0, Math.trunc(amount.value * 100)),
|
||||
});
|
||||
emit('update:valid', valid.value);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="recruitment-command-form" data-testid="recruitment-command-form">
|
||||
<div class="recruitment-intro legacy-bg0">
|
||||
병사를 모집합니다.
|
||||
<template v-if="props.commandKey === 'che_징병'"> 훈련과 사기치는 낮지만 가격이 저렴합니다. </template>
|
||||
<template v-else>훈련과 사기치는 높지만 자금이 많이 듭니다.</template>
|
||||
<br />가능한 수보다 많게 입력하면 가능한 최대 병사를 모집합니다.<br />
|
||||
이미 병사가 있는 경우 추가 {{ commandName }}되며, 병종이 다르면 기존 병사는 소집해제됩니다.<br />
|
||||
현재 {{ commandName }} 가능한 기본 병종은 <span class="legend available">녹색</span>, 특수 병종은
|
||||
<span class="legend special">초록색</span>, 불가능한 병종은
|
||||
<span class="legend unavailable">빨간색</span>으로 표시됩니다.
|
||||
</div>
|
||||
|
||||
<div class="recruitment-list-front">
|
||||
<p v-if="props.commandKey === 'che_모병'" class="mercenary-notice">모병은 가격 2배의 자금이 소요됩니다.</p>
|
||||
<div class="recruitment-status legacy-bg2">
|
||||
<span>현재 기술력 : {{ props.info.techLevel }}등급</span>
|
||||
<span
|
||||
>현재 통솔 :
|
||||
<strong :class="{ injured: props.info.leadership < props.info.fullLeadership }">
|
||||
{{ props.info.leadership }}
|
||||
</strong></span
|
||||
>
|
||||
<span>최대 통솔 : {{ props.info.fullLeadership }}</span>
|
||||
<span>현재 병종 : {{ props.info.currentCrewTypeName }}</span>
|
||||
<span>현재 병사 : {{ props.info.crew.toLocaleString() }}</span>
|
||||
<span>현재 자금 : {{ props.info.gold.toLocaleString() }}</span>
|
||||
</div>
|
||||
|
||||
<div v-if="selectedCrewType" class="mobile-selected-panel legacy-bg0">
|
||||
<div
|
||||
class="crew-image"
|
||||
:style="{ backgroundImage: `url(${JSON.stringify(imageUrl(selectedCrewType.id))})` }"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="crew-name"
|
||||
:class="availabilityClass(selectedCrewType)"
|
||||
:title="selectedCrewType.available ? '현재 선택 가능' : '현재 선택 불가'"
|
||||
>
|
||||
{{ selectedCrewType.name }}<small>{{ selectedCrewType.available ? '가능' : '불가' }}</small>
|
||||
</button>
|
||||
<div class="amount-panel">
|
||||
<div class="quick-buttons">
|
||||
<button type="button" @click="setHalf">절반</button>
|
||||
<button type="button" @click="setFilled">채우기</button>
|
||||
<button type="button" @click="setFull">가득</button>
|
||||
</div>
|
||||
<label>
|
||||
<span>병력</span>
|
||||
<input :value="amount" type="number" min="1" step="1" @input="updateAmount" />
|
||||
<span>00명</span><output>{{ estimatedGold.toLocaleString() }}금</output>
|
||||
</label>
|
||||
</div>
|
||||
<button type="button" class="submit-recruit" :disabled="!valid" @click="submit()">
|
||||
{{ commandName }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="crew-grid crew-header legacy-bg1" aria-hidden="true">
|
||||
<span class="crew-image">사진</span><span class="crew-name">병종</span><span class="attack">공격</span
|
||||
><span class="defence">방어</span><span class="speed">기동</span><span class="avoid">회피</span
|
||||
><span class="cost">가격</span><span class="rice">군량</span><span class="amount-panel">병사 수</span
|
||||
><span class="crew-action">행동</span><span class="crew-info">특징</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="crew-list">
|
||||
<section v-for="group in props.info.groups" :key="group.armType" class="crew-group">
|
||||
<header>
|
||||
<strong>{{ group.armName }} 계열</strong>
|
||||
<button
|
||||
type="button"
|
||||
:class="{ active: showUnavailable[group.armType] }"
|
||||
@click="showUnavailable[group.armType] = !showUnavailable[group.armType]"
|
||||
>
|
||||
{{
|
||||
showUnavailable[group.armType]
|
||||
? '선택 할 수 있는 병종만 보기'
|
||||
: '선택 할 수 없는 병종도 보기'
|
||||
}}
|
||||
</button>
|
||||
</header>
|
||||
<div
|
||||
v-for="crewType in group.values.filter(
|
||||
(entry) => showUnavailable[group.armType] || entry.available
|
||||
)"
|
||||
:key="crewType.id"
|
||||
class="crew-grid crew-row"
|
||||
:class="{ selected: crewType.id === selectedCrewTypeId }"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
:aria-label="`${crewType.name} ${crewType.available ? '선택 가능' : '선택 불가'}`"
|
||||
@click="selectCrewType(crewType)"
|
||||
@keydown.enter="selectCrewType(crewType)"
|
||||
>
|
||||
<span
|
||||
class="crew-image"
|
||||
:style="{ backgroundImage: `url(${JSON.stringify(imageUrl(crewType.id))})` }"
|
||||
/>
|
||||
<span class="crew-name" :class="availabilityClass(crewType)"
|
||||
>{{ crewType.name }}<small>{{ crewType.available ? '가능' : '불가' }}</small></span
|
||||
>
|
||||
<span class="attack"><small>공격</small>{{ crewType.attack }}</span>
|
||||
<span class="defence"><small>방어</small>{{ crewType.defence }}</span>
|
||||
<span class="speed"><small>기동</small>{{ crewType.speed }}</span>
|
||||
<span class="avoid"><small>회피</small>{{ crewType.avoid }}</span>
|
||||
<span class="cost"><small>가격</small>{{ displayDecimal(crewType.baseCost) }}</span>
|
||||
<span class="rice"><small>군량</small>{{ displayDecimal(crewType.baseRice) }}</span>
|
||||
<span class="amount-panel" @click.stop>
|
||||
<span class="quick-buttons">
|
||||
<button
|
||||
type="button"
|
||||
@click="
|
||||
selectCrewType(crewType);
|
||||
setHalf();
|
||||
"
|
||||
>
|
||||
절반
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="
|
||||
selectCrewType(crewType);
|
||||
setFilled();
|
||||
"
|
||||
>
|
||||
채우기
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="
|
||||
selectCrewType(crewType);
|
||||
setFull();
|
||||
"
|
||||
>
|
||||
가득
|
||||
</button>
|
||||
</span>
|
||||
<label>
|
||||
<span>병력</span>
|
||||
<input
|
||||
:value="crewType.id === selectedCrewTypeId ? amount : filledAmount(crewType)"
|
||||
type="number"
|
||||
min="1"
|
||||
step="1"
|
||||
@focus="selectCrewType(crewType)"
|
||||
@input="
|
||||
selectCrewType(crewType);
|
||||
updateAmount($event);
|
||||
"
|
||||
/>
|
||||
<span>00명</span>
|
||||
</label>
|
||||
</span>
|
||||
<span class="crew-action" @click.stop>
|
||||
<button type="button" :disabled="!crewType.available" @click="submit(crewType)">
|
||||
{{ commandName }}
|
||||
</button>
|
||||
</span>
|
||||
<span class="crew-info"
|
||||
><span v-for="line in crewType.info" :key="line">{{ line }}</span></span
|
||||
>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.recruitment-command-form {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
color: #fff;
|
||||
background: #1d1d1d;
|
||||
font: 14px/1.25 var(--sammo-font-sans);
|
||||
}
|
||||
.recruitment-intro {
|
||||
padding: 6px;
|
||||
}
|
||||
.legend,
|
||||
.crew-name {
|
||||
color: #fff;
|
||||
}
|
||||
.available {
|
||||
background: green !important;
|
||||
}
|
||||
.special {
|
||||
background: limegreen !important;
|
||||
}
|
||||
.unavailable {
|
||||
background: #c90000 !important;
|
||||
}
|
||||
.mercenary-notice {
|
||||
margin: 0;
|
||||
padding: 4px;
|
||||
text-align: center;
|
||||
}
|
||||
.recruitment-status {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(6, minmax(0, 1fr));
|
||||
text-align: center;
|
||||
}
|
||||
.recruitment-status > span {
|
||||
padding: 4px 2px;
|
||||
}
|
||||
.injured {
|
||||
color: red;
|
||||
}
|
||||
.crew-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 64px 90px repeat(6, minmax(42px, 1fr)) 210px 72px 250px;
|
||||
align-items: stretch;
|
||||
text-align: center;
|
||||
}
|
||||
.crew-header {
|
||||
min-height: 30px;
|
||||
align-items: center;
|
||||
}
|
||||
.crew-header > span,
|
||||
.crew-row > span {
|
||||
min-width: 0;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
.crew-group > header {
|
||||
min-height: 38px;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 205px;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #777;
|
||||
}
|
||||
.crew-group > header strong {
|
||||
padding: 0 12px;
|
||||
font-size: 1.3em;
|
||||
}
|
||||
.crew-group > header button {
|
||||
min-height: 34px;
|
||||
border: 0;
|
||||
background: #444;
|
||||
color: #fff;
|
||||
}
|
||||
.crew-group > header button.active {
|
||||
background: #d89a00;
|
||||
color: #111;
|
||||
}
|
||||
.crew-row {
|
||||
width: 100%;
|
||||
min-height: 64px;
|
||||
border: 0;
|
||||
border-bottom: 1px solid #777;
|
||||
padding: 0;
|
||||
background: #242424;
|
||||
color: #fff;
|
||||
font: inherit;
|
||||
cursor: pointer;
|
||||
}
|
||||
.crew-row:hover,
|
||||
.crew-row:focus-visible,
|
||||
.crew-row.selected {
|
||||
outline: 2px solid #5bc0de;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
.crew-image {
|
||||
min-height: 64px;
|
||||
background: #222 no-repeat center;
|
||||
background-size: 64px;
|
||||
outline: 1px solid gray;
|
||||
}
|
||||
.crew-name {
|
||||
height: 100%;
|
||||
border: 0;
|
||||
font: inherit;
|
||||
}
|
||||
.crew-name small {
|
||||
display: block;
|
||||
font-size: 0.72em;
|
||||
}
|
||||
.crew-row small {
|
||||
display: none;
|
||||
}
|
||||
.amount-panel {
|
||||
align-content: center;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
.quick-buttons {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
.quick-buttons button,
|
||||
.crew-action button,
|
||||
.submit-recruit {
|
||||
min-height: 28px;
|
||||
border: 1px solid #777;
|
||||
background: #444;
|
||||
color: #fff;
|
||||
}
|
||||
.amount-panel label {
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr) auto;
|
||||
align-items: center;
|
||||
}
|
||||
.amount-panel input {
|
||||
min-width: 0;
|
||||
height: 28px;
|
||||
box-sizing: border-box;
|
||||
text-align: right;
|
||||
}
|
||||
.crew-action button,
|
||||
.submit-recruit {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #375a7f;
|
||||
}
|
||||
.crew-action button:disabled,
|
||||
.submit-recruit:disabled {
|
||||
opacity: 0.45;
|
||||
}
|
||||
.crew-info {
|
||||
padding: 4px 8px;
|
||||
text-align: left;
|
||||
}
|
||||
.crew-info > span {
|
||||
display: block;
|
||||
}
|
||||
.mobile-selected-panel {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.recruitment-command-form {
|
||||
width: 500px;
|
||||
}
|
||||
.recruitment-list-front {
|
||||
position: sticky;
|
||||
z-index: 5;
|
||||
top: 0;
|
||||
background: #1d1d1d;
|
||||
}
|
||||
.recruitment-status {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
.mobile-selected-panel {
|
||||
display: grid;
|
||||
grid-template-columns: 64px 76px 270px 90px;
|
||||
min-height: 64px;
|
||||
align-items: stretch;
|
||||
}
|
||||
.mobile-selected-panel .crew-name {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
.mobile-selected-panel .amount-panel label {
|
||||
grid-template-columns: auto minmax(0, 1fr) auto 85px;
|
||||
}
|
||||
.mobile-selected-panel output {
|
||||
box-sizing: border-box;
|
||||
min-width: 0;
|
||||
padding: 4px;
|
||||
background: #ddd;
|
||||
color: #303030;
|
||||
text-align: right;
|
||||
}
|
||||
.crew-grid {
|
||||
grid-template:
|
||||
'image name attack defence speed info' 32px
|
||||
'image name avoid cost rice info' 32px /
|
||||
64px 76px 30px 30px 30px 270px;
|
||||
}
|
||||
.crew-grid .crew-image {
|
||||
grid-area: image;
|
||||
}
|
||||
.crew-grid .crew-name {
|
||||
grid-area: name;
|
||||
}
|
||||
.crew-grid .attack {
|
||||
grid-area: attack;
|
||||
}
|
||||
.crew-grid .defence {
|
||||
grid-area: defence;
|
||||
}
|
||||
.crew-grid .speed {
|
||||
grid-area: speed;
|
||||
}
|
||||
.crew-grid .avoid {
|
||||
grid-area: avoid;
|
||||
}
|
||||
.crew-grid .cost {
|
||||
grid-area: cost;
|
||||
}
|
||||
.crew-grid .rice {
|
||||
grid-area: rice;
|
||||
}
|
||||
.crew-grid .crew-info {
|
||||
grid-area: info;
|
||||
}
|
||||
.crew-grid .amount-panel,
|
||||
.crew-grid .crew-action {
|
||||
display: none;
|
||||
}
|
||||
.crew-row small {
|
||||
display: block;
|
||||
font-size: 0.62em;
|
||||
line-height: 1;
|
||||
}
|
||||
.crew-row .crew-name small {
|
||||
font-size: 0.72em;
|
||||
}
|
||||
.crew-header .attack,
|
||||
.crew-header .defence,
|
||||
.crew-header .speed,
|
||||
.crew-header .avoid,
|
||||
.crew-header .cost,
|
||||
.crew-header .rice {
|
||||
font-size: 0.78em;
|
||||
}
|
||||
.crew-group > header {
|
||||
grid-template-columns: 7fr 5fr;
|
||||
}
|
||||
.crew-group > header button {
|
||||
font-size: 0.82em;
|
||||
}
|
||||
.crew-info {
|
||||
overflow: hidden;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -4,6 +4,7 @@ import CommandArgumentForm from '../main/CommandArgumentForm.vue';
|
||||
import CommandSelectForm from '../main/CommandSelectForm.vue';
|
||||
import { commandArgumentPresentation } from './commandArgumentPresentation';
|
||||
import DragSelect from './DragSelect.vue';
|
||||
import RecruitmentCommandForm from './RecruitmentCommandForm.vue';
|
||||
import {
|
||||
amplifyPattern,
|
||||
CommandStorage,
|
||||
@@ -132,6 +133,9 @@ const displayRows = computed(() =>
|
||||
props.rows.slice(0, expanded.value || props.compact ? props.rows.length : collapsedRowCount)
|
||||
);
|
||||
const quickPickerTop = computed(() => `${70 + (quickTarget.value ?? 0) * 34.4}px`);
|
||||
const isRecruitmentCommand = computed(
|
||||
() => selectedCommand.value?.key === 'che_징병' || selectedCommand.value?.key === 'che_모병'
|
||||
);
|
||||
const rowLabel = (row: ReservedCommandRow): string => row.label ?? labelMap.value.get(row.action) ?? row.action;
|
||||
const selectedIndices = () => normalizedSelection(selected.value, previousSelected.value, props.rows.length);
|
||||
const pattern = () => extractPattern(props.rows, selectedIndices());
|
||||
@@ -599,6 +603,7 @@ const clickOutsideMenu = (event: Event) => {
|
||||
<div
|
||||
v-if="pickerOpen"
|
||||
class="command-picker"
|
||||
:class="{ 'recruitment-picker': isRecruitmentCommand }"
|
||||
data-testid="command-picker"
|
||||
:style="quickTarget === null || props.compact ? undefined : { top: quickPickerTop }"
|
||||
>
|
||||
@@ -623,8 +628,20 @@ const clickOutsideMenu = (event: Event) => {
|
||||
>현재 상태: {{ selectedCommand.reason }} · 예약 입력은 가능합니다.</small
|
||||
>
|
||||
</div>
|
||||
<RecruitmentCommandForm
|
||||
v-if="
|
||||
isRecruitmentCommand &&
|
||||
props.commandTable?.inputOptions.recruitment &&
|
||||
(selectedCommand.key === 'che_징병' || selectedCommand.key === 'che_모병')
|
||||
"
|
||||
:command-key="selectedCommand.key"
|
||||
:info="props.commandTable.inputOptions.recruitment"
|
||||
@update:args="commandArgs = $event"
|
||||
@update:valid="commandArgsValid = $event"
|
||||
@submit="submitCommand"
|
||||
/>
|
||||
<CommandArgumentForm
|
||||
v-if="selectedCommand.reqArg && props.commandTable"
|
||||
v-else-if="selectedCommand.reqArg && props.commandTable"
|
||||
:command-key="selectedCommand.key"
|
||||
:fields="selectedCommand.inputFields"
|
||||
:options="props.commandTable.inputOptions"
|
||||
@@ -950,6 +967,20 @@ const clickOutsideMenu = (event: Event) => {
|
||||
max-height: 344px;
|
||||
}
|
||||
|
||||
.reserved-command-editor .command-picker.recruitment-picker {
|
||||
position: fixed;
|
||||
z-index: 1100;
|
||||
top: 76px;
|
||||
right: auto;
|
||||
bottom: auto;
|
||||
left: 50%;
|
||||
width: 1000px;
|
||||
height: auto;
|
||||
max-height: calc(100vh - 82px);
|
||||
overflow: auto;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
@media (min-width: 1025px) {
|
||||
.argument-expanded:not(.compact) .command-picker {
|
||||
right: 0;
|
||||
@@ -971,6 +1002,14 @@ const clickOutsideMenu = (event: Event) => {
|
||||
max-height: calc(100vh - 104px);
|
||||
overflow: auto;
|
||||
}
|
||||
.compact:not(.mobile) .command-picker.recruitment-picker {
|
||||
top: 76px;
|
||||
left: 50%;
|
||||
width: 1000px;
|
||||
max-height: calc(100vh - 82px);
|
||||
overflow: auto;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
.mobile.compact .editor-layout {
|
||||
@@ -1016,9 +1055,36 @@ const clickOutsideMenu = (event: Event) => {
|
||||
margin-top: -330px;
|
||||
overflow: visible;
|
||||
}
|
||||
.mobile.compact .command-picker.recruitment-picker {
|
||||
position: fixed;
|
||||
top: 76px;
|
||||
left: 0;
|
||||
width: 500px;
|
||||
height: auto;
|
||||
max-height: calc(100vh - 82px);
|
||||
margin-top: 0;
|
||||
overflow: auto;
|
||||
transform: none;
|
||||
}
|
||||
.mobile.compact .advanced-actions {
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 109px;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.reserved-command-editor .command-picker.recruitment-picker,
|
||||
.reserved-command-editor.compact .command-picker.recruitment-picker {
|
||||
top: 76px;
|
||||
left: 0;
|
||||
width: 500px;
|
||||
height: auto;
|
||||
max-height: calc(100vh - 82px);
|
||||
overflow: auto;
|
||||
padding: 0;
|
||||
border-right: 0;
|
||||
border-left: 0;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -58,6 +58,36 @@ export type CommandAvailability = {
|
||||
|
||||
export type CommandGroup = { category: string; values: CommandAvailability[] };
|
||||
|
||||
export type RecruitmentCrewType = {
|
||||
id: number;
|
||||
armType: number;
|
||||
name: string;
|
||||
available: boolean;
|
||||
special: boolean;
|
||||
attack: number;
|
||||
defence: number;
|
||||
speed: number;
|
||||
avoid: number;
|
||||
baseCost: number;
|
||||
baseRice: number;
|
||||
info: string[];
|
||||
};
|
||||
|
||||
export type RecruitmentInfo = {
|
||||
techLevel: number;
|
||||
leadership: number;
|
||||
fullLeadership: number;
|
||||
currentCrewTypeId: number;
|
||||
currentCrewTypeName: string;
|
||||
crew: number;
|
||||
gold: number;
|
||||
groups: Array<{
|
||||
armType: number;
|
||||
armName: string;
|
||||
values: RecruitmentCrewType[];
|
||||
}>;
|
||||
};
|
||||
|
||||
export type CommandTable = {
|
||||
general: CommandGroup[];
|
||||
nation: CommandGroup[];
|
||||
@@ -70,6 +100,7 @@ export type CommandTable = {
|
||||
nationTypes: CommandOption[];
|
||||
colors: CommandOption[];
|
||||
items: Record<string, CommandOption[]>;
|
||||
recruitment: RecruitmentInfo | null;
|
||||
context?: CommandInputContext;
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user