mirror of
https://github.com/unanmed/HumanBreak.git
synced 2026-04-15 17:31:09 +08:00
Compare commits
No commits in common. "6c75fa507c166226ad55dfe3e7e84f80a7e5fb69" and "b5d9813dcf4707d77415e4dbd6835c91e84ca904" have entirely different histories.
6c75fa507c
...
b5d9813dcf
@ -1,6 +1,6 @@
|
|||||||
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 { hovered, mainUi, openDanmakuPoster } from '@motajs/legacy-ui';
|
import { hovered, mainUi, tip, openDanmakuPoster } from '@motajs/legacy-ui';
|
||||||
import { GameStorage } from '@motajs/legacy-system';
|
import { GameStorage } from '@motajs/legacy-system';
|
||||||
|
|
||||||
export const mainScope = Symbol.for('@key_main');
|
export const mainScope = Symbol.for('@key_main');
|
||||||
@ -220,6 +220,23 @@ gameKey
|
|||||||
defaults: KeyCode.Digit0,
|
defaults: KeyCode.Digit0,
|
||||||
alt: true
|
alt: true
|
||||||
})
|
})
|
||||||
|
//#region 技能按键
|
||||||
|
.group('skill', '技能按键')
|
||||||
|
.register({
|
||||||
|
id: 'skill1',
|
||||||
|
name: '断灭之刃',
|
||||||
|
defaults: KeyCode.Digit1
|
||||||
|
})
|
||||||
|
.register({
|
||||||
|
id: 'skill2',
|
||||||
|
name: '跳跃',
|
||||||
|
defaults: KeyCode.Digit2
|
||||||
|
})
|
||||||
|
.register({
|
||||||
|
id: 'skill3',
|
||||||
|
name: '铸剑为盾',
|
||||||
|
defaults: KeyCode.Digit3
|
||||||
|
})
|
||||||
//#region 系统按键
|
//#region 系统按键
|
||||||
.group('system', '系统按键')
|
.group('system', '系统按键')
|
||||||
.register({
|
.register({
|
||||||
@ -573,6 +590,45 @@ gameKey
|
|||||||
.realize('comment', () => {
|
.realize('comment', () => {
|
||||||
core.actions._clickGameInfo_openComments();
|
core.actions._clickGameInfo_openComments();
|
||||||
})
|
})
|
||||||
|
.realize('skill1', () => {
|
||||||
|
const HeroSkill = Mota.require('@user/data-state').HeroSkill;
|
||||||
|
if (!HeroSkill.learnedSkill(HeroSkill.Blade)) return;
|
||||||
|
if (HeroSkill.getAutoSkill()) {
|
||||||
|
tip('error', '已开启自动切换技能!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
core.playSound('光标移动');
|
||||||
|
HeroSkill.toggleSkill(HeroSkill.Blade);
|
||||||
|
core.status.route.push('useSkill:Blade');
|
||||||
|
core.updateStatusBar();
|
||||||
|
})
|
||||||
|
.realize('skill2', () => {
|
||||||
|
const HeroSkill = Mota.require('@user/data-state').HeroSkill;
|
||||||
|
if (
|
||||||
|
!flags.onChase &&
|
||||||
|
!core.status.floorId.startsWith('tower') &&
|
||||||
|
HeroSkill.learnedSkill(HeroSkill.Jump)
|
||||||
|
) {
|
||||||
|
Mota.require('@user/legacy-plugin-data').jumpSkill();
|
||||||
|
core.status.route.push('useSkill:Jump');
|
||||||
|
} else {
|
||||||
|
if (core.hasItem('pickaxe')) {
|
||||||
|
core.useItem('pickaxe');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.realize('skill3', () => {
|
||||||
|
const HeroSkill = Mota.require('@user/data-state').HeroSkill;
|
||||||
|
if (!HeroSkill.learnedSkill(HeroSkill.Shield)) return;
|
||||||
|
if (HeroSkill.getAutoSkill()) {
|
||||||
|
tip('error', '已开启自动切换技能!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
core.playSound('光标移动');
|
||||||
|
HeroSkill.toggleSkill(HeroSkill.Shield);
|
||||||
|
core.status.route.push('useSkill:Shield');
|
||||||
|
core.updateStatusBar();
|
||||||
|
})
|
||||||
.realize('debug', () => {
|
.realize('debug', () => {
|
||||||
core.debug();
|
core.debug();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1277,7 +1277,6 @@ export class TextContentParser {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TextGuessStatus.NeedSplit: {
|
case TextGuessStatus.NeedSplit: {
|
||||||
this.bsStart = this.blockPointer;
|
|
||||||
this.bsEnd = this.wordBreak.length;
|
this.bsEnd = this.wordBreak.length;
|
||||||
this.splitTextLoop(node, width);
|
this.splitTextLoop(node, width);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -159,10 +159,8 @@ export class FloorItemDetail implements ILayerGroupRenderExtends {
|
|||||||
* @param block 要计算的分块
|
* @param block 要计算的分块
|
||||||
*/
|
*/
|
||||||
calAllItems(block: Set<number>) {
|
calAllItems(block: Set<number>) {
|
||||||
const enable = mainSetting.getValue('screen.itemDetail');
|
if (!core.status.thisMap) return;
|
||||||
if (!core.status.thisMap || !enable) return;
|
|
||||||
if (this.dirtyBlock.size === 0 || block.size === 0) return;
|
if (this.dirtyBlock.size === 0 || block.size === 0) return;
|
||||||
|
|
||||||
let diff: Record<string | symbol, number | undefined> = {};
|
let diff: Record<string | symbol, number | undefined> = {};
|
||||||
const before = core.status.hero;
|
const before = core.status.hero;
|
||||||
const hero = structuredClone(core.status.hero);
|
const hero = structuredClone(core.status.hero);
|
||||||
@ -176,7 +174,8 @@ export class FloorItemDetail implements ILayerGroupRenderExtends {
|
|||||||
core.status.hero = new Proxy(hero, handler);
|
core.status.hero = new Proxy(hero, handler);
|
||||||
|
|
||||||
core.setFlag('__statistics__', true);
|
core.setFlag('__statistics__', true);
|
||||||
this.dirtyBlock.forEach(v => {
|
block.forEach(v => {
|
||||||
|
if (!this.dirtyBlock.has(v)) return;
|
||||||
const data = this.blockData.get(v);
|
const data = this.blockData.get(v);
|
||||||
const detail = this.detailData.get(v);
|
const detail = this.detailData.get(v);
|
||||||
detail?.clear();
|
detail?.clear();
|
||||||
|
|||||||
@ -2,10 +2,12 @@ import { createApp, Font } from '@motajs/render';
|
|||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { DEFAULT_FONT, MAIN_HEIGHT, MAIN_WIDTH } from './shared';
|
import { DEFAULT_FONT, MAIN_HEIGHT, MAIN_WIDTH } from './shared';
|
||||||
import { hook, loading } from '@user/data-base';
|
import { hook, loading } from '@user/data-base';
|
||||||
|
import { createLoopMap } from './loopMap';
|
||||||
import { createElements } from './elements';
|
import { createElements } from './elements';
|
||||||
import { mainRenderer } from './renderer';
|
import { mainRenderer } from './renderer';
|
||||||
import { createUI } from './ui';
|
import { createUI } from './ui';
|
||||||
import { createAction } from './action';
|
import { createAction } from './action';
|
||||||
|
import { createLegacy } from './legacy';
|
||||||
import { sceneController } from './scene';
|
import { sceneController } from './scene';
|
||||||
import { GameTitleUI } from './ui/title';
|
import { GameTitleUI } from './ui/title';
|
||||||
import { createWeather } from './weather';
|
import { createWeather } from './weather';
|
||||||
@ -27,8 +29,10 @@ export function createGameRenderer() {
|
|||||||
|
|
||||||
export function createRender() {
|
export function createRender() {
|
||||||
createElements();
|
createElements();
|
||||||
|
createLegacy();
|
||||||
createUI();
|
createUI();
|
||||||
createAction();
|
createAction();
|
||||||
|
createLoopMap();
|
||||||
createWeather();
|
createWeather();
|
||||||
|
|
||||||
loading.on('loaded', () => {
|
loading.on('loaded', () => {
|
||||||
@ -47,6 +51,7 @@ export function createRender() {
|
|||||||
export * from './components';
|
export * from './components';
|
||||||
export * from './elements';
|
export * from './elements';
|
||||||
export * from './fx';
|
export * from './fx';
|
||||||
|
export * from './legacy';
|
||||||
export * from './ui';
|
export * from './ui';
|
||||||
export * from './utils';
|
export * from './utils';
|
||||||
export * from './weather';
|
export * from './weather';
|
||||||
|
|||||||
64
packages-user/client-modules/src/render/legacy/gameCanvas.ts
Normal file
64
packages-user/client-modules/src/render/legacy/gameCanvas.ts
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import { logger } from '@motajs/common';
|
||||||
|
import { loading } from '@user/data-base';
|
||||||
|
import {
|
||||||
|
ILayerGroupRenderExtends,
|
||||||
|
LayerGroup,
|
||||||
|
LayerGroupFloorBinder
|
||||||
|
} from '../elements';
|
||||||
|
|
||||||
|
const filterMap: [FloorIds[], string][] = [];
|
||||||
|
|
||||||
|
function getCanvasFilterByFloorId(floorId: FloorIds = core.status.floorId) {
|
||||||
|
return filterMap.find(v => v[0].includes(floorId))?.[1] ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
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';
|
||||||
|
|
||||||
|
group!: LayerGroup;
|
||||||
|
binder!: LayerGroupFloorBinder;
|
||||||
|
|
||||||
|
setFilter(floorId: FloorIds) {
|
||||||
|
const filter = getCanvasFilterByFloorId(floorId);
|
||||||
|
this.group.setFilter(filter);
|
||||||
|
// console.log(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private onFloorChange = (floor: FloorIds) => {
|
||||||
|
this.setFilter(floor);
|
||||||
|
};
|
||||||
|
|
||||||
|
private listen() {
|
||||||
|
this.binder.on('floorChange', this.onFloorChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
awake(group: LayerGroup): void {
|
||||||
|
this.group = group;
|
||||||
|
const ex = group.getExtends('floor-binder');
|
||||||
|
if (ex instanceof LayerGroupFloorBinder) {
|
||||||
|
this.binder = ex;
|
||||||
|
this.listen();
|
||||||
|
} else {
|
||||||
|
logger.error(1201);
|
||||||
|
group.removeExtends('floor-damage');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onDestroy(_group: LayerGroup): void {
|
||||||
|
this.binder?.off('floorChange', this.onFloorChange);
|
||||||
|
}
|
||||||
|
}
|
||||||
9
packages-user/client-modules/src/render/legacy/index.ts
Normal file
9
packages-user/client-modules/src/render/legacy/index.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { createGameCanvas } from './gameCanvas';
|
||||||
|
import { createShadow } from './shadow';
|
||||||
|
|
||||||
|
export function createLegacy() {
|
||||||
|
createGameCanvas();
|
||||||
|
createShadow();
|
||||||
|
}
|
||||||
|
|
||||||
|
export * from './shadow';
|
||||||
103
packages-user/client-modules/src/render/legacy/pop.ts
Normal file
103
packages-user/client-modules/src/render/legacy/pop.ts
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
import {
|
||||||
|
wrapInstancedComponent,
|
||||||
|
RenderItem,
|
||||||
|
RenderItemPosition,
|
||||||
|
Transform,
|
||||||
|
MotaOffscreenCanvas2D
|
||||||
|
} from '@motajs/render';
|
||||||
|
|
||||||
|
import { TimingFn } from 'mutate-animate';
|
||||||
|
|
||||||
|
interface PopData {
|
||||||
|
cx: number;
|
||||||
|
cy: number;
|
||||||
|
path: TimingFn<2>;
|
||||||
|
text: string;
|
||||||
|
time: number;
|
||||||
|
start: number;
|
||||||
|
color: CanvasStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parabola(input: number): [number, number] {
|
||||||
|
const x = input * 100;
|
||||||
|
return [x, x ** 2 / 20 - 3 * x];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Pop extends RenderItem {
|
||||||
|
private popList: Set<PopData> = new Set();
|
||||||
|
|
||||||
|
private delegation: number = 0;
|
||||||
|
|
||||||
|
constructor(type: RenderItemPosition) {
|
||||||
|
super(type, false);
|
||||||
|
this.delegation = this.delegateTicker(() => {
|
||||||
|
if (this.popList.size > 0) this.update();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加一个弹出文字
|
||||||
|
* @param text 要显示的文字
|
||||||
|
* @param time 持续时长
|
||||||
|
* @param cx 中心点,也就是从哪弹出的
|
||||||
|
* @param cy 中心点,也就是从哪弹出的
|
||||||
|
* @param path 自定义路径,不填表示默认的抛物线路径
|
||||||
|
*/
|
||||||
|
addPop(
|
||||||
|
text: string,
|
||||||
|
time: number,
|
||||||
|
cx: number,
|
||||||
|
cy: number,
|
||||||
|
color: CanvasStyle,
|
||||||
|
path?: TimingFn<2>
|
||||||
|
) {
|
||||||
|
this.popList.add({
|
||||||
|
text,
|
||||||
|
time,
|
||||||
|
cx,
|
||||||
|
cy,
|
||||||
|
color,
|
||||||
|
path: path ?? parabola,
|
||||||
|
start: Date.now()
|
||||||
|
});
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(
|
||||||
|
canvas: MotaOffscreenCanvas2D,
|
||||||
|
_transform: Transform
|
||||||
|
): void {
|
||||||
|
const ctx = canvas.ctx;
|
||||||
|
const toDelete = new Set<PopData>();
|
||||||
|
const now = Date.now();
|
||||||
|
ctx.strokeStyle = '#000';
|
||||||
|
ctx.font = '22px Verdana';
|
||||||
|
ctx.lineWidth = 3;
|
||||||
|
this.popList.forEach(v => {
|
||||||
|
const { cx, cy, path, text, color, time, start } = v;
|
||||||
|
const dt = now - start;
|
||||||
|
const progress = dt / time;
|
||||||
|
if (progress >= 1) {
|
||||||
|
toDelete.add(v);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const [x, y] = path(progress);
|
||||||
|
const dx = cx + x;
|
||||||
|
const dy = cy + y;
|
||||||
|
ctx.globalAlpha = Math.min(1, 2 - progress * 2);
|
||||||
|
ctx.fillStyle = color;
|
||||||
|
ctx.strokeText(text, dx, dy);
|
||||||
|
ctx.fillText(text, dx, dy);
|
||||||
|
});
|
||||||
|
toDelete.forEach(v => {
|
||||||
|
this.popList.delete(v);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy(): void {
|
||||||
|
super.destroy();
|
||||||
|
this.removeTicker(this.delegation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PopText = wrapInstancedComponent(() => new Pop('static'));
|
||||||
296
packages-user/client-modules/src/render/legacy/portal.ts
Normal file
296
packages-user/client-modules/src/render/legacy/portal.ts
Normal file
@ -0,0 +1,296 @@
|
|||||||
|
import { logger } from '@motajs/common';
|
||||||
|
import { MotaOffscreenCanvas2D } from '@motajs/render';
|
||||||
|
import { mainSetting, MotaSettingItem } from '@motajs/legacy-ui';
|
||||||
|
import { Sprite } from '@motajs/render';
|
||||||
|
import { BluePalace } from '@user/data-state';
|
||||||
|
import {
|
||||||
|
ILayerGroupRenderExtends,
|
||||||
|
LayerGroup,
|
||||||
|
LayerGroupFloorBinder
|
||||||
|
} from '../elements';
|
||||||
|
|
||||||
|
/** 最大粒子数 */
|
||||||
|
const MAX_PARTICLES = 10;
|
||||||
|
/** 粒子持续时长 */
|
||||||
|
const PARTICLE_LAST = 2000;
|
||||||
|
/** 粒子产生间隔 */
|
||||||
|
const PARTICLE_INTERVAL = PARTICLE_LAST / MAX_PARTICLES;
|
||||||
|
|
||||||
|
export class LayerGroupPortal implements ILayerGroupRenderExtends {
|
||||||
|
id: string = 'portal';
|
||||||
|
|
||||||
|
group!: LayerGroup;
|
||||||
|
binder!: LayerGroupFloorBinder;
|
||||||
|
portal!: Portal;
|
||||||
|
|
||||||
|
private onFloorChange = (floor: FloorIds) => {
|
||||||
|
const data = BluePalace.portals;
|
||||||
|
this.portal.cellSize = this.group.cellSize;
|
||||||
|
this.portal.setData(data[floor] ?? []);
|
||||||
|
};
|
||||||
|
|
||||||
|
private listen() {
|
||||||
|
this.binder.on('floorChange', this.onFloorChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
awake(group: LayerGroup): void {
|
||||||
|
this.group = group;
|
||||||
|
const ex = group.getExtends('floor-binder');
|
||||||
|
if (ex instanceof LayerGroupFloorBinder) {
|
||||||
|
this.binder = ex;
|
||||||
|
this.portal = new Portal();
|
||||||
|
this.portal.setHD(true);
|
||||||
|
this.portal.size(group.width, group.height);
|
||||||
|
group.appendChild(this.portal);
|
||||||
|
this.listen();
|
||||||
|
} else {
|
||||||
|
logger.error(1301);
|
||||||
|
group.removeExtends('portal');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onDestroy(_group: LayerGroup): void {
|
||||||
|
this.binder.off('floorChange', this.onFloorChange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DrawingPortal {
|
||||||
|
color: string;
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
particles: Set<PortalParticle>;
|
||||||
|
/** v表示竖向,h表示横向 */
|
||||||
|
type: 'v' | 'h';
|
||||||
|
/** 上一次新增粒子的时间 */
|
||||||
|
lastParticle: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PortalParticle {
|
||||||
|
fx: number;
|
||||||
|
fy: number;
|
||||||
|
totalTime: number;
|
||||||
|
time: number;
|
||||||
|
tx: number;
|
||||||
|
ty: number;
|
||||||
|
r: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Portal extends Sprite {
|
||||||
|
static colors: string[] = ['#0f0', '#ff0', '#0ff', '#fff', '#f0f'];
|
||||||
|
|
||||||
|
cellSize: number = 32;
|
||||||
|
/** 当前的渲染数据 */
|
||||||
|
private renderData: BluePalace.Portal[] = [];
|
||||||
|
/** 渲染内容 */
|
||||||
|
private renderable: Set<DrawingPortal> = new Set();
|
||||||
|
|
||||||
|
/** 粒子开关设置 */
|
||||||
|
private particleSetting: MotaSettingItem;
|
||||||
|
/** 上一帧时刻 */
|
||||||
|
private lastTime: number = 0;
|
||||||
|
|
||||||
|
private delegation: number;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('static', false, true);
|
||||||
|
|
||||||
|
this.particleSetting = mainSetting.getSetting('fx.portalParticle')!;
|
||||||
|
|
||||||
|
this.delegation = this.delegateTicker(() => {
|
||||||
|
if (this.particleSetting.value && this.renderable.size > 0) {
|
||||||
|
this.update(this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setZIndex(35);
|
||||||
|
|
||||||
|
this.setRenderFn((canvas, _transform) => {
|
||||||
|
this.renderPortal(canvas);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置渲染内容
|
||||||
|
*/
|
||||||
|
setData(data: BluePalace.Portal[]) {
|
||||||
|
this.renderData = data;
|
||||||
|
this.generateRenderable();
|
||||||
|
this.update(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private generateRenderable() {
|
||||||
|
this.renderable.clear();
|
||||||
|
if (this.renderData.length === 0) return;
|
||||||
|
const colorLength = Portal.colors.length;
|
||||||
|
const cell = this.cellSize;
|
||||||
|
this.renderData.forEach((v, i) => {
|
||||||
|
const c = Portal.colors[i % colorLength];
|
||||||
|
const { fx, fy, tx, ty, dir, toDir } = v;
|
||||||
|
|
||||||
|
let x1 = fx * cell;
|
||||||
|
let y1 = fy * cell;
|
||||||
|
let x2 = tx * cell;
|
||||||
|
let y2 = ty * cell;
|
||||||
|
|
||||||
|
if (dir === 'down') y1 += cell;
|
||||||
|
else if (dir === 'right') x1 += cell;
|
||||||
|
|
||||||
|
if (toDir === 'down') y2 += cell;
|
||||||
|
else if (toDir === 'right') x2 += cell;
|
||||||
|
|
||||||
|
this.renderable.add({
|
||||||
|
x: x1,
|
||||||
|
y: y1,
|
||||||
|
type: dir === 'left' || dir === 'right' ? 'v' : 'h',
|
||||||
|
color: c,
|
||||||
|
particles: new Set(),
|
||||||
|
lastParticle: Date.now()
|
||||||
|
});
|
||||||
|
|
||||||
|
this.renderable.add({
|
||||||
|
x: x2,
|
||||||
|
y: y2,
|
||||||
|
type: toDir === 'left' || toDir === 'right' ? 'v' : 'h',
|
||||||
|
color: c,
|
||||||
|
particles: new Set(),
|
||||||
|
lastParticle: Date.now()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderPortal(canvas: MotaOffscreenCanvas2D) {
|
||||||
|
if (this.renderable.size === 0) return;
|
||||||
|
const { ctx } = canvas;
|
||||||
|
|
||||||
|
const p = this.particleSetting.value;
|
||||||
|
ctx.lineCap = 'round';
|
||||||
|
ctx.lineWidth = 3;
|
||||||
|
ctx.shadowOffsetX = 0;
|
||||||
|
ctx.shadowOffsetY = 0;
|
||||||
|
if (p) {
|
||||||
|
ctx.shadowBlur = 8;
|
||||||
|
} else {
|
||||||
|
ctx.shadowBlur = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const time = Date.now();
|
||||||
|
const dt = time - this.lastTime;
|
||||||
|
this.lastTime = time;
|
||||||
|
this.renderable.forEach(v => {
|
||||||
|
const { color, x, y, type, lastParticle, particles } = v;
|
||||||
|
|
||||||
|
ctx.strokeStyle = color;
|
||||||
|
ctx.fillStyle = color;
|
||||||
|
ctx.globalAlpha = 1;
|
||||||
|
ctx.shadowColor = color;
|
||||||
|
if (type === 'v') {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x, y - 14);
|
||||||
|
ctx.lineTo(x, y + 30);
|
||||||
|
ctx.stroke();
|
||||||
|
} else {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x + 2, y);
|
||||||
|
ctx.lineTo(x + 30, y);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!p) return;
|
||||||
|
|
||||||
|
const needDelete = new Set<PortalParticle>();
|
||||||
|
particles.forEach(v => {
|
||||||
|
const { fx, fy, tx, ty, time: t, totalTime, r } = v;
|
||||||
|
const progress = t / totalTime;
|
||||||
|
const nx = (tx - fx) * progress + fx;
|
||||||
|
const ny = (ty - fy) * progress + fy;
|
||||||
|
v.time += dt;
|
||||||
|
|
||||||
|
if (progress > 1) {
|
||||||
|
needDelete.add(v);
|
||||||
|
return;
|
||||||
|
} else if (progress > 0.75) {
|
||||||
|
ctx.globalAlpha = (1 - progress) * 4;
|
||||||
|
} else if (progress < 0.25) {
|
||||||
|
ctx.globalAlpha = progress * 4;
|
||||||
|
} else {
|
||||||
|
ctx.globalAlpha = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(nx, ny, r, 0, Math.PI * 2);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.fill();
|
||||||
|
});
|
||||||
|
needDelete.forEach(v => {
|
||||||
|
particles.delete(v);
|
||||||
|
});
|
||||||
|
if (
|
||||||
|
time - lastParticle >= PARTICLE_INTERVAL &&
|
||||||
|
particles.size < MAX_PARTICLES
|
||||||
|
) {
|
||||||
|
// 添加新粒子
|
||||||
|
const direction = Math.random();
|
||||||
|
const k = Math.random() / 2 - 0.3;
|
||||||
|
const verticle = Math.floor(Math.random() * 8 + 8);
|
||||||
|
const r = Math.random() * 2;
|
||||||
|
v.lastParticle = time;
|
||||||
|
if (direction > 0.5) {
|
||||||
|
// 左边 | 上边
|
||||||
|
if (type === 'h') {
|
||||||
|
const fx = Math.floor(Math.random() * 24 + x + 4);
|
||||||
|
particles.add({
|
||||||
|
fx: fx,
|
||||||
|
fy: y - 1,
|
||||||
|
tx: verticle * k + fx + 4,
|
||||||
|
ty: -verticle + y - 1,
|
||||||
|
r: r,
|
||||||
|
time: 0,
|
||||||
|
totalTime: PARTICLE_LAST
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const fy = Math.floor(Math.random() * 44 + y - 14);
|
||||||
|
particles.add({
|
||||||
|
fy: fy,
|
||||||
|
fx: x - 1,
|
||||||
|
ty: verticle * k + fy + 4,
|
||||||
|
tx: -verticle + x - 1,
|
||||||
|
r: r,
|
||||||
|
time: 0,
|
||||||
|
totalTime: PARTICLE_LAST
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 右边 | 下边
|
||||||
|
if (type === 'h') {
|
||||||
|
const fx = Math.floor(Math.random() * 24 + x + 4);
|
||||||
|
particles.add({
|
||||||
|
fx: fx,
|
||||||
|
fy: y + 1,
|
||||||
|
tx: verticle * k + fx + 4,
|
||||||
|
ty: verticle + y - 1,
|
||||||
|
r: r,
|
||||||
|
time: 0,
|
||||||
|
totalTime: PARTICLE_LAST
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const fy = Math.floor(Math.random() * 44 + y - 14);
|
||||||
|
particles.add({
|
||||||
|
fy: fy,
|
||||||
|
fx: x + 1,
|
||||||
|
ty: verticle * k + fy + 4,
|
||||||
|
tx: verticle + x + 1,
|
||||||
|
r: r,
|
||||||
|
time: 0,
|
||||||
|
totalTime: PARTICLE_LAST
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy(): void {
|
||||||
|
super.destroy();
|
||||||
|
this.removeTicker(this.delegation);
|
||||||
|
}
|
||||||
|
}
|
||||||
1433
packages-user/client-modules/src/render/legacy/shadow.ts
Normal file
1433
packages-user/client-modules/src/render/legacy/shadow.ts
Normal file
File diff suppressed because it is too large
Load Diff
108
packages-user/client-modules/src/render/loopMap.ts
Normal file
108
packages-user/client-modules/src/render/loopMap.ts
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
import { Container, MotaRenderer } from '@motajs/render';
|
||||||
|
import { hook } from '@user/data-base';
|
||||||
|
import { MiscData } from '@user/data-state';
|
||||||
|
import { FloorDamageExtends } from './elements/damage';
|
||||||
|
import { FloorItemDetail } from './elements/itemDetail';
|
||||||
|
import {
|
||||||
|
LayerGroup,
|
||||||
|
FloorLayer,
|
||||||
|
LayerGroupFloorBinder,
|
||||||
|
FloorViewport
|
||||||
|
} from './elements';
|
||||||
|
import { MAP_WIDTH } from './shared';
|
||||||
|
|
||||||
|
const loopMaps = MiscData.loopMaps;
|
||||||
|
|
||||||
|
let loopLayer: LayerGroup;
|
||||||
|
let show: boolean = false;
|
||||||
|
/** 循环式地图中,更新视角的委托ticker */
|
||||||
|
let delegation: number = -1;
|
||||||
|
|
||||||
|
export function createLoopMap() {
|
||||||
|
hook.on('changingFloor', (floorId, _heroLoc) => {
|
||||||
|
enableLoopMapElement(floorId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createLayer() {
|
||||||
|
const group = new LayerGroup();
|
||||||
|
['bg', 'bg2', 'event', 'fg', 'fg2'].forEach(v => {
|
||||||
|
group.addLayer(v as FloorLayer);
|
||||||
|
});
|
||||||
|
|
||||||
|
const damage = new FloorDamageExtends();
|
||||||
|
const detail = new FloorItemDetail();
|
||||||
|
group.id = 'layer-loop';
|
||||||
|
group.extends(damage);
|
||||||
|
group.extends(detail);
|
||||||
|
|
||||||
|
loopLayer = group;
|
||||||
|
group.setZIndex(20);
|
||||||
|
}
|
||||||
|
|
||||||
|
function enableLoopMapElement(floorId: FloorIds) {
|
||||||
|
if (!loopMaps.has(floorId)) {
|
||||||
|
disableLoopMapElement();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!loopLayer) createLayer();
|
||||||
|
const render = MotaRenderer.get('render-main');
|
||||||
|
const draw = render?.getElementById('map-draw') as Container;
|
||||||
|
const group = render?.getElementById('layer-main') as LayerGroup;
|
||||||
|
if (!draw || !group) return;
|
||||||
|
const ex = loopLayer.getExtends('floor-binder') as LayerGroupFloorBinder;
|
||||||
|
const viewport = group.getExtends('viewport') as FloorViewport;
|
||||||
|
if (!ex || !viewport) return;
|
||||||
|
ex.bindFloor(floorId);
|
||||||
|
|
||||||
|
draw.appendChild(loopLayer);
|
||||||
|
show = true;
|
||||||
|
|
||||||
|
const floor = core.status.maps[floorId];
|
||||||
|
viewport.setAutoBound(false);
|
||||||
|
const transform = group.camera;
|
||||||
|
const width = floor.width;
|
||||||
|
const testPos = width * loopLayer.cellSize;
|
||||||
|
|
||||||
|
loopLayer.removeTicker(delegation);
|
||||||
|
delegation = loopLayer.delegateTicker(() => {
|
||||||
|
const [x1] = transform.transformed(0, 0);
|
||||||
|
const camera = loopLayer.camera;
|
||||||
|
if (x1 > 0) {
|
||||||
|
// 这个是计算循环地图应该显示在哪
|
||||||
|
const [, y2] = transform.transformed(x1 - testPos, 0);
|
||||||
|
camera.reset();
|
||||||
|
camera.translate(MAP_WIDTH - testPos, y2);
|
||||||
|
loopLayer.pos(transform.x - MAP_WIDTH, 0);
|
||||||
|
loopLayer.show();
|
||||||
|
loopLayer.update(loopLayer);
|
||||||
|
} else {
|
||||||
|
const [x2, y2] = transform.transformed(testPos, 0);
|
||||||
|
if (x2 < MAP_WIDTH) {
|
||||||
|
// 这个不用做其他运算,可以直接显示
|
||||||
|
camera.reset();
|
||||||
|
camera.translate(0, y2);
|
||||||
|
loopLayer.pos(x2, 0);
|
||||||
|
loopLayer.show();
|
||||||
|
loopLayer.update(loopLayer);
|
||||||
|
} else {
|
||||||
|
loopLayer.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function disableLoopMapElement() {
|
||||||
|
if (!show) return;
|
||||||
|
show = false;
|
||||||
|
loopLayer.remove();
|
||||||
|
|
||||||
|
const render = MotaRenderer.get('render-main');
|
||||||
|
const group = render?.getElementById('layer-main') as LayerGroup;
|
||||||
|
if (!group) return;
|
||||||
|
const viewport = group.getExtends('viewport') as FloorViewport;
|
||||||
|
if (!viewport) return;
|
||||||
|
|
||||||
|
viewport.setAutoBound(true);
|
||||||
|
loopLayer.removeTicker(delegation);
|
||||||
|
}
|
||||||
@ -62,7 +62,7 @@ export const CENTER_LOC: ElementLocator = [
|
|||||||
/** 弹框的宽度,使用在内置 UI 与组件中,包括确认框、选择框、等待框等 */
|
/** 弹框的宽度,使用在内置 UI 与组件中,包括确认框、选择框、等待框等 */
|
||||||
export const POP_BOX_WIDTH = MAP_WIDTH / 2;
|
export const POP_BOX_WIDTH = MAP_WIDTH / 2;
|
||||||
/** 默认字体 */
|
/** 默认字体 */
|
||||||
export const DEFAULT_FONT = new Font('Verdana', 16);
|
export const DEFAULT_FONT = new Font('normal', 18);
|
||||||
|
|
||||||
//#region 存档界面
|
//#region 存档界面
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { LayerShadowExtends } from '../legacy/shadow';
|
||||||
import {
|
import {
|
||||||
Props,
|
Props,
|
||||||
Font,
|
Font,
|
||||||
@ -38,8 +39,11 @@ import { ReplayingStatus } from './toolbar';
|
|||||||
import { getHeroStatusOn } from '@user/data-state';
|
import { getHeroStatusOn } from '@user/data-state';
|
||||||
import { hook } from '@user/data-base';
|
import { hook } from '@user/data-base';
|
||||||
import { FloorDamageExtends, FloorItemDetail } from '../elements';
|
import { FloorDamageExtends, FloorItemDetail } from '../elements';
|
||||||
|
import { LayerGroupPortal } from '../legacy/portal';
|
||||||
|
import { LayerGroupFilter } from '../legacy/gameCanvas';
|
||||||
import { LayerGroupHalo } from '../legacy/halo';
|
import { LayerGroupHalo } from '../legacy/halo';
|
||||||
import { FloorChange } from '../legacy/fallback';
|
import { FloorChange } from '../legacy/fallback';
|
||||||
|
import { PopText } from '../legacy/pop';
|
||||||
import { mainUIController } from './controller';
|
import { mainUIController } from './controller';
|
||||||
import {
|
import {
|
||||||
ILayerGroupRenderExtends,
|
ILayerGroupRenderExtends,
|
||||||
@ -57,13 +61,16 @@ const MainScene = defineComponent(() => {
|
|||||||
const layerGroupExtends: ILayerGroupRenderExtends[] = [
|
const layerGroupExtends: ILayerGroupRenderExtends[] = [
|
||||||
new FloorDamageExtends(),
|
new FloorDamageExtends(),
|
||||||
new FloorItemDetail(),
|
new FloorItemDetail(),
|
||||||
|
new LayerGroupFilter(),
|
||||||
|
new LayerGroupPortal(),
|
||||||
new LayerGroupHalo(),
|
new LayerGroupHalo(),
|
||||||
new LayerGroupAnimate(),
|
new LayerGroupAnimate(),
|
||||||
new FloorViewport()
|
new FloorViewport()
|
||||||
];
|
];
|
||||||
const eventExtends: ILayerRenderExtends[] = [
|
const eventExtends: ILayerRenderExtends[] = [
|
||||||
new HeroRenderer(),
|
new HeroRenderer(),
|
||||||
new LayerDoorAnimate()
|
new LayerDoorAnimate(),
|
||||||
|
new LayerShadowExtends()
|
||||||
];
|
];
|
||||||
const mainTextboxProps: Props<typeof Textbox> = {
|
const mainTextboxProps: Props<typeof Textbox> = {
|
||||||
text: '',
|
text: '',
|
||||||
@ -74,7 +81,7 @@ const MainScene = defineComponent(() => {
|
|||||||
titleFill: 'gold',
|
titleFill: 'gold',
|
||||||
font: new Font('normal'),
|
font: new Font('normal'),
|
||||||
titleFont: new Font('normal', 20, 'px', 700),
|
titleFont: new Font('normal', 20, 'px', 700),
|
||||||
winskin: 'winskin.png',
|
winskin: 'winskin2.png',
|
||||||
interval: 30,
|
interval: 30,
|
||||||
lineHeight: 4,
|
lineHeight: 4,
|
||||||
width: MAP_WIDTH
|
width: MAP_WIDTH
|
||||||
@ -276,6 +283,7 @@ const MainScene = defineComponent(() => {
|
|||||||
<layer layer="event" zIndex={30} ex={eventExtends}></layer>
|
<layer layer="event" zIndex={30} ex={eventExtends}></layer>
|
||||||
<layer layer="fg" zIndex={40}></layer>
|
<layer layer="fg" zIndex={40}></layer>
|
||||||
<layer layer="fg2" zIndex={50}></layer>
|
<layer layer="fg2" zIndex={50}></layer>
|
||||||
|
<PopText id="pop-main" zIndex={80}></PopText>
|
||||||
</layer-group>
|
</layer-group>
|
||||||
<Textbox id="main-textbox" {...mainTextboxProps}></Textbox>
|
<Textbox id="main-textbox" {...mainTextboxProps}></Textbox>
|
||||||
<FloorChange id="floor-change" zIndex={50}></FloorChange>
|
<FloorChange id="floor-change" zIndex={50}></FloorChange>
|
||||||
|
|||||||
@ -69,8 +69,8 @@ const saveBtnProps = {
|
|||||||
} satisfies SetupComponentOptions<SaveItemProps>;
|
} satisfies SetupComponentOptions<SaveItemProps>;
|
||||||
|
|
||||||
export const SaveItem = defineComponent<SaveItemProps>(props => {
|
export const SaveItem = defineComponent<SaveItemProps>(props => {
|
||||||
const font = Font.defaults({ size: 16 });
|
const font = Font.defaults({ size: 18 });
|
||||||
const statusFont = Font.defaults({ size: 12 });
|
const statusFont = Font.defaults({ size: 14 });
|
||||||
|
|
||||||
const w = computed(() => props.loc[2] ?? 200);
|
const w = computed(() => props.loc[2] ?? 200);
|
||||||
const h = computed(() => props.loc[3] ?? 200);
|
const h = computed(() => props.loc[3] ?? 200);
|
||||||
@ -267,7 +267,7 @@ export const Save = defineComponent<SaveProps, SaveEmits, keyof SaveEmits>(
|
|||||||
`确认要删除存档 ${index + 1}?`,
|
`确认要删除存档 ${index + 1}?`,
|
||||||
[HALF_WIDTH, HALF_HEIGHT, void 0, void 0, 0.5, 0.5],
|
[HALF_WIDTH, HALF_HEIGHT, void 0, void 0, 0.5, 0.5],
|
||||||
POP_BOX_WIDTH,
|
POP_BOX_WIDTH,
|
||||||
{ winskin: 'winskin.png' }
|
{ winskin: 'winskin2.png' }
|
||||||
);
|
);
|
||||||
if (confirm) {
|
if (confirm) {
|
||||||
emit('delete', index, exist(posIndex));
|
emit('delete', index, exist(posIndex));
|
||||||
|
|||||||
@ -246,7 +246,6 @@ export const ReplaySettings = defineComponent<MainSettingsProps>(props => {
|
|||||||
onChoose={choose}
|
onChoose={choose}
|
||||||
interval={8}
|
interval={8}
|
||||||
scope={scope}
|
scope={scope}
|
||||||
maxHeight={MAIN_HEIGHT - 32}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}, mainSettingsProps);
|
}, mainSettingsProps);
|
||||||
|
|||||||
@ -72,7 +72,7 @@ const gameTitleProps = {
|
|||||||
} satisfies SetupComponentOptions<GameTitleProps>;
|
} satisfies SetupComponentOptions<GameTitleProps>;
|
||||||
|
|
||||||
export const GameTitle = defineComponent<GameTitleProps>(props => {
|
export const GameTitle = defineComponent<GameTitleProps>(props => {
|
||||||
const bg = core.material.images.images['bg.jpg'];
|
const bg = core.material.images.images['bg.webp'];
|
||||||
|
|
||||||
//#region 计算背景图
|
//#region 计算背景图
|
||||||
const [width, height] = adjustCover(
|
const [width, height] = adjustCover(
|
||||||
@ -437,6 +437,7 @@ export const GameTitle = defineComponent<GameTitleProps>(props => {
|
|||||||
image={bg}
|
image={bg}
|
||||||
loc={[HALF_WIDTH, HALF_HEIGHT, width, height]}
|
loc={[HALF_WIDTH, HALF_HEIGHT, width, height]}
|
||||||
anc={[0.5, 0.5]}
|
anc={[0.5, 0.5]}
|
||||||
|
filter="brightness(120%)contrast(110%)"
|
||||||
zIndex={0}
|
zIndex={0}
|
||||||
/>
|
/>
|
||||||
<shader
|
<shader
|
||||||
|
|||||||
@ -32,6 +32,7 @@ import {
|
|||||||
LayerGroupFloorBinder
|
LayerGroupFloorBinder
|
||||||
} from '../elements';
|
} from '../elements';
|
||||||
import { LayerGroupHalo } from '../legacy/halo';
|
import { LayerGroupHalo } from '../legacy/halo';
|
||||||
|
import { LayerGroupPortal } from '../legacy/portal';
|
||||||
import { Font } from '@motajs/render-style';
|
import { Font } from '@motajs/render-style';
|
||||||
import { clamp, mean } from 'lodash-es';
|
import { clamp, mean } from 'lodash-es';
|
||||||
import { calculateStatisticsOne, StatisticsDataOneFloor } from './statistics';
|
import { calculateStatisticsOne, StatisticsDataOneFloor } from './statistics';
|
||||||
@ -65,6 +66,7 @@ export const ViewMap = defineComponent<ViewMapProps>(props => {
|
|||||||
const layerGroupExtends: ILayerGroupRenderExtends[] = [
|
const layerGroupExtends: ILayerGroupRenderExtends[] = [
|
||||||
new FloorDamageExtends(),
|
new FloorDamageExtends(),
|
||||||
new FloorItemDetail(),
|
new FloorItemDetail(),
|
||||||
|
new LayerGroupPortal(),
|
||||||
new LayerGroupHalo(),
|
new LayerGroupHalo(),
|
||||||
new LayerGroupAnimate()
|
new LayerGroupAnimate()
|
||||||
];
|
];
|
||||||
@ -140,7 +142,6 @@ export const ViewMap = defineComponent<ViewMapProps>(props => {
|
|||||||
const openBook = () => core.openBook(true);
|
const openBook = () => core.openBook(true);
|
||||||
|
|
||||||
const fly = () => {
|
const fly = () => {
|
||||||
if (!core.hasItem('fly')) return;
|
|
||||||
const id = viewableFloor[now.value];
|
const id = viewableFloor[now.value];
|
||||||
const success = core.flyTo(id);
|
const success = core.flyTo(id);
|
||||||
if (success) close();
|
if (success) close();
|
||||||
|
|||||||
@ -1,4 +1,10 @@
|
|||||||
import { DamageEnemy, ensureFloorDamage, getEnemy } from '@user/data-state';
|
import {
|
||||||
|
DamageEnemy,
|
||||||
|
ensureFloorDamage,
|
||||||
|
getEnemy,
|
||||||
|
HeroSkill,
|
||||||
|
NightSpecial
|
||||||
|
} from '@user/data-state';
|
||||||
import { hook } from '@user/data-base';
|
import { hook } from '@user/data-base';
|
||||||
import { Patch, PatchClass } from '@motajs/legacy-common';
|
import { Patch, PatchClass } from '@motajs/legacy-common';
|
||||||
import { isNil } from 'lodash-es';
|
import { isNil } from 'lodash-es';
|
||||||
@ -144,6 +150,9 @@ export function patchBattle() {
|
|||||||
patch2.add(
|
patch2.add(
|
||||||
'afterBattle',
|
'afterBattle',
|
||||||
function (enemy: DamageEnemy, x?: number, y?: number) {
|
function (enemy: DamageEnemy, x?: number, y?: number) {
|
||||||
|
const floorId = core.status.floorId;
|
||||||
|
const special = enemy.info.special;
|
||||||
|
|
||||||
// 播放战斗动画
|
// 播放战斗动画
|
||||||
let animate: AnimationIds = 'hand';
|
let animate: AnimationIds = 'hand';
|
||||||
// 检查当前装备是否存在攻击动画
|
// 检查当前装备是否存在攻击动画
|
||||||
@ -171,6 +180,30 @@ export function patchBattle() {
|
|||||||
core.status.hero.statistics.battleDamage += damage;
|
core.status.hero.statistics.battleDamage += damage;
|
||||||
core.status.hero.statistics.battle++;
|
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!;
|
const money = enemy.info.money!;
|
||||||
core.status.hero.money += money;
|
core.status.hero.money += money;
|
||||||
@ -190,6 +223,8 @@ export function patchBattle() {
|
|||||||
exp;
|
exp;
|
||||||
core.drawTip(hint, enemy.id);
|
core.drawTip(hint, enemy.id);
|
||||||
|
|
||||||
|
HeroSkill.disableSkill();
|
||||||
|
|
||||||
// 事件的处理
|
// 事件的处理
|
||||||
const todo: MotaEvent = [];
|
const todo: MotaEvent = [];
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +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';
|
||||||
import {
|
import {
|
||||||
EnemyInfo,
|
EnemyInfo,
|
||||||
DamageInfo,
|
DamageInfo,
|
||||||
@ -284,6 +285,11 @@ export class DamageEnemy implements IDamageEnemy {
|
|||||||
info.atk += flags[`inte_${floorId}`] ?? 0;
|
info.atk += flags[`inte_${floorId}`] ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 极昼永夜
|
||||||
|
const night = NightSpecial.getNight(floorId);
|
||||||
|
info.atk -= night;
|
||||||
|
info.def -= night;
|
||||||
|
|
||||||
// 融化,融化不属于怪物光环,因此不能用provide和inject计算,需要在这里计算
|
// 融化,融化不属于怪物光环,因此不能用provide和inject计算,需要在这里计算
|
||||||
const melt = flags[`melt_${floorId}`];
|
const melt = flags[`melt_${floorId}`];
|
||||||
if (!isNil(melt) && !isNil(this.x) && !isNil(this.y)) {
|
if (!isNil(melt) && !isNil(this.x) && !isNil(this.y)) {
|
||||||
@ -714,9 +720,27 @@ export class DamageEnemy implements IDamageEnemy {
|
|||||||
|
|
||||||
private calEnemyDamageOf(hero: Partial<HeroStatus>, enemy: UserEnemyInfo) {
|
private calEnemyDamageOf(hero: Partial<HeroStatus>, enemy: UserEnemyInfo) {
|
||||||
const status = getHeroStatusOf(hero, realStatus, this.floorId);
|
const status = getHeroStatusOf(hero, realStatus, this.floorId);
|
||||||
const damage = calDamageWith(enemy, status) ?? Infinity;
|
let damage = calDamageWith(enemy, status) ?? Infinity;
|
||||||
|
let bestSkill = -1;
|
||||||
|
|
||||||
return { damage };
|
// 自动切换技能
|
||||||
|
if (HeroSkill.getAutoSkill()) {
|
||||||
|
for (const skill of skills) {
|
||||||
|
if (!HeroSkill.learnedSkill(skill)) continue;
|
||||||
|
HeroSkill.enableSkill(skill);
|
||||||
|
const status = getHeroStatusOf(hero, realStatus);
|
||||||
|
|
||||||
|
const d = calDamageWith(enemy, status) ?? Infinity;
|
||||||
|
|
||||||
|
if (d < damage) {
|
||||||
|
damage = d;
|
||||||
|
bestSkill = skill;
|
||||||
|
}
|
||||||
|
HeroSkill.disableSkill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { damage, skill: bestSkill };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -904,6 +928,10 @@ const realStatus: (keyof HeroStatus)[] = [
|
|||||||
'mana',
|
'mana',
|
||||||
'magicDef'
|
'magicDef'
|
||||||
];
|
];
|
||||||
|
/**
|
||||||
|
* 主动技能列表
|
||||||
|
*/
|
||||||
|
const skills: HeroSkill.Skill[] = [HeroSkill.Blade, HeroSkill.Shield];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算怪物伤害
|
* 计算怪物伤害
|
||||||
|
|||||||
@ -1,2 +1,9 @@
|
|||||||
|
import { createMechanism } from './mechanism';
|
||||||
|
|
||||||
|
export function create() {
|
||||||
|
createMechanism();
|
||||||
|
}
|
||||||
|
|
||||||
export * from './enemy';
|
export * from './enemy';
|
||||||
|
export * from './mechanism';
|
||||||
export * from './state';
|
export * from './state';
|
||||||
|
|||||||
4
packages-user/data-state/src/mechanism/index.ts
Normal file
4
packages-user/data-state/src/mechanism/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export function createMechanism() {}
|
||||||
|
|
||||||
|
export * from './misc';
|
||||||
|
export * from './skillTree';
|
||||||
276
packages-user/data-state/src/mechanism/misc.ts
Normal file
276
packages-user/data-state/src/mechanism/misc.ts
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
import { backDir, has } from '@user/data-utils';
|
||||||
|
import { loading } from '@user/data-base';
|
||||||
|
import type { LayerDoorAnimate } from '@user/client-modules';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 一些零散机制的数据
|
||||||
|
*/
|
||||||
|
export namespace MiscData {
|
||||||
|
/** 循环式地图 */
|
||||||
|
export const loopMaps: Set<FloorIds> = new Set(['tower6']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 永夜/极昼
|
||||||
|
*/
|
||||||
|
export namespace NightSpecial {
|
||||||
|
const nightMap = new Map<FloorIds, number>();
|
||||||
|
|
||||||
|
export function getNight(floor: FloorIds) {
|
||||||
|
return nightMap.get(floor) ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addNight(floor: FloorIds, value: number) {
|
||||||
|
const num = nightMap.get(floor) ?? 0;
|
||||||
|
nightMap.set(floor, num + value);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clearNight(floors: Iterable<FloorIds>) {
|
||||||
|
for (const floor of floors) {
|
||||||
|
nightMap.delete(floor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function saveNight() {
|
||||||
|
return nightMap.entries();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function loadNight(night: Iterable<[FloorIds, number]>) {
|
||||||
|
nightMap.clear();
|
||||||
|
for (const [floor, num] of night) {
|
||||||
|
nightMap.set(floor, num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAll() {
|
||||||
|
return nightMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace BluePalace {
|
||||||
|
type DoorConvertInfo = [id: AllIds, x: number, y: number];
|
||||||
|
|
||||||
|
// ---------- 黄蓝门转换
|
||||||
|
|
||||||
|
export function doorConvert(
|
||||||
|
x?: number,
|
||||||
|
y?: number,
|
||||||
|
floorId: FloorIds = core.status.floorId
|
||||||
|
) {
|
||||||
|
core.autosave();
|
||||||
|
core.extractBlocks(floorId);
|
||||||
|
const blocks = core.status.maps[floorId].blocks;
|
||||||
|
|
||||||
|
const toConvert: DoorConvertInfo[] = [];
|
||||||
|
blocks.forEach(v => {
|
||||||
|
if (v.id === 492) {
|
||||||
|
core.setBlock(494, v.x, v.y, floorId);
|
||||||
|
toConvert.push(['A492', v.x, v.y]);
|
||||||
|
} else if (v.id === 494) {
|
||||||
|
core.setBlock(492, v.x, v.y, floorId);
|
||||||
|
toConvert.push(['A494', v.x, v.y]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (has(x) && has(y)) {
|
||||||
|
core.removeBlock(x, y, floorId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (core.isReplaying() || core.status.floorId !== floorId) {
|
||||||
|
core.doAction();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
core.lockControl();
|
||||||
|
core.playSound('door.opus');
|
||||||
|
|
||||||
|
const Adapter = Mota.require('@motajs/render').RenderAdapter;
|
||||||
|
const adapter = Adapter.get<LayerDoorAnimate>('door-animate');
|
||||||
|
const texture = Mota.require('@user/client-modules').texture;
|
||||||
|
if (adapter) {
|
||||||
|
Promise.all(
|
||||||
|
toConvert.map(v => {
|
||||||
|
const block = core.initBlock(
|
||||||
|
v[1],
|
||||||
|
v[2],
|
||||||
|
texture.idNumberMap[v[0]]
|
||||||
|
);
|
||||||
|
return adapter.all('openDoor', block);
|
||||||
|
})
|
||||||
|
).then(() => {
|
||||||
|
core.unlockControl();
|
||||||
|
core.doAction();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- 传送门部分
|
||||||
|
|
||||||
|
export interface Portal {
|
||||||
|
fx: number;
|
||||||
|
fy: number;
|
||||||
|
dir: Dir;
|
||||||
|
tx: number;
|
||||||
|
ty: number;
|
||||||
|
toDir: Dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PortalTo {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
dir: Dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
type PortalMap = Map<FloorIds, Map<number, Partial<Record<Dir, PortalTo>>>>;
|
||||||
|
|
||||||
|
export const portalMap: PortalMap = new Map();
|
||||||
|
|
||||||
|
export const portals: Partial<Record<FloorIds, Portal[]>> = {
|
||||||
|
MT76: [
|
||||||
|
{ fx: 11, fy: 7, dir: 'right', tx: 4, ty: 6, toDir: 'down' },
|
||||||
|
{ fx: 6, fy: 5, dir: 'left', tx: 8, ty: 13, toDir: 'right' }
|
||||||
|
],
|
||||||
|
MT77: [
|
||||||
|
{ fx: 2, fy: 9, dir: 'up', tx: 10, ty: 13, toDir: 'right' },
|
||||||
|
{ fx: 10, fy: 8, dir: 'right', tx: 3, ty: 0, toDir: 'down' },
|
||||||
|
{ fx: 1, fy: 0, dir: 'down', tx: 8, ty: 1, toDir: 'left' }
|
||||||
|
],
|
||||||
|
MT78: [
|
||||||
|
{ fx: 8, fy: 4, dir: 'right', tx: 8, ty: 6, toDir: 'left' },
|
||||||
|
{ fx: 7, fy: 7, dir: 'up', tx: 1, ty: 0, toDir: 'down' }
|
||||||
|
],
|
||||||
|
MT79: [
|
||||||
|
{ fx: 5, fy: 10, dir: 'right', tx: 9, ty: 7, toDir: 'left' },
|
||||||
|
{ fx: 2, fy: 2, dir: 'up', tx: 7, ty: 5, toDir: 'down' },
|
||||||
|
{ fx: 4, fy: 11, dir: 'up', tx: 5, ty: 7, toDir: 'right' },
|
||||||
|
{ fx: 7, fy: 11, dir: 'down', tx: 7, ty: 9, toDir: 'up' }
|
||||||
|
],
|
||||||
|
MT80: [
|
||||||
|
{ fx: 2, fy: 10, dir: 'right', tx: 1, ty: 2, toDir: 'down' },
|
||||||
|
{ fx: 2, fy: 10, dir: 'left', tx: 13, ty: 5, toDir: 'up' }
|
||||||
|
],
|
||||||
|
MT81: [
|
||||||
|
{ fx: 4, fy: 8, dir: 'right', tx: 1, ty: 11, toDir: 'down' },
|
||||||
|
{ fx: 7, fy: 13, dir: 'right', tx: 13, ty: 5, toDir: 'up' }
|
||||||
|
],
|
||||||
|
MT82: [{ fx: 9, fy: 10, dir: 'left', tx: 6, ty: 5, toDir: 'left' }],
|
||||||
|
MT83: [
|
||||||
|
{ fx: 5, fy: 11, dir: 'left', tx: 9, ty: 11, toDir: 'right' },
|
||||||
|
{ fx: 5, fy: 3, dir: 'left', tx: 9, ty: 3, toDir: 'right' },
|
||||||
|
{ fx: 2, fy: 2, dir: 'up', tx: 2, ty: 12, toDir: 'down' },
|
||||||
|
{ fx: 12, fy: 2, dir: 'up', tx: 12, ty: 12, toDir: 'down' }
|
||||||
|
],
|
||||||
|
MT84: [
|
||||||
|
{ fx: 2, fy: 3, dir: 'right', tx: 12, ty: 3, toDir: 'left' },
|
||||||
|
{ fx: 2, fy: 11, dir: 'right', tx: 12, ty: 11, toDir: 'left' }
|
||||||
|
],
|
||||||
|
MT94: [{ fx: 12, fy: 11, dir: 'left', tx: 5, ty: 1, toDir: 'left' }],
|
||||||
|
MT95: [
|
||||||
|
{ fx: 13, fy: 14, dir: 'up', tx: 7, ty: 8, toDir: 'left' },
|
||||||
|
{ fx: 0, fy: 1, dir: 'right', tx: 14, ty: 1, toDir: 'left' },
|
||||||
|
{ fx: 6, fy: 13, dir: 'right', tx: 6, ty: 0, toDir: 'down' }
|
||||||
|
],
|
||||||
|
MT96: [{ fx: 6, fy: 11, dir: 'down', tx: 4, ty: 14, toDir: 'up' }],
|
||||||
|
MT97: [{ fx: 0, fy: 1, dir: 'right', tx: 8, ty: 9, toDir: 'right' }]
|
||||||
|
};
|
||||||
|
loading.once('coreInit', initPortals);
|
||||||
|
|
||||||
|
function generatePortalMap() {
|
||||||
|
const delta: Record<Dir, [[number, number], [number, number]]> = {
|
||||||
|
// 方向:[正向, 逆向]<进出>
|
||||||
|
left: [
|
||||||
|
[0, 0],
|
||||||
|
[-1, 0]
|
||||||
|
],
|
||||||
|
down: [
|
||||||
|
[0, 0],
|
||||||
|
[0, 1]
|
||||||
|
],
|
||||||
|
right: [
|
||||||
|
[0, 0],
|
||||||
|
[1, 0]
|
||||||
|
],
|
||||||
|
up: [
|
||||||
|
[0, 0],
|
||||||
|
[0, -1]
|
||||||
|
]
|
||||||
|
};
|
||||||
|
for (const [floor, p] of Object.entries(portals)) {
|
||||||
|
const width = core.floors[floor as FloorIds].width;
|
||||||
|
const map = new Map<number, Partial<Record<Dir, PortalTo>>>();
|
||||||
|
portalMap.set(floor as FloorIds, map);
|
||||||
|
|
||||||
|
// 正向映射
|
||||||
|
p.forEach(v => {
|
||||||
|
const [[fdx, fdy], [tdx, tdy]] = delta[v.dir];
|
||||||
|
const [[toFdx, toFdy], [toTdx, toTdy]] =
|
||||||
|
delta[backDir(v.toDir)];
|
||||||
|
const fx = v.fx + fdx;
|
||||||
|
const fy = v.fy + fdy;
|
||||||
|
const tx = v.fx + tdx;
|
||||||
|
const ty = v.fy + tdy;
|
||||||
|
const index = fx + fy * width;
|
||||||
|
const backIndex = tx + ty * width;
|
||||||
|
if (index < 0 || backIndex < 0) return;
|
||||||
|
let data = map.get(index);
|
||||||
|
let backData = map.get(backIndex);
|
||||||
|
if (!data) {
|
||||||
|
data = {};
|
||||||
|
map.set(index, data);
|
||||||
|
}
|
||||||
|
if (!backData) {
|
||||||
|
backData = {};
|
||||||
|
map.set(backIndex, backData);
|
||||||
|
}
|
||||||
|
|
||||||
|
data[v.dir] = {
|
||||||
|
x: v.tx + toFdx,
|
||||||
|
y: v.ty + toFdy,
|
||||||
|
dir: backDir(v.toDir)
|
||||||
|
};
|
||||||
|
backData[backDir(v.dir)] = {
|
||||||
|
x: v.tx - toTdx,
|
||||||
|
y: v.ty - toTdy,
|
||||||
|
dir: v.toDir
|
||||||
|
};
|
||||||
|
});
|
||||||
|
// 逆向映射
|
||||||
|
p.forEach(v => {
|
||||||
|
const [[fdx, fdy], [tdx, tdy]] = delta[backDir(v.toDir)];
|
||||||
|
const [[toFdx, toFdy], [toTdx, toTdy]] = delta[v.dir];
|
||||||
|
const fx = v.tx - fdx;
|
||||||
|
const fy = v.ty - fdy;
|
||||||
|
const tx = v.tx - tdx;
|
||||||
|
const ty = v.ty - tdy;
|
||||||
|
const index = fx + fy * width;
|
||||||
|
const backIndex = tx + ty * width;
|
||||||
|
|
||||||
|
let data = map.get(index);
|
||||||
|
let backData = map.get(backIndex);
|
||||||
|
if (!data) {
|
||||||
|
data = {};
|
||||||
|
map.set(index, data);
|
||||||
|
}
|
||||||
|
if (!backData) {
|
||||||
|
backData = {};
|
||||||
|
map.set(backIndex, backData);
|
||||||
|
}
|
||||||
|
|
||||||
|
data[v.toDir] = {
|
||||||
|
x: v.fx + toFdx,
|
||||||
|
y: v.fy + toFdy,
|
||||||
|
dir: backDir(v.dir)
|
||||||
|
};
|
||||||
|
backData[backDir(v.toDir)] = {
|
||||||
|
x: v.fx + toTdx,
|
||||||
|
y: v.fy + toTdy,
|
||||||
|
dir: v.dir
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initPortals() {
|
||||||
|
generatePortalMap();
|
||||||
|
}
|
||||||
|
}
|
||||||
440
packages-user/data-state/src/mechanism/skillTree.ts
Normal file
440
packages-user/data-state/src/mechanism/skillTree.ts
Normal file
@ -0,0 +1,440 @@
|
|||||||
|
let levels: number[] = [];
|
||||||
|
|
||||||
|
export type Chapter = 'chapter1' | 'chapter2';
|
||||||
|
|
||||||
|
export interface Skill {
|
||||||
|
index: number;
|
||||||
|
title: string;
|
||||||
|
desc: string[];
|
||||||
|
consume: (level: number) => number;
|
||||||
|
front: [skill: number, level: number][];
|
||||||
|
loc: LocArr;
|
||||||
|
max: number;
|
||||||
|
effect: (level: number) => string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Record<Chapter, Skill[]>}
|
||||||
|
*/
|
||||||
|
export const skills: Record<Chapter, Skill[]> = {
|
||||||
|
chapter1: [
|
||||||
|
{
|
||||||
|
index: 0,
|
||||||
|
title: '力量',
|
||||||
|
desc: ['力量就是根本!可以通过智慧增加力量,每级增加2点攻击。'],
|
||||||
|
consume: level => 10 * level + 10,
|
||||||
|
front: [],
|
||||||
|
loc: [1, 2],
|
||||||
|
max: 10,
|
||||||
|
effect: level => [`攻击 + ${level * 2}`]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 1,
|
||||||
|
title: '致命一击',
|
||||||
|
desc: ['爆发出全部力量攻击敌人,每级增加5点额外攻击。'],
|
||||||
|
consume: level => 30 * level + 30,
|
||||||
|
front: [[0, 5]],
|
||||||
|
loc: [2, 1],
|
||||||
|
max: 10,
|
||||||
|
effect: level => [`额外攻击 + ${level * 5}`]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 2,
|
||||||
|
title: '断灭之刃',
|
||||||
|
desc: [
|
||||||
|
'<span style="color: gold">主动技能,快捷键1</span>,',
|
||||||
|
'开启后会在战斗时会额外增加一定量的攻击,但同时减少一定量的防御。'
|
||||||
|
],
|
||||||
|
consume: level => 200 * level + 400,
|
||||||
|
front: [[1, 5]],
|
||||||
|
loc: [4, 1],
|
||||||
|
max: 5,
|
||||||
|
effect: level => [`增加${level * 10}%攻击,减少${level * 10}%防御`]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 3,
|
||||||
|
title: '坚韧',
|
||||||
|
desc: ['由智慧转化出坚韧!每级增加2点防御'],
|
||||||
|
consume: level => 10 * level + 10,
|
||||||
|
front: [],
|
||||||
|
loc: [1, 4],
|
||||||
|
max: 10,
|
||||||
|
effect: level => [`防御 + ${level * 2}`]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 4,
|
||||||
|
title: '回春',
|
||||||
|
desc: ['让智慧化为治愈之泉水!每级增加1点生命回复'],
|
||||||
|
consume: level => 20 * level + 20,
|
||||||
|
front: [[3, 5]],
|
||||||
|
loc: [2, 5],
|
||||||
|
max: 25,
|
||||||
|
effect: level => [`生命回复 + ${level}`]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 5,
|
||||||
|
title: '治愈之泉',
|
||||||
|
desc: [
|
||||||
|
'让生命变得更多一些吧!每吃50瓶血瓶就增加当前生命回复10%的生命回复'
|
||||||
|
],
|
||||||
|
consume: () => 1500,
|
||||||
|
front: [[4, 25]],
|
||||||
|
loc: [4, 5],
|
||||||
|
max: 1,
|
||||||
|
effect: () => [`50瓶血10%生命回复`]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 6,
|
||||||
|
title: '坚固之盾',
|
||||||
|
desc: ['让护甲更加坚硬一些吧!每级增加10点防御'],
|
||||||
|
consume: level => 50 + level * 50,
|
||||||
|
front: [[3, 5]],
|
||||||
|
loc: [2, 3],
|
||||||
|
max: 10,
|
||||||
|
effect: level => [`防御 + ${level * 10}`]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 7,
|
||||||
|
title: '无上之盾',
|
||||||
|
desc: [
|
||||||
|
'<span style="color: #dd4">第一章终极技能</span>,战斗时智慧的 1/10 会充当等量护盾。'
|
||||||
|
],
|
||||||
|
consume: () => 2500,
|
||||||
|
front: [
|
||||||
|
[6, 10],
|
||||||
|
[5, 1],
|
||||||
|
[2, 2]
|
||||||
|
],
|
||||||
|
loc: [5, 3],
|
||||||
|
max: 1,
|
||||||
|
effect: () => [`战斗时智慧会充当护盾`]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
chapter2: [
|
||||||
|
{
|
||||||
|
index: 8,
|
||||||
|
title: '锋利',
|
||||||
|
desc: ['让剑变得更加锋利!每级使攻击增加1%(buff式增加)'],
|
||||||
|
consume: level => (level > 5 ? 50 * level ** 2 : 250 * level + 250),
|
||||||
|
front: [],
|
||||||
|
loc: [1, 2],
|
||||||
|
max: 15,
|
||||||
|
effect: level => [`攻击增加${level}%`]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 9,
|
||||||
|
title: '坚硬',
|
||||||
|
desc: ['让盾牌变得更加坚固!每级使防御增加1%(buff式增加)'],
|
||||||
|
consume: level => (level > 5 ? 50 * level ** 2 : 250 * level + 250),
|
||||||
|
front: [],
|
||||||
|
loc: [1, 4],
|
||||||
|
max: 15,
|
||||||
|
effect: level => [`防御增加${level}%`]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 10,
|
||||||
|
title: '铸剑为盾',
|
||||||
|
desc: [
|
||||||
|
'<span style="color: gold">主动技能,快捷键3</span>,',
|
||||||
|
'减少一定的攻击,增加一定的防御'
|
||||||
|
],
|
||||||
|
consume: level => 1000 * level ** 2 + 1000,
|
||||||
|
front: [[9, 5]],
|
||||||
|
loc: [2, 5],
|
||||||
|
max: 5,
|
||||||
|
effect: level => [
|
||||||
|
`增加${level * 10}%的防御,减少${level * 10}%的攻击`
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 11,
|
||||||
|
title: '魔法盾',
|
||||||
|
desc: ['为主角提供魔法防御,每级增加100点魔法防御'],
|
||||||
|
consume: level => 5000 * level + 5000,
|
||||||
|
front: [
|
||||||
|
[8, 10],
|
||||||
|
[12, 10]
|
||||||
|
],
|
||||||
|
loc: [4, 1],
|
||||||
|
max: 10,
|
||||||
|
effect: level => [`魔法防御 + ${level * 100}`]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 12,
|
||||||
|
title: '聪慧',
|
||||||
|
desc: ['使主角变得更加聪明,每级使绿宝石增加的智慧点上升5%'],
|
||||||
|
consume: level =>
|
||||||
|
level > 5 ? 100 * level ** 2 : 250 * level + 1250,
|
||||||
|
front: [
|
||||||
|
[8, 10],
|
||||||
|
[9, 10]
|
||||||
|
],
|
||||||
|
loc: [3, 3],
|
||||||
|
max: 20,
|
||||||
|
effect: level => [`增加${level * 5}%绿宝石效果`]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 13,
|
||||||
|
title: '治愈',
|
||||||
|
desc: ['使主角能够更好地回复生命,每级使血瓶的加血量增加2%'],
|
||||||
|
consume: level =>
|
||||||
|
level > 5 ? 100 * level ** 2 : 250 * level + 1250,
|
||||||
|
front: [[10, 3]],
|
||||||
|
loc: [4, 5],
|
||||||
|
max: 10,
|
||||||
|
effect: level => [`增加${level * 2}%的血瓶回血量`]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 14,
|
||||||
|
title: '胜利之号',
|
||||||
|
desc: [
|
||||||
|
'<span style="color: #dd4">第二章终极技能</span>,',
|
||||||
|
'勇士攻防增加10%(buff式增加)'
|
||||||
|
],
|
||||||
|
consume: () => 25000,
|
||||||
|
front: [
|
||||||
|
[13, 10],
|
||||||
|
[12, 10],
|
||||||
|
[11, 3]
|
||||||
|
],
|
||||||
|
loc: [5, 3],
|
||||||
|
max: 1,
|
||||||
|
effect: () => [`攻防增加10%`]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
export function resetSkillLevel() {
|
||||||
|
levels = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSkillFromIndex(index: number) {
|
||||||
|
for (const [, skill] of Object.entries(skills)) {
|
||||||
|
const s = skill.find(v => v.index === index);
|
||||||
|
if (s) return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取技能等级
|
||||||
|
*/
|
||||||
|
export function getSkillLevel(skill: number) {
|
||||||
|
levels[skill] ??= 0;
|
||||||
|
return levels[skill];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSkillConsume(skill: number) {
|
||||||
|
const s = getSkillFromIndex(skill);
|
||||||
|
if (!s) return 0;
|
||||||
|
return s.consume(getSkillLevel(skill));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function openTree() {
|
||||||
|
if (main.replayChecking) return;
|
||||||
|
Mota.require('@motajs/legacy-ui').mainUi.open('skillTree');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 能否升级某个技能
|
||||||
|
*/
|
||||||
|
export function canUpgrade(skill: number) {
|
||||||
|
const consume = getSkillConsume(skill);
|
||||||
|
if (consume > core.status.hero.mdef) return false;
|
||||||
|
const level = getSkillLevel(skill);
|
||||||
|
const s = getSkillFromIndex(skill);
|
||||||
|
if (!s) return false;
|
||||||
|
if (level >= s.max) return false;
|
||||||
|
const front = s.front;
|
||||||
|
for (const [skill, level] of front) {
|
||||||
|
if (getSkillLevel(skill) < level) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实际升级效果
|
||||||
|
* @param {number} skill
|
||||||
|
*/
|
||||||
|
export function upgradeSkill(skill: number) {
|
||||||
|
if (!canUpgrade(skill)) return false;
|
||||||
|
switch (skill) {
|
||||||
|
case 0: // 力量 +2攻击
|
||||||
|
core.status.hero.atk += 2;
|
||||||
|
break;
|
||||||
|
case 1: // 致命一击 +5额外攻击
|
||||||
|
core.status.hero.mana += 5;
|
||||||
|
break;
|
||||||
|
case 2: // 断灭之刃
|
||||||
|
HeroSkill.learnSkill(HeroSkill.Blade);
|
||||||
|
break;
|
||||||
|
case 3: // 坚韧 +2防御
|
||||||
|
core.status.hero.def += 2;
|
||||||
|
break;
|
||||||
|
case 4: // 回春 +1回复
|
||||||
|
core.status.hero.hpmax += 1;
|
||||||
|
break;
|
||||||
|
case 5: // 治愈之泉
|
||||||
|
core.setFlag('spring', true);
|
||||||
|
break;
|
||||||
|
case 6: // 坚固之盾 +10防御
|
||||||
|
core.status.hero.def += 10;
|
||||||
|
break;
|
||||||
|
case 7: // 无上之盾
|
||||||
|
core.setFlag('superSheild', true);
|
||||||
|
break;
|
||||||
|
case 8: // 锋利 +1%攻击
|
||||||
|
core.addBuff('atk', 0.01);
|
||||||
|
break;
|
||||||
|
case 9: // 锋利 +1%防御
|
||||||
|
core.addBuff('def', 0.01);
|
||||||
|
break;
|
||||||
|
case 10: // 铸剑为盾
|
||||||
|
HeroSkill.learnSkill(HeroSkill.Shield);
|
||||||
|
break;
|
||||||
|
case 11: // 魔法盾
|
||||||
|
core.status.hero.magicDef += 100;
|
||||||
|
break;
|
||||||
|
case 14:
|
||||||
|
core.addBuff('atk', 0.1);
|
||||||
|
core.addBuff('def', 0.1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const consume = getSkillConsume(skill);
|
||||||
|
core.status.hero.mdef -= consume;
|
||||||
|
levels[skill]++;
|
||||||
|
core.updateStatusBar();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function saveSkillTree() {
|
||||||
|
return levels.slice();
|
||||||
|
}
|
||||||
|
|
||||||
|
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,6 +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';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取勇士在某一点的属性
|
* 获取勇士在某一点的属性
|
||||||
@ -58,6 +59,7 @@ function getRealStatus(
|
|||||||
name: keyof HeroStatus | 'all' | (keyof HeroStatus)[],
|
name: keyof HeroStatus | 'all' | (keyof HeroStatus)[],
|
||||||
floorId: FloorIds = core.status.floorId
|
floorId: FloorIds = core.status.floorId
|
||||||
): any {
|
): any {
|
||||||
|
const { getSkillLevel } = Mota.require('@user/data-state');
|
||||||
if (name instanceof Array) {
|
if (name instanceof Array) {
|
||||||
const res: any = {};
|
const res: any = {};
|
||||||
name.forEach(v => {
|
name.forEach(v => {
|
||||||
@ -92,6 +94,29 @@ function getRealStatus(
|
|||||||
|
|
||||||
if (typeof s !== 'number') return s;
|
if (typeof s !== 'number') return s;
|
||||||
|
|
||||||
|
// 永夜、极昼
|
||||||
|
if (name === 'atk' || name === 'def') {
|
||||||
|
s += NightSpecial.getNight(floorId);
|
||||||
|
}
|
||||||
|
|
||||||
|
const enabled = HeroSkill.getEnabled();
|
||||||
|
// 技能
|
||||||
|
if (enabled === HeroSkill.Blade) {
|
||||||
|
const level = getSkillLevel(2);
|
||||||
|
if (name === 'atk') {
|
||||||
|
s *= 1 + 0.1 * level;
|
||||||
|
} else if (name === 'def') {
|
||||||
|
s *= 1 - 0.1 * level;
|
||||||
|
}
|
||||||
|
} else if (enabled === HeroSkill.Shield) {
|
||||||
|
const level = getSkillLevel(10);
|
||||||
|
if (name === 'atk') {
|
||||||
|
s *= 1 - 0.1 * level;
|
||||||
|
} else if (name === 'def') {
|
||||||
|
s *= 1 + 0.1 * level;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// buff
|
// buff
|
||||||
s *= core.status.hero.buff[name] ?? 1;
|
s *= core.status.hero.buff[name] ?? 1;
|
||||||
s = Math.floor(s);
|
s = Math.floor(s);
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
export function create() {}
|
|
||||||
|
|
||||||
export * from './hero';
|
export * from './hero';
|
||||||
export * from './interface';
|
export * from './interface';
|
||||||
export * from './item';
|
export * from './item';
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import EventEmitter from 'eventemitter3';
|
import EventEmitter from 'eventemitter3';
|
||||||
import { backDir, toDir } from './utils';
|
import { backDir, checkCanMoveExtended, toDir } from './utils';
|
||||||
import { loading } from '@user/data-base';
|
import { loading } from '@user/data-base';
|
||||||
import type { RenderAdapter } from '@motajs/render';
|
import type { RenderAdapter } from '@motajs/render';
|
||||||
import type {
|
import type {
|
||||||
@ -9,8 +9,10 @@ import type {
|
|||||||
HeroRenderer,
|
HeroRenderer,
|
||||||
Layer,
|
Layer,
|
||||||
LayerFloorBinder,
|
LayerFloorBinder,
|
||||||
|
LayerGroup,
|
||||||
LayerMovingRenderable
|
LayerMovingRenderable
|
||||||
} from '@user/client-modules';
|
} from '@user/client-modules';
|
||||||
|
import { BluePalace, MiscData } from '../mechanism/misc';
|
||||||
import { sleep } from '@motajs/common';
|
import { sleep } from '@motajs/common';
|
||||||
|
|
||||||
// todo: 转身功能
|
// todo: 转身功能
|
||||||
@ -412,13 +414,26 @@ interface CanMoveStatus {
|
|||||||
noPass: boolean;
|
noPass: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface PortalStatus {
|
||||||
|
/** 下一步是否会步入传送门 */
|
||||||
|
portal: boolean;
|
||||||
|
/** 传送门会传到哪 */
|
||||||
|
data?: BluePalace.PortalTo;
|
||||||
|
}
|
||||||
|
|
||||||
const enum HeroMoveCode {
|
const enum HeroMoveCode {
|
||||||
Step,
|
Step,
|
||||||
Stop,
|
Stop,
|
||||||
/** 不能移动,并撞击前面一格的图块,触发其触发器 */
|
/** 不能移动,并撞击前面一格的图块,触发其触发器 */
|
||||||
Hit,
|
Hit,
|
||||||
/** 不能移动,同时当前格有CannotOut,或目标格有CannotIn,不会触发前面一格的触发器 */
|
/** 不能移动,同时当前格有CannotOut,或目标格有CannotIn,不会触发前面一格的触发器 */
|
||||||
CannotMove
|
CannotMove,
|
||||||
|
/** 进入传送门 */
|
||||||
|
Portal,
|
||||||
|
/** 循环式地图 */
|
||||||
|
Loop,
|
||||||
|
/** 循环式地图撞击 */
|
||||||
|
LoopHit
|
||||||
}
|
}
|
||||||
|
|
||||||
export class HeroMover extends ObjectMoverBase {
|
export class HeroMover extends ObjectMoverBase {
|
||||||
@ -439,6 +454,9 @@ export class HeroMover extends ObjectMoverBase {
|
|||||||
/** 本次移动开始时的移动速度 */
|
/** 本次移动开始时的移动速度 */
|
||||||
private beforeMoveSpeed: number = 100;
|
private beforeMoveSpeed: number = 100;
|
||||||
|
|
||||||
|
/** 这一步的传送门信息 */
|
||||||
|
private portalData?: BluePalace.PortalTo;
|
||||||
|
|
||||||
override startMove(
|
override startMove(
|
||||||
ignoreTerrain: boolean = false,
|
ignoreTerrain: boolean = false,
|
||||||
noRoute: boolean = false,
|
noRoute: boolean = false,
|
||||||
@ -528,6 +546,16 @@ export class HeroMover extends ObjectMoverBase {
|
|||||||
this.moveDir = dir4Move;
|
this.moveDir = dir4Move;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查传送门
|
||||||
|
if (!this.ignoreTerrain) {
|
||||||
|
const { portal, data } = this.checkPortal(x, y, dir4Move);
|
||||||
|
if (portal && data) {
|
||||||
|
this.portalData = data;
|
||||||
|
await this.renderHeroSwap(data);
|
||||||
|
return HeroMoveCode.Portal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const dir = this.moveDir;
|
const dir = this.moveDir;
|
||||||
if (!this.ignoreTerrain) {
|
if (!this.ignoreTerrain) {
|
||||||
const { noPass, canMove } = this.checkCanMove(x, y, dir4Move);
|
const { noPass, canMove } = this.checkCanMove(x, y, dir4Move);
|
||||||
@ -535,6 +563,25 @@ export class HeroMover extends ObjectMoverBase {
|
|||||||
if (!canMove) {
|
if (!canMove) {
|
||||||
return HeroMoveCode.CannotMove;
|
return HeroMoveCode.CannotMove;
|
||||||
}
|
}
|
||||||
|
// 循环式地图
|
||||||
|
const floorId = core.status.floorId;
|
||||||
|
if (MiscData.loopMaps.has(core.status.floorId)) {
|
||||||
|
const floor = core.status.maps[floorId];
|
||||||
|
const width = floor.width;
|
||||||
|
if (
|
||||||
|
(x === 0 && dir === 'left') ||
|
||||||
|
(x === width - 1 && dir === 'right')
|
||||||
|
) {
|
||||||
|
if (noPass) {
|
||||||
|
return HeroMoveCode.LoopHit;
|
||||||
|
}
|
||||||
|
await Promise.all([
|
||||||
|
this.renderHeroLoop(),
|
||||||
|
this.moveAnimate(nx, ny, showDir, dir)
|
||||||
|
]);
|
||||||
|
return HeroMoveCode.Loop;
|
||||||
|
}
|
||||||
|
}
|
||||||
// 不能移动
|
// 不能移动
|
||||||
if (noPass) {
|
if (noPass) {
|
||||||
return HeroMoveCode.Hit;
|
return HeroMoveCode.Hit;
|
||||||
@ -585,9 +632,25 @@ export class HeroMover extends ObjectMoverBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 本次移动正常完成
|
// 本次移动正常完成
|
||||||
if (code === HeroMoveCode.Step) {
|
if (
|
||||||
core.setHeroLoc('x', nx, true);
|
code === HeroMoveCode.Step ||
|
||||||
core.setHeroLoc('y', ny, true);
|
code === HeroMoveCode.Portal ||
|
||||||
|
code === HeroMoveCode.Loop
|
||||||
|
) {
|
||||||
|
if (code === HeroMoveCode.Portal) {
|
||||||
|
const data = this.portalData;
|
||||||
|
if (!data) return;
|
||||||
|
core.setHeroLoc('x', data.x);
|
||||||
|
core.setHeroLoc('y', data.y);
|
||||||
|
core.setHeroLoc('direction', data.dir);
|
||||||
|
} else if (code === HeroMoveCode.Loop) {
|
||||||
|
const map = core.status.thisMap;
|
||||||
|
if (x === 0) core.setHeroLoc('x', map.width - 1);
|
||||||
|
else core.setHeroLoc('x', 0);
|
||||||
|
} else {
|
||||||
|
core.setHeroLoc('x', nx, true);
|
||||||
|
core.setHeroLoc('y', ny, true);
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.ignoreTerrain) {
|
if (!this.ignoreTerrain) {
|
||||||
const direction = core.getHeroLoc('direction');
|
const direction = core.getHeroLoc('direction');
|
||||||
@ -650,7 +713,7 @@ export class HeroMover extends ObjectMoverBase {
|
|||||||
core.status.automaticRoute.moveStepBeforeStop = [];
|
core.status.automaticRoute.moveStepBeforeStop = [];
|
||||||
core.status.automaticRoute.lastDirection = dir;
|
core.status.automaticRoute.lastDirection = dir;
|
||||||
|
|
||||||
if (core.status.automaticRoute.moveStepBeforeStop.length === 0) {
|
if (core.status.automaticRoute.moveStepBeforeStop.length == 0) {
|
||||||
core.clearContinueAutomaticRoute();
|
core.clearContinueAutomaticRoute();
|
||||||
core.stopAutomaticRoute();
|
core.stopAutomaticRoute();
|
||||||
}
|
}
|
||||||
@ -664,6 +727,20 @@ export class HeroMover extends ObjectMoverBase {
|
|||||||
*/
|
*/
|
||||||
private checkCanMove(x: number, y: number, dir: Dir): CanMoveStatus {
|
private checkCanMove(x: number, y: number, dir: Dir): CanMoveStatus {
|
||||||
// 如果是循环式地图
|
// 如果是循环式地图
|
||||||
|
const floorId = core.status.floorId;
|
||||||
|
if (MiscData.loopMaps.has(floorId)) {
|
||||||
|
const floor = core.status.maps[floorId];
|
||||||
|
const width = floor.width;
|
||||||
|
if (x === 0 && dir === 'left') {
|
||||||
|
const noPass = core.noPass(width - 1, y);
|
||||||
|
const move = checkCanMoveExtended(0, y, width - 1, y, 'left');
|
||||||
|
return { noPass, canMove: move };
|
||||||
|
} else if (x === width - 1 && dir === 'right') {
|
||||||
|
const noPass = core.noPass(0, y);
|
||||||
|
const move = checkCanMoveExtended(width - 1, y, 0, y, 'right');
|
||||||
|
return { noPass, canMove: move };
|
||||||
|
}
|
||||||
|
}
|
||||||
const { x: nx, y: ny } = this.nextLoc(x, y, dir);
|
const { x: nx, y: ny } = this.nextLoc(x, y, dir);
|
||||||
const noPass = core.noPass(nx, ny);
|
const noPass = core.noPass(nx, ny);
|
||||||
const canMove = core.canMoveHero(x, y, dir);
|
const canMove = core.canMoveHero(x, y, dir);
|
||||||
@ -682,6 +759,173 @@ export class HeroMover extends ObjectMoverBase {
|
|||||||
const ny = y + dy;
|
const ny = y + dy;
|
||||||
return { x: nx, y: ny };
|
return { x: nx, y: ny };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查前方一格是否会步入传送门
|
||||||
|
* @param x 横坐标
|
||||||
|
* @param y 纵坐标
|
||||||
|
* @param dir 移动方向
|
||||||
|
*/
|
||||||
|
private checkPortal(x: number, y: number, dir: Dir): PortalStatus {
|
||||||
|
const map = BluePalace.portalMap.get(core.status.floorId);
|
||||||
|
if (!map) {
|
||||||
|
return { portal: false };
|
||||||
|
}
|
||||||
|
const width = core.status.thisMap.width;
|
||||||
|
const index = x + y * width;
|
||||||
|
const data = map?.get(index);
|
||||||
|
if (!data) {
|
||||||
|
return { portal: false };
|
||||||
|
}
|
||||||
|
const to = data[dir];
|
||||||
|
if (to) {
|
||||||
|
return { portal: true, data: to };
|
||||||
|
}
|
||||||
|
return { portal: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderHeroSwap(data: BluePalace.PortalTo) {
|
||||||
|
const adapter = HeroMover.adapter;
|
||||||
|
if (!adapter) return;
|
||||||
|
const list = adapter.items;
|
||||||
|
const { x: tx, y: ty, dir: toDir } = data;
|
||||||
|
const { x, y, direction } = core.status.hero.loc;
|
||||||
|
const { x: dx } = core.utils.scan[direction];
|
||||||
|
const { x: tdx } = core.utils.scan[toDir];
|
||||||
|
|
||||||
|
const promises = [...list].map(v => {
|
||||||
|
if (!v.renderable) return;
|
||||||
|
const renderable = { ...v.renderable };
|
||||||
|
renderable.render = v.getRenderFromDir(toDir);
|
||||||
|
renderable.zIndex = ty;
|
||||||
|
const heroDir = v.moveDir;
|
||||||
|
|
||||||
|
const width = v.renderable.render[0][2];
|
||||||
|
const height = v.renderable.render[0][3];
|
||||||
|
const cell = v.layer.cellSize;
|
||||||
|
const restHeight = height - cell;
|
||||||
|
if (!width || !height) return;
|
||||||
|
|
||||||
|
const originFrom = structuredClone(v.renderable.render);
|
||||||
|
const originTo = structuredClone(renderable.render);
|
||||||
|
v.layer.moving.add(renderable);
|
||||||
|
v.layer.requestUpdateMoving();
|
||||||
|
|
||||||
|
const start = Date.now();
|
||||||
|
return new Promise<void>(res => {
|
||||||
|
const tick = () => {
|
||||||
|
const now = Date.now();
|
||||||
|
const progress = (now - start) / this.moveSpeed;
|
||||||
|
const clipWidth = cell * progress;
|
||||||
|
const clipHeight = cell * progress;
|
||||||
|
const beforeWidth = width - clipWidth;
|
||||||
|
const beforeHeight = height - clipHeight;
|
||||||
|
|
||||||
|
v.renderable!.x = x;
|
||||||
|
v.renderable!.y = y;
|
||||||
|
if (heroDir === 'left' || heroDir === 'right') {
|
||||||
|
v.renderable!.x = x + (clipWidth / 2 / cell) * dx;
|
||||||
|
v.renderable!.render.forEach((v, i) => {
|
||||||
|
v[2] = beforeWidth;
|
||||||
|
if (heroDir === 'left') {
|
||||||
|
v[0] = originFrom[i][0] + clipWidth;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
v.renderable!.render.forEach((v, i) => {
|
||||||
|
v[3] = beforeHeight;
|
||||||
|
if (heroDir === 'up') {
|
||||||
|
v[1] =
|
||||||
|
originFrom[i][1] + clipHeight + restHeight;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
renderable.x = tx;
|
||||||
|
renderable.y = ty;
|
||||||
|
if (toDir === 'left' || toDir === 'right') {
|
||||||
|
renderable.x = tx + (clipWidth / 2 / cell - 0.5) * tdx;
|
||||||
|
renderable.render.forEach((v, i) => {
|
||||||
|
v[2] = clipWidth;
|
||||||
|
if (toDir === 'right') {
|
||||||
|
v[0] = originTo[i][0] + beforeWidth;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (toDir === 'down') renderable.y = ty - 1 + progress;
|
||||||
|
renderable.render.forEach((v, i) => {
|
||||||
|
v[3] = clipHeight + restHeight;
|
||||||
|
if (toDir === 'down') {
|
||||||
|
v[1] = originTo[i][1] + clipHeight + restHeight;
|
||||||
|
v[3] = clipHeight;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
v.layer.delegateTicker(tick, this.moveSpeed, () => {
|
||||||
|
v.renderable!.render = originFrom;
|
||||||
|
v.setAnimateDir(data.dir);
|
||||||
|
v.layer.moving.delete(renderable);
|
||||||
|
v.layer.requestUpdateMoving();
|
||||||
|
res();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.all(promises);
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderHeroLoop() {
|
||||||
|
const adapter = HeroMover.adapter;
|
||||||
|
const viewport = HeroMover.viewport;
|
||||||
|
if (!adapter || !viewport) return;
|
||||||
|
const MotaRenderer = Mota.require('@motajs/render').MotaRenderer;
|
||||||
|
const render = MotaRenderer.get('render-main');
|
||||||
|
const group = render?.getElementById('layer-loop') as LayerGroup;
|
||||||
|
const layer = group?.getLayer('event');
|
||||||
|
const mainGroup = render?.getElementById('layer-main') as LayerGroup;
|
||||||
|
const mainLayer = mainGroup?.getLayer('event');
|
||||||
|
const hero = mainLayer?.getExtends('floor-hero') as HeroRenderer;
|
||||||
|
const renderable = hero?.renderable;
|
||||||
|
if (!layer || !hero || !renderable) return;
|
||||||
|
const { x, y } = core.status.hero.loc;
|
||||||
|
const width = core.status.thisMap.width;
|
||||||
|
const loopHero = { ...renderable };
|
||||||
|
layer.moving.add(loopHero);
|
||||||
|
|
||||||
|
let target: number;
|
||||||
|
let from: number;
|
||||||
|
if (x === 0) {
|
||||||
|
from = width;
|
||||||
|
target = width - 1;
|
||||||
|
} else {
|
||||||
|
from = -1;
|
||||||
|
target = 0;
|
||||||
|
}
|
||||||
|
const delta = target - from;
|
||||||
|
loopHero.x = from;
|
||||||
|
|
||||||
|
layer.requestUpdateMoving();
|
||||||
|
|
||||||
|
const startTime = Date.now();
|
||||||
|
return new Promise<void>(res => {
|
||||||
|
layer.delegateTicker(
|
||||||
|
() => {
|
||||||
|
const progress = (Date.now() - startTime) / this.moveSpeed;
|
||||||
|
const dx = delta * progress;
|
||||||
|
loopHero.x = dx + from;
|
||||||
|
layer.update(layer);
|
||||||
|
},
|
||||||
|
this.moveSpeed,
|
||||||
|
() => {
|
||||||
|
layer.moving.delete(loopHero);
|
||||||
|
layer.requestUpdateMoving();
|
||||||
|
viewport.all('setPosition', x === 0 ? width - 1 : 0, y);
|
||||||
|
res();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface HeroMoveCollection {
|
interface HeroMoveCollection {
|
||||||
|
|||||||
435
packages-user/legacy-plugin-client/src/boss/barrage.ts
Normal file
435
packages-user/legacy-plugin-client/src/boss/barrage.ts
Normal file
@ -0,0 +1,435 @@
|
|||||||
|
import {
|
||||||
|
MotaOffscreenCanvas2D,
|
||||||
|
RenderItem,
|
||||||
|
RenderItemPosition,
|
||||||
|
Transform
|
||||||
|
} from '@motajs/render';
|
||||||
|
import { IStateDamageable } from '@user/data-state';
|
||||||
|
import EventEmitter from 'eventemitter3';
|
||||||
|
import { Ticker } from 'mutate-animate';
|
||||||
|
|
||||||
|
interface BarrageBossEvent {
|
||||||
|
end: [];
|
||||||
|
start: [];
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class BarrageBoss extends EventEmitter<BarrageBossEvent> {
|
||||||
|
ticker: Ticker = new Ticker();
|
||||||
|
/** 这个boss的所有弹幕 */
|
||||||
|
projectiles: Set<Projectile> = new Set();
|
||||||
|
|
||||||
|
/** 开始时刻 */
|
||||||
|
private startTime: number = 0;
|
||||||
|
|
||||||
|
/** 当前帧数 */
|
||||||
|
frame: number = 0;
|
||||||
|
/** 上一帧的时刻 */
|
||||||
|
lastTime: number = 0;
|
||||||
|
|
||||||
|
/** 这个boss战的主渲染元素,所有弹幕都会在此之上渲染 */
|
||||||
|
abstract readonly main: BossSprite;
|
||||||
|
/** 这个boss战中勇士的碰撞箱 */
|
||||||
|
abstract readonly hitbox: Hitbox.HitboxType;
|
||||||
|
/** 勇士的状态 */
|
||||||
|
abstract readonly state: IStateDamageable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* boss的ai,战斗开始后,每帧执行一次
|
||||||
|
* @param time 从战斗开始算起至现在经过了多长时间
|
||||||
|
* @param frame 从战斗开始算起至现在经过了多少帧,即当前是第几帧
|
||||||
|
* @param dt 本帧距上一帧多长时间,即上一帧持续了多长时间
|
||||||
|
*/
|
||||||
|
abstract ai(time: number, frame: number, dt: number): void;
|
||||||
|
|
||||||
|
private tick = () => {
|
||||||
|
const now = Date.now();
|
||||||
|
const dt = now - this.lastTime;
|
||||||
|
this.ai(now - this.startTime, this.frame, dt);
|
||||||
|
this.frame++;
|
||||||
|
this.projectiles.forEach(v => {
|
||||||
|
const time = now - v.startTime;
|
||||||
|
v.time = time;
|
||||||
|
v.ai(this, time, v.frame, dt);
|
||||||
|
v.frame++;
|
||||||
|
if (time > 60_000) {
|
||||||
|
this.destroyProjectile(v);
|
||||||
|
}
|
||||||
|
if (v.isIntersect(this.hitbox)) {
|
||||||
|
v.doDamage(this.state);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.lastTime = now;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始这个弹幕战
|
||||||
|
*/
|
||||||
|
start() {
|
||||||
|
if (this.ticker.funcs.has(this.tick)) {
|
||||||
|
this.ticker.remove(this.tick);
|
||||||
|
}
|
||||||
|
this.startTime = Date.now();
|
||||||
|
this.frame = 0;
|
||||||
|
this.ticker.add(this.tick);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结束这个弹幕战
|
||||||
|
*/
|
||||||
|
end() {
|
||||||
|
if (this.ticker.funcs.has(this.tick)) {
|
||||||
|
this.ticker.remove(this.tick);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 摧毁传入的弹幕
|
||||||
|
*/
|
||||||
|
destroyProjectile(projectile: Projectile) {
|
||||||
|
this.projectiles.delete(projectile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于创建一个弹幕的工厂函数
|
||||||
|
* @param Proj 弹幕类
|
||||||
|
* @param x 弹幕的横坐标
|
||||||
|
* @param y 弹幕的纵坐标
|
||||||
|
*/
|
||||||
|
createProjectile<T extends Projectile>(
|
||||||
|
Proj: new (boss: this) => T,
|
||||||
|
x: number,
|
||||||
|
y: number
|
||||||
|
): T {
|
||||||
|
const projectile = new Proj(this);
|
||||||
|
projectile.setPosition(x, y);
|
||||||
|
return projectile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BossSprite<
|
||||||
|
T extends BarrageBoss = BarrageBoss
|
||||||
|
> extends RenderItem {
|
||||||
|
/** 这个sprite所属的boss */
|
||||||
|
readonly boss: T;
|
||||||
|
|
||||||
|
constructor(type: RenderItemPosition, boss: T) {
|
||||||
|
super(type, false);
|
||||||
|
this.boss = boss;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* override 此方法来实现自定义渲染,默认会调用 {@link renderProjectiles} 方法。
|
||||||
|
* 关于本方法,参考 {@link RenderItem.render}
|
||||||
|
* @param canvas 渲染至的目标画布
|
||||||
|
* @param transform 当前画布相对于父元素的变换矩阵
|
||||||
|
*/
|
||||||
|
protected render(
|
||||||
|
canvas: MotaOffscreenCanvas2D,
|
||||||
|
transform: Transform
|
||||||
|
): void {
|
||||||
|
this.renderProjectiles(canvas, transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渲染所有弹幕
|
||||||
|
* @param canvas 渲染至的画布
|
||||||
|
* @param transform 渲染时的变换矩阵
|
||||||
|
*/
|
||||||
|
protected renderProjectiles(
|
||||||
|
canvas: MotaOffscreenCanvas2D,
|
||||||
|
transform: Transform
|
||||||
|
) {
|
||||||
|
this.boss.projectiles.forEach(v => {
|
||||||
|
v.render(canvas, transform);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class Projectile<T extends BarrageBoss = BarrageBoss> {
|
||||||
|
/** 这个弹幕从属的boss */
|
||||||
|
boss: T;
|
||||||
|
/** 这个弹幕的伤害 */
|
||||||
|
abstract damage: number;
|
||||||
|
|
||||||
|
private _x: number = 0;
|
||||||
|
get x(): number {
|
||||||
|
return this._x;
|
||||||
|
}
|
||||||
|
set x(v: number) {
|
||||||
|
this._x = v;
|
||||||
|
this.updateHitbox(v, this._y);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _y: number = 0;
|
||||||
|
get y(): number {
|
||||||
|
return this._y;
|
||||||
|
}
|
||||||
|
set y(v: number) {
|
||||||
|
this._y = v;
|
||||||
|
this.updateHitbox(this._x, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 弹幕的生成时刻 */
|
||||||
|
startTime: number = Date.now();
|
||||||
|
/** 弹幕当前帧数 */
|
||||||
|
frame: number = 0;
|
||||||
|
/** 当前弹幕持续时长 */
|
||||||
|
time: number = 0;
|
||||||
|
|
||||||
|
/** 这个弹幕的碰撞箱 */
|
||||||
|
abstract hitbox: Hitbox.HitboxType;
|
||||||
|
|
||||||
|
constructor(boss: T) {
|
||||||
|
this.boss = boss;
|
||||||
|
boss.projectiles.add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断一个碰撞箱是否与本弹幕的碰撞箱有交叉。
|
||||||
|
* 此判断应该具有对称性,如果用A检测B发生碰撞,那么用B检测A也应该发生碰撞。
|
||||||
|
* @param hitbox 要检测的碰撞箱
|
||||||
|
*/
|
||||||
|
abstract isIntersect(hitbox: Hitbox.HitboxType): boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当弹幕的横纵坐标改变时,更新碰撞箱
|
||||||
|
* @param x 弹幕的横坐标
|
||||||
|
* @param y 弹幕的纵坐标
|
||||||
|
*/
|
||||||
|
abstract updateHitbox(x: number, y: number): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对一个目标造成伤害
|
||||||
|
* @param target 伤害目标
|
||||||
|
* @returns 是否成功对目标造成伤害
|
||||||
|
*/
|
||||||
|
abstract doDamage(target: IStateDamageable): boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置这个弹幕的位置
|
||||||
|
*/
|
||||||
|
setPosition(x: number, y: number) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.updateHitbox(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 这个弹幕的ai,每帧执行一次,直至被销毁,在1分钟后会强制被摧毁
|
||||||
|
* @param boss 从属的boss
|
||||||
|
* @param time 从弹幕生成开始算起至现在经过了多长时间
|
||||||
|
* @param frame 从弹幕生成开始算起至现在经过了多少帧,即当前是第几帧
|
||||||
|
* @param dt 本帧距上一帧多长时间,即上一帧持续了多长时间
|
||||||
|
*/
|
||||||
|
abstract ai(boss: T, time: number, frame: number, dt: number): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 这个弹幕的渲染函数,原则上一个boss的弹幕应该全部画在同一层,而且渲染前画布不进行矩阵变换
|
||||||
|
* @param canvas 渲染至的画布
|
||||||
|
* @param transform 渲染时的变换矩阵
|
||||||
|
*/
|
||||||
|
abstract render(canvas: MotaOffscreenCanvas2D, transform: Transform): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 摧毁这个弹幕
|
||||||
|
*/
|
||||||
|
destroy() {
|
||||||
|
this.boss.destroyProjectile(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace Hitbox {
|
||||||
|
export type HitboxType = Line | Rect | Circle;
|
||||||
|
|
||||||
|
export class Line {
|
||||||
|
constructor(
|
||||||
|
public x1: number,
|
||||||
|
public y1: number,
|
||||||
|
public x2: number,
|
||||||
|
public y2: number
|
||||||
|
) {}
|
||||||
|
|
||||||
|
setPoint1(x: number, y: number) {
|
||||||
|
this.x1 = x;
|
||||||
|
this.y1 = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
setPoint2(x: number, y: number) {
|
||||||
|
this.x2 = x;
|
||||||
|
this.y2 = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Circle {
|
||||||
|
constructor(
|
||||||
|
public x: number,
|
||||||
|
public y: number,
|
||||||
|
public radius: number
|
||||||
|
) {}
|
||||||
|
|
||||||
|
setRadius(radius: number) {
|
||||||
|
this.radius = radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCenter(x: number, y: number) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Rect {
|
||||||
|
constructor(
|
||||||
|
public x: number,
|
||||||
|
public y: number,
|
||||||
|
public w: number,
|
||||||
|
public h: number
|
||||||
|
) {}
|
||||||
|
|
||||||
|
setPosition(x: number, y: number) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSize(w: number, h: number) {
|
||||||
|
this.w = w;
|
||||||
|
this.h = h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cross(
|
||||||
|
x1: number,
|
||||||
|
y1: number,
|
||||||
|
x2: number,
|
||||||
|
y2: number,
|
||||||
|
x3: number,
|
||||||
|
y3: number
|
||||||
|
): number {
|
||||||
|
const dx1 = x2 - x1;
|
||||||
|
const dy1 = y2 - y1;
|
||||||
|
const dx2 = x3 - x1;
|
||||||
|
const dy2 = y3 - y1;
|
||||||
|
return dx1 * dy2 - dx2 * dy1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查两条线段是否有交叉
|
||||||
|
*/
|
||||||
|
export function checkLineLine(line1: Line, line2: Line) {
|
||||||
|
const { x1, y1, x2, y2 } = line1;
|
||||||
|
const { x1: x3, y1: y3, x2: x4, y2: y4 } = line2;
|
||||||
|
|
||||||
|
if (
|
||||||
|
Math.max(x1, x2) < Math.min(x3, x4) ||
|
||||||
|
Math.min(x1, x2) > Math.max(x3, x4) ||
|
||||||
|
Math.max(y1, y2) < Math.min(y3, y4) ||
|
||||||
|
Math.min(y1, y2) > Math.max(y3, y4)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const d1 = cross(x1, y1, x2, y2, x3, y3);
|
||||||
|
const d2 = cross(x1, y1, x2, y2, x4, y4);
|
||||||
|
const d3 = cross(x3, y3, x4, y4, x1, y1);
|
||||||
|
const d4 = cross(x3, y3, x4, y4, x2, y2);
|
||||||
|
|
||||||
|
return d1 * d2 < 0 && d3 * d4 < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查线段和圆是否有交叉
|
||||||
|
*/
|
||||||
|
export function checkLineCircle(line: Line, circle: Circle) {
|
||||||
|
const { x1, y1, x2, y2 } = line;
|
||||||
|
const { x: cx, y: cy, radius: r } = circle;
|
||||||
|
const minX = Math.min(x1, x2);
|
||||||
|
const maxX = Math.max(x1, x2);
|
||||||
|
const minY = Math.min(y1, y2);
|
||||||
|
const maxY = Math.max(y1, y2);
|
||||||
|
|
||||||
|
// 检查圆心是否在扩展后的矩形范围之外
|
||||||
|
if (cx + r < minX || cx - r > maxX || cy + r < minY || cy - r > maxY) {
|
||||||
|
return false; // 完全不相交
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算线段的方向向量
|
||||||
|
const dx = x2 - x1;
|
||||||
|
const dy = y2 - y1;
|
||||||
|
|
||||||
|
// A, B, C 对应二次方程的系数
|
||||||
|
const a = dx * dx + dy * dy;
|
||||||
|
const b = 2 * (dx * (x1 - cx) + dy * (y1 - cy));
|
||||||
|
const c = (x1 - cx) * (x1 - cx) + (y1 - cy) * (y1 - cy) - r * r;
|
||||||
|
|
||||||
|
// 计算判别式 Δ
|
||||||
|
const discriminant = b ** 2 - 4 * a * c;
|
||||||
|
|
||||||
|
// 如果判别式小于0,则没有交点
|
||||||
|
if (discriminant < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算t的解(参数化线段的参数)
|
||||||
|
const sqrtDiscriminant = Math.sqrt(discriminant);
|
||||||
|
const t1 = (-b - sqrtDiscriminant) / (2 * a);
|
||||||
|
const t2 = (-b + sqrtDiscriminant) / (2 * a);
|
||||||
|
|
||||||
|
// 检查 t1 和 t2 是否在 [0, 1] 之间
|
||||||
|
if ((t1 >= 0 && t1 <= 1) || (t2 >= 0 && t2 <= 1)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 否则没有交点在线段上
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查线段与矩形是否有交叉
|
||||||
|
*/
|
||||||
|
export function checkLineRect(line: Line, rect: Rect) {
|
||||||
|
const { x, y, w, h } = rect;
|
||||||
|
return (
|
||||||
|
checkLineLine(line, new Line(x, y, x + w, y + h)) ||
|
||||||
|
checkLineLine(line, new Line(x + w, y, x, y + h))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查两个圆是否有交叉
|
||||||
|
*/
|
||||||
|
export function checkCircleCircle(circle1: Circle, circle2: Circle) {
|
||||||
|
const dx = circle1.x - circle2.x;
|
||||||
|
const dy = circle1.y - circle2.y;
|
||||||
|
const dis = dx ** 2 + dy ** 2;
|
||||||
|
return dis <= (circle1.radius + circle2.radius) ** 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查圆与矩形是否有交叉
|
||||||
|
*/
|
||||||
|
export function checkCircleRect(circle: Circle, rect: Rect) {
|
||||||
|
const { x: cx, y: cy, radius: r } = circle;
|
||||||
|
const { x, y, w, h } = rect;
|
||||||
|
|
||||||
|
if (cx > x && cx < x + w && cy > y && cy < y + h) return true;
|
||||||
|
|
||||||
|
// 找到圆心到矩形的最近点
|
||||||
|
const closestX = Math.max(x, Math.min(cx, x + w));
|
||||||
|
const closestY = Math.max(y, Math.min(cy, y + h));
|
||||||
|
|
||||||
|
return Math.hypot(closestX - cx, closestY - cy) <= r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查两个矩形是否有交叉
|
||||||
|
*/
|
||||||
|
export function checkRectRect(rect1: Rect, rect2: Rect) {
|
||||||
|
const { x: x1, y: y1, w: w1, h: h1 } = rect1;
|
||||||
|
const { x: x3, y: y3, w: w2, h: h2 } = rect2;
|
||||||
|
const x2 = x1 + w1;
|
||||||
|
const y2 = y1 + h1;
|
||||||
|
const x4 = x3 + w2;
|
||||||
|
const y4 = y3 + h2;
|
||||||
|
|
||||||
|
return x2 >= x3 && x4 >= x1 && y2 >= y3 && y4 >= y1;
|
||||||
|
}
|
||||||
|
}
|
||||||
23
packages-user/legacy-plugin-client/src/boss/index.ts
Normal file
23
packages-user/legacy-plugin-client/src/boss/index.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { hook } from '@user/data-base';
|
||||||
|
import { BarrageBoss } from './barrage';
|
||||||
|
import { TowerBoss } from './towerBoss';
|
||||||
|
|
||||||
|
let boss: BarrageBoss | null;
|
||||||
|
|
||||||
|
export function startTowerBoss() {
|
||||||
|
boss = new TowerBoss();
|
||||||
|
boss.start();
|
||||||
|
boss.once('end', () => {
|
||||||
|
boss = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBoss<T extends BarrageBoss>(): T | null {
|
||||||
|
return boss as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
hook.on('reset', () => {
|
||||||
|
if (boss) {
|
||||||
|
boss.end();
|
||||||
|
}
|
||||||
|
});
|
||||||
132
packages-user/legacy-plugin-client/src/boss/palaceBoss.ts
Normal file
132
packages-user/legacy-plugin-client/src/boss/palaceBoss.ts
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
import { IStateDamageable } from '@user/data-state';
|
||||||
|
import { BarrageBoss, BossSprite, Hitbox } from './barrage';
|
||||||
|
import {
|
||||||
|
Container,
|
||||||
|
MotaRenderer,
|
||||||
|
RenderItem,
|
||||||
|
Shader,
|
||||||
|
Transform,
|
||||||
|
MotaOffscreenCanvas2D
|
||||||
|
} from '@motajs/render';
|
||||||
|
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';
|
||||||
|
import { LayerGroup } from '@user/client-modules';
|
||||||
|
|
||||||
|
loading.once('coreInit', () => {
|
||||||
|
const shader = new Shader();
|
||||||
|
shader.size(480, 480);
|
||||||
|
shader.setHD(true);
|
||||||
|
shader.setZIndex(120);
|
||||||
|
PalaceBoss.shader = shader;
|
||||||
|
PalaceBoss.effect.create(shader, 40);
|
||||||
|
});
|
||||||
|
|
||||||
|
const enum BossStage {
|
||||||
|
Prologue,
|
||||||
|
|
||||||
|
Stage1,
|
||||||
|
Stage2,
|
||||||
|
Stage3,
|
||||||
|
Stage4,
|
||||||
|
|
||||||
|
End
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PalaceBoss extends BarrageBoss {
|
||||||
|
static effect: PointEffect = new PointEffect();
|
||||||
|
static shader: Shader;
|
||||||
|
|
||||||
|
main: BossSprite<BarrageBoss>;
|
||||||
|
hitbox: Hitbox.Circle;
|
||||||
|
state: IStateDamageable;
|
||||||
|
|
||||||
|
private stage: BossStage = BossStage.Prologue;
|
||||||
|
|
||||||
|
/** 用于展示傅里叶频谱的背景元素 */
|
||||||
|
private back: SonicBack;
|
||||||
|
/** 楼层渲染元素 */
|
||||||
|
private group: LayerGroup;
|
||||||
|
/** 楼层渲染容器 */
|
||||||
|
private mapDraw: Container;
|
||||||
|
/** 伤害弹出 */
|
||||||
|
pop: Pop;
|
||||||
|
|
||||||
|
private heroHp: number = 0;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
const render = MotaRenderer.get('render-main')!;
|
||||||
|
this.group = render.getElementById('layer-main') as LayerGroup;
|
||||||
|
this.mapDraw = render.getElementById('map-draw') as Container;
|
||||||
|
this.pop = render.getElementById('pop-main') as Pop;
|
||||||
|
|
||||||
|
this.state = core.status.hero;
|
||||||
|
this.main = new BossEffect('static', this);
|
||||||
|
this.back = new SonicBack('static');
|
||||||
|
const { x, y } = core.status.hero.loc;
|
||||||
|
const cell = 32;
|
||||||
|
this.hitbox = new Hitbox.Circle(x + cell / 2, y + cell / 2, cell / 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
override start(): void {
|
||||||
|
super.start();
|
||||||
|
|
||||||
|
PalaceBoss.shader.appendTo(this.mapDraw);
|
||||||
|
this.main.appendTo(this.group);
|
||||||
|
|
||||||
|
// const event = this.group.getLayer('event');
|
||||||
|
// const hero = event?.getExtends('floor-hero') as HeroRenderer;
|
||||||
|
// hero?.on('moveTick', this.moveTick);
|
||||||
|
|
||||||
|
SplittableBall.init({});
|
||||||
|
this.heroHp = core.status.hero.hp;
|
||||||
|
}
|
||||||
|
|
||||||
|
override end(): void {
|
||||||
|
super.end();
|
||||||
|
|
||||||
|
PalaceBoss.shader.remove();
|
||||||
|
this.main.remove();
|
||||||
|
this.back.remove();
|
||||||
|
this.main.destroy();
|
||||||
|
this.back.destroy();
|
||||||
|
|
||||||
|
// const event = this.group.getLayer('event');
|
||||||
|
// const hero = event?.getExtends('floor-hero') as HeroRenderer;
|
||||||
|
// hero?.off('moveTick', this.moveTick);
|
||||||
|
|
||||||
|
SplittableBall.end();
|
||||||
|
|
||||||
|
PalaceBoss.effect.end();
|
||||||
|
core.status.hero.hp = this.heroHp;
|
||||||
|
|
||||||
|
clip('choices:0');
|
||||||
|
}
|
||||||
|
|
||||||
|
ai(time: number, frame: number): void {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BossEffect extends BossSprite<PalaceBoss> {
|
||||||
|
protected preDraw(
|
||||||
|
canvas: MotaOffscreenCanvas2D,
|
||||||
|
transform: Transform
|
||||||
|
): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected postDraw(
|
||||||
|
canvas: MotaOffscreenCanvas2D,
|
||||||
|
transform: Transform
|
||||||
|
): void {}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SonicBack extends RenderItem {
|
||||||
|
protected render(
|
||||||
|
canvas: MotaOffscreenCanvas2D,
|
||||||
|
transform: Transform
|
||||||
|
): void {}
|
||||||
|
}
|
||||||
@ -0,0 +1,270 @@
|
|||||||
|
import { Transform, MotaOffscreenCanvas2D } from '@motajs/render';
|
||||||
|
import { IStateDamageable } from '@user/data-state';
|
||||||
|
import { Hitbox, Projectile } from './barrage';
|
||||||
|
import type { PalaceBoss } from './palaceBoss';
|
||||||
|
import { clamp } from '@motajs/legacy-ui';
|
||||||
|
import { mainRenderer } from 'packages-user/client-modules/src/render/renderer';
|
||||||
|
|
||||||
|
function popDamage(damage: number, boss: PalaceBoss, color: string) {
|
||||||
|
const { x, y } = core.status.hero.loc;
|
||||||
|
boss.pop.addPop(
|
||||||
|
(-damage).toString(),
|
||||||
|
1000,
|
||||||
|
x * 32 + 16,
|
||||||
|
y * 32 + 16,
|
||||||
|
color
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ISplitData {
|
||||||
|
split: boolean;
|
||||||
|
/** 分裂时刻,以弹幕被创建时刻为基准 */
|
||||||
|
time: number;
|
||||||
|
/** 分裂起始角度,以该弹幕朝向方向为 0 */
|
||||||
|
startAngle: number;
|
||||||
|
/** 分裂终止角度,以该弹幕朝向方向为 0 */
|
||||||
|
endAngle: number;
|
||||||
|
/** 每秒加速度 */
|
||||||
|
acc: number;
|
||||||
|
/** 初始速度 */
|
||||||
|
startVel: number;
|
||||||
|
/** 终止速度 */
|
||||||
|
endVel: number;
|
||||||
|
/** 持续时长 */
|
||||||
|
lastTime: number;
|
||||||
|
/** 分裂数量 */
|
||||||
|
count: number;
|
||||||
|
/** 这个弹幕分裂产生的弹幕的分裂信息,不填则表示产生的弹幕不会分裂 */
|
||||||
|
data?: ISplitData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SplittableBall extends Projectile<PalaceBoss> {
|
||||||
|
damage: number = 10000;
|
||||||
|
hitbox: Hitbox.Circle = new Hitbox.Circle(0, 0, 8);
|
||||||
|
|
||||||
|
static ball: Map<string, MotaOffscreenCanvas2D> = new Map();
|
||||||
|
|
||||||
|
private damaged: boolean = false;
|
||||||
|
private splitData?: ISplitData;
|
||||||
|
private last: number = 60_000;
|
||||||
|
|
||||||
|
/** 角度,水平向右为 0,顺时针旋转一圈为 Math.PI * 2 */
|
||||||
|
private angle: number = 0;
|
||||||
|
/** 每秒加速度 */
|
||||||
|
private acc: number = 0;
|
||||||
|
/** 初始速度,每秒多少像素 */
|
||||||
|
private startVel: number = 0;
|
||||||
|
/** 终止速度 */
|
||||||
|
private endVel: number = 0;
|
||||||
|
/** 弹幕颜色 */
|
||||||
|
private color?: string;
|
||||||
|
|
||||||
|
private startVelX: number = 0;
|
||||||
|
private startVelY: number = 0;
|
||||||
|
private endVelX: number = 0;
|
||||||
|
private endVelY: number = 0;
|
||||||
|
private vx: number = 0;
|
||||||
|
private vy: number = 0;
|
||||||
|
// 加速度
|
||||||
|
private ax: number = 0;
|
||||||
|
private ay: number = 0;
|
||||||
|
|
||||||
|
/** 是否已经分裂过 */
|
||||||
|
private splitted: boolean = false;
|
||||||
|
|
||||||
|
static init(colors: Record<string, string[]>) {
|
||||||
|
this.ball.forEach(v => mainRenderer.deleteCanvas(v));
|
||||||
|
this.ball.clear();
|
||||||
|
for (const [key, color] of Object.entries(colors)) {
|
||||||
|
const canvas = mainRenderer.requireCanvas();
|
||||||
|
canvas.size(32, 32);
|
||||||
|
canvas.setHD(true);
|
||||||
|
const ctx = canvas.ctx;
|
||||||
|
const gradient = ctx.createRadialGradient(16, 16, 8, 16, 16, 16);
|
||||||
|
const step = 1 / (color.length - 1);
|
||||||
|
for (let i = 0; i < color.length; i++) {
|
||||||
|
gradient.addColorStop(i * step, color[i]);
|
||||||
|
}
|
||||||
|
ctx.fillStyle = gradient;
|
||||||
|
ctx.arc(16, 16, 16, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
this.ball.set(key, canvas);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static end() {
|
||||||
|
this.ball.forEach(v => {
|
||||||
|
v.clear();
|
||||||
|
mainRenderer.deleteCanvas(v);
|
||||||
|
});
|
||||||
|
this.ball.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置持续时长
|
||||||
|
* @param time 持续时长
|
||||||
|
*/
|
||||||
|
setLastTime(time: number) {
|
||||||
|
this.last = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置这个弹幕的分裂数据
|
||||||
|
* @param data 分裂数据,不填表示该弹幕不会分裂
|
||||||
|
*/
|
||||||
|
setSplitData(data?: ISplitData) {
|
||||||
|
this.splitData = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算速度分量信息
|
||||||
|
*/
|
||||||
|
private calVel() {
|
||||||
|
const sin = Math.sin(this.angle);
|
||||||
|
const cos = Math.cos(this.angle);
|
||||||
|
const vel = Math.hypot(this.vx, this.vy);
|
||||||
|
|
||||||
|
this.startVelX = this.startVel * cos;
|
||||||
|
this.startVelY = this.startVel * sin;
|
||||||
|
this.endVelX = this.endVel * cos;
|
||||||
|
this.endVelY = this.endVel * sin;
|
||||||
|
this.ax = this.acc * cos;
|
||||||
|
this.ay = this.acc * sin;
|
||||||
|
this.vx = vel * cos;
|
||||||
|
this.vy = vel * sin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置弹幕速度朝向
|
||||||
|
* @param angle 朝向
|
||||||
|
*/
|
||||||
|
setAngle(angle: number) {
|
||||||
|
this.angle = angle;
|
||||||
|
this.calVel();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置速度
|
||||||
|
* @param start 起始速度
|
||||||
|
* @param end 终止速度
|
||||||
|
*/
|
||||||
|
setVel(start: number, end: number) {
|
||||||
|
this.startVel = start;
|
||||||
|
this.endVel = end;
|
||||||
|
this.calVel();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置加速度
|
||||||
|
* @param acc 加速度,每秒加速多少像素
|
||||||
|
*/
|
||||||
|
setAcc(acc: number) {
|
||||||
|
this.acc = acc;
|
||||||
|
this.calVel();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置弹幕的颜色
|
||||||
|
* @param color 颜色
|
||||||
|
*/
|
||||||
|
setColor(color: string) {
|
||||||
|
this.color = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
isIntersect(hitbox: Hitbox.HitboxType): boolean {
|
||||||
|
if (hitbox instanceof Hitbox.Circle) {
|
||||||
|
return Hitbox.checkCircleCircle(hitbox, this.hitbox);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateHitbox(x: number, y: number): void {
|
||||||
|
this.hitbox.setCenter(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
doDamage(target: IStateDamageable): boolean {
|
||||||
|
if (this.damaged) return false;
|
||||||
|
target.hp -= this.damage;
|
||||||
|
this.damaged = true;
|
||||||
|
core.drawHeroAnimate('hand');
|
||||||
|
popDamage(this.damage, this.boss, '#ff8180');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private split(boss: PalaceBoss) {
|
||||||
|
if (!this.splitData?.split) return;
|
||||||
|
if (this.splitted) return;
|
||||||
|
this.splitted = true;
|
||||||
|
const {
|
||||||
|
startAngle,
|
||||||
|
endAngle,
|
||||||
|
startVel,
|
||||||
|
endVel,
|
||||||
|
acc,
|
||||||
|
lastTime,
|
||||||
|
count,
|
||||||
|
data
|
||||||
|
} = this.splitData;
|
||||||
|
|
||||||
|
const sa = this.angle + startAngle;
|
||||||
|
const ea = this.angle + endAngle;
|
||||||
|
const step = (ea - sa - 1) / count;
|
||||||
|
const { x, y } = this.hitbox;
|
||||||
|
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
const proj = boss.createProjectile(SplittableBall, x, y);
|
||||||
|
proj.setAngle(sa + step * i);
|
||||||
|
proj.setAcc(acc);
|
||||||
|
proj.setVel(startVel, endVel);
|
||||||
|
proj.setLastTime(lastTime);
|
||||||
|
proj.setSplitData(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ai(boss: PalaceBoss, time: number, _frame: number, dt: number): void {
|
||||||
|
if (this.splitData?.split) {
|
||||||
|
if (time > this.splitData.time) {
|
||||||
|
this.split(boss);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (time > this.last) {
|
||||||
|
this.destroy();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const p = dt / 1000;
|
||||||
|
this.vx += this.ax * p;
|
||||||
|
this.vy += this.ay * p;
|
||||||
|
|
||||||
|
const sx = Math.sign(this.vx);
|
||||||
|
const sy = Math.sign(this.vy);
|
||||||
|
const cx = clamp(
|
||||||
|
Math.abs(this.vx),
|
||||||
|
Math.abs(this.startVelX),
|
||||||
|
Math.abs(this.endVelX)
|
||||||
|
);
|
||||||
|
const cy = clamp(
|
||||||
|
Math.abs(this.vy),
|
||||||
|
Math.abs(this.startVelY),
|
||||||
|
Math.abs(this.endVelY)
|
||||||
|
);
|
||||||
|
this.vx = cx * sx;
|
||||||
|
this.vy = cy * sy;
|
||||||
|
|
||||||
|
const { x, y } = this.hitbox;
|
||||||
|
this.setPosition(x + this.vx * p, y + this.vy * p);
|
||||||
|
}
|
||||||
|
|
||||||
|
render(canvas: MotaOffscreenCanvas2D, _transform: Transform): void {
|
||||||
|
if (!this.color) return;
|
||||||
|
const texture = SplittableBall.ball.get(this.color);
|
||||||
|
if (!texture) return;
|
||||||
|
const ctx = canvas.ctx;
|
||||||
|
ctx.drawImage(texture.canvas, this.x - 16, this.y - 16, 32, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy(): void {
|
||||||
|
this.split(this.boss);
|
||||||
|
super.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
850
packages-user/legacy-plugin-client/src/boss/towerBoss.ts
Normal file
850
packages-user/legacy-plugin-client/src/boss/towerBoss.ts
Normal file
@ -0,0 +1,850 @@
|
|||||||
|
import {
|
||||||
|
Shader,
|
||||||
|
MotaRenderer,
|
||||||
|
RenderItem,
|
||||||
|
MotaOffscreenCanvas2D,
|
||||||
|
Container
|
||||||
|
} from '@motajs/render';
|
||||||
|
import { PointEffect } from '../fx/pointShader';
|
||||||
|
import { BarrageBoss, BossSprite, Hitbox } from './barrage';
|
||||||
|
import { Animation, hyper, power, sleep, Transition } from 'mutate-animate';
|
||||||
|
import {
|
||||||
|
ArrowProjectile,
|
||||||
|
AttackProjectile,
|
||||||
|
BoomProjectile,
|
||||||
|
ChainProjectile,
|
||||||
|
IceProjectile,
|
||||||
|
PortalProjectile,
|
||||||
|
ProjectileDirection,
|
||||||
|
ThunderBallProjectile,
|
||||||
|
ThunderProjectile
|
||||||
|
} from './towerBossProjectile';
|
||||||
|
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 {
|
||||||
|
HeroRenderer,
|
||||||
|
LayerGroup,
|
||||||
|
WeatherController
|
||||||
|
} from '@user/client-modules';
|
||||||
|
|
||||||
|
loading.once('coreInit', () => {
|
||||||
|
const shader = new Shader();
|
||||||
|
shader.size(480, 480);
|
||||||
|
shader.setHD(true);
|
||||||
|
shader.setZIndex(120);
|
||||||
|
TowerBoss.shader = shader;
|
||||||
|
TowerBoss.effect.create(shader, 40);
|
||||||
|
});
|
||||||
|
|
||||||
|
const enum TowerBossStage {
|
||||||
|
/** 开场白阶段 */
|
||||||
|
Prologue,
|
||||||
|
|
||||||
|
Stage1,
|
||||||
|
Dialogue1,
|
||||||
|
Stage2,
|
||||||
|
Dialogue2,
|
||||||
|
Stage3,
|
||||||
|
Stage4,
|
||||||
|
Stage5,
|
||||||
|
|
||||||
|
End
|
||||||
|
}
|
||||||
|
|
||||||
|
const enum HealthBarStatus {
|
||||||
|
Start,
|
||||||
|
Running,
|
||||||
|
End
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TowerBoss extends BarrageBoss {
|
||||||
|
static effect: PointEffect = new PointEffect();
|
||||||
|
static shader: Shader;
|
||||||
|
|
||||||
|
/** boss战阶段 */
|
||||||
|
stage: TowerBossStage = TowerBossStage.Prologue;
|
||||||
|
/** 当前boss血量 */
|
||||||
|
hp: number = 10000;
|
||||||
|
/** 当前时刻 */
|
||||||
|
time: number = 0;
|
||||||
|
|
||||||
|
readonly hitbox: Hitbox.Rect;
|
||||||
|
readonly state: IStateDamageable;
|
||||||
|
readonly main: BossSprite;
|
||||||
|
|
||||||
|
/** 血条显示元素 */
|
||||||
|
private healthBar: HealthBar;
|
||||||
|
/** 对话文字显示元素 */
|
||||||
|
private word: Word;
|
||||||
|
/** 楼层渲染元素 */
|
||||||
|
private group: LayerGroup;
|
||||||
|
/** 楼层渲染容器 */
|
||||||
|
private mapDraw: Container;
|
||||||
|
/** 伤害弹出 */
|
||||||
|
pop: Pop;
|
||||||
|
|
||||||
|
/** 每个阶段的进度,具体定义参考 ai 函数开头 */
|
||||||
|
private stageProgress: number = 0;
|
||||||
|
/** 当前阶段的开始时刻 */
|
||||||
|
private stageStartTime: number = 0;
|
||||||
|
/** 每一阶段的攻击boss次数 */
|
||||||
|
private attackTime: number = 0;
|
||||||
|
/** 攻击boss的红圈间隔时长 */
|
||||||
|
private attackInterval: number = 7000;
|
||||||
|
|
||||||
|
/** 使用技能1 智慧之矢 的次数 */
|
||||||
|
private skill1Time: number = 0;
|
||||||
|
/** 使用技能2 随机传送 的次数 */
|
||||||
|
private skill2Time: number = 0;
|
||||||
|
/** 使用技能3 冰锥 的次数 */
|
||||||
|
private skill3Time: number = 0;
|
||||||
|
/** 技能1的释放间隔 */
|
||||||
|
private skill1Interval: number = 10000;
|
||||||
|
/** 技能2的释放间隔 */
|
||||||
|
private skill2Interval: number = 7000;
|
||||||
|
/** 技能3的释放间隔 */
|
||||||
|
private skill3Interval: number = 13000;
|
||||||
|
|
||||||
|
/** 使用技能4 随机闪电 的次数 */
|
||||||
|
private skill4Time: number = 0;
|
||||||
|
/** 使用技能5 球形闪电 的次数 */
|
||||||
|
private skill5Time: number = 0;
|
||||||
|
/** 技能4的释放间隔 */
|
||||||
|
private skill4Interval: number = 4000;
|
||||||
|
/** 技能5的释放间隔 */
|
||||||
|
private skill5Interval: number = 12000;
|
||||||
|
|
||||||
|
/** 使用技能6 炸弹 的次数 */
|
||||||
|
private skill6Time: number = 0;
|
||||||
|
/** 使用技能7 连锁闪电 的次数 */
|
||||||
|
private skill7Time: number = 0;
|
||||||
|
/** 技能6的释放间隔 */
|
||||||
|
private skill6Interval: number = 500;
|
||||||
|
/** 技能7的释放间隔 */
|
||||||
|
private skill7Interval: number = 10000;
|
||||||
|
|
||||||
|
private heroHp: number = 0;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.healthBar = new HealthBar('absolute');
|
||||||
|
this.word = new Word('absolute');
|
||||||
|
this.main = new BossSprite('absolute', this);
|
||||||
|
const render = MotaRenderer.get('render-main')!;
|
||||||
|
this.group = render.getElementById('layer-main') as LayerGroup;
|
||||||
|
this.mapDraw = render.getElementById('map-draw') as Container;
|
||||||
|
this.pop = render.getElementById('pop-main') as Pop;
|
||||||
|
|
||||||
|
this.healthBar.init();
|
||||||
|
this.word.init();
|
||||||
|
this.main.size(480, 480);
|
||||||
|
this.main.setHD(true);
|
||||||
|
this.main.setZIndex(80);
|
||||||
|
|
||||||
|
TowerBoss.effect.setTransform(this.group.camera);
|
||||||
|
|
||||||
|
const { x, y } = core.status.hero.loc;
|
||||||
|
const cell = 32;
|
||||||
|
this.hitbox = new Hitbox.Rect(x * cell + 2, y * cell + 2, 28, 28);
|
||||||
|
this.state = core.status.hero;
|
||||||
|
}
|
||||||
|
|
||||||
|
private moveTick = (x: number, y: number) => {
|
||||||
|
this.hitbox.setPosition(x * 32 + 2, y * 32 + 2);
|
||||||
|
};
|
||||||
|
|
||||||
|
override start() {
|
||||||
|
super.start();
|
||||||
|
|
||||||
|
TowerBoss.shader.appendTo(this.mapDraw);
|
||||||
|
this.healthBar.appendTo(this.group);
|
||||||
|
this.word.appendTo(this.group);
|
||||||
|
this.main.appendTo(this.group);
|
||||||
|
|
||||||
|
const event = this.group.getLayer('event');
|
||||||
|
const hero = event?.getExtends('floor-hero') as HeroRenderer;
|
||||||
|
hero?.on('moveTick', this.moveTick);
|
||||||
|
|
||||||
|
ArrowProjectile.init();
|
||||||
|
PortalProjectile.init();
|
||||||
|
ThunderProjectile.init();
|
||||||
|
ThunderBallProjectile.init();
|
||||||
|
AttackProjectile.init();
|
||||||
|
|
||||||
|
TowerBoss.effect.start();
|
||||||
|
TowerBoss.effect.use();
|
||||||
|
|
||||||
|
this.heroHp = core.status.hero.hp;
|
||||||
|
}
|
||||||
|
|
||||||
|
override end() {
|
||||||
|
super.end();
|
||||||
|
TowerBoss.shader.remove();
|
||||||
|
this.healthBar.remove();
|
||||||
|
this.word.remove();
|
||||||
|
this.main.remove();
|
||||||
|
this.main.destroy();
|
||||||
|
this.healthBar.destroy();
|
||||||
|
|
||||||
|
const event = this.group.getLayer('event');
|
||||||
|
const hero = event?.getExtends('floor-hero') as HeroRenderer;
|
||||||
|
hero?.off('moveTick', this.moveTick);
|
||||||
|
|
||||||
|
ArrowProjectile.end();
|
||||||
|
PortalProjectile.end();
|
||||||
|
ThunderProjectile.end();
|
||||||
|
ThunderBallProjectile.end();
|
||||||
|
AttackProjectile.end();
|
||||||
|
|
||||||
|
TowerBoss.effect.end();
|
||||||
|
core.status.hero.hp = this.heroHp;
|
||||||
|
|
||||||
|
clip('choices:0');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于全局检测,例如受伤、攻击boss等
|
||||||
|
*/
|
||||||
|
check(_time: number) {
|
||||||
|
this.checkLose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private checkLose() {
|
||||||
|
if (core.status.hero.hp < 0) {
|
||||||
|
core.lose();
|
||||||
|
core.updateStatusBar();
|
||||||
|
this.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 攻击boss
|
||||||
|
* @param damage 造成的伤害
|
||||||
|
*/
|
||||||
|
attackBoss(damage: number) {
|
||||||
|
this.hp -= damage;
|
||||||
|
if (this.hp < 0) this.hp = 0;
|
||||||
|
this.healthBar.set(this.hp);
|
||||||
|
// 先用drawAnimate凑活一下,等下个版本提供更好的 api
|
||||||
|
if (this.stage === TowerBossStage.Stage3) {
|
||||||
|
core.drawAnimate('hand', 7, 2);
|
||||||
|
this.pop.addPop((-damage).toString(), 1000, 240, 80, '#dafc1d');
|
||||||
|
} else if (this.stage === TowerBossStage.Stage4) {
|
||||||
|
core.drawAnimate('hand', 7, 3);
|
||||||
|
this.pop.addPop((-damage).toString(), 1000, 240, 112, '#dafc1d');
|
||||||
|
} else if (this.stage === TowerBossStage.Stage5) {
|
||||||
|
core.drawAnimate('hand', 7, 4);
|
||||||
|
this.pop.addPop((-damage).toString(), 1000, 240, 144, '#dafc1d');
|
||||||
|
} else {
|
||||||
|
core.drawAnimate('hand', 7, 1);
|
||||||
|
this.pop.addPop((-damage).toString(), 1000, 240, 172, '#dafc1d');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加攻击boss的圆圈
|
||||||
|
* @param last 持续时长
|
||||||
|
* @param damage 造成的伤害
|
||||||
|
*/
|
||||||
|
addAttackCircle(_: number, n: number) {
|
||||||
|
const s = 13 - n * 2;
|
||||||
|
const nx = Math.floor(Math.random() * s + n + 1);
|
||||||
|
const ny = Math.floor(Math.random() * s + n + 1);
|
||||||
|
const proj = this.createProjectile(AttackProjectile, nx * 32, ny * 32);
|
||||||
|
proj.damage = 250 + Math.floor(Math.random() * 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
ai(time: number, frame: number): void {
|
||||||
|
this.time = time;
|
||||||
|
const fixedTime = time - this.stageStartTime;
|
||||||
|
this.main.update();
|
||||||
|
this.check(time);
|
||||||
|
TowerBoss.effect.requestUpdate();
|
||||||
|
switch (this.stage) {
|
||||||
|
case TowerBossStage.Prologue:
|
||||||
|
this.aiPrologue(fixedTime, frame);
|
||||||
|
break;
|
||||||
|
case TowerBossStage.Stage1:
|
||||||
|
this.aiStage1(fixedTime, frame);
|
||||||
|
break;
|
||||||
|
case TowerBossStage.Dialogue1:
|
||||||
|
this.aiDialogue1(fixedTime, frame);
|
||||||
|
break;
|
||||||
|
case TowerBossStage.Stage2:
|
||||||
|
this.aiStage2(fixedTime, frame);
|
||||||
|
break;
|
||||||
|
case TowerBossStage.Dialogue2:
|
||||||
|
this.aiDialogue2(fixedTime, frame);
|
||||||
|
break;
|
||||||
|
case TowerBossStage.Stage3:
|
||||||
|
this.aiStage3(fixedTime, frame);
|
||||||
|
break;
|
||||||
|
case TowerBossStage.Stage4:
|
||||||
|
this.aiStage4(fixedTime, frame);
|
||||||
|
break;
|
||||||
|
case TowerBossStage.Stage5:
|
||||||
|
this.aiStage5(fixedTime, frame);
|
||||||
|
break;
|
||||||
|
case TowerBossStage.End:
|
||||||
|
this.aiEnd(fixedTime, frame);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切换boss阶段
|
||||||
|
* @param stage 切换至的阶段
|
||||||
|
* @param time 在当前阶段经过的时间
|
||||||
|
*/
|
||||||
|
private changeStage(stage: TowerBossStage, time: number) {
|
||||||
|
this.stage = stage;
|
||||||
|
this.stageStartTime += time;
|
||||||
|
this.stageProgress = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private aiPrologue(time: number, _frame: number) {
|
||||||
|
// stageProgress:
|
||||||
|
// 0: 开始; 1: 开始血条动画
|
||||||
|
|
||||||
|
if (this.stageProgress === 0) {
|
||||||
|
this.healthBar.showStart();
|
||||||
|
this.stageProgress = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (time > 1500) {
|
||||||
|
this.changeStage(TowerBossStage.Stage1, time);
|
||||||
|
this.attackTime = 2;
|
||||||
|
this.skill1Time = 1;
|
||||||
|
this.skill2Time = 1;
|
||||||
|
this.skill3Time = 1;
|
||||||
|
core.playBgm('towerBoss.opus');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async releaseSkill1() {
|
||||||
|
const locs = new Set<number>();
|
||||||
|
const count = Math.ceil(Math.random() * 8) + 4;
|
||||||
|
let i = 0;
|
||||||
|
while (i < count) {
|
||||||
|
const dir = Math.floor(Math.random() * 2);
|
||||||
|
const pos = Math.floor(Math.random() * 13);
|
||||||
|
const loc = pos + dir * 13;
|
||||||
|
if (locs.has(loc)) continue;
|
||||||
|
i++;
|
||||||
|
locs.add(loc);
|
||||||
|
const proj = this.createProjectile(ArrowProjectile, 0, 0);
|
||||||
|
proj.setData(dir);
|
||||||
|
if (dir === ProjectileDirection.Horizontal) {
|
||||||
|
proj.setPosition(480 - 32, pos * 32 + 32);
|
||||||
|
} else {
|
||||||
|
proj.setPosition(pos * 32 + 32, 480 - 32);
|
||||||
|
}
|
||||||
|
await sleep(200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
releaseSkill2() {
|
||||||
|
const x = Math.floor(Math.random() * 11 + 2);
|
||||||
|
const y = Math.floor(Math.random() * 11 + 2);
|
||||||
|
const proj = this.createProjectile(PortalProjectile, 0, 0);
|
||||||
|
proj.setTarget(x, y);
|
||||||
|
proj.createEffect(TowerBoss.effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
async releaseSkill3() {
|
||||||
|
const count = Math.floor(Math.random() * 100);
|
||||||
|
const used = new Set<number>();
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
const x = Math.floor(Math.random() * 13 + 1);
|
||||||
|
const y = Math.floor(Math.random() * 13 + 1);
|
||||||
|
const index = x + y * 13;
|
||||||
|
if (used.has(index)) continue;
|
||||||
|
used.add(index);
|
||||||
|
const proj = this.createProjectile(IceProjectile, x * 32, y * 32);
|
||||||
|
proj.setPos(x, y);
|
||||||
|
await sleep(20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private aiStage1(time: number, _frame: number) {
|
||||||
|
// stageProgress:
|
||||||
|
// 0: 开始; 1,2,3,4: 对应对话
|
||||||
|
|
||||||
|
const skill1Release = this.skill1Time * this.skill1Interval;
|
||||||
|
const skill2Release = this.skill2Time * this.skill2Interval;
|
||||||
|
const skill3Release = this.skill3Time * this.skill3Interval;
|
||||||
|
const attack = this.attackTime * this.attackInterval;
|
||||||
|
|
||||||
|
if (time > skill1Release) {
|
||||||
|
this.releaseSkill1();
|
||||||
|
this.skill1Time++;
|
||||||
|
}
|
||||||
|
if (time > skill2Release) {
|
||||||
|
this.releaseSkill2();
|
||||||
|
this.skill2Time++;
|
||||||
|
}
|
||||||
|
if (time > skill3Release) {
|
||||||
|
this.releaseSkill3();
|
||||||
|
this.skill3Time++;
|
||||||
|
}
|
||||||
|
if (time > attack) {
|
||||||
|
this.addAttackCircle(500, 0);
|
||||||
|
this.attackTime++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.hp <= 7000) {
|
||||||
|
this.changeStage(TowerBossStage.Dialogue1, time);
|
||||||
|
this.attackTime = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private aiDialogue1(time: number, _frame: number) {
|
||||||
|
this.changeStage(TowerBossStage.Stage2, time);
|
||||||
|
this.attackTime = 3;
|
||||||
|
this.skill4Time = 5;
|
||||||
|
this.skill5Time = 3;
|
||||||
|
core.playBgm('towerBoss2.opus');
|
||||||
|
const weather = WeatherController.get('main');
|
||||||
|
weather?.activate('rain', 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
releaseSkill4() {
|
||||||
|
const x = Math.floor(Math.random() * 11 + 2);
|
||||||
|
const y = Math.floor(Math.random() * 11 + 2);
|
||||||
|
const power = Math.floor(Math.random() * 6 + 1);
|
||||||
|
const proj = this.createProjectile(ThunderProjectile, 0, 0);
|
||||||
|
proj.setData(x, y, power);
|
||||||
|
proj.createEffect(TowerBoss.effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
async releaseSkill5() {
|
||||||
|
const count = Math.floor(Math.random() * 12 + 6);
|
||||||
|
const used = new Set<number>();
|
||||||
|
let i = 0;
|
||||||
|
while (i < count) {
|
||||||
|
const x = Math.floor(Math.random() * 13 + 1);
|
||||||
|
const y = Math.floor(Math.random() * 13 + 1);
|
||||||
|
const index = x + y * 13;
|
||||||
|
if (used.has(index)) continue;
|
||||||
|
i++;
|
||||||
|
used.add(index);
|
||||||
|
const px = x * 32 + 16;
|
||||||
|
const py = y * 32 + 16;
|
||||||
|
const proj1 = this.createProjectile(ThunderBallProjectile, 0, 0);
|
||||||
|
const proj2 = this.createProjectile(ThunderBallProjectile, 0, 0);
|
||||||
|
const proj3 = this.createProjectile(ThunderBallProjectile, 0, 0);
|
||||||
|
const proj4 = this.createProjectile(ThunderBallProjectile, 0, 0);
|
||||||
|
proj1.setData(ProjectileDirection.BottomToTop, x, y);
|
||||||
|
proj2.setData(ProjectileDirection.LeftToRight, x, y);
|
||||||
|
proj3.setData(ProjectileDirection.RightToLeft, x, y);
|
||||||
|
proj4.setData(ProjectileDirection.TopToBottom, x, y);
|
||||||
|
proj1.setPosition(px, py);
|
||||||
|
proj2.setPosition(px, py);
|
||||||
|
proj3.setPosition(px, py);
|
||||||
|
proj4.setPosition(px, py);
|
||||||
|
await sleep(200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private aiStage2(time: number, _frame: number) {
|
||||||
|
const skill4Release = this.skill4Time * this.skill4Interval;
|
||||||
|
const skill5Release = this.skill5Time * this.skill5Interval;
|
||||||
|
const attack = this.attackTime * this.attackInterval;
|
||||||
|
|
||||||
|
if (time > skill4Release) {
|
||||||
|
this.releaseSkill4();
|
||||||
|
this.skill4Time++;
|
||||||
|
}
|
||||||
|
if (time > skill5Release) {
|
||||||
|
this.releaseSkill5();
|
||||||
|
this.skill5Time++;
|
||||||
|
}
|
||||||
|
if (time > attack) {
|
||||||
|
this.addAttackCircle(500, 0);
|
||||||
|
this.attackTime++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.hp <= 3500) {
|
||||||
|
this.changeStage(TowerBossStage.Dialogue2, time);
|
||||||
|
this.attackTime = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 压缩地形,将地形向内压缩一格
|
||||||
|
*/
|
||||||
|
terrainClose(n: number) {
|
||||||
|
for (let nx = n - 1; nx < 15 - n + 1; nx++) {
|
||||||
|
core.removeBlock(nx, n - 1);
|
||||||
|
core.removeBlock(nx, 15 - n);
|
||||||
|
core.setBgFgBlock('bg', 0, nx, n - 1);
|
||||||
|
core.setBgFgBlock('bg', 0, nx, 15 - n);
|
||||||
|
}
|
||||||
|
for (let ny = n; ny < 15 - n; ny++) {
|
||||||
|
core.removeBlock(n - 1, ny);
|
||||||
|
core.removeBlock(15 - n, ny);
|
||||||
|
core.setBgFgBlock('bg', 0, n - 1, ny);
|
||||||
|
core.setBgFgBlock('bg', 0, 15 - n, ny);
|
||||||
|
}
|
||||||
|
for (let nx = n; nx < 15 - n; nx++) {
|
||||||
|
core.setBlock(527, nx, n);
|
||||||
|
core.setBlock(527, nx, 15 - n - 1);
|
||||||
|
}
|
||||||
|
for (let ny = n + 1; ny < 15 - n - 1; ny++) {
|
||||||
|
core.setBlock(527, n, ny);
|
||||||
|
core.setBlock(527, 15 - n - 1, ny);
|
||||||
|
}
|
||||||
|
core.stopAutomaticRoute();
|
||||||
|
core.setHeroLoc('x', 7);
|
||||||
|
core.setHeroLoc('y', 7);
|
||||||
|
core.setHeroLoc('direction', 'up');
|
||||||
|
core.setBlock(557, 7, n + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private aiDialogue2(time: number, _frame: number) {
|
||||||
|
this.changeStage(TowerBossStage.Stage3, time);
|
||||||
|
this.attackTime = 3;
|
||||||
|
this.terrainClose(1);
|
||||||
|
this.skill6Time = 30;
|
||||||
|
this.skill7Time = 2;
|
||||||
|
core.playBgm('towerBoss3.opus');
|
||||||
|
}
|
||||||
|
|
||||||
|
releaseSkill6(n: number, last: number) {
|
||||||
|
const s = 15 - n * 2;
|
||||||
|
const x = Math.floor(Math.random() * s + n);
|
||||||
|
const y = Math.floor(Math.random() * s + n);
|
||||||
|
const proj = this.createProjectile(BoomProjectile, 0, 0);
|
||||||
|
proj.setData(x, y, last);
|
||||||
|
}
|
||||||
|
|
||||||
|
async releaseSkill7(n: number) {
|
||||||
|
const count = Math.floor(Math.random() * 6 + 3);
|
||||||
|
const nodes: LocArr[] = [];
|
||||||
|
let lastX = -1;
|
||||||
|
let lastY = -1;
|
||||||
|
const s = 15 - n * 2;
|
||||||
|
const used = new Set<number>();
|
||||||
|
let i = 0;
|
||||||
|
while (i < count) {
|
||||||
|
const x = Math.floor(Math.random() * s + n);
|
||||||
|
const y = Math.floor(Math.random() * s + n);
|
||||||
|
const index = x + y * s;
|
||||||
|
if (used.has(index)) continue;
|
||||||
|
i++;
|
||||||
|
used.add(index);
|
||||||
|
nodes.push([x, y]);
|
||||||
|
if (lastX !== -1 && lastY !== -1) {
|
||||||
|
const proj = this.createProjectile(ChainProjectile, 0, 0);
|
||||||
|
proj.hitbox.setPoint1(lastX * 32 + 16, lastY * 32 + 16);
|
||||||
|
proj.hitbox.setPoint2(x * 32 + 16, y * 32 + 16);
|
||||||
|
}
|
||||||
|
lastX = x;
|
||||||
|
lastY = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private aiStage3(time: number, _frame: number) {
|
||||||
|
const skill6Release = this.skill6Time * this.skill6Interval;
|
||||||
|
const skill7Release = this.skill7Time * this.skill7Interval;
|
||||||
|
const attack = this.attackTime * this.attackInterval;
|
||||||
|
|
||||||
|
if (time > skill6Release) {
|
||||||
|
this.releaseSkill6(2, 500);
|
||||||
|
this.skill6Time++;
|
||||||
|
}
|
||||||
|
if (time > skill7Release) {
|
||||||
|
this.releaseSkill7(2);
|
||||||
|
this.skill7Time++;
|
||||||
|
}
|
||||||
|
if (time > attack) {
|
||||||
|
this.addAttackCircle(500, 1);
|
||||||
|
this.attackTime++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.hp <= 2000) {
|
||||||
|
this.changeStage(TowerBossStage.Stage4, time);
|
||||||
|
this.terrainClose(2);
|
||||||
|
this.attackTime = 1;
|
||||||
|
this.skill6Time = 12;
|
||||||
|
this.skill6Interval = 400;
|
||||||
|
this.skill7Time = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private aiStage4(time: number, _frame: number) {
|
||||||
|
const skill6Release = this.skill6Time * this.skill6Interval;
|
||||||
|
const skill7Release = this.skill7Time * this.skill7Interval;
|
||||||
|
const attack = this.attackTime * this.attackInterval;
|
||||||
|
|
||||||
|
if (time > skill6Release) {
|
||||||
|
this.releaseSkill6(3, 500);
|
||||||
|
this.skill6Time++;
|
||||||
|
}
|
||||||
|
if (time > skill7Release) {
|
||||||
|
this.releaseSkill7(3);
|
||||||
|
this.skill7Time++;
|
||||||
|
}
|
||||||
|
if (time > attack) {
|
||||||
|
this.addAttackCircle(500, 2);
|
||||||
|
this.attackTime++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.hp <= 1000) {
|
||||||
|
this.changeStage(TowerBossStage.Stage5, time);
|
||||||
|
this.terrainClose(3);
|
||||||
|
this.attackTime = 1;
|
||||||
|
this.skill6Time = 17;
|
||||||
|
this.skill6Interval = 300;
|
||||||
|
this.skill7Time = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private aiStage5(time: number, _frame: number) {
|
||||||
|
const skill6Release = this.skill6Time * this.skill6Interval;
|
||||||
|
const skill7Release = this.skill7Time * this.skill7Interval;
|
||||||
|
const attack = this.attackTime * this.attackInterval;
|
||||||
|
|
||||||
|
if (time > skill6Release) {
|
||||||
|
this.releaseSkill6(4, 500);
|
||||||
|
this.skill6Time++;
|
||||||
|
}
|
||||||
|
if (time > skill7Release) {
|
||||||
|
this.releaseSkill7(4);
|
||||||
|
this.skill7Time++;
|
||||||
|
}
|
||||||
|
if (time > attack) {
|
||||||
|
this.addAttackCircle(500, 3);
|
||||||
|
this.attackTime++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.hp <= 0) {
|
||||||
|
this.changeStage(TowerBossStage.End, time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private aiEnd(_time: number, _frame: number) {
|
||||||
|
this.end();
|
||||||
|
core.insertAction([
|
||||||
|
{ type: 'openDoor', loc: [13, 6], floorId: 'MT19' },
|
||||||
|
{ type: 'setValue', name: 'flag:boss1', value: 'true' },
|
||||||
|
{ type: 'changeFloor', floorId: 'MT20', loc: [7, 9] },
|
||||||
|
{ type: 'forbidSave' },
|
||||||
|
{ type: 'showStatusBar' }
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TextRenderable {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
blur: number;
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Word extends RenderItem {
|
||||||
|
private ani: Animation = new Animation();
|
||||||
|
|
||||||
|
/** 当前正在显示的文字 */
|
||||||
|
private showing: string = '';
|
||||||
|
/** 文字显示时间间隔 */
|
||||||
|
private showInterval: number = 100;
|
||||||
|
/** 文字显示的虚化时长 */
|
||||||
|
private showBlurTime: number = 200;
|
||||||
|
/** 显示开始时刻 */
|
||||||
|
private showStartTime: number = 0;
|
||||||
|
|
||||||
|
/** 最大虚化程度 */
|
||||||
|
private readonly MAX_BLUR = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化
|
||||||
|
*/
|
||||||
|
init() {
|
||||||
|
this.size(480, 24);
|
||||||
|
this.setHD(true);
|
||||||
|
this.setZIndex(95);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 降下背景
|
||||||
|
*/
|
||||||
|
curtainDown() {
|
||||||
|
this.delegateTicker(() => {
|
||||||
|
this.pos(this.ani.x, this.ani.y);
|
||||||
|
}, 700);
|
||||||
|
this.ani.time(600).mode(hyper('sin', 'out')).absolute().move(0, 24);
|
||||||
|
return sleep(700);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 升起背景
|
||||||
|
*/
|
||||||
|
curtainUp() {
|
||||||
|
this.delegateTicker(() => {
|
||||||
|
this.pos(this.ani.x, this.ani.y);
|
||||||
|
}, 700);
|
||||||
|
this.ani.time(600).mode(hyper('sin', 'out')).absolute().move(0, 0);
|
||||||
|
return sleep(700);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示文字,会将之前的文字取消显示
|
||||||
|
* @param text 要显示的文字
|
||||||
|
*/
|
||||||
|
showText(text: string) {
|
||||||
|
this.showStartTime = Date.now();
|
||||||
|
this.showing = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置文字显示的参数
|
||||||
|
* @param interval 文字显示时间间隔
|
||||||
|
* @param blurTime 文字显示虚化时长
|
||||||
|
*/
|
||||||
|
setParam(interval: number, blurTime: number) {
|
||||||
|
this.showInterval = interval;
|
||||||
|
this.showBlurTime = blurTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getTextRenerable() {
|
||||||
|
const dt = Date.now() - this.showStartTime;
|
||||||
|
const res: TextRenderable[] = [];
|
||||||
|
|
||||||
|
[...this.showing].forEach((v, i) => {
|
||||||
|
const showStartTime = i * this.showInterval;
|
||||||
|
const blurRatio = (dt - showStartTime) / this.showBlurTime;
|
||||||
|
let blur = blurRatio * this.MAX_BLUR;
|
||||||
|
if (blur < 0) blur = 0;
|
||||||
|
else if (blur > this.MAX_BLUR) blur = this.MAX_BLUR;
|
||||||
|
|
||||||
|
const obj: TextRenderable = {
|
||||||
|
blur,
|
||||||
|
x: i * 18,
|
||||||
|
y: 12,
|
||||||
|
text: v
|
||||||
|
};
|
||||||
|
res.push(obj);
|
||||||
|
});
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(canvas: MotaOffscreenCanvas2D): void {
|
||||||
|
const data = this.getTextRenerable();
|
||||||
|
const ctx = canvas.ctx;
|
||||||
|
ctx.font = '18px "normal"';
|
||||||
|
ctx.textAlign = 'left';
|
||||||
|
ctx.textBaseline = 'middle';
|
||||||
|
|
||||||
|
for (const { blur, x, y, text } of data) {
|
||||||
|
if (blur !== 0) {
|
||||||
|
ctx.filter = `blur(${blur}px)`;
|
||||||
|
} else {
|
||||||
|
ctx.filter = 'none';
|
||||||
|
}
|
||||||
|
ctx.fillText(text, x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class HealthBar extends RenderItem {
|
||||||
|
private trans: Transition = new Transition();
|
||||||
|
/** 当前血条状态 */
|
||||||
|
private status: HealthBarStatus = HealthBarStatus.Start;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化
|
||||||
|
*/
|
||||||
|
init() {
|
||||||
|
this.trans.time(2000).absolute().mode(power(3, 'out'));
|
||||||
|
this.trans.value.hp = 0;
|
||||||
|
this.trans.value.x = 0;
|
||||||
|
this.trans.value.y = -16;
|
||||||
|
|
||||||
|
this.size(480, 16);
|
||||||
|
this.setHD(true);
|
||||||
|
this.setZIndex(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置剩余血量
|
||||||
|
*/
|
||||||
|
set(value: number) {
|
||||||
|
this.trans.time(2000).mode(power(3, 'out')).transition('hp', value);
|
||||||
|
this.delegateTicker(() => {
|
||||||
|
this.update();
|
||||||
|
}, 2500);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 展示开始动画
|
||||||
|
*/
|
||||||
|
async showStart() {
|
||||||
|
if (this.status !== HealthBarStatus.Start) return;
|
||||||
|
this.delegateTicker(() => {
|
||||||
|
this.update();
|
||||||
|
}, 2500);
|
||||||
|
this.trans
|
||||||
|
.time(600)
|
||||||
|
.mode(hyper('sin', 'out'))
|
||||||
|
.absolute()
|
||||||
|
.transition('y', 0);
|
||||||
|
this.trans.time(2000).mode(power(3, 'out')).transition('hp', 10000);
|
||||||
|
await sleep(1700);
|
||||||
|
this.status = HealthBarStatus.Running;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 展示结束动画
|
||||||
|
*/
|
||||||
|
async showEnd() {
|
||||||
|
if (this.status !== HealthBarStatus.Running) return;
|
||||||
|
this.delegateTicker(() => {
|
||||||
|
this.update();
|
||||||
|
}, 2500);
|
||||||
|
this.trans
|
||||||
|
.time(600)
|
||||||
|
.mode(hyper('sin', 'in'))
|
||||||
|
.absolute()
|
||||||
|
.transition('y', -16);
|
||||||
|
await sleep(700);
|
||||||
|
this.status = HealthBarStatus.End;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(canvas: MotaOffscreenCanvas2D): void {
|
||||||
|
const ctx = canvas.ctx;
|
||||||
|
|
||||||
|
const hp = this.trans.value.hp;
|
||||||
|
const ratio = hp / 10000;
|
||||||
|
const r = Math.min(255 * 2 - ratio * 2 * 255, 255);
|
||||||
|
const g = Math.min(ratio * 2 * 255, 255);
|
||||||
|
|
||||||
|
ctx.save();
|
||||||
|
ctx.translate(this.trans.value.x, this.trans.value.y);
|
||||||
|
ctx.fillStyle = '#bbb';
|
||||||
|
ctx.fillRect(2, 2, 480 - 4, 16 - 4);
|
||||||
|
|
||||||
|
const color = `rgb(${Math.floor(r)},${Math.floor(g)},0)`;
|
||||||
|
ctx.fillStyle = color;
|
||||||
|
ctx.fillRect(2, 2, (480 - 4) * ratio, 16 - 4);
|
||||||
|
|
||||||
|
ctx.font = '12px "normal"';
|
||||||
|
ctx.textBaseline = 'middle';
|
||||||
|
ctx.textAlign = 'right';
|
||||||
|
ctx.fillStyle = '#fff';
|
||||||
|
ctx.strokeStyle = '#000';
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.strokeText(`${Math.floor(hp)} / 10000`, 472, 8);
|
||||||
|
ctx.fillText(`${Math.floor(hp)} / 10000`, 472, 8);
|
||||||
|
|
||||||
|
ctx.lineWidth = 4;
|
||||||
|
ctx.strokeStyle = '#fff';
|
||||||
|
ctx.shadowBlur = 4;
|
||||||
|
ctx.shadowColor = '#000';
|
||||||
|
ctx.strokeRect(0, 0, 480, 16);
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,986 @@
|
|||||||
|
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 '@user/data-state';
|
||||||
|
import { PointEffect, PointEffectType } from '../fx/pointShader';
|
||||||
|
import { isNil } from 'lodash-es';
|
||||||
|
import { mainRenderer } from '@user/client-modules';
|
||||||
|
|
||||||
|
export const enum ProjectileDirection {
|
||||||
|
Vertical,
|
||||||
|
Horizontal,
|
||||||
|
|
||||||
|
LeftToRight,
|
||||||
|
RightToLeft,
|
||||||
|
TopToBottom,
|
||||||
|
BottomToTop
|
||||||
|
}
|
||||||
|
|
||||||
|
function popDamage(damage: number, boss: TowerBoss, color: string) {
|
||||||
|
const { x, y } = core.status.hero.loc;
|
||||||
|
boss.pop.addPop(
|
||||||
|
(-damage).toString(),
|
||||||
|
1000,
|
||||||
|
x * 32 + 16,
|
||||||
|
y * 32 + 16,
|
||||||
|
color
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AttackProjectile extends Projectile<TowerBoss> {
|
||||||
|
static easeIn?: TimingFn;
|
||||||
|
static easeOut?: TimingFn;
|
||||||
|
|
||||||
|
damage: number = 500;
|
||||||
|
hitbox: Hitbox.Rect = new Hitbox.Rect(0, 0, 32, 32);
|
||||||
|
|
||||||
|
static init() {
|
||||||
|
this.easeIn = hyper('sin', 'out');
|
||||||
|
this.easeOut = hyper('sin', 'in');
|
||||||
|
}
|
||||||
|
|
||||||
|
static end() {
|
||||||
|
this.easeIn = void 0;
|
||||||
|
this.easeOut = void 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
isIntersect(hitbox: Hitbox.HitboxType): boolean {
|
||||||
|
if (hitbox instanceof Hitbox.Rect) {
|
||||||
|
return Hitbox.checkRectRect(this.hitbox, hitbox);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateHitbox(x: number, y: number): void {
|
||||||
|
this.hitbox.setPosition(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
doDamage(target: IStateDamageable): boolean {
|
||||||
|
this.boss.attackBoss(this.damage);
|
||||||
|
this.destroy();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ai(boss: TowerBoss, time: number, frame: number): void {
|
||||||
|
if (time > 4000) {
|
||||||
|
this.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render(canvas: MotaOffscreenCanvas2D, transform: Transform): void {
|
||||||
|
const progress = this.time / 4000;
|
||||||
|
let alpha = 1;
|
||||||
|
let offset = 0;
|
||||||
|
if (progress < 0.1) {
|
||||||
|
alpha = progress * 10;
|
||||||
|
offset = 24 * AttackProjectile.easeIn!(10 * (0.1 - progress));
|
||||||
|
} else if (progress > 0.9) {
|
||||||
|
alpha = 10 * (1 - progress);
|
||||||
|
offset = 24 * AttackProjectile.easeOut!(10 * (progress - 0.9));
|
||||||
|
} else {
|
||||||
|
alpha = 1;
|
||||||
|
offset = 0;
|
||||||
|
}
|
||||||
|
const ctx = canvas.ctx;
|
||||||
|
ctx.save();
|
||||||
|
ctx.strokeStyle = '#ffe229';
|
||||||
|
ctx.fillStyle = '#ffe229';
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.globalAlpha = alpha;
|
||||||
|
ctx.beginPath();
|
||||||
|
const o = offset + 16;
|
||||||
|
const cx = this.x + 16;
|
||||||
|
const cy = this.y + 16;
|
||||||
|
ctx.arc(cx, cy, 2, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(cx, cy, o, 0, Math.PI * 2);
|
||||||
|
ctx.moveTo(cx + o, cy);
|
||||||
|
ctx.lineTo(cx + o + 16, cy);
|
||||||
|
ctx.moveTo(cx, cy + o);
|
||||||
|
ctx.lineTo(cx, cy + o + 16);
|
||||||
|
ctx.moveTo(cx - o, cy);
|
||||||
|
ctx.lineTo(cx - o - 16, cy);
|
||||||
|
ctx.moveTo(cx, cy - o);
|
||||||
|
ctx.lineTo(cx, cy - o - 16);
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ArrowProjectile extends Projectile<TowerBoss> {
|
||||||
|
static easing?: TimingFn;
|
||||||
|
static dangerEasing?: TimingFn;
|
||||||
|
|
||||||
|
static horizontal: MotaOffscreenCanvas2D | null = null;
|
||||||
|
static vertical: MotaOffscreenCanvas2D | null = null;
|
||||||
|
|
||||||
|
hitbox: Hitbox.Rect = new Hitbox.Rect(0, 0, 102, 32);
|
||||||
|
damage: number = 1000;
|
||||||
|
|
||||||
|
/** 弹幕的方向 */
|
||||||
|
direction: ProjectileDirection = ProjectileDirection.Horizontal;
|
||||||
|
|
||||||
|
private damaged: boolean = false;
|
||||||
|
private sounded: boolean = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* boss战开始时初始化
|
||||||
|
*/
|
||||||
|
static init() {
|
||||||
|
this.easing = power(2, 'in');
|
||||||
|
this.dangerEasing = power(3, 'out');
|
||||||
|
this.horizontal = mainRenderer.requireCanvas();
|
||||||
|
this.vertical = mainRenderer.requireCanvas();
|
||||||
|
const hor = this.horizontal;
|
||||||
|
hor.size(480, 32);
|
||||||
|
hor.setHD(true);
|
||||||
|
const ctxHor = hor.ctx;
|
||||||
|
ctxHor.fillStyle = '#f00';
|
||||||
|
ctxHor.globalAlpha = 0.6;
|
||||||
|
for (let i = 0; i < 15; i++) {
|
||||||
|
ctxHor.fillRect(i * 32 + 2, 2, 28, 28);
|
||||||
|
}
|
||||||
|
const ver = this.vertical;
|
||||||
|
ver.size(32, 480);
|
||||||
|
ver.setHD(true);
|
||||||
|
const ctxVer = ver.ctx;
|
||||||
|
ctxVer.fillStyle = '#f00';
|
||||||
|
ctxVer.globalAlpha = 0.6;
|
||||||
|
for (let i = 0; i < 15; i++) {
|
||||||
|
ctxVer.fillRect(2, i * 32 + 2, 28, 28);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* boss战结束后清理
|
||||||
|
*/
|
||||||
|
static end() {
|
||||||
|
this.easing = void 0;
|
||||||
|
this.dangerEasing = void 0;
|
||||||
|
this.horizontal?.clear();
|
||||||
|
if (this.horizontal) mainRenderer.deleteCanvas(this.horizontal);
|
||||||
|
this.horizontal = null;
|
||||||
|
this.vertical?.clear();
|
||||||
|
if (this.vertical) mainRenderer.deleteCanvas(this.vertical);
|
||||||
|
this.vertical = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置弹幕的数据
|
||||||
|
* @param direction 弹幕的方向
|
||||||
|
*/
|
||||||
|
setData(direction: ProjectileDirection) {
|
||||||
|
this.direction = direction;
|
||||||
|
if (direction === ProjectileDirection.Horizontal) {
|
||||||
|
this.hitbox.setSize(102, 32);
|
||||||
|
} else {
|
||||||
|
this.hitbox.setSize(32, 102);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isIntersect(hitbox: Hitbox.HitboxType): boolean {
|
||||||
|
if (hitbox instanceof Hitbox.Rect) {
|
||||||
|
return Hitbox.checkRectRect(hitbox, this.hitbox);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateHitbox(x: number, y: number): void {
|
||||||
|
this.hitbox.setPosition(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
doDamage(target: IStateDamageable): boolean {
|
||||||
|
if (this.damaged) return false;
|
||||||
|
target.hp -= this.damage;
|
||||||
|
this.damaged = true;
|
||||||
|
core.drawHeroAnimate('hand');
|
||||||
|
popDamage(this.damage, this.boss, '#ff8180');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ai(boss: TowerBoss, time: number, frame: number): void {
|
||||||
|
if (time > 3000) {
|
||||||
|
if (!this.sounded) {
|
||||||
|
core.playSound('arrow.opus');
|
||||||
|
this.sounded = true;
|
||||||
|
}
|
||||||
|
const progress = (time - 3000) / 2000;
|
||||||
|
const res = ArrowProjectile.easing!(progress);
|
||||||
|
const dx = res * 640;
|
||||||
|
const x = 480 - 32 - dx;
|
||||||
|
if (this.direction === ProjectileDirection.Horizontal) {
|
||||||
|
this.setPosition(x, this.y);
|
||||||
|
} else {
|
||||||
|
this.setPosition(this.x, x);
|
||||||
|
}
|
||||||
|
} else if (time > 5000) {
|
||||||
|
this.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render(canvas: MotaOffscreenCanvas2D, transform: Transform): void {
|
||||||
|
const ctx = canvas.ctx;
|
||||||
|
ctx.globalAlpha = 1;
|
||||||
|
const ratio = devicePixelRatio * core.domStyle.scale;
|
||||||
|
const cell = 32 * ratio;
|
||||||
|
ctx.save();
|
||||||
|
|
||||||
|
if (this.time < 3000) {
|
||||||
|
let begin = 1;
|
||||||
|
if (this.time < 2000) {
|
||||||
|
begin = ArrowProjectile.dangerEasing!(this.time / 2000);
|
||||||
|
}
|
||||||
|
const len = begin * 13 * 32;
|
||||||
|
const fl = len * ratio;
|
||||||
|
const x1 = 480 - 32 - len;
|
||||||
|
const fx1 = x1 * ratio;
|
||||||
|
|
||||||
|
if (this.direction === ProjectileDirection.Horizontal) {
|
||||||
|
const canvas = ArrowProjectile.horizontal!.canvas;
|
||||||
|
ctx.drawImage(canvas, fx1, 0, fl, cell, x1, this.y, len, 32);
|
||||||
|
} else {
|
||||||
|
const canvas = ArrowProjectile.vertical!.canvas;
|
||||||
|
ctx.drawImage(canvas, 0, fx1, cell, fl, this.x, x1, 32, len);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.direction === ProjectileDirection.Horizontal) {
|
||||||
|
const len = Math.max(this.x - 32, 0);
|
||||||
|
const fl = len * ratio;
|
||||||
|
const canvas = ArrowProjectile.horizontal!.canvas;
|
||||||
|
ctx.drawImage(canvas, cell, 0, fl, cell, 32, this.y, len, 32);
|
||||||
|
} else {
|
||||||
|
const len = Math.max(this.y - 32, 0);
|
||||||
|
const fl = len * ratio;
|
||||||
|
const canvas = ArrowProjectile.vertical!.canvas;
|
||||||
|
ctx.drawImage(canvas, 0, cell, cell, fl, this.x, 32, 32, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const img = core.material.images.images['arrow.png'];
|
||||||
|
if (this.direction === ProjectileDirection.Vertical) {
|
||||||
|
ctx.translate(this.x + 32, this.y);
|
||||||
|
ctx.rotate(Math.PI / 2);
|
||||||
|
ctx.drawImage(img, 0, 0, 102, 32);
|
||||||
|
} else {
|
||||||
|
ctx.drawImage(img, this.x, this.y, 102, 32);
|
||||||
|
}
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PortalProjectile extends Projectile<TowerBoss> {
|
||||||
|
static easing?: TimingFn;
|
||||||
|
|
||||||
|
damage: number = 0;
|
||||||
|
hitbox: Hitbox.Circle = new Hitbox.Circle(0, 0, 0);
|
||||||
|
|
||||||
|
/** 传送目标位置 */
|
||||||
|
private tx: number = 0;
|
||||||
|
/** 传送目标位置 */
|
||||||
|
private ty: number = 0;
|
||||||
|
/** 是否已经传送过 */
|
||||||
|
private transfered: boolean = false;
|
||||||
|
|
||||||
|
private effect?: PointEffect;
|
||||||
|
private effectId1?: number;
|
||||||
|
private effectId2?: number;
|
||||||
|
|
||||||
|
static init() {
|
||||||
|
this.easing = hyper('sin', 'out');
|
||||||
|
}
|
||||||
|
|
||||||
|
static end() {
|
||||||
|
this.easing = void 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
createEffect(effect: PointEffect) {
|
||||||
|
this.effect = effect;
|
||||||
|
const id1 = effect.addEffect(
|
||||||
|
PointEffectType.CircleWarpTangetial,
|
||||||
|
Date.now(),
|
||||||
|
4000,
|
||||||
|
[this.tx * 32 + 16, this.ty * 32 + 16, 0, 32]
|
||||||
|
);
|
||||||
|
const id2 = effect.addEffect(
|
||||||
|
PointEffectType.CircleContrast,
|
||||||
|
Date.now(),
|
||||||
|
4000,
|
||||||
|
[this.tx * 32 + 16, this.ty * 32 + 16, 32, 24]
|
||||||
|
);
|
||||||
|
this.effectId1 = id1;
|
||||||
|
this.effectId2 = id2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置传送目标位置
|
||||||
|
*/
|
||||||
|
setTarget(x: number, y: number) {
|
||||||
|
this.tx = x;
|
||||||
|
this.ty = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
isIntersect(hitbox: Hitbox.HitboxType): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateHitbox(x: number, y: number): void {
|
||||||
|
this.hitbox.setCenter(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
doDamage(target: IStateDamageable): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ai(boss: TowerBoss, time: number, frame: number): void {
|
||||||
|
if (!this.transfered && time > 2000) {
|
||||||
|
this.transfered = true;
|
||||||
|
core.setHeroLoc('x', this.tx);
|
||||||
|
core.setHeroLoc('y', this.ty);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (time > 4000) {
|
||||||
|
this.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render(canvas: MotaOffscreenCanvas2D, transform: Transform): void {
|
||||||
|
const effect = this.effect;
|
||||||
|
const id1 = this.effectId1;
|
||||||
|
const id2 = this.effectId2;
|
||||||
|
if (!effect || isNil(id1) || isNil(id2)) return;
|
||||||
|
const time = this.time;
|
||||||
|
const max = Math.PI * 2;
|
||||||
|
if (time < 2000) {
|
||||||
|
const progress = PortalProjectile.easing!(time / 2000);
|
||||||
|
const ratio = Math.min(progress * 3, 1);
|
||||||
|
effect.setEffect(id1, void 0, [0, max * progress, 0, 0]);
|
||||||
|
effect.setEffect(id2, void 0, [ratio, 0, 0, 0]);
|
||||||
|
} else {
|
||||||
|
const progress = PortalProjectile.easing!((time - 2000) / 2000);
|
||||||
|
const ratio = Math.min((1 - progress) * 3, 1);
|
||||||
|
effect.setEffect(id1, void 0, [max * progress, max, 0, 0]);
|
||||||
|
effect.setEffect(id2, void 0, [ratio, 0, 0, 0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class IceProjectile extends Projectile<TowerBoss> {
|
||||||
|
damage: number = 5000;
|
||||||
|
hitbox: Hitbox.Rect = new Hitbox.Rect(0, 0, 32, 32);
|
||||||
|
|
||||||
|
private damaged: boolean = false;
|
||||||
|
/** 是否已经播放冰冻动画 */
|
||||||
|
private animated: boolean = false;
|
||||||
|
/** 是否已经转换成滑冰图块 */
|
||||||
|
private converted: boolean = false;
|
||||||
|
|
||||||
|
private bx: number = 0;
|
||||||
|
private by: number = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置这个寒冰弹幕的攻击位置
|
||||||
|
*/
|
||||||
|
setPos(x: number, y: number) {
|
||||||
|
this.bx = x;
|
||||||
|
this.by = y;
|
||||||
|
this.updateHitbox(x * 32, y * 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
isIntersect(hitbox: Hitbox.HitboxType): boolean {
|
||||||
|
if (this.damaged) return false;
|
||||||
|
if (this.time < 2000) return false;
|
||||||
|
if (hitbox instanceof Hitbox.Rect) {
|
||||||
|
return Hitbox.checkRectRect(hitbox, this.hitbox);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateHitbox(x: number, y: number): void {
|
||||||
|
this.hitbox.setPosition(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
doDamage(target: IStateDamageable): boolean {
|
||||||
|
if (!this.damaged) return false;
|
||||||
|
target.hp -= this.damage;
|
||||||
|
this.damaged = true;
|
||||||
|
popDamage(this.damage, this.boss, '#6bf8ff');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ai(boss: TowerBoss, time: number, frame: number): void {
|
||||||
|
if (!this.converted && time > 2000) {
|
||||||
|
this.converted = true;
|
||||||
|
core.setBgFgBlock('bg', 167, this.bx, this.by);
|
||||||
|
}
|
||||||
|
if (time > 4000) {
|
||||||
|
core.setBgFgBlock('bg', 526, this.bx, this.by);
|
||||||
|
this.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render(canvas: MotaOffscreenCanvas2D, transform: Transform): void {
|
||||||
|
const ctx = canvas.ctx;
|
||||||
|
if (this.time < 2000) {
|
||||||
|
ctx.fillStyle = 'rgb(150,150,255)';
|
||||||
|
ctx.globalAlpha = 0.6;
|
||||||
|
ctx.fillRect(this.x + 2, this.y + 2, 28, 28);
|
||||||
|
} else {
|
||||||
|
if (!this.animated) {
|
||||||
|
this.animated = true;
|
||||||
|
core.drawAnimate('ice', this.bx, this.by);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ThunderProjectile extends Projectile<TowerBoss> {
|
||||||
|
/** 闪电缓存画布 */
|
||||||
|
static cache: MotaOffscreenCanvas2D | null = null;
|
||||||
|
|
||||||
|
damage: number = 0;
|
||||||
|
hitbox: Hitbox.Rect = new Hitbox.Rect(0, 0, 96, 96);
|
||||||
|
|
||||||
|
private bx: number = 0;
|
||||||
|
private by: number = 0;
|
||||||
|
/** 闪电的强度 */
|
||||||
|
private power: number = 0;
|
||||||
|
private damaged: boolean = false;
|
||||||
|
private cached: boolean = false;
|
||||||
|
private sounded: boolean = false;
|
||||||
|
|
||||||
|
private effect?: PointEffect;
|
||||||
|
private effectId1?: number;
|
||||||
|
private effectId2?: number;
|
||||||
|
|
||||||
|
static init() {
|
||||||
|
this.cache = mainRenderer.requireCanvas();
|
||||||
|
this.cache.setHD(true);
|
||||||
|
this.cache.size(480, 480);
|
||||||
|
}
|
||||||
|
|
||||||
|
static end() {
|
||||||
|
this.cache?.clear();
|
||||||
|
if (this.cache) mainRenderer.deleteCanvas(this.cache);
|
||||||
|
this.cache = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建着色器特效
|
||||||
|
*/
|
||||||
|
createEffect(effect: PointEffect) {
|
||||||
|
this.effect = effect;
|
||||||
|
this.effectId1 = effect.addEffect(
|
||||||
|
PointEffectType.CircleBrightness,
|
||||||
|
Date.now() + 1000,
|
||||||
|
600,
|
||||||
|
[this.bx * 32 + 16, this.by * 32 + 16, 128, 32],
|
||||||
|
[1, 0, 0, 0]
|
||||||
|
);
|
||||||
|
this.effectId2 = effect.addEffect(
|
||||||
|
PointEffectType.CircleWarp,
|
||||||
|
Date.now() + 1000,
|
||||||
|
600,
|
||||||
|
[this.bx * 32 + 16, this.by * 32 + 16, 240 + this.power * 32, 32],
|
||||||
|
[0.1, 6, 0.8, 0],
|
||||||
|
[0, Math.PI, 0, 0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置闪电的信息
|
||||||
|
*/
|
||||||
|
setData(x: number, y: number, power: number) {
|
||||||
|
this.bx = x;
|
||||||
|
this.by = y;
|
||||||
|
this.power = power;
|
||||||
|
this.damage = power * 3000;
|
||||||
|
this.updateHitbox(x * 32 - 32, y * 32 - 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
isIntersect(hitbox: Hitbox.HitboxType): boolean {
|
||||||
|
if (this.damaged) return false;
|
||||||
|
if (this.time < 1000) return false;
|
||||||
|
if (hitbox instanceof Hitbox.Rect) {
|
||||||
|
return Hitbox.checkRectRect(hitbox, this.hitbox);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateHitbox(x: number, y: number): void {
|
||||||
|
this.hitbox.setPosition(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
doDamage(target: IStateDamageable): boolean {
|
||||||
|
if (this.damaged) return false;
|
||||||
|
this.damaged = true;
|
||||||
|
target.hp -= this.damage;
|
||||||
|
popDamage(this.damage, this.boss, '#cfe6fc');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ai(boss: TowerBoss, time: number, frame: number): void {
|
||||||
|
if (time > 500) {
|
||||||
|
if (!this.sounded) {
|
||||||
|
core.playSound('thunder.opus');
|
||||||
|
this.sounded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (time > 2000) {
|
||||||
|
this.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render(canvas: MotaOffscreenCanvas2D, transform: Transform): void {
|
||||||
|
const ctx = canvas.ctx;
|
||||||
|
if (this.time < 1000) {
|
||||||
|
ctx.fillStyle = '#fff';
|
||||||
|
ctx.globalAlpha = 0.6;
|
||||||
|
for (let dx = -1; dx < 2; dx++) {
|
||||||
|
for (let dy = -1; dy < 2; dy++) {
|
||||||
|
const x = (this.bx + dx) * 32 + 2;
|
||||||
|
const y = (this.by + dy) * 32 + 2;
|
||||||
|
ctx.fillRect(x, y, 28, 28);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!this.cached) this.cacheThunder();
|
||||||
|
if (!ThunderProjectile.cache) return;
|
||||||
|
const progress = (this.time - 1000) / 1000;
|
||||||
|
const effect = this.effect;
|
||||||
|
const id = this.effectId1;
|
||||||
|
if (!effect || isNil(id)) return;
|
||||||
|
if (progress < 0.6) {
|
||||||
|
const x = this.bx * 32 + 16;
|
||||||
|
const y = this.by * 32 + 16;
|
||||||
|
effect.setEffect(
|
||||||
|
id,
|
||||||
|
[x, y, 32 + progress * 256, 32],
|
||||||
|
[(0.6 - progress) / 0.6, 0, 0, 0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (progress < 0.5) {
|
||||||
|
ctx.globalAlpha = 1;
|
||||||
|
} else {
|
||||||
|
ctx.globalAlpha = 1 - (progress - 0.5) * 2;
|
||||||
|
}
|
||||||
|
ctx.drawImage(ThunderProjectile.cache.canvas, 0, 0, 480, 480);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private cacheThunder() {
|
||||||
|
const cache = ThunderProjectile.cache;
|
||||||
|
if (!cache) return;
|
||||||
|
this.cached = true;
|
||||||
|
cache.clear();
|
||||||
|
const ctx = cache.ctx;
|
||||||
|
ctx.beginPath();
|
||||||
|
for (let i = 0; i < this.power; i++) {
|
||||||
|
let x = this.bx * 32 + 16;
|
||||||
|
let y = this.by * 32 + 16;
|
||||||
|
ctx.moveTo(x, y);
|
||||||
|
while (y > 0) {
|
||||||
|
x += Math.floor(Math.random() * 30 - 15);
|
||||||
|
y -= Math.floor(Math.random() * 80);
|
||||||
|
ctx.lineTo(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.shadowBlur = 5;
|
||||||
|
ctx.shadowColor = '#62c8f4';
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.globalAlpha = 0.6;
|
||||||
|
ctx.strokeStyle = '#fff';
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ThunderBallProjectile extends Projectile<TowerBoss> {
|
||||||
|
static dangerEasing?: TimingFn;
|
||||||
|
|
||||||
|
static horizontal: MotaOffscreenCanvas2D | null = null;
|
||||||
|
static vertical: MotaOffscreenCanvas2D | null = null;
|
||||||
|
|
||||||
|
damage: number = 3000;
|
||||||
|
hitbox: Hitbox.Rect = new Hitbox.Rect(0, 0, 16, 16);
|
||||||
|
|
||||||
|
private direction: ProjectileDirection = ProjectileDirection.BottomToTop;
|
||||||
|
private cx: number = 0;
|
||||||
|
private cy: number = 0;
|
||||||
|
private damaged: boolean = false;
|
||||||
|
private sounded: boolean = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* boss战开始时初始化
|
||||||
|
*/
|
||||||
|
static init() {
|
||||||
|
this.dangerEasing = power(3, 'out');
|
||||||
|
this.horizontal = mainRenderer.requireCanvas();
|
||||||
|
this.vertical = mainRenderer.requireCanvas();
|
||||||
|
const hor = this.horizontal;
|
||||||
|
hor.size(480, 32);
|
||||||
|
hor.setHD(true);
|
||||||
|
const ctxHor = hor.ctx;
|
||||||
|
ctxHor.fillStyle = '#fff';
|
||||||
|
ctxHor.globalAlpha = 0.6;
|
||||||
|
for (let i = 0; i < 15; i++) {
|
||||||
|
ctxHor.fillRect(i * 32 + 2, 2, 28, 28);
|
||||||
|
}
|
||||||
|
const ver = this.vertical;
|
||||||
|
ver.size(32, 480);
|
||||||
|
ver.setHD(true);
|
||||||
|
const ctxVer = ver.ctx;
|
||||||
|
ctxVer.fillStyle = '#fff';
|
||||||
|
ctxVer.globalAlpha = 0.6;
|
||||||
|
for (let i = 0; i < 15; i++) {
|
||||||
|
ctxVer.fillRect(2, i * 32 + 2, 28, 28);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* boss战结束后清理
|
||||||
|
*/
|
||||||
|
static end() {
|
||||||
|
this.dangerEasing = void 0;
|
||||||
|
this.horizontal?.clear();
|
||||||
|
if (this.horizontal) mainRenderer.deleteCanvas(this.horizontal);
|
||||||
|
this.horizontal = null;
|
||||||
|
this.vertical?.clear();
|
||||||
|
if (this.vertical) mainRenderer.deleteCanvas(this.vertical);
|
||||||
|
this.vertical = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
setData(direction: ProjectileDirection, cx: number, cy: number) {
|
||||||
|
this.cx = cx;
|
||||||
|
this.cy = cy;
|
||||||
|
this.direction = direction;
|
||||||
|
this.setPosition(cx * 32 + 16, cy * 32 + 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
isIntersect(hitbox: Hitbox.HitboxType): boolean {
|
||||||
|
if (this.damaged) return false;
|
||||||
|
if (this.time < 3000) return false;
|
||||||
|
if (hitbox instanceof Hitbox.Rect) {
|
||||||
|
return Hitbox.checkRectRect(this.hitbox, hitbox);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateHitbox(x: number, y: number): void {
|
||||||
|
this.hitbox.setPosition(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
doDamage(target: IStateDamageable): boolean {
|
||||||
|
if (this.damaged) return false;
|
||||||
|
this.damaged = true;
|
||||||
|
target.hp -= this.damage;
|
||||||
|
core.playSound('electron.opus');
|
||||||
|
popDamage(this.damage, this.boss, '#cfe6fc');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ai(boss: TowerBoss, time: number, frame: number): void {
|
||||||
|
if (time > 3000) {
|
||||||
|
if (!this.sounded) {
|
||||||
|
core.playSound('electron.opus');
|
||||||
|
this.sounded = true;
|
||||||
|
}
|
||||||
|
const dt = time - 3000;
|
||||||
|
const dis = dt * 0.2;
|
||||||
|
const cx = this.cx * 32 + 16;
|
||||||
|
const cy = this.cy * 32 + 16;
|
||||||
|
|
||||||
|
switch (this.direction) {
|
||||||
|
case ProjectileDirection.BottomToTop:
|
||||||
|
this.setPosition(cx, cy - dis);
|
||||||
|
break;
|
||||||
|
case ProjectileDirection.LeftToRight:
|
||||||
|
this.setPosition(cx + dis, cy);
|
||||||
|
break;
|
||||||
|
case ProjectileDirection.RightToLeft:
|
||||||
|
this.setPosition(cx - dis, cy);
|
||||||
|
break;
|
||||||
|
case ProjectileDirection.TopToBottom:
|
||||||
|
this.setPosition(cx, cy + dis);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.x < -16 || this.x > 496 || this.y < -16 || this.y > 496) {
|
||||||
|
this.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render(canvas: MotaOffscreenCanvas2D, transform: Transform): void {
|
||||||
|
const ctx = canvas.ctx;
|
||||||
|
const cx = this.cx * 32 + 16;
|
||||||
|
const cy = this.cy * 32 + 16;
|
||||||
|
let left = 0;
|
||||||
|
let right = 0;
|
||||||
|
let top = 0;
|
||||||
|
let bottom = 0;
|
||||||
|
if (this.time < 3000) {
|
||||||
|
let begin = 1;
|
||||||
|
if (this.time < 2000) {
|
||||||
|
begin = ArrowProjectile.dangerEasing!(this.time / 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (this.direction) {
|
||||||
|
case ProjectileDirection.BottomToTop: {
|
||||||
|
const height = (cy - 48) * begin;
|
||||||
|
left = cx - 16;
|
||||||
|
right = cx + 16;
|
||||||
|
bottom = cy + 16;
|
||||||
|
top = cy - height - 16;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ProjectileDirection.LeftToRight: {
|
||||||
|
const width = (432 - cx) * begin;
|
||||||
|
left = cx - 16;
|
||||||
|
right = cx + 16 + width;
|
||||||
|
bottom = cy + 16;
|
||||||
|
top = cy - 16;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ProjectileDirection.RightToLeft: {
|
||||||
|
const width = (cx - 48) * begin;
|
||||||
|
left = cx - width - 16;
|
||||||
|
right = cx + 16;
|
||||||
|
bottom = cy + 16;
|
||||||
|
top = cy - 16;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ProjectileDirection.TopToBottom: {
|
||||||
|
const height = (432 - cy) * begin;
|
||||||
|
left = cx - 16;
|
||||||
|
right = cx + 16;
|
||||||
|
bottom = cy + 16 + height;
|
||||||
|
top = cy + 16;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (this.direction) {
|
||||||
|
case ProjectileDirection.BottomToTop: {
|
||||||
|
left = cx - 16;
|
||||||
|
right = cx + 16;
|
||||||
|
bottom = this.y;
|
||||||
|
top = 32;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ProjectileDirection.LeftToRight: {
|
||||||
|
left = this.x;
|
||||||
|
right = 448;
|
||||||
|
bottom = cy + 16;
|
||||||
|
top = cy - 16;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ProjectileDirection.RightToLeft: {
|
||||||
|
left = 32;
|
||||||
|
right = this.x;
|
||||||
|
bottom = cy + 16;
|
||||||
|
top = cy - 16;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ProjectileDirection.TopToBottom: {
|
||||||
|
left = cx - 16;
|
||||||
|
right = cx + 16;
|
||||||
|
bottom = 448;
|
||||||
|
top = this.y;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const ratio = devicePixelRatio * core.domStyle.scale;
|
||||||
|
const w = right - left;
|
||||||
|
const h = bottom - top;
|
||||||
|
const fw = w * ratio;
|
||||||
|
const fh = h * ratio;
|
||||||
|
const fl = left * ratio;
|
||||||
|
const ft = top * ratio;
|
||||||
|
const cell = 32 * ratio;
|
||||||
|
const hor = ThunderBallProjectile.horizontal!.canvas;
|
||||||
|
const ver = ThunderBallProjectile.vertical!.canvas;
|
||||||
|
ctx.save();
|
||||||
|
if (this.time > 1000 && this.time < 3000) {
|
||||||
|
ctx.globalAlpha = (3000 - this.time) / 2000;
|
||||||
|
} else {
|
||||||
|
ctx.globalAlpha = 1;
|
||||||
|
}
|
||||||
|
if (w > 0 && h > 0 && this.time < 3000) {
|
||||||
|
switch (this.direction) {
|
||||||
|
case ProjectileDirection.BottomToTop:
|
||||||
|
case ProjectileDirection.TopToBottom: {
|
||||||
|
ctx.drawImage(ver, 0, ft, cell, fh, left, top, w, h);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ProjectileDirection.LeftToRight:
|
||||||
|
case ProjectileDirection.RightToLeft: {
|
||||||
|
ctx.drawImage(hor, fl, 0, fw, cell, left, top, w, h);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.globalAlpha = 1;
|
||||||
|
ctx.fillStyle = '#fff';
|
||||||
|
ctx.shadowBlur = 8;
|
||||||
|
ctx.shadowColor = '#62c8f4';
|
||||||
|
ctx.globalAlpha = 0.9;
|
||||||
|
ctx.beginPath();
|
||||||
|
const radius = 7 + Math.floor(Math.random() * 2);
|
||||||
|
ctx.arc(this.x, this.y, radius, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BoomProjectile extends Projectile<TowerBoss> {
|
||||||
|
damage: number = 3000;
|
||||||
|
hitbox: Hitbox.Rect = new Hitbox.Rect(0, 0, 32, 32);
|
||||||
|
|
||||||
|
private bx: number = 0;
|
||||||
|
private by: number = 0;
|
||||||
|
private last: number = 500;
|
||||||
|
|
||||||
|
private damaged: boolean = false;
|
||||||
|
private animated: boolean = false;
|
||||||
|
|
||||||
|
setData(x: number, y: number, last: number) {
|
||||||
|
this.bx = x;
|
||||||
|
this.by = y;
|
||||||
|
this.last = last;
|
||||||
|
this.setPosition(x * 32, y * 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
isIntersect(hitbox: Hitbox.HitboxType): boolean {
|
||||||
|
if (this.time < this.last + 1000) return false;
|
||||||
|
if (this.damaged) return false;
|
||||||
|
if (hitbox instanceof Hitbox.Rect) {
|
||||||
|
return Hitbox.checkRectRect(this.hitbox, hitbox);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateHitbox(x: number, y: number): void {
|
||||||
|
this.hitbox.setPosition(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
doDamage(target: IStateDamageable): boolean {
|
||||||
|
if (this.damaged) return false;
|
||||||
|
target.hp -= this.damage;
|
||||||
|
this.damaged = true;
|
||||||
|
popDamage(this.damage, this.boss, '#e08aff');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ai(boss: TowerBoss, time: number, frame: number): void {
|
||||||
|
if (!this.animated && time > this.last + 1000) {
|
||||||
|
this.animated = true;
|
||||||
|
core.drawAnimate('explosion1', this.bx, this.by);
|
||||||
|
}
|
||||||
|
if (time > this.last + 1100) {
|
||||||
|
this.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render(canvas: MotaOffscreenCanvas2D, transform: Transform): void {
|
||||||
|
const ctx = canvas.ctx;
|
||||||
|
const end = this.last + 1000;
|
||||||
|
const r = 12;
|
||||||
|
const mr = 27;
|
||||||
|
ctx.save();
|
||||||
|
if (this.time < end) {
|
||||||
|
const angle = this.time / 500;
|
||||||
|
const sin = Math.sin(angle);
|
||||||
|
const cos = Math.cos(angle);
|
||||||
|
ctx.fillStyle = 'rgb(255,50,50)';
|
||||||
|
ctx.strokeStyle = 'rgb(255,50,50)';
|
||||||
|
ctx.lineWidth = 1.5;
|
||||||
|
ctx.globalAlpha = 0.8;
|
||||||
|
const cx = this.x + 16;
|
||||||
|
const cy = this.y + 16;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(cx, cy, r, 0, Math.PI * 2);
|
||||||
|
ctx.moveTo(cx + r * cos, cy + r * sin);
|
||||||
|
ctx.lineTo(cx + mr * cos, cy + mr * sin);
|
||||||
|
ctx.moveTo(cx - r * cos, cy - r * sin);
|
||||||
|
ctx.lineTo(cx - mr * cos, cy - mr * sin);
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(cx, cy, 2, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
if (this.time > end - 500) {
|
||||||
|
const dt = this.time - end + 500;
|
||||||
|
const pos = this.y - (1 - dt / 500) * 480;
|
||||||
|
const img = core.material.images.images['boom.png'];
|
||||||
|
ctx.drawImage(img, this.x, pos - 80, 36, 80);
|
||||||
|
}
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ChainProjectile extends Projectile<TowerBoss> {
|
||||||
|
damage: number = 4000;
|
||||||
|
hitbox: Hitbox.Line = new Hitbox.Line(0, 0, 0, 0);
|
||||||
|
|
||||||
|
private damaged: boolean = false;
|
||||||
|
|
||||||
|
isIntersect(hitbox: Hitbox.HitboxType): boolean {
|
||||||
|
if (this.time < 1000) return false;
|
||||||
|
if (this.damaged) return false;
|
||||||
|
if (hitbox instanceof Hitbox.Rect) {
|
||||||
|
return Hitbox.checkLineRect(this.hitbox, hitbox);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateHitbox(x: number, y: number): void {
|
||||||
|
this.hitbox.setPoint1(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
doDamage(target: IStateDamageable): boolean {
|
||||||
|
if (this.damaged) return false;
|
||||||
|
target.hp -= this.damage;
|
||||||
|
this.damaged = true;
|
||||||
|
core.playSound('electron.opus');
|
||||||
|
popDamage(this.damage, this.boss, '#8affd6');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ai(boss: TowerBoss, time: number, frame: number): void {
|
||||||
|
if (time > 2000) {
|
||||||
|
this.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render(canvas: MotaOffscreenCanvas2D, transform: Transform): void {
|
||||||
|
const ctx = canvas.ctx;
|
||||||
|
ctx.save();
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(this.hitbox.x1, this.hitbox.y1);
|
||||||
|
ctx.lineTo(this.hitbox.x2, this.hitbox.y2);
|
||||||
|
|
||||||
|
const progress = (this.time - 1000) / 1000;
|
||||||
|
|
||||||
|
if (this.time < 1000) {
|
||||||
|
ctx.globalAlpha = 0.6;
|
||||||
|
ctx.strokeStyle = 'rgb(220,100,255)';
|
||||||
|
ctx.stroke();
|
||||||
|
} else {
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.strokeStyle = '#fff';
|
||||||
|
ctx.shadowBlur = 12;
|
||||||
|
ctx.shadowColor = '#62c8f4';
|
||||||
|
ctx.globalAlpha = 1 - progress;
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
}
|
||||||
356
packages-user/legacy-plugin-client/src/chase/chase.ts
Normal file
356
packages-user/legacy-plugin-client/src/chase/chase.ts
Normal file
@ -0,0 +1,356 @@
|
|||||||
|
import { MotaOffscreenCanvas2D } from '@motajs/render';
|
||||||
|
import { Container, MotaRenderer, Shader, Sprite } from '@motajs/render';
|
||||||
|
import {
|
||||||
|
CameraAnimation,
|
||||||
|
LayerGroup,
|
||||||
|
disableViewport,
|
||||||
|
enableViewport
|
||||||
|
} from '@user/client-modules';
|
||||||
|
import { loading } from '@user/data-base';
|
||||||
|
import {
|
||||||
|
heroMoveCollection,
|
||||||
|
type HeroMover,
|
||||||
|
type MoveStep
|
||||||
|
} from '@user/data-state';
|
||||||
|
import EventEmitter from 'eventemitter3';
|
||||||
|
import { mainRenderer } from '@user/client-modules';
|
||||||
|
|
||||||
|
export interface IChaseController {
|
||||||
|
/** 本次追逐战实例 */
|
||||||
|
readonly chase: Chase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始这个追逐战
|
||||||
|
* @param fromSave 是否是从存档开始
|
||||||
|
*/
|
||||||
|
start(fromSave: boolean): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结束这个追逐战
|
||||||
|
* @param success 是否逃脱成功
|
||||||
|
*/
|
||||||
|
end(success: boolean): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化这次追逐战的音频
|
||||||
|
* @param fromSave 是否从存档开始
|
||||||
|
*/
|
||||||
|
initAudio(fromSave: boolean): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ChaseData {
|
||||||
|
path: Partial<Record<FloorIds, LocArr[]>>;
|
||||||
|
camera: Partial<Record<FloorIds, CameraAnimation>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TimeListener {
|
||||||
|
fn: (emitTime: number) => void;
|
||||||
|
time: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LocListener {
|
||||||
|
fn: (x: number, y: number) => void;
|
||||||
|
floorId: FloorIds;
|
||||||
|
once: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChaseEvent {
|
||||||
|
changeFloor: [floor: FloorIds];
|
||||||
|
end: [success: boolean];
|
||||||
|
start: [];
|
||||||
|
step: [x: number, y: number];
|
||||||
|
frame: [totalTime: number, floorTime: number];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Chase extends EventEmitter<ChaseEvent> {
|
||||||
|
static shader: Shader;
|
||||||
|
|
||||||
|
/** 本次追逐战的数据 */
|
||||||
|
private readonly data: ChaseData;
|
||||||
|
|
||||||
|
/** 是否显示路线 */
|
||||||
|
private showPath: boolean = false;
|
||||||
|
/** 每层的路线显示 */
|
||||||
|
private pathMap: Map<FloorIds, MotaOffscreenCanvas2D> = new Map();
|
||||||
|
/** 当前的摄像机动画 */
|
||||||
|
private nowCamera?: CameraAnimation;
|
||||||
|
/** 当前楼层 */
|
||||||
|
private nowFloor?: FloorIds;
|
||||||
|
|
||||||
|
/** 开始时刻 */
|
||||||
|
startTime: number = 0;
|
||||||
|
/** 进入当前楼层的时刻 */
|
||||||
|
nowFloorTime: number = 0;
|
||||||
|
/** 是否正在进行追逐战 */
|
||||||
|
started: boolean = false;
|
||||||
|
|
||||||
|
/** 路径显示的sprite */
|
||||||
|
private pathSprite?: Sprite;
|
||||||
|
/** 当前 LayerGroup 渲染元素 */
|
||||||
|
private layer: LayerGroup;
|
||||||
|
/** 委托ticker的id */
|
||||||
|
private delegation: number = -1;
|
||||||
|
|
||||||
|
/** 时间监听器 */
|
||||||
|
private onTimeListener: TimeListener[] = [];
|
||||||
|
/** 楼层时间监听器 */
|
||||||
|
private onFloorTimeListener: Partial<Record<FloorIds, TimeListener[]>> = {};
|
||||||
|
/** 勇士位置监听器 */
|
||||||
|
private onHeroLocListener: Map<number, Set<LocListener>> = new Map();
|
||||||
|
|
||||||
|
/** 勇士移动实例 */
|
||||||
|
private heroMove: HeroMover;
|
||||||
|
|
||||||
|
constructor(data: ChaseData, showPath: boolean = false) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.data = data;
|
||||||
|
this.showPath = showPath;
|
||||||
|
|
||||||
|
const render = MotaRenderer.get('render-main')!;
|
||||||
|
const layer = render.getElementById('layer-main')! as LayerGroup;
|
||||||
|
this.layer = layer;
|
||||||
|
|
||||||
|
const mover = heroMoveCollection.mover;
|
||||||
|
this.heroMove = mover;
|
||||||
|
|
||||||
|
mover.on('stepEnd', this.onStepEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
private onStepEnd = (step: MoveStep) => {
|
||||||
|
if (step.type === 'speed') return;
|
||||||
|
const { x, y } = core.status.hero.loc;
|
||||||
|
this.emitHeroLoc(x, y);
|
||||||
|
this.emit('step', x, y);
|
||||||
|
};
|
||||||
|
|
||||||
|
private emitHeroLoc(x: number, y: number) {
|
||||||
|
if (!this.nowFloor) return;
|
||||||
|
const floor = core.status.maps[this.nowFloor];
|
||||||
|
const width = floor.width;
|
||||||
|
const index = x + y * width;
|
||||||
|
const list = this.onHeroLocListener.get(index);
|
||||||
|
if (!list) return;
|
||||||
|
const toDelete = new Set<LocListener>();
|
||||||
|
list.forEach(v => {
|
||||||
|
if (v.floorId === this.nowFloor) {
|
||||||
|
v.fn(x, y);
|
||||||
|
if (v.once) toDelete.add(v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
toDelete.forEach(v => list.delete(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
private emitTime() {
|
||||||
|
const now = Date.now();
|
||||||
|
const nTime = now - this.startTime;
|
||||||
|
const fTime = now - this.nowFloorTime;
|
||||||
|
|
||||||
|
this.emit('frame', nTime, fTime);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const time = this.onTimeListener[0];
|
||||||
|
if (!time) break;
|
||||||
|
if (time.time <= nTime) {
|
||||||
|
time.fn(nTime);
|
||||||
|
this.onTimeListener.shift();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.nowFloor) return;
|
||||||
|
const floor = this.onFloorTimeListener[this.nowFloor];
|
||||||
|
if (!floor) return;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const time = floor[0];
|
||||||
|
if (!time) break;
|
||||||
|
if (time.time <= fTime) {
|
||||||
|
time.fn(nTime);
|
||||||
|
floor.shift();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private tick = () => {
|
||||||
|
if (!this.started) return;
|
||||||
|
const floor = core.status.floorId;
|
||||||
|
if (floor !== this.nowFloor) {
|
||||||
|
this.changeFloor(floor);
|
||||||
|
}
|
||||||
|
this.emitTime();
|
||||||
|
};
|
||||||
|
|
||||||
|
private readyPath() {
|
||||||
|
for (const [key, nodes] of Object.entries(this.data.path)) {
|
||||||
|
if (nodes.length === 0) return;
|
||||||
|
const floor = key as FloorIds;
|
||||||
|
const canvas = mainRenderer.requireCanvas();
|
||||||
|
const ctx = canvas.ctx;
|
||||||
|
const cell = 32;
|
||||||
|
const half = cell / 2;
|
||||||
|
const { width, height } = core.status.maps[floor];
|
||||||
|
canvas.setHD(true);
|
||||||
|
canvas.size(width * cell, height * cell);
|
||||||
|
const [fx, fy] = nodes.shift()!;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(fx * cell + half, fy * cell + half);
|
||||||
|
nodes.forEach(([x, y]) => {
|
||||||
|
ctx.lineTo(x * cell + half, y * cell + half);
|
||||||
|
});
|
||||||
|
ctx.strokeStyle = '#0ff';
|
||||||
|
ctx.globalAlpha = 0.6;
|
||||||
|
ctx.stroke();
|
||||||
|
this.pathMap.set(floor, canvas);
|
||||||
|
}
|
||||||
|
this.pathSprite = new Sprite('static', false, true);
|
||||||
|
this.pathSprite.size(480, 480);
|
||||||
|
this.pathSprite.pos(0, 0);
|
||||||
|
this.pathSprite.setZIndex(120);
|
||||||
|
this.pathSprite.setAntiAliasing(false);
|
||||||
|
this.layer.appendChild(this.pathSprite);
|
||||||
|
this.pathSprite.setRenderFn(canvas => {
|
||||||
|
const ctx = canvas.ctx;
|
||||||
|
const path = this.pathMap.get(core.status.floorId);
|
||||||
|
if (!path) return;
|
||||||
|
ctx.drawImage(path.canvas, 0, 0, path.width, path.height);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当到达某个时间时触发函数
|
||||||
|
* @param time 触发时刻
|
||||||
|
* @param fn 触发时执行的函数,函数的参数表示实际触发时间
|
||||||
|
*/
|
||||||
|
onTime(time: number, fn: (emitTime: number) => void) {
|
||||||
|
this.onTimeListener.push({ time, fn });
|
||||||
|
if (this.started) {
|
||||||
|
this.onTimeListener.sort((a, b) => a.time - b.time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当在某个楼层中到达某个时间时触发函数
|
||||||
|
* @param floor 触发楼层
|
||||||
|
* @param time 从进入该楼层开始计算的触发时刻
|
||||||
|
* @param fn 触发时执行的函数
|
||||||
|
*/
|
||||||
|
onFloorTime(floor: FloorIds, time: number, fn: (emitTime: number) => void) {
|
||||||
|
this.onFloorTimeListener[floor] ??= [];
|
||||||
|
const list = this.onFloorTimeListener[floor];
|
||||||
|
list.push({ time, fn });
|
||||||
|
if (this.started) {
|
||||||
|
list.sort((a, b) => a.time - b.time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ensureLocListener(index: number) {
|
||||||
|
const listener = this.onHeroLocListener.get(index);
|
||||||
|
if (listener) return listener;
|
||||||
|
else {
|
||||||
|
const set = new Set<LocListener>();
|
||||||
|
this.onHeroLocListener.set(index, set);
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当勇士走到某一层的某一格时执行函数
|
||||||
|
* @param x 触发横坐标
|
||||||
|
* @param y 触发纵坐标
|
||||||
|
* @param floor 触发楼层
|
||||||
|
* @param fn 触发函数
|
||||||
|
* @param once 是否只执行一次
|
||||||
|
*/
|
||||||
|
onLoc(
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
floor: FloorIds,
|
||||||
|
fn: (x: number, y: number) => void,
|
||||||
|
once: boolean = false
|
||||||
|
) {
|
||||||
|
const map = core.status.maps[floor];
|
||||||
|
const { width } = map;
|
||||||
|
const index = x + y * width;
|
||||||
|
const set = this.ensureLocListener(index);
|
||||||
|
set.add({ floorId: floor, fn, once });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当勇士走到某一层的某一格时执行函数,且只执行一次
|
||||||
|
* @param x 触发横坐标
|
||||||
|
* @param y 触发纵坐标
|
||||||
|
* @param floor 触发楼层
|
||||||
|
* @param fn 触发函数
|
||||||
|
*/
|
||||||
|
onceLoc(
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
floor: FloorIds,
|
||||||
|
fn: (x: number, y: number) => void
|
||||||
|
) {
|
||||||
|
this.onLoc(x, y, floor, fn, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切换楼层
|
||||||
|
* @param floor 目标楼层
|
||||||
|
*/
|
||||||
|
changeFloor(floor: FloorIds) {
|
||||||
|
if (floor === this.nowFloor) return;
|
||||||
|
this.nowFloor = floor;
|
||||||
|
if (this.nowCamera) {
|
||||||
|
this.nowCamera.destroy();
|
||||||
|
}
|
||||||
|
const camera = this.data.camera[floor];
|
||||||
|
if (camera) {
|
||||||
|
camera.start();
|
||||||
|
this.nowCamera = camera;
|
||||||
|
}
|
||||||
|
this.nowFloorTime = Date.now();
|
||||||
|
this.emit('changeFloor', floor);
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
disableViewport();
|
||||||
|
if (this.showPath) this.readyPath();
|
||||||
|
this.changeFloor(core.status.floorId);
|
||||||
|
this.startTime = Date.now();
|
||||||
|
this.delegation = this.layer.delegateTicker(this.tick);
|
||||||
|
this.started = true;
|
||||||
|
for (const floorTime of Object.values(this.onFloorTimeListener)) {
|
||||||
|
floorTime.sort((a, b) => a.time - b.time);
|
||||||
|
}
|
||||||
|
this.onTimeListener.sort((a, b) => a.time - b.time);
|
||||||
|
const render = MotaRenderer.get('render-main')!;
|
||||||
|
const mapDraw = render.getElementById('map-draw') as Container;
|
||||||
|
Chase.shader.appendTo(mapDraw);
|
||||||
|
this.emit('start');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结束这次追逐战
|
||||||
|
* @param success 是否成功逃脱
|
||||||
|
*/
|
||||||
|
end(success: boolean) {
|
||||||
|
enableViewport();
|
||||||
|
this.layer.removeTicker(this.delegation);
|
||||||
|
this.pathSprite?.destroy();
|
||||||
|
this.heroMove.off('stepEnd', this.onStepEnd);
|
||||||
|
Chase.shader.remove();
|
||||||
|
this.emit('end', success);
|
||||||
|
this.removeAllListeners();
|
||||||
|
this.pathMap.forEach(v => mainRenderer.deleteCanvas(v));
|
||||||
|
this.pathMap.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loading.once('coreInit', () => {
|
||||||
|
const shader = new Shader();
|
||||||
|
Chase.shader = shader;
|
||||||
|
shader.size(480, 480);
|
||||||
|
shader.setHD(true);
|
||||||
|
});
|
||||||
723
packages-user/legacy-plugin-client/src/chase/chase1.ts
Normal file
723
packages-user/legacy-plugin-client/src/chase/chase1.ts
Normal file
@ -0,0 +1,723 @@
|
|||||||
|
import { Animation, hyper, linear, power, sleep } from 'mutate-animate';
|
||||||
|
import { Chase, ChaseData, IChaseController } from './chase';
|
||||||
|
import { MotaRenderer, Sprite } from '@motajs/render';
|
||||||
|
import { PointEffect, PointEffectType } from '../fx/pointShader';
|
||||||
|
import {
|
||||||
|
bgmController,
|
||||||
|
Camera,
|
||||||
|
CameraAnimation,
|
||||||
|
ICameraScale,
|
||||||
|
LayerGroup
|
||||||
|
} 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: [
|
||||||
|
[23, 23],
|
||||||
|
[0, 23]
|
||||||
|
],
|
||||||
|
MT15: [
|
||||||
|
[63, 4],
|
||||||
|
[61, 4],
|
||||||
|
[61, 5],
|
||||||
|
[58, 5],
|
||||||
|
[58, 8],
|
||||||
|
[54, 8],
|
||||||
|
[54, 11],
|
||||||
|
[51, 11],
|
||||||
|
[51, 8],
|
||||||
|
[45, 8],
|
||||||
|
[45, 4],
|
||||||
|
[47, 4],
|
||||||
|
[47, 6],
|
||||||
|
[51, 6],
|
||||||
|
[51, 5],
|
||||||
|
[52, 5],
|
||||||
|
[52, 3],
|
||||||
|
[50, 3],
|
||||||
|
[50, 5],
|
||||||
|
[48, 5],
|
||||||
|
[48, 3],
|
||||||
|
[35, 3],
|
||||||
|
[35, 5],
|
||||||
|
[31, 5],
|
||||||
|
[31, 7],
|
||||||
|
[34, 7],
|
||||||
|
[34, 9],
|
||||||
|
[31, 9],
|
||||||
|
[31, 11],
|
||||||
|
[12, 11],
|
||||||
|
[12, 8],
|
||||||
|
[1, 8],
|
||||||
|
[1, 7],
|
||||||
|
[0, 7]
|
||||||
|
],
|
||||||
|
MT14: [
|
||||||
|
[127, 7],
|
||||||
|
[126, 7],
|
||||||
|
[126, 8],
|
||||||
|
[124, 8],
|
||||||
|
[124, 7],
|
||||||
|
[115.2, 7],
|
||||||
|
[115.2, 9.2],
|
||||||
|
[110.2, 9.2],
|
||||||
|
[110.2, 11],
|
||||||
|
[109.8, 11],
|
||||||
|
[109.8, 8.8],
|
||||||
|
[111.8, 8.8],
|
||||||
|
[111.8, 7],
|
||||||
|
[104, 7],
|
||||||
|
[104, 3],
|
||||||
|
[100, 3],
|
||||||
|
[100, 4],
|
||||||
|
[98, 4],
|
||||||
|
[98, 3],
|
||||||
|
[96, 3],
|
||||||
|
[96, 6],
|
||||||
|
[95, 6],
|
||||||
|
[95, 7],
|
||||||
|
[88, 7],
|
||||||
|
[88, 6],
|
||||||
|
[85, 6],
|
||||||
|
[85, 8],
|
||||||
|
[83, 8],
|
||||||
|
[83, 9],
|
||||||
|
[81, 9],
|
||||||
|
[81, 11],
|
||||||
|
[72, 11],
|
||||||
|
[72, 5],
|
||||||
|
[68, 5],
|
||||||
|
[68, 8],
|
||||||
|
[67, 8],
|
||||||
|
[67, 10],
|
||||||
|
[65, 10],
|
||||||
|
[65, 11],
|
||||||
|
[62, 11],
|
||||||
|
[62, 9],
|
||||||
|
[60, 9],
|
||||||
|
[60, 11],
|
||||||
|
[57, 11],
|
||||||
|
[57, 9],
|
||||||
|
[54, 9]
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
let back: Sprite | undefined;
|
||||||
|
let contrastId: number = 0;
|
||||||
|
const effect = new PointEffect();
|
||||||
|
|
||||||
|
loading.once('loaded', () => {
|
||||||
|
effect.create(Chase.shader, 40);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化并开始这个追逐战
|
||||||
|
*/
|
||||||
|
export function initChase(): IChaseController {
|
||||||
|
const ani = new Animation();
|
||||||
|
|
||||||
|
const render = MotaRenderer.get('render-main')!;
|
||||||
|
const layer = render.getElementById('layer-main')! as LayerGroup;
|
||||||
|
|
||||||
|
const camera = new Camera(layer);
|
||||||
|
camera.clearOperation();
|
||||||
|
camera.transform = layer.camera;
|
||||||
|
camera.disable();
|
||||||
|
const animation16 = new CameraAnimation(camera);
|
||||||
|
const animation15 = new CameraAnimation(camera);
|
||||||
|
const animation14 = new CameraAnimation(camera);
|
||||||
|
effect.setTransform(layer.camera);
|
||||||
|
|
||||||
|
const data: ChaseData = {
|
||||||
|
path,
|
||||||
|
camera: {
|
||||||
|
MT16: animation16,
|
||||||
|
MT15: animation15,
|
||||||
|
MT14: animation14
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const chase = new Chase(data, flags.chaseHard === 0);
|
||||||
|
|
||||||
|
// 旋转在前,平移在后
|
||||||
|
const translate1 = camera.addTranslate();
|
||||||
|
const scale = camera.addScale();
|
||||||
|
const rotate = camera.addRotate();
|
||||||
|
const translate2 = camera.addTranslate();
|
||||||
|
const translate = camera.addTranslate();
|
||||||
|
|
||||||
|
translate1.x = -7 * 32;
|
||||||
|
translate1.y = -7 * 32;
|
||||||
|
translate2.x = 7 * 32;
|
||||||
|
translate2.y = 7 * 32;
|
||||||
|
|
||||||
|
translate.x = 10 * 32;
|
||||||
|
translate.y = 10 * 32;
|
||||||
|
|
||||||
|
const inOut = hyper('sin', 'in-out');
|
||||||
|
// MT16 摄像机动画
|
||||||
|
animation16.translate(translate, 10, 10, 1, 0, linear());
|
||||||
|
animation16.translate(translate, 0, 10, 1600, 0, hyper('sin', 'in'));
|
||||||
|
// MT15 摄像机动画
|
||||||
|
animation15.rotate(rotate, -Math.PI / 30, 4000, 0, inOut);
|
||||||
|
animation15.rotate(rotate, 0, 3000, 5000, inOut);
|
||||||
|
animation15.rotate(rotate, -Math.PI / 40, 1800, 11000, inOut);
|
||||||
|
animation15.rotate(rotate, 0, 2000, 13000, inOut);
|
||||||
|
animation15.translate(translate, 49, 0, 1, 0, linear());
|
||||||
|
animation15.translate(translate, 45, 0, 2324, 0, hyper('sin', 'in'));
|
||||||
|
animation15.translate(translate, 40, 0, 1992, 2324, hyper('sin', 'out'));
|
||||||
|
animation15.translate(translate, 41, 0, 498, 5312, hyper('sin', 'in-out'));
|
||||||
|
animation15.translate(translate, 37, 0, 1660, 5810, hyper('sin', 'in'));
|
||||||
|
animation15.translate(translate, 29, 0, 830, 7470, hyper('sin', 'out'));
|
||||||
|
animation15.translate(translate, 25, 0, 996, 11454, hyper('sin', 'in'));
|
||||||
|
animation15.translate(translate, 12, 0, 996, 12450, linear());
|
||||||
|
animation15.translate(translate, 0, 0, 1470, 13446, hyper('sin', 'out'));
|
||||||
|
// MT14 摄像机动画
|
||||||
|
animation14.rotate(rotate, -Math.PI / 70, 1000, 0, inOut);
|
||||||
|
animation14.rotate(rotate, 0, 4000, 2000, inOut);
|
||||||
|
animation14.translate(translate, 113, 0, 1, 0, hyper('sin', 'in'));
|
||||||
|
animation14.translate(translate, 109, 0, 1328, 0, hyper('sin', 'in'));
|
||||||
|
animation14.translate(translate, 104, 0, 332, 1328, hyper('sin', 'out'));
|
||||||
|
animation14.translate(translate, 92, 0, 2822, 5478, hyper('sin', 'in'));
|
||||||
|
animation14.translate(translate, 84, 0, 1992, 8300, linear());
|
||||||
|
animation14.translate(translate, 74, 0, 2988, 10292, linear());
|
||||||
|
animation14.translate(translate, 65, 0, 2988, 13280, linear());
|
||||||
|
animation14.translate(translate, 58, 0, 1992, 16268, linear());
|
||||||
|
animation14.translate(translate, 47, 0, 3320, 18260, linear());
|
||||||
|
animation14.translate(translate, 36, 0, 3320, 21580, linear());
|
||||||
|
animation14.translate(translate, 0, 0, 9960, 24900, linear());
|
||||||
|
|
||||||
|
chase.on('end', success => {
|
||||||
|
camera.transform.reset();
|
||||||
|
camera.transform.translate(
|
||||||
|
-translate.x * 32 - 7 * 32,
|
||||||
|
-translate.y * 32 - 7 * 32
|
||||||
|
);
|
||||||
|
animation16.destroy();
|
||||||
|
animation15.destroy();
|
||||||
|
animation14.destroy();
|
||||||
|
camera.destroy();
|
||||||
|
back?.destroy();
|
||||||
|
back = void 0;
|
||||||
|
core.removeFlag('onChase');
|
||||||
|
core.removeFlag('chaseId');
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
// completeAchievement('challenge', 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
judgeFail1(chase, ani, camera);
|
||||||
|
drawBack(chase, ani);
|
||||||
|
para1(chase, ani);
|
||||||
|
para2(chase, ani);
|
||||||
|
para3(chase, ani);
|
||||||
|
processScale(chase, ani, scale, camera);
|
||||||
|
|
||||||
|
chaseInit1();
|
||||||
|
|
||||||
|
chase.on('end', () => {
|
||||||
|
effect.end();
|
||||||
|
camera.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
chase.on('frame', () => {
|
||||||
|
effect.requestUpdate();
|
||||||
|
});
|
||||||
|
|
||||||
|
chase.on('changeFloor', () => {
|
||||||
|
effect.clearEffect();
|
||||||
|
});
|
||||||
|
|
||||||
|
const controller: IChaseController = {
|
||||||
|
chase,
|
||||||
|
start(fromSave) {
|
||||||
|
core.setFlag('onChase', true);
|
||||||
|
core.setFlag('chaseId', 1);
|
||||||
|
chase.start();
|
||||||
|
camera.enable();
|
||||||
|
wolfMove(chase);
|
||||||
|
effect.use();
|
||||||
|
effect.start();
|
||||||
|
if (fromSave) {
|
||||||
|
initFromSave(chase);
|
||||||
|
core.autosave();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
end(success) {
|
||||||
|
chase.end(success);
|
||||||
|
},
|
||||||
|
initAudio(fromSave) {
|
||||||
|
if (fromSave) initFromSave(chase);
|
||||||
|
else initAudio(chase);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
function initAudio(chase: Chase) {
|
||||||
|
playAudio(0, chase);
|
||||||
|
}
|
||||||
|
|
||||||
|
function initFromSave(chase: Chase) {
|
||||||
|
playAudio(43.5, chase);
|
||||||
|
}
|
||||||
|
|
||||||
|
function playAudio(from: number, chase: Chase) {
|
||||||
|
const playing = bgmController.playingBgm;
|
||||||
|
bgmController.play('escape.opus', from);
|
||||||
|
bgmController.blockChange();
|
||||||
|
chase.on('end', () => {
|
||||||
|
bgmController.unblockChange();
|
||||||
|
if (playing) bgmController.play(playing);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function processScale(
|
||||||
|
chase: Chase,
|
||||||
|
ani: Animation,
|
||||||
|
scale: ICameraScale,
|
||||||
|
camera: Camera
|
||||||
|
) {
|
||||||
|
chase.onceLoc(35, 3, 'MT15', () => {
|
||||||
|
camera.applyScaleAnimation(scale, ani, 2200);
|
||||||
|
ani.mode(linear()).time(1).scale(1.2);
|
||||||
|
sleep(150).then(() => {
|
||||||
|
ani.mode(hyper('sin', 'out')).time(2000).scale(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
chase.onFloorTime('MT14', 100, () => {
|
||||||
|
camera.applyScaleAnimation(scale, ani, 30000);
|
||||||
|
ani.mode(hyper('sin', 'in-out')).time(3000).scale(0.8);
|
||||||
|
});
|
||||||
|
chase.onceLoc(57, 10, 'MT14', () => {
|
||||||
|
ani.mode(power(6, 'in')).time(200).scale(1.1);
|
||||||
|
sleep(200).then(() => {
|
||||||
|
ani.mode(hyper('sin', 'in-out')).time(3000).scale(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function wolfMove(_chase: Chase) {
|
||||||
|
core.moveBlock(23, 17, Array(6).fill('down'), 80);
|
||||||
|
await sleep(550);
|
||||||
|
core.setBlock(508, 23, 23);
|
||||||
|
}
|
||||||
|
|
||||||
|
function judgeFail1(chase: Chase, ani: Animation, camera: Camera) {
|
||||||
|
chase.on('frame', () => {
|
||||||
|
const now = Date.now();
|
||||||
|
const time = now - chase.nowFloorTime;
|
||||||
|
if (time < 500) return;
|
||||||
|
if (core.status.hero.loc.x > -camera.transform.x / 32 + 22) {
|
||||||
|
chase.end(false);
|
||||||
|
if (ani.value.rect !== void 0) {
|
||||||
|
ani.time(750).apply('rect', 0);
|
||||||
|
}
|
||||||
|
core.lose('逃跑失败');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawBack(chase: Chase, ani: Animation) {
|
||||||
|
chase.onFloorTime('MT15', 0, () => {
|
||||||
|
ani.register('rect', 0);
|
||||||
|
ani.mode(hyper('sin', 'out')).time(1500).absolute().apply('rect', 64);
|
||||||
|
|
||||||
|
const render = MotaRenderer.get('render-main')!;
|
||||||
|
const layer = render.getElementById('layer-main')! as LayerGroup;
|
||||||
|
back = new Sprite('absolute', false);
|
||||||
|
back.setZIndex(100);
|
||||||
|
back.size(480, 480);
|
||||||
|
back.pos(0, 0);
|
||||||
|
back.appendTo(layer);
|
||||||
|
back.setRenderFn(canvas => {
|
||||||
|
const ctx = canvas.ctx;
|
||||||
|
ctx.fillStyle = '#000';
|
||||||
|
ctx.fillRect(0, 0, 480, ani.value.rect);
|
||||||
|
ctx.fillRect(0, 480, 480, -ani.value.rect);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addCommonWarp(x: number, y: number) {
|
||||||
|
effect.addEffect(
|
||||||
|
PointEffectType.CircleWarp,
|
||||||
|
Date.now(),
|
||||||
|
500,
|
||||||
|
[x * 32 + 16, y * 32 + 16, 48, 32],
|
||||||
|
[1 / 10, 6, 0.8, 0],
|
||||||
|
[0, -Math.PI, 0, 0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addMediumWarp(x: number, y: number) {
|
||||||
|
effect.addEffect(
|
||||||
|
PointEffectType.CircleWarp,
|
||||||
|
Date.now(),
|
||||||
|
5000,
|
||||||
|
[x * 32 + 16, y * 32 + 16, 480, 64],
|
||||||
|
[1 / 40, 1, 0.5, 0],
|
||||||
|
[0, Math.PI * 2, 0, 0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addLargeWarp(x: number, y: number) {
|
||||||
|
effect.addEffect(
|
||||||
|
PointEffectType.CircleWarp,
|
||||||
|
Date.now(),
|
||||||
|
10000,
|
||||||
|
[x * 32 + 16, y * 32 + 16, 1080, 96],
|
||||||
|
[1 / 25, 1, 0.5, 0],
|
||||||
|
[0, Math.PI * 2, 0, 0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addCommonContrast(x: number, y: number, ani: Animation, chase: Chase) {
|
||||||
|
const id = contrastId++;
|
||||||
|
const name = 'contrast' + id;
|
||||||
|
ani.register(name, 1);
|
||||||
|
sleep(500).then(() => {
|
||||||
|
ani.mode(linear()).absolute().time(1500).apply(name, 0);
|
||||||
|
});
|
||||||
|
const fx = effect.addEffect(
|
||||||
|
PointEffectType.CircleContrast,
|
||||||
|
Date.now(),
|
||||||
|
2000,
|
||||||
|
[x * 32 + 16, y * 32 + 16, 48, 8],
|
||||||
|
[1, 0, 0, 0]
|
||||||
|
);
|
||||||
|
const fn = () => {
|
||||||
|
effect.setEffect(fx, void 0, [ani.value[name], 0, 0, 0]);
|
||||||
|
};
|
||||||
|
chase.on('frame', fn);
|
||||||
|
sleep(2000).then(() => {
|
||||||
|
chase.off('frame', fn);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addMediumContrast(x: number, y: number, ani: Animation, chase: Chase) {
|
||||||
|
const id = contrastId++;
|
||||||
|
const name = 'contrast' + id;
|
||||||
|
ani.register(name, 1);
|
||||||
|
sleep(1500).then(() => {
|
||||||
|
ani.mode(linear()).absolute().time(5000).apply(name, 0);
|
||||||
|
});
|
||||||
|
const fx = effect.addEffect(
|
||||||
|
PointEffectType.CircleContrast,
|
||||||
|
Date.now(),
|
||||||
|
7500,
|
||||||
|
[x * 32 + 16, y * 32 + 16, 144, 32],
|
||||||
|
[1, 0, 0, 0]
|
||||||
|
);
|
||||||
|
const fn = () => {
|
||||||
|
effect.setEffect(
|
||||||
|
fx,
|
||||||
|
[x * 32 + 16, y * 32 + 16, 144 + (1 - ani.value[name]) * 240, 32],
|
||||||
|
[ani.value[name], 0, 0, 0]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
chase.on('frame', fn);
|
||||||
|
sleep(7500).then(() => {
|
||||||
|
chase.off('frame', fn);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addLargeContrast(x: number, y: number, ani: Animation, chase: Chase) {
|
||||||
|
const id = contrastId++;
|
||||||
|
const name = 'contrast' + id;
|
||||||
|
ani.register(name, 1);
|
||||||
|
sleep(500).then(() => {
|
||||||
|
ani.mode(linear()).absolute().time(9500).apply(name, 0);
|
||||||
|
});
|
||||||
|
const fx = effect.addEffect(
|
||||||
|
PointEffectType.CircleContrast,
|
||||||
|
Date.now(),
|
||||||
|
7500,
|
||||||
|
[x * 32 + 16, y * 32 + 16, 324, 240],
|
||||||
|
[1, 0, 0, 0]
|
||||||
|
);
|
||||||
|
const fn = () => {
|
||||||
|
effect.setEffect(
|
||||||
|
fx,
|
||||||
|
[x * 32 + 16, y * 32 + 16, 324 + (1 - ani.value[name]) * 720, 240],
|
||||||
|
[ani.value[name], 0, 0, 0]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
chase.on('frame', fn);
|
||||||
|
sleep(10000).then(() => {
|
||||||
|
chase.off('frame', fn);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function explode1(x: number, y: number, ani: Animation, chase: Chase) {
|
||||||
|
core.setBlock(336, x, y);
|
||||||
|
core.drawAnimate('explosion1', x, y);
|
||||||
|
addCommonWarp(x, y);
|
||||||
|
addCommonContrast(x, y, ani, chase);
|
||||||
|
}
|
||||||
|
|
||||||
|
function para1(chase: Chase, ani: Animation) {
|
||||||
|
chase.onFloorTime('MT15', 830, () => {
|
||||||
|
for (let tx = 53; tx < 58; tx++) {
|
||||||
|
for (let ty = 3; ty < 8; ty++) {
|
||||||
|
core.setBlock(336, tx, ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
core.drawAnimate('explosion3', 55, 5);
|
||||||
|
addMediumWarp(55, 5);
|
||||||
|
addMediumContrast(55, 5, ani, chase);
|
||||||
|
});
|
||||||
|
chase.onFloorTime('MT15', 1080, () => {
|
||||||
|
explode1(58, 9, ani, chase);
|
||||||
|
explode1(59, 9, ani, chase);
|
||||||
|
});
|
||||||
|
chase.onFloorTime('MT15', 1190, () => {
|
||||||
|
explode1(53, 8, ani, chase);
|
||||||
|
explode1(52, 8, ani, chase);
|
||||||
|
});
|
||||||
|
chase.onFloorTime('MT15', 1580, () => {
|
||||||
|
explode1(51, 7, ani, chase);
|
||||||
|
});
|
||||||
|
chase.onFloorTime('MT15', 1830, () => {
|
||||||
|
explode1(47, 7, ani, chase);
|
||||||
|
explode1(49, 9, ani, chase);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function para2(chase: Chase, ani: Animation) {
|
||||||
|
let emitted32x9 = false;
|
||||||
|
chase.onceLoc(45, 8, 'MT15', () => {
|
||||||
|
explode1(45, 9, ani, chase);
|
||||||
|
});
|
||||||
|
chase.onceLoc(45, 6, 'MT15', () => {
|
||||||
|
explode1(44, 6, ani, chase);
|
||||||
|
});
|
||||||
|
chase.onceLoc(45, 4, 'MT15', () => {
|
||||||
|
explode1(44, 4, ani, chase);
|
||||||
|
core.drawAnimate('explosion1', 48, 6);
|
||||||
|
core.removeBlock(48, 6);
|
||||||
|
addCommonWarp(48, 6);
|
||||||
|
addCommonContrast(48, 6, ani, chase);
|
||||||
|
});
|
||||||
|
chase.onceLoc(41, 3, 'MT15', () => {
|
||||||
|
explode1(41, 4, ani, chase);
|
||||||
|
explode1(32, 6, ani, chase);
|
||||||
|
});
|
||||||
|
chase.onceLoc(35, 3, 'MT15', () => {
|
||||||
|
core.drawAnimate('explosion3', 37, 7);
|
||||||
|
addMediumWarp(37, 7);
|
||||||
|
addMediumContrast(37, 7, ani, chase);
|
||||||
|
for (let tx = 36; tx < 42; tx++) {
|
||||||
|
for (let ty = 4; ty < 11; ty++) {
|
||||||
|
core.setBlock(336, tx, ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
chase.onceLoc(31, 5, 'MT15', () => {
|
||||||
|
core.removeBlock(34, 8);
|
||||||
|
core.removeBlock(33, 8);
|
||||||
|
core.drawAnimate('explosion1', 34, 8);
|
||||||
|
core.drawAnimate('explosion1', 33, 8);
|
||||||
|
addCommonWarp(34, 8);
|
||||||
|
addCommonWarp(33, 8);
|
||||||
|
addCommonContrast(34, 8, ani, chase);
|
||||||
|
addCommonContrast(33, 8, ani, chase);
|
||||||
|
});
|
||||||
|
chase.onceLoc(33, 7, 'MT15', () => {
|
||||||
|
explode1(32, 9, ani, chase);
|
||||||
|
});
|
||||||
|
chase.onceLoc(33, 9, 'MT15', () => {
|
||||||
|
if (emitted32x9) return;
|
||||||
|
emitted32x9 = true;
|
||||||
|
core.removeBlock(32, 9);
|
||||||
|
core.drawAnimate('explosion1', 32, 9);
|
||||||
|
addCommonWarp(32, 9);
|
||||||
|
addCommonContrast(32, 9, ani, chase);
|
||||||
|
});
|
||||||
|
chase.onceLoc(34, 9, 'MT15', () => {
|
||||||
|
if (emitted32x9) return;
|
||||||
|
emitted32x9 = true;
|
||||||
|
core.removeBlock(32, 9);
|
||||||
|
core.drawAnimate('explosion1', 32, 9);
|
||||||
|
addCommonWarp(32, 9);
|
||||||
|
addCommonContrast(32, 9, ani, chase);
|
||||||
|
});
|
||||||
|
chase.onceLoc(35, 9, 'MT15', () => {
|
||||||
|
if (emitted32x9) return;
|
||||||
|
emitted32x9 = true;
|
||||||
|
core.removeBlock(32, 9);
|
||||||
|
core.drawAnimate('explosion1', 32, 9);
|
||||||
|
addCommonWarp(32, 9);
|
||||||
|
});
|
||||||
|
for (let x = 19; x < 31; x++) {
|
||||||
|
const xx = x;
|
||||||
|
chase.onceLoc(xx, 11, 'MT15', () => {
|
||||||
|
core.setBlock(336, xx + 1, 11);
|
||||||
|
core.drawAnimate('explosion1', xx + 1, 11);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function para3(chase: Chase, ani: Animation) {
|
||||||
|
chase.onceLoc(126, 7, 'MT14', () => {
|
||||||
|
explode1(126, 6, ani, chase);
|
||||||
|
explode1(124, 6, ani, chase);
|
||||||
|
explode1(124, 9, ani, chase);
|
||||||
|
explode1(126, 9, ani, chase);
|
||||||
|
});
|
||||||
|
chase.onceLoc(123, 7, 'MT14', () => {
|
||||||
|
core.setBlock(508, 127, 7);
|
||||||
|
core.jumpBlock(127, 7, 112, 7, 500, true);
|
||||||
|
setTimeout(() => {
|
||||||
|
core.setBlock(509, 112, 7);
|
||||||
|
}, 520);
|
||||||
|
core.drawHeroAnimate('amazed');
|
||||||
|
explode1(121, 6, ani, chase);
|
||||||
|
explode1(122, 6, ani, chase);
|
||||||
|
explode1(120, 8, ani, chase);
|
||||||
|
explode1(121, 8, ani, chase);
|
||||||
|
explode1(122, 8, ani, chase);
|
||||||
|
});
|
||||||
|
let emitted110x10 = false;
|
||||||
|
let emitted112x8 = false;
|
||||||
|
chase.onceLoc(110, 10, 'MT14', () => {
|
||||||
|
explode1(109, 11, ani, chase);
|
||||||
|
core.removeBlock(112, 8);
|
||||||
|
core.drawAnimate('explosion1', 112, 8);
|
||||||
|
addCommonWarp(112, 8);
|
||||||
|
addCommonContrast(112, 8, ani, chase);
|
||||||
|
core.insertAction([
|
||||||
|
{ type: 'moveHero', time: 400, steps: ['backward:1'] }
|
||||||
|
]);
|
||||||
|
emitted110x10 = true;
|
||||||
|
});
|
||||||
|
chase.onLoc(112, 8, 'MT14', () => {
|
||||||
|
if (!emitted110x10 || emitted112x8) return;
|
||||||
|
core.jumpBlock(112, 7, 110, 4, 500, true);
|
||||||
|
core.drawHeroAnimate('amazed');
|
||||||
|
setTimeout(() => {
|
||||||
|
core.setBlock(506, 110, 4);
|
||||||
|
}, 540);
|
||||||
|
emitted112x8 = true;
|
||||||
|
});
|
||||||
|
chase.onceLoc(118, 7, 'MT14', () => {
|
||||||
|
explode1(117, 6, ani, chase);
|
||||||
|
explode1(116, 6, ani, chase);
|
||||||
|
explode1(115, 6, ani, chase);
|
||||||
|
explode1(114, 6, ani, chase);
|
||||||
|
explode1(117, 8, ani, chase);
|
||||||
|
explode1(116, 8, ani, chase);
|
||||||
|
});
|
||||||
|
chase.onceLoc(112, 7, 'MT14', () => {
|
||||||
|
explode1(112, 8, ani, chase);
|
||||||
|
explode1(113, 7, ani, chase);
|
||||||
|
});
|
||||||
|
chase.onceLoc(115, 7, 'MT14', () => {
|
||||||
|
for (let tx = 111; tx <= 115; tx++) {
|
||||||
|
explode1(tx, 10, ani, chase);
|
||||||
|
}
|
||||||
|
explode1(112, 8, ani, chase);
|
||||||
|
});
|
||||||
|
chase.onceLoc(110, 7, 'MT14', () => {
|
||||||
|
core.jumpBlock(97, 4, 120, -3, 2000);
|
||||||
|
for (let tx = 109; tx <= 120; tx++) {
|
||||||
|
for (let ty = 3; ty <= 11; ty++) {
|
||||||
|
if (ty == 7) continue;
|
||||||
|
core.setBlock(336, tx, ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
core.drawAnimate('explosion2', 119, 7);
|
||||||
|
addLargeWarp(119, 7);
|
||||||
|
addLargeContrast(119, 7, ani, chase);
|
||||||
|
core.removeBlock(105, 7);
|
||||||
|
core.drawAnimate('explosion1', 105, 7);
|
||||||
|
addCommonWarp(105, 7);
|
||||||
|
addCommonContrast(105, 7, ani, chase);
|
||||||
|
});
|
||||||
|
chase.onceLoc(97, 3, 'MT14', () => {
|
||||||
|
explode1(95, 3, ani, chase);
|
||||||
|
explode1(93, 6, ani, chase);
|
||||||
|
});
|
||||||
|
chase.onceLoc(88, 6, 'MT14', () => {
|
||||||
|
explode1(87, 4, ani, chase);
|
||||||
|
explode1(88, 5, ani, chase);
|
||||||
|
});
|
||||||
|
chase.onceLoc(86, 6, 'MT14', () => {
|
||||||
|
explode1(84, 6, ani, chase);
|
||||||
|
explode1(85, 5, ani, chase);
|
||||||
|
explode1(86, 8, ani, chase);
|
||||||
|
});
|
||||||
|
chase.onceLoc(81, 9, 'MT14', () => {
|
||||||
|
explode1(81, 8, ani, chase);
|
||||||
|
explode1(82, 11, ani, chase);
|
||||||
|
});
|
||||||
|
chase.onceLoc(72, 11, 'MT14', () => {
|
||||||
|
explode1(73, 8, ani, chase);
|
||||||
|
explode1(72, 4, ani, chase);
|
||||||
|
});
|
||||||
|
chase.onceLoc(72, 7, 'MT14', () => {
|
||||||
|
for (let tx = 74; tx < 86; tx++) {
|
||||||
|
for (let ty = 3; ty < 12; ty++) {
|
||||||
|
core.setBlock(336, tx, ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
core.drawAnimate('explosion2', 79, 7);
|
||||||
|
addLargeWarp(79, 7);
|
||||||
|
addLargeContrast(79, 7, ani, chase);
|
||||||
|
});
|
||||||
|
chase.onceLoc(68, 5, 'MT14', () => {
|
||||||
|
explode1(68, 4, ani, chase);
|
||||||
|
explode1(67, 6, ani, chase);
|
||||||
|
});
|
||||||
|
chase.onceLoc(67, 10, 'MT14', () => {
|
||||||
|
for (let tx = 65; tx <= 72; tx++) {
|
||||||
|
for (let ty = 3; ty <= 9; ty++) {
|
||||||
|
core.setBlock(336, tx, ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
core.setBlock(336, 72, 10);
|
||||||
|
core.setBlock(336, 72, 11);
|
||||||
|
core.drawAnimate('explosion3', 69, 5);
|
||||||
|
addMediumWarp(69, 5);
|
||||||
|
addMediumContrast(69, 5, ani, chase);
|
||||||
|
});
|
||||||
|
chase.onceLoc(64, 11, 'MT14', () => {
|
||||||
|
explode1(63, 9, ani, chase);
|
||||||
|
explode1(60, 8, ani, chase);
|
||||||
|
explode1(56, 11, ani, chase);
|
||||||
|
});
|
||||||
|
chase.onceLoc(57, 9, 'MT14', () => {
|
||||||
|
for (let tx = 58; tx <= 64; tx++) {
|
||||||
|
for (let ty = 3; ty <= 11; ty++) {
|
||||||
|
core.setBlock(336, tx, ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
core.drawAnimate('explosion2', 61, 7);
|
||||||
|
addLargeWarp(61, 7);
|
||||||
|
addLargeContrast(61, 7, ani, chase);
|
||||||
|
});
|
||||||
|
const exploded: Set<number> = new Set();
|
||||||
|
chase.on('step', x => {
|
||||||
|
if (core.status.floorId !== 'MT14') return;
|
||||||
|
if (exploded.has(x)) return;
|
||||||
|
if (x > 20 && x < 49) {
|
||||||
|
for (let ty = 3; ty <= 11; ty++) {
|
||||||
|
core.setBlock(336, x + 4, ty);
|
||||||
|
core.drawAnimate('explosion1', x + 4, ty);
|
||||||
|
}
|
||||||
|
exploded.add(x);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
chase.onceLoc(21, 7, 'MT14', async () => {
|
||||||
|
flags.finishChase1 = true;
|
||||||
|
clip('choices:0');
|
||||||
|
core.showStatusBar();
|
||||||
|
ani.time(750).apply('rect', 0);
|
||||||
|
chase.end(true);
|
||||||
|
await sleep(750);
|
||||||
|
ani.ticker.destroy();
|
||||||
|
core.deleteCanvas('chaseBack');
|
||||||
|
});
|
||||||
|
}
|
||||||
34
packages-user/legacy-plugin-client/src/chase/index.ts
Normal file
34
packages-user/legacy-plugin-client/src/chase/index.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { IChaseController } from './chase';
|
||||||
|
import { initChase as init1 } from './chase1';
|
||||||
|
|
||||||
|
let nowChase: IChaseController | undefined;
|
||||||
|
|
||||||
|
const chaseIndexes: Record<number, () => IChaseController> = {
|
||||||
|
0: init1
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getNow() {
|
||||||
|
return nowChase;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initChase(index: number) {
|
||||||
|
nowChase?.end(false);
|
||||||
|
const controller = chaseIndexes[index]();
|
||||||
|
nowChase = controller;
|
||||||
|
controller.chase.on('end', () => {
|
||||||
|
nowChase = void 0;
|
||||||
|
});
|
||||||
|
return nowChase;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function start(fromSave: boolean) {
|
||||||
|
nowChase?.start(fromSave);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function end(success: boolean) {
|
||||||
|
nowChase?.end(success);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initAudio(fromSave: boolean) {
|
||||||
|
nowChase?.initAudio(fromSave);
|
||||||
|
}
|
||||||
225
packages-user/legacy-plugin-client/src/fx/frag.ts
Normal file
225
packages-user/legacy-plugin-client/src/fx/frag.ts
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
import { Animation, linear, sleep } from 'mutate-animate';
|
||||||
|
// import { has } from '@motajs/legacy-ui';
|
||||||
|
|
||||||
|
// todo: 移植到渲染树
|
||||||
|
|
||||||
|
interface SplittedImage {
|
||||||
|
canvas: HTMLCanvasElement;
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FraggingImage extends SplittedImage {
|
||||||
|
/** 横坐标增量 */
|
||||||
|
deltaX: number;
|
||||||
|
/** 纵坐标增量 */
|
||||||
|
deltaY: number;
|
||||||
|
endRad: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 最大移动距离,最终位置距离中心的距离变成原来的几倍 */
|
||||||
|
const MAX_MOVE_LENGTH = 1.15;
|
||||||
|
/** 移动距离波动,在最大移动距离的基础上加上多少倍距离的波动距离 */
|
||||||
|
const MOVE_FLUSH = 0.7;
|
||||||
|
/** 最大旋转角,单位是弧度 */
|
||||||
|
const MAX_ROTATE = 0.5;
|
||||||
|
/** 碎裂动画的速率曲线函数 */
|
||||||
|
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();
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyFragWith(
|
||||||
|
canvas: HTMLCanvasElement,
|
||||||
|
length: number = 4,
|
||||||
|
time: number = 1000,
|
||||||
|
config: any = {} // todo: 类型标注
|
||||||
|
) {
|
||||||
|
// 先切分图片
|
||||||
|
const imgs = splitCanvas(canvas, length);
|
||||||
|
const cx = canvas.width / 2;
|
||||||
|
const cy = canvas.height / 2;
|
||||||
|
|
||||||
|
let maxX = 0;
|
||||||
|
let maxY = 0;
|
||||||
|
const toMove: FraggingImage[] = imgs.map(v => {
|
||||||
|
const centerX = v.x + v.canvas.width / 2;
|
||||||
|
const centerY = v.y + v.canvas.height / 2;
|
||||||
|
const onX = centerX === cx;
|
||||||
|
const onY = centerY === cy;
|
||||||
|
const mml = config.maxMoveLength ?? MAX_MOVE_LENGTH;
|
||||||
|
const mf = config.moveFlush ?? MOVE_FLUSH;
|
||||||
|
const rate = mml - 1 + Math.random() ** 3 * mf;
|
||||||
|
let endX = onY ? 0 : (centerX - cx) * rate;
|
||||||
|
let endY = onX ? 0 : (centerY - cy) * rate;
|
||||||
|
const mx = Math.abs(endX + centerX) + Math.abs(v.canvas.width);
|
||||||
|
const my = Math.abs(endY + centerY) + Math.abs(v.canvas.height);
|
||||||
|
if (mx > maxX) maxX = mx;
|
||||||
|
if (my > maxY) maxY = my;
|
||||||
|
const r = config.maxRotate ?? MAX_ROTATE;
|
||||||
|
const endRad = Math.random() * r * 2 - r;
|
||||||
|
|
||||||
|
return {
|
||||||
|
deltaX: endX,
|
||||||
|
deltaY: endY,
|
||||||
|
endRad,
|
||||||
|
x: centerX,
|
||||||
|
y: centerY,
|
||||||
|
canvas: v.canvas
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// 再执行动画
|
||||||
|
const frag = document.createElement('canvas');
|
||||||
|
const ctx = frag.getContext('2d')!;
|
||||||
|
const ani = new Animation();
|
||||||
|
ani.register('rate', 0);
|
||||||
|
const ft = config.fragTiming ?? FRAG_TIMING;
|
||||||
|
ani.absolute().time(time).mode(ft).apply('rate', 1);
|
||||||
|
frag.width = maxX * 2;
|
||||||
|
frag.height = maxY * 2;
|
||||||
|
ctx.save();
|
||||||
|
const dw = maxX - canvas.width / 2;
|
||||||
|
const dh = maxY - canvas.height / 2;
|
||||||
|
|
||||||
|
const fragFn = () => {
|
||||||
|
const rate = ani.value.rate;
|
||||||
|
const opacity = 1 - rate;
|
||||||
|
ctx.globalAlpha = opacity;
|
||||||
|
ctx.clearRect(0, 0, frag.width, frag.height);
|
||||||
|
toMove.forEach(v => {
|
||||||
|
ctx.save();
|
||||||
|
const nx = v.deltaX * rate;
|
||||||
|
const ny = v.deltaY * rate;
|
||||||
|
const rotate = v.endRad * rate;
|
||||||
|
|
||||||
|
ctx.translate(nx + v.x + dw, ny + v.y + dh);
|
||||||
|
ctx.rotate(rotate);
|
||||||
|
ctx.drawImage(
|
||||||
|
v.canvas,
|
||||||
|
nx - v.canvas.width / 2,
|
||||||
|
ny - v.canvas.height / 2
|
||||||
|
);
|
||||||
|
ctx.restore();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const onEnd = () => {};
|
||||||
|
ani.ticker.add(fragFn);
|
||||||
|
|
||||||
|
return makeFragManager(frag, ani, time, onEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeFragManager(
|
||||||
|
canvas: HTMLCanvasElement,
|
||||||
|
ani: Animation,
|
||||||
|
time: number,
|
||||||
|
onEnd: () => void
|
||||||
|
) {
|
||||||
|
const promise = sleep(time + 50);
|
||||||
|
|
||||||
|
return {
|
||||||
|
animation: ani,
|
||||||
|
onEnd: promise.then(() => {
|
||||||
|
ani.ticker.destroy();
|
||||||
|
onEnd();
|
||||||
|
}),
|
||||||
|
canvas
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function withImage(
|
||||||
|
image: CanvasImageSource,
|
||||||
|
sx: number,
|
||||||
|
sy: number,
|
||||||
|
sw: number,
|
||||||
|
sh: number
|
||||||
|
): SplittedImage {
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
const ctx = canvas.getContext('2d')!;
|
||||||
|
canvas.width = sw;
|
||||||
|
canvas.height = sh;
|
||||||
|
ctx.drawImage(image, sx, sy, sw, sh, 0, 0, sw, sh);
|
||||||
|
return { canvas, x: sx, y: sy };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切分画布
|
||||||
|
* @param canvas 要被切分的画布
|
||||||
|
* @param l 切分小块的边长
|
||||||
|
*/
|
||||||
|
function splitCanvas(canvas: HTMLCanvasElement, l: number): SplittedImage[] {
|
||||||
|
if (canvas.width / l < 2 || canvas.height / l < 2) {
|
||||||
|
console.warn('切分画布要求切分边长大于等于画布长宽的一半!');
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const w = canvas.width;
|
||||||
|
const h = canvas.height;
|
||||||
|
const numX = Math.floor(w / l);
|
||||||
|
const numY = Math.floor(h / l);
|
||||||
|
const rw = (w - numX * l) / 2;
|
||||||
|
const rh = (h - numY * l) / 2;
|
||||||
|
|
||||||
|
const res: SplittedImage[] = [];
|
||||||
|
|
||||||
|
if (rw > 0) {
|
||||||
|
if (rh > 0) {
|
||||||
|
res.push(
|
||||||
|
withImage(canvas, 0, 0, rw, rh),
|
||||||
|
withImage(canvas, 0, canvas.height - rh, rw, rh),
|
||||||
|
withImage(canvas, canvas.width - rw, 0, rw, rh),
|
||||||
|
withImage(canvas, canvas.width - rw, canvas.height - rh, rw, rh)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (const x of [0, canvas.width - rw]) {
|
||||||
|
for (let ny = 0; ny < numY; ny++) {
|
||||||
|
res.push(withImage(canvas, x, rh + l * ny, rw, l));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rh > 0) {
|
||||||
|
for (const y of [0, canvas.height - rh]) {
|
||||||
|
for (let nx = 0; nx < numX; nx++) {
|
||||||
|
res.push(withImage(canvas, rw + l * nx, y, l, rh));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let nx = 0; nx < numX; nx++) {
|
||||||
|
for (let ny = 0; ny < numY; ny++) {
|
||||||
|
res.push(withImage(canvas, rw + l * nx, rh + l * ny, l, l));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
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';
|
||||||
652
packages-user/legacy-plugin-client/src/fx/pointShader.ts
Normal file
652
packages-user/legacy-plugin-client/src/fx/pointShader.ts
Normal file
@ -0,0 +1,652 @@
|
|||||||
|
import { logger } from '@motajs/common';
|
||||||
|
import { UniformType, Shader, ShaderProgram, Transform } from '@motajs/render';
|
||||||
|
|
||||||
|
export const enum PointEffectType {
|
||||||
|
/**
|
||||||
|
* 无特效,在此之后的所有特效将会被截断,因此不要在空特效之后添加特效,也不要手动添加空特效
|
||||||
|
*/
|
||||||
|
None,
|
||||||
|
/**
|
||||||
|
* 反色特效,可与任意特效叠加\
|
||||||
|
* 参数分别为:\
|
||||||
|
* `data1: x, y, radius, decay` | 横坐标,纵坐标,半径,衰减开始半径\
|
||||||
|
* `data2: ratio, _, _, _` | 反色强度,空,空,空
|
||||||
|
*/
|
||||||
|
CircleInvert,
|
||||||
|
/**
|
||||||
|
* 灰度特效,可与任意特效叠加\
|
||||||
|
* 参数分别为:\
|
||||||
|
* `data1: x, y, radius, decay` | 横坐标,纵坐标,半径,衰减开始半径\
|
||||||
|
* `data2: ratio, _, _, _` | 黑白强度,空,空,空
|
||||||
|
*/
|
||||||
|
CircleGray,
|
||||||
|
/**
|
||||||
|
* 对比度特效,可与任意特效叠加\
|
||||||
|
* 参数分别为:\
|
||||||
|
* `data1: x, y, radius, decay` | 横坐标,纵坐标,半径,衰减开始半径\
|
||||||
|
* `data2: ratio, _, _, _` | 对比度强度(0表示不变,-1表示灰色,1表示2倍对比度),空,空,空
|
||||||
|
*/
|
||||||
|
CircleContrast,
|
||||||
|
/**
|
||||||
|
* 饱和度特效,可与任意特效叠加\
|
||||||
|
* 参数分别为:\
|
||||||
|
* `data1: x, y, radius, decay` | 横坐标,纵坐标,半径,衰减开始半径\
|
||||||
|
* `data2: ratio, _, _, _` | 对比度强度(0表示不变,-1表示灰色,1表示2倍饱和度),空,空,空
|
||||||
|
*/
|
||||||
|
CircleSaturate,
|
||||||
|
/**
|
||||||
|
* 饱和度特效,可与任意特效叠加\
|
||||||
|
* 参数分别为:\
|
||||||
|
* `data1: x, y, radius, decay` | 横坐标,纵坐标,半径,衰减开始半径\
|
||||||
|
* `data2: angle, _, _, _` | 旋转角度(0表示旋转0度,1表示旋转360度),空,空,空
|
||||||
|
*/
|
||||||
|
CircleHue,
|
||||||
|
/**
|
||||||
|
* 圆形扭曲特效\
|
||||||
|
* 参数分别为:\
|
||||||
|
* `data1: x, y, maxRaius, waveRadius` | 中心横坐标,中心纵坐标,波纹最大传播距离,波纹环的半径\
|
||||||
|
* `data2: amplitude, attenuation, linear, tangential` \
|
||||||
|
*   * `amplitude`: 震动幅度(1表示幅度为1个屏幕,受摄像机缩放影响)\
|
||||||
|
*   * `attenuation`: 衰减比例,即衰减速度\
|
||||||
|
*   * `linear`: 开始线性衰减时间(为确保波纹最终归于平静,在衰减时,会首先平方反比衰减,
|
||||||
|
* 然后到此值时转为线性衰减。此参数范围0-1,0.5表示从一半时间处开始线性衰减)\
|
||||||
|
*   * `tangential`: 切向扭曲比例(1表示与振动幅度一致)\
|
||||||
|
* `data3: startPhase, endPhase, _, _` | 起始位置相位(靠近波纹中心的位置),终止位置相位(远离波纹中心位置),空,空,
|
||||||
|
* 其中相位一个周期为2 * PI,填 Math.PI * 2 则表示相位从一个周期处开始
|
||||||
|
*/
|
||||||
|
CircleWarp,
|
||||||
|
/**
|
||||||
|
* 圆形切向扭曲特效\
|
||||||
|
* 参数分别为:\
|
||||||
|
* `data1: x, y, minRadius, maxRadius` | 中心横坐标,中心纵坐标,扭曲环内圈半径,扭曲环外圈半径
|
||||||
|
* (1表示扭曲了相位角的程度,例如Math.PI的相位,幅度为1,表示旋转整整一圈)\
|
||||||
|
* `data2: startPhase, endPhase, _, _` 起始位置相位(靠近波纹中心的位置),终止位置相位(远离波纹中心的位置),空,空
|
||||||
|
*/
|
||||||
|
CircleWarpTangetial,
|
||||||
|
/**
|
||||||
|
* 圆形亮度特效,可与任何特效叠加\
|
||||||
|
* 参数分别为:\
|
||||||
|
* `data1: x, y, radius, decay` | 中心横坐标,中心纵坐标,半径,衰减开始半径\
|
||||||
|
* `data2: ratio, _, _, _` | 亮度(0表示不变,1表示2倍亮度),空,空,空
|
||||||
|
*/
|
||||||
|
CircleBrightness
|
||||||
|
}
|
||||||
|
|
||||||
|
type EffectData = [x0: number, x1: number, x2: number, x3: number];
|
||||||
|
|
||||||
|
const warpEffect = new Set<PointEffectType>();
|
||||||
|
warpEffect
|
||||||
|
.add(PointEffectType.CircleWarp)
|
||||||
|
.add(PointEffectType.CircleWarpTangetial);
|
||||||
|
|
||||||
|
export class PointEffect {
|
||||||
|
/** 着色器程序 */
|
||||||
|
private program?: ShaderProgram;
|
||||||
|
/** 着色器渲染元素 */
|
||||||
|
private shader?: Shader;
|
||||||
|
|
||||||
|
/** 是否开始计时器 */
|
||||||
|
private started: boolean = false;
|
||||||
|
/** 计时器委托id */
|
||||||
|
private delegation: number = -1;
|
||||||
|
/** 特效id计数器,用于修改对应特效的数据 */
|
||||||
|
private effectId: number = 0;
|
||||||
|
/** 特效id到特效索引的映射 */
|
||||||
|
private idIndexMap: Map<number, number> = new Map();
|
||||||
|
/** 变换矩阵 */
|
||||||
|
private transform?: Transform;
|
||||||
|
|
||||||
|
/** 特效最大数量 */
|
||||||
|
private effectCount: number = 0;
|
||||||
|
/** 特效数据 */
|
||||||
|
private dataList: Float32Array = new Float32Array();
|
||||||
|
/** 经过矩阵变换操作后的特效数据 */
|
||||||
|
private transformed: Float32Array = new Float32Array();
|
||||||
|
/** 当前的数据指针,也就是下一次添加特效应该添加至哪 */
|
||||||
|
private dataPointer: number = 0;
|
||||||
|
/** 是否需要更新数据 */
|
||||||
|
private needUpdateData: boolean = false;
|
||||||
|
/** 需要在之后添加的特效 */
|
||||||
|
private toAdd: Set<number[]> = new Set();
|
||||||
|
/** 每个特效的开始时间 */
|
||||||
|
private startTime: Map<number, number> = new Map();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为着色器创建程序
|
||||||
|
* @param shader 着色器渲染元素
|
||||||
|
* @param itemCount 最大效果数量
|
||||||
|
*/
|
||||||
|
create(shader: Shader, itemCount: number) {
|
||||||
|
if (this.program || this.shader || this.started) return;
|
||||||
|
const program = shader.createProgram(ShaderProgram);
|
||||||
|
program.fs(generateFragment(itemCount));
|
||||||
|
program.requestCompile();
|
||||||
|
|
||||||
|
// 声明变量
|
||||||
|
program.defineUniform('u_count', shader.UNIFORM_1i);
|
||||||
|
program.defineUniform('u_screen', shader.UNIFORM_2f);
|
||||||
|
program.defineUniformBlock(
|
||||||
|
'EffectInfo',
|
||||||
|
itemCount * 16,
|
||||||
|
shader.gl.DYNAMIC_DRAW,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
this.program = program;
|
||||||
|
this.shader = shader;
|
||||||
|
this.effectCount = itemCount;
|
||||||
|
this.dataList = new Float32Array(itemCount * 16);
|
||||||
|
this.transformed = new Float32Array(itemCount * 16);
|
||||||
|
this.transform = shader.transform;
|
||||||
|
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在下一帧更新特效数据
|
||||||
|
*/
|
||||||
|
requestUpdate() {
|
||||||
|
if (this.dataList[0] !== PointEffectType.None) {
|
||||||
|
this.needUpdateData = true;
|
||||||
|
}
|
||||||
|
if (this.shader) this.shader.update(this.shader);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置本特效实例的变换矩阵,变换矩阵可以在设置特效位置时自动进行变换,配合摄像机视角
|
||||||
|
* @param tranform 变换矩阵
|
||||||
|
*/
|
||||||
|
setTransform(tranform: Transform) {
|
||||||
|
this.transform = tranform;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加一个特效,如果特效还未开始,那么会在其开始时添加特效,注意特效数据必须填四位,不足者补0
|
||||||
|
* @param type 特效类型
|
||||||
|
* @param startTime 特效开始时间
|
||||||
|
* @param time 特效持续时间
|
||||||
|
* @param data1 第一组自定义数据,可选
|
||||||
|
* @param data2 第二组自定义数据,可选
|
||||||
|
* @param data3 第三组自定义数据,可选
|
||||||
|
* @returns 这个特效的唯一id,可用于设置特效
|
||||||
|
*/
|
||||||
|
addEffect(
|
||||||
|
type: PointEffectType,
|
||||||
|
startTime: number,
|
||||||
|
time: number,
|
||||||
|
data1: EffectData = [0, 0, 0, 0],
|
||||||
|
data2: EffectData = [0, 0, 0, 0],
|
||||||
|
data3: EffectData = [0, 0, 0, 0]
|
||||||
|
) {
|
||||||
|
if (type === PointEffectType.None) return -1;
|
||||||
|
const now = Date.now();
|
||||||
|
// 如果已经结束,那么什么都不干
|
||||||
|
if (now > time + startTime) return -1;
|
||||||
|
// 填充 0 是因为 std140 布局中,每个数据都会占据 16 的倍数个字节
|
||||||
|
// 不过第二项填充一个整数,是为了能够对每个特效进行标识,从而能够对某个特效进行修改
|
||||||
|
const id = this.effectId++;
|
||||||
|
// 第三项为特效的进度
|
||||||
|
const data = [type, id, 0, time, ...data1, ...data2, ...data3];
|
||||||
|
this.startTime.set(id, startTime);
|
||||||
|
|
||||||
|
if (now >= startTime) {
|
||||||
|
this.addEffectToList(data);
|
||||||
|
} else {
|
||||||
|
// 如果还没开始,那么添加至预备队列
|
||||||
|
this.toAdd.add(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 立刻删除一个特效
|
||||||
|
* @param id 要删除的特效的id
|
||||||
|
*/
|
||||||
|
deleteEffect(id: number) {
|
||||||
|
this.removeEffect(this.findIndexById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除所有特性
|
||||||
|
*/
|
||||||
|
clearEffect() {
|
||||||
|
this.dataList.fill(0);
|
||||||
|
this.dataPointer = 0;
|
||||||
|
this.needUpdateData = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置一个特效的数据,注意特效数据必须填四位,不足者补0
|
||||||
|
* @param id 特效id
|
||||||
|
* @param data1 第一组自定义数据,可选
|
||||||
|
* @param data2 第二组自定义数据,可选
|
||||||
|
* @param data3 第三组自定义数据,可选
|
||||||
|
*/
|
||||||
|
setEffect(
|
||||||
|
id: number,
|
||||||
|
data1?: EffectData,
|
||||||
|
data2?: EffectData,
|
||||||
|
data3?: EffectData
|
||||||
|
) {
|
||||||
|
const index = this.findIndexById(id);
|
||||||
|
if (index >= this.dataPointer || index === -1) return;
|
||||||
|
const list = this.dataList;
|
||||||
|
if (data1) {
|
||||||
|
list.set(data1, index * 16 + 4);
|
||||||
|
}
|
||||||
|
if (data2) {
|
||||||
|
list.set(data2, index * 16 + 8);
|
||||||
|
}
|
||||||
|
if (data3) {
|
||||||
|
list.set(data3, index * 16 + 12);
|
||||||
|
}
|
||||||
|
this.needUpdateData = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private findIndexById(id: number) {
|
||||||
|
const map = this.idIndexMap.get(id);
|
||||||
|
if (map !== void 0) return map;
|
||||||
|
let index = -1;
|
||||||
|
const list = this.dataList;
|
||||||
|
for (let i = 0; i < this.effectCount; i++) {
|
||||||
|
const realIndex = i * 16 + 1;
|
||||||
|
if (list[realIndex] === id) {
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (index !== -1) {
|
||||||
|
this.idIndexMap.set(id, index);
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
private addEffectToList(data: number[]) {
|
||||||
|
if (this.dataPointer >= this.effectCount) {
|
||||||
|
logger.warn(1101);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const type = data[0];
|
||||||
|
const list = this.dataList;
|
||||||
|
if (warpEffect.has(type)) {
|
||||||
|
list.copyWithin(16, 0);
|
||||||
|
list.set(data, 0);
|
||||||
|
this.dataPointer++;
|
||||||
|
this.idIndexMap.clear();
|
||||||
|
} else {
|
||||||
|
const id = data[1];
|
||||||
|
list.set(data, this.dataPointer * 16);
|
||||||
|
this.idIndexMap.set(id, this.dataPointer);
|
||||||
|
this.dataPointer++;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.needUpdateData = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private removeEffect(index: number) {
|
||||||
|
if (index >= this.effectCount) return;
|
||||||
|
if (this.dataPointer === 0) return;
|
||||||
|
const list = this.dataList;
|
||||||
|
const id = list[index * 16 + 1];
|
||||||
|
this.startTime.delete(id);
|
||||||
|
list.copyWithin(index * 16, index * 16 + 16);
|
||||||
|
list.fill(0, -16, -1);
|
||||||
|
this.dataPointer--;
|
||||||
|
this.needUpdateData = true;
|
||||||
|
this.idIndexMap.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用这个特效作为着色器程序
|
||||||
|
*/
|
||||||
|
use() {
|
||||||
|
if (!this.shader || !this.program) return;
|
||||||
|
this.shader.useProgram(this.program);
|
||||||
|
const uCount =
|
||||||
|
this.program.getUniform<UniformType.Uniform1f>('u_count');
|
||||||
|
uCount?.set(this.effectCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始计时器
|
||||||
|
*/
|
||||||
|
start() {
|
||||||
|
if (!this.shader || !this.program) return;
|
||||||
|
this.started = true;
|
||||||
|
const block = this.program.getUniformBlock('EffectInfo')!;
|
||||||
|
const screen =
|
||||||
|
this.program.getUniform<UniformType.Uniform2f>('u_screen');
|
||||||
|
const { width, height } = this.shader;
|
||||||
|
const scale = core.domStyle.scale * devicePixelRatio;
|
||||||
|
screen?.set(width * scale, height * scale);
|
||||||
|
|
||||||
|
// 不知道性能怎么样,只能试试看了
|
||||||
|
this.delegation = this.shader.delegateTicker(() => {
|
||||||
|
if (!this.started) {
|
||||||
|
this.shader?.removeTicker(this.delegation);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const now = Date.now();
|
||||||
|
const toDelete = new Set<number[]>();
|
||||||
|
this.toAdd.forEach(v => {
|
||||||
|
const id = v[1];
|
||||||
|
const startTime = this.startTime.get(id);
|
||||||
|
if (!startTime) return;
|
||||||
|
const time = v[3];
|
||||||
|
if (now > startTime + time) {
|
||||||
|
toDelete.add(v);
|
||||||
|
} else if (now >= startTime) {
|
||||||
|
this.addEffectToList(v);
|
||||||
|
toDelete.add(v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
toDelete.forEach(v => this.toAdd.delete(v));
|
||||||
|
|
||||||
|
const toRemove: number[] = [];
|
||||||
|
const list = this.dataList;
|
||||||
|
|
||||||
|
// 倒序以保证删除时不会影响到其他删除
|
||||||
|
for (let i = this.effectCount - 1; i >= 0; i--) {
|
||||||
|
const type = list[i * 16];
|
||||||
|
if (type === PointEffectType.None) continue;
|
||||||
|
const id = list[i * 16 + 1];
|
||||||
|
const start = this.startTime.get(id);
|
||||||
|
if (!start) {
|
||||||
|
toRemove.push(i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const time = list[i * 16 + 3];
|
||||||
|
const progress = (now - start) / time;
|
||||||
|
if (progress > 1) toRemove.push(i);
|
||||||
|
list[i * 16 + 2] = progress;
|
||||||
|
}
|
||||||
|
toRemove.forEach(v => {
|
||||||
|
this.removeEffect(v);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.needUpdateData) {
|
||||||
|
const list = this.dataList;
|
||||||
|
const transformed = this.transformed;
|
||||||
|
transformed.set(list);
|
||||||
|
this.transformData();
|
||||||
|
block.set(transformed);
|
||||||
|
this.needUpdateData = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结束计时器
|
||||||
|
*/
|
||||||
|
end() {
|
||||||
|
if (!this.started || !this.shader || !this.program) return;
|
||||||
|
this.shader.removeTicker(this.delegation);
|
||||||
|
this.started = false;
|
||||||
|
this.dataList.fill(0);
|
||||||
|
this.dataPointer = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.end();
|
||||||
|
if (this.shader && this.program) {
|
||||||
|
this.shader.deleteProgram(this.program);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private transformData() {
|
||||||
|
const transform = this.transform;
|
||||||
|
if (!transform) return;
|
||||||
|
const count = this.effectCount;
|
||||||
|
const list = this.dataList;
|
||||||
|
const transformed = this.transformed;
|
||||||
|
let ratio = core.domStyle.scale;
|
||||||
|
if (this.shader?.highResolution) ratio *= devicePixelRatio;
|
||||||
|
const scale = transform.scaleX * ratio;
|
||||||
|
const scaleTransform = new Transform();
|
||||||
|
scaleTransform.scale(ratio, ratio);
|
||||||
|
const scaled = scaleTransform.multiply(transform);
|
||||||
|
const fixedHeight = core._PY_ * ratio;
|
||||||
|
|
||||||
|
const transformXY = (index: number) => {
|
||||||
|
const x = list[index + 4];
|
||||||
|
const y = list[index + 5];
|
||||||
|
const [tx, ty] = Transform.transformed(scaled, x, y);
|
||||||
|
transformed[index + 4] = tx;
|
||||||
|
transformed[index + 5] = fixedHeight - ty;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
const index = i * 16;
|
||||||
|
const type = list[index];
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case PointEffectType.CircleGray:
|
||||||
|
case PointEffectType.CircleInvert:
|
||||||
|
case PointEffectType.CircleContrast:
|
||||||
|
case PointEffectType.CircleSaturate:
|
||||||
|
case PointEffectType.CircleHue:
|
||||||
|
case PointEffectType.CircleBrightness:
|
||||||
|
case PointEffectType.CircleWarpTangetial: {
|
||||||
|
transformXY(index);
|
||||||
|
transformed[index + 6] *= scale;
|
||||||
|
transformed[index + 7] *= scale;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PointEffectType.CircleWarp: {
|
||||||
|
transformXY(index);
|
||||||
|
transformed[index + 6] *= scale;
|
||||||
|
transformed[index + 7] *= scale;
|
||||||
|
transformed[index + 8] *= scale;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PointEffectType.None: {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateFragment(itemCount: number) {
|
||||||
|
return /* glsl */ `
|
||||||
|
uniform int u_count;
|
||||||
|
uniform vec2 u_screen; // 画布长宽
|
||||||
|
|
||||||
|
out vec4 outColor;
|
||||||
|
|
||||||
|
struct Effect {
|
||||||
|
vec2 type; // 效果类型
|
||||||
|
vec2 time; // 开始时间,持续时长
|
||||||
|
vec4 info1; // 第一组信息,表示自定义参数
|
||||||
|
vec4 info2; // 第二组信息,表示自定义参数
|
||||||
|
vec4 info3; // 第三组信息,表示自定义参数
|
||||||
|
};
|
||||||
|
|
||||||
|
layout (std140) uniform EffectInfo {
|
||||||
|
Effect effects[${itemCount}];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper function: RGB to HSL conversion
|
||||||
|
vec3 rgb2hsl(vec3 color) {
|
||||||
|
float maxC = max(max(color.r, color.g), color.b);
|
||||||
|
float minC = min(min(color.r, color.g), color.b);
|
||||||
|
float delta = maxC - minC;
|
||||||
|
|
||||||
|
float h = 0.0;
|
||||||
|
float s = 0.0;
|
||||||
|
float l = (maxC + minC) * 0.5;
|
||||||
|
|
||||||
|
if (delta != 0.0) {
|
||||||
|
s = (l < 0.5) ? delta / (maxC + minC) : delta / (2.0 - maxC - minC);
|
||||||
|
|
||||||
|
if (maxC == color.r) {
|
||||||
|
h = (color.g - color.b) / delta + (color.g < color.b ? 6.0 : 0.0);
|
||||||
|
} else if (maxC == color.g) {
|
||||||
|
h = (color.b - color.r) / delta + 2.0;
|
||||||
|
} else {
|
||||||
|
h = (color.r - color.g) / delta + 4.0;
|
||||||
|
}
|
||||||
|
h /= 6.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vec3(h, s, l);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function: Hue to RGB conversion
|
||||||
|
float hue2rgb(float p, float q, float t) {
|
||||||
|
if (t < 0.0) t += 1.0;
|
||||||
|
if (t > 1.0) t -= 1.0;
|
||||||
|
if (t < 1.0 / 6.0) return p + (q - p) * 6.0 * t;
|
||||||
|
if (t < 1.0 / 2.0) return q;
|
||||||
|
if (t < 2.0 / 3.0) return p + (q - p) * (2.0 / 3.0 - t) * 6.0;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function: HSL to RGB conversion
|
||||||
|
vec3 hsl2rgb(vec3 hsl) {
|
||||||
|
float h = hsl.x;
|
||||||
|
float s = hsl.y;
|
||||||
|
float l = hsl.z;
|
||||||
|
|
||||||
|
float r, g, b;
|
||||||
|
|
||||||
|
if (s == 0.0) {
|
||||||
|
r = g = b = l; // Achromatic (gray)
|
||||||
|
} else {
|
||||||
|
float q = (l < 0.5) ? (l * (1.0 + s)) : (l + s - l * s);
|
||||||
|
float p = 2.0 * l - q;
|
||||||
|
|
||||||
|
r = hue2rgb(p, q, h + 1.0 / 3.0);
|
||||||
|
g = hue2rgb(p, q, h);
|
||||||
|
b = hue2rgb(p, q, h - 1.0 / 3.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return vec3(r, g, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// x: 横坐标 y: 纵坐标 z: 半径 w: 衰减开始半径
|
||||||
|
// 计算圆形效果的衰减程度
|
||||||
|
float calCircleDecay(vec4 data) {
|
||||||
|
float dis = distance(data.xy, gl_FragCoord.xy);
|
||||||
|
if (dis <= data.w) return 1.0;
|
||||||
|
if (dis >= data.z) return 0.0;
|
||||||
|
if (data.z <= data.w) return 1.0;
|
||||||
|
return 1.0 - (dis - data.w) / (data.z - data.w);
|
||||||
|
}
|
||||||
|
|
||||||
|
// data1: x, y, maxRadius, waveRadius
|
||||||
|
// data2: amplitude, attenuation, linear, _
|
||||||
|
// 计算波纹扭曲的衰减程度,从 x = 1 的平方反比函数开始,衰减至 x = 1 + attenuation,然后线性衰减
|
||||||
|
float calWarpDecay(float progress, vec4 data1, vec4 data2) {
|
||||||
|
if (progress >= data2.z) {
|
||||||
|
float end = 1.0 / pow(1.0 + data2.y, 2.0);
|
||||||
|
return end - end * (progress - data2.z) / (1.0 - data2.z);
|
||||||
|
} else {
|
||||||
|
return 1.0 / pow(1.0 + (progress / data2.z) * data2.y, 2.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 coord = v_texCoord;
|
||||||
|
vec4 color = texture(u_sampler, v_texCoord);
|
||||||
|
for (int i = 0; i < u_count; i++) {
|
||||||
|
Effect effect = effects[i];
|
||||||
|
int effectType = int(effect.type.x);
|
||||||
|
vec2 timeInfo = effect.time;
|
||||||
|
vec4 data1 = effect.info1;
|
||||||
|
vec4 data2 = effect.info2;
|
||||||
|
vec4 data3 = effect.info3;
|
||||||
|
if (effectType == ${PointEffectType.None}) break;
|
||||||
|
float progress = timeInfo.x;
|
||||||
|
|
||||||
|
// 下面开始实施对应的着色器特效
|
||||||
|
|
||||||
|
// 反色,data1: x y radius decay;data2: ratio _ _ _
|
||||||
|
if (effectType == ${PointEffectType.CircleInvert}) {
|
||||||
|
float ratio = data2.x * calCircleDecay(data1);
|
||||||
|
if (ratio > 0.0) {
|
||||||
|
vec3 delta = (1.0 - 2.0 * color.rgb) * ratio;
|
||||||
|
color.rgb = clamp(color.rgb + delta, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 灰度,data1: x y radius decay;data2: ratio _ _ _
|
||||||
|
else if (effectType == ${PointEffectType.CircleGray}) {
|
||||||
|
float ratio = data2.x * calCircleDecay(data1);
|
||||||
|
if (ratio > 0.0) {
|
||||||
|
float gray = dot(color.rgb, vec3(0.2126, 0.7125, 0.0722));
|
||||||
|
vec3 grayed = color.rgb - gray;
|
||||||
|
color = vec4(color.rgb - grayed * ratio, 1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 亮度,data1: x y radius decay;data2: ratio _ _ _
|
||||||
|
else if (effectType == ${PointEffectType.CircleBrightness}) {
|
||||||
|
float ratio = data2.x * calCircleDecay(data1) + 1.0;
|
||||||
|
if (ratio > 0.0) {
|
||||||
|
color.rgb *= ratio;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 对比度,data1: x y radius decay;data2: ratio _ _ _
|
||||||
|
else if (effectType == ${PointEffectType.CircleContrast}) {
|
||||||
|
float ratio = data2.x * calCircleDecay(data1) + 1.0;
|
||||||
|
if (ratio > 0.0) {
|
||||||
|
color.rgb = mix(vec3(0.5), color.rgb, ratio);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 饱和度,data1: x y radius decay;data2: ratio _ _ _
|
||||||
|
else if (effectType == ${PointEffectType.CircleSaturate}) {
|
||||||
|
float ratio = data2.x * calCircleDecay(data1) + 1.0;
|
||||||
|
if (ratio > 0.0) {
|
||||||
|
float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114));
|
||||||
|
color.rgb = mix(vec3(gray), color.rgb, ratio);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 色相旋转,data1: x y radius decay;data2: angle _ _ _
|
||||||
|
else if (effectType == ${PointEffectType.CircleHue}) {
|
||||||
|
float ratio = data2.x * calCircleDecay(data1);
|
||||||
|
if (ratio > 0.0) {
|
||||||
|
vec3 hsl = rgb2hsl(color.rgb);
|
||||||
|
hsl.x = mod(hsl.x + data2.x * ratio, 1.0);
|
||||||
|
color.rgb = hsl2rgb(hsl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 扭曲,data1: x, y, maxRadius, waveRadius; data2: amplitude, attenuation, linear, _
|
||||||
|
// data3: startPhase, endPhase, _, _
|
||||||
|
else if (effectType == ${PointEffectType.CircleWarp}) {
|
||||||
|
float dis = distance(data1.xy, gl_FragCoord.xy);
|
||||||
|
// 当前半径
|
||||||
|
float radius = progress * data1.z;
|
||||||
|
if (dis > radius + data1.w || dis < radius - data1.w) continue;
|
||||||
|
float ratio = data2.x * calWarpDecay(progress, data1, data2);
|
||||||
|
float halfRadius = data1.w / 2.0;
|
||||||
|
if (ratio > 0.0 && abs(dis - radius) <= halfRadius) {
|
||||||
|
// 计算当前相位
|
||||||
|
float x = ((dis - radius) / data1.w + 0.5) * (data3.y - data3.x) + data3.x;
|
||||||
|
float wave = sin(x) * ratio;
|
||||||
|
float xRatio = (gl_FragCoord.x - data1.x) / dis;
|
||||||
|
float yRatio = (gl_FragCoord.y - data1.y) / dis;
|
||||||
|
coord.x += wave * xRatio + wave * yRatio * data2.w;
|
||||||
|
coord.y += wave * yRatio + wave * xRatio * data2.w;
|
||||||
|
color = texture(u_sampler, coord);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 切向扭曲,data1: x, y, minRadius, maxRadius; data2: startPhase, endPhase, _, _
|
||||||
|
else if (effectType == ${PointEffectType.CircleWarpTangetial}) {
|
||||||
|
float dis = distance(data1.xy, gl_FragCoord.xy);
|
||||||
|
float ratio = (dis - data1.z) / (data1.w - data1.z);
|
||||||
|
if (ratio <= 1.0 && ratio > 0.0) {
|
||||||
|
float phase = ratio * (data2.y - data2.x) + data2.x;
|
||||||
|
float waveSin = sin(phase);
|
||||||
|
float waveCos = cos(phase);
|
||||||
|
// 相对坐标
|
||||||
|
vec2 relCoord = gl_FragCoord.xy - data1.xy;
|
||||||
|
coord.x = (data1.x + relCoord.x * waveCos - relCoord.y * waveSin) / u_screen.x;
|
||||||
|
coord.y = 1.0 - (data1.y + relCoord.x * waveSin + relCoord.y * waveCos) / u_screen.y;
|
||||||
|
color = texture(u_sampler, coord);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outColor = color;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
@ -2,4 +2,6 @@ if (import.meta.env.DEV) {
|
|||||||
import('./dev/hotReload');
|
import('./dev/hotReload');
|
||||||
}
|
}
|
||||||
|
|
||||||
export {};
|
export * from './boss';
|
||||||
|
export * from './chase';
|
||||||
|
export * from './fx';
|
||||||
|
|||||||
19
packages-user/legacy-plugin-data/src/chase.ts
Normal file
19
packages-user/legacy-plugin-data/src/chase.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
export function chaseInit1() {
|
||||||
|
const ids: FloorIds[] = ['MT13', 'MT14', 'MT15'];
|
||||||
|
const toRemove: [number, number, FloorIds][] = [];
|
||||||
|
ids.forEach(v => {
|
||||||
|
core.status.maps[v].cannotMoveDirectly = true;
|
||||||
|
core.extractBlocks(v);
|
||||||
|
core.status.maps[v].blocks.forEach(vv => {
|
||||||
|
if (
|
||||||
|
['animates', 'items'].includes(vv.event.cls) &&
|
||||||
|
!vv.event.id.endsWith('Portal')
|
||||||
|
) {
|
||||||
|
toRemove.push([vv.x, vv.y, v]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
toRemove.forEach(v => {
|
||||||
|
core.removeBlock(...v);
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -1,13 +1,22 @@
|
|||||||
|
import { has, ofDir } from '@user/data-utils';
|
||||||
|
|
||||||
export function createCheckBlock() {
|
export function createCheckBlock() {
|
||||||
// 伤害弹出
|
// 伤害弹出
|
||||||
// 复写阻激夹域检测
|
// 复写阻激夹域检测
|
||||||
control.prototype.checkBlock = function () {
|
control.prototype.checkBlock = function (forceMockery: boolean = false) {
|
||||||
const x = core.getHeroLoc('x'),
|
const x = core.getHeroLoc('x'),
|
||||||
y = core.getHeroLoc('y'),
|
y = core.getHeroLoc('y'),
|
||||||
loc = x + ',' + y;
|
loc = x + ',' + y;
|
||||||
const info = core.status.thisMap.enemy.mapDamage[loc];
|
const info = core.status.thisMap.enemy.mapDamage[loc];
|
||||||
const damage = info?.damage;
|
const damage = info?.damage;
|
||||||
if (damage) {
|
if (damage) {
|
||||||
|
if (!main.replayChecking) {
|
||||||
|
// addPop(
|
||||||
|
// (x - core.bigmap.offsetX / 32) * 32 + 12,
|
||||||
|
// (y - core.bigmap.offsetY / 32) * 32 + 20,
|
||||||
|
// (-damage).toString()
|
||||||
|
// );
|
||||||
|
}
|
||||||
core.status.hero.hp -= damage;
|
core.status.hero.hp -= damage;
|
||||||
const type = [...info.type];
|
const type = [...info.type];
|
||||||
const text = type.join(',') || '伤害';
|
const text = type.join(',') || '伤害';
|
||||||
@ -24,5 +33,120 @@ export function createCheckBlock() {
|
|||||||
core.updateStatusBar();
|
core.updateStatusBar();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
checkMockery(loc, forceMockery);
|
||||||
|
checkHunt(loc);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function checkMockery(loc: string, force: boolean = false) {
|
||||||
|
if (core.status.lockControl && !force) return;
|
||||||
|
const mockery = core.status.thisMap.enemy.mapDamage[loc]?.mockery;
|
||||||
|
if (mockery) {
|
||||||
|
mockery.sort((a, b) => (a[0] === b[0] ? a[1] - b[1] : a[0] - b[0]));
|
||||||
|
const action = [];
|
||||||
|
const [tx, ty] = mockery[0];
|
||||||
|
let { x, y } = core.status.hero.loc;
|
||||||
|
const dir = x > tx ? 'left' : x < tx ? 'right' : y > ty ? 'up' : 'down';
|
||||||
|
const { x: dx, y: dy } = core.utils.scan[dir];
|
||||||
|
|
||||||
|
action.push({ type: 'forbidSave', forbid: true });
|
||||||
|
action.push({ type: 'changePos', direction: dir });
|
||||||
|
const blocks = core.getMapBlocksObj();
|
||||||
|
while (true) {
|
||||||
|
x += dx;
|
||||||
|
y += dy;
|
||||||
|
const block = blocks[`${x},${y}`];
|
||||||
|
if (block) {
|
||||||
|
if (
|
||||||
|
[
|
||||||
|
'animates',
|
||||||
|
'autotile',
|
||||||
|
'tileset',
|
||||||
|
'npcs',
|
||||||
|
'npc48',
|
||||||
|
'terrains'
|
||||||
|
].includes(block.event.cls) &&
|
||||||
|
block.event.noPass
|
||||||
|
) {
|
||||||
|
action.push(
|
||||||
|
{
|
||||||
|
type: 'hide',
|
||||||
|
loc: [[x, y]],
|
||||||
|
remove: true,
|
||||||
|
time: 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'function',
|
||||||
|
function: `function() { core.removeGlobalAnimate(${x}, ${y}) }`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'animate',
|
||||||
|
name: 'hand',
|
||||||
|
loc: [x, y],
|
||||||
|
async: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (block.event.cls.startsWith('enemy')) {
|
||||||
|
action.push({ type: 'moveAction' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
action.push({ type: 'moveAction' });
|
||||||
|
if (x === tx && y === ty) break;
|
||||||
|
}
|
||||||
|
action.push({
|
||||||
|
type: 'function',
|
||||||
|
function: `function() { core.checkBlock(true); }`
|
||||||
|
});
|
||||||
|
action.push({ type: 'stopAsync' });
|
||||||
|
action.push({ type: 'forbidSave' });
|
||||||
|
core.insertAction(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkHunt(loc: string) {
|
||||||
|
const hunt = core.status.thisMap.enemy.mapDamage[loc]?.hunt;
|
||||||
|
if (!hunt) return;
|
||||||
|
const { x: hx, y: hy } = core.status.hero.loc;
|
||||||
|
|
||||||
|
const action: any = [];
|
||||||
|
hunt.sort((a, b) => {
|
||||||
|
return a[0] === b[0] ? a[1] - b[1] : a[0] - b[0];
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const [x, y, dir] of hunt) {
|
||||||
|
const [tx, ty] = ofDir(x, y, dir);
|
||||||
|
if (core.getBlock(tx, ty)) continue;
|
||||||
|
action.push(
|
||||||
|
{
|
||||||
|
type: 'move',
|
||||||
|
loc: [x, y],
|
||||||
|
time: 100,
|
||||||
|
keep: true,
|
||||||
|
steps: [`${dir}:1`]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'update'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (has(hy) && x === hx) {
|
||||||
|
if (Math.abs(y - hy) <= 2) {
|
||||||
|
action.push({
|
||||||
|
type: 'battle',
|
||||||
|
loc: [tx, ty]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (has(hx) && y === hy) {
|
||||||
|
if (Math.abs(x - hx) <= 2) {
|
||||||
|
action.push({
|
||||||
|
type: 'battle',
|
||||||
|
loc: [tx, ty]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
core.insertAction(action);
|
||||||
|
}
|
||||||
|
|||||||
@ -5,3 +5,4 @@ export function createEnemy() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export * from './checkblock';
|
export * from './checkblock';
|
||||||
|
export * from './remainEnemy';
|
||||||
|
|||||||
54
packages-user/legacy-plugin-data/src/enemy/remainEnemy.ts
Normal file
54
packages-user/legacy-plugin-data/src/enemy/remainEnemy.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// todo: 优化,直接调用 floor.enemy.list 进行计算
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查漏怪
|
||||||
|
*/
|
||||||
|
export function checkRemainEnemy(floorIds: FloorIds[]) {
|
||||||
|
const enemy: Partial<Record<FloorIds, { loc: LocArr; id: EnemyIds }[]>> =
|
||||||
|
{};
|
||||||
|
floorIds.forEach(v => {
|
||||||
|
core.extractBlocks(v);
|
||||||
|
const blocks = core.status.maps[v].blocks;
|
||||||
|
blocks.forEach(block => {
|
||||||
|
if (!block.event.cls.startsWith('enemy') || block.disable) return;
|
||||||
|
const id: EnemyIds = block.event.id as EnemyIds;
|
||||||
|
enemy[v] ??= [];
|
||||||
|
const info = enemy[v]!;
|
||||||
|
info.push({ loc: [block.x, block.y], id });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return enemy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取剩余怪物字符串
|
||||||
|
*/
|
||||||
|
export function getRemainEnemyString(floorIds: FloorIds[]) {
|
||||||
|
const enemy = checkRemainEnemy(floorIds);
|
||||||
|
const str = [];
|
||||||
|
let now = [];
|
||||||
|
for (const floor in enemy) {
|
||||||
|
const all: { loc: LocArr; id: EnemyIds }[] = enemy[floor as FloorIds]!;
|
||||||
|
const remain: Partial<Record<EnemyIds, number>> = {};
|
||||||
|
all.forEach(v => {
|
||||||
|
const id = v.id;
|
||||||
|
remain[id] ??= 0;
|
||||||
|
remain[id]!++;
|
||||||
|
});
|
||||||
|
const title = core.status.maps[floor as FloorIds].title;
|
||||||
|
for (const id in remain) {
|
||||||
|
const name = core.material.enemys[id as EnemyIds].name;
|
||||||
|
now.push(`${title}(${floor}): ${name} * ${remain[id as EnemyIds]}`);
|
||||||
|
if (now.length === 10) {
|
||||||
|
str.push(now.join('\n'));
|
||||||
|
now = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (now.length > 0) {
|
||||||
|
str.push(now.join('\n'));
|
||||||
|
str[0] = `当前剩余怪物:\n${str[0]}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
@ -18,9 +18,11 @@ export function createLegacy() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export * from './enemy';
|
export * from './enemy';
|
||||||
|
export * from './chase';
|
||||||
export * from './fallback';
|
export * from './fallback';
|
||||||
export * from './fiveLayer';
|
export * from './fiveLayer';
|
||||||
export * from './removeMap';
|
export * from './removeMap';
|
||||||
export * from './replay';
|
export * from './replay';
|
||||||
export * from './shop';
|
export * from './shop';
|
||||||
|
export * from './skill';
|
||||||
export * from './ui';
|
export * from './ui';
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
|
import { HeroSkill, getSkillFromIndex, upgradeSkill } from '@user/data-state';
|
||||||
import { canOpenShop } from './shop';
|
import { canOpenShop } from './shop';
|
||||||
import { hook } from '@user/data-base';
|
import { hook } from '@user/data-base';
|
||||||
|
import { jumpSkill } from './skill';
|
||||||
|
|
||||||
|
const replayableSettings = ['autoSkill'];
|
||||||
|
|
||||||
let cliping = false;
|
let cliping = false;
|
||||||
let startIndex = 0;
|
let startIndex = 0;
|
||||||
@ -28,6 +32,51 @@ export function initReplay() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 注册修改设置的录像操作
|
||||||
|
const settingNameMap: Record<string, string> = {
|
||||||
|
autoSkill: '自动切换技能'
|
||||||
|
};
|
||||||
|
core.registerReplayAction('settings', name => {
|
||||||
|
if (!name.startsWith('set:')) return false;
|
||||||
|
const [, setting, value] = name.split(':');
|
||||||
|
const v = eval(value);
|
||||||
|
if (typeof v !== 'boolean') return false;
|
||||||
|
if (!replayableSettings.includes(setting)) return false;
|
||||||
|
switch (setting) {
|
||||||
|
case 'autoSkill':
|
||||||
|
HeroSkill.setAutoSkill(v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const settingName = settingNameMap[setting];
|
||||||
|
core.status.route.push(name);
|
||||||
|
if (settingName) {
|
||||||
|
tipAndWait(`切换设置:${settingName}`, 1000).then(() => {
|
||||||
|
core.replay();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
core.replay();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
core.registerReplayAction('upgradeSkill', name => {
|
||||||
|
if (!name.startsWith('skill:')) return false;
|
||||||
|
const skill = parseInt(name.slice(6));
|
||||||
|
const success = upgradeSkill(skill);
|
||||||
|
const s = getSkillFromIndex(skill);
|
||||||
|
const skillName = s?.title;
|
||||||
|
core.status.route.push(name);
|
||||||
|
if (!success) {
|
||||||
|
const { tip } = Mota.require('@motajs/legacy-ui');
|
||||||
|
tip('error', `升级技能:${skillName}失败`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
tipAndWait(`升级技能:${skillName}`, 1000).then(() => {
|
||||||
|
core.replay();
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
// 商店
|
// 商店
|
||||||
let shopOpened = false;
|
let shopOpened = false;
|
||||||
let openedShopId = '';
|
let openedShopId = '';
|
||||||
@ -94,6 +143,89 @@ export function initReplay() {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const skillNameMap: Record<string, string> = {
|
||||||
|
Blade: '断灭之刃',
|
||||||
|
Shield: '铸剑为盾'
|
||||||
|
};
|
||||||
|
function skillAction(skill: string) {
|
||||||
|
let toEmit = skill;
|
||||||
|
|
||||||
|
// 兼容性处理
|
||||||
|
if (skill === '1') {
|
||||||
|
toEmit = 'Blade';
|
||||||
|
} else if (skill === '2') {
|
||||||
|
toEmit = 'Jump';
|
||||||
|
} else if (skill === '3') {
|
||||||
|
toEmit = 'Shield';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toEmit === 'Jump') {
|
||||||
|
if (
|
||||||
|
!flags.onChase &&
|
||||||
|
!core.status.floorId.startsWith('tower') &&
|
||||||
|
HeroSkill.learnedSkill(HeroSkill.Jump)
|
||||||
|
) {
|
||||||
|
const success = jumpSkill();
|
||||||
|
core.status.route.push(`useSkill:${toEmit}`);
|
||||||
|
if (!success) core.replay();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if (core.hasItem('pickaxe')) {
|
||||||
|
core.useItem('pickaxe');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (HeroSkill.getAutoSkill()) {
|
||||||
|
core.replay();
|
||||||
|
core.updateStatusBar();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
let num = HeroSkill.Skill.None;
|
||||||
|
switch (toEmit) {
|
||||||
|
case 'Blade':
|
||||||
|
num = HeroSkill.Blade;
|
||||||
|
break;
|
||||||
|
case 'Shield':
|
||||||
|
num = HeroSkill.Shield;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
HeroSkill.toggleSkill(num);
|
||||||
|
}
|
||||||
|
core.updateStatusBar();
|
||||||
|
const name = skillNameMap[toEmit];
|
||||||
|
core.status.route.push(`useSkill:${toEmit}`);
|
||||||
|
if (name) {
|
||||||
|
tipAndWait(`切换技能:${name}`, 1000).then(() => {
|
||||||
|
core.replay();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
core.replay();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
core.registerReplayAction('useSkill', name => {
|
||||||
|
if (!name.startsWith('useSkill:')) return false;
|
||||||
|
const [, skill] = name.split(':');
|
||||||
|
return skillAction(skill);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 兼容旧版
|
||||||
|
core.registerReplayAction('key', name => {
|
||||||
|
if (!name.startsWith('key:')) return false;
|
||||||
|
const key = parseInt(name.slice(4));
|
||||||
|
|
||||||
|
if (key === 49) {
|
||||||
|
return skillAction('1');
|
||||||
|
} else if (key === 50) {
|
||||||
|
return skillAction('2');
|
||||||
|
} else if (key === 51) {
|
||||||
|
return skillAction('3');
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
core.registerReplayAction('fly', action => {
|
core.registerReplayAction('fly', action => {
|
||||||
if (!action.startsWith('fly:')) return false;
|
if (!action.startsWith('fly:')) return false;
|
||||||
const floorId = action.slice(4) as FloorIds;
|
const floorId = action.slice(4) as FloorIds;
|
||||||
|
|||||||
232
packages-user/legacy-plugin-data/src/skill.ts
Normal file
232
packages-user/legacy-plugin-data/src/skill.ts
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
import { HeroSkill } from '@user/data-state';
|
||||||
|
|
||||||
|
// 所有的主动技能效果
|
||||||
|
var ignoreInJump = {
|
||||||
|
event: ['X20007', 'X20001', 'X20006', 'X20014', 'X20010', 'X20007'],
|
||||||
|
bg: [
|
||||||
|
'X20037',
|
||||||
|
'X20038',
|
||||||
|
'X20039',
|
||||||
|
'X20045',
|
||||||
|
'X20047',
|
||||||
|
'X20053',
|
||||||
|
'X20054',
|
||||||
|
'X20055',
|
||||||
|
'X20067',
|
||||||
|
'X20068',
|
||||||
|
'X20075',
|
||||||
|
'X20076'
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
export const jumpIgnoreFloor: Set<FloorIds> = new Set([
|
||||||
|
'MT31',
|
||||||
|
'snowTown',
|
||||||
|
'MT36',
|
||||||
|
'MT37',
|
||||||
|
'MT38',
|
||||||
|
'MT39',
|
||||||
|
'MT40',
|
||||||
|
'MT42',
|
||||||
|
'MT43',
|
||||||
|
'MT44',
|
||||||
|
'MT45',
|
||||||
|
'MT46',
|
||||||
|
'MT47',
|
||||||
|
'MT48',
|
||||||
|
'MT49',
|
||||||
|
'MT50',
|
||||||
|
'MT57',
|
||||||
|
'MT59',
|
||||||
|
'MT60',
|
||||||
|
'MT61',
|
||||||
|
'MT71',
|
||||||
|
'MT72',
|
||||||
|
'MT73',
|
||||||
|
'MT74',
|
||||||
|
'MT75',
|
||||||
|
'MT84',
|
||||||
|
'MT93'
|
||||||
|
]);
|
||||||
|
// 跳跃
|
||||||
|
export function jumpSkill(callback?: () => void) {
|
||||||
|
if (core.status.floorId.startsWith('tower')) {
|
||||||
|
callback?.();
|
||||||
|
core.drawTip('当前无法使用该技能');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (jumpIgnoreFloor.has(core.status.floorId) || flags.onChase) {
|
||||||
|
callback?.();
|
||||||
|
core.drawTip('当前楼层无法使用该技能');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!HeroSkill.learnedSkill(HeroSkill.Jump)) {
|
||||||
|
callback?.();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!flags['jump_' + core.status.floorId])
|
||||||
|
flags['jump_' + core.status.floorId] = 0;
|
||||||
|
if (core.status.floorId == 'MT14') {
|
||||||
|
const loc = core.status.hero.loc;
|
||||||
|
if (loc.x === 77 && loc.y === 5) {
|
||||||
|
flags.MT14Jump = true;
|
||||||
|
}
|
||||||
|
if (flags.jump_MT14 === 2 && !flags.MT14Jump) {
|
||||||
|
callback?.();
|
||||||
|
core.drawTip('该地图还有一个必跳的地方,你还没有跳');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (flags['jump_' + core.status.floorId] >= 3) {
|
||||||
|
callback?.();
|
||||||
|
core.drawTip('当前地图使用次数已用完');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var direction = core.status.hero.loc.direction;
|
||||||
|
var loc = core.status.hero.loc;
|
||||||
|
var checkLoc = {};
|
||||||
|
switch (direction) {
|
||||||
|
case 'up':
|
||||||
|
checkLoc.x = loc.x;
|
||||||
|
checkLoc.y = loc.y - 1;
|
||||||
|
break;
|
||||||
|
case 'right':
|
||||||
|
checkLoc.x = loc.x + 1;
|
||||||
|
checkLoc.y = loc.y;
|
||||||
|
break;
|
||||||
|
case 'down':
|
||||||
|
checkLoc.x = loc.x;
|
||||||
|
checkLoc.y = loc.y + 1;
|
||||||
|
break;
|
||||||
|
case 'left':
|
||||||
|
checkLoc.x = loc.x - 1;
|
||||||
|
checkLoc.y = loc.y;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// 前方是否可通行 或 是怪物
|
||||||
|
var cls = core.getBlockCls(checkLoc.x, checkLoc.y);
|
||||||
|
var noPass = core.noPass(checkLoc.x, checkLoc.y);
|
||||||
|
var id = core.getBlockId(checkLoc.x, checkLoc.y) || '';
|
||||||
|
var bgId =
|
||||||
|
core.getBlockByNumber(core.getBgNumber(checkLoc.x, checkLoc.y)).event
|
||||||
|
.id || '';
|
||||||
|
// 可以通行
|
||||||
|
if (
|
||||||
|
!noPass ||
|
||||||
|
cls == 'items' ||
|
||||||
|
(id.startsWith('X') && !ignoreInJump.event.includes(id)) ||
|
||||||
|
(bgId.startsWith('X') && !ignoreInJump.bg.includes(bgId))
|
||||||
|
) {
|
||||||
|
callback?.();
|
||||||
|
core.drawTip('当前无法使用技能');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// debugger;
|
||||||
|
// 不是怪物且不可以通行
|
||||||
|
if (noPass && !(cls == 'enemys' || cls == 'enemy48')) {
|
||||||
|
var toLoc = checkNoPass(direction, checkLoc.x, checkLoc.y, true);
|
||||||
|
if (!toLoc) {
|
||||||
|
callback?.();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
core.autosave();
|
||||||
|
if (flags.chapter <= 1) core.status.hero.hp -= 200 * flags.hard;
|
||||||
|
core.updateStatusBar();
|
||||||
|
flags['jump_' + core.status.floorId]++;
|
||||||
|
if (core.status.hero.hp <= 0) {
|
||||||
|
core.status.hero.hp = 0;
|
||||||
|
core.updateStatusBar();
|
||||||
|
core.events.lose('你跳死了');
|
||||||
|
callback?.();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
core.playSound('015-Jump01.opus');
|
||||||
|
core.insertAction(
|
||||||
|
[{ type: 'jumpHero', loc: [toLoc.x, toLoc.y], time: 500 }],
|
||||||
|
void 0,
|
||||||
|
void 0,
|
||||||
|
callback
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 是怪物
|
||||||
|
if (cls == 'enemys' || cls == 'enemy48') {
|
||||||
|
var firstNoPass = checkNoPass(direction, checkLoc.x, checkLoc.y, false);
|
||||||
|
if (!firstNoPass) {
|
||||||
|
callback?.();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
core.autosave();
|
||||||
|
if (flags.chapter <= 1) core.status.hero.hp -= 200 * flags.hard;
|
||||||
|
core.updateStatusBar();
|
||||||
|
flags['jump_' + core.status.floorId]++;
|
||||||
|
if (core.status.hero.hp <= 0) {
|
||||||
|
core.status.hero.hp = 0;
|
||||||
|
core.updateStatusBar();
|
||||||
|
core.events.lose('你跳死了');
|
||||||
|
callback?.();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
core.playSound('015-Jump01.opus');
|
||||||
|
core.insertAction(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
type: 'jump',
|
||||||
|
from: [checkLoc.x, checkLoc.y],
|
||||||
|
to: [firstNoPass.x, firstNoPass.y],
|
||||||
|
time: 500,
|
||||||
|
keep: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
void 0,
|
||||||
|
void 0,
|
||||||
|
callback
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// 检查一条线上的不可通过
|
||||||
|
function checkNoPass(direction, x, y, startNo) {
|
||||||
|
if (!startNo) startNo = false;
|
||||||
|
switch (direction) {
|
||||||
|
case 'up':
|
||||||
|
y--;
|
||||||
|
break;
|
||||||
|
case 'right':
|
||||||
|
x++;
|
||||||
|
break;
|
||||||
|
case 'down':
|
||||||
|
y++;
|
||||||
|
break;
|
||||||
|
case 'left':
|
||||||
|
x--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
x > core.status.thisMap.width - 1 ||
|
||||||
|
y > core.status.thisMap.height - 1 ||
|
||||||
|
x < 0 ||
|
||||||
|
y < 0
|
||||||
|
) {
|
||||||
|
return core.drawTip('当前无法使用技能');
|
||||||
|
}
|
||||||
|
var id = core.getBlockId(x, y) || '';
|
||||||
|
if (core.getBgNumber(x, y))
|
||||||
|
var bgId =
|
||||||
|
core.getBlockByNumber(core.getBgNumber(x, y)).event.id || '';
|
||||||
|
else var bgId = '';
|
||||||
|
if (
|
||||||
|
core.noPass(x, y) ||
|
||||||
|
core.getBlockCls(x, y) == 'items' ||
|
||||||
|
(id.startsWith('X') && !ignoreInJump.event.includes(id)) ||
|
||||||
|
(bgId.startsWith('X') && !ignoreInJump.bg.includes(bgId)) ||
|
||||||
|
core.getBlockCls(x, y) == 'animates'
|
||||||
|
)
|
||||||
|
return checkNoPass(direction, x, y, true);
|
||||||
|
if (!startNo) return checkNoPass(direction, x, y, false);
|
||||||
|
return { x: x, y: y };
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -31,7 +31,7 @@ export function initUI() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
control.prototype.showStatusBar = function () {
|
control.prototype.showStatusBar = function () {
|
||||||
if (main.mode === 'editor') return;
|
if (main.mode == 'editor') return;
|
||||||
core.removeFlag('hideStatusBar');
|
core.removeFlag('hideStatusBar');
|
||||||
if (mainSetting.getValue('ui.tips')) {
|
if (mainSetting.getValue('ui.tips')) {
|
||||||
if (!fixedUi.hasName('tips')) {
|
if (!fixedUi.hasName('tips')) {
|
||||||
@ -42,7 +42,7 @@ export function initUI() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
control.prototype.hideStatusBar = function (showToolbox) {
|
control.prototype.hideStatusBar = function (showToolbox) {
|
||||||
if (main.mode === 'editor') return;
|
if (main.mode == 'editor') return;
|
||||||
|
|
||||||
// 如果原本就是隐藏的,则先显示
|
// 如果原本就是隐藏的,则先显示
|
||||||
if (!core.domStyle.showStatusBar) this.showStatusBar();
|
if (!core.domStyle.showStatusBar) this.showStatusBar();
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
"是否全屏进行游戏,全屏后按ESC退出全屏,开启后将不能通过按ESC开启系统设置菜单,",
|
"是否全屏进行游戏,全屏后按ESC退出全屏,开启后将不能通过按ESC开启系统设置菜单,",
|
||||||
"请按下方的按钮打开。进入或退出全屏后请存读档一下,以解决一部分绘制问题。"
|
"请按下方的按钮打开。进入或退出全屏后请存读档一下,以解决一部分绘制问题。"
|
||||||
],
|
],
|
||||||
|
"halo": ["开启后,会在地图上显示范围光环。"],
|
||||||
"itemDetail": ["是否在地图上显示宝石血瓶装备等增加的属性值"],
|
"itemDetail": ["是否在地图上显示宝石血瓶装备等增加的属性值"],
|
||||||
"transition": [
|
"transition": [
|
||||||
"是否展示当一个ui界面,如怪物手册等的打开与关闭时的动画。当此项开启时,",
|
"是否展示当一个ui界面,如怪物手册等的打开与关闭时的动画。当此项开启时,",
|
||||||
@ -15,11 +16,43 @@
|
|||||||
"criticalGem": ["临界是否显示为在当前地图要吃的宝石数"]
|
"criticalGem": ["临界是否显示为在当前地图要吃的宝石数"]
|
||||||
},
|
},
|
||||||
"action": {
|
"action": {
|
||||||
|
"autoSkill": [
|
||||||
|
"开启后,打怪物的时候会自动选择伤害最低的技能。同时显伤也会显示此状态下的伤害,",
|
||||||
|
"临界也会考虑技能在内"
|
||||||
|
],
|
||||||
|
"fixed": [
|
||||||
|
"开启后,当鼠标移动到怪物上时,会以盒子的形式展示该点的怪物信息。手机端此功能无效。",
|
||||||
|
"<br>",
|
||||||
|
"<br>",
|
||||||
|
"注:当鼠标移动到怪物上时,经过200毫秒才会显示信息,防止误操作。"
|
||||||
|
],
|
||||||
"hotkey": ["设置游戏中会用到的一些快捷键"]
|
"hotkey": ["设置游戏中会用到的一些快捷键"]
|
||||||
},
|
},
|
||||||
"utils": {
|
"utils": {
|
||||||
|
"betterLoad": [
|
||||||
|
"<span style=\"color: yellow; font-weight: 700\">试验性功能</span>",
|
||||||
|
"<br>",
|
||||||
|
"开启后游戏将对加载进行优化,缩短进入游戏时的加载时长,而在游戏中对资源进行部分性按需加载,从而对加载进行优化。",
|
||||||
|
"该设置不会影响你的正常游戏,但如果网络环境较差,可能会导致部分楼层转换时间明显变长。",
|
||||||
|
"<br>",
|
||||||
|
"<br>",
|
||||||
|
"注:修改后刷新页面起效。"
|
||||||
|
],
|
||||||
"autoScale": [
|
"autoScale": [
|
||||||
"开启后,每次进入游戏时会自动缩放游戏画面至合适值。该项只对电脑端有效。"
|
"开启后,每次进入游戏时会自动缩放游戏画面至合适值。该项只对电脑端有效。",
|
||||||
|
"<br>",
|
||||||
|
"<br>",
|
||||||
|
"缩放原则如下:",
|
||||||
|
"<br>",
|
||||||
|
"1. 首先尝试缩放至最大缩放比例",
|
||||||
|
"<br>",
|
||||||
|
"2. 如果缩放后游戏画面高度高于页面高度的95%,那么缩小一个缩放比例,否则保持最大比例"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"fx": {
|
||||||
|
"paraLight": [
|
||||||
|
"是否开启野外的平行光阴影,在野外将会显示平行光阴影,模拟太阳光,拥有不错的视觉效果"
|
||||||
|
],
|
||||||
|
"frag": ["开启后,在打败怪物后会触发怪物碎裂特效。"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -66,6 +66,8 @@ mainSetting.on('valueChange', (key, n, o) => {
|
|||||||
|
|
||||||
if (root === 'screen') {
|
if (root === 'screen') {
|
||||||
handleScreenSetting(setting, n, o);
|
handleScreenSetting(setting, n, o);
|
||||||
|
} else if (root === 'action') {
|
||||||
|
handleActionSetting(setting, n, o);
|
||||||
} else if (root === 'audio') {
|
} else if (root === 'audio') {
|
||||||
handleAudioSetting(setting, n, o);
|
handleAudioSetting(setting, n, o);
|
||||||
} else if (root === 'ui') {
|
} else if (root === 'ui') {
|
||||||
@ -83,25 +85,30 @@ function handleScreenSetting<T extends number | boolean>(
|
|||||||
if (key === 'fullscreen') {
|
if (key === 'fullscreen') {
|
||||||
// 全屏
|
// 全屏
|
||||||
triggerFullscreen(n as boolean);
|
triggerFullscreen(n as boolean);
|
||||||
|
} else if (key === 'heroDetail') {
|
||||||
|
// 勇士显伤
|
||||||
|
core.drawHero();
|
||||||
} else if (key === 'fontSize') {
|
} else if (key === 'fontSize') {
|
||||||
// 字体大小
|
// 字体大小
|
||||||
root.style.fontSize = `${n}px`;
|
root.style.fontSize = `${n}px`;
|
||||||
const absoluteSize = (n as number) * devicePixelRatio;
|
const absoluteSize = (n as number) * devicePixelRatio;
|
||||||
storage.setValue('@@absoluteFontSize', absoluteSize);
|
storage.setValue('@@absoluteFontSize', absoluteSize);
|
||||||
storage.write();
|
storage.write();
|
||||||
} else if (key === 'scale') {
|
} else if (key === 'fontSizeStatus') {
|
||||||
const { MAIN_HEIGHT, MAIN_WIDTH } = Mota.require(
|
// fontSize.value = n as number;
|
||||||
'@user/client-modules'
|
}
|
||||||
);
|
}
|
||||||
const max = Math.min(
|
|
||||||
(window.innerHeight / MAIN_HEIGHT) * 100,
|
function handleActionSetting<T extends number | boolean>(
|
||||||
(window.innerWidth / MAIN_WIDTH) * 100,
|
key: string,
|
||||||
n as number
|
n: T,
|
||||||
);
|
_o: T
|
||||||
const scale = Number((Math.floor((max / 100) * 4) / 4).toFixed(2));
|
) {
|
||||||
// @ts-expect-error 遗留问题
|
if (key === 'autoSkill') {
|
||||||
core.domStyle.scale = scale;
|
// 自动切换技能
|
||||||
Mota.require('@user/client-modules').mainRenderer.setScale(scale);
|
const HeroSkill = Mota.require('@user/data-state').HeroSkill;
|
||||||
|
HeroSkill.setAutoSkill(n as boolean);
|
||||||
|
core.status.route.push(`set:autoSkill:${n}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,11 +153,19 @@ mainSetting
|
|||||||
'显示设置',
|
'显示设置',
|
||||||
new MotaSetting()
|
new MotaSetting()
|
||||||
.register('fullscreen', '全屏游戏', false, COM.Boolean)
|
.register('fullscreen', '全屏游戏', false, COM.Boolean)
|
||||||
.register('scale', '画面缩放', 100, COM.Number, [50, 500, 25])
|
.register('halo', '光环显示', true, COM.Boolean)
|
||||||
.setDisplayFunc('scale', value => `${value}%`)
|
|
||||||
.register('itemDetail', '宝石血瓶显伤', true, COM.Boolean)
|
.register('itemDetail', '宝石血瓶显伤', true, COM.Boolean)
|
||||||
|
.register('heroDetail', '勇士显伤', false, COM.Boolean)
|
||||||
.register('transition', '界面动画', false, COM.Boolean)
|
.register('transition', '界面动画', false, COM.Boolean)
|
||||||
.register('fontSize', '字体大小', 16, COM.Number, [2, 48, 1])
|
.register('fontSize', '字体大小', 16, COM.Number, [2, 48, 1])
|
||||||
|
.register(
|
||||||
|
'fontSizeStatus',
|
||||||
|
'状态栏字体',
|
||||||
|
16,
|
||||||
|
COM.Number,
|
||||||
|
[10, 300, 10]
|
||||||
|
)
|
||||||
|
.register('smoothView', '平滑镜头', true, COM.Boolean)
|
||||||
.register('criticalGem', '临界显示方式', false, COM.Boolean)
|
.register('criticalGem', '临界显示方式', false, COM.Boolean)
|
||||||
.setDisplayFunc('criticalGem', value => (value ? '宝石数' : '攻击'))
|
.setDisplayFunc('criticalGem', value => (value ? '宝石数' : '攻击'))
|
||||||
.register('keyScale', '虚拟键盘缩放', 100, COM.Number, [25, 5, 500])
|
.register('keyScale', '虚拟键盘缩放', 100, COM.Number, [25, 5, 500])
|
||||||
@ -160,6 +175,8 @@ mainSetting
|
|||||||
'action',
|
'action',
|
||||||
'操作设置',
|
'操作设置',
|
||||||
new MotaSetting()
|
new MotaSetting()
|
||||||
|
.register('autoSkill', '自动切换技能', true, COM.Boolean)
|
||||||
|
.register('fixed', '定点查看', true, COM.Boolean)
|
||||||
.register('hotkey', '快捷键', false, COM.HotkeySetting)
|
.register('hotkey', '快捷键', false, COM.HotkeySetting)
|
||||||
.setDisplayFunc('hotkey', () => '')
|
.setDisplayFunc('hotkey', () => '')
|
||||||
)
|
)
|
||||||
@ -175,12 +192,25 @@ mainSetting
|
|||||||
.register(
|
.register(
|
||||||
'utils',
|
'utils',
|
||||||
'系统设置',
|
'系统设置',
|
||||||
new MotaSetting().register('autoScale', '自动放缩', true, COM.Boolean)
|
new MotaSetting()
|
||||||
|
.register('betterLoad', '优化加载', true, COM.Boolean)
|
||||||
|
.register('autoScale', '自动放缩', true, COM.Boolean)
|
||||||
|
)
|
||||||
|
.register(
|
||||||
|
'fx',
|
||||||
|
'特效设置',
|
||||||
|
new MotaSetting()
|
||||||
|
.register('paraLight', '野外阴影', true, COM.Boolean)
|
||||||
|
.register('frag', '打怪特效', true, COM.Boolean)
|
||||||
|
.register('portalParticle', '传送门特效', true, COM.Boolean)
|
||||||
)
|
)
|
||||||
.register(
|
.register(
|
||||||
'ui',
|
'ui',
|
||||||
'ui设置',
|
'ui设置',
|
||||||
new MotaSetting()
|
new MotaSetting()
|
||||||
|
.register('mapScale', '小地图缩放', 100, COM.Number, [50, 1000, 50])
|
||||||
|
.setDisplayFunc('mapScale', value => `${value}%`)
|
||||||
|
.register('mapLazy', '小地图懒更新', false, COM.Boolean)
|
||||||
.register(
|
.register(
|
||||||
'bookScale',
|
'bookScale',
|
||||||
'怪物手册缩放',
|
'怪物手册缩放',
|
||||||
@ -189,6 +219,9 @@ mainSetting
|
|||||||
[10, 500, 10]
|
[10, 500, 10]
|
||||||
)
|
)
|
||||||
.setDisplayFunc('bookScale', value => `${value}%`)
|
.setDisplayFunc('bookScale', value => `${value}%`)
|
||||||
|
.register('danmaku', '显示弹幕', true, COM.Boolean)
|
||||||
|
.register('danmakuSpeed', '弹幕速度', 60, COM.Number, [10, 1000, 5])
|
||||||
|
.register('tips', '小贴士', true, COM.Boolean)
|
||||||
);
|
);
|
||||||
|
|
||||||
interface SettingTextData {
|
interface SettingTextData {
|
||||||
@ -200,13 +233,26 @@ mainSetting
|
|||||||
.setDescription('audio.bgmVolume', `背景音乐的音量`)
|
.setDescription('audio.bgmVolume', `背景音乐的音量`)
|
||||||
.setDescription('audio.soundEnabled', `是否开启音效`)
|
.setDescription('audio.soundEnabled', `是否开启音效`)
|
||||||
.setDescription('audio.soundVolume', `音效的音量`)
|
.setDescription('audio.soundVolume', `音效的音量`)
|
||||||
|
.setDescription('ui.mapScale', `楼传小地图的缩放,百分比格式`)
|
||||||
|
.setDescription(
|
||||||
|
'ui.mapLazy',
|
||||||
|
`是否启用小地图懒更新模式,此模式下剩余怪物数量不会实时更新而变成切换地图后更新,打开小地图时出现卡顿可以尝试开启此设置`
|
||||||
|
)
|
||||||
.setDescription(
|
.setDescription(
|
||||||
'ui.bookScale',
|
'ui.bookScale',
|
||||||
`怪物手册界面中每个怪物框体的高度缩放,最小值限定为 20% 屏幕高度`
|
`怪物手册界面中每个怪物框体的高度缩放,最小值限定为 20% 屏幕高度`
|
||||||
)
|
)
|
||||||
|
.setDescription('ui.danmaku', '是否显示弹幕')
|
||||||
|
.setDescription('ui.danmakuSpeed', '弹幕速度,刷新或开关弹幕显示后起效')
|
||||||
|
.setDescription('ui.tips', `是否在游戏画面右上角常亮显示小贴士`)
|
||||||
|
.setDescription('screen.fontSizeStatus', `修改状态栏的字体大小`)
|
||||||
.setDescription(
|
.setDescription(
|
||||||
'screen.blur',
|
'screen.blur',
|
||||||
'打开任意ui界面时是否有背景虚化效果,移动端打开后可能会有掉帧或者发热现象。关闭ui后生效'
|
'打开任意ui界面时是否有背景虚化效果,移动端打开后可能会有掉帧或者发热现象。关闭ui后生效'
|
||||||
|
)
|
||||||
|
.setDescription(
|
||||||
|
'fx.portalParticle',
|
||||||
|
'是否启用苍蓝之殿的传送门粒子特效,启用后可能对性能及设备发热有所影响'
|
||||||
);
|
);
|
||||||
|
|
||||||
function setFontSize() {
|
function setFontSize() {
|
||||||
@ -240,17 +286,24 @@ export function createSetting() {
|
|||||||
loading.once('coreInit', () => {
|
loading.once('coreInit', () => {
|
||||||
mainSetting.reset({
|
mainSetting.reset({
|
||||||
'screen.fullscreen': !!document.fullscreenElement,
|
'screen.fullscreen': !!document.fullscreenElement,
|
||||||
'screen.scale': storage.getValue('screen.scale', 100),
|
'screen.halo': !!storage.getValue('screen.showHalo', true),
|
||||||
'screen.itemDetail': !!storage.getValue('screen.itemDetail', true),
|
'screen.itemDetail': !!storage.getValue('screen.itemDetail', true),
|
||||||
|
'screen.heroDetail': !!storage.getValue('screen.heroDetail', false),
|
||||||
'screen.transition': !!storage.getValue('screen.transition', false),
|
'screen.transition': !!storage.getValue('screen.transition', false),
|
||||||
'screen.fontSize': storage.getValue(
|
'screen.fontSize': storage.getValue(
|
||||||
'screen.fontSize',
|
'screen.fontSize',
|
||||||
isMobile ? 9 : 16
|
isMobile ? 9 : 16
|
||||||
),
|
),
|
||||||
|
'screen.smoothView': !!storage.getValue('screen.smoothView', true),
|
||||||
'screen.criticalGem': !!storage.getValue(
|
'screen.criticalGem': !!storage.getValue(
|
||||||
'screen.criticalGem',
|
'screen.criticalGem',
|
||||||
false
|
false
|
||||||
),
|
),
|
||||||
|
'screen.fontSizeStatus': storage.getValue(
|
||||||
|
'screen.fontSizeStatus',
|
||||||
|
100
|
||||||
|
),
|
||||||
|
'action.fixed': !!storage.getValue('action.fixed', true),
|
||||||
'audio.bgmEnabled': !!storage.getValue('audio.bgmEnabled', true),
|
'audio.bgmEnabled': !!storage.getValue('audio.bgmEnabled', true),
|
||||||
'audio.bgmVolume': storage.getValue('audio.bgmVolume', 80),
|
'audio.bgmVolume': storage.getValue('audio.bgmVolume', 80),
|
||||||
'audio.soundEnabled': !!storage.getValue(
|
'audio.soundEnabled': !!storage.getValue(
|
||||||
@ -258,11 +311,26 @@ export function createSetting() {
|
|||||||
true
|
true
|
||||||
),
|
),
|
||||||
'audio.soundVolume': storage.getValue('audio.soundVolume', 80),
|
'audio.soundVolume': storage.getValue('audio.soundVolume', 80),
|
||||||
|
'utils.betterLoad': !!storage.getValue('utils.betterLoad', true),
|
||||||
'utils.autoScale': !!storage.getValue('utils.autoScale', true),
|
'utils.autoScale': !!storage.getValue('utils.autoScale', true),
|
||||||
|
'fx.paraLight': !!storage.getValue('fx.paraLight', true),
|
||||||
|
'fx.frag': !!storage.getValue('fx.frag', true),
|
||||||
|
'fx.portalParticle': !!storage.getValue('fx.portalParticle', true),
|
||||||
|
'ui.mapScale': storage.getValue(
|
||||||
|
'ui.mapScale',
|
||||||
|
isMobile ? 300 : Math.floor(window.innerWidth / 600) * 50
|
||||||
|
),
|
||||||
|
'ui.mapLazy': storage.getValue('ui.mapLazy', false),
|
||||||
'ui.bookScale': storage.getValue(
|
'ui.bookScale': storage.getValue(
|
||||||
'ui.bookScale',
|
'ui.bookScale',
|
||||||
isMobile ? 100 : 80
|
isMobile ? 100 : 80
|
||||||
)
|
),
|
||||||
|
'ui.danmaku': storage.getValue('ui.danmaku', true),
|
||||||
|
'ui.danmakuSpeed': storage.getValue(
|
||||||
|
'ui.danmakuSpeed',
|
||||||
|
Math.floor(window.innerWidth / 30) * 5
|
||||||
|
),
|
||||||
|
'ui.tips': storage.getValue('ui.tips', true)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -179,7 +179,6 @@ export function getMapData(
|
|||||||
const floor = core.floors[now];
|
const floor = core.floors[now];
|
||||||
const change = floor.changeFloor;
|
const change = floor.changeFloor;
|
||||||
for (const [loc, ev] of Object.entries(change)) {
|
for (const [loc, ev] of Object.entries(change)) {
|
||||||
if (!ev) continue;
|
|
||||||
const target = ev.floorId as FloorIds;
|
const target = ev.floorId as FloorIds;
|
||||||
if (target.startsWith(':')) continue;
|
if (target.startsWith(':')) continue;
|
||||||
const [x, y] = loc.split(',').map(v => parseInt(v));
|
const [x, y] = loc.split(',').map(v => parseInt(v));
|
||||||
|
|||||||
@ -46,7 +46,9 @@ window.addEventListener('resize', () => {
|
|||||||
checkMobile();
|
checkMobile();
|
||||||
|
|
||||||
sleep(2000).then(() => {
|
sleep(2000).then(() => {
|
||||||
tip('info', `2.B 暂不支持竖屏游玩,手机端请考虑横屏游玩`);
|
if (!isMobile) {
|
||||||
|
tip('info', `注意,不推荐使用浏览器的缩放功能,使用游戏内的缩放即可`);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function checkMobile() {
|
function checkMobile() {
|
||||||
|
|||||||
@ -295,12 +295,16 @@ export function ensureArray<T>(arr: T): T extends any[] ? T : T[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function triggerFullscreen(full: boolean) {
|
export async function triggerFullscreen(full: boolean) {
|
||||||
|
const { maxGameScale } = Mota.require('@user/data-utils');
|
||||||
if (!!document.fullscreenElement && !full) {
|
if (!!document.fullscreenElement && !full) {
|
||||||
if (window.jsinterface) {
|
if (window.jsinterface) {
|
||||||
window.jsinterface.requestPortrait();
|
window.jsinterface.requestPortrait();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await document.exitFullscreen();
|
await document.exitFullscreen();
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
maxGameScale(1);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (full && !document.fullscreenElement) {
|
if (full && !document.fullscreenElement) {
|
||||||
if (window.jsinterface) {
|
if (window.jsinterface) {
|
||||||
@ -308,6 +312,9 @@ export async function triggerFullscreen(full: boolean) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await document.body.requestFullscreen();
|
await document.body.requestFullscreen();
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
maxGameScale();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,10 +349,10 @@ export function formatSize(size: number) {
|
|||||||
return size < 1 << 10
|
return size < 1 << 10
|
||||||
? `${size.toFixed(2)}B`
|
? `${size.toFixed(2)}B`
|
||||||
: size < 1 << 20
|
: size < 1 << 20
|
||||||
? `${(size / (1 << 10)).toFixed(2)}KB`
|
? `${(size / (1 << 10)).toFixed(2)}KB`
|
||||||
: size < 1 << 30
|
: size < 1 << 30
|
||||||
? `${(size / (1 << 20)).toFixed(2)}MB`
|
? `${(size / (1 << 20)).toFixed(2)}MB`
|
||||||
: `${(size / (1 << 30)).toFixed(2)}GB`;
|
: `${(size / (1 << 30)).toFixed(2)}GB`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getIconHeight(icon: AllIds | 'hero') {
|
export function getIconHeight(icon: AllIds | 'hero') {
|
||||||
|
|||||||
@ -435,9 +435,10 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
|
|||||||
ctx.globalAlpha = this.alpha;
|
ctx.globalAlpha = this.alpha;
|
||||||
ctx.globalCompositeOperation = this.composite;
|
ctx.globalCompositeOperation = this.composite;
|
||||||
if (this.enableCache) {
|
if (this.enableCache) {
|
||||||
const { width, height } = this.cache;
|
const { width, height, ctx } = this.cache;
|
||||||
if (this.cacheDirty) {
|
if (this.cacheDirty) {
|
||||||
this.cache.clear();
|
const { canvas } = this.cache;
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
this.render(this.cache, tran);
|
this.render(this.cache, tran);
|
||||||
this.cacheDirty = false;
|
this.cacheDirty = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -562,6 +562,12 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = {
|
|||||||
},
|
},
|
||||||
"_data": "状态栏显示项"
|
"_data": "状态栏显示项"
|
||||||
},
|
},
|
||||||
|
"extendToolbar": {
|
||||||
|
"_leaf": true,
|
||||||
|
"_type": "checkbox",
|
||||||
|
"_docs": "横屏底部工具栏",
|
||||||
|
"_data": "在横屏状态下是否将工具栏挪动到游戏画布下方,从而完全解放状态栏空间"
|
||||||
|
},
|
||||||
"flyNearStair": {
|
"flyNearStair": {
|
||||||
"_leaf": true,
|
"_leaf": true,
|
||||||
"_type": "checkbox",
|
"_type": "checkbox",
|
||||||
@ -580,6 +586,12 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = {
|
|||||||
"_docs": "首次道具进行提示",
|
"_docs": "首次道具进行提示",
|
||||||
"_data": "首次获得道具是否提示"
|
"_data": "首次获得道具是否提示"
|
||||||
},
|
},
|
||||||
|
"equipboxButton": {
|
||||||
|
"_leaf": true,
|
||||||
|
"_type": "checkbox",
|
||||||
|
"_docs": "状态栏装备按钮",
|
||||||
|
"_data": "状态栏的装备按钮。若此项为true则将状态栏中的楼层转换器按钮换为装备栏按钮"
|
||||||
|
},
|
||||||
"enableAddPoint": {
|
"enableAddPoint": {
|
||||||
"_leaf": true,
|
"_leaf": true,
|
||||||
"_type": "checkbox",
|
"_type": "checkbox",
|
||||||
@ -598,6 +610,30 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = {
|
|||||||
"_docs": "夹击不超伤害值",
|
"_docs": "夹击不超伤害值",
|
||||||
"_data": "夹击伤害是否不超过怪物伤害值。"
|
"_data": "夹击伤害是否不超过怪物伤害值。"
|
||||||
},
|
},
|
||||||
|
"useLoop": {
|
||||||
|
"_leaf": true,
|
||||||
|
"_type": "checkbox",
|
||||||
|
"_docs": "二分临界",
|
||||||
|
"_data": "是否循环计算临界;如果此项为true则使用循环法(而不是回合数计算法)来算临界\n从V2.5.3开始,对于大数据的循环法将改为使用二分法进行计算"
|
||||||
|
},
|
||||||
|
"startUsingCanvas": {
|
||||||
|
"_leaf": true,
|
||||||
|
"_type": "checkbox",
|
||||||
|
"_docs": "标题开启事件化",
|
||||||
|
"_data": "是否开始菜单canvas化;如果此项为true,则将使用canvas来绘制开始菜单"
|
||||||
|
},
|
||||||
|
"statusCanvas": {
|
||||||
|
"_leaf": true,
|
||||||
|
"_type": "checkbox",
|
||||||
|
"_docs": "开启自绘状态栏",
|
||||||
|
"_data": "是否状态栏canvas化,即手动自定义绘制状态栏。\n如果此项开启,则可在脚本编辑的drawStatusBar中自定义绘制菜单栏。"
|
||||||
|
},
|
||||||
|
"enableEnemyPoint": {
|
||||||
|
"_leaf": true,
|
||||||
|
"_type": "checkbox",
|
||||||
|
"_docs": "定点怪显",
|
||||||
|
"_data": "是否开启怪物的定点显示功能,即属性不同的怪物会在怪物手册单列;用户可以手动在菜单栏中开关"
|
||||||
|
},
|
||||||
"enableGentleClick": {
|
"enableGentleClick": {
|
||||||
"_leaf": true,
|
"_leaf": true,
|
||||||
"_type": "checkbox",
|
"_type": "checkbox",
|
||||||
@ -640,6 +676,12 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = {
|
|||||||
"_docs": "虚化前景层",
|
"_docs": "虚化前景层",
|
||||||
"_data": "是否虚化前景层;如果此项开启,则在游戏中事件层有东西(如宝石等)时虚化前景层。"
|
"_data": "是否虚化前景层;如果此项开启,则在游戏中事件层有东西(如宝石等)时虚化前景层。"
|
||||||
},
|
},
|
||||||
|
"autoScale": {
|
||||||
|
"_leaf": true,
|
||||||
|
"_type": "checkbox",
|
||||||
|
"_docs": "自动缩放最大化",
|
||||||
|
"_data": "是否自动缩放最大化,关闭后不再最大化"
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -583,6 +583,13 @@ actions.prototype._sys_ondown = function (x, y, px, py) {
|
|||||||
y: Math.floor((py + core.bigmap.offsetY) / 32)
|
y: Math.floor((py + core.bigmap.offsetY) / 32)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const loopMaps = Mota.require('@user/data-state').MiscData.loopMaps;
|
||||||
|
if (loopMaps.has(core.status.floorId)) {
|
||||||
|
const floor = core.status.thisMap;
|
||||||
|
if (pos.x < 0) pos.x += floor.width;
|
||||||
|
if (pos.x >= floor.width) pos.x -= floor.width;
|
||||||
|
}
|
||||||
|
|
||||||
core.status.stepPostfix = [];
|
core.status.stepPostfix = [];
|
||||||
core.status.stepPostfix.push(pos);
|
core.status.stepPostfix.push(pos);
|
||||||
|
|
||||||
|
|||||||
@ -2120,6 +2120,10 @@ control.prototype._doSL_load_afterGet = function (id, data) {
|
|||||||
core.myconfirm('此存档可能存在风险,你想要播放录像么?', _replay);
|
core.myconfirm('此存档可能存在风险,你想要播放录像么?', _replay);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// 追逐战
|
||||||
|
Mota.r(() => {
|
||||||
|
Mota.require('@user/legacy-plugin-client').end(false);
|
||||||
|
});
|
||||||
// core.ui.closePanel();
|
// core.ui.closePanel();
|
||||||
core.loadData(data, function () {
|
core.loadData(data, function () {
|
||||||
core.removeFlag('__fromLoad__');
|
core.removeFlag('__fromLoad__');
|
||||||
@ -2989,7 +2993,16 @@ control.prototype.checkBgm = function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
///// 设置屏幕放缩 //////
|
///// 设置屏幕放缩 //////
|
||||||
control.prototype.setDisplayScale = function (delta) {};
|
control.prototype.setDisplayScale = function (delta) {
|
||||||
|
var index = core.domStyle.availableScale.indexOf(core.domStyle.scale);
|
||||||
|
if (index < 0) return;
|
||||||
|
index =
|
||||||
|
(index + delta + core.domStyle.availableScale.length) %
|
||||||
|
core.domStyle.availableScale.length;
|
||||||
|
core.domStyle.scale = core.domStyle.availableScale[index];
|
||||||
|
core.setLocalStorage('scale', core.domStyle.scale);
|
||||||
|
core.resize();
|
||||||
|
};
|
||||||
|
|
||||||
// ------ 状态栏,工具栏等相关 ------ //
|
// ------ 状态栏,工具栏等相关 ------ //
|
||||||
|
|
||||||
@ -3072,33 +3085,33 @@ control.prototype.resize = function () {
|
|||||||
const width = window.innerWidth;
|
const width = window.innerWidth;
|
||||||
const height = window.innerHeight;
|
const height = window.innerHeight;
|
||||||
|
|
||||||
// if (window.innerWidth >= 600) {
|
if (window.innerWidth >= 600) {
|
||||||
// // 横屏
|
// 横屏
|
||||||
// core.domStyle.isVertical = false;
|
core.domStyle.isVertical = false;
|
||||||
// core.domStyle.availableScale = [];
|
core.domStyle.availableScale = [];
|
||||||
// const maxScale = Math.min(width / core._PX_, height / core._PY_);
|
const maxScale = Math.min(width / core._PX_, height / core._PY_);
|
||||||
// [1, 1.25, 1.5, 1.75, 2, 2.25, 2.5].forEach(function (v) {
|
[1, 1.25, 1.5, 1.75, 2, 2.25, 2.5].forEach(function (v) {
|
||||||
// if (v < maxScale) {
|
if (v < maxScale) {
|
||||||
// core.domStyle.availableScale.push(v);
|
core.domStyle.availableScale.push(v);
|
||||||
// }
|
}
|
||||||
// });
|
});
|
||||||
// if (!core.domStyle.availableScale.includes(core.domStyle.scale)) {
|
if (!core.domStyle.availableScale.includes(core.domStyle.scale)) {
|
||||||
// core.domStyle.scale = 1;
|
core.domStyle.scale = 1;
|
||||||
// }
|
}
|
||||||
// } else {
|
} else {
|
||||||
// // 竖屏
|
// 竖屏
|
||||||
// core.domStyle.isVertical = true;
|
core.domStyle.isVertical = true;
|
||||||
// core.domStyle.scale = window.innerWidth / core._PX_;
|
core.domStyle.scale = window.innerWidth / core._PX_;
|
||||||
// core.domStyle.availableScale = [];
|
core.domStyle.availableScale = [];
|
||||||
// }
|
}
|
||||||
|
|
||||||
// if (!core.domStyle.isVertical) {
|
if (!core.domStyle.isVertical) {
|
||||||
// const height = window.innerHeight;
|
const height = window.innerHeight;
|
||||||
// const width = window.innerWidth;
|
const width = window.innerWidth;
|
||||||
// const maxScale = Math.min(height / core._PY_, width / core._PX_);
|
const maxScale = Math.min(height / core._PY_, width / core._PX_);
|
||||||
// const target = Number((Math.floor(maxScale * 4) / 4).toFixed(2));
|
const target = Number((Math.floor(maxScale * 4) / 4).toFixed(2));
|
||||||
// core.domStyle.scale = target - 0.25;
|
core.domStyle.scale = target - 0.25;
|
||||||
// }
|
}
|
||||||
|
|
||||||
this._doResize({});
|
this._doResize({});
|
||||||
this.setToolbarButton();
|
this.setToolbarButton();
|
||||||
|
|||||||
@ -7,14 +7,14 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function core() {
|
function core() {
|
||||||
this._WIDTH_ = 13;
|
this._WIDTH_ = 15;
|
||||||
this._HEIGHT_ = 13;
|
this._HEIGHT_ = 15;
|
||||||
this._PX_ = this._WIDTH_ * 32;
|
this._PX_ = this._WIDTH_ * 32;
|
||||||
this._PY_ = this._HEIGHT_ * 32;
|
this._PY_ = this._HEIGHT_ * 32;
|
||||||
this._HALF_WIDTH_ = Math.floor(this._WIDTH_ / 2);
|
this._HALF_WIDTH_ = Math.floor(this._WIDTH_ / 2);
|
||||||
this._HALF_HEIGHT_ = Math.floor(this._HEIGHT_ / 2);
|
this._HALF_HEIGHT_ = Math.floor(this._HEIGHT_ / 2);
|
||||||
|
|
||||||
this.__SIZE__ = main.mode == 'editor' ? 13 : this._HEIGHT_;
|
this.__SIZE__ = main.mode == 'editor' ? 15 : this._HEIGHT_;
|
||||||
this.__PIXELS__ = this.__SIZE__ * 32;
|
this.__PIXELS__ = this.__SIZE__ * 32;
|
||||||
this.__HALF_SIZE__ = Math.floor(this.__SIZE__ / 2);
|
this.__HALF_SIZE__ = Math.floor(this.__SIZE__ / 2);
|
||||||
this.material = {
|
this.material = {
|
||||||
|
|||||||
@ -26,6 +26,7 @@ events.prototype.startGame = function (hard, seed, route, callback) {
|
|||||||
hard = hard || '';
|
hard = hard || '';
|
||||||
|
|
||||||
if (main.mode != 'play') return;
|
if (main.mode != 'play') return;
|
||||||
|
Mota.require('@user/data-state').resetSkillLevel();
|
||||||
|
|
||||||
// 无动画的开始游戏
|
// 无动画的开始游戏
|
||||||
if (core.flags.startUsingCanvas || route != null) {
|
if (core.flags.startUsingCanvas || route != null) {
|
||||||
|
|||||||
@ -806,6 +806,8 @@ maps.prototype.generateMovableArray = function (floorId) {
|
|||||||
for (var x = 0; x < width; ++x) {
|
for (var x = 0; x < width; ++x) {
|
||||||
array[x] = Array(height).fill([]);
|
array[x] = Array(height).fill([]);
|
||||||
}
|
}
|
||||||
|
const loopMaps = Mota.require('@user/data-state').MiscData.loopMaps;
|
||||||
|
const isLoop = loopMaps.has(floorId);
|
||||||
var v2 = floorId == core.status.floorId && core.bigmap.v2;
|
var v2 = floorId == core.status.floorId && core.bigmap.v2;
|
||||||
const half = core._HALF_WIDTH_;
|
const half = core._HALF_WIDTH_;
|
||||||
var startX = v2
|
var startX = v2
|
||||||
@ -827,19 +829,24 @@ maps.prototype.generateMovableArray = function (floorId) {
|
|||||||
)
|
)
|
||||||
: height;
|
: height;
|
||||||
|
|
||||||
|
if (isLoop) {
|
||||||
|
startX = 0;
|
||||||
|
endX = core.status.maps[floorId].width;
|
||||||
|
}
|
||||||
|
|
||||||
for (var x = startX; x < endX; x++) {
|
for (var x = startX; x < endX; x++) {
|
||||||
for (var y = startY; y < endY; y++) {
|
for (var y = startY; y < endY; y++) {
|
||||||
array[x][y] = ['left', 'down', 'up', 'right'].filter(
|
array[x][y] = ['left', 'down', 'up', 'right'].filter(function (
|
||||||
function (direction) {
|
direction
|
||||||
return core.maps._canMoveHero_checkPoint(
|
) {
|
||||||
x,
|
return core.maps._canMoveHero_checkPoint(
|
||||||
y,
|
x,
|
||||||
direction,
|
y,
|
||||||
floorId,
|
direction,
|
||||||
arrays
|
floorId,
|
||||||
);
|
arrays
|
||||||
}
|
);
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return array;
|
return array;
|
||||||
@ -881,6 +888,11 @@ maps.prototype._canMoveHero_checkPoint = function (
|
|||||||
var nx = x + core.utils.scan[direction].x,
|
var nx = x + core.utils.scan[direction].x,
|
||||||
ny = y + core.utils.scan[direction].y;
|
ny = y + core.utils.scan[direction].y;
|
||||||
|
|
||||||
|
const loopMaps = Mota.require('@user/data-state').MiscData.loopMaps;
|
||||||
|
if (loopMaps.has(floorId)) {
|
||||||
|
if (nx < 0) nx = floor.width - 1;
|
||||||
|
if (nx >= floor.width) nx = 0;
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
nx < 0 ||
|
nx < 0 ||
|
||||||
ny < 0 ||
|
ny < 0 ||
|
||||||
@ -1116,6 +1128,9 @@ maps.prototype.automaticRoute = function (destX, destY) {
|
|||||||
// BFS找寻最短路径
|
// BFS找寻最短路径
|
||||||
var route = this._automaticRoute_bfs(startX, startY, destX, destY);
|
var route = this._automaticRoute_bfs(startX, startY, destX, destY);
|
||||||
if (route[destX + ',' + destY] == null) return [];
|
if (route[destX + ',' + destY] == null) return [];
|
||||||
|
const floor = core.status.thisMap;
|
||||||
|
const loopMaps = Mota.require('@user/data-state').MiscData.loopMaps;
|
||||||
|
|
||||||
// 路径数组转换
|
// 路径数组转换
|
||||||
var ans = [],
|
var ans = [],
|
||||||
nowX = destX,
|
nowX = destX,
|
||||||
@ -1125,6 +1140,10 @@ maps.prototype.automaticRoute = function (destX, destY) {
|
|||||||
ans.push({ direction: dir, x: nowX, y: nowY });
|
ans.push({ direction: dir, x: nowX, y: nowY });
|
||||||
nowX -= core.utils.scan[dir].x;
|
nowX -= core.utils.scan[dir].x;
|
||||||
nowY -= core.utils.scan[dir].y;
|
nowY -= core.utils.scan[dir].y;
|
||||||
|
if (loopMaps.has(core.status.floorId)) {
|
||||||
|
if (nowX < 0) nowX += floor.width;
|
||||||
|
if (nowX >= floor.width) nowX -= floor.width;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ans.reverse();
|
ans.reverse();
|
||||||
return ans;
|
return ans;
|
||||||
@ -1143,6 +1162,7 @@ maps.prototype._automaticRoute_bfs = function (startX, startY, destX, destY) {
|
|||||||
queue.queue({ depth: 0, x: startX, y: startY });
|
queue.queue({ depth: 0, x: startX, y: startY });
|
||||||
var blocks = core.getMapBlocksObj();
|
var blocks = core.getMapBlocksObj();
|
||||||
const floor = core.status.thisMap;
|
const floor = core.status.thisMap;
|
||||||
|
const loopMaps = Mota.require('@user/data-state').MiscData.loopMaps;
|
||||||
|
|
||||||
while (queue.length != 0) {
|
while (queue.length != 0) {
|
||||||
var curr = queue.dequeue(),
|
var curr = queue.dequeue(),
|
||||||
@ -1153,16 +1173,22 @@ maps.prototype._automaticRoute_bfs = function (startX, startY, destX, destY) {
|
|||||||
if (!core.inArray(canMoveArray[nowX][nowY], direction)) continue;
|
if (!core.inArray(canMoveArray[nowX][nowY], direction)) continue;
|
||||||
var nx = nowX + core.utils.scan[direction].x;
|
var nx = nowX + core.utils.scan[direction].x;
|
||||||
var ny = nowY + core.utils.scan[direction].y;
|
var ny = nowY + core.utils.scan[direction].y;
|
||||||
|
if (loopMaps.has(core.status.floorId)) {
|
||||||
if (
|
if (nx < 0) nx = floor.width - 1;
|
||||||
nx < 0 ||
|
if (nx >= floor.width) nx = 0;
|
||||||
nx >= core.bigmap.width ||
|
if (route[nx + ',' + ny] || ny < 0 || ny >= floor.height) {
|
||||||
ny < 0 ||
|
continue;
|
||||||
ny >= core.bigmap.height ||
|
}
|
||||||
route[nx + ',' + ny] != null
|
} else {
|
||||||
)
|
if (
|
||||||
continue;
|
nx < 0 ||
|
||||||
|
nx >= core.bigmap.width ||
|
||||||
|
ny < 0 ||
|
||||||
|
ny >= core.bigmap.height ||
|
||||||
|
route[nx + ',' + ny] != null
|
||||||
|
)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// 重点
|
// 重点
|
||||||
if (nx == destX && ny == destY) {
|
if (nx == destX && ny == destY) {
|
||||||
route[nx + ',' + ny] = direction;
|
route[nx + ',' + ny] = direction;
|
||||||
@ -2643,25 +2669,22 @@ maps.prototype._drawThumbnail_realDrawTempCanvas = function (
|
|||||||
options.heroIcon =
|
options.heroIcon =
|
||||||
options.heroIcon || core.status.hero.image || 'hero.png';
|
options.heroIcon || core.status.hero.image || 'hero.png';
|
||||||
options.heroIcon = core.getMappedName(options.heroIcon);
|
options.heroIcon = core.getMappedName(options.heroIcon);
|
||||||
const image = core.material.images.images[options.heroIcon];
|
var icon = core.material.icons.hero[options.heroLoc.direction];
|
||||||
if (image) {
|
var height = core.material.images.images[options.heroIcon].height / 4;
|
||||||
var icon = core.material.icons.hero[options.heroLoc.direction];
|
var width =
|
||||||
var height =
|
(core.material.images.images[options.heroIcon].width || 128) / 4;
|
||||||
core.material.images.images[options.heroIcon].height / 4;
|
core.drawImage(
|
||||||
var width = (image.width || 128) / 4;
|
options.ctx,
|
||||||
core.drawImage(
|
core.material.images.images[options.heroIcon],
|
||||||
options.ctx,
|
icon.stop * width,
|
||||||
core.material.images.images[options.heroIcon],
|
icon.loc * height,
|
||||||
icon.stop * width,
|
width,
|
||||||
icon.loc * height,
|
height,
|
||||||
width,
|
32 * options.heroLoc.x + 32 - width,
|
||||||
height,
|
32 * options.heroLoc.y + 32 - height,
|
||||||
32 * options.heroLoc.x + 32 - width,
|
width,
|
||||||
32 * options.heroLoc.y + 32 - height,
|
height
|
||||||
width,
|
);
|
||||||
height
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// 缩略图:前景
|
// 缩略图:前景
|
||||||
this.drawFg(floorId, options);
|
this.drawFg(floorId, options);
|
||||||
@ -3868,14 +3891,7 @@ maps.prototype._moveBlock_doMove = function (
|
|||||||
_run();
|
_run();
|
||||||
} else
|
} else
|
||||||
core.maps._moveBlock_moving(blockInfo, canvases, moveInfo);
|
core.maps._moveBlock_moving(blockInfo, canvases, moveInfo);
|
||||||
} else
|
} else core.maps._moveJumpBlock_finished(blockInfo, canvases, moveInfo, animate, cb);
|
||||||
core.maps._moveJumpBlock_finished(
|
|
||||||
blockInfo,
|
|
||||||
canvases,
|
|
||||||
moveInfo,
|
|
||||||
animate,
|
|
||||||
cb
|
|
||||||
);
|
|
||||||
}, moveInfo.per_time);
|
}, moveInfo.per_time);
|
||||||
|
|
||||||
core.animateFrame.lastAsyncId = animate;
|
core.animateFrame.lastAsyncId = animate;
|
||||||
|
|||||||
@ -297,18 +297,21 @@ main.prototype.loadAsync = async function (mode, callback) {
|
|||||||
if (main.mode === 'editor') return;
|
if (main.mode === 'editor') return;
|
||||||
|
|
||||||
// 自动放缩最大化
|
// 自动放缩最大化
|
||||||
const mainSetting = Mota.require('@motajs/legacy-ui').mainSetting;
|
let auto = Mota.require('@motajs/legacy-ui').mainSetting.getValue(
|
||||||
const auto = mainSetting.getValue('utils.autoScale', true);
|
'autoScale',
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
// 暂时不考虑手机端
|
if (auto && !core.domStyle.isVertical) {
|
||||||
if (auto) {
|
|
||||||
const height = window.innerHeight;
|
const height = window.innerHeight;
|
||||||
const width = window.innerWidth;
|
const width = window.innerWidth;
|
||||||
const maxScale = Math.min(height / core._PY_, width / core._PX_);
|
const maxScale = Math.min(height / core._PY_, width / core._PX_);
|
||||||
const target = Number((Math.floor(maxScale * 4) / 4).toFixed(2));
|
const target = Number((Math.floor(maxScale * 4) / 4).toFixed(2));
|
||||||
mainSetting.setValue('screen.scale', Math.round(target * 100) - 25);
|
core.domStyle.scale = target - 0.25;
|
||||||
|
}
|
||||||
|
if (core.domStyle.isVertical) {
|
||||||
|
core.domStyle.scale = window.innerWidth / core._PX_;
|
||||||
}
|
}
|
||||||
|
|
||||||
Mota.r(() => {
|
Mota.r(() => {
|
||||||
Mota.require('@user/client-modules').mainRenderer.setScale(
|
Mota.require('@user/client-modules').mainRenderer.setScale(
|
||||||
core.domStyle.scale
|
core.domStyle.scale
|
||||||
|
|||||||
85
public/maps/index.html
Normal file
85
public/maps/index.html
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>人类:开天辟地 缩略图集</title>
|
||||||
|
<style>
|
||||||
|
html {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: black;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#map-group {
|
||||||
|
display: flex;
|
||||||
|
width: 50%;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#map-group span {
|
||||||
|
font-size: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: aqua;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color 0.2s linear;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: aquamarine;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 90%;
|
||||||
|
border: 1px solid #ddd4;
|
||||||
|
border-style: dashed;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-one {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="map-group">
|
||||||
|
<span>人类:开天辟地 缩略图集</span>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
const list = ['草原', '洞穴', '勇气之路', '智慧小径', '冰封雪原', '冰封高原'];
|
||||||
|
let html = '';
|
||||||
|
list.forEach(v => {
|
||||||
|
html += `
|
||||||
|
<div class="map-one">
|
||||||
|
<a href="${v}.png" class="map-a" target="_blank">${v}</a>
|
||||||
|
<img src="${v}.png" class="map-img" onclick="window.open('${v}.png')"></img>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
});
|
||||||
|
|
||||||
|
const div = document.getElementById('map-group');
|
||||||
|
div.innerHTML += html;
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
BIN
public/maps/冰封雪原.png
Normal file
BIN
public/maps/冰封雪原.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
BIN
public/maps/冰封高原.png
Normal file
BIN
public/maps/冰封高原.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.7 MiB |
BIN
public/maps/勇气之路.png
Normal file
BIN
public/maps/勇气之路.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
BIN
public/maps/智慧小径.png
Normal file
BIN
public/maps/智慧小径.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.0 MiB |
BIN
public/maps/洞穴.png
Normal file
BIN
public/maps/洞穴.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
BIN
public/maps/草原.png
Normal file
BIN
public/maps/草原.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.9 MiB |
BIN
public/project/bgms/beforeBoss.opus
Normal file
BIN
public/project/bgms/beforeBoss.opus
Normal file
Binary file not shown.
BIN
public/project/bgms/beforeNight.opus
Normal file
BIN
public/project/bgms/beforeNight.opus
Normal file
Binary file not shown.
Binary file not shown.
BIN
public/project/bgms/cave.opus
Normal file
BIN
public/project/bgms/cave.opus
Normal file
Binary file not shown.
BIN
public/project/bgms/chapter2ED.opus
Normal file
BIN
public/project/bgms/chapter2ED.opus
Normal file
Binary file not shown.
BIN
public/project/bgms/escape.opus
Normal file
BIN
public/project/bgms/escape.opus
Normal file
Binary file not shown.
BIN
public/project/bgms/escape2.opus
Normal file
BIN
public/project/bgms/escape2.opus
Normal file
Binary file not shown.
BIN
public/project/bgms/grass.opus
Normal file
BIN
public/project/bgms/grass.opus
Normal file
Binary file not shown.
BIN
public/project/bgms/mount.opus
Normal file
BIN
public/project/bgms/mount.opus
Normal file
Binary file not shown.
BIN
public/project/bgms/night.opus
Normal file
BIN
public/project/bgms/night.opus
Normal file
Binary file not shown.
BIN
public/project/bgms/palaceCenter.opus
Normal file
BIN
public/project/bgms/palaceCenter.opus
Normal file
Binary file not shown.
BIN
public/project/bgms/palaceNorth.opus
Normal file
BIN
public/project/bgms/palaceNorth.opus
Normal file
Binary file not shown.
BIN
public/project/bgms/palaceSouth.opus
Normal file
BIN
public/project/bgms/palaceSouth.opus
Normal file
Binary file not shown.
BIN
public/project/bgms/plot1.opus
Normal file
BIN
public/project/bgms/plot1.opus
Normal file
Binary file not shown.
BIN
public/project/bgms/road.opus
Normal file
BIN
public/project/bgms/road.opus
Normal file
Binary file not shown.
BIN
public/project/bgms/title.opus
Normal file
BIN
public/project/bgms/title.opus
Normal file
Binary file not shown.
BIN
public/project/bgms/tower.opus
Normal file
BIN
public/project/bgms/tower.opus
Normal file
Binary file not shown.
BIN
public/project/bgms/towerBoss.opus
Normal file
BIN
public/project/bgms/towerBoss.opus
Normal file
Binary file not shown.
BIN
public/project/bgms/towerBoss2.opus
Normal file
BIN
public/project/bgms/towerBoss2.opus
Normal file
Binary file not shown.
BIN
public/project/bgms/towerBoss3.opus
Normal file
BIN
public/project/bgms/towerBoss3.opus
Normal file
Binary file not shown.
BIN
public/project/bgms/winter.opus
Normal file
BIN
public/project/bgms/winter.opus
Normal file
Binary file not shown.
BIN
public/project/bgms/winterTown.opus
Normal file
BIN
public/project/bgms/winterTown.opus
Normal file
Binary file not shown.
@ -3,22 +3,170 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
|
|||||||
"main": {
|
"main": {
|
||||||
"floorIds": [
|
"floorIds": [
|
||||||
"empty",
|
"empty",
|
||||||
"sample0",
|
"MT0",
|
||||||
"sample1",
|
"MT1",
|
||||||
"sample2",
|
"MT2",
|
||||||
"MT0"
|
"MT3",
|
||||||
|
"MT4",
|
||||||
|
"MT5",
|
||||||
|
"MT6",
|
||||||
|
"MT7",
|
||||||
|
"MT8",
|
||||||
|
"MT9",
|
||||||
|
"MT10",
|
||||||
|
"MT11",
|
||||||
|
"MT12",
|
||||||
|
"MT13",
|
||||||
|
"MT14",
|
||||||
|
"MT15",
|
||||||
|
"MT16",
|
||||||
|
"MT17",
|
||||||
|
"MT18",
|
||||||
|
"MT19",
|
||||||
|
"MT20",
|
||||||
|
"MT21",
|
||||||
|
"tower1",
|
||||||
|
"tower2",
|
||||||
|
"tower3",
|
||||||
|
"tower4",
|
||||||
|
"tower5",
|
||||||
|
"tower6",
|
||||||
|
"tower7",
|
||||||
|
"MT22",
|
||||||
|
"MT23",
|
||||||
|
"MT24",
|
||||||
|
"MT25",
|
||||||
|
"MT26",
|
||||||
|
"MT27",
|
||||||
|
"MT28",
|
||||||
|
"MT29",
|
||||||
|
"MT30",
|
||||||
|
"MT31",
|
||||||
|
"MT32",
|
||||||
|
"MT33",
|
||||||
|
"MT34",
|
||||||
|
"MT35",
|
||||||
|
"MT36",
|
||||||
|
"MT37",
|
||||||
|
"MT38",
|
||||||
|
"MT39",
|
||||||
|
"MT40",
|
||||||
|
"MT41",
|
||||||
|
"MT42",
|
||||||
|
"MT43",
|
||||||
|
"MT44",
|
||||||
|
"MT45",
|
||||||
|
"snowTown",
|
||||||
|
"snowShop",
|
||||||
|
"MT46",
|
||||||
|
"MT47",
|
||||||
|
"MT48",
|
||||||
|
"MT49",
|
||||||
|
"MT50",
|
||||||
|
"MT51",
|
||||||
|
"MT52",
|
||||||
|
"MT53",
|
||||||
|
"MT54",
|
||||||
|
"MT55",
|
||||||
|
"MT56",
|
||||||
|
"MT57",
|
||||||
|
"MT58",
|
||||||
|
"MT59",
|
||||||
|
"MT60",
|
||||||
|
"MT61",
|
||||||
|
"MT62",
|
||||||
|
"MT63",
|
||||||
|
"MT64",
|
||||||
|
"MT65",
|
||||||
|
"MT66",
|
||||||
|
"MT67",
|
||||||
|
"MT68",
|
||||||
|
"MT69",
|
||||||
|
"MT71",
|
||||||
|
"MT72",
|
||||||
|
"MT73",
|
||||||
|
"MT74",
|
||||||
|
"MT75",
|
||||||
|
"MT76",
|
||||||
|
"MT77",
|
||||||
|
"MT78",
|
||||||
|
"MT79",
|
||||||
|
"MT80",
|
||||||
|
"MT81",
|
||||||
|
"MT82",
|
||||||
|
"MT83",
|
||||||
|
"MT84",
|
||||||
|
"MT85",
|
||||||
|
"MT86",
|
||||||
|
"MT87",
|
||||||
|
"MT88",
|
||||||
|
"MT89",
|
||||||
|
"MT90",
|
||||||
|
"MT91",
|
||||||
|
"MT92",
|
||||||
|
"MT93",
|
||||||
|
"MT94",
|
||||||
|
"MT95",
|
||||||
|
"MT96",
|
||||||
|
"MT97"
|
||||||
|
],
|
||||||
|
"floorPartitions": [
|
||||||
|
[
|
||||||
|
"MT0",
|
||||||
|
"MT16"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"MT17",
|
||||||
|
"MT100"
|
||||||
|
]
|
||||||
],
|
],
|
||||||
"floorPartitions": [],
|
|
||||||
"images": [
|
"images": [
|
||||||
"bear.png",
|
"IQ.png",
|
||||||
"bg.jpg",
|
"arrow.png",
|
||||||
"brave.png",
|
"atk.png",
|
||||||
"dragon.png",
|
"bg.webp",
|
||||||
"hero.png",
|
"boom.png",
|
||||||
"winskin.png"
|
"botton.png",
|
||||||
|
"cloud.png",
|
||||||
|
"def.png",
|
||||||
|
"exp.png",
|
||||||
|
"fog.png",
|
||||||
|
"hero1.png",
|
||||||
|
"hero2.png",
|
||||||
|
"hp.png",
|
||||||
|
"money.png",
|
||||||
|
"skill.png",
|
||||||
|
"skill0.png",
|
||||||
|
"skill1.png",
|
||||||
|
"skill10.png",
|
||||||
|
"skill11.png",
|
||||||
|
"skill12.png",
|
||||||
|
"skill13.png",
|
||||||
|
"skill14.png",
|
||||||
|
"skill2.png",
|
||||||
|
"skill3.png",
|
||||||
|
"skill4.png",
|
||||||
|
"skill5.png",
|
||||||
|
"skill6.png",
|
||||||
|
"skill7.png",
|
||||||
|
"skill8.png",
|
||||||
|
"skill9.png",
|
||||||
|
"sun.png",
|
||||||
|
"tower7.webp",
|
||||||
|
"winskin.png",
|
||||||
|
"winskin2.png",
|
||||||
|
"winskin3.png"
|
||||||
],
|
],
|
||||||
"tilesets": [
|
"tilesets": [
|
||||||
"magictower.png"
|
"magictower.png",
|
||||||
|
"043-Cave01.png",
|
||||||
|
"004-Mountain01.png",
|
||||||
|
"Map-Tower01.png",
|
||||||
|
"Caverna1.png",
|
||||||
|
"map-tower.png",
|
||||||
|
"winter1.png",
|
||||||
|
"snowTown.png",
|
||||||
|
"room.png"
|
||||||
],
|
],
|
||||||
"animates": [
|
"animates": [
|
||||||
"amazed",
|
"amazed",
|
||||||
@ -45,7 +193,27 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
|
|||||||
"zone"
|
"zone"
|
||||||
],
|
],
|
||||||
"bgms": [
|
"bgms": [
|
||||||
"bgm.opus"
|
"beforeBoss.opus",
|
||||||
|
"beforeNight.opus",
|
||||||
|
"cave.opus",
|
||||||
|
"chapter2ED.opus",
|
||||||
|
"escape.opus",
|
||||||
|
"escape2.opus",
|
||||||
|
"grass.opus",
|
||||||
|
"mount.opus",
|
||||||
|
"night.opus",
|
||||||
|
"palaceCenter.opus",
|
||||||
|
"palaceNorth.opus",
|
||||||
|
"palaceSouth.opus",
|
||||||
|
"plot1.opus",
|
||||||
|
"road.opus",
|
||||||
|
"title.opus",
|
||||||
|
"tower.opus",
|
||||||
|
"towerBoss.opus",
|
||||||
|
"towerBoss2.opus",
|
||||||
|
"towerBoss3.opus",
|
||||||
|
"winter.opus",
|
||||||
|
"winterTown.opus"
|
||||||
],
|
],
|
||||||
"sounds": [
|
"sounds": [
|
||||||
"008-System08.opus",
|
"008-System08.opus",
|
||||||
@ -89,7 +257,10 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
|
|||||||
"tree.opus",
|
"tree.opus",
|
||||||
"zone.opus"
|
"zone.opus"
|
||||||
],
|
],
|
||||||
"fonts": [],
|
"fonts": [
|
||||||
|
"normal",
|
||||||
|
"FiraCode"
|
||||||
|
],
|
||||||
"nameMap": {
|
"nameMap": {
|
||||||
"确定": "confirm.opus",
|
"确定": "confirm.opus",
|
||||||
"取消": "cancel.opus",
|
"取消": "cancel.opus",
|
||||||
@ -188,7 +359,7 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
|
|||||||
"首饰",
|
"首饰",
|
||||||
"首饰"
|
"首饰"
|
||||||
],
|
],
|
||||||
"startBgm": "bgm.opus",
|
"startBgm": "title.opus",
|
||||||
"styles": {
|
"styles": {
|
||||||
"floorChangingStyle": " ",
|
"floorChangingStyle": " ",
|
||||||
"statusBarColor": [
|
"statusBarColor": [
|
||||||
@ -214,14 +385,14 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
|
|||||||
"splitImages": []
|
"splitImages": []
|
||||||
},
|
},
|
||||||
"firstData": {
|
"firstData": {
|
||||||
"title": "魔塔样板",
|
"title": "人类:开天辟地",
|
||||||
"name": "template",
|
"name": "HumanBreak",
|
||||||
"version": "Ver 2.B",
|
"version": "Ver 2.7.3.1",
|
||||||
"floorId": "sample0",
|
"floorId": "MT0",
|
||||||
"hero": {
|
"hero": {
|
||||||
"image": "hero.png",
|
"image": "hero1.png",
|
||||||
"animate": false,
|
"animate": false,
|
||||||
"name": "阳光",
|
"name": "原始人",
|
||||||
"lv": 1,
|
"lv": 1,
|
||||||
"hpmax": 0,
|
"hpmax": 0,
|
||||||
"hp": 500,
|
"hp": 500,
|
||||||
@ -240,8 +411,8 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
|
|||||||
},
|
},
|
||||||
"loc": {
|
"loc": {
|
||||||
"direction": "up",
|
"direction": "up",
|
||||||
"x": 6,
|
"x": 7,
|
||||||
"y": 10
|
"y": 13
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"followers": [],
|
"followers": [],
|
||||||
@ -379,12 +550,100 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
|
|||||||
],
|
],
|
||||||
"startText": [
|
"startText": [
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "hideStatusBar"
|
||||||
"text": "欢迎使用古祠制作的 2.B 样板,本样板主要针对渲染系统进行了重构,现在我们有了更加方便强大的渲染系统,也对部分相关事件进行了重置!"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "setText",
|
||||||
"text": "这里是开场剧情,可以在编辑器全塔属性中修改,试着修改一下吧!"
|
"position": "down",
|
||||||
|
"text": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"background": "winskin3.png",
|
||||||
|
"textfont": 20,
|
||||||
|
"time": 25,
|
||||||
|
"letterSpacing": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "setGlobalAttribute",
|
||||||
|
"name": "font",
|
||||||
|
"value": "normal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "playSound",
|
||||||
|
"name": "paper.opus"
|
||||||
|
},
|
||||||
|
"人们说要铭记历史,但他们却忘记了历史。\n ——我是这样评价这个故事的。",
|
||||||
|
{
|
||||||
|
"type": "playSound",
|
||||||
|
"name": "paper.opus"
|
||||||
|
},
|
||||||
|
"人类简史——起源篇",
|
||||||
|
{
|
||||||
|
"type": "playSound",
|
||||||
|
"name": "paper.opus"
|
||||||
|
},
|
||||||
|
"在历史的长河中,山火、暴雨、地震不过是自然界的常态,是时间流逝中微不足道的涟漪。",
|
||||||
|
{
|
||||||
|
"type": "playSound",
|
||||||
|
"name": "paper.opus"
|
||||||
|
},
|
||||||
|
"这些自然现象如同大地的呼吸,时而平静,时而狂暴。",
|
||||||
|
{
|
||||||
|
"type": "playSound",
|
||||||
|
"name": "paper.opus"
|
||||||
|
},
|
||||||
|
"对于动物和植物而言,这些变化是生存的考验,是自然选择的无情法则。",
|
||||||
|
{
|
||||||
|
"type": "playSound",
|
||||||
|
"name": "paper.opus"
|
||||||
|
},
|
||||||
|
"每一次山火,都意味着森林的重生与毁灭;每一场暴雨,都带来了生命的滋润与洪水的威胁;每一次地震,都改变了地貌,塑造了新的环境。",
|
||||||
|
{
|
||||||
|
"type": "playSound",
|
||||||
|
"name": "paper.opus"
|
||||||
|
},
|
||||||
|
"在这片土地上,生命在自然的力量中挣扎、适应、繁衍。",
|
||||||
|
{
|
||||||
|
"type": "playSound",
|
||||||
|
"name": "paper.opus"
|
||||||
|
},
|
||||||
|
"那些无法适应的,最终被淘汰;而那些幸存者,则继续在这片土地上书写着生命的传奇。",
|
||||||
|
{
|
||||||
|
"type": "playSound",
|
||||||
|
"name": "paper.opus"
|
||||||
|
},
|
||||||
|
"然而,对于那些在这片土地上生存的原始人而言,这些自然现象不仅仅是生存的考验,更是他们日常生活中不可或缺的一部分。",
|
||||||
|
{
|
||||||
|
"type": "playSound",
|
||||||
|
"name": "paper.opus"
|
||||||
|
},
|
||||||
|
"在公元前8000年,这里曾有一个不起眼的山洞,隐匿于群山之间,仿佛与世隔绝。山洞中,原始人正忙碌着,准备迎接即将到来的季节变化。",
|
||||||
|
{
|
||||||
|
"type": "playSound",
|
||||||
|
"name": "paper.opus"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "sleep",
|
||||||
|
"time": 1000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "setText",
|
||||||
|
"position": "down",
|
||||||
|
"text": [
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"background": "winskin2.png",
|
||||||
|
"textfont": 20,
|
||||||
|
"time": 25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "showStatusBar"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"shops": [
|
"shops": [
|
||||||
@ -537,7 +796,7 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
|
|||||||
"counterAttack": 0.1,
|
"counterAttack": 0.1,
|
||||||
"purify": 3,
|
"purify": 3,
|
||||||
"hatred": 2,
|
"hatred": 2,
|
||||||
"animateSpeed": 400,
|
"animateSpeed": 277.7778,
|
||||||
"statusCanvasRowsOnMobile": 3,
|
"statusCanvasRowsOnMobile": 3,
|
||||||
"floorChangeTime": 200,
|
"floorChangeTime": 200,
|
||||||
"moveSpeed": null
|
"moveSpeed": null
|
||||||
@ -554,16 +813,27 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
|
|||||||
],
|
],
|
||||||
"flyNearStair": false,
|
"flyNearStair": false,
|
||||||
"flyRecordPosition": true,
|
"flyRecordPosition": true,
|
||||||
|
"steelDoorWithoutKey": true,
|
||||||
"itemFirstText": false,
|
"itemFirstText": false,
|
||||||
|
"equipboxButton": false,
|
||||||
"enableAddPoint": false,
|
"enableAddPoint": false,
|
||||||
"enableNegativeDamage": false,
|
"enableNegativeDamage": true,
|
||||||
"betweenAttackMax": true,
|
"betweenAttackMax": false,
|
||||||
|
"useLoop": true,
|
||||||
|
"startUsingCanvas": false,
|
||||||
|
"statusCanvas": true,
|
||||||
|
"displayEnemyDamage": true,
|
||||||
|
"displayCritical": true,
|
||||||
|
"displayExtraDamage": true,
|
||||||
"enableGentleClick": true,
|
"enableGentleClick": true,
|
||||||
"ignoreChangeFloor": true,
|
"ignoreChangeFloor": true,
|
||||||
"canGoDeadZone": false,
|
"canGoDeadZone": false,
|
||||||
"enableMoveDirectly": true,
|
"enableMoveDirectly": true,
|
||||||
"enableRouteFolding": true,
|
"enableRouteFolding": true,
|
||||||
"disableShopOnDamage": false,
|
"disableShopOnDamage": false,
|
||||||
"blurFg": true
|
"blurFg": true,
|
||||||
|
"extendToolbar": false,
|
||||||
|
"enableEnemyPoint": null,
|
||||||
|
"autoScale": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,84 +1,219 @@
|
|||||||
var enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80 =
|
var enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80 =
|
||||||
{
|
{
|
||||||
"greenSlime": {"name":"绿头怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"greenSlime": {"name":"绿头怪","hp":100,"atk":11,"def":3,"money":0,"exp":1,"point":0,"special":[],"description":"别小看这些家伙,虽然他们只是一种极其低级的怪物,低级到普通人用手都可以打死,但数量是他们的优势。"},
|
||||||
"redSlime": {"name":"红头怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[16,18],"value":10},
|
"redSlime": {"name":"红头怪","hp":120,"atk":16,"def":6,"money":0,"exp":2,"point":0,"special":[],"value":10,"description":"即使是最弱的怪物,也有进化的时候,对吧?据说,红头怪便是绿头怪进化形成的。"},
|
||||||
"blackSlime": {"name":"青头怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"blackSlime": {"name":"青头怪","hp":170,"atk":20,"def":8,"money":0,"exp":3,"point":0,"special":[],"description":"看,这就是最弱的怪物进化出的最强的怪物之一了。他们弱吗?"},
|
||||||
"slimelord": {"name":"怪王","hp":100,"atk":120,"def":0,"money":10,"exp":0,"point":0,"special":[1,9]},
|
"slimelord": {"name":"粘液王","hp":200,"atk":58,"def":24,"money":0,"exp":8,"point":0,"special":[],"description":"看上去黏糊糊的,实际也确实黏糊糊的,据说是史莱姆族的长老级人物,拥有不俗的实力。"},
|
||||||
"bat": {"name":"小蝙蝠","hp":100,"atk":120,"def":0,"money":2,"exp":0,"point":0,"special":[1]},
|
"bat": {"name":"小蝙蝠","hp":60,"atk":15,"def":0,"money":0,"exp":2,"point":0,"special":[4],"description":"经常出现在山洞中,再平常不过了。但是这次它却有了攻击性。"},
|
||||||
"bigBat": {"name":"大蝙蝠","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"bigBat": {"name":"大蝙蝠","hp":150,"atk":17,"def":5,"money":0,"exp":4,"point":0,"special":[4],"crit":0,"charge":0,"courage":0,"together":0,"hungry":0,"value":100,"n":1000,"description":"或许是小蝙蝠的父亲?一种极其强悍的蝙蝠,实力不够千万不要靠近他。"},
|
||||||
"redBat": {"name":"红蝙蝠","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"redBat": {"name":"恐怖蝙蝠","hp":1200,"atk":260,"def":110,"money":1,"exp":32,"point":0,"special":[5],"description":"恐惧?或许他们并不知道恐惧是什么,他们不会害怕任何东西,即使这个东西能够威胁到自己的生命。"},
|
||||||
"vampire": {"name":"冥灵魔王","hp":888,"atk":888,"def":888,"money":888,"exp":888,"point":0,"special":[6],"n":8},
|
"vampire": {"name":"冥灵魔王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
"skeleton": {"name":"骷髅人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"skeleton": {"name":"骷髅人","hp":300,"atk":80,"def":10,"money":0,"exp":9,"point":0,"special":[1],"crit":300,"description":"骷髅?看起来是被人动了手脚,或许是未来的高科技机器人呢?"},
|
||||||
"skeletonCaptain": {"name":"骷髅队长","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"skeletonCaptain": {"name":"骷髅队长","hp":750,"atk":200,"def":50,"money":0,"exp":21,"point":0,"special":[1],"crit":1000,"description":"机器人也会有领袖吗?这还真是第一次听说。"},
|
||||||
"zombie": {"name":"兽人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"zombie": {"name":"兽人","hp":150,"atk":43,"def":14,"money":0,"exp":6,"point":0,"special":[],"description":"野兽嘛,在远古时期,再正常不过了。"},
|
||||||
"zombieKnight": {"name":"兽人武士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"zombieKnight": {"name":"兽人武士","hp":480,"atk":62,"def":30,"money":0,"exp":15,"point":0,"special":[],"description":"他们总是认为,要变得强大,杀掉其他野兽,自己才能存活下来。"},
|
||||||
"rock": {"name":"石头人","hp":50,"atk":50,"def":0,"money":3,"exp":0,"point":0,"special":[3]},
|
"rock": {"name":"洞穴巨石","hp":31,"atk":25,"def":0,"money":0,"exp":4,"point":0,"special":[3],"description":"一些具有攻击性的巨石?或许就是兽人穿上了一层石头外衣吧。"},
|
||||||
"bluePriest": {"name":"初级法师","hp":100,"atk":120,"def":0,"money":3,"exp":0,"point":1,"special":[9]},
|
"bluePriest": {"name":"初级法师","hp":100,"atk":120,"def":0,"money":3,"exp":0,"point":1,"special":[2]},
|
||||||
"redPriest": {"name":"高级法师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"redPriest": {"name":"高级法师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
"brownWizard": {"name":"初级巫师","hp":100,"atk":120,"def":0,"money":16,"exp":0,"point":0,"special":[15],"value":100,"range":2},
|
"brownWizard": {"name":"苍蓝巫师","hp":16000,"atk":5000,"def":2000,"money":4,"exp":250,"point":0,"special":[28],"value":100,"range":2,"melt":null,"paleShield":25},
|
||||||
"redWizard": {"name":"高级巫师","hp":1000,"atk":1200,"def":0,"money":160,"exp":0,"point":0,"special":[15],"value":200,"zoneSquare":true},
|
"redWizard": {"name":"靛红巫师","hp":12000,"atk":6000,"def":4000,"money":4,"exp":300,"point":0,"special":[25],"value":200,"zoneSquare":true,"melt":30},
|
||||||
"swordsman": {"name":"双手剑士","hp":100,"atk":120,"def":0,"money":6,"exp":0,"point":0,"special":[4]},
|
"swordsman": {"name":"野蛮剑士","hp":250,"atk":55,"def":27,"money":0,"exp":9,"point":0,"special":[15],"value":75,"description":"剑?这是什么东西?他们拿的只是比较锋利的骨头吧。"},
|
||||||
"soldier": {"name":"冥战士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"soldier": {"name":"冥战士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
"yellowKnight": {"name":"金骑士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"yellowKnight": {"name":"勇气骑士","hp":2000,"atk":500,"def":250,"money":1,"exp":30,"point":0,"special":[11],"charge":500,"description":"人们总是说,战斗,需要的就是勇气。而骑士,或许就站在了这勇气之巅了吧。有什么用呢?战场上,该死的时候就是得死,哪怕是在这远古时期,即使不是战场,一场战斗也足以决定生死。"},
|
||||||
"redKnight": {"name":"红骑士","hp":500,"atk":200,"def":50,"money":0,"exp":0,"point":0,"special":[7]},
|
"redKnight": {"name":"靛红骑士","hp":30000,"atk":9000,"def":3000,"money":5,"exp":600,"point":0,"special":[29],"specialHalo":[27],"iceCore":15,"haloRange":3,"fireCore":15},
|
||||||
"darkKnight": {"name":"黑骑士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"darkKnight": {"name":"黑骑士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
"blueKnight": {"name":"蓝骑士","hp":100,"atk":120,"def":0,"money":9,"exp":0,"point":0,"special":[8]},
|
"blueKnight": {"name":"苍蓝骑士","hp":40000,"atk":9000,"def":4000,"money":6,"exp":600,"point":0,"special":[4]},
|
||||||
"goldSlime": {"name":"黄头怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"goldSlime": {"name":"黄头怪","hp":1000,"atk":50,"def":50,"money":0,"exp":18,"point":0,"special":[2]},
|
||||||
"poisonSkeleton": {"name":"紫骷髅","hp":50,"atk":60,"def":70,"money":80,"exp":0,"point":0,"special":[13]},
|
"poisonSkeleton": {"name":"紫骷髅","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
"poisonBat": {"name":"紫蝙蝠","hp":100,"atk":120,"def":0,"money":14,"exp":0,"point":0,"special":[13]},
|
"poisonBat": {"name":"山间蝙蝠","hp":800,"atk":170,"def":50,"money":1,"exp":24,"point":0,"special":[5],"description":"山的高出,总会有一些恐怖的东西,就比如这只蝙蝠。"},
|
||||||
"skeletonPriest": {"name":"骷髅法师","hp":100,"atk":100,"def":0,"money":0,"exp":0,"point":0,"special":[18],"value":20},
|
"skeletonPriest": {"name":"智慧骷髅","hp":4000,"atk":1200,"def":900,"money":1,"exp":75,"point":0,"special":[1,13],"value":20,"crit":500,"description":"人们说智慧可以做到任何事情,而这只骷髅却将可以变为了很容易。“我挥一挥法杖,智慧便会如泉般涌来。”,他说。"},
|
||||||
"skeletonKing": {"name":"骷髅王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"skeletonKing": {"name":"骷髅王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
"evilHero": {"name":"迷失勇者","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"evilHero": {"name":"迷失勇者","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
"demonPriest": {"name":"魔神法师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"demonPriest": {"name":"苍蓝法师","hp":20000,"atk":4000,"def":3000,"money":4,"exp":250,"point":0,"special":[13]},
|
||||||
"goldHornSlime": {"name":"金角怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"goldHornSlime": {"name":"尖角怪","hp":1500,"atk":366,"def":166,"money":1,"exp":35,"point":0,"special":[],"description":"一个奇怪的物种,长着两只角就了不起了吗?或许还真是!"},
|
||||||
"silverSlime": {"name":"银头怪","hp":100,"atk":120,"def":0,"money":15,"exp":0,"point":0,"special":[14]},
|
"silverSlime": {"name":"银头怪","hp":250,"atk":50,"def":20,"money":0,"exp":11,"point":0,"special":[2]},
|
||||||
"whiteHornSlime": {"name":"尖角怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"whiteHornSlime": {"name":"恐怖尖角怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
"redSwordsman": {"name":"剑王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"redSwordsman": {"name":"山间盗贼","hp":1000,"atk":175,"def":40,"money":1,"exp":24,"point":0,"special":[4],"n":8,"description":"即使是现代法治社会,也总会有人去抢夺别人的东西,更何况远古时期呢?"},
|
||||||
"poisonZombie": {"name":"绿兽人","hp":100,"atk":120,"def":0,"money":13,"exp":0,"point":0,"special":[12]},
|
"poisonZombie": {"name":"绿兽人","hp":100,"atk":120,"def":0,"money":13,"exp":0,"point":0,"special":[12]},
|
||||||
"octopus": {"name":"血影","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"bigImage":null},
|
"octopus": {"name":"血影","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
"princessEnemy": {"name":"假公主","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"princessEnemy": {"name":"假公主","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
"angel": {"name":"天使","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"angel": {"name":"天使","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
"elemental": {"name":"元素生物","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"elemental": {"name":"元素生物","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
"steelGuard": {"name":"铁守卫","hp":50,"atk":50,"def":50,"money":0,"exp":0,"point":0,"special":[18],"value":20},
|
"steelGuard": {"name":"铁守卫","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[18],"value":20},
|
||||||
"evilBat": {"name":"邪恶蝙蝠","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"evilBat": {"name":"邪恶蝙蝠","hp":1000,"atk":800,"def":350,"money":1,"exp":40,"point":0,"special":[2],"bigImage":null},
|
||||||
|
"frozenSkeleton": {"name":"冻死骨","hp":7500,"atk":2500,"def":1000,"money":2,"exp":90,"point":0,"special":[1,20],"crit":500,"ice":10,"description":"弱小,无助,哀嚎,这就是残酷的现实。哪怕你身处极寒之中,也难有人对你伸出援手。人类总会帮助他人,但在这绝望的环境下,人类的本性便暴露无遗。这一个个精致却又无情的骷髅,便是那些在极寒之中死去的冤魂。"},
|
||||||
"silverSlimelord": {"name":"银怪王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"silverSlimelord": {"name":"银怪王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
"goldSlimelord": {"name":"金怪王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"goldSlimelord": {"name":"金怪王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
"skeletonWarrior": {"name":"骷髅士兵","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"skeletonWarrior": {"name":"骷髅士兵","hp":500,"atk":100,"def":20,"money":0,"exp":12,"point":0,"special":[1],"crit":500,"description":"看来未来的机器人并不满足与赤手空拳,他们也拿上了武器。"},
|
||||||
"whiteSlimeman": {"name":"水银战士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"whiteSlimeman": {"name":"水银史莱姆人","hp":750,"atk":100,"def":45,"money":0,"exp":20,"point":0,"special":[4],"description":"汞?这听起来不像是一个远古时期应该存在的名字,但是把它拆分开,叫做水银,是不是可爱了一些?"},
|
||||||
"slimeman": {"name":"影子战士","hp":100,"atk":0,"def":0,"money":11,"exp":0,"point":0,"special":[9],"atkValue":2,"defValue":3},
|
"slimeman": {"name":"莱姆人","hp":125,"atk":30,"def":10,"money":0,"exp":4,"point":0,"special":[4],"atkValue":2,"defValue":3,"description":"又有谁不能保证史莱姆也能进化成人型生物呢?"},
|
||||||
"yellowGateKeeper": {"name":"初级卫兵","hp":100,"atk":120,"def":0,"money":10,"exp":0,"point":0,"special":[]},
|
"yellowGateKeeper": {"name":"神秘卫兵","hp":375,"atk":200,"def":15,"money":1,"exp":25,"point":0,"special":[1],"crit":5000,"description":"神秘卫兵。确实很神秘,他们从不说话,只知道看着你,当你攻击他们的时候,他们会给你致命的反击。"},
|
||||||
"blueGateKeeper": {"name":"中级卫兵","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"blueGateKeeper": {"name":"神秘雕像","hp":1000,"atk":275,"def":120,"money":1,"exp":38,"point":0,"special":[1],"crit":1000,"description":"大概,或许,跟神秘卫兵是同一类东西?"},
|
||||||
"redGateKeeper": {"name":"高级卫兵","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"redGateKeeper": {"name":"勇气卫兵","hp":1000,"atk":450,"def":250,"money":1,"exp":30,"point":0,"special":[1],"crit":2000,"description":"没有人知道这些卫兵是什么。他们有红通通的外表,一颗怀揣着勇气的心。但是,勇气这东西,并不是越多越好,为什么?跟他打一架,他的下场便是勇气过多的下场。"},
|
||||||
"magicMaster": {"name":"黑暗大法师","hp":100,"atk":120,"def":0,"money":12,"exp":0,"point":0,"special":[11],"value":0.3333333333333333,"add":true,"notBomb":true},
|
"magicMaster": {"name":"黑暗大法师","hp":100,"atk":120,"def":0,"money":12,"exp":0,"point":0,"special":[11],"value":0.3333333333333333,"add":true,"notBomb":true},
|
||||||
"devilWarrior": {"name":"魔神武士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"devilWarrior": {"name":"魔神武士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
"fairyEnemy": {"name":"仙子","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"fairyEnemy": {"name":"仙子","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
"dragon": {"name":"魔龙","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"bigImage":null},
|
"dragon": {"name":"魔龙","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
"skeletonKnight": {"name":"骷髅武士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"skeletonKnight": {"name":"骷髅骑士","hp":25000,"atk":5000,"def":4000,"money":5,"exp":450,"point":0,"special":[1,29],"crit":300,"value":5000,"specialHalo":[4],"haloRange":2},
|
||||||
"skeletonPresbyter": {"name":"骷髅巫师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"skeletonPresbyter": {"name":"骷髅巫师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"description":"法杖,人们总觉得这种东西只应该出现在虚拟的世界中。可是,智慧却成功将这件事变为了现实,而产物便是这只骷髅巫师。"},
|
||||||
"ironRock": {"name":"铁面人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"ironRock": {"name":"山间巨石","hp":750,"atk":150,"def":0,"money":0,"exp":20,"point":0,"special":[3],"description":"恐怖的东西,除了那只蝙蝠,还有...这个巨石。"},
|
||||||
"grayRock": {"name":"灰色石头人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"grayRock": {"name":"林间巨石","hp":100,"atk":60,"def":0,"money":0,"exp":12,"point":0,"special":[3],"description":"貌似比山洞里面的那些家伙硬了一些?哼,那又能有什么用呢?"},
|
||||||
"yellowPriest": {"name":"中级法师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"yellowPriest": {"name":"中级法师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
"evilPrincess": {"name":"痛苦魔女","hp":1000,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[10]},
|
"evilPrincess": {"name":"痛苦魔女","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
"blademaster": {"name":"剑圣","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"blademaster": {"name":"苍蓝之剑","hp":5000,"atk":10000,"def":5000,"money":5,"exp":500,"point":0,"special":[1,5],"courage":null,"crit":250},
|
||||||
"evilFairy": {"name":"黑暗仙子","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"evilFairy": {"name":"黑暗仙子","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"blueRock": {"name":"勇气之石","hp":2000,"atk":450,"def":230,"money":1,"exp":30,"point":0,"special":[],"description":"巨石,这本身是一种不可怕的怪物。而可怕的是,当这些巨石拥有了勇气,或许就很少有人敢于直面他们了。"},
|
||||||
|
"skeletonLite": {"name":"骷髅精英","hp":2000,"atk":275,"def":240,"money":1,"exp":35,"point":0,"special":[9],"description":"骷髅们总是说,没有防御力,让我怎么去攻击别人?呵,这防御力不就来了吗?可是,这又有何用呢?"},
|
||||||
"greenKnight": {"name":"强盾骑士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"greenKnight": {"name":"强盾骑士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
"bowman": {"name":"初级弓兵","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"bowman": {"name":"猎人","hp":500,"atk":100,"def":50,"money":0,"exp":16,"point":0,"special":[24],"value":75,"description":"没人知道这些人怎么做出的弓,也没人知道他们怎么收集的这么多剑。而其他人唯一能做的事,便是远离他们。"},
|
||||||
"watcherSlime": {"name":"邪眼怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"liteBowman": {"name":"山间猎手","hp":1200,"atk":200,"def":60,"money":1,"exp":27,"point":0,"special":[24],"description":"这箭,或许就是那些败于他弓下的那些不知好歹的莽夫的骨头吧。或许,绕开他的视野才是躲避他的攻击的最好办法。"},
|
||||||
"devilKnight": {"name":"恶灵骑士","hp":150,"atk":100,"def":50,"money":0,"exp":0,"point":0,"special":[1,5,7,8]},
|
"crimsonZombie": {"name":"勇气之兽","hp":1800,"atk":2000,"def":-100,"money":1,"exp":35,"point":0,"special":[],"description":"没人会愿意跟这些野兽一起吧?至少我是不想。上天赋予的勇气,却让他们更加渴望鲜血,这不是很可悲吗?"},
|
||||||
"grayPriest": {"name":"混沌法师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"watcherSlime": {"name":"邪眼史莱姆","hp":5000,"atk":1200,"def":600,"money":1,"exp":50,"point":0,"special":[17],"description":"成群结队地出现在森林中,看遍百花齐放,经历万物凋零。他们守在这森林中,将那些企图突破这里的人置于死地。"},
|
||||||
"greenGateKeeper": {"name":"卫兵队长","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"mutantSlimeman": {"name":"变异史莱姆人","hp":350,"atk":70,"def":27,"money":0,"exp":13,"point":0,"special":[],"description":"据说,史莱姆人也会基因突变,这样就产生了这种变异史莱姆人。"},
|
||||||
"ghostSoldier": {"name":"冥队长","hp":200,"atk":100,"def":50,"money":0,"exp":0,"point":0,"special":[8]},
|
"devilKnight": {"name":"恶灵骑士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
"frostBat": {"name":"寒蝙蝠","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"grayPriest": {"name":"智慧法王","hp":3000,"atk":600,"def":250,"money":1,"exp":40,"point":0,"special":[13],"description":"法杖?他已经不需要了。没有人知道他长什么样,只知道他的智慧已非常人能及,只知道他的法术能让一个人在瞬间化为灰烬。"},
|
||||||
|
"greenGateKeeper": {"name":"睿智雕像","hp":5000,"atk":1250,"def":900,"money":1,"exp":65,"point":0,"special":[1],"crit":1000},
|
||||||
|
"ghostSoldier": {"name":"山间骷髅","hp":750,"atk":180,"def":40,"money":0,"exp":18,"point":0,"special":[],"description":"这次,他们穿上了盔甲。"},
|
||||||
|
"frostBat": {"name":"寒霜蝙蝠","hp":20000,"atk":3200,"def":2000,"money":2,"exp":2000,"point":0,"special":[4,20],"ice":90,"description":"“穿梭于寒风里,行走在锋芒中”,寒霜蝙蝠将这句话运用到了极致。别看那小小的身体,它足以将你拆的七零八落。在它面前,任何小把戏都会被它看得一清二楚。它那凶恶的眼神,是否在哪里见过呢?"},
|
||||||
"blackKing": {"name":"黑衣魔王","hp":1000,"atk":500,"def":0,"money":1000,"exp":1000,"point":0,"special":[],"notBomb":true},
|
"blackKing": {"name":"黑衣魔王","hp":1000,"atk":500,"def":0,"money":1000,"exp":1000,"point":0,"special":[],"notBomb":true},
|
||||||
"yellowKing": {"name":"黄衣魔王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"yellowKing": {"name":"黄衣魔王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
"greenKing": {"name":"青衣武士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"greenKing": {"name":"青衣武士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
"redKing": {"name":"红衣魔王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"redKing": {"name":"红衣魔王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
"blueKing": {"name":"白衣武士","hp":100,"atk":120,"def":0,"money":17,"exp":0,"point":0,"special":[16]},
|
"blueKing": {"name":"白衣武士","hp":100,"atk":120,"def":0,"money":17,"exp":0,"point":0,"special":[16]},
|
||||||
"keiskeiFairy": {"name":"铃兰花妖","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"E368": {"name":"绿头武装怪","hp":400,"atk":75,"def":30,"money":0,"exp":14,"point":0,"special":[]},
|
||||||
"tulipFairy": {"name":"郁金香花妖","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"E369": {"name":"红头武装怪","hp":450,"atk":85,"def":35,"money":0,"exp":17,"point":0,"special":[]},
|
||||||
"purpleBowman": {"name":"高级弓兵","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
"E370": {"name":"青头武装怪","hp":600,"atk":100,"def":50,"money":0,"exp":20,"point":0,"special":[1],"crit":400},
|
||||||
"bearDown": {"name":"熊出没","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"faceIds":{"down":"bearDown","left":"bearLeft","right":"bearRight","up":"bearUp"},"bigImage":null},
|
"E371": {"name":"武装怪王","hp":1500,"atk":230,"def":80,"money":1,"exp":30,"point":0,"special":[1],"crit":750},
|
||||||
"bearLeft": {"name":"熊出没","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"faceIds":{"down":"bearDown","left":"bearLeft","right":"bearRight","up":"bearUp"}},
|
"E372": {"name":"高级绿头怪","hp":280,"atk":66,"def":33,"money":0,"exp":12,"point":0,"special":[]},
|
||||||
"bearRight": {"name":"熊出没","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"faceIds":{"down":"bearDown","left":"bearLeft","right":"bearRight","up":"bearUp"}},
|
"E373": {"name":"高级红头怪","hp":333,"atk":77,"def":33,"money":0,"exp":15,"point":0,"special":[1],"crit":200},
|
||||||
"bearUp": {"name":"熊出没","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"faceIds":{"down":"bearDown","left":"bearLeft","right":"bearRight","up":"bearUp"}}
|
"E374": {"name":"高级青头怪","hp":666,"atk":111,"def":44,"money":0,"exp":19,"point":0,"special":[]},
|
||||||
|
"E375": {"name":"怪圣","hp":2000,"atk":270,"def":100,"money":1,"exp":33,"point":0,"special":[]},
|
||||||
|
"E498": {"name":"山间野兽","hp":1200,"atk":175,"def":60,"money":1,"exp":30,"point":0,"special":[7],"hungry":20},
|
||||||
|
"E499": {"name":"武装野兽","hp":1500,"atk":240,"def":100,"money":1,"exp":32,"point":0,"special":[7],"hungry":30},
|
||||||
|
"E500": {"name":"智慧兽人","hp":4500,"atk":1200,"def":700,"money":1,"exp":55,"point":0,"special":[7],"hungry":30},
|
||||||
|
"E501": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E502": {"name":"普通史塔茹","hp":900,"atk":160,"def":55,"money":1,"exp":25,"point":0,"special":[8],"together":20},
|
||||||
|
"E503": {"name":"武装史塔茹","hp":1300,"atk":240,"def":110,"money":1,"exp":31,"point":0,"special":[8],"value":null,"crit":null,"together":25},
|
||||||
|
"E504": {"name":"精英史塔茹","hp":2000,"atk":450,"def":150,"money":1,"exp":40,"point":0,"special":[8],"together":30},
|
||||||
|
"E505": {"name":"勇气史塔茹","hp":3000,"atk":500,"def":250,"money":1,"exp":35,"point":0,"special":[8],"together":10},
|
||||||
|
"E511": {"name":"山野蝙蝠","hp":1500,"atk":300,"def":120,"money":1,"exp":37,"point":0,"special":[5]},
|
||||||
|
"E512": {"name":"专业盗贼","hp":1800,"atk":400,"def":170,"money":1,"exp":40,"point":0,"special":[5]},
|
||||||
|
"E513": {"name":"黑影剑客","hp":10000,"atk":20000,"def":0,"money":5,"exp":600,"point":null,"special":[6],"n":6},
|
||||||
|
"E514": {"name":"雪原剑客","hp":10000,"atk":3000,"def":500,"money":2,"exp":110,"point":0,"special":[5,19]},
|
||||||
|
"E515": {"name":"勇气之剑","hp":2500,"atk":550,"def":270,"money":2,"exp":40,"point":0,"special":[5,10],"courage":500},
|
||||||
|
"E517": {"name":"勇气之盾","hp":4000,"atk":400,"def":350,"money":1,"exp":30,"point":0,"special":[9]},
|
||||||
|
"E518": {"name":"勇气圣骑","hp":4000,"atk":1000,"def":400,"money":1,"exp":45,"point":0,"special":[11],"charge":600},
|
||||||
|
"E519": {"name":"勇气之魄","hp":3000,"atk":1000,"def":150,"money":1,"exp":45,"point":0,"special":[11],"charge":300},
|
||||||
|
"E520": {"name":"勇气之魂","hp":4000,"atk":1200,"def":200,"money":1,"exp":45,"point":0,"special":[1],"crit":500},
|
||||||
|
"E521": {"name":"勇气之箭","hp":2500,"atk":425,"def":250,"money":1,"exp":30,"point":0,"special":[1,12],"value":400,"crit":500},
|
||||||
|
"E522": {"name":"勇气骷髅","hp":2000,"atk":600,"def":200,"money":1,"exp":30,"point":0,"special":[]},
|
||||||
|
"E523": {"name":"红色骷髅士兵","hp":3000,"atk":1000,"def":600,"money":1,"exp":50,"point":0,"special":[]},
|
||||||
|
"E524": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E525": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E536": {"name":"智慧法师","hp":2000,"atk":400,"def":200,"money":1,"exp":35,"point":0,"special":[13]},
|
||||||
|
"E537": {"name":"智慧蝴蝶","hp":1500,"atk":450,"def":200,"money":1,"exp":35,"point":0,"special":[5,14]},
|
||||||
|
"E538": {"name":"再生法师","hp":30000,"atk":6000,"def":6000,"money":5,"exp":500,"point":0,"special":[31],"hpHalo":25},
|
||||||
|
"E539": {"name":"苍蓝恶魔","hp":15000,"atk":4500,"def":2000,"money":4,"exp":200,"point":0,"special":[8],"together":25},
|
||||||
|
"E544": {"name":"智慧具形","hp":2500,"atk":550,"def":225,"money":1,"exp":35,"point":0,"special":[]},
|
||||||
|
"E545": {"name":"勇气圣法","hp":3000,"atk":800,"def":350,"money":1,"exp":40,"point":0,"special":[13]},
|
||||||
|
"E546": {"name":"智慧信仰者","hp":2000,"atk":600,"def":250,"money":1,"exp":35,"point":0,"special":[]},
|
||||||
|
"E547": {"name":"智慧主教","hp":3000,"atk":700,"def":300,"money":1,"exp":40,"point":0,"special":[1],"crit":500},
|
||||||
|
"E548": {"name":"智慧之灵","hp":1000,"atk":550,"def":250,"money":1,"exp":35,"point":0,"special":[]},
|
||||||
|
"E549": {"name":"智慧之史","hp":1000,"atk":1000,"def":100,"money":1,"exp":35,"point":0,"special":[1],"crit":10000},
|
||||||
|
"E550": {"name":"智慧之兔","hp":1500,"atk":600,"def":200,"money":1,"exp":35,"point":0,"special":[8],"together":20},
|
||||||
|
"E556": {"name":"智慧之姆","hp":3000,"atk":800,"def":200,"money":1,"exp":45,"point":0,"special":[8],"together":20},
|
||||||
|
"E557": {"name":"智慧之神","hp":10000,"atk":2000,"def":1500,"money":10,"exp":500,"point":0,"special":[]},
|
||||||
|
"E561": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E562": {"name":"嘲讽剑客","hp":5000,"atk":1600,"def":500,"money":1,"exp":60,"point":0,"special":[19]},
|
||||||
|
"E563": {"name":"嘲讽剑圣","hp":15000,"atk":6000,"def":3000,"money":4,"exp":250,"point":0,"special":[19]},
|
||||||
|
"E564": {"name":"苍蓝剑圣","hp":30000,"atk":6000,"def":3000,"money":5,"exp":400,"point":0,"special":[29],"haloRange":3,"specialHalo":[19]},
|
||||||
|
"E566": {"name":"智慧史莱姆","hp":6000,"atk":1200,"def":600,"money":1,"exp":50,"point":0,"special":[8],"together":25},
|
||||||
|
"E567": {"name":"精明史莱姆","hp":5000,"atk":1200,"def":550,"money":0,"exp":0,"point":0,"special":[8],"together":30},
|
||||||
|
"E568": {"name":"精明史莱姆","hp":5000,"atk":1500,"def":600,"money":1,"exp":60,"point":0,"special":[8],"together":30,"displayIdInBook":null},
|
||||||
|
"E569": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E570": {"name":"粘液史莱姆","hp":3000,"atk":1000,"def":400,"money":1,"exp":45,"point":0,"special":[4,17]},
|
||||||
|
"E571": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E572": {"name":"石化史莱姆","hp":4000,"atk":1200,"def":0,"money":1,"exp":60,"point":0,"special":[3]},
|
||||||
|
"E573": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E576": {"name":"智慧守卫","hp":7500,"atk":2500,"def":1250,"money":10,"exp":500,"point":0,"special":[1],"crit":250},
|
||||||
|
"E577": {"name":"智慧守护者","hp":15000,"atk":2500,"def":1200,"money":50,"exp":2500,"point":0,"special":[1],"crit":1000},
|
||||||
|
"E578": {"name":"苍蓝守卫","hp":25000,"atk":8000,"def":4000,"money":5,"exp":600,"point":0,"special":[1],"crit":300},
|
||||||
|
"E579": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E590": {"name":"冰封史莱姆","hp":7500,"atk":1750,"def":1000,"money":2,"exp":75,"point":0,"special":[20],"ice":25},
|
||||||
|
"E591": {"name":"新敌人","hp":10000,"atk":2000,"def":1000,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E592": {"name":"冰封巨兽","hp":10000,"atk":2000,"def":1000,"money":2,"exp":85,"point":0,"special":[20,21],"iceHalo":20,"ice":50},
|
||||||
|
"E593": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E594": {"name":"苍蓝骑士","hp":20000,"atk":5000,"def":2500,"money":4,"exp":300,"point":0,"special":[29],"charge":500,"specialHalo":[11],"haloRange":3},
|
||||||
|
"E595": {"name":"寒冰兽人","hp":12500,"atk":1800,"def":800,"money":2,"exp":100,"point":0,"special":[7],"hungry":25},
|
||||||
|
"E596": {"name":"苍蓝兽人","hp":22000,"atk":6000,"def":2000,"money":4,"exp":400,"point":0,"special":[1,28],"paleShield":30,"specialHalo":[],"iceHalo":20,"haloRange":2,"value":1000,"melt":50,"together":20,"fireCore":20,"crit":500},
|
||||||
|
"E597": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E598": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E599": {"name":"魔焰骷髅","hp":40000,"atk":6000,"def":5000,"money":5,"exp":550,"point":0,"special":[29],"specialHalo":[13],"haloRange":2},
|
||||||
|
"E600": {"name":"寒冰巨石","hp":3000,"atk":2500,"def":0,"money":2,"exp":100,"point":0,"special":[3]},
|
||||||
|
"E601": {"name":"苍蓝巨石","hp":5000,"atk":6000,"def":0,"money":5,"exp":400,"point":0,"special":[3]},
|
||||||
|
"E602": {"name":"永夜蝙","hp":6000,"atk":2000,"def":800,"money":1,"exp":65,"point":0,"special":[22],"night":100},
|
||||||
|
"E603": {"name":"极昼蝠","hp":8000,"atk":1750,"def":1000,"money":1,"exp":65,"point":0,"special":[23],"day":100},
|
||||||
|
"E605": {"name":"乾坤之卫","hp":30000,"atk":7500,"def":2000,"money":4,"exp":300,"point":0,"special":[30],"translation":[2,2]},
|
||||||
|
"E606": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E607": {"name":"呐喊之魂","hp":40000,"atk":20000,"def":4000,"money":8,"exp":800,"point":0,"special":[29,30],"specialHalo":[27],"haloRange":3,"iceCore":null,"fireCore":10,"translation":[1,0]},
|
||||||
|
"E608": {"name":"寒冰护卫","hp":20000,"atk":2750,"def":2000,"money":3,"exp":225,"point":0,"special":[]},
|
||||||
|
"E609": {"name":"具形雪人","hp":7500,"atk":2250,"def":1250,"money":2,"exp":90,"point":0,"special":[25],"melt":15},
|
||||||
|
"E610": {"name":"高冷雪人","hp":10000,"atk":2500,"def":1500,"money":2,"exp":150,"point":0,"special":[25],"melt":25},
|
||||||
|
"E611": {"name":"具形雪人法师","hp":15000,"atk":2500,"def":1750,"money":2,"exp":175,"point":0,"special":[13,25],"melt":30},
|
||||||
|
"E612": {"name":"苍蓝雪人","hp":30000,"atk":7500,"def":3000,"money":5,"exp":500,"point":0,"special":[29],"specialHalo":[25],"haloRange":3,"melt":10},
|
||||||
|
"E613": {"name":"寒冰核心","hp":20000,"atk":2750,"def":1500,"money":3,"exp":150,"point":0,"special":[26],"iceCore":20},
|
||||||
|
"E614": {"name":"火焰核心","hp":15000,"atk":2750,"def":1750,"money":3,"exp":140,"point":0,"special":[27],"fireCore":20},
|
||||||
|
"E615": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E616": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E617": {"name":"冰封雕像","hp":12500,"atk":2750,"def":2000,"money":2,"exp":150,"point":0,"special":[]},
|
||||||
|
"E618": {"name":"圣殿守卫","hp":30000,"atk":12500,"def":7500,"money":10,"exp":1000,"point":0,"special":[]},
|
||||||
|
"E643": {"name":"苍蓝史莱姆","hp":17500,"atk":2800,"def":2000,"money":3,"exp":175,"point":0,"special":[28],"paleShield":30},
|
||||||
|
"E644": {"name":"苍蓝融合怪","hp":40000,"atk":7500,"def":5000,"money":8,"exp":1000,"point":0,"special":[32],"assimilateRange":2},
|
||||||
|
"E645": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E646": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E647": {"name":"苍蓝之灵-虚","hp":30000,"atk":5000,"def":3000,"money":10,"exp":2500,"point":0,"special":[28],"paleShield":10},
|
||||||
|
"E656": {"name":"触手史莱姆","hp":12500,"atk":4500,"def":2500,"money":4,"exp":200,"point":0,"special":[8],"together":25},
|
||||||
|
"E657": {"name":"触手法师","hp":50000,"atk":2000,"def":3000,"money":5,"exp":300,"point":0,"special":[8,13],"crit":null,"together":25},
|
||||||
|
"E658": {"name":"天域莱姆","hp":15000,"atk":8000,"def":7000,"money":5,"exp":500,"point":0,"special":[8],"together":10},
|
||||||
|
"E659": {"name":"圣殿莱姆","hp":50000,"atk":8000,"def":3000,"money":5,"exp":600,"point":0,"special":[8,30],"translation":[-2,-1],"together":20},
|
||||||
|
"E666": {"name":"苍蓝之灵-视","hp":150000,"atk":8000,"def":2000,"money":10,"exp":2500,"point":0,"special":[29],"n":5,"specialHalo":[4],"haloRange":5},
|
||||||
|
"E667": {"name":"苍之蓝","hp":50000,"atk":6000,"def":2000,"money":5,"exp":500,"point":0,"special":[29],"together":25,"specialHalo":[8],"haloRange":3},
|
||||||
|
"E668": {"name":"冰封护卫","hp":30000,"atk":7000,"def":2500,"money":5,"exp":550,"point":0,"special":[29],"specialHalo":[26],"iceCore":15,"haloRange":3},
|
||||||
|
"E669": {"name":"苍蓝禁卫","hp":75000,"atk":16000,"def":8000,"money":10,"exp":1000,"point":0,"special":[33],"horn":[5,1,1]},
|
||||||
|
"E670": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E671": {"name":"圣殿骑士","hp":60000,"atk":12000,"def":4000,"money":7,"exp":750,"point":0,"special":[1],"crit":300},
|
||||||
|
"E672": {"name":"坚固骑士","hp":7500,"atk":10000,"def":0,"money":8,"exp":800,"point":0,"special":[29],"specialHalo":[3],"haloRange":2},
|
||||||
|
"E673": {"name":"苍蓝之灵-域","hp":250000,"atk":12000,"def":3000,"money":10,"exp":2500,"point":0,"special":[]},
|
||||||
|
"E674": {"name":"进攻亡灵","hp":80000,"atk":14000,"def":5000,"money":7,"exp":700,"point":0,"special":[29,30],"specialHalo":[12],"haloRange":3,"translation":[0,-1]},
|
||||||
|
"E675": {"name":"双刃骷髅","hp":50000,"atk":13000,"def":6000,"money":0,"exp":0,"point":0,"special":[5]},
|
||||||
|
"E676": {"name":"冲锋骷髅","hp":50000,"atk":8000,"def":4000,"money":6,"exp":500,"point":0,"special":[12]},
|
||||||
|
"E677": {"name":"骷髅巫师","hp":150000,"atk":2000,"def":5000,"money":5,"exp":600,"point":0,"special":[13],"crit":null},
|
||||||
|
"E678": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E679": {"name":"剑盾之卫","hp":30000,"atk":10000,"def":6000,"money":7,"exp":700,"point":0,"special":[1],"crit":600},
|
||||||
|
"E680": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E681": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E682": {"name":"苍蓝聚形","hp":25000,"atk":7000,"def":4000,"money":5,"exp":400,"point":0,"special":[8],"together":20},
|
||||||
|
"E683": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E684": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E685": {"name":"守卫队长","hp":100000,"atk":14000,"def":4000,"money":10,"exp":1000,"point":0,"special":[28,29],"specialHalo":[32],"paleShield":20,"haloRange":3,"assimilateRange":2},
|
||||||
|
"E686": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E687": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E688": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E689": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E690": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E691": {"name":"游离之魂","hp":60000,"atk":12000,"def":8000,"money":0,"exp":0,"point":0,"special":[29],"specialHalo":[8],"together":10,"haloRange":3},
|
||||||
|
"E692": {"name":"苍蓝之灵-圣","hp":150000,"atk":15000,"def":5000,"money":20,"exp":5000,"point":0,"special":[4,28],"paleShield":25},
|
||||||
|
"E693": {"name":"苍蓝之灵-战","hp":250000,"atk":25000,"def":6000,"money":20,"exp":5000,"point":0,"special":[32],"assimilateRange":6},
|
||||||
|
"E698": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E699": {"name":"苍蓝朝拜者","hp":80000,"atk":12000,"def":6000,"money":9,"exp":900,"point":0,"special":[1,30],"crit":400,"translation":[0,1]},
|
||||||
|
"E700": {"name":"亡语之魂","hp":75000,"atk":10000,"def":9000,"money":8,"exp":800,"point":0,"special":[29,30],"haloRange":3,"specialHalo":[25],"melt":15,"translation":[-1,0]},
|
||||||
|
"E701": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E702": {"name":"防守之魂","hp":10000,"atk":12000,"def":4000,"money":0,"exp":0,"point":0,"special":[3,29,30],"specialHalo":[26],"iceCore":10,"translation":[1,0],"haloRange":3},
|
||||||
|
"E703": {"name":"呐喊之魂","hp":40000,"atk":16000,"def":6000,"money":8,"exp":800,"point":0,"special":[29],"specialHalo":[27],"haloRange":3,"iceCore":null,"fireCore":10,"translation":null,"displayIdInBook":null},
|
||||||
|
"E704": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E705": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E707": {"name":"宝箱之灵","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[19,34],"description":"前身似乎是...宝箱怪?不知受到了什么法术,变成了这幅鬼样子。闪亮闪亮的,让人欲罢不能,看到他就想要立刻冲过去拿下他。这里的造物主似乎喜欢开玩笑,总是在那些关键位置放置些这些东西,然后在沿途放上物资。这样,那些胜利者,或者说...正在征战四方这位无名开国者,便能够在这稍作休息,恢复精力。"},
|
||||||
|
"E708": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E709": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||||
|
"E710": {"name":"新敌人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]}
|
||||||
}
|
}
|
||||||
@ -1,36 +1,113 @@
|
|||||||
main.floors.MT0=
|
main.floors.MT0=
|
||||||
{
|
{
|
||||||
"floorId": "MT0",
|
"floorId": "MT0",
|
||||||
"title": "主塔 0 层",
|
"title": "洞穴",
|
||||||
"name": "0",
|
"name": "0",
|
||||||
"canFlyTo": true,
|
"canFlyTo": true,
|
||||||
"canFlyFrom": true,
|
"canFlyFrom": true,
|
||||||
"canUseQuickShop": true,
|
"canUseQuickShop": true,
|
||||||
"cannotViewMap": false,
|
"cannotViewMap": false,
|
||||||
"defaultGround": "ground",
|
"defaultGround": "T331",
|
||||||
"images": [],
|
"images": [],
|
||||||
"ratio": 1,
|
"ratio": 1,
|
||||||
"map": [
|
"map": [
|
||||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
[20049,20049,20049,20049,20049,20049,20050, 91,20048,20049,20049,20049,20049,20049,20043],
|
||||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
[20057,20057,20057,20057,20057,20057,20058, 0,20056,20057,20057,20057,20057,20057,20040],
|
||||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
[20065,20065,20065,20065,20065,20065,20074, 0,20064,20065,20065,20065,20065,20065,20040],
|
||||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
[141,141,141,141, 0, 0, 0, 0, 0, 0, 0,141, 33, 33,20040],
|
||||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
[141, 34, 34,141, 0,141, 0, 0, 0,141, 0,494,482,482,20040],
|
||||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
[141, 33, 33,492, 0,141, 0, 0, 0,141, 0,141, 33, 33,20040],
|
||||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
[141, 34, 34,141, 0,141, 0, 0, 0,141, 0,141,141,141,20040],
|
||||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
[141,141,141,141, 92, 0, 0,141, 0, 0, 0,141, 33, 33,20040],
|
||||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
[141, 34, 34,141, 0,141, 0, 0, 0,141, 0,494,482,482,20040],
|
||||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
[141, 33, 33,492, 0,141, 0,642, 0,141, 0,141, 33, 33,20040],
|
||||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
[141, 34, 34,141, 0,141, 45,559, 46,141, 0,141,141,141,20040],
|
||||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
[141,141,141,141, 0, 0,558, 0,560, 0, 0,141, 33, 33,20040],
|
||||||
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
[141, 33, 33,141, 0,141,367, 0,129,141, 0,494,482,482,20040],
|
||||||
|
[141, 33, 33,492, 0,141,129, 0,129,141, 0,141, 33, 33,20040],
|
||||||
|
[141,141,141,141,141,141,141,141,141,141,141,141,141,141,20040]
|
||||||
],
|
],
|
||||||
"firstArrive": [],
|
"firstArrive": [
|
||||||
|
{
|
||||||
|
"type": "setCurtain",
|
||||||
|
"color": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"time": 500,
|
||||||
|
"keep": true
|
||||||
|
},
|
||||||
|
"\t[原始人]又到了秋天,天气开始变凉了",
|
||||||
|
"秋风从石头的缝隙中穿过,原始人站在山洞中,感受着秋风的凉意,心中涌起一丝熟悉的紧迫感。",
|
||||||
|
"他知道,随着秋天的到来,山上的树木将变得干燥,而山火的风险也会随之增加。",
|
||||||
|
"他也知道,每一次暴雨过后,山洞外的河流可能会泛滥,淹没他们的狩猎场。",
|
||||||
|
"\t[原始人]柴火的消耗逐渐变多了,看来需要再上山砍柴了啊。",
|
||||||
|
"他从石头的缝隙中看去,看到满山的枫叶在秋风中摇曳,仿佛在提醒他时间的流逝。",
|
||||||
|
"这些自然的变迁,虽然无情,却也教会了他如何适应和生存。",
|
||||||
|
"\t[原始人]今天的天气似乎不错,那就上山看看吧。",
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": "function(){\nif (!core.isReplaying()) Mota.require('@motajs/legacy-ui').fixedUi.open('chapter', { chapter: '序章 起源' });\n}"
|
||||||
|
}
|
||||||
|
],
|
||||||
"parallelDo": "",
|
"parallelDo": "",
|
||||||
"events": {},
|
"events": {
|
||||||
"changeFloor": {},
|
"6,12": [
|
||||||
|
"\t[原始人]\b[up,hero]出去找些柴火"
|
||||||
|
],
|
||||||
|
"8,13": [
|
||||||
|
"本塔有很多新的功能,所有的说明都详细地写在了前方的百科全书里面,里面包含所有的功能说明,不阅读可能会影响正常的游戏体验,请仔细阅读。",
|
||||||
|
"例如你现在首先感受到的应该是状态栏的变动,你可以打开百科全书阅读状态栏相关内容。里面包含状态栏的功能说明与布局说明等。",
|
||||||
|
"注意百科全书中的内容非常基础详细,如果对魔塔有一定的了解,可以选择性地阅读。",
|
||||||
|
"打开百科全书的快捷键是H"
|
||||||
|
],
|
||||||
|
"8,12": [
|
||||||
|
"第一章计分方式:生命+5000*黄钥匙+15000*蓝钥匙"
|
||||||
|
],
|
||||||
|
"6,13": [
|
||||||
|
"原声音乐可以在网易云音乐搜索:魔塔 人类:开天辟地 bgm,部分音乐因为版权问题可能无法播放或者不在歌单内"
|
||||||
|
],
|
||||||
|
"3,7": [
|
||||||
|
"这里允许你跳转到第二章。如果你没玩过第一章,那么建议自己游玩,如果玩过却又不想再打一遍,可以由此跳转",
|
||||||
|
{
|
||||||
|
"type": "confirm",
|
||||||
|
"text": "是否跳转到第二章?",
|
||||||
|
"yes": [
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": "function(){\nMota.require('@motajs/legacy-ui').swapChapter(2, flags.hard);\n}"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"no": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"changeFloor": {
|
||||||
|
"7,0": {
|
||||||
|
"floorId": "MT1",
|
||||||
|
"loc": [
|
||||||
|
7,
|
||||||
|
14
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"afterBattle": {},
|
"afterBattle": {},
|
||||||
"afterGetItem": {},
|
"afterGetItem": {
|
||||||
|
"6,11": [
|
||||||
|
"这个可以查看bgm,也可以设置bgm,也可以清空设置的bgm"
|
||||||
|
],
|
||||||
|
"8,11": [
|
||||||
|
"请仔细阅读这个道具内的说明",
|
||||||
|
"注意,虽然内容很多,但是大部分都是“无用”信息,例如对那些ui的说明,基本上打开ui后就能看出来不同区域的功能的,百科全书的说明基本只是对一些细节进行了说明。因此一般来说是不需要非常认真地读关于ui的信息的。",
|
||||||
|
"而对于那些新的内容,例如怪物标记等,可能需要阅读一下。",
|
||||||
|
"这里说一个非常重要的一点,那就是本塔中几乎所有ui都是可以滚动的,尝试用滚轮或者手指拖动进行滚动,包括状态栏!"
|
||||||
|
],
|
||||||
|
"7,10": [
|
||||||
|
"里面包含了所有游戏的设置,请仔细查看设置"
|
||||||
|
]
|
||||||
|
},
|
||||||
"afterOpenDoor": {},
|
"afterOpenDoor": {},
|
||||||
"cannotMove": {},
|
"cannotMove": {},
|
||||||
"bgmap": [
|
"bgmap": [
|
||||||
@ -39,7 +116,16 @@ main.floors.MT0=
|
|||||||
"fgmap": [
|
"fgmap": [
|
||||||
|
|
||||||
],
|
],
|
||||||
"width": 13,
|
"width": 15,
|
||||||
"height": 13,
|
"height": 15,
|
||||||
"autoEvent": {}
|
"autoEvent": {},
|
||||||
|
"bgm": "cave.opus",
|
||||||
|
"beforeBattle": {},
|
||||||
|
"bg2map": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"fg2map": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"cannotMoveIn": {}
|
||||||
}
|
}
|
||||||
105
public/project/floors/MT1.js
Normal file
105
public/project/floors/MT1.js
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
main.floors.MT1=
|
||||||
|
{
|
||||||
|
"floorId": "MT1",
|
||||||
|
"title": "洞穴",
|
||||||
|
"name": "1",
|
||||||
|
"width": 15,
|
||||||
|
"height": 15,
|
||||||
|
"canFlyTo": true,
|
||||||
|
"canFlyFrom": true,
|
||||||
|
"canUseQuickShop": true,
|
||||||
|
"cannotViewMap": false,
|
||||||
|
"images": [],
|
||||||
|
"ratio": 1,
|
||||||
|
"defaultGround": "T331",
|
||||||
|
"bgm": "cave.opus",
|
||||||
|
"firstArrive": [
|
||||||
|
"\t[原始人]不知为何,最近这些蝙蝠的攻击性变得很强,而且还不知道从哪冒出来了这些黏糊糊的东西。",
|
||||||
|
"\t[原始人]之前捡到了一个来历不明的方块状东西(怪物手册),好像能打开,不知道里面有没有写什么",
|
||||||
|
"\t[原始人]或许会有一些东西吧,但愿是我能看懂的文字,别像之前杰克给我的东西一样,完全看不懂是什么文字。",
|
||||||
|
"\t[系统提示]游戏中每个怪物都有自己的说明,这些说明不会影响正常的剧情流程,但查看它们可以更好地了解本游戏的世界观,剧情玩家建议阅读。",
|
||||||
|
{
|
||||||
|
"type": "if",
|
||||||
|
"condition": "(flag:hard===1)",
|
||||||
|
"true": [
|
||||||
|
"游戏的基础玩法可以在百科全书中查看,你可以按H或者在道具栏打开。"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"eachArrive": [],
|
||||||
|
"parallelDo": "",
|
||||||
|
"events": {},
|
||||||
|
"changeFloor": {
|
||||||
|
"7,14": {
|
||||||
|
"floorId": "MT0",
|
||||||
|
"loc": [
|
||||||
|
7,
|
||||||
|
0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"0,4": {
|
||||||
|
"floorId": "MT2",
|
||||||
|
"loc": [
|
||||||
|
14,
|
||||||
|
4
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"11,0": {
|
||||||
|
"floorId": "MT3",
|
||||||
|
"loc": [
|
||||||
|
10,
|
||||||
|
14
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"afterBattle": {},
|
||||||
|
"afterGetItem": {},
|
||||||
|
"afterOpenDoor": {},
|
||||||
|
"autoEvent": {},
|
||||||
|
"cannotMove": {},
|
||||||
|
"map": [
|
||||||
|
[20044,20049,20049,20049,20049,20049,20049,20049,20049,20049,20050, 91,20048,20049,20043],
|
||||||
|
[20050,20057,20057,20057,20057,20057,20057,20057,20057,20057,20058, 0,20056,20057,20040],
|
||||||
|
[20058,20065,20065,20065,20065,20065,20065,20065,20065,20065,20074, 0,20064,20065,20040],
|
||||||
|
[20074, 0,141, 27, 0, 28, 0, 0,206,141,141, 32, 0, 0,20040],
|
||||||
|
[ 92, 27,141,141,141,141,141,141, 0, 31, 0,202,141,203,20040],
|
||||||
|
[20034, 0, 31, 0, 0,141, 0,141,141,141,141,141,141, 0,20040],
|
||||||
|
[20042,205,141,141,201,141, 32, 0,141, 28, 0,141, 0, 0,20040],
|
||||||
|
[20042, 0, 0,141, 0,141,141,202,141, 0, 31,202, 0, 31,20040],
|
||||||
|
[20042, 0, 31,141, 0, 0, 0, 0, 0,205, 17, 17, 17, 17,20040],
|
||||||
|
[20042,141,141,141,205,141,141,141,141, 0,203, 32, 0, 27,20040],
|
||||||
|
[20042, 0, 0,201, 0, 0,141, 0,141,202, 17, 0, 0, 0,20040],
|
||||||
|
[20042,202, 17, 17, 0, 0,205, 31, 0, 28, 17, 28, 0, 32,20040],
|
||||||
|
[20042, 0, 27, 17, 31,141,141,141,141,201, 17, 17, 17, 17,20040],
|
||||||
|
[20042, 32, 0, 17, 0,201, 0, 0,201, 31,202, 0, 32, 0,20040],
|
||||||
|
[20036,20033,20033,20033,20033,20033,20034, 93,20032,20033,20033,20033,20033,20033,20035]
|
||||||
|
],
|
||||||
|
"bgmap": [
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,141,141,141,141,141],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,141, 0, 0, 0, 0],
|
||||||
|
[ 0, 0,141,141, 0, 0, 0, 0, 0, 0,141, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0,141, 0, 0, 0, 0, 0, 0,141,141,141,141,141],
|
||||||
|
[ 0, 0, 0,141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0,141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
],
|
||||||
|
"fgmap": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"beforeBattle": {},
|
||||||
|
"cannotMoveIn": {},
|
||||||
|
"bg2map": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"fg2map": [
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
76
public/project/floors/MT10.js
Normal file
76
public/project/floors/MT10.js
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
main.floors.MT10=
|
||||||
|
{
|
||||||
|
"floorId": "MT10",
|
||||||
|
"title": "草原",
|
||||||
|
"name": "10",
|
||||||
|
"width": 15,
|
||||||
|
"height": 15,
|
||||||
|
"canFlyTo": true,
|
||||||
|
"canFlyFrom": true,
|
||||||
|
"canUseQuickShop": true,
|
||||||
|
"cannotViewMap": false,
|
||||||
|
"images": [],
|
||||||
|
"ratio": 1,
|
||||||
|
"defaultGround": "grass",
|
||||||
|
"bgm": "grass.opus",
|
||||||
|
"firstArrive": [],
|
||||||
|
"eachArrive": [],
|
||||||
|
"parallelDo": "",
|
||||||
|
"events": {},
|
||||||
|
"changeFloor": {
|
||||||
|
"0,7": {
|
||||||
|
"floorId": "MT6",
|
||||||
|
"loc": [
|
||||||
|
14,
|
||||||
|
7
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"7,0": {
|
||||||
|
"floorId": "MT11",
|
||||||
|
"loc": [
|
||||||
|
7,
|
||||||
|
14
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"afterBattle": {},
|
||||||
|
"afterGetItem": {},
|
||||||
|
"afterOpenDoor": {},
|
||||||
|
"autoEvent": {},
|
||||||
|
"cannotMove": {},
|
||||||
|
"map": [
|
||||||
|
[ 20, 20, 20, 20, 20, 20, 20, 91, 20, 20, 20, 20, 20, 20, 20],
|
||||||
|
[ 20, 0, 28,372, 29, 20, 0, 0,267, 0, 0, 20, 0, 0, 20],
|
||||||
|
[ 20, 33,381, 20, 32, 20, 33, 0, 20, 33, 29, 20, 33, 0, 20],
|
||||||
|
[ 20, 0, 27, 20, 29,368, 0, 0, 20, 0, 0,276, 0, 0, 20],
|
||||||
|
[ 20,492, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,369, 20],
|
||||||
|
[ 20, 0, 29, 20, 0, 27,372, 0, 20, 0, 0, 20, 0, 0, 20],
|
||||||
|
[ 20,204, 20, 20, 21, 34, 20, 0, 20, 29, 34, 20, 29, 33, 20],
|
||||||
|
[ 92, 0, 33, 20, 20, 20, 20,224, 20, 0, 0,209, 0, 0, 20],
|
||||||
|
[ 20, 0, 0,204, 0, 20, 0, 28, 0,204, 20, 20, 20, 20, 20],
|
||||||
|
[ 20, 20, 20, 20, 0, 20, 34, 0, 20, 0, 20, 0, 27, 0, 20],
|
||||||
|
[ 20, 32, 29, 0, 34, 20, 20, 20, 20, 0,368, 29, 33, 29, 20],
|
||||||
|
[ 20,209, 20, 20, 20, 20, 0, 0, 20, 20, 20, 20, 20, 0, 20],
|
||||||
|
[ 20, 0, 20, 33, 0,204, 29, 34, 20, 0, 33, 0, 20, 0, 20],
|
||||||
|
[ 20, 33,276, 0, 33, 20, 0, 0,209, 0, 0, 0,276, 0, 20],
|
||||||
|
[ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20]
|
||||||
|
],
|
||||||
|
"bgmap": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"fgmap": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"beforeBattle": {},
|
||||||
|
"weather": [
|
||||||
|
"sun",
|
||||||
|
8
|
||||||
|
],
|
||||||
|
"cannotMoveIn": {},
|
||||||
|
"bg2map": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"fg2map": [
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
84
public/project/floors/MT11.js
Normal file
84
public/project/floors/MT11.js
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
main.floors.MT11=
|
||||||
|
{
|
||||||
|
"floorId": "MT11",
|
||||||
|
"title": "草原",
|
||||||
|
"name": "11",
|
||||||
|
"width": 15,
|
||||||
|
"height": 15,
|
||||||
|
"canFlyTo": true,
|
||||||
|
"canFlyFrom": true,
|
||||||
|
"canUseQuickShop": true,
|
||||||
|
"cannotViewMap": false,
|
||||||
|
"images": [],
|
||||||
|
"ratio": 1,
|
||||||
|
"defaultGround": "grass",
|
||||||
|
"bgm": "grass.opus",
|
||||||
|
"firstArrive": [],
|
||||||
|
"eachArrive": [],
|
||||||
|
"parallelDo": "",
|
||||||
|
"events": {
|
||||||
|
"7,11": [
|
||||||
|
"设定说明之额外伤害",
|
||||||
|
"这是一种无法被减免的伤害,在破防的基础上,会在计算过每回合造成伤害后将额外伤害加在每回合伤害上",
|
||||||
|
"例如,你对A怪物每回合造成10点伤害,你有10点额外伤害,那么这时候你每回合就可以对它造成20点伤害",
|
||||||
|
"该属性对坚固怪同样有效",
|
||||||
|
"更多详细信息可在百科全书中查看"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"changeFloor": {
|
||||||
|
"7,14": {
|
||||||
|
"floorId": "MT10",
|
||||||
|
"loc": [
|
||||||
|
7,
|
||||||
|
0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"7,0": {
|
||||||
|
"floorId": "MT12",
|
||||||
|
"loc": [
|
||||||
|
7,
|
||||||
|
14
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"afterBattle": {},
|
||||||
|
"afterGetItem": {},
|
||||||
|
"afterOpenDoor": {},
|
||||||
|
"autoEvent": {},
|
||||||
|
"cannotMove": {},
|
||||||
|
"map": [
|
||||||
|
[ 20, 20, 20, 20, 20, 20, 20, 91, 20, 20, 20, 20, 20, 20, 20],
|
||||||
|
[ 20, 33, 0,369, 0, 0, 20, 33, 0,373, 0, 20, 29, 0, 20],
|
||||||
|
[ 20, 0,381, 20, 0, 0, 20, 0, 0, 20, 28, 20, 0, 33, 20],
|
||||||
|
[ 20, 20, 20, 20,368, 20, 20, 20, 20, 20, 0, 20, 20, 0, 20],
|
||||||
|
[ 20, 0, 28,369, 33, 0,369, 27, 0, 20, 33,369, 0, 28, 20],
|
||||||
|
[ 20, 33, 0, 20, 0, 0, 20, 0, 27,368, 0, 20, 20, 20, 20],
|
||||||
|
[ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,276, 20, 0, 29, 20],
|
||||||
|
[ 20, 34, 0, 20, 0, 32, 20, 0, 27, 20, 0,372, 29, 0, 20],
|
||||||
|
[ 20, 0, 29,368, 32, 29, 20, 33, 0,369, 32, 20, 20, 20, 20],
|
||||||
|
[ 20, 20, 20, 20, 20,372, 20, 20, 20, 20, 0,372, 0, 34, 20],
|
||||||
|
[ 20, 34, 32, 20, 34, 0, 20, 0, 32, 28,224, 20, 29, 0, 20],
|
||||||
|
[ 20, 29, 29,370,372, 20, 20,129, 20, 20, 0, 20, 0, 34, 20],
|
||||||
|
[ 20, 29, 29, 20, 0, 27,368, 33, 33, 20, 32, 20, 20, 20, 20],
|
||||||
|
[ 20, 32, 34, 20, 29, 0, 20, 33, 33,368, 0,276, 0, 34, 20],
|
||||||
|
[ 20, 20, 20, 20, 20, 20, 20, 93, 20, 20, 20, 20, 20, 20, 20]
|
||||||
|
],
|
||||||
|
"bgmap": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"fgmap": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"beforeBattle": {},
|
||||||
|
"weather": [
|
||||||
|
"sun",
|
||||||
|
8
|
||||||
|
],
|
||||||
|
"cannotMoveIn": {},
|
||||||
|
"bg2map": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"fg2map": [
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
156
public/project/floors/MT12.js
Normal file
156
public/project/floors/MT12.js
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
main.floors.MT12=
|
||||||
|
{
|
||||||
|
"floorId": "MT12",
|
||||||
|
"title": "草原",
|
||||||
|
"name": "12",
|
||||||
|
"width": 15,
|
||||||
|
"height": 15,
|
||||||
|
"canFlyTo": true,
|
||||||
|
"canFlyFrom": true,
|
||||||
|
"canUseQuickShop": true,
|
||||||
|
"cannotViewMap": false,
|
||||||
|
"images": [],
|
||||||
|
"ratio": 1,
|
||||||
|
"defaultGround": "grass",
|
||||||
|
"bgm": "grass.opus",
|
||||||
|
"firstArrive": [],
|
||||||
|
"eachArrive": [],
|
||||||
|
"parallelDo": "",
|
||||||
|
"events": {
|
||||||
|
"7,1": [
|
||||||
|
"\t[原始人]\b[down,hero]杰克?你怎么在这?",
|
||||||
|
"\t[杰克,thief]\b[down,7,1]我刚刚出山洞,就发现了一堆怪物。",
|
||||||
|
"\t[杰克,thief]\b[down,7,1]那些怪物冲着我就跑过来,我根本打不过他们。",
|
||||||
|
"\t[杰克,thief]\b[down,7,1]我就跑到了这里。",
|
||||||
|
"\t[杰克,thief]\b[down,7,1]多谢你救了我。",
|
||||||
|
"\t[原始人]\b[down,hero]不用多谢了,咱都是兄弟的。",
|
||||||
|
"\t[杰克,thief]\b[down,7,1]你来这里是要干什么?",
|
||||||
|
"\t[原始人]\b[down,hero](他好像没有注意到绿色结晶,就不要提这件事了吧)",
|
||||||
|
"\t[原始人]\b[down,hero]我要上山砍一些柴火。",
|
||||||
|
"\t[原始人]\b[down,hero]但是上山的路被强大的怪物堵死了,我根本上不去。",
|
||||||
|
"\t[杰克,thief]\b[down,7,1]这样啊。",
|
||||||
|
"\t[杰克,thief]\b[down,7,1]正好,我最近学会了一招,能让你避开那些怪物,我来教给你吧!",
|
||||||
|
"获得技能:跳跃\n快捷键2,消耗200点生命值,困难消耗400点,一个地图只能使用3次\n如果前方为可通行的地面,则不能使用该技能\n如果前方为怪物,则将怪物移至勇士视线上第一个不能通行的方块后\n如果前方为障碍物,则直接跳到该障碍物的后方",
|
||||||
|
"\t[原始人]\b[down,hero]多谢杰克兄弟了啊。",
|
||||||
|
"\t[杰克,thief]\b[down,7,1]不过还是要小心山上的那些怪物,那些怪物都很强。",
|
||||||
|
"\t[原始人]\b[down,hero]我明白,但是我必须上山,不然都要饿死冻死了。",
|
||||||
|
"\t[杰克,thief]\b[down,7,1]那我就不打扰你了,咱们之后再见。",
|
||||||
|
"去南方那个之前过不去的地方推进游戏剧情",
|
||||||
|
"手机端可以点击右下角的难度来切换下方工具栏至数字键",
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": "function(){\nconst HeroSkill = Mota.require('@user/data-state').HeroSkill;\nHeroSkill.learnSkill(HeroSkill.Jump);\n}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "hide",
|
||||||
|
"remove": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"7,9": [
|
||||||
|
"把四个骷髅卫兵杀死开启机关门"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"changeFloor": {
|
||||||
|
"7,14": {
|
||||||
|
"floorId": "MT11",
|
||||||
|
"loc": [
|
||||||
|
7,
|
||||||
|
0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"afterBattle": {
|
||||||
|
"1,1": [
|
||||||
|
{
|
||||||
|
"type": "setValue",
|
||||||
|
"name": "flag:door_MT12_7_3",
|
||||||
|
"operator": "+=",
|
||||||
|
"value": "1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"4,2": [
|
||||||
|
{
|
||||||
|
"type": "setValue",
|
||||||
|
"name": "flag:door_MT12_7_3",
|
||||||
|
"operator": "+=",
|
||||||
|
"value": "1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"10,2": [
|
||||||
|
{
|
||||||
|
"type": "setValue",
|
||||||
|
"name": "flag:door_MT12_7_3",
|
||||||
|
"operator": "+=",
|
||||||
|
"value": "1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"13,1": [
|
||||||
|
{
|
||||||
|
"type": "setValue",
|
||||||
|
"name": "flag:door_MT12_7_3",
|
||||||
|
"operator": "+=",
|
||||||
|
"value": "1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"afterGetItem": {},
|
||||||
|
"afterOpenDoor": {},
|
||||||
|
"autoEvent": {
|
||||||
|
"7,3": {
|
||||||
|
"0": {
|
||||||
|
"condition": "flag:door_MT12_7_3==4",
|
||||||
|
"currentFloor": true,
|
||||||
|
"priority": 0,
|
||||||
|
"delayExecute": false,
|
||||||
|
"multiExecute": false,
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"type": "openDoor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "setValue",
|
||||||
|
"name": "flag:door_MT12_7_3",
|
||||||
|
"operator": "=",
|
||||||
|
"value": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cannotMove": {},
|
||||||
|
"map": [
|
||||||
|
[ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20],
|
||||||
|
[ 20,210, 0, 29, 33, 20,381,123,381, 20, 33, 29, 0,210, 20],
|
||||||
|
[ 20, 0, 20, 20,210, 20, 33,381, 33, 20,210, 20, 20, 0, 20],
|
||||||
|
[ 20, 33, 20, 20, 20, 20, 20, 85, 20, 20, 20, 20, 20, 33, 20],
|
||||||
|
[ 20, 27, 29, 0,369, 20, 0,373, 0, 20,369, 0, 29, 27, 20],
|
||||||
|
[ 20, 20, 20, 20, 0, 20, 34, 20, 34, 20, 0, 20, 20, 20, 20],
|
||||||
|
[ 20, 29, 0, 34,209, 0, 28, 20, 28, 0,209, 34, 0, 29, 20],
|
||||||
|
[ 20, 20, 20,368, 20, 20, 20, 20, 20, 20, 20,368, 20, 20, 20],
|
||||||
|
[ 20, 0,372, 0, 0, 20, 29, 20, 29, 20, 0, 0,372, 0, 20],
|
||||||
|
[ 20, 0, 20, 0, 20, 20, 0,129, 0, 20, 20, 0, 20, 0, 20],
|
||||||
|
[ 20, 34, 20, 27, 34, 0,248, 0,248, 0, 34, 27, 20, 34, 20],
|
||||||
|
[ 20, 28, 20, 20, 20, 20, 20,248, 20, 20, 20, 20, 20, 28, 20],
|
||||||
|
[ 20, 0, 20, 0, 34, 0,224, 0,224, 0, 34, 0, 20, 0, 20],
|
||||||
|
[ 20, 0,276, 0, 29, 0, 20, 0, 20, 0, 29, 0,276, 0, 20],
|
||||||
|
[ 20, 20, 20, 20, 20, 20, 20, 93, 20, 20, 20, 20, 20, 20, 20]
|
||||||
|
],
|
||||||
|
"bgmap": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"fgmap": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"beforeBattle": {},
|
||||||
|
"weather": [
|
||||||
|
"sun",
|
||||||
|
8
|
||||||
|
],
|
||||||
|
"cannotMoveIn": {},
|
||||||
|
"bg2map": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"fg2map": [
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
80
public/project/floors/MT13.js
Normal file
80
public/project/floors/MT13.js
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
main.floors.MT13=
|
||||||
|
{
|
||||||
|
"floorId": "MT13",
|
||||||
|
"title": "山脚",
|
||||||
|
"name": "13",
|
||||||
|
"width": 15,
|
||||||
|
"height": 15,
|
||||||
|
"canFlyTo": true,
|
||||||
|
"canFlyFrom": true,
|
||||||
|
"canUseQuickShop": true,
|
||||||
|
"cannotViewMap": false,
|
||||||
|
"images": [],
|
||||||
|
"ratio": 1,
|
||||||
|
"defaultGround": "grass",
|
||||||
|
"bgm": "mount.opus",
|
||||||
|
"firstArrive": [],
|
||||||
|
"eachArrive": [],
|
||||||
|
"parallelDo": "",
|
||||||
|
"events": {},
|
||||||
|
"changeFloor": {
|
||||||
|
"7,14": {
|
||||||
|
"floorId": "MT14",
|
||||||
|
"loc": [
|
||||||
|
7,
|
||||||
|
0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"7,0": {
|
||||||
|
"floorId": "MT7",
|
||||||
|
"loc": [
|
||||||
|
7,
|
||||||
|
14
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"afterBattle": {},
|
||||||
|
"afterGetItem": {},
|
||||||
|
"afterOpenDoor": {},
|
||||||
|
"autoEvent": {
|
||||||
|
"7,14": {
|
||||||
|
"2": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cannotMove": {},
|
||||||
|
"map": [
|
||||||
|
[152,152,152,152,152,152,152, 91,152,152,152,152,152,152,152],
|
||||||
|
[152, 27,403,152, 0,152, 34, 0, 34,152, 0, 33,255, 32,152],
|
||||||
|
[152,482, 28,152, 29,152, 34,381, 34,152, 27, 0,152, 29,152],
|
||||||
|
[152,498,152,152, 34,152,152,210,152,152,152,152,152, 28,152],
|
||||||
|
[152, 0, 29, 34,370, 0, 27, 0, 0, 34, 29,152, 32, 32,152],
|
||||||
|
[152,152,152,152,152,152,152, 0,152,152, 0,152,152,210,152],
|
||||||
|
[152, 27, 33,152, 0, 0, 34,271, 0, 34,370, 0, 0, 0,152],
|
||||||
|
[152, 33,381,152,152,152, 0,152,381, 0,152,255,152,152,152],
|
||||||
|
[152,152,211, 34, 28,152, 0,152,152,152,152, 29, 0, 29,152],
|
||||||
|
[152,381, 0,152, 34,152,374, 0,492,482,492, 34, 28, 34,152],
|
||||||
|
[152,152,152,152,370,152, 34,152, 33,152,492,152,152,152,152],
|
||||||
|
[152, 0, 29, 34, 0,152, 27, 0,210,152, 27, 27,482,403,152],
|
||||||
|
[152,152,152,374,152,152,152,152, 0,152,152,152,498,152,152],
|
||||||
|
[152, 34, 0, 0,210, 0, 0, 0,271, 0, 34,381, 0, 34,152],
|
||||||
|
[152,152,152,152,152,152,152, 93,152,152,152,152,152,152,152]
|
||||||
|
],
|
||||||
|
"bgmap": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"fgmap": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"beforeBattle": {},
|
||||||
|
"weather": [
|
||||||
|
"sun",
|
||||||
|
8
|
||||||
|
],
|
||||||
|
"cannotMoveIn": {},
|
||||||
|
"bg2map": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"fg2map": [
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
439
public/project/floors/MT14.js
Normal file
439
public/project/floors/MT14.js
Normal file
@ -0,0 +1,439 @@
|
|||||||
|
main.floors.MT14=
|
||||||
|
{
|
||||||
|
"floorId": "MT14",
|
||||||
|
"title": "山路",
|
||||||
|
"name": "14",
|
||||||
|
"width": 128,
|
||||||
|
"height": 15,
|
||||||
|
"canFlyTo": true,
|
||||||
|
"canFlyFrom": true,
|
||||||
|
"canUseQuickShop": true,
|
||||||
|
"cannotViewMap": false,
|
||||||
|
"images": [],
|
||||||
|
"ratio": 1,
|
||||||
|
"defaultGround": "T331",
|
||||||
|
"bgm": "mount.opus",
|
||||||
|
"firstArrive": null,
|
||||||
|
"eachArrive": [
|
||||||
|
{
|
||||||
|
"type": "loadBgm",
|
||||||
|
"name": "plot1.opus"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parallelDo": "",
|
||||||
|
"events": {
|
||||||
|
"24,7": {
|
||||||
|
"trigger": "action",
|
||||||
|
"enable": true,
|
||||||
|
"noPass": null,
|
||||||
|
"displayDamage": true,
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"type": "if",
|
||||||
|
"condition": "(switch:A===true)",
|
||||||
|
"true": [
|
||||||
|
"\t[原始人]\b[up,hero]看来是有什么机关"
|
||||||
|
],
|
||||||
|
"false": [
|
||||||
|
"\t[原始人]\b[up,hero]这里有个门?",
|
||||||
|
"\t[原始人]\b[up,hero]奇怪,之前都没有的",
|
||||||
|
"\t[原始人]\b[up,hero]暴力破坏试试",
|
||||||
|
{
|
||||||
|
"type": "animate",
|
||||||
|
"name": "hand",
|
||||||
|
"loc": [
|
||||||
|
24,
|
||||||
|
7
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "animate",
|
||||||
|
"name": "hand",
|
||||||
|
"loc": [
|
||||||
|
24,
|
||||||
|
7
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "animate",
|
||||||
|
"name": "hand",
|
||||||
|
"loc": [
|
||||||
|
24,
|
||||||
|
7
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"\t[原始人]\b[up,hero]不行,连一点痕迹都没有",
|
||||||
|
"\t[原始人]\b[up,hero]看来是有什么机关",
|
||||||
|
{
|
||||||
|
"type": "setValue",
|
||||||
|
"name": "switch:A",
|
||||||
|
"value": "true"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"78,5": [
|
||||||
|
"别给我说这个地方不知道怎么过,用2技能就好了",
|
||||||
|
{
|
||||||
|
"type": "setText",
|
||||||
|
"position": "down"
|
||||||
|
},
|
||||||
|
"注意,2技能的要求是前方是不可通行的图块,由于这里的左面是可以通行的地面,所以只能在这里使用",
|
||||||
|
{
|
||||||
|
"type": "setText",
|
||||||
|
"position": "center"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"7,1": {
|
||||||
|
"trigger": null,
|
||||||
|
"enable": false,
|
||||||
|
"noPass": null,
|
||||||
|
"displayDamage": true,
|
||||||
|
"opacity": 1,
|
||||||
|
"filter": {
|
||||||
|
"blur": 0,
|
||||||
|
"hue": 0,
|
||||||
|
"grayscale": 0,
|
||||||
|
"invert": false,
|
||||||
|
"shadow": 0
|
||||||
|
},
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"type": "if",
|
||||||
|
"condition": "flag:finishChase1",
|
||||||
|
"true": [
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": "function(){\ndelete flags.__bgm__;\ndelete flags.MT14Jump;\ndelete flags['MT14@24@7@A'];\ndelete flags.finishChase1;\ndelete flags.cave;\n}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "confirm",
|
||||||
|
"text": "是否跳过剧情",
|
||||||
|
"yes": [
|
||||||
|
{
|
||||||
|
"type": "changeFloor",
|
||||||
|
"floorId": "MT17",
|
||||||
|
"loc": [
|
||||||
|
0,
|
||||||
|
7
|
||||||
|
],
|
||||||
|
"direction": "right"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"no": [
|
||||||
|
{
|
||||||
|
"type": "pauseBgm"
|
||||||
|
},
|
||||||
|
"\t[野蛮人,hero]\b[down,hero]呼!",
|
||||||
|
"\t[野蛮人,hero]\b[down,hero]总算逃出来了。",
|
||||||
|
"\t[野蛮人,hero]\b[down,hero]没想到柴火没砍成,还碰到了狼。",
|
||||||
|
"\t[野蛮人,hero]\b[down,hero]真是倒了血霉了。",
|
||||||
|
"\t[野蛮人,hero]\b[down,hero]算了,明天再砍柴吧。",
|
||||||
|
{
|
||||||
|
"type": "setCurtain",
|
||||||
|
"color": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"time": 1500,
|
||||||
|
"keep": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "setText",
|
||||||
|
"text": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"background": "winskin3.png"
|
||||||
|
},
|
||||||
|
"人类简史——起源篇",
|
||||||
|
{
|
||||||
|
"type": "playSound",
|
||||||
|
"name": "paper.opus"
|
||||||
|
},
|
||||||
|
"但他已经抑制不了自己的好奇心。",
|
||||||
|
{
|
||||||
|
"type": "playSound",
|
||||||
|
"name": "paper.opus"
|
||||||
|
},
|
||||||
|
"人类嘛,总会在好奇心的驱使下前进。",
|
||||||
|
{
|
||||||
|
"type": "playSound",
|
||||||
|
"name": "paper.opus"
|
||||||
|
},
|
||||||
|
"但是,他们却用好奇心给自己带来了灾难,",
|
||||||
|
{
|
||||||
|
"type": "playSound",
|
||||||
|
"name": "paper.opus"
|
||||||
|
},
|
||||||
|
"只得利用历史中的野蛮人的好奇心来拯救自己。",
|
||||||
|
{
|
||||||
|
"type": "playSound",
|
||||||
|
"name": "paper.opus"
|
||||||
|
},
|
||||||
|
"不出所料,这个野蛮人走上了勇气之路。",
|
||||||
|
{
|
||||||
|
"type": "playSound",
|
||||||
|
"name": "paper.opus"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "setText",
|
||||||
|
"text": [
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
255,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"background": "winskin2.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "changeFloor",
|
||||||
|
"floorId": "MT17",
|
||||||
|
"loc": [
|
||||||
|
0,
|
||||||
|
7
|
||||||
|
],
|
||||||
|
"direction": "right"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"21,7": [
|
||||||
|
{
|
||||||
|
"type": "forbidSave"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"127,1": [
|
||||||
|
"你是怎么办到的?!",
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": "function(){\nif (!core.isReplaying()) {\n\tcore.addMountSign(2);\n}\n}"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"26,14": [
|
||||||
|
"不作死就不会死",
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": "function(){\nif (!core.isReplaying()) {\n\tcore.addMountSign(1);\n}\n}"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"8,2": [
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"text": "这一层有一个必须使用跳跃的地方,需要1个跳跃次数,你一共有3个跳跃次数,请规划好"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"text": "不要吐槽为什么天气变化这么大("
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"80,3": [
|
||||||
|
"你可以使用楼传传出去"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"changeFloor": {
|
||||||
|
"7,0": {
|
||||||
|
"floorId": "MT13",
|
||||||
|
"loc": [
|
||||||
|
7,
|
||||||
|
14
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"127,7": {
|
||||||
|
"floorId": "MT15",
|
||||||
|
"loc": [
|
||||||
|
0,
|
||||||
|
7
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"afterBattle": {
|
||||||
|
"21,9": [
|
||||||
|
{
|
||||||
|
"type": "setValue",
|
||||||
|
"name": "flag:door_MT14_24_7",
|
||||||
|
"operator": "+=",
|
||||||
|
"value": "1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"21,10": [
|
||||||
|
{
|
||||||
|
"type": "setValue",
|
||||||
|
"name": "flag:door_MT14_24_7",
|
||||||
|
"operator": "+=",
|
||||||
|
"value": "1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"22,10": [
|
||||||
|
{
|
||||||
|
"type": "setValue",
|
||||||
|
"name": "flag:door_MT14_24_7",
|
||||||
|
"operator": "+=",
|
||||||
|
"value": "1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"22,9": [
|
||||||
|
{
|
||||||
|
"type": "setValue",
|
||||||
|
"name": "flag:door_MT14_24_7",
|
||||||
|
"operator": "+=",
|
||||||
|
"value": "1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"91,3": [
|
||||||
|
{
|
||||||
|
"type": "setValue",
|
||||||
|
"name": "flag:door_MT14_95_3",
|
||||||
|
"operator": "+=",
|
||||||
|
"value": "1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"93,3": [
|
||||||
|
{
|
||||||
|
"type": "setValue",
|
||||||
|
"name": "flag:door_MT14_95_3",
|
||||||
|
"operator": "+=",
|
||||||
|
"value": "1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"93,5": [
|
||||||
|
{
|
||||||
|
"type": "setValue",
|
||||||
|
"name": "flag:door_MT14_95_3",
|
||||||
|
"operator": "+=",
|
||||||
|
"value": "1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"91,5": [
|
||||||
|
{
|
||||||
|
"type": "setValue",
|
||||||
|
"name": "flag:door_MT14_95_3",
|
||||||
|
"operator": "+=",
|
||||||
|
"value": "1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"afterGetItem": {
|
||||||
|
"26,7": [
|
||||||
|
"\t[原始人]\b[up,hero]下面是一段很长的山路,要集中精力,不要被埋伏了",
|
||||||
|
"游戏特性:使用跳跃技能时,悬崖视为不可通行但不可对着它使用跳跃"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"afterOpenDoor": {},
|
||||||
|
"autoEvent": {
|
||||||
|
"24,7": {
|
||||||
|
"0": {
|
||||||
|
"condition": "flag:door_MT14_24_7==4",
|
||||||
|
"currentFloor": true,
|
||||||
|
"priority": 0,
|
||||||
|
"delayExecute": false,
|
||||||
|
"multiExecute": false,
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"type": "openDoor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "setValue",
|
||||||
|
"name": "flag:door_MT14_24_7",
|
||||||
|
"value": "null"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "sleep",
|
||||||
|
"time": 500
|
||||||
|
},
|
||||||
|
"\t[原始人]\b[up,hero]原来是这样吗"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"1": null
|
||||||
|
},
|
||||||
|
"95,3": {
|
||||||
|
"0": {
|
||||||
|
"condition": "flag:door_MT14_95_3==4",
|
||||||
|
"currentFloor": true,
|
||||||
|
"priority": 0,
|
||||||
|
"delayExecute": false,
|
||||||
|
"multiExecute": false,
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"type": "openDoor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "setValue",
|
||||||
|
"name": "flag:door_MT14_95_3",
|
||||||
|
"operator": "=",
|
||||||
|
"value": "null"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"7,1": {
|
||||||
|
"0": null,
|
||||||
|
"1": null,
|
||||||
|
"2": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cannotMove": {},
|
||||||
|
"map": [
|
||||||
|
[152,152,152,152,152,152,152, 91,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,20053, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20075],
|
||||||
|
[152,376,482,152,482, 27,152, 0, 0,236, 0, 0, 33,152,482,378, 21,381, 0, 27,152, 34, 29,152,152,20056, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,129],
|
||||||
|
[152, 0, 28,152,381, 0,235, 21,129,152,502,152,381,492,494,152,152,152,152,482,152, 29, 34,152,152,20064,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065, 0],
|
||||||
|
[152,152, 0,152,492,152,152, 0,152, 29, 33,152, 0,152,376,482, 21,378,152, 0,498, 34, 29,152,152, 27,336, 33, 0, 28, 0,345, 33,381, 0,235,141, 27,492,376,378,403,484, 0, 22,141, 21,336, 27, 28,381, 0, 34,502, 34,141,141,141,376,403,403, 0,254,333, 0, 0, 32,336, 32,333,376, 0,482,403,482,503, 0, 34,378,336,129, 34,376, 0,336, 28,492,376,378, 21,403,503, 33,503,482, 85, 0,378,375,20007,482, 0,375,494, 0,207, 0,141, 0,499, 0,141, 22, 27,345,492, 0,376,345,403, 21, 34,378,336,376,381,376,20053],
|
||||||
|
[152,482,498, 0, 27, 0,152,482,152, 0,152,152, 0,152,482,152,371,403,152,152,152,492,254,152,152,482,498, 0,345,345, 0,345, 0,336,336, 0,381, 32,141,141,492,141,141,141,371,141, 27,336,141,141,498,141,502,333,502, 0,403,492, 34,141,333,333, 0, 32, 32, 27, 21,502, 0,333,141,141,492,141,503,141,141,141, 34,336,336, 33,378, 0,336, 28,345,222,141,141,141, 33,336, 33,390,396, 0,482, 0,492, 0,20007, 33,20007, 34,20007, 27,141,482,141, 0,141, 28, 33,345, 0,336, 34,345, 0,340,376, 0,336, 32,378, 32,20056],
|
||||||
|
[152,492,152,482, 0,152,152,152,152,152,152, 0,502,152,152,152, 0,152,152, 0,482, 29, 0,152,152,381,336, 27,492, 27,272, 28,498, 33,336,502,141,141,141,235, 0, 34,403,492, 0,254, 33,336, 0, 27, 0,492, 34,502, 34,141, 0,371, 0,141, 0,378,211,333, 0,503,336,336,502, 0, 28, 32,498,141, 0,376,482,498,129, 0,375, 34,403, 0,336, 28,345, 0, 33,141,141,503, 33,503,403,403, 0,336,378,482,499,20007,378,20007,381,20007,381,378, 0,141, 34,141,345,381, 0,499,336, 0,345, 32,340,492,222,336,492,336,222, 0],
|
||||||
|
[152,482,152,152,235,152, 29, 34,152, 33, 0, 21,152,152,272, 0,502,482,502, 28, 0,235,152,152,152,336,336, 0,345,345, 33,345,336, 28,336, 0, 0,502, 0, 32,141,141, 0,141, 33,141, 0,381,211, 33,141,141,502,333,502,141,141,141,141,141,336,482,336,336,371,482,376,492, 33,333,502,141,381,141,375,141,141, 0,345, 0, 0, 0, 0, 0, 0,375, 0,381,371,141,141,141,141, 21,336,403,371, 0,20007,492,20007,20007, 33,20007,378,20007,141,141,141,141,378,141,345,345,492, 0, 27,503,345,345,340, 0, 0,336, 0,336, 34,336],
|
||||||
|
[152, 0, 28, 21, 0,492, 34,502, 0, 0, 27,212, 0, 0, 33, 29,152,152,152,492,152, 0, 33, 0, 85, 0,487,254, 0,340,381,340,502, 0, 0,381,333,141,141,503,141,141,498, 0, 28,141,498,492,336, 0, 28,381, 34,502, 34,141,381, 28,376, 0,503,381,492,403,403,381,482,336, 27,333, 0, 32, 0,141, 0, 32,141, 27, 33,371,345, 0,333,336,336,482,336,336, 33, 27,378,381, 33,207,403,482,340,351, 34,381,378, 34,503,20007,381,20007, 0, 0, 34,376,272,381, 33, 0, 0,371,492, 34,403, 0, 0,492, 0,381, 32,336,403, 94],
|
||||||
|
[152,152,152, 0,152,152,152,152,502,492,152,152,152,152, 0, 0,152, 0, 29, 33,152,152,152,152,152,336,336,340, 0,340,502, 0, 27,336,336,492,333,376, 0,403,336,141, 27,345,235, 0, 33, 27,336,141,141,141,494,492, 21,492, 0,141,141,141,482,336,336,403,403, 0, 0, 0, 0, 0, 0, 0,272, 0,381, 32,141,336,336,403,345, 0,333,381, 0,503, 34,403, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,371,20007,20007,20007,20007,20007, 34,20007,141,141,141,141,345,345,503,345,345, 0, 28,503,336,336, 34, 0,375,336, 0,207, 34,336],
|
||||||
|
[152,381, 0,221,152, 0, 0, 33, 0,212,152,381, 34,492,236,492,152, 0,152, 0,152,498,498,152,152,381,336, 0,502, 33, 29,340,502, 0,333,378,403,503,141, 34,336,381, 0,336,141,141,141, 0,336,482,482,141, 33,336,403, 0,371, 0,366,366,381,498, 0,492,345, 0, 0,498,336, 0, 0, 0, 0,141,141,141,141, 27, 28,484,345,498, 0, 33, 27,345,371, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 33,381,378,381, 0,503,492, 34,376, 0,141, 33,492,376, 0,503, 33,336,378, 34,499, 0,336, 0,336,371,336,492,20037],
|
||||||
|
[152,152,152, 0,152,152,152,152,152, 0,152, 34,381,152, 0,482, 0,212,152, 27,152,498,498,152,152,482,211, 0,340,340,340,340, 0,333,333, 33, 17, 28,141,378,492,492,336,336, 34, 28,211, 0,336,492,141,141,403,333,141,141,366, 0,378,494,376,336, 0, 32,345, 0,211, 0, 0,340,376,403, 27,336, 33,403,336,336,336,336,336, 0,336,336,345,345,482,492,376,378,336, 0,482,376, 0,207,333,333,494,336,336,336,336,336,336,336, 0,141,141,141, 0,375, 21, 33,492, 0,336,336,336,494,336, 0, 27,336, 28,381, 28,20045],
|
||||||
|
[152, 0,482, 27,492,381, 27,482,152, 33,492,492,152,152,152,152,152, 21,152, 0,152,152,492,152,152, 27,336,371,403, 33,376,492,381, 28, 0,371, 17, 34, 27,375,376,378,403, 33,336, 0,336,254, 32, 28,207,376,378,333,482,482,492,482, 0,375, 0,336, 32, 0,235, 0,336, 0, 34,371,482,378, 0, 0,211, 0,492,376,378,403, 33,207, 33, 0, 0,345,403,403,403,403, 33,222,336,403,333,482,376,376,376,503,503,503, 0,403,403, 34,503, 34, 0,381,376,345,378, 0,333,511,390,396,403,484,336, 28, 32,336, 32, 27, 32,20045],
|
||||||
|
[152,152,211,152,152,152,152,254,152, 0,152,482, 27,152, 0,482,502, 0,152, 0,152,482, 0,152,152,20037,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20067],
|
||||||
|
[152, 0, 0, 0, 29, 33, 0, 0, 0,374,152, 29,482,211, 0, 28,152,272, 0,211, 0, 22, 27,152,152,20045, 0,336, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17],
|
||||||
|
[152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,20045,129,336, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17]
|
||||||
|
],
|
||||||
|
"bgmap": [
|
||||||
|
[305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305, 0, 0, 0, 0,20053,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20075],
|
||||||
|
[305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305, 0, 0, 0,20056,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20045],
|
||||||
|
[305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305, 0, 0, 0,20064,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20073,20045],
|
||||||
|
[305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20037,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20039, 0, 0, 0, 0,20037,20038,20038,20038,20038,20038,20038,20038,20038,20038,20039, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20053],
|
||||||
|
[305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20045, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20047, 0, 0, 0, 0,20045, 0, 0, 0, 0, 0, 0, 0, 0, 0,20047, 0, 0,20045,20047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20056],
|
||||||
|
[305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20045, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20045, 0, 0, 0, 0, 0, 0, 0, 0, 0,20047, 0, 0,20045,20047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20064],
|
||||||
|
[305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20045, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20068,20038,20038,20038,20038,20067, 0, 0, 0, 0, 0, 0, 0, 0, 0,20068,20038,20038,20067,20047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20053,20054, 0, 0,20054,20054,20054,20075, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20076,20054,20054,20054,20054,20054,20054,20054,20054,20054,20055, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20056,20057,20152,20153,20057,20057,20057,20045, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20047,20057,20057,20057,20057,20057,20057,20057,20057,20057,20058, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20064,20065,20152,20153,20065,20065,20065,20045, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20047,20065,20065,20065,20065,20065,20065,20065,20065,20065,20074, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20037],
|
||||||
|
[305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20045, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20045],
|
||||||
|
[305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20053,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20055, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20045],
|
||||||
|
[305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305, 0, 0,20037,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20067],
|
||||||
|
[305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305, 0, 0, 0,20045, 0,20000,20000,20000,20000,20000,20000,20000,20000,20000,20000,20000,20000,20000,20000,20000,20000,20000,20000,20000,20000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305, 0, 0,20045,20000,20000,20000,20000,20000,20000,20000,20000,20000,20000,20000,20000,20000,20000,20000,20000,20000,20000,20000,20000,20000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
],
|
||||||
|
"fgmap": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"color": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0.3
|
||||||
|
],
|
||||||
|
"weather": [
|
||||||
|
"cloud",
|
||||||
|
5
|
||||||
|
],
|
||||||
|
"beforeBattle": {},
|
||||||
|
"cannotMoveIn": {},
|
||||||
|
"bg2map": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"fg2map": [
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
145
public/project/floors/MT15.js
Normal file
145
public/project/floors/MT15.js
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
main.floors.MT15=
|
||||||
|
{
|
||||||
|
"floorId": "MT15",
|
||||||
|
"title": "山路",
|
||||||
|
"name": "15",
|
||||||
|
"width": 64,
|
||||||
|
"height": 15,
|
||||||
|
"canFlyTo": true,
|
||||||
|
"canFlyFrom": true,
|
||||||
|
"canUseQuickShop": true,
|
||||||
|
"cannotViewMap": false,
|
||||||
|
"images": [],
|
||||||
|
"ratio": 1,
|
||||||
|
"defaultGround": "T331",
|
||||||
|
"bgm": "mount.opus",
|
||||||
|
"color": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0.3
|
||||||
|
],
|
||||||
|
"weather": [
|
||||||
|
"cloud",
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"firstArrive": [
|
||||||
|
{
|
||||||
|
"type": "loadBgm",
|
||||||
|
"name": "escape.opus"
|
||||||
|
},
|
||||||
|
"\t[野蛮人]\b[up,hero]山路开始崎岖多变了,要更小心一些"
|
||||||
|
],
|
||||||
|
"eachArrive": [],
|
||||||
|
"parallelDo": "",
|
||||||
|
"events": {
|
||||||
|
"44,0": [
|
||||||
|
"不愧是你!!!",
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": "function(){\nif (!core.isReplaying()) {\n\tcore.addMountSign(4);\n}\n}"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"62,0": [
|
||||||
|
"卧槽!你连这都到了?!",
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": "function(){\nif (!core.isReplaying()) {\n\tcore.addMountSign(5);\n}\n}"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"9,0": [
|
||||||
|
"这边没有彩蛋,往右边走→_→",
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": "function(){\nif (!core.isReplaying()) {\n\tcore.addMountSign(3);\n}\n}"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"13,7": [
|
||||||
|
"看到下面那个绝对防御怪了吗?",
|
||||||
|
"它可以刷血",
|
||||||
|
"具体方法嘛,就是把 攻击+额外攻击 加到刚好破防,然后接近防杀怪物的时候,就可以刷血了,前提是你得有生命回复才行",
|
||||||
|
"所以嘛,你可能需要压攻击了",
|
||||||
|
"不过如果你是简单难度不刷血也无所谓啦,完全可以通关"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"changeFloor": {
|
||||||
|
"0,7": {
|
||||||
|
"floorId": "MT14",
|
||||||
|
"loc": [
|
||||||
|
127,
|
||||||
|
7
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"63,4": {
|
||||||
|
"floorId": "MT16",
|
||||||
|
"loc": [
|
||||||
|
0,
|
||||||
|
23
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"afterBattle": {},
|
||||||
|
"afterGetItem": {},
|
||||||
|
"afterOpenDoor": {},
|
||||||
|
"autoEvent": {},
|
||||||
|
"cannotMove": {},
|
||||||
|
"map": [
|
||||||
|
[30144,30145,30146,30147,30144,30145,30146,30147, 0,129, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,129, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,129,20055],
|
||||||
|
[30152,30153,30154,30155,30152,30153,30154,30155, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20057,20057,20057,20057, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20058],
|
||||||
|
[30160,30161,30162,30163,30160,30161,30162,30163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,336,141,141,141,141,141,141,141,141,141,141,20007,20065,20065,20065,20065, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20074],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0,390,403,378, 21,484,492, 0, 27, 0, 0, 0, 28, 32,492,396,376,484,403,492,378,492,376,30152,30153,30154,30155, 34,511,484,376,403,378, 0,499, 0, 28,381, 28, 33,511,141,484, 0,269, 0,336,378, 0,512,336,482, 27,381, 0,20007],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0,141,141,141,141,512,141, 33, 0, 28, 0,503,141,141,141, 0,336,336,336,340, 0,141, 0,30160,30161,30162,30163, 28,336,336,336,336,336,494,484,390,403,403,396, 0, 0,492, 0,336, 0,482,492,482,336, 0,336,336,336,492,244, 94],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 33,378,492, 0,381, 0,503,141,141,141, 0, 27, 0,141,512, 0, 32,403,340,403,141,403, 0,244, 34, 27,381,492,403, 0,482, 0,378,336,336,336,512,336,492,376,403,378, 28, 0, 0,511, 0,492,390, 0,403, 0,484, 0, 0],
|
||||||
|
[336,499, 27, 34,381, 34, 28,503,141,376, 33,340,333,333,333, 0, 34,503, 0,336,336,503,336, 0,336,336,403,340, 34,141, 33, 0,492,336,336,336,336, 0,340,340,492, 0,482,482,492, 0,336, 0,340, 0, 27,482,340,336,336,336,492, 0, 0,504,336,492,269, 0],
|
||||||
|
[ 92, 0,141,141,141,141,141, 33,141,504,340,340,378,129,333, 0, 0,141, 0, 0,336, 0,336, 28, 0,333, 0,340,376,141,378,390,482,403,482,396,512, 0,340,482, 0,376,482,482,336,484,336,381,340,333,333,511,340, 0, 28,482, 0,269,492, 0, 0,378, 0, 0],
|
||||||
|
[336, 0,492, 27, 0, 34, 27,503, 0, 34, 0,244, 0, 27,482,499,141,141,381, 27,381, 28,499, 0,375,396, 0,511, 0,504, 0,340,340,340,340,340,340,340,340,511,492,340,336,336,336, 0,381,504, 0,482,376, 0, 0, 21, 0, 0,403, 0,512, 0,504,492,336, 0],
|
||||||
|
[20038,20038,20038,20038, 0, 0, 0, 0, 0,345,345,345,269,492,336, 27, 28,141,381, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0,482,378, 0,403, 0,376,482,504, 0, 0, 0, 27, 0, 0,511,340,381,340,512,333,504,141,141,511,141,141,141, 0,376, 0,376, 0, 0],
|
||||||
|
[30152,30153,30154,30155, 0, 0, 0, 0, 0,378,482,504, 0, 28,336,381,381,141,492, 0, 0, 0, 0, 0,492,512, 0, 0, 0, 0, 0,492,336,336,336,336,336,336,336,336,336, 0, 0,141,141,141,340,340,340, 0,333, 27, 27,141, 28, 28,141,482,340,482,336,492,512, 0],
|
||||||
|
[30160,30161,30162,30163, 0, 0, 0, 0, 0,403,376,492, 27, 0,511, 0, 0,511,396,503,503,503, 33, 28,381,381, 28, 33,503,503,503, 34,503, 34,503, 34,503, 34,503, 34,503, 34, 28, 0,403,492,390,484,396, 0,333, 27, 27,492, 28, 28,141,378, 21,403,396,484,403, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,336,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,336, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
],
|
||||||
|
"bgmap": [
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0,20076,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20075, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20076,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20055],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0,20047,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20045, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20047, 0, 0, 0, 0,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0,20047,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20045, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20047, 0, 0, 0, 0,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065, 0],
|
||||||
|
[20054,20054,20054,20054,20054,20054,20054,20054,20055, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20045, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[20057,20057,20057,20057,20057,20057,20057,20057,20058, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20045, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20037,20038,20038,20038,20038,20039, 0, 0, 0,20045,20047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[20065,20065,20065,20065,20065,20065,20065,20065,20074, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20045, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20045, 0, 0, 0, 0,20047, 0, 0, 0,20045,20047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20037],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20045, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20045, 0, 0, 0, 0,20068,20038,20038,20038,20067,20047, 0, 0, 0, 0, 0,20045,20047, 0, 0, 0,20045],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20045, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20045, 0, 0, 0, 0, 0, 0, 0, 0, 0,20047, 0, 0, 0, 0, 0,20045,20047, 0, 0, 0,20045],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20053,20054,20054,20054,20054, 0, 0,20054,20054,20054,20054,20055, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20045, 0, 0, 0, 0, 0, 0, 0, 0, 0,20068,20038,20038,20038,20038,20038,20067,20047, 0, 0, 0,20045],
|
||||||
|
[20038,20038,20038,20038,20038,20038,20038,20038,20039, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20056,20057,20057,20057,20057,20152,20153,20057,20057,20057,20057,20058, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20045, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20047, 0, 0, 0,20045],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0,20047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20064,20065,20065,20065,20065,20152,20153,20065,20065,20065,20065,20074, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20045, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20047, 0, 0, 0,20045],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0,20047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20045, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20047, 0, 0, 0,20045],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0,20068,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20067, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20068,20038,20038,20038,20067],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
],
|
||||||
|
"fgmap": [
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,30128,30129,30130,30131, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,30136,30137,30138,30139, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,30144,30145,30146,30147, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[30128,30129,30130,30131, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[30136,30137,30138,30139, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[30144,30145,30146,30147, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
],
|
||||||
|
"beforeBattle": {},
|
||||||
|
"cannotMoveIn": {},
|
||||||
|
"bg2map": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"fg2map": [
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
491
public/project/floors/MT16.js
Normal file
491
public/project/floors/MT16.js
Normal file
@ -0,0 +1,491 @@
|
|||||||
|
main.floors.MT16=
|
||||||
|
{
|
||||||
|
"floorId": "MT16",
|
||||||
|
"title": "山顶",
|
||||||
|
"name": "16",
|
||||||
|
"width": 25,
|
||||||
|
"height": 25,
|
||||||
|
"canFlyTo": true,
|
||||||
|
"canFlyFrom": false,
|
||||||
|
"canUseQuickShop": true,
|
||||||
|
"cannotViewMap": false,
|
||||||
|
"images": [],
|
||||||
|
"ratio": 1,
|
||||||
|
"defaultGround": "T331",
|
||||||
|
"bgm": "mount.opus",
|
||||||
|
"color": null,
|
||||||
|
"weather": [
|
||||||
|
"cloud",
|
||||||
|
2
|
||||||
|
],
|
||||||
|
"firstArrive": [
|
||||||
|
{
|
||||||
|
"type": "pauseBgm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "loadBgm",
|
||||||
|
"name": "escape.opus"
|
||||||
|
},
|
||||||
|
"\t[野蛮人]\b[up,hero]这里就是山顶了",
|
||||||
|
"\t[野蛮人]\b[up,hero]砍一些柴火就赶快回去吧",
|
||||||
|
"\t[野蛮人]\b[up,hero]不要待太久",
|
||||||
|
"下面是一场追逐战,建议把音乐打开",
|
||||||
|
"追逐战很简单,跑就完事了"
|
||||||
|
],
|
||||||
|
"eachArrive": [
|
||||||
|
{
|
||||||
|
"type": "pauseBgm"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parallelDo": "",
|
||||||
|
"events": {
|
||||||
|
"23,19": [
|
||||||
|
{
|
||||||
|
"type": "confirm",
|
||||||
|
"text": "是否跳过追逐战",
|
||||||
|
"yes": [
|
||||||
|
{
|
||||||
|
"type": "setValue",
|
||||||
|
"name": "flag:finishChase1",
|
||||||
|
"value": "true"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "changeFloor",
|
||||||
|
"floorId": "MT14",
|
||||||
|
"loc": [
|
||||||
|
21,
|
||||||
|
7
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "setBlock",
|
||||||
|
"number": "T336",
|
||||||
|
"loc": [
|
||||||
|
[
|
||||||
|
25,
|
||||||
|
7
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"floorId": "MT14"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": "function(){\ncore.status.maps.MT14.canFlyFrom = false;\nMota.require('@user/legacy-plugin-data').chaseInit1();\n}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "show",
|
||||||
|
"loc": [
|
||||||
|
[
|
||||||
|
7,
|
||||||
|
1
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"floorId": "MT14"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "exit"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"no": [
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": "function(){\nMota.require('@user/legacy-plugin-data').readyClip();\n}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "choices",
|
||||||
|
"text": "请选择难度",
|
||||||
|
"choices": [
|
||||||
|
{
|
||||||
|
"text": "简单(显示逃跑路线)",
|
||||||
|
"color": [
|
||||||
|
0,
|
||||||
|
255,
|
||||||
|
93,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"action": [
|
||||||
|
{
|
||||||
|
"type": "setValue",
|
||||||
|
"name": "flag:chaseHard",
|
||||||
|
"value": "0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "困难(不显示逃跑路线)",
|
||||||
|
"color": [
|
||||||
|
255,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"action": [
|
||||||
|
{
|
||||||
|
"type": "setValue",
|
||||||
|
"name": "flag:chaseHard",
|
||||||
|
"value": "1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"追逐战后录像会进行自动修复,不用担心录像问题",
|
||||||
|
"如果逃脱失败,或者想重新开始追逐战,直接读取自动存档即可,会跳过前奏",
|
||||||
|
{
|
||||||
|
"type": "hideStatusBar",
|
||||||
|
"toolbox": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": "function(){\ncore.status.maps.MT15.canFlyFrom = false\n}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "forbidSave",
|
||||||
|
"forbid": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": "function(){\nconst controller = Mota.require('@user/legacy-plugin-client').initChase(0);\ncontroller.initAudio(false);\n}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "show",
|
||||||
|
"loc": [
|
||||||
|
[
|
||||||
|
7,
|
||||||
|
1
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"floorId": "MT14"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "animate",
|
||||||
|
"name": "amazed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "sleep",
|
||||||
|
"time": 1000,
|
||||||
|
"noSkip": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "moveHero",
|
||||||
|
"time": 1000,
|
||||||
|
"steps": [
|
||||||
|
"backward:3"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "setViewport",
|
||||||
|
"dxy": [
|
||||||
|
0,
|
||||||
|
-2
|
||||||
|
],
|
||||||
|
"time": 500
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "autoText",
|
||||||
|
"text": "\t[野蛮人]\b[up,hero]狼!",
|
||||||
|
"time": 2000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "autoText",
|
||||||
|
"text": "\t[野蛮人]\b[up,hero]在睡觉",
|
||||||
|
"time": 2000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "autoText",
|
||||||
|
"text": "\t[野蛮人]\b[up,hero]悄悄过去把它打死",
|
||||||
|
"time": 3000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "autoText",
|
||||||
|
"text": "\t[野蛮人]\b[up,hero]千万不能惊动它",
|
||||||
|
"time": 2600
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "setViewport",
|
||||||
|
"dxy": [
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
"time": 500
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "moveHero",
|
||||||
|
"time": 600,
|
||||||
|
"steps": [
|
||||||
|
"up:10"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "setCurtain",
|
||||||
|
"color": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"time": 75,
|
||||||
|
"moveMode": "easeIn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "setBlock",
|
||||||
|
"number": "A506",
|
||||||
|
"loc": [
|
||||||
|
[
|
||||||
|
23,
|
||||||
|
8
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "setCurtain",
|
||||||
|
"color": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"time": 300,
|
||||||
|
"moveMode": "easeOut"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "setCurtain",
|
||||||
|
"time": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "autoText",
|
||||||
|
"text": "\t[野蛮人]\b[up,hero]!!!!!",
|
||||||
|
"time": 2000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "autoText",
|
||||||
|
"text": "\t[野蛮人]\b[up,hero]它醒了",
|
||||||
|
"time": 2000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "move",
|
||||||
|
"loc": [
|
||||||
|
23,
|
||||||
|
8
|
||||||
|
],
|
||||||
|
"time": 300,
|
||||||
|
"keep": true,
|
||||||
|
"steps": [
|
||||||
|
"down:1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "sleep",
|
||||||
|
"time": 200,
|
||||||
|
"noSkip": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "moveHero",
|
||||||
|
"time": 300,
|
||||||
|
"steps": [
|
||||||
|
"backward:1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "sleep",
|
||||||
|
"time": 200,
|
||||||
|
"noSkip": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "move",
|
||||||
|
"loc": [
|
||||||
|
23,
|
||||||
|
9
|
||||||
|
],
|
||||||
|
"time": 300,
|
||||||
|
"keep": true,
|
||||||
|
"steps": [
|
||||||
|
"down:1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "sleep",
|
||||||
|
"time": 200,
|
||||||
|
"noSkip": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "moveHero",
|
||||||
|
"time": 300,
|
||||||
|
"steps": [
|
||||||
|
"backward:1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "sleep",
|
||||||
|
"time": 200,
|
||||||
|
"noSkip": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "autoText",
|
||||||
|
"text": "\t[野蛮人]\b[up,hero]糟了,我可打不过它",
|
||||||
|
"time": 3000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "autoText",
|
||||||
|
"text": "\t[野蛮人]\b[up,hero]跑,只能跑了!",
|
||||||
|
"time": 3000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "moveHero",
|
||||||
|
"time": 200,
|
||||||
|
"steps": [
|
||||||
|
"backward:9"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "jump",
|
||||||
|
"from": [
|
||||||
|
23,
|
||||||
|
10
|
||||||
|
],
|
||||||
|
"dxy": [
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
"time": 500,
|
||||||
|
"keep": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "sleep",
|
||||||
|
"time": 1000,
|
||||||
|
"noSkip": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "jump",
|
||||||
|
"from": [
|
||||||
|
23,
|
||||||
|
12
|
||||||
|
],
|
||||||
|
"dxy": [
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
"time": 500,
|
||||||
|
"keep": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "sleep",
|
||||||
|
"time": 1000,
|
||||||
|
"noSkip": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "jump",
|
||||||
|
"from": [
|
||||||
|
23,
|
||||||
|
14
|
||||||
|
],
|
||||||
|
"dxy": [
|
||||||
|
0,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"time": 500,
|
||||||
|
"keep": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "sleep",
|
||||||
|
"time": 1000,
|
||||||
|
"noSkip": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "autoText",
|
||||||
|
"text": "\t[野蛮人]\b[up,hero]跑,快跑!!!!!!!!!",
|
||||||
|
"time": 3000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": "function(){\nMota.require('legacy-plugin-client').start(false);\n}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "autoSave"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"2,23": [
|
||||||
|
"这里是漏怪检测,将会检测\r[gold]洞穴、山路、山脚、平原\r[white]地区的怪物是否清完",
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": "function(){\nconst enemy = Mota.require('@user/legacy-plugin-data').getRemainEnemyString(core.floorIds.slice(5, 17));\nif (enemy.length === 0) {\n\tcore.insertAction(['当前无剩余怪物!', { \"type\": \"hide\", \"remove\": true }, ]);\n} else {\n\tcore.insertAction(enemy);\n}\n}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "loadBgm",
|
||||||
|
"name": "escape.opus"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"3,23": [
|
||||||
|
"即将开始追逐战,最好打开背景音乐,有耳机尽量佩戴耳机,这样游戏体验更佳",
|
||||||
|
"为了防止你撞上不该开的门,现在会将所有门打开,并删除所有物品",
|
||||||
|
"追逐的时候不能用2技能,不能用楼传,逃跑后要原路返回山洞",
|
||||||
|
"追逐战分为两个难度,简单难度会显示逃跑路径,困难模式不显示,困难模式逃跑成功可以获得成就",
|
||||||
|
"前方会有大约40秒的剧情,之后开始追逐战并自动存档,如果逃跑失败需要重打,可以直接读自动存档",
|
||||||
|
"\t[野蛮人]\b[up,hero]这墙上有文字?",
|
||||||
|
"\t[野蛮人]\b[up,hero]真相就藏在智慧之塔中,而想要到达智慧之塔,你必须经过勇气的考验,通过勇气之路。",
|
||||||
|
"\t[野蛮人]\b[up,hero]智慧之塔?勇气之路?",
|
||||||
|
"\t[野蛮人]\b[up,hero]算了,先砍柴吧。",
|
||||||
|
{
|
||||||
|
"type": "hide",
|
||||||
|
"remove": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"changeFloor": {
|
||||||
|
"0,23": {
|
||||||
|
"floorId": "MT15",
|
||||||
|
"loc": [
|
||||||
|
63,
|
||||||
|
4
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"afterBattle": {},
|
||||||
|
"afterGetItem": {},
|
||||||
|
"afterOpenDoor": {},
|
||||||
|
"autoEvent": {},
|
||||||
|
"cannotMove": {},
|
||||||
|
"map": [
|
||||||
|
[20076,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20054,20075],
|
||||||
|
[20047,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20057,20045],
|
||||||
|
[20047,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20065,20045],
|
||||||
|
[20047,30128,30129,30130,30131,30128,30129,30130,30131,30128,30129,30130,30131,30128,30129,30130,30131,30128,30129,30130,30131,30174,30175, 0,20045],
|
||||||
|
[20047,30136,30137,30138,30139,30136,30137,30138,30139,30136,30137,30138,30139,30136,30137,30138,30139,30136,30137,30138,30139,30182,30183, 0,20045],
|
||||||
|
[20047,30144,30145,30146,30147,30144,30145,30146,30147,30144,30145,30146,30147,30144,30145,30146,30147,30144,30145,30146,30147,30190,30191, 0,20045],
|
||||||
|
[20047,30152,30153,30154,30155,30152,30153,30154,30155,30152,30153,30154,30155,30152,30153,30154,30155,30152,30153,30154,30155,30198,30199, 0,20045],
|
||||||
|
[20047,30160,30161,30162,30163,30160,30161,30162,30163,30160,30161,30162,30163,30160,30161,30162,30163,30160,30161,30162,30163,30206,30207, 0,20045],
|
||||||
|
[20047,30128,30129,30130,30131,30128,30129,30130,30131,30128,30129,30130,30131,30128,30129,30130,30131,30128,30129,30130,30131,30174,30175,507,20045],
|
||||||
|
[20047,30136,30137,30138,30139,30136,30137,30138,30139,30136,30137,30138,30139,30136,30137,30138,30139,30136,30137,30138,30139,30182,30183, 0,20045],
|
||||||
|
[20047,30144,30145,30146,30147,30144,30145,30146,30147,30144,30145,30146,30147,30144,30145,30146,30147,30144,30145,30146,30147,30190,30191, 0,20045],
|
||||||
|
[20047,30152,30153,30154,30155,30152,30153,30154,30155,30152,30153,30154,30155,30152,30153,30154,30155,30152,30153,30154,30155,30198,30199, 0,20045],
|
||||||
|
[20047,30160,30161,30162,30163,30160,30161,30162,30163,30160,30161,30162,30163,30160,30161,30162,30163,30160,30161,30162,30163,30206,30207, 0,20045],
|
||||||
|
[20047,30128,30129,30130,30131,30128,30129,30130,30131,30128,30129,30130,30131,30128,30129,30130,30131,30128,30129,30130,30131,30174,30175, 0,20045],
|
||||||
|
[20047,30136,30137,30138,30139,30136,30137,30138,30139,30136,30137,30138,30139,30136,30137,30138,30139,30136,30137,30138,30139,30182,30183, 0,20045],
|
||||||
|
[20047,30144,30145,30146,30147,30144,30145,30146,30147,30144,30145,30146,30147,30144,30145,30146,30147,30144,30145,30146,30147,30190,30191, 0,20045],
|
||||||
|
[20047,30152,30153,30154,30155,30152,30153,30154,30155,30152,30153,30154,30155,30152,30153,30154,30155,30152,30153,30154,30155,30198,30199, 0,20045],
|
||||||
|
[20047,30160,30161,30162,30163,30160,30161,30162,30163,30160,30161,30162,30163,30160,30161,30162,30163,30160,30161,30162,30163,30206,30207, 0,20045],
|
||||||
|
[20047,30128,30129,30130,30131,30128,30129,30130,30131,30128,30129,30130,30131,30128,30129,30130,30131,30128,30129,30130,30131,30174,30175, 0,20045],
|
||||||
|
[20047,30136,30137,30138,30139,30136,30137,30138,30139,30136,30137,30138,30139,30136,30137,30138,30139,30136,30137,30138,30139,30182,30183, 0,20045],
|
||||||
|
[20055,30144,30145,30146,30147,30144,30145,30146,30147,30144,30145,30146,30147,30144,30145,30146,30147,30144,30145,30146,30147,30190,30191, 0,20045],
|
||||||
|
[20058,30152,30153,30154,30155,30152,30153,30154,30155,30152,30153,30154,30155,30152,30153,30154,30155,30152,30153,30154,30155,30198,30199, 0,20045],
|
||||||
|
[20074,30160,30161,30162,30163,30160,30161,30162,30163,30160,30161,30162,30163,30160,30161,30162,30163,30160,30161,30162,30163,30206,30166, 0,20045],
|
||||||
|
[ 92, 0,516, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20045],
|
||||||
|
[20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20038,20067]
|
||||||
|
],
|
||||||
|
"bgmap": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"fgmap": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"beforeBattle": {},
|
||||||
|
"bg2map": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"fg2map": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"cannotMoveIn": {}
|
||||||
|
}
|
||||||
120
public/project/floors/MT17.js
Normal file
120
public/project/floors/MT17.js
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
main.floors.MT17=
|
||||||
|
{
|
||||||
|
"floorId": "MT17",
|
||||||
|
"title": "勇气之路",
|
||||||
|
"name": "17",
|
||||||
|
"width": 15,
|
||||||
|
"height": 15,
|
||||||
|
"canFlyTo": true,
|
||||||
|
"canFlyFrom": true,
|
||||||
|
"canUseQuickShop": true,
|
||||||
|
"cannotViewMap": false,
|
||||||
|
"images": [],
|
||||||
|
"ratio": 2,
|
||||||
|
"defaultGround": "grass",
|
||||||
|
"bgm": "plot1.opus",
|
||||||
|
"color": null,
|
||||||
|
"weather": null,
|
||||||
|
"firstArrive": [
|
||||||
|
{
|
||||||
|
"type": "setCurtain",
|
||||||
|
"time": 1000
|
||||||
|
},
|
||||||
|
"\t[野蛮人]\b[up,hero]勇气之路,智慧之塔,我来了!",
|
||||||
|
"血瓶宝石效果已变成两倍",
|
||||||
|
{
|
||||||
|
"type": "setGlobalValue",
|
||||||
|
"name": "animateSpeed",
|
||||||
|
"value": 333.333
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": "function(){\ndelete flags.__bgm__;\n}"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"eachArrive": [],
|
||||||
|
"parallelDo": "",
|
||||||
|
"events": {
|
||||||
|
"6,7": [
|
||||||
|
"在地图上拥有追猎怪物的时候无法瞬移"
|
||||||
|
],
|
||||||
|
"13,6": [
|
||||||
|
"这个绝对坚固怪能刷血还是尽量刷吧,塔里面有盾,拿了盾之后差不多就能防杀了,大概可以刷10w血",
|
||||||
|
"注意,如果你开启了自动切换技能,那么当你未防杀他的时候系统可能会自动选择开启技能导致你破了防。这时候你需要自行计算距离不开技能破防还需要吃多少攻"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"changeFloor": {
|
||||||
|
"14,7": {
|
||||||
|
"floorId": "MT18",
|
||||||
|
"loc": [
|
||||||
|
0,
|
||||||
|
7
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"afterBattle": {
|
||||||
|
"12,6": [
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": "function(){\nif (core.status.hero.hp - flags.hphphp >= 150000) {\n\t//Mota.require('achievement_r').completeAchievement('normal', 1);\n}\ndelete flags.hphphp;\n}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"afterGetItem": {},
|
||||||
|
"afterOpenDoor": {},
|
||||||
|
"autoEvent": {},
|
||||||
|
"cannotMove": {},
|
||||||
|
"map": [
|
||||||
|
[142,142,142,142,142,142,142,142,142,142,142,142,142,142,142],
|
||||||
|
[142,142,142,142,142,142,142,142,142,142,142,142,142,142,142],
|
||||||
|
[142,142,142,142,142,142,142,142,142,142,142,142,142,142,142],
|
||||||
|
[142,142, 0, 27,492, 0, 29,142,142,142,142,482, 0,482,142],
|
||||||
|
[142,142,142, 34,142,142, 34,142, 27,142,142,142,142,142,142],
|
||||||
|
[142, 28, 34, 29,226, 21, 27,492,381,142, 29, 28, 22, 27,142],
|
||||||
|
[142,142,142,142,142,268,142,142, 33,142,142,142,517,129,142],
|
||||||
|
[ 0,209, 0, 0,491, 0,129,143,273,143,143,143, 33, 0, 94],
|
||||||
|
[143,143,143,143, 0,491,226, 0, 0, 0, 0, 0, 0,521,143],
|
||||||
|
[143, 34, 27,143,522,143,143,143,143,522,143,143,143, 0,143],
|
||||||
|
[143, 28, 29,143, 33, 28, 0, 29,492,403,403,143, 0, 28,143],
|
||||||
|
[143, 34, 27,505, 0,143,223,492,143,143,143,143,273,143,143],
|
||||||
|
[143,492,143,143,505,143, 34,403,143,376, 33,143,381, 0,143],
|
||||||
|
[143, 28, 29, 34,376,492,403, 34,515, 33,378,143, 0, 28,143],
|
||||||
|
[143,143,143,143,143,143,143,143,143,143,143,143,143,143,143]
|
||||||
|
],
|
||||||
|
"bgmap": [
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[144,144,144,144,144, 0, 0, 0, 0, 0, 0, 0, 0,144,144],
|
||||||
|
[ 0, 0, 0, 0,144,144,144,144,144,144, 0, 0, 0,144, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0,144, 0, 0, 0,144, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0,144,144,144,144,144, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
],
|
||||||
|
"fgmap": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"beforeBattle": {
|
||||||
|
"12,6": [
|
||||||
|
{
|
||||||
|
"type": "setValue",
|
||||||
|
"name": "flag:hphphp",
|
||||||
|
"value": "core.status.hero.hp"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"bg2map": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"fg2map": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"cannotMoveIn": {}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user