refactor: 修复所有的循环引用

This commit is contained in:
unanmed 2025-03-09 19:15:22 +08:00
parent 4fda246a9a
commit 23a8c0bf3a
82 changed files with 1015 additions and 2087 deletions

View File

@ -11,13 +11,6 @@ import {
Font Font
} from '@motajs/render'; } from '@motajs/render';
import { WeatherController } from '../../weather'; import { WeatherController } from '../../weather';
import {
FloorChange,
LayerGroupFilter,
LayerGroupHalo,
LayerGroupPortal,
PopText
} from '@user/legacy-plugin-client';
import { defineComponent, onMounted, reactive, ref } from 'vue'; import { defineComponent, onMounted, reactive, ref } from 'vue';
import { Textbox, Tip } from '../components'; import { Textbox, Tip } from '../components';
import { GameUI, UIController } from '@motajs/system-ui'; import { GameUI, UIController } from '@motajs/system-ui';
@ -40,6 +33,11 @@ import { jumpIgnoreFloor } from '@user/legacy-plugin-data';
import { hook } from '@user/data-base'; import { hook } from '@user/data-base';
import { FloorDamageExtends } from '../damage'; import { FloorDamageExtends } from '../damage';
import { FloorItemDetail } from '../itemDetail'; import { FloorItemDetail } from '../itemDetail';
import { LayerGroupPortal } from '../legacy/portal';
import { LayerGroupFilter } from '../legacy/gameCanvas';
import { LayerGroupHalo } from '../legacy/halo';
import { FloorChange } from '../legacy/fallback';
import { PopText } from '../legacy/pop';
const MainScene = defineComponent(() => { const MainScene = defineComponent(() => {
const layerGroupExtends: ILayerGroupRenderExtends[] = [ const layerGroupExtends: ILayerGroupRenderExtends[] = [

View File

@ -13,17 +13,14 @@ import {
StepForward, StepForward,
ViewMapIcon ViewMapIcon
} from '../components/icons'; } from '../components/icons';
import { import { getVitualKeyOnce, openDanmakuPoster } from '@motajs/legacy-ui';
generateBinary,
getVitualKeyOnce,
openDanmakuPoster
} from '@motajs/legacy-ui';
import { gameKey } from '@motajs/system-action'; import { gameKey } from '@motajs/system-action';
import { generateKeyboardEvent } from '@motajs/system-action'; import { generateKeyboardEvent } from '@motajs/system-action';
import { transitioned } from '../use'; import { transitioned } from '../use';
import { linear } from 'mutate-animate'; import { linear } from 'mutate-animate';
import { KeyCode } from '@motajs/client-base'; import { KeyCode } from '@motajs/client-base';
import { Progress } from '../components/misc'; import { Progress } from '../components/misc';
import { generateBinary } from '@motajs/legacy-common';
interface ToolbarProps extends DefaultProps { interface ToolbarProps extends DefaultProps {
loc?: ElementLocator; loc?: ElementLocator;

View File

@ -2,7 +2,7 @@ import { getHeroStatusOf, getHeroStatusOn } from '../state/hero';
import { Range, ensureArray, has, manhattan } from '@user/data-utils'; import { Range, ensureArray, has, manhattan } from '@user/data-utils';
import EventEmitter from 'eventemitter3'; import EventEmitter from 'eventemitter3';
import { hook } from '@user/data-base'; import { hook } from '@user/data-base';
import { HeroSkill, NightSpecial } from '../mechanism/misc'; import { HeroSkill, NightSpecial } from '../mechanism';
import { import {
EnemyInfo, EnemyInfo,
DamageInfo, DamageInfo,
@ -16,6 +16,7 @@ import {
HaloType, HaloType,
IEnemyCollectionEvent IEnemyCollectionEvent
} from '@motajs/types'; } from '@motajs/types';
import { isNil } from 'lodash-es';
// todo: 光环划分优先级,从而可以实现光环的多级运算 // todo: 光环划分优先级,从而可以实现光环的多级运算
@ -293,7 +294,7 @@ export class DamageEnemy implements IDamageEnemy {
// 融化融化不属于怪物光环因此不能用provide和inject计算需要在这里计算 // 融化融化不属于怪物光环因此不能用provide和inject计算需要在这里计算
const melt = flags[`melt_${floorId}`]; const melt = flags[`melt_${floorId}`];
if (has(melt) && has(this.x) && has(this.y)) { if (!isNil(melt) && !isNil(this.x) && !isNil(this.y)) {
for (const [loc, per] of Object.entries(melt)) { for (const [loc, per] of Object.entries(melt)) {
const [mx, my] = loc.split(',').map(v => parseInt(v)); const [mx, my] = loc.split(',').map(v => parseInt(v));
if ( if (

View File

@ -1 +1,2 @@
export * from './misc'; export * from './misc';
export * from './skillTree';

View File

@ -48,131 +48,6 @@ export namespace NightSpecial {
} }
} }
export namespace HeroSkill {
export const enum Skill {
None,
/** 断灭之刃 */
Blade,
/** 铸剑为盾 */
Shield,
/** 跳跃 */
Jump
}
export const Blade = Skill.Blade;
export const Shield = Skill.Shield;
export const Jump = Skill.Jump;
const skillNameMap = new Map<Skill, string>([
[Skill.Blade, '断灭之刃'],
[Skill.Shield, '铸剑为盾'],
[Skill.Jump, '跳跃']
]);
const skillDesc = new Map<Skill, (level: number) => string>([
[
Skill.Blade,
level => `攻击上升 ${level * 10}%,防御下降 ${level * 10}%`
],
[
Skill.Shield,
level => `防御上升 ${level * 10}%,攻击下降 ${level * 10}%`
],
[Skill.Jump, () => `跳过前方障碍,或踢走面前的怪物`]
]);
interface SkillSave {
autoSkill: boolean;
learned: Skill[];
}
const learned = new Set<Skill>();
let autoSkill = true;
let enabled: Skill = Skill.None;
export function getLevel(skill: Skill = getEnabled()) {
switch (skill) {
case Blade:
return getSkillLevel(2);
case Jump:
return learned.has(Jump) ? 1 : 0;
case Shield:
return getSkillLevel(10);
}
return 0;
}
export function getSkillName(skill: Skill = getEnabled()) {
return skillNameMap.get(skill) ?? '未开启技能';
}
export function getSkillDesc(
skill: Skill = getEnabled(),
level: number = getLevel()
) {
return skillDesc.get(skill)?.(level) ?? '';
}
export function setAutoSkill(auto: boolean) {
autoSkill = auto;
}
export function getAutoSkill() {
return autoSkill;
}
export function learnedSkill(skill: Skill) {
return learned.has(skill);
}
export function learnSkill(skill: Skill) {
learned.add(skill);
}
export function forgetSkill(skill: Skill) {
learned.delete(skill);
}
export function clearSkill() {
learned.clear();
}
export function saveSkill(): SkillSave {
return { autoSkill, learned: [...learned] };
}
export function loadSkill(skills: SkillSave) {
learned.clear();
for (const skill of skills.learned) {
learned.add(skill);
}
autoSkill = skills.autoSkill;
}
export function getAll() {
return learned;
}
export function toggleSkill(skill: Skill) {
if (!learned.has(skill)) return;
if (enabled !== skill) enabled = skill;
else enabled = Skill.None;
}
export function enableSkill(skill: Skill) {
if (!learned.has(skill)) return;
enabled = skill;
}
export function disableSkill() {
enabled = Skill.None;
}
export function getEnabled() {
return enabled;
}
}
export namespace BluePalace { export namespace BluePalace {
type DoorConvertInfo = [id: AllIds, x: number, y: number]; type DoorConvertInfo = [id: AllIds, x: number, y: number];

View File

@ -1,5 +1,3 @@
import { HeroSkill } from './misc';
let levels: number[] = []; let levels: number[] = [];
export type Chapter = 'chapter1' | 'chapter2'; export type Chapter = 'chapter1' | 'chapter2';
@ -314,3 +312,128 @@ export function saveSkillTree() {
export function loadSkillTree(data: number[]) { export function loadSkillTree(data: number[]) {
levels = data ?? []; levels = data ?? [];
} }
export namespace HeroSkill {
export const enum Skill {
None,
/** 断灭之刃 */
Blade,
/** 铸剑为盾 */
Shield,
/** 跳跃 */
Jump
}
export const Blade = Skill.Blade;
export const Shield = Skill.Shield;
export const Jump = Skill.Jump;
const skillNameMap = new Map<Skill, string>([
[Skill.Blade, '断灭之刃'],
[Skill.Shield, '铸剑为盾'],
[Skill.Jump, '跳跃']
]);
const skillDesc = new Map<Skill, (level: number) => string>([
[
Skill.Blade,
level => `攻击上升 ${level * 10}%,防御下降 ${level * 10}%`
],
[
Skill.Shield,
level => `防御上升 ${level * 10}%,攻击下降 ${level * 10}%`
],
[Skill.Jump, () => `跳过前方障碍,或踢走面前的怪物`]
]);
interface SkillSave {
autoSkill: boolean;
learned: Skill[];
}
const learned = new Set<Skill>();
let autoSkill = true;
let enabled: Skill = Skill.None;
export function getLevel(skill: Skill = getEnabled()) {
switch (skill) {
case Blade:
return getSkillLevel(2);
case Jump:
return learned.has(Jump) ? 1 : 0;
case Shield:
return getSkillLevel(10);
}
return 0;
}
export function getSkillName(skill: Skill = getEnabled()) {
return skillNameMap.get(skill) ?? '未开启技能';
}
export function getSkillDesc(
skill: Skill = getEnabled(),
level: number = getLevel()
) {
return skillDesc.get(skill)?.(level) ?? '';
}
export function setAutoSkill(auto: boolean) {
autoSkill = auto;
}
export function getAutoSkill() {
return autoSkill;
}
export function learnedSkill(skill: Skill) {
return learned.has(skill);
}
export function learnSkill(skill: Skill) {
learned.add(skill);
}
export function forgetSkill(skill: Skill) {
learned.delete(skill);
}
export function clearSkill() {
learned.clear();
}
export function saveSkill(): SkillSave {
return { autoSkill, learned: [...learned] };
}
export function loadSkill(skills: SkillSave) {
learned.clear();
for (const skill of skills.learned) {
learned.add(skill);
}
autoSkill = skills.autoSkill;
}
export function getAll() {
return learned;
}
export function toggleSkill(skill: Skill) {
if (!learned.has(skill)) return;
if (enabled !== skill) enabled = skill;
else enabled = Skill.None;
}
export function enableSkill(skill: Skill) {
if (!learned.has(skill)) return;
enabled = skill;
}
export function disableSkill() {
enabled = Skill.None;
}
export function getEnabled() {
return enabled;
}
}

View File

@ -1,7 +1,7 @@
import { logger } from '@motajs/common'; import { logger } from '@motajs/common';
import { EventEmitter } from 'eventemitter3'; import { EventEmitter } from 'eventemitter3';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
import { HeroSkill, NightSpecial } from '../mechanism/misc'; import { HeroSkill, NightSpecial } from '../mechanism';
/** /**
* *

View File

@ -9,7 +9,7 @@ import {
Transform, Transform,
MotaOffscreenCanvas2D MotaOffscreenCanvas2D
} from '@motajs/render'; } from '@motajs/render';
import { Pop } from '../fx/pop'; import { Pop } from '../../../client-modules/src/render/legacy/pop';
import { SplittableBall } from './palaceBossProjectile'; import { SplittableBall } from './palaceBossProjectile';
import { PointEffect } from '../fx/pointShader'; import { PointEffect } from '../fx/pointShader';
import { loading } from '@user/data-base'; import { loading } from '@user/data-base';

View File

@ -22,10 +22,10 @@ import {
ThunderProjectile ThunderProjectile
} from './towerBossProjectile'; } from './towerBossProjectile';
import { IStateDamageable } from '@user/data-state'; import { IStateDamageable } from '@user/data-state';
import { Pop } from '../fx/pop'; import { Pop } from '../../../client-modules/src/render/legacy/pop';
import { WeatherController } from '@user/client-modules';
import { loading } from '@user/data-base'; import { loading } from '@user/data-base';
import { clip } from '@user/legacy-plugin-data'; import { clip } from '@user/legacy-plugin-data';
import { WeatherController } from '@user/client-modules';
loading.once('coreInit', () => { loading.once('coreInit', () => {
const shader = new Shader(); const shader = new Shader();

View File

@ -1,6 +1,6 @@
import { Animation, hyper, linear, power, sleep } from 'mutate-animate'; import { Animation, hyper, linear, power, sleep } from 'mutate-animate';
import { Chase, ChaseData, IChaseController } from './chase'; import { Chase, ChaseData, IChaseController } from './chase';
import { completeAchievement } from '@motajs/legacy-ui'; // import { completeAchievement } from '@motajs/legacy-ui';
import { import {
Camera, Camera,
CameraAnimation, CameraAnimation,
@ -206,7 +206,7 @@ export function initChase(): IChaseController {
core.removeFlag('chaseId'); core.removeFlag('chaseId');
if (success) { if (success) {
completeAchievement('challenge', 0); // completeAchievement('challenge', 0);
} }
}); });

View File

@ -1,5 +1 @@
export * from './gameCanvas';
export * from './halo';
export * from './pointShader'; export * from './pointShader';
export * from './pop';
export * from './portal';

View File

@ -1,6 +1,3 @@
export * from './boss'; export * from './boss';
export * from './chase'; export * from './chase';
export * from './fx'; export * from './fx';
export * from './fallback';
export * from '../../client-modules/src/render/loopMap';

View File

@ -21,5 +21,4 @@ export * from './removeMap';
export * from './replay'; export * from './replay';
export * from './shop'; export * from './shop';
export * from './skill'; export * from './skill';
export * from '../../data-state/src/mechanism/skillTree';
export * from './ui'; export * from './ui';

View File

@ -1,6 +1,6 @@
// @ts-nocheck // @ts-nocheck
import { HeroSkill } from '@/game/mechanism/misc'; import { HeroSkill } from '@user/data-state';
// 所有的主动技能效果 // 所有的主动技能效果
var ignoreInJump = { var ignoreInJump = {

View File

@ -2,3 +2,4 @@ export * from './patch';
export * from './disposable'; export * from './disposable';
export * from './eventEmitter'; export * from './eventEmitter';
export * from './resource'; export * from './resource';
export * from './utils';

View File

@ -0,0 +1,48 @@
import { EVENT_KEY_CODE_MAP } from '@motajs/client-base';
export function flipBinary(num: number, col: number) {
const n = 1 << col;
if (num & n) return num & ~n;
else return num | n;
}
/**
*
* @param arr
*/
export function generateBinary(arr: boolean[]) {
let num = 0;
arr.forEach((v, i) => {
if (v) {
num |= 1 << i;
}
});
return num;
}
/**
*
* @param arr
* @param ele
*/
export function deleteWith<T>(arr: T[], ele: T): T[] {
const index = arr.indexOf(ele);
if (index === -1) return arr;
arr.splice(index, 1);
return arr;
}
export function spliceBy<T>(arr: T[], from: T): T[] {
const index = arr.indexOf(from);
if (index === -1) return arr;
arr.splice(index);
return arr;
}
/**
* keycode对应的键
* @param key
*/
export function keycode(key: number) {
return EVENT_KEY_CODE_MAP[key];
}

View File

@ -49,8 +49,13 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, onUnmounted, onUpdated, ref, watch } from 'vue'; import { onMounted, onUnmounted, onUpdated, ref, watch } from 'vue';
import { ArrowsAltOutlined, DragOutlined } from '@ant-design/icons-vue'; import { ArrowsAltOutlined, DragOutlined } from '@ant-design/icons-vue';
import { isMobile, useDrag, cancelGlobalDrag } from '../use'; import {
import { has, requireUniqueSymbol } from '../utils'; isMobile,
useDrag,
cancelGlobalDrag,
requireUniqueSymbol
} from '../use';
import { isNil } from 'lodash-es';
// todo: // todo:
@ -200,10 +205,10 @@ function resize() {
if (!main) return; if (!main) return;
if (has(props.width)) width.value = props.width; if (!isNil(props.width)) width.value = props.width;
if (has(props.height)) height.value = props.height; if (!isNil(props.height)) height.value = props.height;
if (has(props.left)) left.value = props.left; if (!isNil(props.left)) left.value = props.left;
if (has(props.top)) top.value = props.top; if (!isNil(props.top)) top.value = props.top;
const beforeWidth = width.value; const beforeWidth = width.value;
const beforeHeight = height.value; const beforeHeight = height.value;

View File

@ -9,7 +9,8 @@
<script lang="tsx" setup> <script lang="tsx" setup>
import { onMounted, onUnmounted, onUpdated } from 'vue'; import { onMounted, onUnmounted, onUpdated } from 'vue';
import { addAnimate, removeAnimate } from '../animateController'; import { addAnimate, removeAnimate } from '../animateController';
import { has, requireUniqueSymbol } from '../utils'; import { requireUniqueSymbol } from '../use';
import { isNil } from 'lodash-es';
const id = requireUniqueSymbol().toFixed(0); const id = requireUniqueSymbol().toFixed(0);
@ -27,7 +28,7 @@ let ctx: CanvasRenderingContext2D;
let drawFn: () => void; let drawFn: () => void;
function draw() { function draw() {
if (has(drawFn)) removeAnimate(drawFn); if (!isNil(drawFn)) removeAnimate(drawFn);
const cls = core.getClsFromId(props.id as AllIds); const cls = core.getClsFromId(props.id as AllIds);
const frames = core.getAnimateFrames(cls); const frames = core.getAnimateFrames(cls);

View File

@ -26,8 +26,8 @@
import { onMounted, onUpdated } from 'vue'; import { onMounted, onUpdated } from 'vue';
import { LeftOutlined } from '@ant-design/icons-vue'; import { LeftOutlined } from '@ant-design/icons-vue';
import Scroll from './scroll.vue'; import Scroll from './scroll.vue';
import { isMobile } from '../use'; import { isMobile, requireUniqueSymbol } from '../use';
import { has, requireUniqueSymbol } from '../utils'; import { isNil } from 'lodash-es';
const emits = defineEmits<{ const emits = defineEmits<{
(e: 'close'): void; (e: 'close'): void;
@ -51,10 +51,10 @@ function resize() {
left = document.getElementById(`column-left-${id}`) as HTMLDivElement; left = document.getElementById(`column-left-${id}`) as HTMLDivElement;
right = document.getElementById(`column-right-${id}`) as HTMLDivElement; right = document.getElementById(`column-right-${id}`) as HTMLDivElement;
if (has(props.width) && !isMobile) main.style.width = `${props.width}%`; if (!isNil(props.width) && !isMobile) main.style.width = `${props.width}%`;
if (has(props.height)) main.style.height = `${props.height}%`; if (!isNil(props.height)) main.style.height = `${props.height}%`;
if (has(props.left)) left.style.flexBasis = `${props.left}%`; if (!isNil(props.left)) left.style.flexBasis = `${props.left}%`;
if (has(props.right)) right.style.flexBasis = `${props.right}%`; if (!isNil(props.right)) right.style.flexBasis = `${props.right}%`;
} }
onMounted(async () => { onMounted(async () => {

View File

@ -16,7 +16,7 @@
></BoxAnimate> ></BoxAnimate>
<div <div
class="special-text" class="special-text"
v-if="has(enemy.special) && enemy.special.length > 0" v-if="!isNil(enemy.special) && enemy.special.length > 0"
> >
<template v-for="(text, i) in enemy.showSpecial"> <template v-for="(text, i) in enemy.showSpecial">
<span v-if="i < (isMobile ? 1 : 2)" <span v-if="i < (isMobile ? 1 : 2)"
@ -110,10 +110,10 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { has } from '../utils';
import BoxAnimate from '../components/boxAnimate.vue'; import BoxAnimate from '../components/boxAnimate.vue';
import { isMobile } from '../use'; import { isMobile } from '../use';
import { ToShowEnemy } from '../tools/book'; import { ToShowEnemy } from '../tools/book';
import { isNil } from 'lodash-es';
const props = defineProps<{ const props = defineProps<{
enemy: ToShowEnemy; enemy: ToShowEnemy;

View File

@ -4,11 +4,10 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, onUnmounted } from 'vue'; import { onMounted, onUnmounted } from 'vue';
import { requireUniqueSymbol } from '../utils';
import { MinimapDrawer, getArea } from '../tools/fly'; import { MinimapDrawer, getArea } from '../tools/fly';
import { useDrag, useWheel } from '../use'; import { useDrag, useWheel, requireUniqueSymbol } from '../use';
import { debounce } from 'lodash-es'; import { debounce } from 'lodash-es';
import { mainSetting } from '../preset/ui'; import { mainSetting } from '../preset/settingIns';
const props = defineProps<{ const props = defineProps<{
action?: boolean; action?: boolean;

View File

@ -12,8 +12,12 @@
<script lang="ts" setup> <script lang="ts" setup>
import { sleep } from 'mutate-animate'; import { sleep } from 'mutate-animate';
import { onMounted, onUnmounted, onUpdated } from 'vue'; import { onMounted, onUnmounted, onUpdated } from 'vue';
import { cancelGlobalDrag, useDrag, useWheel } from '../use'; import {
import { requireUniqueSymbol } from '../utils'; cancelGlobalDrag,
useDrag,
useWheel,
requireUniqueSymbol
} from '../use';
let main: HTMLDivElement; let main: HTMLDivElement;

View File

@ -1,5 +1,6 @@
import { Component, shallowReactive } from 'vue'; import { Component, shallowReactive } from 'vue';
import { EventEmitter } from '@motajs/legacy-common'; import { EventEmitter } from '@motajs/legacy-common';
import { IGameUi, IUiController } from './interface';
interface FocusEvent<T> { interface FocusEvent<T> {
focus: (before: T | null, after: T) => void; focus: (before: T | null, after: T) => void;
@ -128,7 +129,7 @@ interface MountedVBind {
[x: string]: any; [x: string]: any;
} }
export class GameUi extends EventEmitter<GameUiEvent> { export class GameUi extends EventEmitter<GameUiEvent> implements IGameUi {
static uiList: GameUi[] = []; static uiList: GameUi[] = [];
component: Component; component: Component;
@ -161,7 +162,10 @@ interface HoldOnController {
end(noClosePanel?: boolean): void; end(noClosePanel?: boolean): void;
} }
export class UiController extends Focus<IndexedGameUi> { export class UiController
extends Focus<IndexedGameUi>
implements IUiController
{
static list: UiController[] = []; static list: UiController[] = [];
list: Record<string, GameUi> = {}; list: Record<string, GameUi> = {};
num: number = 0; num: number = 0;

View File

@ -1,9 +1,11 @@
import { EventEmitter } from 'eventemitter3'; import { EventEmitter } from 'eventemitter3';
import { logger } from '@motajs/common'; import { logger } from '@motajs/common';
import { deleteWith, ensureArray, parseCss, tip } from '@motajs/legacy-ui';
import { ResponseBase } from '@motajs/client-base'; import { ResponseBase } from '@motajs/client-base';
import axios, { AxiosResponse, toFormData } from 'axios'; import axios, { AxiosResponse, toFormData } from 'axios';
import { VNode, h, shallowReactive } from 'vue'; import { VNode, h, shallowReactive } from 'vue';
import { ensureArray, parseCss } from './utils';
import { deleteWith } from '@motajs/legacy-common';
import { tip } from './use';
// /* @__PURE__ */ import { id, password } from '../../../../user'; // /* @__PURE__ */ import { id, password } from '../../../../user';
type CSSObj = Partial<Record<CanParseCss, string>>; type CSSObj = Partial<Record<CanParseCss, string>>;

View File

@ -1,6 +1,7 @@
import { ensureArray, tip } from '@motajs/legacy-ui'; import { ensureArray } from '../utils';
import { sleep } from 'mutate-animate'; import { sleep } from 'mutate-animate';
import { logger } from '@motajs/common'; import { logger } from '@motajs/common';
import { tip } from '../use';
const { gl, gl2 } = checkSupport(); const { gl, gl2 } = checkSupport();

View File

@ -9,7 +9,8 @@ export * from './fx';
export * from './animateController'; export * from './animateController';
export * from './controller'; export * from './controller';
export * from './danmaku'; export * from './danmaku';
export * from './mark'; // export * from './mark';
export * from './setting'; export * from './setting';
export * from './use'; export * from './use';
export * from './utils'; export * from './utils';
export * from './uiUtils';

View File

@ -0,0 +1,99 @@
export interface IGameUi {
id: string;
symbol: symbol;
}
export interface IMountedVBind {
num: number;
ui: IGameUi;
controller: IUiController;
[x: string]: any;
}
interface HoldOnController {
end(noClosePanel?: boolean): void;
}
type UiVOn = Record<string, (param?: any) => void>;
type UiVBind = Record<string, any>;
export interface IUiController {
stack: any[];
/**
* ui
*/
showEnd(): void;
/**
* ui
*/
showAll(): void;
/**
* id获取到ui
* @param id ui的id
*/
get(id: string): void;
/**
* ui不会导致ui整体被关闭ui背景闪烁
* holdOn使 app:
* ```txt
* hold on -> close -> use item -> hook -> stack.length === 0 ? end(): no action
* ```
*/
holdOn(): HoldOnController;
/**
* uiui都会同时关闭掉
* @param num ui的唯一标识符
*/
close(num: number): void;
/**
* id关闭所有同id的uiui后的所有ui都关闭掉
* @param id ui的id
*/
closeByName(id: string): void;
/**
* ui
* @param id ui的id
* @param vOn
* @param vBind
* @returns ui的唯一标识符
*/
open(id: string, vBind?: UiVBind, vOn?: UiVOn): void;
/**
* ui
* @param id ui的id
* @param ui GameUi实例
*/
register(...ui: IGameUi[]): void;
/**
* ui
* @param id ui的id
*/
unregister(...id: string[]): void;
/**
* ui的唯一标识符进行聚焦
* @param num ui的唯一标识符
*/
focusByNum(num: number): void;
/**
* ui
* @param num ui的唯一标识符
*/
getByNum(num: number): void;
/**
* ui的唯一标识符来判断当前是否存在某个ui
* @param id ui的唯一标识符
*/
hasName(id: string): void;
}

View File

@ -1,164 +0,0 @@
import { IDamageEnemy } from '@motajs/types';
import { fixedUi } from './preset/ui';
import { tip } from './utils';
import { ref, Ref } from 'vue';
export interface MarkInfo<T extends EnemyIds> {
id: T;
enemy: IDamageEnemy;
/**
*
* 1.
* 2.
* 3. 2/3
* 4. 1/3
* 5.
* 6.
*/
mode: number;
/** 当前提示状态,提示模式的 2-6 */
status: number;
lastAtk: number;
lastDamage: number;
markDamage?: number;
/** 数据更新用,取反更新标记信息 */
update: Ref<boolean>;
}
const uiMap = new Map<EnemyIds, number>();
const marked: MarkInfo<EnemyIds>[] = [];
/**
* 2/31/3
* @param id id
*/
export function markEnemy(id: EnemyIds) {
if (hasMarkedEnemy(id)) return;
const { DamageEnemy, getHeroStatusOn } = Mota.require('@user/data-state');
const enemy = new DamageEnemy(core.material.enemys[id]);
enemy.calAttribute();
enemy.getRealInfo();
const info: MarkInfo<EnemyIds> = {
id,
enemy,
mode: 0b011111,
lastAtk: getHeroStatusOn('atk', 'empty'),
lastDamage: enemy.calDamage().damage,
status: 0b0,
update: ref(true)
};
marked.push(info);
uiMap.set(id, fixedUi.open('markedEnemy', { enemy: info }));
tip('success', `已标记 ${enemy.enemy.name}`);
}
export function unmarkEnemy(id: EnemyIds) {
fixedUi.close(uiMap.get(id) ?? -1);
uiMap.delete(id);
const index = marked.findIndex(v => v.id === id);
if (index === -1) return;
tip('success', `已取消标记 ${marked[index].enemy.enemy.name}`);
marked.splice(index, 1);
}
export function checkMarkedEnemy() {
const { getHeroStatusOn } = Mota.require('@user/data-state');
marked.forEach(v => {
const { id, enemy, mode, lastAtk, lastDamage, markDamage } = v;
const atk = getHeroStatusOn('atk', 'empty');
let tip = 0;
if (mode & 0b11110) {
const damage = enemy.calDamage().damage;
const hp = core.status.hero.hp;
v.lastDamage = damage;
if (damage > lastDamage) return;
// 重置标记状态
if (damage > hp) {
v.status &= 0b100001;
}
if (damage > (markDamage ?? Infinity)) {
v.status &= 0b1;
}
// 能打过怪物提示、2/3提示、1/3提示、零伤提示、指定伤害提示
if (mode & (1 << 1) && damage < hp && damage > (hp * 2) / 3) {
if (!(v.status & (1 << 1))) {
v.status &= 0b100001;
v.status |= 1 << 1;
tip |= 1 << 1;
}
} else if (mode & (1 << 2) && damage > hp / 3) {
if (!(v.status & (1 << 2))) {
v.status &= 0b100011;
v.status |= 1 << 2;
tip |= 1 << 2;
}
} else if (mode & (1 << 3) && damage > 0) {
if (!(v.status & (1 << 3))) {
v.status &= 0b100111;
v.status |= 1 << 3;
tip |= 1 << 3;
}
} else if (mode & (1 << 4)) {
if (!(v.status & (1 << 4))) {
v.status &= 0b101111;
v.status |= 1 << 4;
tip |= 1 << 4;
}
}
if (mode & (1 << 5) && damage < (markDamage ?? Infinity)) {
if (!(v.status & (1 << 5))) {
if (damage < (markDamage ?? Infinity)) {
v.status |= 1 << 5;
} else {
v.status &= 0b011111;
}
}
}
}
// 临界提示
if (mode & (1 << 0)) {
const critical = enemy.calCritical(1)[0]?.atkDelta ?? Infinity;
v.lastAtk = atk + critical;
if (critical + atk > lastAtk) {
tip |= 1 << 0;
}
}
makeTip(id, tip, v);
v.update.value = !v.update.value;
});
}
function makeTip(enemy: EnemyIds, mode: number, info: MarkInfo<EnemyIds>) {
const name = core.material.enemys[enemy].name;
if (mode & (1 << 0)) {
tip('success', `已踩到 ${name} 的临界!`);
}
if (mode & (1 << 1)) {
tip('success', `已能打过 ${name}`);
}
if (mode & (1 << 2)) {
tip('success', `${name} 的伤害已降至 2/3`);
}
if (mode & (1 << 3)) {
tip('success', `${name} 的伤害已降至 1/3`);
}
if (mode & (1 << 4)) {
tip('success', `${name} 已零伤!`);
}
if (mode & (1 << 5)) {
const damage = core.formatBigNumber(info.markDamage ?? Infinity);
tip('success', `${name} 的伤害已降至 ${damage}`);
}
}
export function hasMarkedEnemy(id: EnemyIds) {
return marked.some(v => v.id === id);
}
const { hook } = Mota.require('@user/data-base');
hook.on('statusBarUpdate', () => {
checkMarkedEnemy();
});

View File

@ -67,12 +67,12 @@
> >
<span class="changable" :change="nowDamageChangable" <span class="changable" :change="nowDamageChangable"
><span style="font-family: 'FiraCode'">{{ ><span style="font-family: 'FiraCode'">{{
(nowDamage[0] as number) < 0 && !has(enemy.damage) (nowDamage[0] as number) < 0 && isNil(enemy.damage)
? '=>' ? '=>'
: '' : ''
}}</span }}</span
>{{ >{{
(nowDamage[0] as number) < 0 && !has(enemy.damage) (nowDamage[0] as number) < 0 && isNil(enemy.damage)
? format(-nowDamage[0]) ? format(-nowDamage[0])
: format(nowDamage[0]) : format(nowDamage[0])
}}</span }}</span
@ -96,8 +96,8 @@
import { computed, onMounted, ref, watch } from 'vue'; import { computed, onMounted, ref, watch } from 'vue';
import { detailInfo, getCriticalDamage, getDefDamage } from '../tools/book'; import { detailInfo, getCriticalDamage, getDefDamage } from '../tools/book';
import Chart, { ChartConfiguration } from 'chart.js/auto'; import Chart, { ChartConfiguration } from 'chart.js/auto';
import { has, setCanvasSize } from '../utils'; import { setCanvasSize } from '../utils';
import { debounce } from 'lodash-es'; import { debounce, isNil } from 'lodash-es';
import { isMobile } from '../use'; import { isMobile } from '../use';
import { createChangable } from '../tools/common'; import { createChangable } from '../tools/common';
@ -113,10 +113,10 @@ const ceil = Math.ceil;
const x = ref<number>(); const x = ref<number>();
const y = ref<number>(); const y = ref<number>();
x.value = has(x.value) x.value = !isNil(x.value)
? Math.round(x.value + core.bigmap.offsetX / 32) ? Math.round(x.value + core.bigmap.offsetX / 32)
: void 0; : void 0;
y.value = has(y.value) y.value = !isNil(y.value)
? Math.round(y.value + core.bigmap.offsetY / 32) ? Math.round(y.value + core.bigmap.offsetY / 32)
: void 0; : void 0;

View File

@ -1,85 +0,0 @@
<template>
<div id="enemy-target">
<div id="enemy-desc">
<span>怪物描述</span>
<Scroll id="enemy-desc-scroll">
<span
>&nbsp;&nbsp;&nbsp;&nbsp;{{
enemy.enemy.enemy.description
}}</span
>
</Scroll>
</div>
<a-divider dashed style="border-color: #ddd4"></a-divider>
<div>
<div id="mark-target">
<span
id="mark-info"
:style="{ color: marked ? 'lightgreen' : 'lightcoral' }"
>{{ marked ? '已标记该怪物' : '未标记该怪物' }}</span
>
<span class="button-text" @click.stop="mark">{{
marked ? '取消标记该怪物' : '标记该怪物为目标'
}}</span>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import Scroll from '../components/scroll.vue';
import { detailInfo } from '../tools/book';
import { hasMarkedEnemy, markEnemy } from '../mark';
const enemy = detailInfo.enemy!;
const marked = ref(hasMarkedEnemy(enemy.enemy.id));
function mark() {
markEnemy(enemy.enemy.id);
marked.value = hasMarkedEnemy(enemy.enemy.id);
}
</script>
<style lang="less" scoped>
#enemy-target {
width: 100%;
font-size: 160%;
}
#enemy-desc {
width: 100%;
height: 30vh;
display: flex;
flex-direction: column;
align-items: center;
}
#enemy-desc-scroll {
height: 100%;
width: 100%;
}
#mark-target {
margin-top: 10%;
width: 100%;
display: flex;
flex-direction: row;
justify-content: space-around;
font-size: 3.3vh;
}
#mark-info {
transition: color 0.2s linear;
}
@media screen and (max-width: 600px) {
#enemy-desc {
font-size: 70%;
}
#mark-target {
font-size: 100%;
}
}
</style>

View File

@ -1,6 +1,6 @@
import { Danmaku } from '../danmaku'; import { Danmaku } from '../danmaku';
import { Component, h } from 'vue'; import { Component, h } from 'vue';
import { mainSetting } from './ui'; import { mainSetting } from './settingIns';
import { getIconHeight } from '../utils'; import { getIconHeight } from '../utils';
import { BoxAnimate } from '../components'; import { BoxAnimate } from '../components';

View File

@ -1,5 +1,4 @@
import { debounce } from 'lodash-es'; import { debounce } from 'lodash-es';
import { fixedUi, mainUi } from './ui';
import { ref } from 'vue'; import { ref } from 'vue';
import { sleep } from 'mutate-animate'; import { sleep } from 'mutate-animate';
@ -16,6 +15,7 @@ const showFixed = debounce((block: Block) => {
if (!e) return; if (!e) return;
const enemy = core.status.thisMap.enemy.get(block.x, block.y); const enemy = core.status.thisMap.enemy.get(block.x, block.y);
if (!enemy) return; if (!enemy) return;
const { fixedUi } = Mota.require('@motajs/legacy-ui');
fixedUi.open( fixedUi.open(
'fixed', 'fixed',
{ enemy, close, loc: [cx, cy], hovered }, { enemy, close, loc: [cx, cy], hovered },
@ -29,6 +29,7 @@ const showFixed = debounce((block: Block) => {
const closeFixed = () => { const closeFixed = () => {
close.value = true; close.value = true;
sleep(200).then(() => { sleep(200).then(() => {
const { fixedUi } = Mota.require('@motajs/legacy-ui');
fixedUi.closeByName('fixed'); fixedUi.closeByName('fixed');
close.value = false; close.value = false;
}); });
@ -58,6 +59,7 @@ gameListener.on('mouseMove', e => {
}); });
hook.once('mounted', () => { hook.once('mounted', () => {
const { mainUi } = Mota.require('@motajs/legacy-ui');
mainUi.on('start', () => { mainUi.on('start', () => {
showFixed.cancel(); showFixed.cancel();
closeFixed(); closeFixed();

View File

@ -1,15 +1,11 @@
import { KeyCode } from '@motajs/client-base'; import { KeyCode } from '@motajs/client-base';
import { gameKey, HotkeyJSON } from '@motajs/system-action'; import { gameKey, HotkeyJSON } from '@motajs/system-action';
import {
openDanmakuPoster,
tip,
hasMarkedEnemy,
markEnemy,
unmarkEnemy
} from '@motajs/legacy-ui';
import { hovered } from './fixed'; import { hovered } from './fixed';
import { mainUi } from './ui'; import { mainUi } from './uiIns';
import { GameStorage } from '@motajs/legacy-system'; import { GameStorage } from '@motajs/legacy-system';
// import { hasMarkedEnemy, markEnemy, unmarkEnemy } from '../mark';
import { openDanmakuPoster } from '../uiUtils';
import { tip } from '../use';
export const mainScope = Symbol.for('@key_main'); export const mainScope = Symbol.for('@key_main');
@ -506,9 +502,9 @@ gameKey
.realize('mark', () => { .realize('mark', () => {
const cls = hovered?.event.cls; const cls = hovered?.event.cls;
if (cls === 'enemys' || cls === 'enemy48') { if (cls === 'enemys' || cls === 'enemy48') {
const id = hovered!.event.id as EnemyIds; // const id = hovered!.event.id as EnemyIds;
if (hasMarkedEnemy(id)) unmarkEnemy(id); // if (hasMarkedEnemy(id)) unmarkEnemy(id);
else markEnemy(id); // else markEnemy(id);
} }
}) })
.realize('special', () => { .realize('special', () => {

View File

@ -4,3 +4,5 @@ export * from './danmaku';
export * from './fixed'; export * from './fixed';
export * from './hotkey'; export * from './hotkey';
export * from './keyboard'; export * from './keyboard';
export * from './uiIns';
export * from './settingIns';

View File

@ -0,0 +1,3 @@
import { MotaSetting } from '../setting';
export const mainSetting = new MotaSetting();

View File

@ -1,6 +1,6 @@
import type { SettingComponent, SettingComponentProps } from '../setting'; import type { SettingComponent, SettingComponentProps } from '../setting';
import { Button, InputNumber, Radio } from 'ant-design-vue'; import { Button, InputNumber, Radio } from 'ant-design-vue';
import { mainUi } from './ui'; import { mainUi } from './uiIns';
import { gameKey } from '@motajs/system-action'; import { gameKey } from '@motajs/system-action';
interface Components { interface Components {

View File

@ -1,50 +1,13 @@
import { GameStorage, VirtualKey } from '@motajs/legacy-system'; import { GameStorage } from '@motajs/legacy-system';
import { import { createSettingComponents } from './settings';
createSettingComponents, import { isMobile } from '../use';
GameUi, import { MotaSetting } from '../setting';
isMobile, import { triggerFullscreen } from '../utils';
MotaSetting,
triggerFullscreen,
UI,
UiController
} from '@motajs/legacy-ui';
import settingsText from '../data/settings.json'; import settingsText from '../data/settings.json';
import { fixedUi, mainUi } from './uiIns';
import { mainSetting } from './settingIns';
//#region legacy-ui //#region legacy-ui
export const mainUi = new UiController();
mainUi.register(
new GameUi('book', UI.Book),
new GameUi('toolbox', UI.Toolbox),
new GameUi('equipbox', UI.Equipbox),
new GameUi('settings', UI.Settings),
new GameUi('desc', UI.Desc),
new GameUi('skill', UI.Skill),
new GameUi('skillTree', UI.SkillTree),
new GameUi('fly', UI.Fly),
new GameUi('fixedDetail', UI.FixedDetail),
new GameUi('shop', UI.Shop),
new GameUi('achievement', UI.Achievement),
new GameUi('hotkey', UI.Hotkey),
new GameUi('toolEditor', UI.ToolEditor),
new GameUi('virtualKey', VirtualKey)
// todo: 把游戏主 div 加入到 mainUi 里面
);
mainUi.showAll();
export const fixedUi = new UiController(true);
fixedUi.register(
new GameUi('markedEnemy', UI.Marked),
new GameUi('fixed', UI.Fixed),
new GameUi('chapter', UI.Chapter),
new GameUi('completeAchi', UI.CompleteAchi),
new GameUi('start', UI.Start),
new GameUi('toolbar', UI.Toolbar),
new GameUi('load', UI.Load),
new GameUi('danmaku', UI.Danmaku),
new GameUi('danmakuEditor', UI.DanmakuEditor),
new GameUi('tips', UI.Tips)
);
fixedUi.showAll();
const { hook } = Mota.require('@user/data-base'); const { hook } = Mota.require('@user/data-base');
hook.once('mounted', () => { hook.once('mounted', () => {
@ -84,7 +47,6 @@ hook.once('mounted', () => {
const COM = createSettingComponents(); const COM = createSettingComponents();
export const mainSetting = new MotaSetting();
// 添加不参与全局存储的设置 // 添加不参与全局存储的设置
MotaSetting.noStorage.push('action.autoSkill', 'screen.fullscreen'); MotaSetting.noStorage.push('action.autoSkill', 'screen.fullscreen');

View File

@ -0,0 +1,38 @@
import { GameUi, UiController } from '../controller';
import * as UI from '../ui';
import { VirtualKey } from '@motajs/legacy-system';
export const mainUi = new UiController();
mainUi.register(
new GameUi('book', UI.Book),
new GameUi('toolbox', UI.Toolbox),
new GameUi('equipbox', UI.Equipbox),
new GameUi('settings', UI.Settings),
new GameUi('desc', UI.Desc),
new GameUi('skill', UI.Skill),
new GameUi('skillTree', UI.SkillTree),
new GameUi('fly', UI.Fly),
new GameUi('fixedDetail', UI.FixedDetail),
new GameUi('shop', UI.Shop),
// new GameUi('achievement', UI.Achievement),
new GameUi('hotkey', UI.Hotkey),
new GameUi('toolEditor', UI.ToolEditor),
new GameUi('virtualKey', VirtualKey)
// todo: 把游戏主 div 加入到 mainUi 里面
);
mainUi.showAll();
export const fixedUi = new UiController(true);
fixedUi.register(
new GameUi('markedEnemy', UI.Marked),
new GameUi('fixed', UI.Fixed),
new GameUi('chapter', UI.Chapter),
new GameUi('completeAchi', UI.CompleteAchi),
new GameUi('start', UI.Start),
new GameUi('toolbar', UI.Toolbar),
new GameUi('load', UI.Load),
new GameUi('danmaku', UI.Danmaku),
new GameUi('danmakuEditor', UI.DanmakuEditor),
new GameUi('tips', UI.Tips)
);
fixedUi.showAll();

View File

@ -1,9 +1,6 @@
import { FunctionalComponent, reactive } from 'vue'; import { FunctionalComponent, reactive } from 'vue';
import { EventEmitter } from '@motajs/legacy-common'; import { EventEmitter } from '@motajs/legacy-common';
import { has } from './utils'; import { isNil } from 'lodash-es';
import { createSettingComponents } from './preset';
const COM = createSettingComponents();
export interface SettingComponentProps { export interface SettingComponentProps {
item: MotaSettingItem; item: MotaSettingItem;
@ -85,7 +82,7 @@ export class MotaSetting extends EventEmitter<SettingEvent> {
key: string, key: string,
name: string, name: string,
value: MotaSettingType, value: MotaSettingType,
com: SettingComponent = COM.Default, com: SettingComponent,
step: [number, number, number] = [0, 100, 1] step: [number, number, number] = [0, 100, 1]
) { ) {
const setting: MotaSettingItem = { const setting: MotaSettingItem = {
@ -162,12 +159,12 @@ export class MotaSetting extends EventEmitter<SettingEvent> {
defaultValue?: T defaultValue?: T
): T | undefined { ): T | undefined {
const setting = this.getSetting(key); const setting = this.getSetting(key);
if (!has(setting) && !has(defaultValue)) return void 0; if (isNil(setting) && isNil(defaultValue)) return void 0;
if (setting instanceof MotaSetting) { if (setting instanceof MotaSetting) {
if (has(setting)) return defaultValue; if (!isNil(setting)) return defaultValue;
return void 0; return void 0;
} else { } else {
return has(setting) ? (setting.value as T) : (defaultValue as T); return !isNil(setting) ? (setting.value as T) : (defaultValue as T);
} }
} }

View File

@ -1,120 +0,0 @@
import list from '../data/achievement.json';
import { achiDict, checkCompletionAchievement } from './completion';
import { changeLocalStorage, has } from '../utils';
import { fixedUi } from '../preset';
type AchievementList = typeof list;
export type AchievementType = keyof AchievementList;
type AchievementData = Record<AchievementType, boolean[]>;
export interface Achievement {
name: string;
text: string[];
point: number;
hide?: string;
progress?: string;
percent?: boolean;
}
export default function init() {
return { completeAchievement, hasCompletedAchievement, addMountSign };
}
export const totalPoint = Object.values(list)
.map((v: Achievement[]) =>
v.reduce((prev, curr) => {
return curr.point + prev;
}, 0)
)
.reduce((prev, curr) => prev + curr);
/**
*
* @param type
* @param index
*/
export function completeAchievement(type: AchievementType, index: number) {
if (flags.debug || hasCompletedAchievement(type, index)) return;
changeLocalStorage<AchievementData>(
'achievement',
data => {
data[type][index] = true;
return data;
},
{
normal: [],
challenge: [],
explore: []
}
);
if (type === 'explore' && !Object.values(achiDict).includes(index)) {
checkCompletionAchievement();
}
fixedUi.open('completeAchi', {
complete: `${type},${index}`
});
}
/**
*
* @param type
* @param index
*/
export function hasCompletedAchievement(type: AchievementType, index: number) {
let data = core.getLocalStorage<AchievementData>('achievement');
if (!has(data)) {
const d = {
normal: [],
challenge: [],
explore: []
};
data = d;
core.setLocalStorage('achievement', d);
}
return data[type][index] ?? false;
}
/**
*
*/
export function getNowPoint() {
let res = 0;
for (const [type, achi] of Object.entries(list)) {
achi.forEach((v, i) => {
if (hasCompletedAchievement(type as AchievementType, i)) {
res += v.point;
}
});
}
return res;
}
// ----- 各个成就相关的函数
/**
*
* @param id id
*/
export function addMountSign(id: number) {
if (flags.debug) return;
if (
!core.getLocalStorage(`mountSign_${id}`, false) &&
!hasCompletedAchievement('explore', 1)
) {
changeLocalStorage(
'mountSign',
n => {
if (n + 1 >= 5) {
completeAchievement('explore', 1);
for (const i of [1, 2, 3, 4, 5]) {
core.removeLocalStorage(`mountSign_${i}`);
}
}
return n + 1;
},
0
);
core.setLocalStorage(`mountSign_${id}`, true);
}
}

View File

@ -1,4 +1,4 @@
import { has } from '../utils'; import { isNil } from 'lodash-es';
import { IDamageEnemy } from '@motajs/types'; import { IDamageEnemy } from '@motajs/types';
export interface CurrentEnemy { export interface CurrentEnemy {
@ -73,7 +73,7 @@ export function getDefDamage(
if (res.length === 0) { if (res.length === 0) {
origin = dam.damage; origin = dam.damage;
if (has(origin)) { if (!isNil(origin)) {
res.push([addDef + i * ratio, origin]); res.push([addDef + i * ratio, origin]);
last = origin; last = origin;
} }
@ -113,7 +113,7 @@ export function getCriticalDamage(
if (res.length === 0) { if (res.length === 0) {
origin = dam.damage; origin = dam.damage;
if (has(origin)) { if (!isNil(origin)) {
res.push([addAtk + i * ratio, origin]); res.push([addAtk + i * ratio, origin]);
last = origin; last = origin;
} }

View File

@ -1,113 +0,0 @@
import {
AchievementType,
completeAchievement,
hasCompletedAchievement
} from './achievement';
import { changeLocalStorage } from '../utils';
import list from '../data/achievement.json';
export const floors: Record<number, FloorIds[]> = {
1: ['MT0', 'tower7']
};
const achis: Record<number, Record<AchievementType, number[]>> = {
1: {
normal: [0, 1],
challenge: [0],
explore: [1]
}
};
export const achiDict: Record<number, number> = {
1: 0
};
export function init() {
Object.values(floors).forEach((v, i) => {
const from = core.floorIds.indexOf(v[0]);
const to = core.floorIds.indexOf(v[1]);
const all = core.floorIds.slice(from, to + 1);
floors[i + 1] = all;
});
}
/**
*
*/
export function checkVisitedFloor() {
changeLocalStorage<Partial<Record<FloorIds, boolean>>>(
'visitedFloor',
data => {
let needUpdate = false;
core.floorIds.forEach(v => {
if (core.hasVisitedFloor(v)) {
data[v] = true;
needUpdate = true;
}
});
if (needUpdate) {
checkCompletionAchievement();
}
return data;
},
{}
);
}
/**
*
* @param num
*/
export function getChapterCompletion(num: number) {
if (!achis[num]) return 0;
let res = 0;
const all = floors[num];
const achiNum = Object.values(achis[num]).reduce(
(pre, cur) => pre + cur.length,
0
);
// 计算到达过的楼层
let visitedFloor = 0;
const visited = core.getLocalStorage<Partial<Record<FloorIds, boolean>>>(
'visitedFloor',
{}
);
all.forEach(v => {
if (visited[v]) visitedFloor++;
});
const floorRatio = all.length / (all.length + achiNum);
const floorPoint = (floorRatio * visitedFloor) / all.length;
let completedPoint = 0;
let totalPoint = 0;
// 计算成就,占比按成就点走
for (const [type, achi] of Object.entries(achis[num]) as [
AchievementType,
number[]
][]) {
achi.forEach(v => {
totalPoint += list[type][v].point;
if (hasCompletedAchievement(type, v)) {
completedPoint += list[type][v].point;
}
});
}
const achiPoint = (completedPoint / totalPoint) * (1 - floorRatio);
res = floorPoint + achiPoint;
return Math.floor(res * 100);
}
/**
*
*/
export function checkCompletionAchievement() {
[1].forEach(v => {
if (getChapterCompletion(v) >= 100) {
completeAchievement('explore', achiDict[v]);
}
});
}

View File

@ -1,4 +1,5 @@
import { getStatusLabel, has } from '../utils'; import { isNil } from 'lodash-es';
import { getStatusLabel } from '../utils';
/** /**
* *
@ -71,7 +72,7 @@ export function getNowStatus(nowEquip?: Equip, onCol: boolean = false) {
else status = getHeroStatusOn(v)?.toString(); else status = getHeroStatusOn(v)?.toString();
let add = 0; let add = 0;
if (has(nowEquip)) { if (!isNil(nowEquip)) {
add += Math.floor( add += Math.floor(
(nowEquip.value[v] ?? 0) * core.getBuff(v) (nowEquip.value[v] ?? 0) * core.getBuff(v)
); );

View File

@ -1,5 +1,7 @@
import { mainSetting } from '../preset/ui'; import { isNil } from 'lodash-es';
import { downloadCanvasImage, has, tip } from '../utils'; import { mainSetting } from '../preset/settingIns';
import { tip } from '../use';
import { downloadCanvasImage } from '../utils';
type BFSFromString = `${FloorIds},${number},${number},${Dir}`; type BFSFromString = `${FloorIds},${number},${number},${Dir}`;
type BFSToString = `${FloorIds},${number},${number}`; type BFSToString = `${FloorIds},${number},${number}`;
@ -163,7 +165,7 @@ export function getMapData(
noCache: boolean = false noCache: boolean = false
): MapBFSResult { ): MapBFSResult {
if (!floorId) return { maps: [], link: {} }; if (!floorId) return { maps: [], link: {} };
if (has(bfsCache[floorId]) && !noCache) return bfsCache[floorId]!; if (!isNil(bfsCache[floorId]) && !noCache) return bfsCache[floorId]!;
const queue = [floorId]; const queue = [floorId];
const used: Partial<Record<FloorIds, boolean>> = { const used: Partial<Record<FloorIds, boolean>> = {

View File

@ -1,7 +1,10 @@
export * from './achievement'; // import { init } from './achievement';
// init();
// export * from './achievement';
export * from './book'; export * from './book';
export * from './common'; export * from './common';
export * from './completion';
export * from './equipbox'; export * from './equipbox';
export * from './fixed'; export * from './fixed';
export * from './fly'; export * from './fly';

View File

@ -1,340 +0,0 @@
<template>
<div id="achievement">
<div id="tools">
<span id="back" class="button-text tools" @click="exit"
><left-outlined />返回游戏</span
>
</div>
<div id="column">
<div class="achievement-column" v-for="c of column">
<span
class="column-text button-text"
:active="selectedColumn === c"
@click="selectedColumn = c"
>{{ columnName[c] }}</span
>
</div>
</div>
<a-divider dashed id="divider"></a-divider>
<div id="list">
<div id="achievement-list" :style="{ left: `-${offset}%` }">
<div v-for="t of column" class="achievement-one">
<Scroll class="list-scroll" :width="isMobile ? 10 : 20">
<div class="list-div">
<div
v-for="a of getAllAchievements(t)"
class="list-one"
>
<div
class="list-content"
:complete="a.complete"
>
<span class="list-name">{{ a.name }}</span>
<span
class="list-text"
v-html="a.text"
></span>
<div class="list-end">
<div class="end-info">
<span
class="complete"
:complete="a.complete"
>完成情况:
{{
a.complete
? '已完成'
: '未完成'
}}</span
>
<span class="point"
>成就点数: {{ a.point }}</span
>
</div>
<div
v-if="a.progress"
class="list-progress"
>
<a-progress
:percent="a.percent"
:strokeColor="{
'0%': '#108ee9',
'100%': '#87d068'
}"
:strokeWidth="height / 150"
:format="
() =>
a.usePercent
? `${a.percent}%`
: a.progress
"
></a-progress>
</div>
</div>
</div>
<a-divider id="divider" dashed></a-divider>
</div>
</div>
</Scroll>
</div>
</div>
</div>
<div id="total-progress">
<a-progress
id="point-progress"
:percent="(nowPoint / totalPoint) * 100"
:strokeColor="{
'0%': '#108ee9',
'100%': '#87d068'
}"
:strokeWidth="height / 150"
:showInfo="false"
></a-progress>
<span id="point-number"
>成就点: {{ nowPoint }} / {{ totalPoint }}</span
>
</div>
</div>
</template>
<script lang="ts" setup>
import { computed, ref } from 'vue';
import { LeftOutlined } from '@ant-design/icons-vue';
import list from '../data/achievement.json';
import {
Achievement,
getNowPoint,
hasCompletedAchievement
} from '../tools/achievement';
import Scroll from '../components/scroll.vue';
import { isMobile } from '../use';
import { mainUi } from '../preset';
const props = defineProps<{
num: number;
}>();
type AchievementList = typeof list;
type AchievementType = keyof AchievementList;
interface ResolvedAchievement {
name: string;
text: string;
complete: boolean;
point: number;
/** number / number */
progress?: string;
percent?: number;
usePercent?: boolean;
}
const column: AchievementType[] = ['normal', 'challenge', 'explore'];
const columnName = {
normal: '普通成就',
challenge: '挑战成就',
explore: '探索成就'
};
const selectedColumn = ref<AchievementType>('normal');
const offset = computed(() => {
return column.indexOf(selectedColumn.value) * 100;
});
const height = window.innerHeight;
const width = window.innerWidth;
const totalPoint = Object.values(list)
.map((v: Achievement[]) =>
v.reduce((prev, curr) => {
return curr.point + prev;
}, 0)
)
.reduce((prev, curr) => prev + curr);
const nowPoint = getNowPoint();
/**
* 获取一个类型的所有成就
* @param type 成就类型
*/
function getAllAchievements(type: AchievementType): ResolvedAchievement[] {
return list[type].map<ResolvedAchievement>((v: Achievement, i) => {
const complete = hasCompletedAchievement(type, i);
const text = v.hide && !complete ? v.hide : v.text.join('');
const res: ResolvedAchievement = {
text,
name: v.name,
point: v.point,
complete
};
if (v.progress) {
const p = eval('`' + v.progress + '`') as string;
res.progress = p;
res.percent = Math.floor(eval(p) * 100);
if (v.percent) res.usePercent = true;
}
return res;
});
}
function exit() {
mainUi.close(props.num);
}
</script>
<style lang="less" scoped>
#achievement {
width: 90vh;
height: 90vh;
font-family: 'normal';
font-size: 150%;
display: flex;
flex-direction: column;
user-select: none;
}
#divider {
margin: 1% 0;
border-color: #ddd4;
}
#tools {
height: 5vh;
font-size: 3.2vh;
}
#column {
display: flex;
flex-direction: row;
justify-content: space-around;
margin-top: 3%;
font-size: 130%;
}
.list-scroll {
width: 100%;
height: 100%;
}
#list {
overflow: hidden;
width: 100%;
height: max-content;
}
#achievement-list {
position: relative;
width: 300%;
height: 100%;
display: flex;
flex-direction: row;
transition: left 0.4s ease;
}
.achievement-one {
width: 90vh;
}
.list-div {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
}
.list-one {
width: 70%;
.list-content {
height: 18vh;
display: flex;
flex-direction: column;
align-items: center;
border: 2px double rgba(132, 132, 132, 0.17);
border-radius: 10px;
margin: 2% 0 2.5% 0;
background-color: rgba(59, 59, 59, 0.281);
}
.list-content[complete='true'] {
background-color: rgba(239, 255, 63, 0.205);
}
.list-name {
border-bottom: 1px solid #ddd4;
}
.list-text {
font-size: 100%;
padding: 0 20px;
}
.list-end {
width: 90%;
height: 95%;
display: flex;
flex-direction: column-reverse;
font-size: 90%;
.end-info {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: end;
}
.complete {
color: lightcoral;
}
.complete[complete='true'] {
color: lightgreen;
}
}
.list-progress {
display: flex;
flex-direction: row;
align-items: center;
.progress {
width: 100%;
}
}
}
#total-progress {
width: 100%;
display: flex;
flex-direction: row;
align-items: center;
#point-progress {
width: 100%;
}
#point-number {
font-size: 70%;
margin-left: 2%;
white-space: nowrap;
}
}
@media screen and (max-width: 600px) {
#achievement {
width: 90vw;
height: 90vh;
}
.list-one {
width: 90%;
.list-content {
height: 15vh;
}
.list-end {
margin-bottom: 0.8vh;
}
}
}
</style>

View File

@ -41,21 +41,17 @@
import { onUnmounted, ref } from 'vue'; import { onUnmounted, ref } from 'vue';
import EnemyOne from '../components/enemyOne.vue'; import EnemyOne from '../components/enemyOne.vue';
import Scroll from '../components/scroll.vue'; import Scroll from '../components/scroll.vue';
import { has } from '../utils';
import BookDetail from './bookDetail.vue'; import BookDetail from './bookDetail.vue';
import { LeftOutlined } from '@ant-design/icons-vue'; import { LeftOutlined } from '@ant-design/icons-vue';
import { ToShowEnemy, detailInfo } from '../tools/book'; import { ToShowEnemy, detailInfo } from '../tools/book';
import { getDetailedEnemy } from '../tools/fixed'; import { getDetailedEnemy } from '../tools/fixed';
import { GameUi } from '../controller';
import { gameKey } from '@motajs/system-action'; import { gameKey } from '@motajs/system-action';
import { mainUi } from '../preset/ui'; import { mainSetting } from '../preset/settingIns';
import { mainSetting } from '../preset/ui';
import { isMobile } from '../use'; import { isMobile } from '../use';
import { IMountedVBind } from '../interface';
import { isNil } from 'lodash-es';
const props = defineProps<{ const props = defineProps<IMountedVBind>();
num: number;
ui: GameUi;
}>();
const floorId = const floorId =
// @ts-ignore // @ts-ignore
@ -122,12 +118,12 @@ async function show() {
* 退出怪物手册 * 退出怪物手册
*/ */
async function exit() { async function exit() {
const hold = mainUi.holdOn(); const hold = props.controller.holdOn();
mainUi.close(props.num); props.controller.close(props.num);
if (core.events.recoverEvents(core.status.event.interval)) { if (core.events.recoverEvents(core.status.event.interval)) {
hold.end(true); hold.end(true);
return; return;
} else if (has(core.status.event.ui)) { } else if (!isNil(core.status.event.ui)) {
core.status.boxAnimateObjs = []; core.status.boxAnimateObjs = [];
// @ts-ignore // @ts-ignore
core.ui._drawViewMaps(core.status.event.ui); core.ui._drawViewMaps(core.status.event.ui);

View File

@ -22,7 +22,7 @@
:from-book="fromBook" :from-book="fromBook"
v-else-if="panel === 'critical'" v-else-if="panel === 'critical'"
></EnemyCritical> ></EnemyCritical>
<EnemyTarget v-else-if="panel === 'target'"></EnemyTarget> <!-- <EnemyTarget v-else-if="panel === 'target'"></EnemyTarget> -->
</Transition> </Transition>
<div id="detail-more"> <div id="detail-more">
<Transition name="detail"> <Transition name="detail">
@ -35,8 +35,8 @@
id="enemy-target" id="enemy-target"
class="button-text more" class="button-text more"
@click="changePanel($event, 'target')" @click="changePanel($event, 'target')"
><LeftOutlined /> 怪物更多信息</span ><LeftOutlined />
> </span>
<span <span
id="critical-more" id="critical-more"
class="button-text more" class="button-text more"
@ -81,7 +81,7 @@ import { useDrag } from '../use';
import EnemySpecial from '../panel/enemySpecial.vue'; import EnemySpecial from '../panel/enemySpecial.vue';
import { LeftOutlined, RightOutlined } from '@ant-design/icons-vue'; import { LeftOutlined, RightOutlined } from '@ant-design/icons-vue';
import EnemyCritical from '../panel/enemyCritical.vue'; import EnemyCritical from '../panel/enemyCritical.vue';
import EnemyTarget from '../panel/enemyTarget.vue'; // import EnemyTarget from '../panel/enemyTarget.vue';
import { detailInfo } from '../tools/book'; import { detailInfo } from '../tools/book';
import { gameKey } from '@motajs/system-action'; import { gameKey } from '@motajs/system-action';

View File

@ -1,22 +1,17 @@
<template> <template>
<div id="chapter"> <div id="chapter">
<canvas id="chapter-back"></canvas> <canvas id="chapter-back"></canvas>
<span id="chapter-text">{{ chapter }}</span> <span id="chapter-text">{{ props.chapter }}</span>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { Animation, hyper, sleep } from 'mutate-animate'; import { Animation, hyper, sleep } from 'mutate-animate';
import { onMounted } from 'vue'; import { onMounted } from 'vue';
import { has } from '../utils'; import { IMountedVBind } from '../interface';
import { GameUi } from '../controller'; import { isNil } from 'lodash-es';
import { fixedUi } from '../preset/ui';
const props = defineProps<{ const props = defineProps<IMountedVBind>();
num: number;
ui: GameUi;
chapter: string;
}>();
let can: HTMLCanvasElement; let can: HTMLCanvasElement;
let ctx: CanvasRenderingContext2D; let ctx: CanvasRenderingContext2D;
@ -50,14 +45,14 @@ onMounted(async () => {
let started = false; let started = false;
ani.ticker.add(time => { ani.ticker.add(time => {
if (!has(time) || isNaN(time)) return; if (isNil(time) || isNaN(time)) return;
if (!started) { if (!started) {
started = true; started = true;
return; return;
} }
if (time >= 4050) { if (time >= 4050) {
fixedUi.close(props.num); props.controller.close(props.num);
ani.ticker.destroy(); ani.ticker.destroy();
} }

View File

@ -1,113 +0,0 @@
<template>
<Box id="complete-box">
<div id="complete">
<span>完成成就&nbsp;&nbsp;&nbsp;&nbsp;{{ achi.name }}</span>
<a-progress
id="progress"
:percent="progress * 100"
:strokeColor="{
'0%': '#108ee9',
'100%': '#87d068'
}"
:strokeWidth="height / 200"
:showInfo="false"
></a-progress>
<span id="point-number">成就点: {{ now }} / {{ totalPoint }}</span>
</div>
</Box>
</template>
<script lang="ts" setup>
import { sleep, Ticker } from 'mutate-animate';
import { computed, onMounted, ref } from 'vue';
import Box from '../components/box.vue';
import list from '../data/achievement.json';
import { AchievementType, getNowPoint, totalPoint } from '../tools/achievement';
import { GameUi } from '../controller';
import { fixedUi } from '../preset/ui';
const height = window.innerHeight;
const props = defineProps<{
num: number;
ui: GameUi;
complete: string;
}>();
const c = props.complete.split(',');
const type = c[0] as AchievementType;
const index = parseInt(c[1]);
const achi = list[type][index];
const point = achi.point;
const nowPoint = getNowPoint() - point;
const now = ref(nowPoint);
const progress = computed(() => now.value / totalPoint);
onMounted(async () => {
await sleep(500);
const ticker = new Ticker();
const time = Date.now();
ticker.add(() => {
const nowTime = Date.now();
if (nowTime - time > 1000) {
now.value = nowPoint + point;
ticker.destroy();
}
const ratio = (nowTime - time) / 1000;
now.value = Math.floor(nowPoint + point * ratio);
});
await sleep(4600);
fixedUi.close(props.num);
});
</script>
<style lang="less" scoped>
#complete-box {
width: 30vw;
height: 13vh;
left: 35vw;
position: fixed;
background-color: #000d;
animation: ani 5s ease 0s 1 forwards running;
z-index: 10000;
}
#complete {
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
font-family: 'normal';
font-size: 2.2vh;
align-items: center;
justify-content: center;
}
#progress {
width: 90%;
}
@keyframes ani {
0% {
top: -30vh;
}
20% {
top: 4vh;
}
80% {
top: 4vh;
}
100% {
top: -30vh;
}
}
@media screen and (max-width: 600px) {
#complete-box {
width: 90vw;
left: 5%;
}
}
</style>

View File

@ -30,7 +30,7 @@
import { nextTick, onUnmounted, reactive, watch } from 'vue'; import { nextTick, onUnmounted, reactive, watch } from 'vue';
import { Danmaku } from '../danmaku'; import { Danmaku } from '../danmaku';
import { LikeFilled } from '@ant-design/icons-vue'; import { LikeFilled } from '@ant-design/icons-vue';
import { mainSetting } from '../preset/ui'; import { mainSetting } from '../preset/settingIns';
import { debounce } from 'lodash-es'; import { debounce } from 'lodash-es';
interface ElementMap { interface ElementMap {
@ -205,10 +205,7 @@ onUnmounted(() => {});
} }
.danmaku-info { .danmaku-info {
text-shadow: text-shadow: 1px 1px 1px black, 1px -1px 1px black, -1px 1px 1px black,
1px 1px 1px black,
1px -1px 1px black,
-1px 1px 1px black,
-1px -1px 1px black; -1px -1px 1px black;
} }

View File

@ -159,21 +159,17 @@ import {
UpOutlined UpOutlined
} from '@ant-design/icons-vue'; } from '@ant-design/icons-vue';
import { Danmaku } from '../danmaku'; import { Danmaku } from '../danmaku';
import { GameUi } from '../controller';
import { sleep } from 'mutate-animate'; import { sleep } from 'mutate-animate';
import { fixedUi } from '../preset/ui'; import { calStringSize, stringifyCSS, parseCss, getIconHeight } from '../utils';
import { calStringSize, tip } from '../utils';
import { gameKey } from '@motajs/system-action'; import { gameKey } from '@motajs/system-action';
import { isNil } from 'lodash-es'; import { isNil } from 'lodash-es';
import { stringifyCSS, parseCss, getIconHeight } from '../utils';
import { logger, LogLevel } from '@motajs/common'; import { logger, LogLevel } from '@motajs/common';
import Scroll from '../components/scroll.vue'; import Scroll from '../components/scroll.vue';
import BoxAnimate from '../components/boxAnimate.vue'; import BoxAnimate from '../components/boxAnimate.vue';
import { IMountedVBind } from '../interface';
import { tip } from '../use';
const props = defineProps<{ const props = defineProps<IMountedVBind>();
num: number;
ui: GameUi;
}>();
const frequentlyIcon: (AllIds | 'hero' | `X${number}`)[] = [ const frequentlyIcon: (AllIds | 'hero' | `X${number}`)[] = [
'hero', 'hero',
@ -301,7 +297,7 @@ function close() {
mainDiv.classList.remove('danmaku-startup'); mainDiv.classList.remove('danmaku-startup');
mainDiv.classList.add('danmaku-close'); mainDiv.classList.add('danmaku-close');
sleep(200).then(() => { sleep(200).then(() => {
fixedUi.close(props.num); props.controller.close(props.num);
}); });
} }

View File

@ -22,21 +22,17 @@ import { computed, onUnmounted, ref } from 'vue';
import desc from '../data/desc.json'; import desc from '../data/desc.json';
import { splitText } from '../utils'; import { splitText } from '../utils';
import Colomn from '../components/colomn.vue'; import Colomn from '../components/colomn.vue';
import { GameUi } from '../controller';
import { gameKey } from '@motajs/system-action'; import { gameKey } from '@motajs/system-action';
import { mainUi } from '../preset/ui'; import { IMountedVBind } from '../interface';
const props = defineProps<{ const props = defineProps<IMountedVBind>();
num: number;
ui: GameUi;
}>();
type DescKey = keyof typeof desc; type DescKey = keyof typeof desc;
const selected = ref(Object.keys(desc)[0] as DescKey); const selected = ref(Object.keys(desc)[0] as DescKey);
function exit() { function exit() {
mainUi.close(props.num); props.controller.close(props.num);
} }
const content = computed(() => { const content = computed(() => {

View File

@ -125,8 +125,8 @@
<BoxAnimate <BoxAnimate
:id=" :id="
isCol isCol
? (equiped[selected] ?? 'none') ? equiped[selected] ?? 'none'
: (toShow[selected]?.[0] ?? 'none') : toShow[selected]?.[0] ?? 'none'
" "
></BoxAnimate> ></BoxAnimate>
<span>{{ equip.name }}</span> <span>{{ equip.name }}</span>
@ -183,18 +183,14 @@ import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
import Scroll from '../components/scroll.vue'; import Scroll from '../components/scroll.vue';
import { getAddStatus, getEquips, getNowStatus } from '../tools/equipbox'; import { getAddStatus, getEquips, getNowStatus } from '../tools/equipbox';
import BoxAnimate from '../components/boxAnimate.vue'; import BoxAnimate from '../components/boxAnimate.vue';
import { has, tip, type } from '../utils'; import { type, getStatusLabel } from '../utils';
import { cancelGlobalDrag, isMobile, useDrag } from '../use'; import { cancelGlobalDrag, isMobile, tip, useDrag } from '../use';
import { hyper } from 'mutate-animate'; import { hyper } from 'mutate-animate';
import { GameUi } from '../controller';
import { gameKey } from '@motajs/system-action'; import { gameKey } from '@motajs/system-action';
import { getStatusLabel } from '../utils'; import { IMountedVBind } from '../interface';
import { mainUi } from '../preset/ui'; import { isNil } from 'lodash-es';
const props = defineProps<{ const props = defineProps<IMountedVBind>();
num: number;
ui: GameUi;
}>();
const equips = ref(getEquips()); const equips = ref(getEquips());
const col = ref('all'); const col = ref('all');
@ -242,10 +238,10 @@ const equip = computed(() => {
if (isCol.value) { if (isCol.value) {
const id = equiped.value[selected.value]; const id = equiped.value[selected.value];
const e = core.material.items[id]; const e = core.material.items[id];
if (!has(e)) return none; if (isNil(e)) return none;
return e; return e;
} }
if (!has(index)) return none; if (isNil(index)) return none;
return all[index[0]]; return all[index[0]];
}); });
@ -276,7 +272,7 @@ const toShow = computed(() => {
const e = all[v[0]].equip!; const e = all[v[0]].equip!;
const t = e.type; const t = e.type;
if (sortNorm !== 'none') { if (sortNorm !== 'none') {
if (!has(e[sortBy][sortNorm])) return false; if (isNil(e[sortBy][sortNorm])) return false;
} }
if (col.value === 'all') return true; if (col.value === 'all') return true;
if (typeof t === 'string') return t === col.value; if (typeof t === 'string') return t === col.value;
@ -309,7 +305,7 @@ function changeSort() {
} }
function exit() { function exit() {
mainUi.close(props.num); props.controller.close(props.num);
} }
function clickList(i: number) { function clickList(i: number) {
@ -350,7 +346,7 @@ function canDragin(type: number) {
if (type < 0) return false; if (type < 0) return false;
const et = equip.value.equip?.type; const et = equip.value.equip?.type;
if (!core.canEquip(toShow.value[selected.value]?.[0])) return false; if (!core.canEquip(toShow.value[selected.value]?.[0])) return false;
if (!has(et)) return false; if (isNil(et)) return false;
if (typeof et === 'number') return type === et; if (typeof et === 'number') return type === et;
return equipCol[type] === et; return equipCol[type] === et;
} }
@ -427,10 +423,10 @@ function dragout(e: Event) {
} }
function toTool() { function toTool() {
mainUi.holdOn(); props.controller.holdOn();
exit(); exit();
nextTick(() => { nextTick(() => {
mainUi.open('toolbox'); props.controller.open('toolbox');
}); });
} }

View File

@ -31,13 +31,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, onUpdated, Ref, ref, watch } from 'vue'; import { onMounted, onUpdated, Ref, ref, watch } from 'vue';
import Box from '../components/box.vue'; import Box from '../components/box.vue';
import { GameUi } from '../controller';
import { nextFrame } from '../utils'; import { nextFrame } from '../utils';
import { EnemyInfo, IDamageEnemy } from '@motajs/types'; import { EnemyInfo, IDamageEnemy } from '@motajs/types';
const props = defineProps<{ const props = defineProps<{
num: number; num: number;
ui: GameUi;
enemy: IDamageEnemy; enemy: IDamageEnemy;
close: Ref<boolean>; close: Ref<boolean>;
loc: [x: number, y: number]; loc: [x: number, y: number];

View File

@ -13,14 +13,9 @@ import { getDetailedEnemy } from '../tools/fixed';
import BookDetail from './bookDetail.vue'; import BookDetail from './bookDetail.vue';
import { detailInfo } from '../tools/book'; import { detailInfo } from '../tools/book';
import { hovered } from '../preset/fixed'; import { hovered } from '../preset/fixed';
import { GameUi } from '../controller'; import { IMountedVBind } from '../interface';
import { mainUi } from '../preset/ui';
const props = defineProps<{ const props = defineProps<IMountedVBind>();
num: number;
ui: GameUi;
panel?: 'special' | 'critical' | 'target';
}>();
const panel = props.panel ?? 'special'; const panel = props.panel ?? 'special';
@ -40,7 +35,7 @@ if (hovered) {
} }
function close() { function close() {
mainUi.close(props.num); props.controller.close(props.num);
} }
</script> </script>

View File

@ -98,18 +98,14 @@ import {
DoubleRightOutlined DoubleRightOutlined
} from '@ant-design/icons-vue'; } from '@ant-design/icons-vue';
import { debounce } from 'lodash-es'; import { debounce } from 'lodash-es';
import { tip } from '../utils'; import { tip } from '../use';
import { GameUi } from '../controller';
import { gameKey } from '@motajs/system-action'; import { gameKey } from '@motajs/system-action';
import { createChangable } from '../tools/common'; import { createChangable } from '../tools/common';
import { mainUi } from '../preset/ui'; import { mainSetting } from '../preset/settingIns';
import { mainSetting } from '../preset/ui';
import { GameStorage } from '@motajs/legacy-system'; import { GameStorage } from '@motajs/legacy-system';
import { IMountedVBind } from '../interface';
const props = defineProps<{ const props = defineProps<IMountedVBind>();
num: number;
ui: GameUi;
}>();
type Loc2 = [number, number, number, number]; type Loc2 = [number, number, number, number];
@ -162,7 +158,7 @@ let thumb: HTMLCanvasElement;
let thumbCtx: CanvasRenderingContext2D; let thumbCtx: CanvasRenderingContext2D;
function exit() { function exit() {
mainUi.close(props.num); props.controller.close(props.num);
} }
const title = computed(() => { const title = computed(() => {
@ -584,10 +580,7 @@ onUnmounted(() => {
max-width: 50%; max-width: 50%;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
text-shadow: text-shadow: 1px 1px 1px black, 1px -1px 1px black, -1px 1px 1px black,
1px 1px 1px black,
1px -1px 1px black,
-1px 1px 1px black,
-1px -1px 1px black; -1px -1px 1px black;
} }

View File

@ -37,14 +37,13 @@
<script lang="ts" setup> <script lang="ts" setup>
import { Hotkey } from '@motajs/system-action'; import { Hotkey } from '@motajs/system-action';
import { GameUi } from '../controller';
import Column from '../components/colomn.vue'; import Column from '../components/colomn.vue';
import { mainUi } from '../preset/ui';
import { computed, onMounted, onUnmounted, reactive, ref } from 'vue'; import { computed, onMounted, onUnmounted, reactive, ref } from 'vue';
import { KeyCode, KeyCodeUtils } from '@motajs/client-base'; import { KeyCode, KeyCodeUtils } from '@motajs/client-base';
import { generateBinary, keycode } from '../utils';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
import { gameKey } from '@motajs/system-action'; import { gameKey } from '@motajs/system-action';
import { generateBinary, keycode } from '@motajs/legacy-common';
import { IMountedVBind } from '../interface';
interface HotkeyKeys { interface HotkeyKeys {
index: number; index: number;
@ -64,16 +63,12 @@ interface SelectedKey {
index: number; index: number;
} }
const props = defineProps<{ const props = defineProps<IMountedVBind & { hotkey: Hotkey }>();
num: number;
ui: GameUi;
hotkey: Hotkey;
}>();
const hotkey = props.hotkey; const hotkey = props.hotkey;
function close() { function close() {
mainUi.close(props.num); props.controller.close(props.num);
} }
const selectedGroup = ref('ui'); const selectedGroup = ref('ui');

View File

@ -1,4 +1,4 @@
export { default as Achievement } from './achievement.vue'; // export { default as Achievement } from './achievement.vue';
export { default as BgmList } from './bgmList.vue'; export { default as BgmList } from './bgmList.vue';
export { default as Book } from './book.vue'; export { default as Book } from './book.vue';
export { default as BookDetail } from './bookDetail.vue'; export { default as BookDetail } from './bookDetail.vue';

View File

@ -34,14 +34,10 @@ import {
import { GameUi } from '../controller'; import { GameUi } from '../controller';
import { formatSize } from '../utils'; import { formatSize } from '../utils';
import { logger } from '@motajs/common'; import { logger } from '@motajs/common';
import { fixedUi } from '../preset/ui';
import { sleep } from 'mutate-animate'; import { sleep } from 'mutate-animate';
import { IMountedVBind } from '../interface';
const props = defineProps<{ const props = defineProps<IMountedVBind>();
ui: GameUi;
num: number;
callback?: () => void;
}>();
const loading = ref(0); const loading = ref(0);
const loaded = ref(0); const loaded = ref(0);
@ -76,8 +72,8 @@ onMounted(async () => {
loadDiv.style.opacity = '0'; loadDiv.style.opacity = '0';
Mota.require('@user/data-base').loading.emit('loaded'); Mota.require('@user/data-base').loading.emit('loaded');
await sleep(1000); await sleep(1000);
fixedUi.close(props.num); props.controller.close(props.num);
fixedUi.open('start'); props.controller.open('start');
}); });
loadDiv = document.getElementById('load') as HTMLDivElement; loadDiv = document.getElementById('load') as HTMLDivElement;
}); });

View File

@ -1,157 +0,0 @@
<template>
<div id="marked-enemy">
<Box
v-model:left="boxPos.left"
v-model:top="boxPos.top"
v-model:width="boxPos.width"
v-model:height="boxPos.height"
:resizable="true"
:dragable="true"
>
<Scroll class="box-scroll" :no-scroll="true">
<div class="marked-main">
<div class="marked-info">
<BoxAnimate
:id="enemy.id"
:width="24"
:height="24"
></BoxAnimate>
<span class="marked-name marked-item">{{
getName()
}}</span>
</div>
<span class="marked-damage marked-item"
>伤害{{ format(info.damage) }}</span
>
<span class="marked-critical marked-item"
>临界{{ format(info.critical) }}</span
>
<span class="marked-critical-damage marked-item"
>减伤{{ format(info.criticalDam) }}</span
>
<span class="marked-def marked-item"
>{{ ratio }}{{ format(info.defDamage) }}</span
>
<div class="marked-button">
<span
class="marked-hide button-text"
@click.stop="hidden = true"
>隐藏盒子</span
>
<span
class="marked-cancel button-text"
@click.stop="unmarkEnemy(enemy.id)"
>取消标记</span
>
</div>
</div></Scroll
>
</Box>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref, watch } from 'vue';
import { MarkInfo, unmarkEnemy } from '../mark';
import Box from '../components/box.vue';
import Scroll from '../components/scroll.vue';
import BoxAnimate from '../components/boxAnimate.vue';
import { GameUi } from '../controller';
import { fixedUi } from '../preset/ui';
const props = defineProps<{
num: number;
ui: GameUi;
enemy: MarkInfo<EnemyIds>;
}>();
interface BoxPos {
left: number;
top: number;
width: number;
height: number;
}
interface MarkedEnemy {
damage: number;
critical: number;
criticalDam: number;
defDamage: number;
}
const enemy = props.enemy;
const ratio = core.status.thisMap?.ratio ?? 1;
const format = core.formatBigNumber;
const boxPos = reactive<BoxPos>({
left: window.innerWidth - 300,
top: 100,
width: 200,
height: 150
});
const info = reactive<MarkedEnemy>({
damage: 0,
critical: 0,
criticalDam: 0,
defDamage: 0
});
const hidden = ref(false);
watch(hidden, n => {
if (n) fixedUi.close(props.num);
});
watch(enemy.update, update);
function update() {
info.damage = enemy.enemy.calDamage().damage;
const critical = enemy.enemy.calCritical()[0];
info.critical = critical?.atkDelta ?? 0;
info.criticalDam = critical?.delta ?? 0;
info.defDamage = enemy.enemy.calDefDamage(ratio).delta;
}
function getName() {
return enemy.enemy.enemy.name;
}
</script>
<style lang="less" scoped>
#marked-enemy {
width: 100%;
height: 100%;
}
.box-scroll {
height: 100%;
width: 100%;
}
.marked-main {
padding: 1vh 0;
background-color: #0009;
display: flex;
flex-direction: column;
overflow: hidden;
}
.marked-info {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
.marked-item {
margin-left: 10%;
}
.marked-button {
align-self: center;
width: 80%;
display: flex;
flex-direction: row;
justify-content: space-around;
}
</style>

View File

@ -73,7 +73,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, onUnmounted, ref, shallowRef } from 'vue'; import { computed, onUnmounted, ref, shallowRef } from 'vue';
import { mainSetting } from '../preset/ui'; import { mainSetting } from '../preset/settingIns';
import { import {
MotaSetting, MotaSetting,
MotaSettingItem, MotaSettingItem,
@ -85,14 +85,9 @@ import { splitText } from '../utils';
import Scroll from '../components/scroll.vue'; import Scroll from '../components/scroll.vue';
import { isMobile } from '../use'; import { isMobile } from '../use';
import { gameKey } from '@motajs/system-action'; import { gameKey } from '@motajs/system-action';
import { GameUi } from '../controller'; import { IMountedVBind } from '../interface';
import { mainUi } from '../preset/ui';
const props = defineProps<{ const props = defineProps<IMountedVBind>();
info?: MotaSetting;
num: number;
ui: GameUi;
}>();
const setting = props.info ?? mainSetting; const setting = props.info ?? mainSetting;
const display = shallowRef<SettingDisplayInfo[]>([]); const display = shallowRef<SettingDisplayInfo[]>([]);
@ -145,7 +140,7 @@ function click(key: string, index: number, item: MotaSettingItem) {
} }
function exit() { function exit() {
mainUi.close(props.num); props.controller.close(props.num);
} }
gameKey.use(props.ui.symbol); gameKey.use(props.ui.symbol);

View File

@ -169,18 +169,14 @@ import {
RightOutlined, RightOutlined,
DoubleRightOutlined DoubleRightOutlined
} from '@ant-design/icons-vue'; } from '@ant-design/icons-vue';
import { splitText, tip } from '../utils'; import { splitText } from '../utils';
import Scroll from '../components/scroll.vue'; import Scroll from '../components/scroll.vue';
import BoxAnimate from '../components/boxAnimate.vue'; import BoxAnimate from '../components/boxAnimate.vue';
import { GameUi } from '../controller';
import { gameKey } from '@motajs/system-action'; import { gameKey } from '@motajs/system-action';
import { mainUi } from '../preset/ui'; import { IMountedVBind } from '../interface';
import { tip } from '../use';
const props = defineProps<{ const props = defineProps<IMountedVBind>();
num: number;
ui: GameUi;
shopId: string;
}>();
const id = props.shopId; const id = props.shopId;
const shop = core.status.shops[id] as ItemShopEvent; const shop = core.status.shops[id] as ItemShopEvent;
@ -315,7 +311,7 @@ gameKey
function exit() { function exit() {
if (bought) core.status.route.push('closeShop'); if (bought) core.status.route.push('closeShop');
mainUi.close(props.num); props.controller.close(props.num);
} }
onMounted(async () => { onMounted(async () => {

View File

@ -19,13 +19,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref } from 'vue'; import { computed, ref } from 'vue';
import skills from '../data/skill.json'; import skills from '../data/skill.json';
import { has } from '../utils';
import Column from '../components/colomn.vue'; import Column from '../components/colomn.vue';
import { mainUi } from '../preset/ui'; import { IMountedVBind } from '../interface';
import { isNil } from 'lodash-es';
const props = defineProps<{ const props = defineProps<IMountedVBind>();
num: number;
}>();
type Skills = keyof typeof skills; type Skills = keyof typeof skills;
@ -47,7 +45,7 @@ const content = computed(() => {
.map((v, i, a) => { .map((v, i, a) => {
if (/^\d+\./.test(v)) return `${'&nbsp;'.repeat(12)}${v}`; if (/^\d+\./.test(v)) return `${'&nbsp;'.repeat(12)}${v}`;
else if ( else if (
(has(a[i - 1]) && (!isNil(a[i - 1]) &&
v !== '<br>' && v !== '<br>' &&
a[i - 1] === '<br>') || a[i - 1] === '<br>') ||
i === 0 i === 0
@ -65,7 +63,7 @@ const content = computed(() => {
}); });
function exit() { function exit() {
mainUi.close(props.num); props.controller.close(props.num);
} }
</script> </script>

View File

@ -81,17 +81,14 @@
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'; import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
import { LeftOutlined, RightOutlined } from '@ant-design/icons-vue'; import { LeftOutlined, RightOutlined } from '@ant-design/icons-vue';
import Scroll from '../components/scroll.vue'; import Scroll from '../components/scroll.vue';
import { has, splitText, tip } from '../utils'; import { splitText } from '../utils';
import { isMobile } from '../use'; import { isMobile, tip } from '../use';
import { sleep } from 'mutate-animate'; import { sleep } from 'mutate-animate';
import { gameKey } from '@motajs/system-action'; import { gameKey } from '@motajs/system-action';
import { GameUi } from '../controller'; import { IMountedVBind } from '../interface';
import { mainUi } from '../preset/ui'; import { isNil } from 'lodash-es';
const props = defineProps<{ const props = defineProps<IMountedVBind>();
num: number;
ui: GameUi;
}>();
const skillTree = Mota.require('@user/legacy-plugin-data'); const skillTree = Mota.require('@user/legacy-plugin-data');
@ -179,7 +176,7 @@ const level = computed(() => {
}); });
function exit() { function exit() {
mainUi.close(props.num); props.controller.close(props.num);
} }
function resize() { function resize() {
@ -298,7 +295,7 @@ function selectChapter(delta: number) {
const now = chapterList.indexOf(chapter.value); const now = chapterList.indexOf(chapter.value);
const to = now + delta; const to = now + delta;
if (has(chapterList[to]) && flags.chapter > to) { if (!isNil(chapterList[to]) && flags.chapter > to) {
selected.value = s[chapterList[to]][0].index; selected.value = s[chapterList[to]][0].index;
chapter.value = chapterList[to]; chapter.value = chapterList[to];
update.value = !update.value; update.value = !update.value;

View File

@ -64,19 +64,14 @@ import {
FullscreenExitOutlined FullscreenExitOutlined
} from '@ant-design/icons-vue'; } from '@ant-design/icons-vue';
import { sleep } from 'mutate-animate'; import { sleep } from 'mutate-animate';
import { doByInterval } from '../utils'; import { doByInterval, triggerFullscreen } from '../utils';
import { triggerFullscreen } from '../utils';
import { isMobile } from '../use'; import { isMobile } from '../use';
import { GameUi } from '../controller';
import { gameKey } from '@motajs/system-action'; import { gameKey } from '@motajs/system-action';
import { mainUi } from '../preset/ui'; import { mainSetting } from '../preset/settingIns';
import { mainSetting } from '../preset/ui';
import { mat4 } from 'gl-matrix'; import { mat4 } from 'gl-matrix';
import { IMountedVBind } from '../interface';
const props = defineProps<{ const props = defineProps<IMountedVBind>();
num: number;
ui: GameUi;
}>();
const bg = core.material.images.images['bg.webp']; const bg = core.material.images.images['bg.webp'];
@ -158,7 +153,7 @@ async function clickStartButton(id: string) {
} }
if (id === 'replay') core.chooseReplayFile(); if (id === 'replay') core.chooseReplayFile();
if (id === 'achievement') { if (id === 'achievement') {
mainUi.open('achievement'); props.controller.open('achievement');
} }
} }

View File

@ -85,8 +85,8 @@
<span>{{ <span>{{
selected === 'none' selected === 'none'
? '永久道具' ? '永久道具'
: (getClsName(all[selected].cls as ItemMode) ?? : getClsName(all[selected].cls as ItemMode) ??
'永久道具') '永久道具'
}}</span> }}</span>
</div> </div>
</div> </div>
@ -113,17 +113,14 @@ import Scroll from '../components/scroll.vue';
import BoxAnimate from '../components/boxAnimate.vue'; import BoxAnimate from '../components/boxAnimate.vue';
import { getClsName, getItems } from '../tools/toolbox'; import { getClsName, getItems } from '../tools/toolbox';
import { isMobile } from '../use'; import { isMobile } from '../use';
import { type, has } from '../utils'; import { type } from '../utils';
import { hyper } from 'mutate-animate'; import { hyper } from 'mutate-animate';
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
import { GameUi } from '../controller';
import { gameKey } from '@motajs/system-action'; import { gameKey } from '@motajs/system-action';
import { mainUi } from '../preset/ui'; import { IMountedVBind } from '../interface';
import { isNil } from 'lodash-es';
const props = defineProps<{ const props = defineProps<IMountedVBind>();
num: number;
ui: GameUi;
}>();
type ItemMode = 'tools' | 'constants'; type ItemMode = 'tools' | 'constants';
type ShowItemIds = ItemIdOf<'constants' | 'tools'> | 'none'; type ShowItemIds = ItemIdOf<'constants' | 'tools'> | 'none';
@ -146,7 +143,7 @@ watch(index, n => {
}); });
watch(mode, n => { watch(mode, n => {
if (!has(items[n][index.value])) { if (isNil(items[n][index.value])) {
selected.value = 'none'; selected.value = 'none';
return; return;
} }
@ -172,17 +169,17 @@ async function select(id: ShowItemIds, nouse: boolean = false) {
} }
function exit() { function exit() {
mainUi.close(props.num); props.controller.close(props.num);
} }
function use(id: ShowItemIds) { function use(id: ShowItemIds) {
if (id === 'none') return; if (id === 'none') return;
if (core.canUseItem(id)) { if (core.canUseItem(id)) {
const hold = mainUi.holdOn(); const hold = props.controller.holdOn();
exit(); exit();
nextTick(() => { nextTick(() => {
core.tryUseItem(id, false, () => { core.tryUseItem(id, false, () => {
if (mainUi.stack.length === 0) { if (props.controller.stack.length === 0) {
hold.end(core.status.event.id !== 'toolbox'); hold.end(core.status.event.id !== 'toolbox');
} }
}); });
@ -196,10 +193,10 @@ function use(id: ShowItemIds) {
} }
async function toEquip() { async function toEquip() {
mainUi.holdOn(); props.controller.holdOn();
exit(); exit();
nextTick(() => { nextTick(() => {
mainUi.open('equipbox'); props.controller.open('equipbox');
}); });
} }

View File

@ -0,0 +1,47 @@
import { KeyCode } from '@motajs/client-base';
import { KeyboardEmits, Keyboard, isAssist } from '@motajs/system-action';
import { mainUi, fixedUi } from './preset/uiIns';
/**
*
* @param emitAssist true时
*
* @param assist
*/
export function getVitualKeyOnce(
emitAssist: boolean = false,
assist: number = 0,
emittable: KeyCode[] = []
): Promise<KeyboardEmits> {
// todo: 正确触发后删除监听器
return new Promise(res => {
const key = Keyboard.get('full')!;
key.withAssist(assist);
const id = mainUi.open('virtualKey', { keyboard: key });
key.on('emit', (item, assist, _index, ev) => {
ev.preventDefault();
if (emitAssist) {
if (emittable.length === 0 || emittable.includes(item.key)) {
res({ key: item.key, assist: 0 });
key.disposeScope();
mainUi.close(id);
}
} else {
if (
!isAssist(item.key) &&
(emittable.length === 0 || emittable.includes(item.key))
) {
res({ key: item.key, assist });
key.disposeScope();
mainUi.close(id);
}
}
});
});
}
export function openDanmakuPoster() {
if (!fixedUi.hasName('danmakuEditor')) {
fixedUi.open('danmakuEditor');
}
}

View File

@ -1,8 +1,23 @@
import { sleep } from 'mutate-animate'; import { sleep } from 'mutate-animate';
import { tip } from './utils'; import { message } from 'ant-design-vue';
import { MessageApi } from 'ant-design-vue/lib/message';
export default function init() { message.config({
return { useDrag, useWheel, useUp, isMobile }; maxCount: 3
});
export function tip(
type: Exclude<keyof MessageApi, 'open' | 'config' | 'destroy'>,
text: string
) {
message[type]({
content: text,
class: 'antdv-message'
});
}
let num = 0;
export function requireUniqueSymbol() {
return num++;
} }
type DragFn = (x: number, y: number, e: MouseEvent | TouchEvent) => void; type DragFn = (x: number, y: number, e: MouseEvent | TouchEvent) => void;

View File

@ -1,13 +1,10 @@
import { message } from 'ant-design-vue';
import { MessageApi } from 'ant-design-vue/lib/message';
import { isNil } from 'lodash-es'; import { isNil } from 'lodash-es';
import { Animation, sleep, TimingFn } from 'mutate-animate'; import { Animation, sleep, TimingFn } from 'mutate-animate';
import { Ref, ref } from 'vue'; import { Ref, ref } from 'vue';
import { EVENT_KEY_CODE_MAP, KeyCode } from '@motajs/client-base'; import { KeyCode } from '@motajs/client-base';
import axios from 'axios'; import axios from 'axios';
import { decompressFromBase64 } from 'lz-string'; import { decompressFromBase64 } from 'lz-string';
import { Keyboard, KeyboardEmits, isAssist } from '@motajs/system-action'; import { Keyboard, KeyboardEmits, isAssist } from '@motajs/system-action';
import { fixedUi, mainUi } from './preset/ui';
import { logger } from '@motajs/common'; import { logger } from '@motajs/common';
type CanParseCss = keyof { type CanParseCss = keyof {
@ -18,25 +15,6 @@ type CanParseCss = keyof {
: never]: CSSStyleDeclaration[P]; : never]: CSSStyleDeclaration[P];
}; };
export default function init() {
return {
has,
getDamageColor,
parseCss,
tip,
changeLocalStorage,
swapChapter
};
}
/**
* undefined或null
* @param value
*/
export function has<T>(value: T): value is NonNullable<T> {
return !isNil(value);
}
/** /**
* *
* @param damage * @param damage
@ -68,14 +46,6 @@ export function setCanvasSize(
canvas.style.height = `${h}px`; canvas.style.height = `${h}px`;
} }
/**
* keycode对应的键
* @param key
*/
export function keycode(key: number) {
return EVENT_KEY_CODE_MAP[key];
}
/** /**
* css字符串为CSSStyleDeclaration对象 * css字符串为CSSStyleDeclaration对象
* @param css css字符串 * @param css css字符串
@ -195,7 +165,7 @@ export function type(
const all = toShow.length; const all = toShow.length;
const fn = (time: number) => { const fn = (time: number) => {
if (!has(time)) return; if (isNil(time)) return;
const now = ani.x; const now = ani.x;
content.value = toShow.slice(0, Math.floor(now)); content.value = toShow.slice(0, Math.floor(now));
if (Math.floor(now) === all) { if (Math.floor(now) === all) {
@ -213,19 +183,6 @@ export function type(
return content; return content;
} }
message.config({
maxCount: 3
});
export function tip(
type: Exclude<keyof MessageApi, 'open' | 'config' | 'destroy'>,
text: string
) {
message[type]({
content: text,
class: 'antdv-message'
});
}
/** /**
* *
* @param str * @param str
@ -235,7 +192,7 @@ export function splitText(str: string[]) {
.map((v, i, a) => { .map((v, i, a) => {
if (/^\d+\./.test(v)) return `${'&nbsp;'.repeat(12)}${v}`; if (/^\d+\./.test(v)) return `${'&nbsp;'.repeat(12)}${v}`;
else if ( else if (
(has(a[i - 1]) && v !== '<br>' && a[i - 1] === '<br>') || (!isNil(a[i - 1]) && v !== '<br>' && a[i - 1] === '<br>') ||
i === 0 i === 0
) { ) {
return `${'&nbsp;'.repeat(8)}${v}`; return `${'&nbsp;'.repeat(8)}${v}`;
@ -339,25 +296,6 @@ export function ensureArray<T>(arr: T): T extends any[] ? T : T[] {
return arr instanceof Array ? arr : [arr]; return arr instanceof Array ? arr : [arr];
} }
/**
*
* @param arr
* @param ele
*/
export function deleteWith<T>(arr: T[], ele: T): T[] {
const index = arr.indexOf(ele);
if (index === -1) return arr;
arr.splice(index, 1);
return arr;
}
export function spliceBy<T>(arr: T[], from: T): T[] {
const index = arr.indexOf(from);
if (index === -1) return arr;
arr.splice(index);
return arr;
}
export async function triggerFullscreen(full: boolean) { export async function triggerFullscreen(full: boolean) {
const { maxGameScale } = Mota.require('@user/data-utils'); const { maxGameScale } = Mota.require('@user/data-utils');
if (!!document.fullscreenElement && !full) { if (!!document.fullscreenElement && !full) {
@ -382,20 +320,6 @@ export async function triggerFullscreen(full: boolean) {
} }
} }
/**
*
* @param arr
*/
export function generateBinary(arr: boolean[]) {
let num = 0;
arr.forEach((v, i) => {
if (v) {
num |= 1 << i;
}
});
return num;
}
/** /**
* *
* @param name * @param name
@ -423,50 +347,6 @@ export function getStatusLabel(name: string) {
); );
} }
export function flipBinary(num: number, col: number) {
const n = 1 << col;
if (num & n) return num & ~n;
else return num | n;
}
/**
*
* @param emitAssist true时
*
* @param assist
*/
export function getVitualKeyOnce(
emitAssist: boolean = false,
assist: number = 0,
emittable: KeyCode[] = []
): Promise<KeyboardEmits> {
// todo: 正确触发后删除监听器
return new Promise(res => {
const key = Keyboard.get('full')!;
key.withAssist(assist);
const id = mainUi.open('virtualKey', { keyboard: key });
key.on('emit', (item, assist, _index, ev) => {
ev.preventDefault();
if (emitAssist) {
if (emittable.length === 0 || emittable.includes(item.key)) {
res({ key: item.key, assist: 0 });
key.disposeScope();
mainUi.close(id);
}
} else {
if (
!isAssist(item.key) &&
(emittable.length === 0 || emittable.includes(item.key))
) {
res({ key: item.key, assist });
key.disposeScope();
mainUi.close(id);
}
}
});
});
}
export function formatSize(size: number) { export function formatSize(size: number) {
return size < 1 << 10 return size < 1 << 10
? `${size.toFixed(2)}B` ? `${size.toFixed(2)}B`
@ -477,17 +357,6 @@ export function formatSize(size: number) {
: `${(size / (1 << 30)).toFixed(2)}GB`; : `${(size / (1 << 30)).toFixed(2)}GB`;
} }
let num = 0;
export function requireUniqueSymbol() {
return num++;
}
export function openDanmakuPoster() {
if (!fixedUi.hasName('danmakuEditor')) {
fixedUi.open('danmakuEditor');
}
}
export function getIconHeight(icon: AllIds | 'hero') { export function getIconHeight(icon: AllIds | 'hero') {
if (icon === 'hero') { if (icon === 'hero') {
if (core.isPlaying()) { if (core.isPlaying()) {

View File

@ -1,415 +0,0 @@
import EventEmitter from 'eventemitter3';
import {
FloorLayer,
ILayerGroupRenderExtends,
ILayerRenderExtends,
Layer,
LayerGroup,
LayerMovingRenderable
} from './layer';
import { texture } from './cache';
import { sleep } from 'mutate-animate';
import { RenderAdapter } from '@motajs/render-core';
const { hook } = Mota.require('@user/data-base');
hook.on('setBlock', (x, y, floor, block) => {
const isNow = floor === core.status.floorId;
LayerGroupFloorBinder.activedBinder.forEach(v => {
if (floor === v.floor || (isNow && v.bindThisFloor)) {
v.setBlock('event', block, x, y);
}
});
LayerFloorBinder.listenedBinder.forEach(v => {
if (v.layer.layer === 'event') {
if (v.floor === floor || (isNow && v.bindThisFloor)) {
v.setBlock(block, x, y);
}
}
});
});
hook.on('changingFloor', floor => {
// 潜在隐患如果putRenderData改成异步那么会变成两帧后才能真正刷新并渲染
// 考虑到楼层转换一般不会同时执行很多次,因此这里改为立刻更新
LayerGroupFloorBinder.activedBinder.forEach(v => {
if (v.bindThisFloor) v.updateBindData();
v.emit('floorChange', floor);
});
LayerFloorBinder.listenedBinder.forEach(v => {
if (v.bindThisFloor) v.updateBindData();
});
});
hook.on('setBgFgBlock', (name, number, x, y, floor) => {
const isNow = floor === core.status.floorId;
LayerGroupFloorBinder.activedBinder.forEach(v => {
if (floor === v.floor || (isNow && v.bindThisFloor)) {
v.setBlock(name, number, x, y);
}
});
LayerFloorBinder.listenedBinder.forEach(v => {
if (v.layer.layer === name) {
if (v.floor === floor || (isNow && v.bindThisFloor)) {
v.setBlock(number, x, y);
}
}
});
});
interface LayerGroupBinderEvent {
update: [floor: FloorIds];
setBlock: [x: number, y: number, floor: FloorIds, block: AllNumbers];
floorChange: [floor: FloorIds];
}
/**
* LayerGroup
* LayerGroup包含的子Layer上添加LayerFloorBinder拓展
*
*/
export class LayerGroupFloorBinder
extends EventEmitter<LayerGroupBinderEvent>
implements ILayerGroupRenderExtends
{
id: string = 'floor-binder';
bindThisFloor: boolean = true;
floor?: FloorIds;
group!: LayerGroup;
/** 附属的子LayerFloorBinder拓展 */
layerBinders: Set<LayerFloorBinder> = new Set();
private needUpdate: boolean = false;
static activedBinder: Set<LayerGroupFloorBinder> = new Set();
/**
*
*/
bindThis() {
this.floor = void 0;
this.bindThisFloor = true;
this.layerBinders.forEach(v => v.bindThis());
this.updateBind();
}
/**
*
* @param floorId id
*/
bindFloor(floorId: FloorIds) {
this.bindThisFloor = false;
this.floor = floorId;
this.layerBinders.forEach(v => v.bindFloor(floorId));
this.updateBind();
}
/**
*
*/
updateBind() {
if (this.needUpdate || !this.group) return;
this.needUpdate = true;
this.group.requestBeforeFrame(() => {
this.needUpdate = false;
this.updateBindData();
});
}
/**
*
*/
updateBindData() {
this.layerBinders.forEach(v => {
v.updateBindData();
});
const floor = this.getFloor();
this.emit('update', floor);
}
getFloor() {
return this.bindThisFloor ? core.status.floorId : this.floor!;
}
/**
*
*/
setBlock(layer: FloorLayer, block: AllNumbers, x: number, y: number) {
const ex = this.group
.getLayer(layer)
?.getExtends('floor-binder') as LayerFloorBinder;
if (!ex) return;
ex.setBlock(block, x, y);
const floor = this.bindThisFloor ? core.status.floorId : this.floor!;
this.emit('setBlock', x, y, floor, block);
}
checkLayerExtends(layer: Layer) {
const ex = layer.getExtends('floor-binder');
if (!ex) {
const extend = new LayerFloorBinder(this);
layer.extends(extend);
this.layerBinders.add(extend);
} else {
if (ex instanceof LayerFloorBinder) {
ex.setParent(this);
this.layerBinders.add(ex);
}
}
}
awake(group: LayerGroup) {
this.group = group;
for (const layer of group.layers.values()) {
this.checkLayerExtends(layer);
}
LayerGroupFloorBinder.activedBinder.add(this);
}
onLayerAdd(_group: LayerGroup, layer: Layer): void {
this.checkLayerExtends(layer);
}
onDestroy(group: LayerGroup) {
LayerGroupFloorBinder.activedBinder.delete(this);
group.layers.forEach(v => {
v.removeExtends('floor-binder');
});
this.removeAllListeners();
}
}
/**
* Layer的楼层渲染
* Layer是LayerGroup的子元素LayerGroupFloorBinder拓展
*
*/
export class LayerFloorBinder implements ILayerRenderExtends {
id: string = 'floor-binder';
parent?: LayerGroupFloorBinder;
layer!: Layer;
bindThisFloor: boolean = true;
floor?: FloorIds;
static listenedBinder: Set<LayerFloorBinder> = new Set();
private needUpdate: boolean = false;
constructor(parent?: LayerGroupFloorBinder) {
this.parent = parent;
}
/**
*
*/
bindThis() {
this.floor = void 0;
this.bindThisFloor = true;
this.updateBind();
}
/**
*
* @param floorId id
*/
bindFloor(floorId: FloorIds) {
this.bindThisFloor = false;
this.floor = floorId;
this.updateBind();
}
getFloor() {
return this.bindThisFloor ? core.status.floorId : this.floor!;
}
/**
* LayerGroupFloorBinder拓展
* @param parent
*/
setParent(parent?: LayerGroupFloorBinder) {
this.parent = parent;
this.checkListen();
}
private checkListen() {
if (this.parent) LayerFloorBinder.listenedBinder.delete(this);
else LayerFloorBinder.listenedBinder.add(this);
}
/**
*
*/
updateBind() {
if (this.needUpdate) return;
this.needUpdate = true;
this.layer.requestBeforeFrame(() => {
this.needUpdate = false;
this.updateBindData();
});
}
/**
*
*/
setBlock(block: AllNumbers, x: number, y: number) {
this.layer.putRenderData([block], 1, x, y);
}
/**
*
*/
updateBindData() {
const floor = this.getFloor();
if (!floor) return;
core.extractBlocks(floor);
const map = core.status.maps[floor];
this.layer.setMapSize(map.width, map.height);
const image = core.status.maps[this.getFloor()].images;
if (this.layer.layer === 'event') {
const m = map.map;
this.layer.putRenderData(m.flat(), map.width, 0, 0);
} else {
const m = core.maps._getBgFgMapArray(this.layer.layer!, floor);
this.layer.putRenderData(m.flat(), map.width, 0, 0);
}
if (this.layer.layer === 'bg') {
// 别忘了背景图块
this.layer.setBackground(texture.idNumberMap[map.defaultGround]);
}
const toDraw = image?.filter(v => v.canvas === this.layer.layer);
this.layer.setFloorImage(toDraw ?? []);
}
awake(layer: Layer) {
this.layer = layer;
if (!this.parent) {
const group = layer.parent;
if (group instanceof LayerGroup) {
const ex = group.getExtends('floor-binder');
if (ex instanceof LayerGroupFloorBinder) {
ex.checkLayerExtends(layer);
this.parent = ex;
}
}
}
this.checkListen();
}
onDestroy(_layer: Layer) {
LayerFloorBinder.listenedBinder.delete(this);
this.parent?.layerBinders.delete(this);
}
}
interface DoorAnimateRenderable {
renderable: LayerMovingRenderable;
count: number;
perTime: number;
}
export class LayerDoorAnimate implements ILayerRenderExtends {
id: string = 'door-animate';
layer!: Layer;
private moving: Set<LayerMovingRenderable> = new Set();
private getRenderable(block: Block): DoorAnimateRenderable | null {
const { x, y, id } = block;
const renderable = texture.getRenderable(id);
if (!renderable) return null;
const image = renderable.autotile
? renderable.image[0]
: renderable.image;
const time = block.event.doorInfo?.time ?? 160;
const frame = renderable.render.length;
const perTime = time / frame;
const data: LayerMovingRenderable = {
x,
y,
zIndex: y,
image,
autotile: false,
animate: 0,
frame,
bigImage: false,
render: renderable.render,
alpha: 1
};
return { renderable: data, count: frame, perTime };
}
/**
*
* @param block
*/
async openDoor(block: Block) {
const renderable = this.getRenderable(block);
if (!renderable) return Promise.reject();
const { renderable: data, count: frame, perTime } = renderable;
data.animate = 0;
this.moving.add(data);
this.layer.requestUpdateMoving();
let now = 0;
while (now < frame) {
await sleep(perTime);
data.animate = ++now;
this.layer.update(this.layer);
}
this.moving.delete(data);
this.layer.requestUpdateMoving();
return Promise.resolve();
}
/**
*
* @param block
*/
async closeDoor(block: Block) {
const renderable = this.getRenderable(block);
if (!renderable) return Promise.reject();
const { renderable: data, count: frame, perTime } = renderable;
data.animate = frame - 1;
this.moving.add(data);
this.layer.requestUpdateMoving();
let now = 0;
while (now >= 0) {
await sleep(perTime);
data.animate = --now;
this.layer.update(this.layer);
}
this.moving.delete(data);
this.layer.requestUpdateMoving();
return Promise.resolve();
}
awake(layer: Layer) {
this.layer = layer;
doorAdapter.add(this);
}
onMovingUpdate(_layer: Layer, renderable: LayerMovingRenderable[]): void {
renderable.push(...this.moving);
}
onDestroy(_layer: Layer): void {
doorAdapter.remove(this);
}
}
const doorAdapter = new RenderAdapter<LayerDoorAnimate>('door-animate');
doorAdapter.receive('openDoor', (item, block: Block) => {
return item.openDoor(block);
});
doorAdapter.receive('closeDoor', (item, block: Block) => {
return item.closeDoor(block);
});

View File

@ -2,7 +2,6 @@ export * from './animate';
export * from './block'; export * from './block';
export * from './cache'; export * from './cache';
export * from './camera'; export * from './camera';
export * from './floor';
export * from './frame'; export * from './frame';
export * from './graphics'; export * from './graphics';
export * from './hero'; export * from './hero';

View File

@ -8,11 +8,11 @@ import {
RenderAdapter RenderAdapter
} from '@motajs/render-core'; } from '@motajs/render-core';
import { logger } from '@motajs/common'; import { logger } from '@motajs/common';
import { TimingFn } from 'mutate-animate'; import { sleep, TimingFn } from 'mutate-animate';
import { RenderableData, texture } from './cache'; import { RenderableData, texture } from './cache';
import { BlockCacher, CanvasCacheItem, ICanvasCacheItem } from './block'; import { BlockCacher, CanvasCacheItem, ICanvasCacheItem } from './block';
import { LayerFloorBinder, LayerGroupFloorBinder } from './floor';
import { IAnimateFrame, renderEmits } from './frame'; import { IAnimateFrame, renderEmits } from './frame';
import { EventEmitter } from 'eventemitter3';
export interface ILayerGroupRenderExtends { export interface ILayerGroupRenderExtends {
/** 拓展的唯一标识符 */ /** 拓展的唯一标识符 */
@ -1550,3 +1550,406 @@ export class Layer extends Container<ELayerEvent> {
} }
const layerAdapter = new RenderAdapter<Layer>('layer'); const layerAdapter = new RenderAdapter<Layer>('layer');
const { hook } = Mota.require('@user/data-base');
hook.on('setBlock', (x, y, floor, block) => {
const isNow = floor === core.status.floorId;
LayerGroupFloorBinder.activedBinder.forEach(v => {
if (floor === v.floor || (isNow && v.bindThisFloor)) {
v.setBlock('event', block, x, y);
}
});
LayerFloorBinder.listenedBinder.forEach(v => {
if (v.layer.layer === 'event') {
if (v.floor === floor || (isNow && v.bindThisFloor)) {
v.setBlock(block, x, y);
}
}
});
});
hook.on('changingFloor', floor => {
// 潜在隐患如果putRenderData改成异步那么会变成两帧后才能真正刷新并渲染
// 考虑到楼层转换一般不会同时执行很多次,因此这里改为立刻更新
LayerGroupFloorBinder.activedBinder.forEach(v => {
if (v.bindThisFloor) v.updateBindData();
v.emit('floorChange', floor);
});
LayerFloorBinder.listenedBinder.forEach(v => {
if (v.bindThisFloor) v.updateBindData();
});
});
hook.on('setBgFgBlock', (name, number, x, y, floor) => {
const isNow = floor === core.status.floorId;
LayerGroupFloorBinder.activedBinder.forEach(v => {
if (floor === v.floor || (isNow && v.bindThisFloor)) {
v.setBlock(name, number, x, y);
}
});
LayerFloorBinder.listenedBinder.forEach(v => {
if (v.layer.layer === name) {
if (v.floor === floor || (isNow && v.bindThisFloor)) {
v.setBlock(number, x, y);
}
}
});
});
interface LayerGroupBinderEvent {
update: [floor: FloorIds];
setBlock: [x: number, y: number, floor: FloorIds, block: AllNumbers];
floorChange: [floor: FloorIds];
}
/**
* LayerGroup
* LayerGroup包含的子Layer上添加LayerFloorBinder拓展
*
*/
export class LayerGroupFloorBinder
extends EventEmitter<LayerGroupBinderEvent>
implements ILayerGroupRenderExtends
{
id: string = 'floor-binder';
bindThisFloor: boolean = true;
floor?: FloorIds;
group!: LayerGroup;
/** 附属的子LayerFloorBinder拓展 */
layerBinders: Set<LayerFloorBinder> = new Set();
private needUpdate: boolean = false;
static activedBinder: Set<LayerGroupFloorBinder> = new Set();
/**
*
*/
bindThis() {
this.floor = void 0;
this.bindThisFloor = true;
this.layerBinders.forEach(v => v.bindThis());
this.updateBind();
}
/**
*
* @param floorId id
*/
bindFloor(floorId: FloorIds) {
this.bindThisFloor = false;
this.floor = floorId;
this.layerBinders.forEach(v => v.bindFloor(floorId));
this.updateBind();
}
/**
*
*/
updateBind() {
if (this.needUpdate || !this.group) return;
this.needUpdate = true;
this.group.requestBeforeFrame(() => {
this.needUpdate = false;
this.updateBindData();
});
}
/**
*
*/
updateBindData() {
this.layerBinders.forEach(v => {
v.updateBindData();
});
const floor = this.getFloor();
this.emit('update', floor);
}
getFloor() {
return this.bindThisFloor ? core.status.floorId : this.floor!;
}
/**
*
*/
setBlock(layer: FloorLayer, block: AllNumbers, x: number, y: number) {
const ex = this.group
.getLayer(layer)
?.getExtends('floor-binder') as LayerFloorBinder;
if (!ex) return;
ex.setBlock(block, x, y);
const floor = this.bindThisFloor ? core.status.floorId : this.floor!;
this.emit('setBlock', x, y, floor, block);
}
checkLayerExtends(layer: Layer) {
const ex = layer.getExtends('floor-binder');
if (!ex) {
const extend = new LayerFloorBinder(this);
layer.extends(extend);
this.layerBinders.add(extend);
} else {
if (ex instanceof LayerFloorBinder) {
ex.setParent(this);
this.layerBinders.add(ex);
}
}
}
awake(group: LayerGroup) {
this.group = group;
for (const layer of group.layers.values()) {
this.checkLayerExtends(layer);
}
LayerGroupFloorBinder.activedBinder.add(this);
}
onLayerAdd(_group: LayerGroup, layer: Layer): void {
this.checkLayerExtends(layer);
}
onDestroy(group: LayerGroup) {
LayerGroupFloorBinder.activedBinder.delete(this);
group.layers.forEach(v => {
v.removeExtends('floor-binder');
});
this.removeAllListeners();
}
}
/**
* Layer的楼层渲染
* Layer是LayerGroup的子元素LayerGroupFloorBinder拓展
*
*/
export class LayerFloorBinder implements ILayerRenderExtends {
id: string = 'floor-binder';
parent?: LayerGroupFloorBinder;
layer!: Layer;
bindThisFloor: boolean = true;
floor?: FloorIds;
static listenedBinder: Set<LayerFloorBinder> = new Set();
private needUpdate: boolean = false;
constructor(parent?: LayerGroupFloorBinder) {
this.parent = parent;
}
/**
*
*/
bindThis() {
this.floor = void 0;
this.bindThisFloor = true;
this.updateBind();
}
/**
*
* @param floorId id
*/
bindFloor(floorId: FloorIds) {
this.bindThisFloor = false;
this.floor = floorId;
this.updateBind();
}
getFloor() {
return this.bindThisFloor ? core.status.floorId : this.floor!;
}
/**
* LayerGroupFloorBinder拓展
* @param parent
*/
setParent(parent?: LayerGroupFloorBinder) {
this.parent = parent;
this.checkListen();
}
private checkListen() {
if (this.parent) LayerFloorBinder.listenedBinder.delete(this);
else LayerFloorBinder.listenedBinder.add(this);
}
/**
*
*/
updateBind() {
if (this.needUpdate) return;
this.needUpdate = true;
this.layer.requestBeforeFrame(() => {
this.needUpdate = false;
this.updateBindData();
});
}
/**
*
*/
setBlock(block: AllNumbers, x: number, y: number) {
this.layer.putRenderData([block], 1, x, y);
}
/**
*
*/
updateBindData() {
const floor = this.getFloor();
if (!floor) return;
core.extractBlocks(floor);
const map = core.status.maps[floor];
this.layer.setMapSize(map.width, map.height);
const image = core.status.maps[this.getFloor()].images;
if (this.layer.layer === 'event') {
const m = map.map;
this.layer.putRenderData(m.flat(), map.width, 0, 0);
} else {
const m = core.maps._getBgFgMapArray(this.layer.layer!, floor);
this.layer.putRenderData(m.flat(), map.width, 0, 0);
}
if (this.layer.layer === 'bg') {
// 别忘了背景图块
this.layer.setBackground(texture.idNumberMap[map.defaultGround]);
}
const toDraw = image?.filter(v => v.canvas === this.layer.layer);
this.layer.setFloorImage(toDraw ?? []);
}
awake(layer: Layer) {
this.layer = layer;
if (!this.parent) {
const group = layer.parent;
if (group instanceof LayerGroup) {
const ex = group.getExtends('floor-binder');
if (ex instanceof LayerGroupFloorBinder) {
ex.checkLayerExtends(layer);
this.parent = ex;
}
}
}
this.checkListen();
}
onDestroy(_layer: Layer) {
LayerFloorBinder.listenedBinder.delete(this);
this.parent?.layerBinders.delete(this);
}
}
interface DoorAnimateRenderable {
renderable: LayerMovingRenderable;
count: number;
perTime: number;
}
export class LayerDoorAnimate implements ILayerRenderExtends {
id: string = 'door-animate';
layer!: Layer;
private moving: Set<LayerMovingRenderable> = new Set();
private getRenderable(block: Block): DoorAnimateRenderable | null {
const { x, y, id } = block;
const renderable = texture.getRenderable(id);
if (!renderable) return null;
const image = renderable.autotile
? renderable.image[0]
: renderable.image;
const time = block.event.doorInfo?.time ?? 160;
const frame = renderable.render.length;
const perTime = time / frame;
const data: LayerMovingRenderable = {
x,
y,
zIndex: y,
image,
autotile: false,
animate: 0,
frame,
bigImage: false,
render: renderable.render,
alpha: 1
};
return { renderable: data, count: frame, perTime };
}
/**
*
* @param block
*/
async openDoor(block: Block) {
const renderable = this.getRenderable(block);
if (!renderable) return Promise.reject();
const { renderable: data, count: frame, perTime } = renderable;
data.animate = 0;
this.moving.add(data);
this.layer.requestUpdateMoving();
let now = 0;
while (now < frame) {
await sleep(perTime);
data.animate = ++now;
this.layer.update(this.layer);
}
this.moving.delete(data);
this.layer.requestUpdateMoving();
return Promise.resolve();
}
/**
*
* @param block
*/
async closeDoor(block: Block) {
const renderable = this.getRenderable(block);
if (!renderable) return Promise.reject();
const { renderable: data, count: frame, perTime } = renderable;
data.animate = frame - 1;
this.moving.add(data);
this.layer.requestUpdateMoving();
let now = 0;
while (now >= 0) {
await sleep(perTime);
data.animate = --now;
this.layer.update(this.layer);
}
this.moving.delete(data);
this.layer.requestUpdateMoving();
return Promise.resolve();
}
awake(layer: Layer) {
this.layer = layer;
doorAdapter.add(this);
}
onMovingUpdate(_layer: Layer, renderable: LayerMovingRenderable[]): void {
renderable.push(...this.moving);
}
onDestroy(_layer: Layer): void {
doorAdapter.remove(this);
}
}
const doorAdapter = new RenderAdapter<LayerDoorAnimate>('door-animate');
doorAdapter.receive('openDoor', (item, block: Block) => {
return item.openDoor(block);
});
doorAdapter.receive('closeDoor', (item, block: Block) => {
return item.closeDoor(block);
});

View File

@ -1,7 +1,7 @@
import { RenderAdapter } from '@motajs/render-core'; import { RenderAdapter } from '@motajs/render-core';
import { HeroRenderer } from './hero'; import { HeroRenderer } from './hero';
import { ILayerGroupRenderExtends, LayerGroup } from './layer'; import { ILayerGroupRenderExtends, LayerGroup } from './layer';
import { LayerGroupFloorBinder } from './floor'; import { LayerGroupFloorBinder } from './layer';
import { hyper, TimingFn } from 'mutate-animate'; import { hyper, TimingFn } from 'mutate-animate';
export class FloorViewport implements ILayerGroupRenderExtends { export class FloorViewport implements ILayerGroupRenderExtends {

View File

@ -4,7 +4,7 @@ import {
generateBinary, generateBinary,
keycode, keycode,
spliceBy spliceBy
} from '@motajs/legacy-ui'; } from '@motajs/legacy-common';
import { EventEmitter } from 'eventemitter3'; import { EventEmitter } from 'eventemitter3';
import { isNil } from 'lodash-es'; import { isNil } from 'lodash-es';

View File

@ -1,8 +1,12 @@
import { EventEmitter, Listener } from '@motajs/legacy-common'; import {
EventEmitter,
Listener,
deleteWith,
flipBinary
} from '@motajs/legacy-common';
import { KeyCode } from '@motajs/client-base'; import { KeyCode } from '@motajs/client-base';
import { gameKey } from './hotkey'; import { gameKey } from './hotkey';
import { unwarpBinary } from './hotkey'; import { unwarpBinary } from './hotkey';
import { deleteWith, flipBinary } from '@motajs/legacy-ui';
import { cloneDeep } from 'lodash-es'; import { cloneDeep } from 'lodash-es';
import { shallowReactive } from 'vue'; import { shallowReactive } from 'vue';

View File

@ -24,6 +24,14 @@ const aliases = glob.sync('packages/*/src').map((srcPath) => {
}; };
}); });
const aliasesUser = glob.sync('packages-user/*/src').map((srcPath) => {
const packageName = path.basename(path.dirname(srcPath));
return {
find: `@user/${packageName}`,
replacement: path.resolve(__dirname, srcPath),
};
});
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [ plugins: [
@ -51,7 +59,8 @@ export default defineConfig({
base: `./`, base: `./`,
resolve: { resolve: {
alias: [ alias: [
...aliases ...aliases,
...aliasesUser
] ]
}, },
build: { build: {