|
|
|
@@ -32,6 +32,11 @@
|
|
|
|
|
|
|
|
|
|
<div class="commandSelectFormAnchor">
|
|
|
|
|
<div v-if="isEditMode" class="row gx-0">
|
|
|
|
|
<div class="col-4 d-grid">
|
|
|
|
|
<BDropdown left text="보관함">
|
|
|
|
|
<!-- 즐겨찾기 모드 -->
|
|
|
|
|
</BDropdown>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="col-3 d-grid">
|
|
|
|
|
<BDropdown left text="범위">
|
|
|
|
|
<BDropdownItem @click="selectTurn()">해제</BDropdownItem>
|
|
|
|
@@ -54,37 +59,33 @@
|
|
|
|
|
</BDropdownText>
|
|
|
|
|
</BDropdown>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="col-4 d-grid">
|
|
|
|
|
<BDropdown left text="보관함">
|
|
|
|
|
<!-- 즐겨찾기 모드 -->
|
|
|
|
|
</BDropdown>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="col-5 d-grid">
|
|
|
|
|
<BDropdown right variant="info" text="선택한 턴을">
|
|
|
|
|
<BDropdownItem>
|
|
|
|
|
<BDropdownItem @click="clipboardCut">
|
|
|
|
|
<i class="bi bi-scissors"></i> 잘라내기
|
|
|
|
|
</BDropdownItem>
|
|
|
|
|
<BDropdownItem>
|
|
|
|
|
<BDropdownItem @click="clipboardCopy">
|
|
|
|
|
<i class="bi bi-files"></i> 복사하기
|
|
|
|
|
</BDropdownItem>
|
|
|
|
|
<BDropdownItem>
|
|
|
|
|
<BDropdownItem @click="clipboardPaste">
|
|
|
|
|
<i class="bi bi-clipboard-fill"></i> 붙여넣기
|
|
|
|
|
</BDropdownItem>
|
|
|
|
|
<BDropdownDivider />
|
|
|
|
|
<BDropdownItem>
|
|
|
|
|
<i class="bi bi-bookmark-plus-fill"></i> 보관하기
|
|
|
|
|
</BDropdownItem>
|
|
|
|
|
<BDropdownItem>
|
|
|
|
|
<BDropdownItem @click="subRepeatCommand">
|
|
|
|
|
<i class="bi bi-arrow-repeat"></i> 반복하기
|
|
|
|
|
</BDropdownItem>
|
|
|
|
|
<BDropdownDivider />
|
|
|
|
|
<BDropdownItem>
|
|
|
|
|
<BDropdownItem @click="eraseSelectedTurnList">
|
|
|
|
|
<i class="bi bi-eraser"></i> 비우기
|
|
|
|
|
</BDropdownItem>
|
|
|
|
|
<BDropdownItem>
|
|
|
|
|
<BDropdownItem @click="eraseAndPullCommand">
|
|
|
|
|
<i class="bi bi-arrow-bar-up"></i> 지우고 당기기
|
|
|
|
|
</BDropdownItem>
|
|
|
|
|
<BDropdownItem>
|
|
|
|
|
<BDropdownItem @click="pushEmptyCommand">
|
|
|
|
|
<i class="bi bi-arrow-bar-down"></i> 뒤로 밀기
|
|
|
|
|
</BDropdownItem>
|
|
|
|
|
<!-- 최근에 실행한 10턴 -->
|
|
|
|
@@ -263,11 +264,7 @@ selectTurn(...$event);
|
|
|
|
|
</BDropdown>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="col d-grid">
|
|
|
|
|
<BButton @click="toggleViewMaxTurn">
|
|
|
|
|
{{
|
|
|
|
|
flippedMaxTurn == viewMaxTurn ? "펼치기" : "접기"
|
|
|
|
|
}}
|
|
|
|
|
</BButton>
|
|
|
|
|
<BButton @click="toggleViewMaxTurn">{{ flippedMaxTurn == viewMaxTurn ? "펼치기" : "접기" }}</BButton>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
@@ -302,7 +299,7 @@ declare const staticValues: {
|
|
|
|
|
<script lang="ts" setup>
|
|
|
|
|
import addMilliseconds from "date-fns/esm/addMilliseconds";
|
|
|
|
|
import addMinutes from "date-fns/esm/addMinutes";
|
|
|
|
|
import { isString, range } from "lodash";
|
|
|
|
|
import { clone, isString, min, range } from "lodash";
|
|
|
|
|
import { stringifyUrl } from "query-string";
|
|
|
|
|
import { onMounted, ref, watch } from "vue";
|
|
|
|
|
import { formatTime } from "@util/formatTime";
|
|
|
|
@@ -317,6 +314,7 @@ import CommandSelectForm from "@/components/CommandSelectForm.vue";
|
|
|
|
|
import { BButton, BButtonGroup, BDropdownItem, BDropdown, BDropdownText, BDropdownDivider } from "bootstrap-vue-3";
|
|
|
|
|
import { StoredActionsHelper } from "./util/StoredActionsHelper";
|
|
|
|
|
import type { TurnObj } from '@/defs';
|
|
|
|
|
import { unwrap } from "./util/unwrap";
|
|
|
|
|
|
|
|
|
|
type TurnObjWithTime = TurnObj & {
|
|
|
|
|
time: string;
|
|
|
|
@@ -645,17 +643,28 @@ async function reserveCommandDirect(args: [number[], TurnObj][], reload = true):
|
|
|
|
|
return success;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function reserveCommand() {
|
|
|
|
|
let reqTurnList: number[];
|
|
|
|
|
if (turnList.value.size == 0) {
|
|
|
|
|
reqTurnList = Array.from(prevTurnList.value.values());
|
|
|
|
|
} else {
|
|
|
|
|
reqTurnList = Array.from(turnList.value.values());
|
|
|
|
|
function getSelectedTurnList(): number[] {
|
|
|
|
|
if (turnList.value.size) {
|
|
|
|
|
return Array.from(turnList.value);
|
|
|
|
|
}
|
|
|
|
|
if (prevTurnList.value.size) {
|
|
|
|
|
return Array.from(prevTurnList.value);
|
|
|
|
|
}
|
|
|
|
|
return [0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (reqTurnList.length == 0) {
|
|
|
|
|
reqTurnList.push(0);
|
|
|
|
|
function releaseSelectedTurnList() {
|
|
|
|
|
if (turnList.value.size > 0) {
|
|
|
|
|
prevTurnList.value.clear();
|
|
|
|
|
for (const v of turnList.value) {
|
|
|
|
|
prevTurnList.value.add(v);
|
|
|
|
|
}
|
|
|
|
|
turnList.value.clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function reserveCommand() {
|
|
|
|
|
let reqTurnList: number[] = getSelectedTurnList();
|
|
|
|
|
|
|
|
|
|
const commandName = selectedCommand.value.value;
|
|
|
|
|
|
|
|
|
@@ -682,13 +691,9 @@ async function reserveCommand() {
|
|
|
|
|
arg: {}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (turnList.value.size > 0) {
|
|
|
|
|
prevTurnList.value.clear();
|
|
|
|
|
for (const v of turnList.value) {
|
|
|
|
|
prevTurnList.value.add(v);
|
|
|
|
|
}
|
|
|
|
|
turnList.value.clear();
|
|
|
|
|
}
|
|
|
|
|
releaseSelectedTurnList();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error(e);
|
|
|
|
|
alert(`실패했습니다: ${e}`);
|
|
|
|
@@ -738,11 +743,221 @@ watch(isEditMode, newEditMode => {
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const emptyTurnObj: TurnObj = { action: '휴식', brief: '휴식', arg: {} };
|
|
|
|
|
|
|
|
|
|
const storedActionsHelper = new StoredActionsHelper(staticValues.serverNick, 'general');
|
|
|
|
|
|
|
|
|
|
const recentActions = storedActionsHelper.recentActions;
|
|
|
|
|
const storedActions = storedActionsHelper.storedActions;
|
|
|
|
|
|
|
|
|
|
async function eraseSelectedTurnList(releaseSelect = true): Promise<boolean> {
|
|
|
|
|
const result = await reserveCommandDirect([[
|
|
|
|
|
getSelectedTurnList(),
|
|
|
|
|
emptyTurnObj
|
|
|
|
|
]]);
|
|
|
|
|
if (releaseSelect) {
|
|
|
|
|
releaseSelectedTurnList();
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function refineQueryActions(): [number[], TurnObj][] {
|
|
|
|
|
const reqTurnList = getSelectedTurnList();
|
|
|
|
|
const selectedMinTurnIdx = unwrap(min<number>(reqTurnList));
|
|
|
|
|
const buffer: [number[], TurnObj][] = [];
|
|
|
|
|
for (const rawTurnIdx of reqTurnList) {
|
|
|
|
|
const turnIdx = rawTurnIdx - selectedMinTurnIdx;
|
|
|
|
|
const rawAction = reservedCommandList.value[rawTurnIdx]
|
|
|
|
|
buffer.push([[turnIdx], {
|
|
|
|
|
action: rawAction.action,
|
|
|
|
|
arg: clone(rawAction.arg),
|
|
|
|
|
brief: rawAction.brief
|
|
|
|
|
}]);
|
|
|
|
|
}
|
|
|
|
|
return buffer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function amplifyQueryActions(rawActions: [number[], TurnObj][], reqTurnList: number[]): [number[], TurnObj][] {
|
|
|
|
|
if (reqTurnList.length < 1) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let minQueryIdx = maxTurn;
|
|
|
|
|
let maxQueryIdx = 0;
|
|
|
|
|
for (const [turnList] of rawActions) {
|
|
|
|
|
for (const turnIdx of turnList) {
|
|
|
|
|
minQueryIdx = Math.min(minQueryIdx, turnIdx);
|
|
|
|
|
maxQueryIdx = Math.max(maxQueryIdx, turnIdx);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const queryLength = maxQueryIdx - minQueryIdx + 1;
|
|
|
|
|
|
|
|
|
|
const queryTurnList: number[] = [reqTurnList[0]];
|
|
|
|
|
for (const reqTurnIdx of reqTurnList) {
|
|
|
|
|
const last = queryTurnList[queryTurnList.length - 1];
|
|
|
|
|
if (reqTurnIdx < last + queryLength) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
queryTurnList.push(reqTurnIdx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const actions: [number[], TurnObj][] = [];
|
|
|
|
|
for (const [baseTurnList, action] of rawActions) {
|
|
|
|
|
const subTurnList: number[] = [];
|
|
|
|
|
for (const baseTurnIdx of baseTurnList) {
|
|
|
|
|
for (const queryTurnIdx of queryTurnList) {
|
|
|
|
|
const targetTurn = baseTurnIdx + queryTurnIdx;
|
|
|
|
|
if (targetTurn >= maxTurn) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
subTurnList.push(baseTurnIdx + queryTurnIdx);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (subTurnList.length == 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
actions.push([subTurnList, action]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return actions;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const clipboard = ref<[number[], TurnObj][] | undefined>(undefined);
|
|
|
|
|
|
|
|
|
|
async function clipboardCut(releaseSelect = true) {
|
|
|
|
|
clipboardCopy(false);
|
|
|
|
|
return eraseSelectedTurnList(releaseSelect);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function clipboardCopy(releaseSelect = true) {
|
|
|
|
|
clipboard.value = refineQueryActions();
|
|
|
|
|
if (releaseSelect) {
|
|
|
|
|
releaseSelectedTurnList();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function clipboardPaste(releaseSelect = true) {
|
|
|
|
|
const rawActions = clipboard.value;
|
|
|
|
|
if (rawActions === undefined) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const actions = amplifyQueryActions(rawActions, getSelectedTurnList());
|
|
|
|
|
if (actions.length === 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const result = await reserveCommandDirect(actions);
|
|
|
|
|
if (releaseSelect) {
|
|
|
|
|
releaseSelectedTurnList();
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function subRepeatCommand(releaseSelect = true): Promise<boolean> {
|
|
|
|
|
const reqTurnList = getSelectedTurnList().sort();
|
|
|
|
|
const selectedMinTurnIdx = reqTurnList[0];
|
|
|
|
|
const selectedMaxTurnIdx = reqTurnList[reqTurnList.length - 1];
|
|
|
|
|
const queryLength = selectedMaxTurnIdx - selectedMinTurnIdx + 1;
|
|
|
|
|
|
|
|
|
|
const rawActions = refineQueryActions();
|
|
|
|
|
const actions = amplifyQueryActions(rawActions, range(selectedMinTurnIdx, maxTurn, queryLength));
|
|
|
|
|
|
|
|
|
|
const result = await reserveCommandDirect(actions);
|
|
|
|
|
if (releaseSelect) {
|
|
|
|
|
releaseSelectedTurnList();
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function eraseAndPullCommand(releaseSelect = true): Promise<boolean> {
|
|
|
|
|
const reqTurnList = getSelectedTurnList().sort();
|
|
|
|
|
const selectedMinTurnIdx = reqTurnList[0];
|
|
|
|
|
const selectedMaxTurnIdx = reqTurnList[reqTurnList.length - 1];
|
|
|
|
|
const queryLength = selectedMaxTurnIdx - selectedMinTurnIdx + 1;
|
|
|
|
|
|
|
|
|
|
if (selectedMinTurnIdx === 0) {
|
|
|
|
|
await pushGeneralCommand(-queryLength);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (selectedMinTurnIdx + queryLength == maxTurn) {
|
|
|
|
|
return eraseSelectedTurnList(releaseSelect);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const actions: [number[], TurnObj][] = [];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const emptyTurnList: number[] = [];
|
|
|
|
|
|
|
|
|
|
for (const srcTurnIdx of range(selectedMinTurnIdx + queryLength, maxTurn)) {
|
|
|
|
|
const rawAction = reservedCommandList.value[srcTurnIdx];
|
|
|
|
|
if (rawAction.action == emptyTurnObj.action) {
|
|
|
|
|
emptyTurnList.push(srcTurnIdx - queryLength);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
actions.push([[srcTurnIdx - queryLength], {
|
|
|
|
|
action: rawAction.action,
|
|
|
|
|
arg: rawAction.arg,
|
|
|
|
|
brief: rawAction.brief
|
|
|
|
|
}]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
emptyTurnList.push(...range(maxTurn - queryLength, maxTurn));
|
|
|
|
|
actions.push([emptyTurnList, emptyTurnObj]);
|
|
|
|
|
|
|
|
|
|
const result = await reserveCommandDirect(actions);
|
|
|
|
|
if (releaseSelect) {
|
|
|
|
|
releaseSelectedTurnList();
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function pushEmptyCommand(releaseSelect = true): Promise<boolean> {
|
|
|
|
|
const reqTurnList = getSelectedTurnList().sort();
|
|
|
|
|
const selectedMinTurnIdx = reqTurnList[0];
|
|
|
|
|
const selectedMaxTurnIdx = reqTurnList[reqTurnList.length - 1];
|
|
|
|
|
const queryLength = selectedMaxTurnIdx - selectedMinTurnIdx + 1;
|
|
|
|
|
|
|
|
|
|
if (selectedMinTurnIdx === 0) {
|
|
|
|
|
await pushGeneralCommand(queryLength);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (selectedMaxTurnIdx == maxTurn) {
|
|
|
|
|
return eraseSelectedTurnList(releaseSelect);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const actions: [number[], TurnObj][] = [];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const emptyTurnList: number[] = [];
|
|
|
|
|
|
|
|
|
|
for (const srcTurnIdx of range(selectedMinTurnIdx, maxTurn - queryLength)) {
|
|
|
|
|
const rawAction = reservedCommandList.value[srcTurnIdx];
|
|
|
|
|
if (rawAction.action == emptyTurnObj.action) {
|
|
|
|
|
emptyTurnList.push(srcTurnIdx + queryLength);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
actions.push([[srcTurnIdx + queryLength], {
|
|
|
|
|
action: rawAction.action,
|
|
|
|
|
arg: rawAction.arg,
|
|
|
|
|
brief: rawAction.brief
|
|
|
|
|
}]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
emptyTurnList.push(...range(selectedMinTurnIdx, selectedMinTurnIdx + queryLength));
|
|
|
|
|
actions.push([emptyTurnList, emptyTurnObj]);
|
|
|
|
|
|
|
|
|
|
const result = await reserveCommandDirect(actions);
|
|
|
|
|
if (releaseSelect) {
|
|
|
|
|
releaseSelectedTurnList();
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
void reloadCommandList();
|
|
|
|
|
});
|
|
|
|
|