refactor: 部分 UI 换成新 UI

This commit is contained in:
unanmed 2025-06-25 23:43:19 +08:00
parent 010383a914
commit 45c1d8c952
9 changed files with 264 additions and 1735 deletions

View File

@ -6,6 +6,7 @@ import { TextAlign } from './textboxTyper';
import { Page, PageExpose } from './page';
import { GameUI, IUIMountable, SetupComponentOptions } from '@motajs/system-ui';
import { useKey } from '../use';
import { sleep } from 'mutate-animate';
export interface ConfirmBoxProps extends DefaultProps, TextContentProps {
text: string;
@ -192,7 +193,7 @@ export const ConfirmBox = defineComponent<
);
}, confirmBoxProps);
export type ChoiceKey = string | number | symbol;
export type ChoiceKey = string | number;
export type ChoiceItem = [key: ChoiceKey, text: string];
export interface ChoicesProps extends DefaultProps, TextContentProps {
@ -211,6 +212,7 @@ export interface ChoicesProps extends DefaultProps, TextContentProps {
titleFill?: CanvasStyle;
pad?: number;
interval?: number;
selected?: number;
}
export type ChoicesEmits = {
@ -233,7 +235,8 @@ const choicesProps = {
'titleFont',
'titleFill',
'pad',
'interval'
'interval',
'selected'
],
emits: ['choose']
} satisfies SetupComponentOptions<
@ -283,7 +286,7 @@ export const Choices = defineComponent<
>((props, { emit, attrs }) => {
const titleHeight = ref(0);
const contentHeight = ref(0);
const selected = ref(0);
const selected = ref(props.selected ?? 0);
const pageCom = ref<PageExpose>();
const choiceSize = reactive<[number, number][]>([]);
@ -634,6 +637,122 @@ export function getChoice<T extends ChoiceKey = ChoiceKey>(
});
}
function getChoiceRoute() {
const route = core.status.replay.toReplay[0];
if (!route.startsWith('choices:')) {
return 0;
} else {
return Number(route.slice(8));
}
}
/**
* getConfirm
* 使
* ```ts
* const confirm = await routedConfirm(
* // 在哪个 UI 控制器上打开,对于一般 UI 组件来说,直接填写 props.controller 即可
* props.controller,
* // 确认内容
* '确认要 xxx 吗?',
* // 确认框的位置,宽度由下一个参数指定,高度参数由组件内部计算得出,指定无效
* [240, 240, void 0, void 0, 0.5, 0.5],
* // 宽度设为 240
* 240,
* // 可以给选择框传入其他的 props例如指定字体此项可选
* { font: new Font('Verdana', 20) }
* );
* // 之后,就可以直接判断 confirm 来执行不同的操作了
* if (confirm) { ... }
* ```
* @param controller UI
* @param text
* @param loc
* @param width
* @param props props {@link ConfirmBoxProps}
*/
export async function routedConfirm(
controller: IUIMountable,
text: string,
loc: ElementLocator,
width: number,
props?: Partial<ConfirmBoxProps>
) {
if (core.isReplaying()) {
const confirm = getChoiceRoute() === 1;
const timeout = core.control.__replay_getTimeout();
if (timeout === 0) return confirm;
const instance = controller.open(ConfirmBoxUI, {
...(props ?? {}),
text,
loc,
width,
defaultYes: confirm
});
await sleep(core.control.__replay_getTimeout());
controller.close(instance);
return confirm;
} else {
const confirm = await getConfirm(controller, text, loc, width, props);
core.status.route.push(`choices:${confirm ? 1 : 0}`);
return confirm;
}
}
/**
* getChoice
* 使
* ```ts
* const choice = await routedChoice(
* // 在哪个 UI 控制器上打开,对于一般 UI 组件来说,直接填写 props.controller 即可
* props.controller,
* // 选项内容,参考 Choices 的注释
* [[0, '选项1'], [1, '选项2'], [2, '选项3']],
* // 选择框的位置,宽度由下一个参数指定,高度参数由组件内部计算得出,指定无效
* [240, 240, void 0, void 0, 0.5, 0.5],
* // 宽度设为 240
* 240,
* // 可以给选择框传入其他的 props例如指定标题此项可选
* { title: '选项标题' }
* );
* // 之后,就可以直接判断 choice 来执行不同的操作了
* if (choice === 0) { ... }
* ```
* @param controller UI
* @param choices
* @param loc
* @param width
* @param props props {@link ChoicesProps}
*/
export async function routedChoices(
controller: IUIMountable,
choices: ChoiceItem[],
loc: ElementLocator,
width: number,
props?: Partial<ChoicesProps>
) {
if (core.isReplaying()) {
const selected = getChoiceRoute();
const timeout = core.control.__replay_getTimeout();
if (timeout === 0) return selected;
const instance = controller.open(ChoicesUI, {
...(props ?? {}),
choices,
loc,
width,
selected
});
await sleep(core.control.__replay_getTimeout());
controller.close(instance);
return selected;
} else {
const choice = await getChoice(controller, choices, loc, width, props);
const index = choices.findIndex(v => v[1] === choice);
core.status.route.push(`choices:${index}`);
return choice;
}
}
/** @see {@link ConfirmBox} */
export const ConfirmBoxUI = new GameUI('confirm-box', ConfirmBox);
/** @see {@link Choices} */

View File

@ -1363,20 +1363,6 @@ export function calMapWalls(floor: FloorIds, nocache: boolean = false) {
return res;
}
/* @__PURE__ */ export function drawPolygons(floor: FloorIds) {
const polygons = calMapPolygons(floor);
const ctx = core.createCanvas('polygons', 0, 0, 480, 480, 130);
ctx.lineWidth = 1;
ctx.lineJoin = 'round';
ctx.strokeStyle = 'white';
for (const p of polygons) {
for (const [x, y, w, h] of p) {
ctx.strokeRect(x, y, w, h);
}
}
}
export class LayerShadowExtends implements ILayerRenderExtends {
static shadowList: Set<LayerShadowExtends> = new Set();
id: string = 'shadow';

View File

@ -30,6 +30,7 @@
<script lang="ts" setup>
import { isMobile } from '../use';
import { detailInfo, getSpecialHint } from '../tools/book';
import Scroll from '../components/scroll.vue';
const props = defineProps<{
fromBook?: boolean;

View File

@ -31,12 +31,7 @@
class="detial-more"
v-if="panel === 'special'"
>
<span
id="enemy-target"
class="button-text more"
@click="changePanel($event, 'target')"
><LeftOutlined />
</span>
<span id="enemy-target" class="button-text more"> </span>
<span
id="critical-more"
class="button-text more"
@ -81,7 +76,6 @@ import { useDrag } from '../use';
import EnemySpecial from '../panel/enemySpecial.vue';
import { LeftOutlined, RightOutlined } from '@ant-design/icons-vue';
import EnemyCritical from '../panel/enemyCritical.vue';
// import EnemyTarget from '../panel/enemyTarget.vue';
import { detailInfo } from '../tools/book';
import { gameKey } from '@motajs/system-action';

View File

@ -1272,7 +1272,6 @@ control.prototype.startReplay = function (list) {
core.status.replay.totalList = core.status.route.concat(list);
core.status.replay.steps = 0;
core.status.replay.save = [];
core.createCanvas('replay', 0, core._PY_ - 40, core._PX_, 40, 199);
core.setOpacity('replay', 0.6);
this._replay_drawProgress();
core.updateStatusBar(false, true);
@ -1407,7 +1406,6 @@ control.prototype.rewindReplay = function () {
steps: data.replay.steps,
save: save
};
core.createCanvas('replay', 0, core._PY_ - 40, core._PX_, 40, 199);
core.setOpacity('replay', 0.6);
core.control._replay_drawProgress();
core.updateStatusBar(false, true);

View File

@ -2520,70 +2520,6 @@ events.prototype._precompile_switch = function (data) {
};
events.prototype._action_choices = function (data, x, y, prefix) {
data.choices = data.choices.filter(function (x) {
if (x._disabled) return false;
if (x.condition == null || x.condition == '') return true;
try {
return core.calValue(x.condition, prefix);
} catch (e) {
return true;
}
});
if (data.choices.length == 0) return this.doAction();
if (core.isReplaying()) {
var action = core.status.replay.toReplay.shift();
if (
action.indexOf('choices:') == 0 &&
!(action == 'choices:none' && !data.timeout)
) {
var index = action.substring(8);
if (!this.__action_choices_replaying(data, index)) {
core.control._replay_error(action);
return;
}
} else {
// 容错录像
if (main.replayChecking) {
// 录像验证系统中选最后一项
if (action != 'choices:none')
core.status.replay.toReplay.unshift(action); // 首先归还刚才读出的下一步操作
core.events.__action_choices_replaying(data, -1);
} else {
// 正常游戏中弹窗选择
core.myprompt(
'录像回放出错!当前需要执行选择项但录像中未记录。\n如需修复请输入您要选的项从0起点击取消将不会修复。',
0,
function (value) {
if (value == null) {
core.control._replay_error(action);
return;
}
if (action != 'choices:none')
core.status.replay.toReplay.unshift(action); // 首先归还刚才读出的下一步操作
core.events.__action_choices_replaying(
data,
((parseInt(value) || 0) + data.choices.length) %
data.choices.length
);
}
);
}
}
} else {
if (data.timeout) {
core.status.event.interval = setTimeout(function () {
core.status.route.push('choices:none');
core.setFlag('timeout', 0);
core.doAction();
}, data.timeout);
}
core.status.event.timeout = new Date().getTime() + (data.timeout || 0);
}
for (var i = 0; i < data.choices.length; i++) {
if (typeof data.choices[i] === 'string')
data.choices[i] = { text: data.choices[i] };
data.choices[i].text = core.replaceText(data.choices[i].text, prefix);
}
core.ui.drawChoices(
core.replaceText(data.text, prefix),
data.choices,
@ -2646,41 +2582,17 @@ events.prototype._precompile_choices = function (data) {
events.prototype._action_confirm = function (data, x, y, prefix) {
data.text = core.replaceText(data.text, prefix);
core.status.event.ui = { text: data.text, yes: data.yes, no: data.no };
if (core.isReplaying()) {
var action = core.status.replay.toReplay.shift();
if (
action.indexOf('choices:') == 0 &&
!(action == 'choices:none' && !data.timeout)
) {
var index = action.substring(8);
if (
index == 'none' ||
((index = parseInt(index)) >= 0 && index % 100 < 2)
) {
this.__action_confirm_replaying(data, index);
} else {
core.control._replay_error(action);
return;
}
} else {
// 录像中未记录选了哪个,则选默认值,而不是直接报错
if (action != 'choices:none')
core.status.replay.toReplay.unshift(action);
this.__action_confirm_replaying(data, data['default'] ? 0 : 1);
core.ui.drawConfirmBox(
data.text,
() => {
core.insertAction(data.yes ?? []);
core.doAction();
},
() => {
core.insertAction(data.no ?? []);
core.doAction();
}
} else {
core.status.event.selection = data['default'] ? 0 : 1;
if (data.timeout) {
core.status.event.interval = setTimeout(function () {
core.status.route.push('choices:none');
core.setFlag('timeout', 0);
core.doAction();
}, data.timeout);
}
core.status.event.timeout = new Date().getTime() + (data.timeout || 0);
}
core.ui.drawConfirmBox(data.text);
);
};
events.prototype.__action_confirm_replaying = function (data, index) {

File diff suppressed because it is too large Load Diff

View File

@ -1179,6 +1179,7 @@ interface Control {
): boolean;
_setAutomaticRoute_drawRoute(step: any): void;
_setAutomaticRoute_setAutoSteps(step: any): void;
__replay_getTimeout(): number;
}
declare const control: new () => Control;

View File

@ -764,7 +764,7 @@ interface Ui {
*/
drawChoices(
content: string,
choices: string[],
choices: object[],
width?: number,
ctx?: CtxRefer
): void;