mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-04-24 16:13:24 +08:00
Compare commits
6 Commits
cca10a429c
...
05e254fee0
Author | SHA1 | Date | |
---|---|---|---|
05e254fee0 | |||
5c520e3d52 | |||
74f0386fd9 | |||
23a8c0bf3a | |||
4fda246a9a | |||
0679bbb1a5 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -49,4 +49,5 @@ script/people.ts
|
||||
user.ts
|
||||
.antlr
|
||||
graph.svg
|
||||
docs/.vitepress
|
||||
docs/.vitepress/cache
|
||||
docs/.vitepress/dist
|
||||
|
59
docs/.vitepress/config.ts
Normal file
59
docs/.vitepress/config.ts
Normal file
@ -0,0 +1,59 @@
|
||||
import { defineConfig } from 'vitepress';
|
||||
|
||||
// https://vitepress.dev/reference/site-config
|
||||
export default defineConfig({
|
||||
title: 'HTML5 魔塔样板 V2.B',
|
||||
description: 'HTML5 魔塔样板 V2.B 帮助文档',
|
||||
base: '/_docs/',
|
||||
themeConfig: {
|
||||
// https://vitepress.dev/reference/default-theme-config
|
||||
nav: [
|
||||
{ text: '主页', link: '/' },
|
||||
{ text: '指南', link: '/guide/diff' },
|
||||
{ text: 'API', link: '/api/' }
|
||||
],
|
||||
sidebar: {
|
||||
'/guide/': [
|
||||
{
|
||||
text: '深度指南',
|
||||
items: [
|
||||
{ text: '差异说明', link: '/guide/diff' },
|
||||
{ text: '系统说明', link: '/guide/system' }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
socialLinks: [
|
||||
{ icon: 'github', link: 'https://github.com/unanmed/HumanBreak' }
|
||||
],
|
||||
search: {
|
||||
provider: 'local',
|
||||
options: {
|
||||
locales: {
|
||||
zh: {
|
||||
translations: {
|
||||
button: {
|
||||
buttonText: '搜索文档',
|
||||
buttonAriaLabel: '搜索文档'
|
||||
},
|
||||
modal: {
|
||||
noResultsText: '无法找到相关结果',
|
||||
resetButtonTitle: '清除查询条件',
|
||||
footer: {
|
||||
selectText: '选择',
|
||||
navigateText: '切换'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
locales: {
|
||||
root: {
|
||||
lang: 'zh',
|
||||
label: '中文'
|
||||
}
|
||||
}
|
||||
});
|
5
docs/api/index.md
Normal file
5
docs/api/index.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
lang: zh-CN
|
||||
---
|
||||
|
||||
# API 列表
|
34
docs/guide/diff.md
Normal file
34
docs/guide/diff.md
Normal file
@ -0,0 +1,34 @@
|
||||
---
|
||||
lang: zh-CN
|
||||
---
|
||||
|
||||
# 差异说明
|
||||
|
||||
本文档暂时只会对新样板新增内容进行说明,其余请查看[旧样板文档](https://h5mota.com/games/template/_docs/#/)。
|
||||
|
||||
本指南建立在你已经大致了解 js 的基础语法的基础上。如果还不了解可以尝试对指南内容进行模仿,或者查看[人类塔解析](https://h5mota.com/bbs/thread/?tid=1018&p=1)
|
||||
|
||||
如果你有能力直接使用源码版样板进行创作,也可以直接 fork 或 clone 2.B 样板[存储库](https://github.com/unanmed/HumanBreak/tree/template-v2.B)。2.B 样板使用了 vite 作为了构建工具,同时使用了 ts 等作为了开发语言。
|
||||
|
||||
本文将描述 2.B 样板与 2.10.3 及 2.A 样板的差异。
|
||||
|
||||
## 注意事项
|
||||
|
||||
对于新样板,由于拥有了近乎完整的类型标注,因此更推荐使用 `VS Code` 进行代码编写,这样你可以获取到完整的类型标注,而由于类型标注的复杂性,样板编辑器完全无法部署,因此样板编辑器不会有任何新版的类型标注。在之后的更新中,样板 API 会进行大幅度的改动,因此每次更新都可能会弃用一部分 API,同时这些 API 会在若干个版本后被彻底删除。因此如果你的代码中使用到了弃用的 API,请尽快更换写法以保证可以向后接档。
|
||||
|
||||
## 主要差异
|
||||
|
||||
- 开发语言换为 TypeScript,可以享受到完整的类型支持
|
||||
- 使用全新的 UI 编写方式,速度快,效率高
|
||||
- 模块化,可以使用 ES6 模块化语法
|
||||
- 移除插件系统,可以自定义代码目录结构,更加自由
|
||||
- 优化渲染端(client 端)与数据端(data 端)的通讯,渲染段现在可以直接引用数据端,不过数据端还不能直接引用渲染端
|
||||
|
||||
## 差异内容
|
||||
|
||||
相比于 2.10.3 及 2.A,有如下改动:
|
||||
|
||||
- [系统说明](./system)
|
||||
- [UI 编写](./ui)
|
||||
- [UI 系统](./ui-system)
|
||||
- [音频系统](./audio)
|
157
docs/guide/system.md
Normal file
157
docs/guide/system.md
Normal file
@ -0,0 +1,157 @@
|
||||
---
|
||||
lang: zh-CN
|
||||
---
|
||||
|
||||
# 系统说明
|
||||
|
||||
本文将介绍 2.B 的系统都做了哪些更改
|
||||
|
||||
## 模块化
|
||||
|
||||
2.B 样板现在已经迁移至了 monorepo,将代码模块化,共分为 20 余个模块,每个模块的具体内容可以参考 API 文档,模块列表如下:
|
||||
|
||||
- [@motajs/client](../api/motajs-client)
|
||||
- [@motajs/client-base](../api/motajs-client-base)
|
||||
- [@motajs/common](../api/motajs-common)
|
||||
- [@motajs/legacy-client](../api/motajs-legacy-client)
|
||||
- [@motajs/legacy-common](../api/motajs-legacy-common)
|
||||
- [@motajs/legacy-data](../api/motajs-legacy-data)
|
||||
- [@motajs/legacy-system](../api/motajs-legacy-system)
|
||||
- [@motajs/legacy-ui](../api/motajs-legacy-ui)
|
||||
- [@motajs/render](../api/motajs-render)
|
||||
- [@motajs/render-core](../api/motajs-render-core)
|
||||
- [@motajs/render-elements](../api/motajs-render-elements)
|
||||
- [@motajs/render-style](../api/motajs-render-style)
|
||||
- [@motajs/render-vue](../api/motajs-render-vue)
|
||||
- [@motajs/system](../api/motajs-system)
|
||||
- [@motajs/system-action](../api/motajs-system-action)
|
||||
- [@motajs/system-ui](../api/motajs-system-ui)
|
||||
- [@motajs/types](../api/types)
|
||||
- [@user/client-modules](../api/user-client-modules)
|
||||
- [@user/data-base](../api/user-data-base)
|
||||
- [@user/data-fallback](../api/user-data-fallback)
|
||||
- [@user/data-state](../api/user-data-state)
|
||||
- [@user/data-utils](../api/user-data-utils)
|
||||
- [@user/entry-client](../api/user-entry-client)
|
||||
- [@user/entry-data](../api/user-entry-data)
|
||||
- [@user/legacy-plugin-client](../api/user-legacy-plugin-client)
|
||||
- [@user/legacy-plugin-data](../api/user-legacy-plugin-data)
|
||||
|
||||
## Mota 全局变量
|
||||
|
||||
与 2.A 不同,2.B 对 `Mota` 全局变量做了简化,不再拥有 `Mota.Plugin` `Mota.Package` `Mota.requireAll` 属性与方法,它们全部整合至了 `Mota.require` 方法中,同时该方法的用法与 2.A 也不同,在 2.A 中,我们往往使用 `Mota.require('var', 'xxx')` 的方式调用,繁琐且不直观。在 2.B 中,我们可以直接填入模块名称,就可以获取到其内容了,例如:
|
||||
|
||||
```ts
|
||||
const { hook, loading } = Mota.require('@user/data-base'); // 获取 hook 与 loading
|
||||
const { Font } = Mota.require('@motajs/render'); // 获取 Font 字体类
|
||||
```
|
||||
|
||||
我们只需要填写一个参数,而不需要填写两个参数了,更加直观,而且与 ES6 模块语法类似,便于转换。
|
||||
|
||||
多数情况下,我们是不需要使用 `Mota` 全局变量的。不过,还是有一些特殊情况需要使用该全局变量才可以,这些情况包括:
|
||||
|
||||
- 在数据端调用渲染端接口,数据端需要跑录像验证,因此不能直接引入渲染端接口,需要通过此全局变量才可以。
|
||||
- 在 `libs` `functions.js` 中调用接口,这两个地方暂时还没有模块化,因此无法直接引入,需要通过此全局变量调用。
|
||||
|
||||
## 渲染端与数据端通信
|
||||
|
||||
一般情况下,渲染端**可以**直接引入数据端的内容,例如你可以在 `@user/client-modules` 里面直接引入 `@user/data-state` 的接口,这是没有问题的。不过,由于数据端需要在服务器上跑录像验证,因此**不能**直接引入渲染端的内容,否则会导致验证报错。如果需要在数据端引用渲染端接口,我们需要这么做:
|
||||
|
||||
```ts
|
||||
// @user/data-state 中的某文件
|
||||
const num = 100;
|
||||
Mota.r(() => {
|
||||
// 使用 r 方法包裹,这样这个函数就会在渲染端运行,可以有返回值,但是在录像验证中只会是 undefined
|
||||
const { Font } = Mota.require('@motajs/render');
|
||||
const font = new Font('Verdana', 18);
|
||||
// 函数内也可以调用外部变量,例如这里就调用了外部的 num 变量,但是极度不推荐在渲染端修改数据端的内容
|
||||
// 否则很可能导致录像不能运行,这里这个例子就会导致录像运行出错,因为录像验证时并不会执行这段代码,
|
||||
// 勇士的血量也就不会变大,于是就出错了。
|
||||
core.status.hero.hp += num;
|
||||
});
|
||||
```
|
||||
|
||||
除此之外,我们还可以使用钩子来进行数据通信。示例如下:
|
||||
|
||||
```ts
|
||||
// 渲染端和数据端都可以使用这个方式引入
|
||||
import { hook } from '@user/data-base';
|
||||
// 也可以通过 Mota.require 方法引入
|
||||
const { hook } = Mota.require('@user/data-base');
|
||||
|
||||
// 监听战后函数,每次与怪物战斗后,都会执行这个函数
|
||||
// 每个钩子的参数定义可以参考 package-user/data-base/src/game.ts GameEvent 接口
|
||||
hook.on('afterBattle', enemy => {
|
||||
console.log('与怪物战斗:', enemy.id);
|
||||
});
|
||||
```
|
||||
|
||||
## 加载流程
|
||||
|
||||
与 2.A 相比,加载流程也不太一样,下面是 2.B 的加载流程:
|
||||
|
||||
1. 加载 `index.html`
|
||||
2. 加载 2.x 样板的第三方库
|
||||
|
||||
3. 如果是游戏中,加载 `src/main.ts`
|
||||
|
||||
1. 加载渲染端入口
|
||||
2. 加载数据端入口
|
||||
3. 并行初始化数据端,写入 `Mota` 全局变量
|
||||
4. 初始化完毕后执行 `loading.emit('dataRegistered')` 钩子
|
||||
5. 并行初始化渲染端
|
||||
6. 初始化完毕后执行 `loading.emit('clientRegistered')` 钩子
|
||||
7. 二者都初始化完毕后执行 `loading.emit('registered')` 钩子
|
||||
8. 执行数据端各个模块的初始化函数
|
||||
9. 执行渲染段各个模块的初始化函数
|
||||
|
||||
4. 如果是录像验证中:
|
||||
|
||||
1. 加载数据端入口
|
||||
2. 初始化数据端,写入 `Mota` 全局变量
|
||||
3. 初始化完毕后执行 `loading.emit('dataRegistered')` 与 `loading.emit('registered')` 钩子
|
||||
4. 执行数据端各个模块的初始化函数
|
||||
|
||||
5. 执行 `main.js` 初始化
|
||||
6. 加载全塔属性
|
||||
7. 加载 `core.js` 及其他 `libs` 中的脚本
|
||||
8. 加载完毕后执行 `loading.emit('coreInit')` 钩子
|
||||
9. 开始资源加载
|
||||
10. 自动元件加载完毕后执行 `loading.emit('autotileLoaded')` 钩子
|
||||
11. 资源加载完毕后执行 `loading.emit('loaded')` 钩子
|
||||
12. 进入标题界面
|
||||
|
||||
## 函数重写
|
||||
|
||||
在 2.B 模式下,如果想改 `libs` 的内容,如果直接在里面改会很麻烦,而且两端通讯也不方便,因此我们建议在 `package-user` 中对函数重写,这样的话就可以使用模块化语法,更加方便。同时,2.B 也提供了函数重写接口,他在 `@motajs/legacy-common` 模块中,我们可以这么使用它:
|
||||
|
||||
```ts
|
||||
// 新建一个 ts 文件,例如叫做 override.ts,放在 client-modules 文件夹下
|
||||
import { Patch, PatchClass } from '@motajs/legacy-common';
|
||||
|
||||
// 新建函数,这个操作是必要的,我们不能直接在顶层使用这个接口
|
||||
export function patchMyFunctions() {
|
||||
// 创建 Patch 实例,参数表示这个 Patch 示例要重写哪个文件中的函数
|
||||
// 如果需要复写两个文件,那么就需要创建两个实例
|
||||
const patch = new Patch(PatchClass.Control);
|
||||
|
||||
// 使用 add 函数来重写,第一个参数会有自动补全
|
||||
// 如果要重写的函数以下划线开头,可能会有报错
|
||||
// 这时候需要去 types/declaration 中对应的文件中添加声明
|
||||
patch.add('getFlag', (name, defaultValue) => {
|
||||
// 重写 getFlag,如果变量是数字,那么 +100 后返回
|
||||
const value = core.status.?hero.?flags[name] ?? defaultValue;
|
||||
return typeof value === 'number' ? value + 100 : value;
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
然后,我们找到 `client-modules` 文件夹下的 `index.ts` 文件,然后在 `create` 函数中调用 `patchMyFunctions`,这样我们的函数重写就完成了。
|
||||
|
||||
::: warning
|
||||
**注意**,在渲染端重写的函数在录像验证中将无效,因为录像验证不会执行任何渲染端内容!
|
||||
:::
|
||||
|
||||
## 目录结构
|
||||
|
||||
我们建议每个文件夹中都有一个 `index.ts` 文件,将本文件夹中的其他文件经由此文件导出,这样方便管理,同时结构清晰。可以参考 `packages-user/client-modules` 文件夹中是如何做的。
|
0
docs/guide/ui-system.md
Normal file
0
docs/guide/ui-system.md
Normal file
0
docs/guide/ui.md
Normal file
0
docs/guide/ui.md
Normal file
@ -4,7 +4,7 @@ layout: home
|
||||
|
||||
hero:
|
||||
name: 'mota-js'
|
||||
text: 'HTML5魔塔样板V2.A'
|
||||
text: 'HTML5魔塔样板V2.B'
|
||||
tagline: HTML5魔塔样板从 2.x 到 3.0 的过渡版本
|
||||
actions:
|
||||
- theme: brand
|
||||
|
8
packages-user/client-modules/package.json
Normal file
8
packages-user/client-modules/package.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "@user/client-modules",
|
||||
"dependencies": {
|
||||
"@motajs/render": "workspace:*",
|
||||
"@motajs/legacy-ui": "workspace:*",
|
||||
"@user/legacy-plugin-client": "workspace:*"
|
||||
}
|
||||
}
|
1
packages-user/client-modules/src/action/index.ts
Normal file
1
packages-user/client-modules/src/action/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './move';
|
@ -258,7 +258,7 @@ export class BgmController<
|
||||
export const bgmController = new BgmController<BgmIds>(audioPlayer);
|
||||
|
||||
export function loadAllBgm() {
|
||||
const loading = Mota.require('var', 'loading');
|
||||
const { loading } = Mota.require('@user/data-base');
|
||||
loading.once('coreInit', () => {
|
||||
const data = data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d;
|
||||
for (const bgm of data.main.bgms) {
|
@ -3,9 +3,11 @@ import { OpusDecoder, VorbisDecoder } from './decoder';
|
||||
import { AudioType } from './support';
|
||||
import { AudioDecoder } from './decoder';
|
||||
|
||||
loadAllBgm();
|
||||
AudioDecoder.registerDecoder(AudioType.Ogg, VorbisDecoder);
|
||||
AudioDecoder.registerDecoder(AudioType.Opus, OpusDecoder);
|
||||
export function createAudio() {
|
||||
loadAllBgm();
|
||||
AudioDecoder.registerDecoder(AudioType.Ogg, VorbisDecoder);
|
||||
AudioDecoder.registerDecoder(AudioType.Opus, OpusDecoder);
|
||||
}
|
||||
|
||||
export * from './support';
|
||||
export * from './effect';
|
@ -51,5 +51,3 @@ isAudioSupport(AudioType.Wav);
|
||||
isAudioSupport(AudioType.Flac);
|
||||
isAudioSupport(AudioType.Opus);
|
||||
isAudioSupport(AudioType.Aac);
|
||||
|
||||
console.log(supportMap);
|
@ -1,4 +1,3 @@
|
||||
import { Patch } from '@motajs/legacy-common';
|
||||
import { patchAudio } from './audio';
|
||||
import { patchWeather } from './weather';
|
||||
import { patchUI } from './ui';
|
||||
@ -7,8 +6,4 @@ export function patchAll() {
|
||||
patchAudio();
|
||||
patchWeather();
|
||||
patchUI();
|
||||
const loading = Mota.require('var', 'loading');
|
||||
loading.once('coreInit', () => {
|
||||
Patch.patchAll();
|
||||
});
|
||||
}
|
20
packages-user/client-modules/src/index.ts
Normal file
20
packages-user/client-modules/src/index.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { loading } from '@user/data-base';
|
||||
import { createAudio } from './audio';
|
||||
import { patchAll } from './fallback';
|
||||
import { createGameRenderer, createRender } from './render';
|
||||
|
||||
export function create() {
|
||||
createAudio();
|
||||
patchAll();
|
||||
createRender();
|
||||
loading.once('coreInit', () => {
|
||||
createGameRenderer();
|
||||
});
|
||||
}
|
||||
|
||||
export * from './action';
|
||||
export * from './weather';
|
||||
export * from './audio';
|
||||
export * from './loader';
|
||||
export * from './fallback';
|
||||
export * from './render';
|
@ -8,17 +8,19 @@ import {
|
||||
import { logger } from '@motajs/common';
|
||||
import EventEmitter from 'eventemitter3';
|
||||
import { isNil } from 'lodash-es';
|
||||
import { LayerGroupFloorBinder } from './floor';
|
||||
import {
|
||||
BlockCacher,
|
||||
CanvasCacheItem,
|
||||
ICanvasCacheItem,
|
||||
calNeedRenderOf,
|
||||
ILayerGroupRenderExtends,
|
||||
Layer,
|
||||
LayerGroup
|
||||
} from './layer';
|
||||
import { BlockCacher, CanvasCacheItem, ICanvasCacheItem } from './block';
|
||||
LayerGroup,
|
||||
LayerGroupFloorBinder,
|
||||
tagMap
|
||||
} from '@motajs/render';
|
||||
import { IDamageEnemy, IEnemyCollection, MapDamage } from '@motajs/types';
|
||||
|
||||
const ensureFloorDamage = Mota.require('fn', 'ensureFloorDamage');
|
||||
import { UserEnemyInfo } from '@user/data-state';
|
||||
|
||||
/**
|
||||
* 根据伤害大小获取颜色
|
||||
@ -55,6 +57,7 @@ export class FloorDamageExtends
|
||||
if (!this.sprite || !floor) return;
|
||||
const map = core.status.maps[floor];
|
||||
this.sprite.setMapSize(map.width, map.height);
|
||||
const { ensureFloorDamage } = Mota.require('@user/data-state');
|
||||
ensureFloorDamage(floor);
|
||||
const enemy = core.status.maps[floor].enemy;
|
||||
|
||||
@ -75,6 +78,7 @@ export class FloorDamageExtends
|
||||
|
||||
private onUpdate = (floor: FloorIds) => {
|
||||
if (!this.floorBinder.bindThisFloor) {
|
||||
const { ensureFloorDamage } = Mota.require('@user/data-state');
|
||||
ensureFloorDamage(floor);
|
||||
core.status.maps[floor].enemy.calRealAttribute();
|
||||
}
|
||||
@ -323,7 +327,7 @@ export class Damage extends RenderItem<EDamageEvent> {
|
||||
const y = enemy.y!;
|
||||
const { damage } = enemy.calDamage();
|
||||
const cri = enemy.calCritical(1)[0]?.atkDelta ?? Infinity;
|
||||
const real = enemy.getRealInfo();
|
||||
const real = enemy.getRealInfo() as UserEnemyInfo;
|
||||
|
||||
const dam1: DamageRenderable = {
|
||||
align: 'left',
|
||||
@ -587,4 +591,9 @@ export class Damage extends RenderItem<EDamageEvent> {
|
||||
}
|
||||
}
|
||||
|
||||
// 注册为内部元素
|
||||
tagMap.register<EDamageEvent, Damage>('damage', (_0, _1, _props) => {
|
||||
return new Damage();
|
||||
});
|
||||
|
||||
// const adapter = new RenderAdapter<Damage>('damage');
|
@ -3,9 +3,12 @@ import { defineComponent } from 'vue';
|
||||
import { UIController } from '@motajs/system-ui';
|
||||
import { mainSceneUI } from './ui/main';
|
||||
import { MAIN_HEIGHT, MAIN_WIDTH } from './shared';
|
||||
import { TextboxStore } from './components';
|
||||
import { hook } from '@user/data-base';
|
||||
import { createItemDetail } from './itemDetail';
|
||||
import { createLoopMap } from './loopMap';
|
||||
import { createGameCanvas } from './legacy/gameCanvas';
|
||||
|
||||
export function create() {
|
||||
export function createGameRenderer() {
|
||||
const main = new MotaRenderer();
|
||||
main.size(MAIN_WIDTH, MAIN_HEIGHT);
|
||||
|
||||
@ -23,20 +26,22 @@ export function create() {
|
||||
main.hide();
|
||||
createApp(App).mount(main);
|
||||
|
||||
Mota.require('var', 'hook').on('reset', () => {
|
||||
hook.on('reset', () => {
|
||||
main.show();
|
||||
});
|
||||
|
||||
Mota.require('var', 'hook').on('restart', () => {
|
||||
hook.on('restart', () => {
|
||||
main.hide();
|
||||
});
|
||||
|
||||
console.log(main);
|
||||
}
|
||||
|
||||
Mota.register('module', 'MainUI', {
|
||||
TextboxStore
|
||||
});
|
||||
export function createRender() {
|
||||
createGameCanvas();
|
||||
createItemDetail();
|
||||
createLoopMap();
|
||||
}
|
||||
|
||||
export * from './components';
|
||||
export * from './ui';
|
@ -1,13 +1,13 @@
|
||||
import { logger } from '@motajs/common';
|
||||
import { mainSetting } from '@motajs/legacy-ui';
|
||||
import {
|
||||
Damage,
|
||||
DamageRenderable,
|
||||
FloorDamageExtends,
|
||||
LayerGroupFloorBinder,
|
||||
ILayerGroupRenderExtends,
|
||||
LayerGroup
|
||||
} from '@motajs/render';
|
||||
import { hook } from '@user/data-base';
|
||||
import { ItemState } from '@user/data-state';
|
||||
import { Damage, DamageRenderable, FloorDamageExtends } from './damage';
|
||||
|
||||
interface ItemDetailData {
|
||||
x: number;
|
||||
@ -21,12 +21,13 @@ interface ItemData {
|
||||
y: number;
|
||||
}
|
||||
|
||||
const ItemState = Mota.require('module', 'State').ItemState;
|
||||
Mota.require('var', 'hook').on('setBlock', (x, y, floorId, block) => {
|
||||
FloorItemDetail.listened.forEach(v => {
|
||||
v.setBlock(block, x, y);
|
||||
export function createItemDetail() {
|
||||
hook.on('setBlock', (x, y, floorId, block) => {
|
||||
FloorItemDetail.listened.forEach(v => {
|
||||
v.setBlock(block, x, y);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export class FloorItemDetail implements ILayerGroupRenderExtends {
|
||||
id: string = 'item-detail';
|
||||
@ -199,8 +200,7 @@ export class FloorItemDetail implements ILayerGroupRenderExtends {
|
||||
return;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
ItemState.item(id)?.itemEffectFn();
|
||||
ItemState.item(id)?.itemEffectFn?.();
|
||||
detail?.set(index, { x, y, diff });
|
||||
});
|
||||
});
|
@ -4,6 +4,7 @@ import {
|
||||
ILayerGroupRenderExtends,
|
||||
LayerGroup
|
||||
} from '@motajs/render';
|
||||
import { loading } from '@user/data-base';
|
||||
|
||||
const filterMap: [FloorIds[], string][] = [];
|
||||
|
||||
@ -11,15 +12,19 @@ function getCanvasFilterByFloorId(floorId: FloorIds = core.status.floorId) {
|
||||
return filterMap.find(v => v[0].includes(floorId))?.[1] ?? '';
|
||||
}
|
||||
|
||||
Mota.require('var', 'loading').once('coreInit', () => {
|
||||
filterMap.push(
|
||||
[['MT50', 'MT60', 'MT61'], 'contrast(120%)'], // 童心佬的滤镜(
|
||||
[
|
||||
core.floorIds.slice(61, 70).concat(core.floorIds.slice(72, 107)),
|
||||
'contrast(120%)'
|
||||
] // 童心佬的滤镜(
|
||||
);
|
||||
});
|
||||
export function createGameCanvas() {
|
||||
loading.once('coreInit', () => {
|
||||
filterMap.push(
|
||||
[['MT50', 'MT60', 'MT61'], 'contrast(120%)'], // 童心佬的滤镜(
|
||||
[
|
||||
core.floorIds
|
||||
.slice(61, 70)
|
||||
.concat(core.floorIds.slice(72, 107)),
|
||||
'contrast(120%)'
|
||||
] // 童心佬的滤镜(
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export class LayerGroupFilter implements ILayerGroupRenderExtends {
|
||||
id: string = 'filter';
|
@ -8,8 +8,7 @@ import {
|
||||
Sprite,
|
||||
Transform
|
||||
} from '@motajs/render';
|
||||
|
||||
const gameListener = Mota.require('var', 'gameListener');
|
||||
import { gameListener, hook } from '@user/data-base';
|
||||
|
||||
export class LayerGroupHalo implements ILayerGroupRenderExtends {
|
||||
id: string = 'halo';
|
||||
@ -130,7 +129,7 @@ function updateHalo(block: Block) {
|
||||
}
|
||||
}
|
||||
|
||||
Mota.require('var', 'hook').on('enemyExtract', col => {
|
||||
hook.on('enemyExtract', col => {
|
||||
LayerGroupHalo.sprites.forEach(v => {
|
||||
const floor = v.binder.getFloor();
|
||||
if (col.floorId === floor) {
|
@ -7,7 +7,7 @@ import {
|
||||
LayerGroup,
|
||||
Sprite
|
||||
} from '@motajs/render';
|
||||
import type { BluePalace } from '@/game/mechanism/misc';
|
||||
import { BluePalace } from '@user/data-state';
|
||||
|
||||
/** 最大粒子数 */
|
||||
const MAX_PARTICLES = 10;
|
||||
@ -24,7 +24,7 @@ export class LayerGroupPortal implements ILayerGroupRenderExtends {
|
||||
portal!: Portal;
|
||||
|
||||
private onFloorChange = (floor: FloorIds) => {
|
||||
const data = Mota.require('module', 'Mechanism').BluePalace.portals;
|
||||
const data = BluePalace.portals;
|
||||
this.portal.cellSize = this.group.cellSize;
|
||||
this.portal.setData(data[floor] ?? []);
|
||||
};
|
@ -1,25 +1,28 @@
|
||||
import {
|
||||
Container,
|
||||
FloorDamageExtends,
|
||||
LayerGroupFloorBinder,
|
||||
FloorLayer,
|
||||
LayerGroup,
|
||||
FloorViewport,
|
||||
MotaRenderer
|
||||
} from '@motajs/render';
|
||||
import { FloorItemDetail } from '@/plugin/fx/itemDetail';
|
||||
import { hook } from '@user/data-base';
|
||||
import { MiscData } from '@user/data-state';
|
||||
import { FloorDamageExtends } from './damage';
|
||||
import { FloorItemDetail } from './itemDetail';
|
||||
|
||||
const loopMaps = Mota.require('module', 'Mechanism').MiscData.loopMaps;
|
||||
const loopMaps = MiscData.loopMaps;
|
||||
|
||||
let loopLayer: LayerGroup;
|
||||
let show: boolean = false;
|
||||
/** 循环式地图中,更新视角的委托ticker */
|
||||
let delegation: number = -1;
|
||||
|
||||
const hook = Mota.require('var', 'hook');
|
||||
hook.on('changingFloor', (floorId, heroLoc) => {
|
||||
enableLoopMapElement(floorId);
|
||||
});
|
||||
export function createLoopMap() {
|
||||
hook.on('changingFloor', (floorId, heroLoc) => {
|
||||
enableLoopMapElement(floorId);
|
||||
});
|
||||
}
|
||||
|
||||
function createLayer() {
|
||||
const group = new LayerGroup();
|
@ -1,7 +1,6 @@
|
||||
import { LayerShadowExtends } from '@/core/fx/shadow';
|
||||
import { LayerShadowExtends } from '@motajs/legacy-ui';
|
||||
import {
|
||||
ILayerGroupRenderExtends,
|
||||
FloorDamageExtends,
|
||||
LayerGroupAnimate,
|
||||
FloorViewport,
|
||||
ILayerRenderExtends,
|
||||
@ -11,13 +10,7 @@ import {
|
||||
LayerGroup,
|
||||
Font
|
||||
} from '@motajs/render';
|
||||
import { WeatherController } from '@/module/weather';
|
||||
import { FloorChange } from '@/plugin/fallback';
|
||||
import { LayerGroupFilter } from '@/plugin/fx/gameCanvas';
|
||||
import { LayerGroupHalo } from '@/plugin/fx/halo';
|
||||
import { FloorItemDetail } from '@/plugin/fx/itemDetail';
|
||||
import { PopText } from '@/plugin/fx/pop';
|
||||
import { LayerGroupPortal } from '@/plugin/fx/portal';
|
||||
import { WeatherController } from '../../weather';
|
||||
import { defineComponent, onMounted, reactive, ref } from 'vue';
|
||||
import { Textbox, Tip } from '../components';
|
||||
import { GameUI, UIController } from '@motajs/system-ui';
|
||||
@ -35,6 +28,16 @@ import {
|
||||
} from './statusBar';
|
||||
import { onLoaded } from '../use';
|
||||
import { ReplayingStatus } from './toolbar';
|
||||
import { getHeroStatusOn, HeroSkill, NightSpecial } from '@user/data-state';
|
||||
import { jumpIgnoreFloor } from '@user/legacy-plugin-data';
|
||||
import { hook } from '@user/data-base';
|
||||
import { FloorDamageExtends } from '../damage';
|
||||
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 layerGroupExtends: ILayerGroupRenderExtends[] = [
|
||||
@ -112,8 +115,6 @@ const MainScene = defineComponent(() => {
|
||||
night: 0
|
||||
});
|
||||
|
||||
const { getHeroStatusOn } = Mota.requireAll('fn');
|
||||
|
||||
const updateStatus = () => {
|
||||
if (!core.status || !core.status.hero || !core.status.floorId) return;
|
||||
hideStatus.value = core.getFlag('hideStatusBar', false);
|
||||
@ -135,7 +136,6 @@ const MainScene = defineComponent(() => {
|
||||
leftStatus.exAtk = getHeroStatusOn('mana');
|
||||
leftStatus.magicDef = getHeroStatusOn('magicDef');
|
||||
|
||||
const { HeroSkill, NightSpecial } = Mota.require('module', 'Mechanism');
|
||||
rightStatus.autoSkill = HeroSkill.getAutoSkill();
|
||||
rightStatus.skillName = HeroSkill.getSkillName();
|
||||
rightStatus.skillDesc = HeroSkill.getSkillDesc();
|
||||
@ -148,7 +148,7 @@ const MainScene = defineComponent(() => {
|
||||
replayStatus.played = totalList.length - toReplay.length;
|
||||
replayStatus.total = totalList.length;
|
||||
if (HeroSkill.learnedSkill(HeroSkill.Jump)) {
|
||||
if (Mota.Plugin.require('skill_g').jumpIgnoreFloor.has(floor)) {
|
||||
if (jumpIgnoreFloor.has(floor)) {
|
||||
rightStatus.jumpCount = -2;
|
||||
} else {
|
||||
rightStatus.jumpCount = 3 - (flags[`jump_${floor}`] ?? 0);
|
||||
@ -168,7 +168,7 @@ const MainScene = defineComponent(() => {
|
||||
loaded.value = true;
|
||||
});
|
||||
|
||||
Mota.require('var', 'hook').on('statusBarUpdate', updateStatus);
|
||||
hook.on('statusBarUpdate', updateStatus);
|
||||
|
||||
return () => (
|
||||
<container id="main-scene" width={MAIN_WIDTH} height={MAIN_HEIGHT}>
|
@ -14,7 +14,7 @@ import { mainUi } from '@motajs/legacy-ui';
|
||||
import { gameKey } from '@motajs/system-action';
|
||||
import { generateKeyboardEvent } from '@motajs/system-action';
|
||||
import { getVitualKeyOnce } from '@motajs/legacy-ui';
|
||||
import { getAllSavesData, getSaveData } from '@/module/utils';
|
||||
import { getAllSavesData, getSaveData } from '../../utils';
|
||||
|
||||
export interface SettingsProps extends Partial<ChoicesProps>, UIComponentProps {
|
||||
loc: ElementLocator;
|
||||
@ -410,7 +410,9 @@ export const DownloadSaveSelect = defineComponent<SettingsProps>(props => {
|
||||
{ text: '请等待处理完毕' }
|
||||
);
|
||||
core.download(
|
||||
`${core.firstData.name}_${core.formatDate2(new Date())}.h5save`,
|
||||
`${core.firstData.name}_${core.formatDate2(
|
||||
new Date()
|
||||
)}.h5save`,
|
||||
data
|
||||
);
|
||||
}
|
||||
@ -426,7 +428,9 @@ export const DownloadSaveSelect = defineComponent<SettingsProps>(props => {
|
||||
if (confirm) {
|
||||
const data = await getSaveData(core.saves.saveIndex);
|
||||
core.download(
|
||||
`${core.firstData.name}_${core.formatDate2(new Date())}.h5save`,
|
||||
`${core.firstData.name}_${core.formatDate2(
|
||||
new Date()
|
||||
)}.h5save`,
|
||||
data
|
||||
);
|
||||
}
|
@ -18,6 +18,7 @@ import {
|
||||
ReplayingStatus,
|
||||
ReplayingToolbar
|
||||
} from './toolbar';
|
||||
import { HeroSkill } from '@user/data-state';
|
||||
|
||||
export interface ILeftHeroStatus {
|
||||
hp: number;
|
||||
@ -275,7 +276,6 @@ export const RightStatusBar = defineComponent<StatusBarProps<IRightHeroStatus>>(
|
||||
};
|
||||
|
||||
const changeAutoSkill = () => {
|
||||
const { HeroSkill } = Mota.require('module', 'Mechanism');
|
||||
const auto = !s.autoSkill;
|
||||
HeroSkill.setAutoSkill(auto);
|
||||
core.status.route.push(`set:autoSkill:${auto}`);
|
@ -13,17 +13,14 @@ import {
|
||||
StepForward,
|
||||
ViewMapIcon
|
||||
} from '../components/icons';
|
||||
import {
|
||||
generateBinary,
|
||||
getVitualKeyOnce,
|
||||
openDanmakuPoster
|
||||
} from '@motajs/legacy-ui';
|
||||
import { getVitualKeyOnce, openDanmakuPoster } from '@motajs/legacy-ui';
|
||||
import { gameKey } from '@motajs/system-action';
|
||||
import { generateKeyboardEvent } from '@motajs/system-action';
|
||||
import { transitioned } from '../use';
|
||||
import { linear } from 'mutate-animate';
|
||||
import { KeyCode } from '@motajs/client-base';
|
||||
import { Progress } from '../components/misc';
|
||||
import { generateBinary } from '@motajs/legacy-common';
|
||||
|
||||
interface ToolbarProps extends DefaultProps {
|
||||
loc?: ElementLocator;
|
@ -1,3 +1,4 @@
|
||||
import { loading } from '@user/data-base';
|
||||
import { TimingFn, Transition } from 'mutate-animate';
|
||||
import {
|
||||
ComponentInternalInstance,
|
||||
@ -59,7 +60,6 @@ export function onOrientationChange(hook: OrientationHook) {
|
||||
* @param hook 当游戏加载完成时执行的函数
|
||||
*/
|
||||
export function onLoaded(hook: () => void) {
|
||||
const loading = Mota.require('var', 'loading');
|
||||
if (!loading.loaded) {
|
||||
loading.once('loaded', hook);
|
||||
} else {
|
0
packages-user/client-modules/src/weather/cloud.ts
Normal file
0
packages-user/client-modules/src/weather/cloud.ts
Normal file
@ -10,6 +10,7 @@ import {
|
||||
MotaOffscreenCanvas2D
|
||||
} from '@motajs/render';
|
||||
import { IWeather } from './weather';
|
||||
import { loading } from '@user/data-base';
|
||||
|
||||
const snowVs = /* glsl */ `
|
||||
in vec2 a_snowVertex;
|
||||
@ -105,7 +106,7 @@ void main() {
|
||||
/** 雨滴顶点坐标 */
|
||||
const vertex = new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]);
|
||||
|
||||
Mota.require('var', 'loading').once('coreInit', () => {
|
||||
loading.once('coreInit', () => {
|
||||
const shader = new SnowShader();
|
||||
const gl = shader.gl;
|
||||
shader.size(480, 480);
|
@ -7,6 +7,9 @@ interface GameLoadEvent {
|
||||
autotileLoaded: [];
|
||||
coreInit: [];
|
||||
loaded: [];
|
||||
registered: [];
|
||||
dataRegistered: [];
|
||||
clientRegistered: [];
|
||||
}
|
||||
|
||||
class GameLoading extends EventEmitter<GameLoadEvent> {
|
||||
@ -58,6 +61,25 @@ class GameLoading extends EventEmitter<GameLoadEvent> {
|
||||
}
|
||||
|
||||
export const loading = new GameLoading();
|
||||
main.loading = loading;
|
||||
|
||||
let clientRegistered = false;
|
||||
let dataRegistered = false;
|
||||
|
||||
function checkRegistered() {
|
||||
if (clientRegistered && dataRegistered) {
|
||||
loading.emit('registered');
|
||||
}
|
||||
}
|
||||
|
||||
loading.once('clientRegistered', () => {
|
||||
clientRegistered = true;
|
||||
checkRegistered();
|
||||
});
|
||||
loading.once('dataRegistered', () => {
|
||||
dataRegistered = true;
|
||||
checkRegistered();
|
||||
});
|
||||
|
||||
export interface GameEvent {
|
||||
/** Emitted in libs/events.js resetGame. */
|
||||
@ -107,6 +129,8 @@ export interface GameEvent {
|
||||
];
|
||||
/** Emitted in lib/control.js */
|
||||
replayStatus: [replaying: boolean];
|
||||
/** Emitted in project/functions.js */
|
||||
loadData: [];
|
||||
}
|
||||
|
||||
export const hook = new EventEmitter<GameEvent>();
|
||||
|
@ -16,8 +16,9 @@ export interface CurrentEnemy {
|
||||
onMapEnemy: DamageEnemy[];
|
||||
}
|
||||
|
||||
function init() {
|
||||
export function patchBattle() {
|
||||
const patch = new Patch(PatchClass.Enemys);
|
||||
const patch2 = new Patch(PatchClass.Events);
|
||||
|
||||
patch.add('canBattle', function (x, y, floorId) {
|
||||
const enemy = typeof x === 'number' ? getEnemy(x, y!, floorId) : x;
|
||||
@ -31,7 +32,7 @@ function init() {
|
||||
return damage < core.status.hero.hp;
|
||||
});
|
||||
|
||||
core.events.battle = function battle(
|
||||
function battle(
|
||||
x: number | DamageEnemy,
|
||||
y: number,
|
||||
force: boolean = false,
|
||||
@ -59,7 +60,7 @@ function init() {
|
||||
// 战后事件
|
||||
core.afterBattle(enemy, isLoc ? x : enemy.x, y);
|
||||
callback?.();
|
||||
};
|
||||
}
|
||||
|
||||
const getFacedId = (enemy: DamageEnemy) => {
|
||||
const e = enemy.enemy;
|
||||
@ -69,9 +70,7 @@ function init() {
|
||||
return e.id;
|
||||
};
|
||||
|
||||
core.enemys.getCurrentEnemys = function getCurrentEnemys(
|
||||
floorId = core.status.floorId
|
||||
) {
|
||||
patch.add('getCurrentEnemys', function (floorId = core.status.floorId) {
|
||||
floorId = floorId || core.status.floorId;
|
||||
const enemys: CurrentEnemy[] = [];
|
||||
const used: Record<string, DamageEnemy[]> = {};
|
||||
@ -100,9 +99,9 @@ function init() {
|
||||
const bd = b.enemy.calDamage().damage;
|
||||
return ad - bd;
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
core.events._sys_battle = function (data: Block, callback?: () => void) {
|
||||
patch2.add('_sys_battle', function (data: Block, callback?: () => void) {
|
||||
// 检查战前事件
|
||||
const floor = core.floors[core.status.floorId];
|
||||
const beforeBattle: MotaEvent = [];
|
||||
@ -126,11 +125,11 @@ function init() {
|
||||
core.insertAction(beforeBattle, data.x, data.y, callback);
|
||||
}
|
||||
} else {
|
||||
core.battle(data.x, data.y, false, callback);
|
||||
battle(data.x, data.y, false, callback);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
core.events._action_battle = function (data, x, y, prefix) {
|
||||
patch2.add('_action_battle', function (data, x, y, prefix) {
|
||||
if (data.id) {
|
||||
const enemy = getSingleEnemy(data.id as EnemyIds);
|
||||
// todo: 与不在地图上的怪物战斗
|
||||
@ -139,120 +138,125 @@ function init() {
|
||||
core.doAction();
|
||||
return;
|
||||
}
|
||||
const [ex, ey] = this.__action_getLoc(
|
||||
const [ex, ey] = core.events.__action_getLoc(
|
||||
data.loc,
|
||||
x,
|
||||
y,
|
||||
prefix
|
||||
) as LocArr;
|
||||
core.battle(ex, ey, true, core.doAction);
|
||||
battle(ex, ey, true, core.doAction);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
core.events.afterBattle = function afterBattle(
|
||||
enemy: DamageEnemy,
|
||||
x?: number,
|
||||
y?: number
|
||||
) {
|
||||
const floorId = core.status.floorId;
|
||||
const special = enemy.info.special;
|
||||
patch2.add(
|
||||
'afterBattle',
|
||||
function (enemy: DamageEnemy, x?: number, y?: number) {
|
||||
const floorId = core.status.floorId;
|
||||
const special = enemy.info.special;
|
||||
|
||||
// 播放战斗动画
|
||||
let animate: AnimationIds = 'hand';
|
||||
// 检查当前装备是否存在攻击动画
|
||||
const equipId = core.getEquip(0);
|
||||
if (equipId && (core.material.items[equipId].equip || {}).animate)
|
||||
animate = core.material.items[equipId].equip.animate;
|
||||
// 播放战斗动画
|
||||
let animate: AnimationIds = 'hand';
|
||||
// 检查当前装备是否存在攻击动画
|
||||
const equipId = core.getEquip(0);
|
||||
if (equipId && (core.material.items[equipId].equip || {}).animate)
|
||||
animate = core.material.items[equipId].equip.animate;
|
||||
|
||||
// 检查该动画是否存在SE,如果不存在则使用默认音效
|
||||
if (!core.material.animates[animate]?.se) core.playSound('attack.opus');
|
||||
// 检查该动画是否存在SE,如果不存在则使用默认音效
|
||||
if (!core.material.animates[animate]?.se)
|
||||
core.playSound('attack.opus');
|
||||
|
||||
// 战斗伤害
|
||||
const info = enemy.calDamage(core.status.hero);
|
||||
const damage = info.damage;
|
||||
// 判定是否致死
|
||||
if (damage >= core.status.hero.hp) {
|
||||
core.status.hero.hp = 0;
|
||||
core.updateStatusBar(false, true);
|
||||
core.events.lose('战斗失败');
|
||||
return;
|
||||
// 战斗伤害
|
||||
const info = enemy.calDamage(core.status.hero);
|
||||
const damage = info.damage;
|
||||
// 判定是否致死
|
||||
if (damage >= core.status.hero.hp) {
|
||||
core.status.hero.hp = 0;
|
||||
core.updateStatusBar(false, true);
|
||||
core.events.lose('战斗失败');
|
||||
return;
|
||||
}
|
||||
|
||||
// 扣减体力值并记录统计数据
|
||||
core.status.hero.hp -= damage;
|
||||
core.status.hero.statistics.battleDamage += damage;
|
||||
core.status.hero.statistics.battle++;
|
||||
|
||||
// 智慧之源
|
||||
if (special.has(14) && flags.hard === 2) {
|
||||
core.addFlag(
|
||||
'inte_' + floorId,
|
||||
Math.ceil((core.status.hero.mdef / 10) * 0.3) * 10
|
||||
);
|
||||
core.status.hero.mdef -=
|
||||
Math.ceil((core.status.hero.mdef / 10) * 0.3) * 10;
|
||||
}
|
||||
|
||||
// 极昼永夜
|
||||
if (special.has(22)) {
|
||||
NightSpecial.addNight(floorId, -enemy.info.night!);
|
||||
}
|
||||
if (special.has(23)) {
|
||||
NightSpecial.addNight(floorId, enemy.info.day!);
|
||||
}
|
||||
|
||||
// 如果是融化怪,需要特殊标记一下
|
||||
if (special.has(25) && !isNil(x) && !isNil(y)) {
|
||||
flags[`melt_${floorId}`] ??= {};
|
||||
flags[`melt_${floorId}`][`${x},${y}`] = enemy.info.melt;
|
||||
}
|
||||
|
||||
// 获得金币
|
||||
const money = enemy.info.money!;
|
||||
core.status.hero.money += money;
|
||||
core.status.hero.statistics.money += money;
|
||||
|
||||
// 获得经验
|
||||
const exp = enemy.info.exp!;
|
||||
core.status.hero.exp += exp;
|
||||
core.status.hero.statistics.exp += exp;
|
||||
|
||||
const hint =
|
||||
'打败 ' +
|
||||
enemy.enemy.name +
|
||||
',金币+' +
|
||||
money +
|
||||
',经验+' +
|
||||
exp;
|
||||
core.drawTip(hint, enemy.id);
|
||||
|
||||
HeroSkill.disableSkill();
|
||||
|
||||
// 事件的处理
|
||||
const todo: MotaEvent = [];
|
||||
|
||||
// 战后事件
|
||||
if (!isNil(core.status.floorId)) {
|
||||
const loc = `${x},${y}` as LocString;
|
||||
todo.push(
|
||||
...(core.floors[core.status.floorId].afterBattle[loc] ?? [])
|
||||
);
|
||||
}
|
||||
todo.push(...(enemy.enemy.afterBattle ?? []));
|
||||
|
||||
// 如果事件不为空,将其插入
|
||||
if (todo.length > 0) core.insertAction(todo, x, y);
|
||||
|
||||
if (!isNil(x) && !isNil(y)) {
|
||||
core.drawAnimate(animate, x, y);
|
||||
core.removeBlock(x, y);
|
||||
} else core.drawHeroAnimate(animate);
|
||||
|
||||
// 如果已有事件正在处理中
|
||||
if (core.status.event.id == null) core.continueAutomaticRoute();
|
||||
else core.clearContinueAutomaticRoute();
|
||||
|
||||
core.checkAutoEvents();
|
||||
|
||||
hook.emit('afterBattle', enemy, x, y);
|
||||
}
|
||||
|
||||
// 扣减体力值并记录统计数据
|
||||
core.status.hero.hp -= damage;
|
||||
core.status.hero.statistics.battleDamage += damage;
|
||||
core.status.hero.statistics.battle++;
|
||||
|
||||
// 智慧之源
|
||||
if (special.has(14) && flags.hard === 2) {
|
||||
core.addFlag(
|
||||
'inte_' + floorId,
|
||||
Math.ceil((core.status.hero.mdef / 10) * 0.3) * 10
|
||||
);
|
||||
core.status.hero.mdef -=
|
||||
Math.ceil((core.status.hero.mdef / 10) * 0.3) * 10;
|
||||
}
|
||||
|
||||
// 极昼永夜
|
||||
if (special.has(22)) {
|
||||
NightSpecial.addNight(floorId, -enemy.info.night!);
|
||||
}
|
||||
if (special.has(23)) {
|
||||
NightSpecial.addNight(floorId, enemy.info.day!);
|
||||
}
|
||||
|
||||
// 如果是融化怪,需要特殊标记一下
|
||||
if (special.has(25) && !isNil(x) && !isNil(y)) {
|
||||
flags[`melt_${floorId}`] ??= {};
|
||||
flags[`melt_${floorId}`][`${x},${y}`] = enemy.info.melt;
|
||||
}
|
||||
|
||||
// 获得金币
|
||||
const money = enemy.info.money!;
|
||||
core.status.hero.money += money;
|
||||
core.status.hero.statistics.money += money;
|
||||
|
||||
// 获得经验
|
||||
const exp = enemy.info.exp!;
|
||||
core.status.hero.exp += exp;
|
||||
core.status.hero.statistics.exp += exp;
|
||||
|
||||
const hint =
|
||||
'打败 ' + enemy.enemy.name + ',金币+' + money + ',经验+' + exp;
|
||||
core.drawTip(hint, enemy.id);
|
||||
|
||||
HeroSkill.disableSkill();
|
||||
|
||||
// 事件的处理
|
||||
const todo: MotaEvent = [];
|
||||
|
||||
// 战后事件
|
||||
if (!isNil(core.status.floorId)) {
|
||||
const loc = `${x},${y}` as LocString;
|
||||
todo.push(
|
||||
...(core.floors[core.status.floorId].afterBattle[loc] ?? [])
|
||||
);
|
||||
}
|
||||
todo.push(...(enemy.enemy.afterBattle ?? []));
|
||||
|
||||
// 如果事件不为空,将其插入
|
||||
if (todo.length > 0) core.insertAction(todo, x, y);
|
||||
|
||||
if (!isNil(x) && !isNil(y)) {
|
||||
core.drawAnimate(animate, x, y);
|
||||
core.removeBlock(x, y);
|
||||
} else core.drawHeroAnimate(animate);
|
||||
|
||||
// 如果已有事件正在处理中
|
||||
if (core.status.event.id == null) core.continueAutomaticRoute();
|
||||
else core.clearContinueAutomaticRoute();
|
||||
|
||||
core.checkAutoEvents();
|
||||
|
||||
hook.emit('afterBattle', enemy, x, y);
|
||||
};
|
||||
);
|
||||
}
|
||||
loading.once('coreInit', init);
|
||||
loading.once('coreInit', patchBattle);
|
||||
|
||||
declare global {
|
||||
interface Enemys {
|
||||
|
@ -0,0 +1,5 @@
|
||||
import { patchBattle } from './battle';
|
||||
|
||||
export function patchAll() {
|
||||
patchBattle();
|
||||
}
|
@ -1,9 +1,8 @@
|
||||
import { getHeroStatusOf, getHeroStatusOn } from '../state/hero';
|
||||
import { Range } from '@user/data-utils';
|
||||
import { ensureArray, has, manhattan } from '@/plugin/game/utils';
|
||||
import { Range, ensureArray, has, manhattan } from '@user/data-utils';
|
||||
import EventEmitter from 'eventemitter3';
|
||||
import { hook } from '@user/data-base';
|
||||
import { HeroSkill, NightSpecial } from '../mechanism/misc';
|
||||
import { HeroSkill, NightSpecial } from '../mechanism';
|
||||
import {
|
||||
EnemyInfo,
|
||||
DamageInfo,
|
||||
@ -14,8 +13,10 @@ import {
|
||||
HaloFn,
|
||||
IEnemyCollection,
|
||||
IDamageEnemy,
|
||||
HaloType
|
||||
HaloType,
|
||||
IEnemyCollectionEvent
|
||||
} from '@motajs/types';
|
||||
import { isNil } from 'lodash-es';
|
||||
|
||||
// todo: 光环划分优先级,从而可以实现光环的多级运算
|
||||
|
||||
@ -53,13 +54,8 @@ specialValue
|
||||
.set(31, ['hpHalo'])
|
||||
.set(32, ['assimilateRange']);
|
||||
|
||||
interface EnemyCollectionEvent {
|
||||
extract: [];
|
||||
calculated: [];
|
||||
}
|
||||
|
||||
export class EnemyCollection
|
||||
extends EventEmitter<EnemyCollectionEvent>
|
||||
extends EventEmitter<IEnemyCollectionEvent>
|
||||
implements IEnemyCollection
|
||||
{
|
||||
floorId: FloorIds;
|
||||
@ -298,7 +294,7 @@ export class DamageEnemy implements IDamageEnemy {
|
||||
|
||||
// 融化,融化不属于怪物光环,因此不能用provide和inject计算,需要在这里计算
|
||||
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)) {
|
||||
const [mx, my] = loc.split(',').map(v => parseInt(v));
|
||||
if (
|
||||
|
@ -1 +1,2 @@
|
||||
export * from './misc';
|
||||
export * from './skillTree';
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { backDir, has } from '@/plugin/game/utils';
|
||||
import { backDir, has } from '@user/data-utils';
|
||||
import { loading } from '@user/data-base';
|
||||
import type { LayerDoorAnimate } from '@motajs/render';
|
||||
import { getSkillLevel } from '@/plugin/game/skillTree';
|
||||
import { getSkillLevel } from './skillTree';
|
||||
|
||||
/**
|
||||
* 一些零散机制的数据
|
||||
@ -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 {
|
||||
type DoorConvertInfo = [id: AllIds, x: number, y: number];
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
import { HeroSkill } from '@/game/mechanism/misc';
|
||||
|
||||
let levels: number[] = [];
|
||||
|
||||
export type Chapter = 'chapter1' | 'chapter2';
|
||||
@ -233,7 +231,7 @@ export function getSkillConsume(skill: number) {
|
||||
|
||||
export function openTree() {
|
||||
if (main.replayChecking) return;
|
||||
Mota.require('var', 'mainUi').open('skillTree');
|
||||
Mota.require('@motajs/legacy-ui').mainUi.open('skillTree');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -314,3 +312,128 @@ export function saveSkillTree() {
|
||||
export function loadSkillTree(data: number[]) {
|
||||
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;
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import { logger } from '@motajs/common';
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { HeroSkill, NightSpecial } from '../mechanism/misc';
|
||||
import { HeroSkill, NightSpecial } from '../mechanism';
|
||||
|
||||
/**
|
||||
* 获取勇士在某一点的属性
|
||||
@ -59,7 +59,7 @@ function getRealStatus(
|
||||
name: keyof HeroStatus | 'all' | (keyof HeroStatus)[],
|
||||
floorId: FloorIds = core.status.floorId
|
||||
): any {
|
||||
const { getSkillLevel } = Mota.Plugin.require('skillTree_g');
|
||||
const { getSkillLevel } = Mota.require('@user/legacy-plugin-data');
|
||||
if (name instanceof Array) {
|
||||
const res: any = {};
|
||||
name.forEach(v => {
|
||||
|
@ -11,7 +11,7 @@ import type {
|
||||
LayerMovingRenderable,
|
||||
LayerFloorBinder
|
||||
} from '@motajs/render';
|
||||
import type { HeroKeyMover } from '@/module/action/move';
|
||||
import type { HeroKeyMover } from '@user/client-modules';
|
||||
import { BluePalace, MiscData } from '../mechanism/misc';
|
||||
import { sleep } from '@motajs/common';
|
||||
|
||||
@ -939,7 +939,7 @@ export const heroMoveCollection: HeroMoveCollection = {
|
||||
loading.once('coreInit', () => {
|
||||
// 注册按键操作
|
||||
Mota.r(() => {
|
||||
const { HeroKeyMover } = Mota.require('@motajs/system-action');
|
||||
const { HeroKeyMover } = Mota.require('@user/client-modules');
|
||||
const { gameKey } = Mota.require('@motajs/system-action');
|
||||
const keyMover = new HeroKeyMover(gameKey, heroMover);
|
||||
heroMoveCollection.keyMover = keyMover;
|
||||
|
@ -1 +1,2 @@
|
||||
export * from './range';
|
||||
export * from './utils';
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@motajs/entry-client",
|
||||
"name": "@user/entry-client",
|
||||
"dependencies": {
|
||||
"@motajs/client": "workspace:*",
|
||||
"@motajs/client-base": "workspace:*",
|
||||
@ -16,6 +16,8 @@
|
||||
"@motajs/legacy-client": "workspace:*",
|
||||
"@motajs/legacy-data": "workspace:*",
|
||||
"@motajs/legacy-ui": "workspace:*",
|
||||
"@motajs/legacy-system": "workspace:*"
|
||||
"@motajs/legacy-system": "workspace:*",
|
||||
"@user/client-modules": "workspace:*",
|
||||
"@user/legacy-plugin-client": "workspace:*"
|
||||
}
|
||||
}
|
@ -14,8 +14,15 @@ import * as RenderVue from '@motajs/render-vue';
|
||||
import * as System from '@motajs/system';
|
||||
import * as SystemAction from '@motajs/system-action';
|
||||
import * as SystemUI from '@motajs/system-ui';
|
||||
import * as ClientModules from '@user/client-modules';
|
||||
import * as LegacyPluginClient from '@user/legacy-plugin-client';
|
||||
import * as MutateAnimate from 'mutate-animate';
|
||||
import * as Vue from 'vue';
|
||||
import { hook, loading } from '@user/data-base';
|
||||
|
||||
export function create() {
|
||||
loading.once('registered', createModule);
|
||||
|
||||
Mota.register('@motajs/client', Client);
|
||||
Mota.register('@motajs/client-base', ClientBase);
|
||||
Mota.register('@motajs/common', Common);
|
||||
@ -31,4 +38,20 @@ export function create() {
|
||||
Mota.register('@motajs/system', System);
|
||||
Mota.register('@motajs/system-action', SystemAction);
|
||||
Mota.register('@motajs/system-ui', SystemUI);
|
||||
Mota.register('@user/client-modules', ClientModules);
|
||||
Mota.register('@user/legacy-plugin-client', LegacyPluginClient);
|
||||
Mota.register('MutateAnimate', MutateAnimate);
|
||||
Mota.register('Vue', Vue);
|
||||
|
||||
loading.emit('clientRegistered');
|
||||
}
|
||||
|
||||
async function createModule() {
|
||||
LegacyUI.create();
|
||||
RenderElements.create();
|
||||
ClientModules.create();
|
||||
|
||||
await import('ant-design-vue/dist/antd.dark.css');
|
||||
main.renderLoaded = true;
|
||||
hook.emit('renderLoaded');
|
||||
}
|
||||
|
@ -1 +1,5 @@
|
||||
export * from './create';
|
||||
import { create } from './create';
|
||||
|
||||
export function createGame() {
|
||||
create();
|
||||
}
|
||||
|
@ -1,3 +1,11 @@
|
||||
{
|
||||
"name": "@user/entry-data"
|
||||
"name": "@user/entry-data",
|
||||
"dependencies": {
|
||||
"@motajs/legacy-common": "workspace:*",
|
||||
"@user/data-base": "workspace:*",
|
||||
"@user/data-fallback": "workspace:*",
|
||||
"@user/data-state": "workspace:*",
|
||||
"@user/data-utils": "workspace:*",
|
||||
"@user/legacy-plugin-data": "workspace:*"
|
||||
}
|
||||
}
|
22
packages-user/entry-data/src/create.ts
Normal file
22
packages-user/entry-data/src/create.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { Mota } from './mota';
|
||||
import * as DataBase from '@user/data-base';
|
||||
import * as DataFallback from '@user/data-fallback';
|
||||
import * as DataState from '@user/data-state';
|
||||
import * as DataUtils from '@user/data-utils';
|
||||
import * as LegacyPluginData from '@user/legacy-plugin-data';
|
||||
|
||||
export function create() {
|
||||
DataBase.loading.once('registered', createModule);
|
||||
|
||||
Mota.register('@user/data-base', DataBase);
|
||||
Mota.register('@user/data-fallback', DataFallback);
|
||||
Mota.register('@user/data-state', DataState);
|
||||
Mota.register('@user/data-utils', DataUtils);
|
||||
Mota.register('@user/legacy-plugin-data', LegacyPluginData);
|
||||
|
||||
DataBase.loading.emit('dataRegistered');
|
||||
}
|
||||
|
||||
function createModule() {
|
||||
LegacyPluginData.create();
|
||||
}
|
@ -1,5 +1,15 @@
|
||||
import { createMota } from './mota';
|
||||
import { create } from './create';
|
||||
import { patchAll } from '@user/data-fallback';
|
||||
import { loading } from '@user/data-base';
|
||||
import { Patch } from '@motajs/legacy-common';
|
||||
|
||||
createMota();
|
||||
patchAll();
|
||||
create();
|
||||
|
||||
loading.once('coreInit', () => {
|
||||
Patch.patchAll();
|
||||
});
|
||||
|
||||
export * from './mota';
|
||||
|
@ -13,6 +13,16 @@ import type * as RenderVue from '@motajs/render-vue';
|
||||
import type * as System from '@motajs/system';
|
||||
import type * as SystemAction from '@motajs/system-action';
|
||||
import type * as SystemUI from '@motajs/system-ui';
|
||||
import type * as ClientModules from '@user/client-modules';
|
||||
import type * as DataBase from '@user/data-base';
|
||||
import type * as DataFallback from '@user/data-fallback';
|
||||
import type * as DataState from '@user/data-state';
|
||||
import type * as DataUtils from '@user/data-utils';
|
||||
import type * as LegacyPluginClient from '@user/legacy-plugin-client';
|
||||
import type * as LegacyPluginData from '@user/legacy-plugin-data';
|
||||
// ---------- 必要的第三方库
|
||||
import type * as MutateAnimate from 'mutate-animate';
|
||||
import type * as Vue from 'vue';
|
||||
|
||||
interface ModuleInterface {
|
||||
'@motajs/client': typeof Client;
|
||||
@ -30,6 +40,16 @@ interface ModuleInterface {
|
||||
'@motajs/system': typeof System;
|
||||
'@motajs/system-action': typeof SystemAction;
|
||||
'@motajs/system-ui': typeof SystemUI;
|
||||
'@user/client-modules': typeof ClientModules;
|
||||
'@user/data-base': typeof DataBase;
|
||||
'@user/data-fallback': typeof DataFallback;
|
||||
'@user/data-state': typeof DataState;
|
||||
'@user/data-utils': typeof DataUtils;
|
||||
'@user/legacy-plugin-client': typeof LegacyPluginClient;
|
||||
'@user/legacy-plugin-data': typeof LegacyPluginData;
|
||||
// ---------- 必要的第三方库
|
||||
MutateAnimate: typeof MutateAnimate;
|
||||
Vue: typeof Vue;
|
||||
}
|
||||
|
||||
export interface IMota {
|
||||
@ -73,10 +93,6 @@ class MotaSystem implements IMota {
|
||||
r = r;
|
||||
rf = rf;
|
||||
|
||||
constructor() {
|
||||
throw new Error(`System interface class cannot be constructed.`);
|
||||
}
|
||||
|
||||
require(key: string): any {
|
||||
const data = this.modules[key];
|
||||
if (data) return data;
|
||||
@ -137,7 +153,7 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
export const Mota = new MotaSystem();
|
||||
export const Mota: IMota = new MotaSystem();
|
||||
|
||||
export function createMota() {
|
||||
window.Mota = Mota;
|
||||
|
7
packages-user/legacy-plugin-client/package.json
Normal file
7
packages-user/legacy-plugin-client/package.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "@user/legacy-plugin-client",
|
||||
"dependencies": {
|
||||
"@user/client-modules": "workspace:*",
|
||||
"@user/data-state": "workspace:*"
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ import {
|
||||
RenderItemPosition,
|
||||
Transform
|
||||
} from '@motajs/render';
|
||||
import { IStateDamageable } from '@/game/state/interface';
|
||||
import { IStateDamageable } from '@user/data-state';
|
||||
import EventEmitter from 'eventemitter3';
|
||||
import { Ticker } from 'mutate-animate';
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { hook } from '@user/data-base';
|
||||
import { BarrageBoss } from './barrage';
|
||||
import { TowerBoss } from './towerBoss';
|
||||
|
||||
@ -15,7 +16,7 @@ export function getBoss<T extends BarrageBoss>(): T | null {
|
||||
return boss as T;
|
||||
}
|
||||
|
||||
Mota.require('var', 'hook').on('reset', () => {
|
||||
hook.on('reset', () => {
|
||||
if (boss) {
|
||||
boss.end();
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { IStateDamageable } from '@/game/state/interface';
|
||||
import { IStateDamageable } from '@user/data-state';
|
||||
import { BarrageBoss, BossSprite, Hitbox } from './barrage';
|
||||
import {
|
||||
Container,
|
||||
@ -9,11 +9,13 @@ import {
|
||||
Transform,
|
||||
MotaOffscreenCanvas2D
|
||||
} from '@motajs/render';
|
||||
import { Pop } from '../fx/pop';
|
||||
import { Pop } from '../../../client-modules/src/render/legacy/pop';
|
||||
import { SplittableBall } from './palaceBossProjectile';
|
||||
import { PointEffect } from '../fx/pointShader';
|
||||
import { loading } from '@user/data-base';
|
||||
import { clip } from '@user/legacy-plugin-data';
|
||||
|
||||
Mota.require('var', 'loading').once('coreInit', () => {
|
||||
loading.once('coreInit', () => {
|
||||
const shader = new Shader();
|
||||
shader.size(480, 480);
|
||||
shader.setHD(true);
|
||||
@ -102,7 +104,7 @@ export class PalaceBoss extends BarrageBoss {
|
||||
PalaceBoss.effect.end();
|
||||
core.status.hero.hp = this.heroHp;
|
||||
|
||||
Mota.Plugin.require('replay_g').clip('choices:0');
|
||||
clip('choices:0');
|
||||
}
|
||||
|
||||
ai(time: number, frame: number): void {}
|
@ -1,5 +1,5 @@
|
||||
import { Transform, MotaOffscreenCanvas2D } from '@motajs/render';
|
||||
import { IStateDamageable } from '@/game/state/interface';
|
||||
import { IStateDamageable } from '@user/data-state';
|
||||
import { Hitbox, Projectile } from './barrage';
|
||||
import type { PalaceBoss } from './palaceBoss';
|
||||
import { clamp } from '@motajs/legacy-ui';
|
@ -21,11 +21,13 @@ import {
|
||||
ThunderBallProjectile,
|
||||
ThunderProjectile
|
||||
} from './towerBossProjectile';
|
||||
import { IStateDamageable } from '@/game/state/interface';
|
||||
import { Pop } from '../fx/pop';
|
||||
import { WeatherController } from '@/module';
|
||||
import { IStateDamageable } from '@user/data-state';
|
||||
import { Pop } from '../../../client-modules/src/render/legacy/pop';
|
||||
import { loading } from '@user/data-base';
|
||||
import { clip } from '@user/legacy-plugin-data';
|
||||
import { WeatherController } from '@user/client-modules';
|
||||
|
||||
Mota.require('var', 'loading').once('coreInit', () => {
|
||||
loading.once('coreInit', () => {
|
||||
const shader = new Shader();
|
||||
shader.size(480, 480);
|
||||
shader.setHD(true);
|
||||
@ -198,7 +200,7 @@ export class TowerBoss extends BarrageBoss {
|
||||
TowerBoss.effect.end();
|
||||
core.status.hero.hp = this.heroHp;
|
||||
|
||||
Mota.Plugin.require('replay_g').clip('choices:0');
|
||||
clip('choices:0');
|
||||
}
|
||||
|
||||
/**
|
@ -2,7 +2,7 @@ import { hyper, power, TimingFn } from 'mutate-animate';
|
||||
import { Hitbox, Projectile } from './barrage';
|
||||
import { MotaOffscreenCanvas2D, Transform } from '@motajs/render';
|
||||
import type { TowerBoss } from './towerBoss';
|
||||
import { IStateDamageable } from '@/game/state/interface';
|
||||
import { IStateDamageable } from '@user/data-state';
|
||||
import { PointEffect, PointEffectType } from '../fx/pointShader';
|
||||
import { isNil } from 'lodash-es';
|
||||
|
@ -9,7 +9,12 @@ import {
|
||||
disableViewport,
|
||||
enableViewport
|
||||
} from '@motajs/render';
|
||||
import type { HeroMover, MoveStep } from '@/game/state/move';
|
||||
import { loading } from '@user/data-base';
|
||||
import {
|
||||
heroMoveCollection,
|
||||
type HeroMover,
|
||||
type MoveStep
|
||||
} from '@user/data-state';
|
||||
import EventEmitter from 'eventemitter3';
|
||||
|
||||
export interface IChaseController {
|
||||
@ -108,7 +113,7 @@ export class Chase extends EventEmitter<ChaseEvent> {
|
||||
const layer = render.getElementById('layer-main')! as LayerGroup;
|
||||
this.layer = layer;
|
||||
|
||||
const mover = Mota.require('module', 'State').heroMoveCollection.mover;
|
||||
const mover = heroMoveCollection.mover;
|
||||
this.heroMove = mover;
|
||||
|
||||
mover.on('stepEnd', this.onStepEnd);
|
||||
@ -345,7 +350,7 @@ export class Chase extends EventEmitter<ChaseEvent> {
|
||||
}
|
||||
}
|
||||
|
||||
Mota.require('var', 'loading').once('coreInit', () => {
|
||||
loading.once('coreInit', () => {
|
||||
const shader = new Shader();
|
||||
Chase.shader = shader;
|
||||
shader.size(480, 480);
|
@ -1,6 +1,6 @@
|
||||
import { Animation, hyper, linear, power, sleep } from 'mutate-animate';
|
||||
import { Chase, ChaseData, IChaseController } from './chase';
|
||||
import { completeAchievement } from '@motajs/legacy-ui';
|
||||
// import { completeAchievement } from '@motajs/legacy-ui';
|
||||
import {
|
||||
Camera,
|
||||
CameraAnimation,
|
||||
@ -10,7 +10,9 @@ import {
|
||||
Sprite
|
||||
} from '@motajs/render';
|
||||
import { PointEffect, PointEffectType } from '../fx/pointShader';
|
||||
import { bgmController } from '@/module';
|
||||
import { bgmController } from '@user/client-modules';
|
||||
import { loading } from '@user/data-base';
|
||||
import { chaseInit1, clip } from '@user/legacy-plugin-data';
|
||||
|
||||
const path: Partial<Record<FloorIds, LocArr[]>> = {
|
||||
MT16: [
|
||||
@ -107,7 +109,7 @@ let back: Sprite | undefined;
|
||||
let contrastId: number = 0;
|
||||
const effect = new PointEffect();
|
||||
|
||||
Mota.require('var', 'loading').once('loaded', () => {
|
||||
loading.once('loaded', () => {
|
||||
effect.create(Chase.shader, 40);
|
||||
});
|
||||
|
||||
@ -204,7 +206,7 @@ export function initChase(): IChaseController {
|
||||
core.removeFlag('chaseId');
|
||||
|
||||
if (success) {
|
||||
completeAchievement('challenge', 0);
|
||||
// completeAchievement('challenge', 0);
|
||||
}
|
||||
});
|
||||
|
||||
@ -215,7 +217,7 @@ export function initChase(): IChaseController {
|
||||
para3(chase, ani);
|
||||
processScale(chase, ani, scale, camera);
|
||||
|
||||
Mota.Plugin.require('chase_g').chaseInit1();
|
||||
chaseInit1();
|
||||
|
||||
chase.on('end', () => {
|
||||
effect.end();
|
||||
@ -712,7 +714,7 @@ function para3(chase: Chase, ani: Animation) {
|
||||
});
|
||||
chase.onceLoc(21, 7, 'MT14', async () => {
|
||||
flags.finishChase1 = true;
|
||||
Mota.Plugin.require('replay_g').clip('choices:0');
|
||||
clip('choices:0');
|
||||
core.showStatusBar();
|
||||
ani.time(750).apply('rect', 0);
|
||||
chase.end(true);
|
@ -1,5 +1,5 @@
|
||||
import { Animation, linear, sleep } from 'mutate-animate';
|
||||
import { has } from '@motajs/legacy-ui';
|
||||
// import { has } from '@motajs/legacy-ui';
|
||||
|
||||
// todo: 移植到渲染树
|
||||
|
||||
@ -28,37 +28,37 @@ const FRAG_TIMING = linear();
|
||||
|
||||
export function init() {
|
||||
return;
|
||||
Mota.rewrite(core.events, 'afterBattle', 'add', (_, enemy, x, y) => {
|
||||
// 打怪特效
|
||||
const setting = Mota.require('var', 'mainSetting');
|
||||
if (setting.getValue('fx.frag') && has(x) && has(y)) {
|
||||
const frame = core.status.globalAnimateStatus % 2;
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = 32;
|
||||
canvas.height = 32;
|
||||
core.drawIcon(canvas, enemy.id, 0, 0, 32, 32, frame);
|
||||
const manager = applyFragWith(canvas);
|
||||
const frag = manager.canvas;
|
||||
frag.style.imageRendering = 'pixelated';
|
||||
frag.style.width = `${frag.width * core.domStyle.scale}px`;
|
||||
frag.style.height = `${frag.height * core.domStyle.scale}px`;
|
||||
const left =
|
||||
(x * 32 + 16 - frag.width / 2 - core.bigmap.offsetX) *
|
||||
core.domStyle.scale;
|
||||
const top =
|
||||
(y * 32 + 16 - frag.height / 2 - core.bigmap.offsetY) *
|
||||
core.domStyle.scale;
|
||||
frag.style.left = `${left}px`;
|
||||
frag.style.top = `${top}px`;
|
||||
frag.style.zIndex = '45';
|
||||
frag.style.position = 'absolute';
|
||||
frag.style.filter = 'sepia(20%)brightness(120%)';
|
||||
core.dom.gameDraw.appendChild(frag);
|
||||
manager.onEnd.then(() => {
|
||||
frag.remove();
|
||||
});
|
||||
}
|
||||
});
|
||||
// Mota.rewrite(core.events, 'afterBattle', 'add', (_, enemy, x, y) => {
|
||||
// // 打怪特效
|
||||
// const setting = Mota.require('var', 'mainSetting');
|
||||
// if (setting.getValue('fx.frag') && has(x) && has(y)) {
|
||||
// const frame = core.status.globalAnimateStatus % 2;
|
||||
// const canvas = document.createElement('canvas');
|
||||
// canvas.width = 32;
|
||||
// canvas.height = 32;
|
||||
// core.drawIcon(canvas, enemy.id, 0, 0, 32, 32, frame);
|
||||
// const manager = applyFragWith(canvas);
|
||||
// const frag = manager.canvas;
|
||||
// frag.style.imageRendering = 'pixelated';
|
||||
// frag.style.width = `${frag.width * core.domStyle.scale}px`;
|
||||
// frag.style.height = `${frag.height * core.domStyle.scale}px`;
|
||||
// const left =
|
||||
// (x * 32 + 16 - frag.width / 2 - core.bigmap.offsetX) *
|
||||
// core.domStyle.scale;
|
||||
// const top =
|
||||
// (y * 32 + 16 - frag.height / 2 - core.bigmap.offsetY) *
|
||||
// core.domStyle.scale;
|
||||
// frag.style.left = `${left}px`;
|
||||
// frag.style.top = `${top}px`;
|
||||
// frag.style.zIndex = '45';
|
||||
// frag.style.position = 'absolute';
|
||||
// frag.style.filter = 'sepia(20%)brightness(120%)';
|
||||
// core.dom.gameDraw.appendChild(frag);
|
||||
// manager.onEnd.then(() => {
|
||||
// frag.remove();
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
export function applyFragWith(
|
1
packages-user/legacy-plugin-client/src/fx/index.ts
Normal file
1
packages-user/legacy-plugin-client/src/fx/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './pointShader';
|
3
packages-user/legacy-plugin-client/src/index.ts
Normal file
3
packages-user/legacy-plugin-client/src/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './boss';
|
||||
export * from './chase';
|
||||
export * from './fx';
|
8
packages-user/legacy-plugin-data/package.json
Normal file
8
packages-user/legacy-plugin-data/package.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "@user/legacy-plugin-data",
|
||||
"dependencies": {
|
||||
"@user/data-state": "workspace:*",
|
||||
"@user/data-base": "workspace:*",
|
||||
"@user/data-utils": "workspace:*"
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
///<reference path="../../../../src/types/core.d.ts" />
|
||||
// @ts-nocheck
|
||||
|
||||
export {};
|
||||
|
||||
/* @__PURE__ */ (function () {
|
||||
@ -55,8 +56,8 @@ export {};
|
||||
core.status.maps[data].enemy?.calRealAttribute();
|
||||
core.updateStatusBar(true, true);
|
||||
}
|
||||
Mota.require('module', 'Shadow').Shadow.update(true);
|
||||
const Binder = Mota.require('module', 'Render').LayerGroupFloorBinder;
|
||||
Mota.require('@motajs/legacy-ui').Shadow.update(true);
|
||||
const Binder = Mota.require('@motajs/render').LayerGroupFloorBinder;
|
||||
Binder.activedBinder.forEach(v => {
|
||||
if (v.getFloor() === core.status.floorId) {
|
||||
v.updateBindData();
|
@ -1,4 +1,4 @@
|
||||
import { has, ofDir } from '@/plugin/game/utils';
|
||||
import { has, ofDir } from '@user/data-utils';
|
||||
|
||||
export function init() {
|
||||
// 伤害弹出
|
||||
@ -11,11 +11,11 @@ export function init() {
|
||||
const damage = info?.damage;
|
||||
if (damage) {
|
||||
if (!main.replayChecking) {
|
||||
Mota.Plugin.require('pop_r').addPop(
|
||||
(x - core.bigmap.offsetX / 32) * 32 + 12,
|
||||
(y - core.bigmap.offsetY / 32) * 32 + 20,
|
||||
(-damage).toString()
|
||||
);
|
||||
// addPop(
|
||||
// (x - core.bigmap.offsetX / 32) * 32 + 12,
|
||||
// (y - core.bigmap.offsetY / 32) * 32 + 20,
|
||||
// (-damage).toString()
|
||||
// );
|
||||
}
|
||||
core.status.hero.hp -= damage;
|
||||
const type = [...info.type];
|
6
packages-user/legacy-plugin-data/src/enemy/index.ts
Normal file
6
packages-user/legacy-plugin-data/src/enemy/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { init as initCheckBlock } from './checkblock';
|
||||
|
||||
initCheckBlock();
|
||||
|
||||
export * from './checkblock';
|
||||
export * from './remainEnemy';
|
720
packages-user/legacy-plugin-data/src/fallback.ts
Normal file
720
packages-user/legacy-plugin-data/src/fallback.ts
Normal file
@ -0,0 +1,720 @@
|
||||
import type {
|
||||
RenderAdapter,
|
||||
LayerDoorAnimate,
|
||||
LayerGroupAnimate,
|
||||
LayerFloorBinder,
|
||||
HeroRenderer,
|
||||
Layer,
|
||||
LayerGroup,
|
||||
FloorViewport
|
||||
} from '@motajs/render';
|
||||
import type { TimingFn } from 'mutate-animate';
|
||||
import { BlockMover, heroMoveCollection, MoveStep } from '@user/data-state';
|
||||
import { hook, loading } from '@user/data-base';
|
||||
import { Patch, PatchClass } from '@motajs/legacy-common';
|
||||
|
||||
// 向后兼容用,会充当两个版本间过渡的作用
|
||||
|
||||
interface Adapters {
|
||||
'hero-adapter'?: RenderAdapter<HeroRenderer>;
|
||||
'door-animate'?: RenderAdapter<LayerDoorAnimate>;
|
||||
animate?: RenderAdapter<LayerGroupAnimate>;
|
||||
layer?: RenderAdapter<Layer>;
|
||||
viewport?: RenderAdapter<FloorViewport>;
|
||||
}
|
||||
|
||||
const adapters: Adapters = {};
|
||||
|
||||
export function initFallback() {
|
||||
let fallbackIds: number = 1e8;
|
||||
|
||||
if (!main.replayChecking && main.mode === 'play') {
|
||||
const Adapter = Mota.require('@motajs/render').RenderAdapter;
|
||||
const hero = Adapter.get<HeroRenderer>('hero-adapter');
|
||||
const doorAnimate = Adapter.get<LayerDoorAnimate>('door-animate');
|
||||
const animate = Adapter.get<LayerGroupAnimate>('animate');
|
||||
const layer = Adapter.get<Layer>('layer');
|
||||
const viewport = Adapter.get<FloorViewport>('viewport');
|
||||
|
||||
adapters['hero-adapter'] = hero;
|
||||
adapters['door-animate'] = doorAnimate;
|
||||
adapters['animate'] = animate;
|
||||
adapters['layer'] = layer;
|
||||
adapters['viewport'] = viewport;
|
||||
}
|
||||
|
||||
const { mover: heroMover } = heroMoveCollection;
|
||||
|
||||
// ----- 工具函数
|
||||
|
||||
/**
|
||||
* 根据事件中给出的移动数组解析出全部的移动步骤
|
||||
*/
|
||||
function getMoveSteps(steps: string[]) {
|
||||
const moveSteps: string[] = [];
|
||||
steps.forEach(v => {
|
||||
const [type, number] = v.split(':');
|
||||
if (!number) moveSteps.push(type);
|
||||
else {
|
||||
if (type === 'speed') moveSteps.push(v);
|
||||
else {
|
||||
moveSteps.push(...Array(Number(number)).fill(type));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return moveSteps;
|
||||
}
|
||||
|
||||
function setHeroDirection(dir: Dir) {
|
||||
heroMover.setFaceDir(dir);
|
||||
heroMover.setMoveDir(dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成跳跃函数
|
||||
*/
|
||||
function generateJumpFn(dx: number, dy: number): TimingFn<3> {
|
||||
const distance = Math.hypot(dx, dy);
|
||||
const peak = 3 + distance;
|
||||
|
||||
return (progress: number) => {
|
||||
const x = dx * progress;
|
||||
const y = progress * dy + (progress ** 2 - progress) * peak;
|
||||
|
||||
return [x, y, Math.ceil(y)];
|
||||
};
|
||||
}
|
||||
|
||||
Mota.r(() => {
|
||||
// ----- 引入
|
||||
const { Camera, MotaRenderer: Renderer } =
|
||||
Mota.require('@motajs/render');
|
||||
const Animation = Mota.require('MutateAnimate');
|
||||
|
||||
const patch = new Patch(PatchClass.Control);
|
||||
const patch2 = new Patch(PatchClass.Events);
|
||||
const patch3 = new Patch(PatchClass.Maps);
|
||||
|
||||
//#region 勇士移动相关
|
||||
patch.add('moveAction', async function (callback?: () => void) {
|
||||
heroMover.clearMoveQueue();
|
||||
heroMover.oneStep('forward');
|
||||
const lock = core.status.lockControl;
|
||||
const controller = heroMover.startMove(false, true, lock);
|
||||
controller?.onEnd.then(() => {
|
||||
callback?.();
|
||||
});
|
||||
heroMover.once('stepEnd', () => {
|
||||
controller?.stop();
|
||||
});
|
||||
});
|
||||
|
||||
patch.add('_moveAction_moving', () => {});
|
||||
|
||||
patch2.add(
|
||||
'_action_moveAction',
|
||||
function (data: any, x: number, y: number, prefix: any) {
|
||||
if (core.canMoveHero()) {
|
||||
var nx = core.nextX(),
|
||||
ny = core.nextY();
|
||||
// 检查noPass决定是撞击还是移动
|
||||
if (core.noPass(nx, ny)) {
|
||||
core.insertAction([{ type: 'trigger', loc: [nx, ny] }]);
|
||||
} else {
|
||||
// 先移动一格,然后尝试触发事件
|
||||
core.insertAction([
|
||||
{
|
||||
type: 'function',
|
||||
function:
|
||||
'function() { core.moveAction(core.doAction); }',
|
||||
async: true
|
||||
},
|
||||
{ type: '_label' }
|
||||
]);
|
||||
}
|
||||
}
|
||||
core.doAction();
|
||||
}
|
||||
);
|
||||
|
||||
patch2.add(
|
||||
'eventMoveHero',
|
||||
async function (
|
||||
steps: string[],
|
||||
time: number = 500,
|
||||
callback?: () => void
|
||||
) {
|
||||
if (heroMover.moving) return;
|
||||
const moveSteps = getMoveSteps(steps);
|
||||
|
||||
const resolved = moveSteps.map<MoveStep>(v => {
|
||||
if (v.startsWith('speed')) {
|
||||
return { type: 'speed', value: Number(v.slice(6)) };
|
||||
} else {
|
||||
return { type: 'dir', value: v as Move2 };
|
||||
}
|
||||
});
|
||||
const start: MoveStep = { type: 'speed', value: time };
|
||||
|
||||
heroMover.insertMove(...[start, ...resolved]);
|
||||
const controller = heroMover.startMove(true, true, true, false);
|
||||
if (!controller) {
|
||||
callback?.();
|
||||
return;
|
||||
}
|
||||
controller.onEnd.then(() => {
|
||||
callback?.();
|
||||
});
|
||||
|
||||
const animate = fallbackIds++;
|
||||
|
||||
core.animateFrame.lastAsyncId = animate;
|
||||
core.animateFrame.asyncId[animate] = controller.stop;
|
||||
}
|
||||
);
|
||||
|
||||
patch.add(
|
||||
'setHeroLoc',
|
||||
function (
|
||||
name: 'x' | 'y' | 'direction',
|
||||
value: number | Dir,
|
||||
noGather?: boolean
|
||||
) {
|
||||
if (!core.status.hero) return;
|
||||
// @ts-ignore
|
||||
core.status.hero.loc[name] = value;
|
||||
if ((name === 'x' || name === 'y') && !noGather) {
|
||||
core.control.gatherFollowers();
|
||||
}
|
||||
if (name === 'direction') {
|
||||
adapters['hero-adapter']?.sync('turn', value);
|
||||
adapters['hero-adapter']?.sync('setAnimateDir', value);
|
||||
setHeroDirection(value as Dir);
|
||||
} else if (name === 'x') {
|
||||
// 为了防止逆天样板出问题
|
||||
core.bigmap.posX = value as number;
|
||||
adapters['hero-adapter']?.sync('setHeroLoc', value);
|
||||
} else {
|
||||
// 为了防止逆天样板出问题
|
||||
core.bigmap.posY = value as number;
|
||||
adapters['hero-adapter']?.sync('setHeroLoc', void 0, value);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
patch.add('waitHeroToStop', function (callback?: () => void) {
|
||||
core.stopAutomaticRoute();
|
||||
core.clearContinueAutomaticRoute();
|
||||
heroMover.controller?.stop();
|
||||
if (callback) {
|
||||
core.status.replay.animate = true;
|
||||
core.lockControl();
|
||||
core.status.automaticRoute.moveDirectly = false;
|
||||
setTimeout(
|
||||
function () {
|
||||
core.status.replay.animate = false;
|
||||
callback();
|
||||
},
|
||||
core.status.replay.speed === 24 ? 1 : 30
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
patch.add(
|
||||
'moveHero',
|
||||
async function (
|
||||
direction?: Dir,
|
||||
callback?: () => void,
|
||||
noRoute: boolean = false
|
||||
) {
|
||||
if (heroMover.moving) return;
|
||||
heroMover.clearMoveQueue();
|
||||
heroMover.oneStep(direction ?? 'forward');
|
||||
const lock = core.status.lockControl;
|
||||
const controller = heroMover.startMove(false, noRoute, lock);
|
||||
controller?.onEnd.then(() => {
|
||||
callback?.();
|
||||
});
|
||||
heroMover.once('stepEnd', () => {
|
||||
controller?.stop();
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
patch2.add('setHeroIcon', function (name: ImageIds) {
|
||||
const img = core.material.images.images[name];
|
||||
if (!img) return;
|
||||
core.status.hero.image = name;
|
||||
adapters['hero-adapter']?.sync('setImage', img);
|
||||
});
|
||||
|
||||
patch.add('isMoving', function () {
|
||||
return heroMover.moving;
|
||||
});
|
||||
|
||||
patch.add(
|
||||
'setAutomaticRoute',
|
||||
function (destX: number, destY: number, stepPostfix: DiredLoc[]) {
|
||||
if (heroMover.moving) return;
|
||||
if (!core.status.played || core.status.lockControl) return;
|
||||
if (core.control._setAutomaticRoute_isMoving(destX, destY))
|
||||
return;
|
||||
if (
|
||||
core.control._setAutomaticRoute_isTurning(
|
||||
destX,
|
||||
destY,
|
||||
stepPostfix
|
||||
)
|
||||
)
|
||||
return;
|
||||
if (
|
||||
core.control._setAutomaticRoute_clickMoveDirectly(
|
||||
destX,
|
||||
destY,
|
||||
stepPostfix
|
||||
)
|
||||
)
|
||||
return;
|
||||
// 找寻自动寻路路线
|
||||
const moveStep = core.automaticRoute(destX, destY);
|
||||
if (
|
||||
moveStep.length == 0 &&
|
||||
(destX != core.status.hero.loc.x ||
|
||||
destY != core.status.hero.loc.y ||
|
||||
stepPostfix.length == 0)
|
||||
)
|
||||
return;
|
||||
moveStep.push(...stepPostfix);
|
||||
core.status.automaticRoute.destX = destX;
|
||||
core.status.automaticRoute.destY = destY;
|
||||
core.control._setAutomaticRoute_drawRoute(moveStep);
|
||||
core.control._setAutomaticRoute_setAutoSteps(moveStep);
|
||||
|
||||
// ???
|
||||
core.setAutoHeroMove();
|
||||
|
||||
// 执行移动
|
||||
const steps: MoveStep[] = moveStep.map(v => {
|
||||
return { type: 'dir', value: v.direction };
|
||||
});
|
||||
heroMover.clearMoveQueue();
|
||||
heroMover.insertMove(...steps);
|
||||
heroMover.startMove();
|
||||
}
|
||||
);
|
||||
|
||||
//#region 开关门
|
||||
|
||||
patch2.add(
|
||||
'openDoor',
|
||||
function (
|
||||
x: number,
|
||||
y: number,
|
||||
needKey: boolean,
|
||||
callback?: () => void
|
||||
) {
|
||||
var block = core.getBlock(x, y);
|
||||
core.saveAndStopAutomaticRoute();
|
||||
if (!core.events._openDoor_check(block, x, y, needKey)) {
|
||||
var locked = core.status.lockControl;
|
||||
core.waitHeroToStop(function () {
|
||||
if (!locked) core.unlockControl();
|
||||
if (callback) callback();
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (core.status.replay.speed === 24) {
|
||||
core.status.replay.animate = true;
|
||||
core.removeBlock(x, y);
|
||||
setTimeout(function () {
|
||||
core.status.replay.animate = false;
|
||||
hook.emit(
|
||||
'afterOpenDoor',
|
||||
block.event.id as AllIdsOf<'animates'>,
|
||||
x,
|
||||
y
|
||||
);
|
||||
if (callback) callback();
|
||||
}, 1); // +1是为了录像检测系统
|
||||
} else {
|
||||
const locked = core.status.lockControl;
|
||||
core.lockControl();
|
||||
core.status.replay.animate = true;
|
||||
core.removeBlock(x, y);
|
||||
|
||||
const cb = () => {
|
||||
core.maps._removeBlockFromMap(
|
||||
core.status.floorId,
|
||||
block
|
||||
);
|
||||
if (!locked) core.unlockControl();
|
||||
core.status.replay.animate = false;
|
||||
hook.emit(
|
||||
'afterOpenDoor',
|
||||
block.event.id as AllIdsOf<'animates'>,
|
||||
x,
|
||||
y
|
||||
);
|
||||
callback?.();
|
||||
};
|
||||
|
||||
adapters['door-animate']?.all('openDoor', block).then(cb);
|
||||
|
||||
const animate = fallbackIds++;
|
||||
core.animateFrame.lastAsyncId = animate;
|
||||
core.animateFrame.asyncId[animate] = cb;
|
||||
// this._openDoor_animate(block, x, y, callback);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
patch2.add(
|
||||
'closeDoor',
|
||||
function (x: number, y: number, id: AllIds, callback?: () => void) {
|
||||
id = id || '';
|
||||
if (
|
||||
// @ts-ignore
|
||||
(core.material.icons.animates[id] == null &&
|
||||
// @ts-ignore
|
||||
core.material.icons.npc48[id] == null) ||
|
||||
core.getBlock(x, y) != null
|
||||
) {
|
||||
if (callback) callback();
|
||||
return;
|
||||
}
|
||||
var block = core.getBlockById(id);
|
||||
var doorInfo = (block.event || {}).doorInfo;
|
||||
if (!doorInfo) {
|
||||
if (callback) callback();
|
||||
return;
|
||||
}
|
||||
|
||||
core.playSound(doorInfo.closeSound);
|
||||
|
||||
const locked = core.status.lockControl;
|
||||
core.lockControl();
|
||||
core.status.replay.animate = true;
|
||||
const cb = function () {
|
||||
if (!locked) core.unlockControl();
|
||||
core.status.replay.animate = false;
|
||||
core.setBlock(id, x, y);
|
||||
core.showBlock(x, y);
|
||||
callback?.();
|
||||
};
|
||||
|
||||
if (core.status.replay.speed === 24) {
|
||||
cb();
|
||||
} else {
|
||||
adapters['door-animate']
|
||||
?.all('closeDoor', block)
|
||||
.then(() => {
|
||||
cb();
|
||||
});
|
||||
|
||||
const animate = fallbackIds++;
|
||||
core.animateFrame.lastAsyncId = animate;
|
||||
core.animateFrame.asyncId[animate] = cb;
|
||||
core.events._openDoor_animate(block, x, y, callback);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
//#region 动画
|
||||
|
||||
patch3.add(
|
||||
'drawAnimate',
|
||||
function (
|
||||
name: AnimationIds,
|
||||
x: number,
|
||||
y: number,
|
||||
alignWindow?: boolean,
|
||||
callback?: () => void
|
||||
) {
|
||||
// @ts-ignore
|
||||
name = core.getMappedName(name);
|
||||
|
||||
// 正在播放录像:不显示动画
|
||||
if (
|
||||
core.isReplaying() ||
|
||||
!core.material.animates[name] ||
|
||||
x == null ||
|
||||
y == null
|
||||
) {
|
||||
if (callback) callback();
|
||||
return -1;
|
||||
}
|
||||
|
||||
adapters.animate
|
||||
?.all(
|
||||
'drawAnimate',
|
||||
name,
|
||||
x * 32 + 16,
|
||||
y * 32 + 16,
|
||||
alignWindow ?? false
|
||||
)
|
||||
.then(() => {
|
||||
callback?.();
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
patch3.add(
|
||||
'drawHeroAnimate',
|
||||
function (name: AnimationIds, callback?: () => void) {
|
||||
// @ts-ignore
|
||||
name = core.getMappedName(name);
|
||||
|
||||
// 正在播放录像或动画不存在:不显示动画
|
||||
if (core.isReplaying() || !core.material.animates[name]) {
|
||||
if (callback) callback();
|
||||
return -1;
|
||||
}
|
||||
|
||||
adapters.animate?.global('drawHeroAnimate', name).then(() => {
|
||||
callback?.();
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
patch3.add(
|
||||
'moveBlock',
|
||||
async function (
|
||||
x: number,
|
||||
y: number,
|
||||
steps: string[],
|
||||
time: number = 500,
|
||||
keep: boolean = false,
|
||||
callback?: () => void
|
||||
) {
|
||||
if (!steps || steps.length === 0) {
|
||||
callback?.();
|
||||
return;
|
||||
}
|
||||
const block = core.getBlock(x, y);
|
||||
if (!block) {
|
||||
callback?.();
|
||||
return;
|
||||
}
|
||||
const mover = new BlockMover(
|
||||
x,
|
||||
y,
|
||||
core.status.floorId,
|
||||
'event'
|
||||
);
|
||||
const moveSteps = getMoveSteps(steps);
|
||||
const resolved = moveSteps.map<MoveStep>(v => {
|
||||
if (v.startsWith('speed')) {
|
||||
return { type: 'speed', value: Number(v.slice(6)) };
|
||||
} else {
|
||||
return { type: 'dir', value: v as Move2 };
|
||||
}
|
||||
});
|
||||
const start: MoveStep = { type: 'speed', value: time };
|
||||
mover.insertMove(...[start, ...resolved]);
|
||||
const controller = mover.startMove();
|
||||
|
||||
if (controller) {
|
||||
await controller.onEnd;
|
||||
}
|
||||
|
||||
if (!keep) {
|
||||
core.removeBlock(mover.x, mover.y);
|
||||
}
|
||||
callback?.();
|
||||
}
|
||||
);
|
||||
|
||||
patch3.add(
|
||||
'jumpBlock',
|
||||
async function (
|
||||
sx: number,
|
||||
sy: number,
|
||||
ex: number,
|
||||
ey: number,
|
||||
time: number = 500,
|
||||
keep: boolean = false,
|
||||
callback?: () => void
|
||||
) {
|
||||
const block = core.getBlock(sx, sy);
|
||||
if (!block) {
|
||||
callback?.();
|
||||
return;
|
||||
}
|
||||
time /= core.status.replay.speed;
|
||||
if (core.status.replay.speed === 24) time = 1;
|
||||
const dx = ex - sx;
|
||||
const dy = ey - sy;
|
||||
|
||||
const fn = generateJumpFn(dx, dy);
|
||||
|
||||
const list = adapters.layer?.items ?? [];
|
||||
const items = [...list].filter(v => {
|
||||
if (v.layer !== 'event') return false;
|
||||
const ex = v.getExtends('floor-binder') as LayerFloorBinder;
|
||||
if (!ex) return false;
|
||||
return ex.getFloor() === core.status.floorId;
|
||||
});
|
||||
const width = core.status.thisMap.width;
|
||||
const index = sx + sy * width;
|
||||
|
||||
const promise = Promise.all(
|
||||
items.map(v => {
|
||||
return v.moveAs(index, ex, ey, fn, time, keep);
|
||||
})
|
||||
);
|
||||
|
||||
core.updateStatusBar();
|
||||
core.removeBlock(sx, sy);
|
||||
await promise;
|
||||
if (keep) {
|
||||
core.setBlock(block.id, ex, ey);
|
||||
}
|
||||
core.updateStatusBar();
|
||||
|
||||
callback?.();
|
||||
}
|
||||
);
|
||||
|
||||
patch2.add(
|
||||
'jumpHero',
|
||||
async function (
|
||||
ex: number,
|
||||
ey: number,
|
||||
time: number = 500,
|
||||
callback?: () => void
|
||||
) {
|
||||
if (heroMover.moving) return;
|
||||
|
||||
const sx = core.getHeroLoc('x');
|
||||
const sy = core.getHeroLoc('y');
|
||||
adapters.viewport?.all('mutateTo', ex, ey, time);
|
||||
|
||||
const locked = core.status.lockControl;
|
||||
core.lockControl();
|
||||
const list = adapters['hero-adapter']?.items ?? [];
|
||||
const items = [...list];
|
||||
|
||||
time /= core.status.replay.speed;
|
||||
if (core.status.replay.speed === 24) time = 1;
|
||||
const fn = generateJumpFn(ex - sx, ey - sy);
|
||||
await Promise.all(
|
||||
items.map(v => {
|
||||
if (!v.renderable) return Promise.reject();
|
||||
return v.layer.moveRenderable(
|
||||
v.renderable,
|
||||
sx,
|
||||
sy,
|
||||
fn,
|
||||
time
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
if (!locked) core.unlockControl();
|
||||
core.setHeroLoc('x', ex);
|
||||
core.setHeroLoc('y', ey);
|
||||
callback?.();
|
||||
}
|
||||
);
|
||||
|
||||
//#region 视角处理
|
||||
|
||||
patch.add(
|
||||
'moveDirectly',
|
||||
function (destX: number, destY: number, ignoreSteps: number) {
|
||||
const data = core.control.controldata;
|
||||
const success = data.moveDirectly(destX, destY, ignoreSteps);
|
||||
if (success) adapters.viewport?.all('mutateTo', destX, destY);
|
||||
return success;
|
||||
}
|
||||
);
|
||||
|
||||
patch.add(
|
||||
'moveViewport',
|
||||
function (
|
||||
x: number,
|
||||
y: number,
|
||||
_moveMode: EaseMode,
|
||||
time: number = 1,
|
||||
callback?: () => void
|
||||
) {
|
||||
const main = Renderer.get('render-main');
|
||||
const layer = main?.getElementById('layer-main') as LayerGroup;
|
||||
if (!layer) return;
|
||||
const camera = Camera.for(layer);
|
||||
camera.clearOperation();
|
||||
const translate = camera.addTranslate();
|
||||
|
||||
const animateTime =
|
||||
time / Math.max(core.status.replay.speed, 1);
|
||||
const animate = new Animation.Animation();
|
||||
animate
|
||||
.absolute()
|
||||
.time(1)
|
||||
.mode(Animation.linear())
|
||||
.move(core.bigmap.offsetX, core.bigmap.offsetY);
|
||||
animate.time(animateTime).move(x * 32, y * 32);
|
||||
|
||||
camera.applyTranslateAnimation(
|
||||
translate,
|
||||
animate,
|
||||
animateTime + 50
|
||||
);
|
||||
camera.transform = layer.camera;
|
||||
|
||||
const end = () => {
|
||||
core.bigmap.offsetX = x * 32;
|
||||
core.bigmap.offsetY = y * 32;
|
||||
camera.destroy();
|
||||
callback?.();
|
||||
};
|
||||
|
||||
const timeout = window.setTimeout(end, animateTime + 50);
|
||||
|
||||
const id = fallbackIds++;
|
||||
core.animateFrame.lastAsyncId = id;
|
||||
core.animateFrame.asyncId[id] = () => {
|
||||
end();
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
loading.once('loaded', () => {
|
||||
for (const animate of Object.values(core.material.animates)) {
|
||||
animate.se ??= {};
|
||||
if (typeof animate.se === 'string') {
|
||||
animate.se = { 1: animate.se };
|
||||
}
|
||||
animate.pitch ??= {};
|
||||
}
|
||||
});
|
||||
loading.once('coreInit', () => {
|
||||
const moveAction = new Set<string>(['up', 'down', 'left', 'right']);
|
||||
// 复写录像的移动
|
||||
core.registerReplayAction('move', action => {
|
||||
if (moveAction.has(action)) {
|
||||
if (!heroMover.moving) {
|
||||
heroMover.startMove();
|
||||
}
|
||||
if (!heroMover.controller) {
|
||||
return false;
|
||||
}
|
||||
heroMover.controller.push({
|
||||
type: 'dir',
|
||||
value: action as Dir
|
||||
});
|
||||
|
||||
heroMover.controller.onEnd.then(() => {
|
||||
core.replay();
|
||||
});
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user