Compare commits

..

28 Commits

Author SHA1 Message Date
6c75fa507c chore: 音频改为样板音频 2025-09-29 20:39:37 +08:00
8ea1779e06 fix: TextContent 卡死 2025-09-29 20:19:49 +08:00
43b0aa186d Merge branch 'template' of github.com:unanmed/HumanBreak into template 2025-09-29 20:16:13 +08:00
6ace764dc9 chore: 地图与素材配置等改为样板内容 2025-09-29 20:15:54 +08:00
5de5153441 feat: 默认状态栏 2025-09-29 20:15:54 +08:00
beb1ac485c chore: 右侧状态栏改为示例 2025-09-29 20:15:29 +08:00
7c9c4c3aa8 feat: 默认状态栏 2025-09-29 20:14:53 +08:00
ef73e08358 chore: 右侧状态栏改为示例 2025-09-29 20:14:53 +08:00
eac37b8cd1 chore: 调整变基结果 2025-09-29 20:13:32 +08:00
141dabb14e chore: 样板不自带弹幕功能 2025-09-29 20:13:32 +08:00
a5aebfadf0 feat: 默认状态栏 2025-09-29 20:13:32 +08:00
4cf970895f chore: 右侧状态栏改为示例 2025-09-29 20:13:32 +08:00
8531a7eea6 chore: 右侧状态栏改为示例 2025-09-29 20:13:31 +08:00
038d8b06c3 chore: 调整存档界面的默认间距 2025-09-29 20:13:31 +08:00
3cb0dd9e21 feat: 默认状态栏 2025-09-29 20:13:31 +08:00
4c54c18665 chore: 右侧状态栏改为示例 2025-09-29 20:13:31 +08:00
ebe20cc285 fix: 浏览地图没有楼传也能传 2025-09-29 20:11:22 +08:00
c541415e1f chore: 地图与素材配置等改为样板内容 2025-09-29 20:08:41 +08:00
a8291ecdd1 Merge branch 'template' of github.com:unanmed/HumanBreak into template 2025-09-29 17:10:44 +08:00
3c88d399ff chore: 调整变基结果 2025-09-29 17:10:27 +08:00
1c05542b8c chore: 样板不自带弹幕功能 2025-09-29 17:09:33 +08:00
ce41eb2538 feat: 默认状态栏 2025-09-29 17:09:33 +08:00
baf2e239f3 chore: 右侧状态栏改为示例 2025-09-29 17:09:21 +08:00
a951f1c957 chore: 右侧状态栏改为示例 2025-09-29 17:08:57 +08:00
ce6d6a03ed chore: 调整存档界面的默认间距 2025-09-29 17:08:00 +08:00
c0782169e2 feat: 默认状态栏 2025-09-29 17:08:00 +08:00
015f959c1d chore: 右侧状态栏改为示例 2025-09-29 17:08:00 +08:00
695d069797 chore: 调整设置内容,添加画面放缩,删除遗留内容 2025-09-29 13:03:29 +08:00
248 changed files with 476 additions and 25045 deletions

View File

@ -1,6 +1,6 @@
import { KeyCode } from '@motajs/client-base';
import { gameKey, HotkeyJSON } from '@motajs/system-action';
import { hovered, mainUi, tip, openDanmakuPoster } from '@motajs/legacy-ui';
import { hovered, mainUi, openDanmakuPoster } from '@motajs/legacy-ui';
import { GameStorage } from '@motajs/legacy-system';
export const mainScope = Symbol.for('@key_main');
@ -220,23 +220,6 @@ gameKey
defaults: KeyCode.Digit0,
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 系统按键
.group('system', '系统按键')
.register({
@ -590,45 +573,6 @@ gameKey
.realize('comment', () => {
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', () => {
core.debug();
});

View File

@ -1277,6 +1277,7 @@ export class TextContentParser {
break;
}
case TextGuessStatus.NeedSplit: {
this.bsStart = this.blockPointer;
this.bsEnd = this.wordBreak.length;
this.splitTextLoop(node, width);
break;

View File

@ -159,8 +159,10 @@ export class FloorItemDetail implements ILayerGroupRenderExtends {
* @param block
*/
calAllItems(block: Set<number>) {
if (!core.status.thisMap) return;
const enable = mainSetting.getValue('screen.itemDetail');
if (!core.status.thisMap || !enable) return;
if (this.dirtyBlock.size === 0 || block.size === 0) return;
let diff: Record<string | symbol, number | undefined> = {};
const before = core.status.hero;
const hero = structuredClone(core.status.hero);
@ -174,8 +176,7 @@ export class FloorItemDetail implements ILayerGroupRenderExtends {
core.status.hero = new Proxy(hero, handler);
core.setFlag('__statistics__', true);
block.forEach(v => {
if (!this.dirtyBlock.has(v)) return;
this.dirtyBlock.forEach(v => {
const data = this.blockData.get(v);
const detail = this.detailData.get(v);
detail?.clear();

View File

@ -2,12 +2,10 @@ import { createApp, Font } from '@motajs/render';
import { defineComponent } from 'vue';
import { DEFAULT_FONT, MAIN_HEIGHT, MAIN_WIDTH } from './shared';
import { hook, loading } from '@user/data-base';
import { createLoopMap } from './loopMap';
import { createElements } from './elements';
import { mainRenderer } from './renderer';
import { createUI } from './ui';
import { createAction } from './action';
import { createLegacy } from './legacy';
import { sceneController } from './scene';
import { GameTitleUI } from './ui/title';
import { createWeather } from './weather';
@ -29,10 +27,8 @@ export function createGameRenderer() {
export function createRender() {
createElements();
createLegacy();
createUI();
createAction();
createLoopMap();
createWeather();
loading.on('loaded', () => {
@ -51,7 +47,6 @@ export function createRender() {
export * from './components';
export * from './elements';
export * from './fx';
export * from './legacy';
export * from './ui';
export * from './utils';
export * from './weather';

View File

@ -1,64 +0,0 @@
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);
}
}

View File

@ -1,9 +0,0 @@
import { createGameCanvas } from './gameCanvas';
import { createShadow } from './shadow';
export function createLegacy() {
createGameCanvas();
createShadow();
}
export * from './shadow';

View File

@ -1,103 +0,0 @@
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'));

View File

@ -1,296 +0,0 @@
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);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,108 +0,0 @@
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);
}

View File

@ -62,7 +62,7 @@ export const CENTER_LOC: ElementLocator = [
/** 弹框的宽度,使用在内置 UI 与组件中,包括确认框、选择框、等待框等 */
export const POP_BOX_WIDTH = MAP_WIDTH / 2;
/** 默认字体 */
export const DEFAULT_FONT = new Font('normal', 18);
export const DEFAULT_FONT = new Font('Verdana', 16);
//#region 存档界面

View File

@ -1,4 +1,3 @@
import { LayerShadowExtends } from '../legacy/shadow';
import {
Props,
Font,
@ -39,11 +38,8 @@ import { ReplayingStatus } from './toolbar';
import { getHeroStatusOn } from '@user/data-state';
import { hook } from '@user/data-base';
import { FloorDamageExtends, FloorItemDetail } from '../elements';
import { LayerGroupPortal } from '../legacy/portal';
import { LayerGroupFilter } from '../legacy/gameCanvas';
import { LayerGroupHalo } from '../legacy/halo';
import { FloorChange } from '../legacy/fallback';
import { PopText } from '../legacy/pop';
import { mainUIController } from './controller';
import {
ILayerGroupRenderExtends,
@ -61,16 +57,13 @@ const MainScene = defineComponent(() => {
const layerGroupExtends: ILayerGroupRenderExtends[] = [
new FloorDamageExtends(),
new FloorItemDetail(),
new LayerGroupFilter(),
new LayerGroupPortal(),
new LayerGroupHalo(),
new LayerGroupAnimate(),
new FloorViewport()
];
const eventExtends: ILayerRenderExtends[] = [
new HeroRenderer(),
new LayerDoorAnimate(),
new LayerShadowExtends()
new LayerDoorAnimate()
];
const mainTextboxProps: Props<typeof Textbox> = {
text: '',
@ -81,7 +74,7 @@ const MainScene = defineComponent(() => {
titleFill: 'gold',
font: new Font('normal'),
titleFont: new Font('normal', 20, 'px', 700),
winskin: 'winskin2.png',
winskin: 'winskin.png',
interval: 30,
lineHeight: 4,
width: MAP_WIDTH
@ -283,7 +276,6 @@ const MainScene = defineComponent(() => {
<layer layer="event" zIndex={30} ex={eventExtends}></layer>
<layer layer="fg" zIndex={40}></layer>
<layer layer="fg2" zIndex={50}></layer>
<PopText id="pop-main" zIndex={80}></PopText>
</layer-group>
<Textbox id="main-textbox" {...mainTextboxProps}></Textbox>
<FloorChange id="floor-change" zIndex={50}></FloorChange>

View File

@ -69,8 +69,8 @@ const saveBtnProps = {
} satisfies SetupComponentOptions<SaveItemProps>;
export const SaveItem = defineComponent<SaveItemProps>(props => {
const font = Font.defaults({ size: 18 });
const statusFont = Font.defaults({ size: 14 });
const font = Font.defaults({ size: 16 });
const statusFont = Font.defaults({ size: 12 });
const w = computed(() => props.loc[2] ?? 200);
const h = computed(() => props.loc[3] ?? 200);
@ -267,7 +267,7 @@ export const Save = defineComponent<SaveProps, SaveEmits, keyof SaveEmits>(
`确认要删除存档 ${index + 1}`,
[HALF_WIDTH, HALF_HEIGHT, void 0, void 0, 0.5, 0.5],
POP_BOX_WIDTH,
{ winskin: 'winskin2.png' }
{ winskin: 'winskin.png' }
);
if (confirm) {
emit('delete', index, exist(posIndex));

View File

@ -246,6 +246,7 @@ export const ReplaySettings = defineComponent<MainSettingsProps>(props => {
onChoose={choose}
interval={8}
scope={scope}
maxHeight={MAIN_HEIGHT - 32}
/>
);
}, mainSettingsProps);

View File

@ -72,7 +72,7 @@ const gameTitleProps = {
} satisfies SetupComponentOptions<GameTitleProps>;
export const GameTitle = defineComponent<GameTitleProps>(props => {
const bg = core.material.images.images['bg.webp'];
const bg = core.material.images.images['bg.jpg'];
//#region 计算背景图
const [width, height] = adjustCover(
@ -437,7 +437,6 @@ export const GameTitle = defineComponent<GameTitleProps>(props => {
image={bg}
loc={[HALF_WIDTH, HALF_HEIGHT, width, height]}
anc={[0.5, 0.5]}
filter="brightness(120%)contrast(110%)"
zIndex={0}
/>
<shader

View File

@ -32,7 +32,6 @@ import {
LayerGroupFloorBinder
} from '../elements';
import { LayerGroupHalo } from '../legacy/halo';
import { LayerGroupPortal } from '../legacy/portal';
import { Font } from '@motajs/render-style';
import { clamp, mean } from 'lodash-es';
import { calculateStatisticsOne, StatisticsDataOneFloor } from './statistics';
@ -66,7 +65,6 @@ export const ViewMap = defineComponent<ViewMapProps>(props => {
const layerGroupExtends: ILayerGroupRenderExtends[] = [
new FloorDamageExtends(),
new FloorItemDetail(),
new LayerGroupPortal(),
new LayerGroupHalo(),
new LayerGroupAnimate()
];
@ -142,6 +140,7 @@ export const ViewMap = defineComponent<ViewMapProps>(props => {
const openBook = () => core.openBook(true);
const fly = () => {
if (!core.hasItem('fly')) return;
const id = viewableFloor[now.value];
const success = core.flyTo(id);
if (success) close();

View File

@ -1,10 +1,4 @@
import {
DamageEnemy,
ensureFloorDamage,
getEnemy,
HeroSkill,
NightSpecial
} from '@user/data-state';
import { DamageEnemy, ensureFloorDamage, getEnemy } from '@user/data-state';
import { hook } from '@user/data-base';
import { Patch, PatchClass } from '@motajs/legacy-common';
import { isNil } from 'lodash-es';
@ -150,9 +144,6 @@ export function patchBattle() {
patch2.add(
'afterBattle',
function (enemy: DamageEnemy, x?: number, y?: number) {
const floorId = core.status.floorId;
const special = enemy.info.special;
// 播放战斗动画
let animate: AnimationIds = 'hand';
// 检查当前装备是否存在攻击动画
@ -180,30 +171,6 @@ export function patchBattle() {
core.status.hero.statistics.battleDamage += damage;
core.status.hero.statistics.battle++;
// 智慧之源
if (special.has(14) && flags.hard === 2) {
core.addFlag(
'inte_' + floorId,
Math.ceil((core.status.hero.mdef / 10) * 0.3) * 10
);
core.status.hero.mdef -=
Math.ceil((core.status.hero.mdef / 10) * 0.3) * 10;
}
// 极昼永夜
if (special.has(22)) {
NightSpecial.addNight(floorId, -enemy.info.night!);
}
if (special.has(23)) {
NightSpecial.addNight(floorId, enemy.info.day!);
}
// 如果是融化怪,需要特殊标记一下
if (special.has(25) && !isNil(x) && !isNil(y)) {
flags[`melt_${floorId}`] ??= {};
flags[`melt_${floorId}`][`${x},${y}`] = enemy.info.melt;
}
// 获得金币
const money = enemy.info.money!;
core.status.hero.money += money;
@ -223,8 +190,6 @@ export function patchBattle() {
exp;
core.drawTip(hint, enemy.id);
HeroSkill.disableSkill();
// 事件的处理
const todo: MotaEvent = [];

View File

@ -2,7 +2,6 @@ import { getHeroStatusOf, getHeroStatusOn } from '../state/hero';
import { Range, ensureArray, has, manhattan } from '@user/data-utils';
import EventEmitter from 'eventemitter3';
import { hook } from '@user/data-base';
import { HeroSkill, NightSpecial } from '../mechanism';
import {
EnemyInfo,
DamageInfo,
@ -285,11 +284,6 @@ export class DamageEnemy implements IDamageEnemy {
info.atk += flags[`inte_${floorId}`] ?? 0;
}
// 极昼永夜
const night = NightSpecial.getNight(floorId);
info.atk -= night;
info.def -= night;
// 融化融化不属于怪物光环因此不能用provide和inject计算需要在这里计算
const melt = flags[`melt_${floorId}`];
if (!isNil(melt) && !isNil(this.x) && !isNil(this.y)) {
@ -720,27 +714,9 @@ export class DamageEnemy implements IDamageEnemy {
private calEnemyDamageOf(hero: Partial<HeroStatus>, enemy: UserEnemyInfo) {
const status = getHeroStatusOf(hero, realStatus, this.floorId);
let damage = calDamageWith(enemy, status) ?? Infinity;
let bestSkill = -1;
const damage = calDamageWith(enemy, status) ?? Infinity;
// 自动切换技能
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 };
return { damage };
}
/**
@ -928,10 +904,6 @@ const realStatus: (keyof HeroStatus)[] = [
'mana',
'magicDef'
];
/**
*
*/
const skills: HeroSkill.Skill[] = [HeroSkill.Blade, HeroSkill.Shield];
/**
*

View File

@ -1,9 +1,2 @@
import { createMechanism } from './mechanism';
export function create() {
createMechanism();
}
export * from './enemy';
export * from './mechanism';
export * from './state';

View File

@ -1,4 +0,0 @@
export function createMechanism() {}
export * from './misc';
export * from './skillTree';

View File

@ -1,276 +0,0 @@
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();
}
}

View File

@ -1,440 +0,0 @@
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;
}
}

View File

@ -1,7 +1,6 @@
import { logger } from '@motajs/common';
import { EventEmitter } from 'eventemitter3';
import { cloneDeep } from 'lodash-es';
import { HeroSkill, NightSpecial } from '../mechanism';
/**
*
@ -59,7 +58,6 @@ function getRealStatus(
name: keyof HeroStatus | 'all' | (keyof HeroStatus)[],
floorId: FloorIds = core.status.floorId
): any {
const { getSkillLevel } = Mota.require('@user/data-state');
if (name instanceof Array) {
const res: any = {};
name.forEach(v => {
@ -94,29 +92,6 @@ function getRealStatus(
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
s *= core.status.hero.buff[name] ?? 1;
s = Math.floor(s);

View File

@ -1,3 +1,5 @@
export function create() {}
export * from './hero';
export * from './interface';
export * from './item';

View File

@ -1,5 +1,5 @@
import EventEmitter from 'eventemitter3';
import { backDir, checkCanMoveExtended, toDir } from './utils';
import { backDir, toDir } from './utils';
import { loading } from '@user/data-base';
import type { RenderAdapter } from '@motajs/render';
import type {
@ -9,10 +9,8 @@ import type {
HeroRenderer,
Layer,
LayerFloorBinder,
LayerGroup,
LayerMovingRenderable
} from '@user/client-modules';
import { BluePalace, MiscData } from '../mechanism/misc';
import { sleep } from '@motajs/common';
// todo: 转身功能
@ -414,26 +412,13 @@ interface CanMoveStatus {
noPass: boolean;
}
interface PortalStatus {
/** 下一步是否会步入传送门 */
portal: boolean;
/** 传送门会传到哪 */
data?: BluePalace.PortalTo;
}
const enum HeroMoveCode {
Step,
Stop,
/** 不能移动,并撞击前面一格的图块,触发其触发器 */
Hit,
/** 不能移动同时当前格有CannotOut或目标格有CannotIn不会触发前面一格的触发器 */
CannotMove,
/** 进入传送门 */
Portal,
/** 循环式地图 */
Loop,
/** 循环式地图撞击 */
LoopHit
CannotMove
}
export class HeroMover extends ObjectMoverBase {
@ -454,9 +439,6 @@ export class HeroMover extends ObjectMoverBase {
/** 本次移动开始时的移动速度 */
private beforeMoveSpeed: number = 100;
/** 这一步的传送门信息 */
private portalData?: BluePalace.PortalTo;
override startMove(
ignoreTerrain: boolean = false,
noRoute: boolean = false,
@ -546,16 +528,6 @@ export class HeroMover extends ObjectMoverBase {
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;
if (!this.ignoreTerrain) {
const { noPass, canMove } = this.checkCanMove(x, y, dir4Move);
@ -563,25 +535,6 @@ export class HeroMover extends ObjectMoverBase {
if (!canMove) {
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) {
return HeroMoveCode.Hit;
@ -632,25 +585,9 @@ export class HeroMover extends ObjectMoverBase {
}
// 本次移动正常完成
if (
code === HeroMoveCode.Step ||
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 (code === HeroMoveCode.Step) {
core.setHeroLoc('x', nx, true);
core.setHeroLoc('y', ny, true);
if (!this.ignoreTerrain) {
const direction = core.getHeroLoc('direction');
@ -713,7 +650,7 @@ export class HeroMover extends ObjectMoverBase {
core.status.automaticRoute.moveStepBeforeStop = [];
core.status.automaticRoute.lastDirection = dir;
if (core.status.automaticRoute.moveStepBeforeStop.length == 0) {
if (core.status.automaticRoute.moveStepBeforeStop.length === 0) {
core.clearContinueAutomaticRoute();
core.stopAutomaticRoute();
}
@ -727,20 +664,6 @@ export class HeroMover extends ObjectMoverBase {
*/
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 noPass = core.noPass(nx, ny);
const canMove = core.canMoveHero(x, y, dir);
@ -759,173 +682,6 @@ export class HeroMover extends ObjectMoverBase {
const ny = y + dy;
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 {

View File

@ -1,435 +0,0 @@
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);
}
/**
* ai1
* @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;
}
}

View File

@ -1,23 +0,0 @@
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();
}
});

View File

@ -1,132 +0,0 @@
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 {}
}

View File

@ -1,270 +0,0 @@
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();
}
}

View File

@ -1,850 +0,0 @@
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();
}
}

View File

@ -1,986 +0,0 @@
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();
}
}

View File

@ -1,356 +0,0 @@
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);
});

View File

@ -1,723 +0,0 @@
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');
});
}

View File

@ -1,34 +0,0 @@
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);
}

View File

@ -1,225 +0,0 @@
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;
}

View File

@ -1 +0,0 @@
export * from './pointShader';

View File

@ -1,652 +0,0 @@
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-112
*/
CircleContrast,
/**
* \
* \
* `data1: x, y, radius, decay` | \
* `data2: ratio, _, _, _` | 0-112
*/
CircleSaturate,
/**
* \
* \
* `data1: x, y, radius, decay` | \
* `data2: angle, _, _, _` | 001360
*/
CircleHue,
/**
* \
* \
* `data1: x, y, maxRaius, waveRadius` | \
* `data2: amplitude, attenuation, linear, tangential` \
* &ensp;&ensp;* `amplitude`: 11\
* &ensp;&ensp;* `attenuation`: \
* &ensp;&ensp;* `linear`: 线
* 线0-10.5线\
* &ensp;&ensp;* `tangential`: 1\
* `data3: startPhase, endPhase, _, _` |
* 2 * PI Math.PI * 2
*/
CircleWarp,
/**
* \
* \
* `data1: x, y, minRadius, maxRadius` |
* 1Math.PI的相位1\
* `data2: startPhase, endPhase, _, _`
*/
CircleWarpTangetial,
/**
* \
* \
* `data1: x, y, radius, decay` | \
* `data2: ratio, _, _, _` | 012
*/
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 decaydata2: 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 decaydata2: 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 decaydata2: 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 decaydata2: 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 decaydata2: 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 decaydata2: 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;
}
`;
}

View File

@ -2,6 +2,4 @@ if (import.meta.env.DEV) {
import('./dev/hotReload');
}
export * from './boss';
export * from './chase';
export * from './fx';
export {};

View File

@ -1,19 +0,0 @@
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);
});
}

View File

@ -1,22 +1,13 @@
import { has, ofDir } from '@user/data-utils';
export function createCheckBlock() {
// 伤害弹出
// 复写阻激夹域检测
control.prototype.checkBlock = function (forceMockery: boolean = false) {
control.prototype.checkBlock = function () {
const x = core.getHeroLoc('x'),
y = core.getHeroLoc('y'),
loc = x + ',' + y;
const info = core.status.thisMap.enemy.mapDamage[loc];
const damage = info?.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;
const type = [...info.type];
const text = type.join('') || '伤害';
@ -33,120 +24,5 @@ export function createCheckBlock() {
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);
}

View File

@ -5,4 +5,3 @@ export function createEnemy() {
}
export * from './checkblock';
export * from './remainEnemy';

View File

@ -1,54 +0,0 @@
// 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;
}

View File

@ -18,11 +18,9 @@ export function createLegacy() {
}
export * from './enemy';
export * from './chase';
export * from './fallback';
export * from './fiveLayer';
export * from './removeMap';
export * from './replay';
export * from './shop';
export * from './skill';
export * from './ui';

View File

@ -1,9 +1,5 @@
import { HeroSkill, getSkillFromIndex, upgradeSkill } from '@user/data-state';
import { canOpenShop } from './shop';
import { hook } from '@user/data-base';
import { jumpSkill } from './skill';
const replayableSettings = ['autoSkill'];
let cliping = false;
let startIndex = 0;
@ -32,51 +28,6 @@ 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 openedShopId = '';
@ -143,89 +94,6 @@ export function initReplay() {
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 => {
if (!action.startsWith('fly:')) return false;
const floorId = action.slice(4) as FloorIds;

View File

@ -1,232 +0,0 @@
// @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 };
}
}

View File

@ -31,7 +31,7 @@ export function initUI() {
};
control.prototype.showStatusBar = function () {
if (main.mode == 'editor') return;
if (main.mode === 'editor') return;
core.removeFlag('hideStatusBar');
if (mainSetting.getValue('ui.tips')) {
if (!fixedUi.hasName('tips')) {
@ -42,7 +42,7 @@ export function initUI() {
};
control.prototype.hideStatusBar = function (showToolbox) {
if (main.mode == 'editor') return;
if (main.mode === 'editor') return;
// 如果原本就是隐藏的,则先显示
if (!core.domStyle.showStatusBar) this.showStatusBar();

View File

@ -4,7 +4,6 @@
"是否全屏进行游戏全屏后按ESC退出全屏开启后将不能通过按ESC开启系统设置菜单",
"请按下方的按钮打开。进入或退出全屏后请存读档一下,以解决一部分绘制问题。"
],
"halo": ["开启后,会在地图上显示范围光环。"],
"itemDetail": ["是否在地图上显示宝石血瓶装备等增加的属性值"],
"transition": [
"是否展示当一个ui界面如怪物手册等的打开与关闭时的动画。当此项开启时",
@ -16,43 +15,11 @@
"criticalGem": ["临界是否显示为在当前地图要吃的宝石数"]
},
"action": {
"autoSkill": [
"开启后,打怪物的时候会自动选择伤害最低的技能。同时显伤也会显示此状态下的伤害,",
"临界也会考虑技能在内"
],
"fixed": [
"开启后,当鼠标移动到怪物上时,会以盒子的形式展示该点的怪物信息。手机端此功能无效。",
"<br>",
"<br>",
"注当鼠标移动到怪物上时经过200毫秒才会显示信息防止误操作。"
],
"hotkey": ["设置游戏中会用到的一些快捷键"]
},
"utils": {
"betterLoad": [
"<span style=\"color: yellow; font-weight: 700\">试验性功能</span>",
"<br>",
"开启后游戏将对加载进行优化,缩短进入游戏时的加载时长,而在游戏中对资源进行部分性按需加载,从而对加载进行优化。",
"该设置不会影响你的正常游戏,但如果网络环境较差,可能会导致部分楼层转换时间明显变长。",
"<br>",
"<br>",
"注:修改后刷新页面起效。"
],
"autoScale": [
"开启后,每次进入游戏时会自动缩放游戏画面至合适值。该项只对电脑端有效。",
"<br>",
"<br>",
"缩放原则如下:",
"<br>",
"1. 首先尝试缩放至最大缩放比例",
"<br>",
"2. 如果缩放后游戏画面高度高于页面高度的95%,那么缩小一个缩放比例,否则保持最大比例"
"开启后,每次进入游戏时会自动缩放游戏画面至合适值。该项只对电脑端有效。"
]
},
"fx": {
"paraLight": [
"是否开启野外的平行光阴影,在野外将会显示平行光阴影,模拟太阳光,拥有不错的视觉效果"
],
"frag": ["开启后,在打败怪物后会触发怪物碎裂特效。"]
}
}

View File

@ -66,8 +66,6 @@ mainSetting.on('valueChange', (key, n, o) => {
if (root === 'screen') {
handleScreenSetting(setting, n, o);
} else if (root === 'action') {
handleActionSetting(setting, n, o);
} else if (root === 'audio') {
handleAudioSetting(setting, n, o);
} else if (root === 'ui') {
@ -85,30 +83,25 @@ function handleScreenSetting<T extends number | boolean>(
if (key === 'fullscreen') {
// 全屏
triggerFullscreen(n as boolean);
} else if (key === 'heroDetail') {
// 勇士显伤
core.drawHero();
} else if (key === 'fontSize') {
// 字体大小
root.style.fontSize = `${n}px`;
const absoluteSize = (n as number) * devicePixelRatio;
storage.setValue('@@absoluteFontSize', absoluteSize);
storage.write();
} else if (key === 'fontSizeStatus') {
// fontSize.value = n as number;
}
}
function handleActionSetting<T extends number | boolean>(
key: string,
n: T,
_o: T
) {
if (key === 'autoSkill') {
// 自动切换技能
const HeroSkill = Mota.require('@user/data-state').HeroSkill;
HeroSkill.setAutoSkill(n as boolean);
core.status.route.push(`set:autoSkill:${n}`);
} else if (key === 'scale') {
const { MAIN_HEIGHT, MAIN_WIDTH } = Mota.require(
'@user/client-modules'
);
const max = Math.min(
(window.innerHeight / MAIN_HEIGHT) * 100,
(window.innerWidth / MAIN_WIDTH) * 100,
n as number
);
const scale = Number((Math.floor((max / 100) * 4) / 4).toFixed(2));
// @ts-expect-error 遗留问题
core.domStyle.scale = scale;
Mota.require('@user/client-modules').mainRenderer.setScale(scale);
}
}
@ -153,19 +146,11 @@ mainSetting
'显示设置',
new MotaSetting()
.register('fullscreen', '全屏游戏', false, COM.Boolean)
.register('halo', '光环显示', true, COM.Boolean)
.register('scale', '画面缩放', 100, COM.Number, [50, 500, 25])
.setDisplayFunc('scale', value => `${value}%`)
.register('itemDetail', '宝石血瓶显伤', true, COM.Boolean)
.register('heroDetail', '勇士显伤', false, COM.Boolean)
.register('transition', '界面动画', false, COM.Boolean)
.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)
.setDisplayFunc('criticalGem', value => (value ? '宝石数' : '攻击'))
.register('keyScale', '虚拟键盘缩放', 100, COM.Number, [25, 5, 500])
@ -175,8 +160,6 @@ mainSetting
'action',
'操作设置',
new MotaSetting()
.register('autoSkill', '自动切换技能', true, COM.Boolean)
.register('fixed', '定点查看', true, COM.Boolean)
.register('hotkey', '快捷键', false, COM.HotkeySetting)
.setDisplayFunc('hotkey', () => '')
)
@ -192,25 +175,12 @@ mainSetting
.register(
'utils',
'系统设置',
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)
new MotaSetting().register('autoScale', '自动放缩', true, COM.Boolean)
)
.register(
'ui',
'ui设置',
new MotaSetting()
.register('mapScale', '小地图缩放', 100, COM.Number, [50, 1000, 50])
.setDisplayFunc('mapScale', value => `${value}%`)
.register('mapLazy', '小地图懒更新', false, COM.Boolean)
.register(
'bookScale',
'怪物手册缩放',
@ -219,9 +189,6 @@ mainSetting
[10, 500, 10]
)
.setDisplayFunc('bookScale', value => `${value}%`)
.register('danmaku', '显示弹幕', true, COM.Boolean)
.register('danmakuSpeed', '弹幕速度', 60, COM.Number, [10, 1000, 5])
.register('tips', '小贴士', true, COM.Boolean)
);
interface SettingTextData {
@ -233,26 +200,13 @@ mainSetting
.setDescription('audio.bgmVolume', `背景音乐的音量`)
.setDescription('audio.soundEnabled', `是否开启音效`)
.setDescription('audio.soundVolume', `音效的音量`)
.setDescription('ui.mapScale', `楼传小地图的缩放,百分比格式`)
.setDescription(
'ui.mapLazy',
`是否启用小地图懒更新模式,此模式下剩余怪物数量不会实时更新而变成切换地图后更新,打开小地图时出现卡顿可以尝试开启此设置`
)
.setDescription(
'ui.bookScale',
`怪物手册界面中每个怪物框体的高度缩放,最小值限定为 20% 屏幕高度`
)
.setDescription('ui.danmaku', '是否显示弹幕')
.setDescription('ui.danmakuSpeed', '弹幕速度,刷新或开关弹幕显示后起效')
.setDescription('ui.tips', `是否在游戏画面右上角常亮显示小贴士`)
.setDescription('screen.fontSizeStatus', `修改状态栏的字体大小`)
.setDescription(
'screen.blur',
'打开任意ui界面时是否有背景虚化效果移动端打开后可能会有掉帧或者发热现象。关闭ui后生效'
)
.setDescription(
'fx.portalParticle',
'是否启用苍蓝之殿的传送门粒子特效,启用后可能对性能及设备发热有所影响'
);
function setFontSize() {
@ -286,24 +240,17 @@ export function createSetting() {
loading.once('coreInit', () => {
mainSetting.reset({
'screen.fullscreen': !!document.fullscreenElement,
'screen.halo': !!storage.getValue('screen.showHalo', true),
'screen.scale': storage.getValue('screen.scale', 100),
'screen.itemDetail': !!storage.getValue('screen.itemDetail', true),
'screen.heroDetail': !!storage.getValue('screen.heroDetail', false),
'screen.transition': !!storage.getValue('screen.transition', false),
'screen.fontSize': storage.getValue(
'screen.fontSize',
isMobile ? 9 : 16
),
'screen.smoothView': !!storage.getValue('screen.smoothView', true),
'screen.criticalGem': !!storage.getValue(
'screen.criticalGem',
false
),
'screen.fontSizeStatus': storage.getValue(
'screen.fontSizeStatus',
100
),
'action.fixed': !!storage.getValue('action.fixed', true),
'audio.bgmEnabled': !!storage.getValue('audio.bgmEnabled', true),
'audio.bgmVolume': storage.getValue('audio.bgmVolume', 80),
'audio.soundEnabled': !!storage.getValue(
@ -311,26 +258,11 @@ export function createSetting() {
true
),
'audio.soundVolume': storage.getValue('audio.soundVolume', 80),
'utils.betterLoad': !!storage.getValue('utils.betterLoad', 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',
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)
)
});
});
}

View File

@ -179,6 +179,7 @@ export function getMapData(
const floor = core.floors[now];
const change = floor.changeFloor;
for (const [loc, ev] of Object.entries(change)) {
if (!ev) continue;
const target = ev.floorId as FloorIds;
if (target.startsWith(':')) continue;
const [x, y] = loc.split(',').map(v => parseInt(v));

View File

@ -46,9 +46,7 @@ window.addEventListener('resize', () => {
checkMobile();
sleep(2000).then(() => {
if (!isMobile) {
tip('info', `注意,不推荐使用浏览器的缩放功能,使用游戏内的缩放即可`);
}
tip('info', `2.B 暂不支持竖屏游玩,手机端请考虑横屏游玩`);
});
function checkMobile() {

View File

@ -295,16 +295,12 @@ export function ensureArray<T>(arr: T): T extends any[] ? T : T[] {
}
export async function triggerFullscreen(full: boolean) {
const { maxGameScale } = Mota.require('@user/data-utils');
if (!!document.fullscreenElement && !full) {
if (window.jsinterface) {
window.jsinterface.requestPortrait();
return;
}
await document.exitFullscreen();
requestAnimationFrame(() => {
maxGameScale(1);
});
}
if (full && !document.fullscreenElement) {
if (window.jsinterface) {
@ -312,9 +308,6 @@ export async function triggerFullscreen(full: boolean) {
return;
}
await document.body.requestFullscreen();
requestAnimationFrame(() => {
maxGameScale();
});
}
}
@ -349,10 +342,10 @@ export function formatSize(size: number) {
return size < 1 << 10
? `${size.toFixed(2)}B`
: size < 1 << 20
? `${(size / (1 << 10)).toFixed(2)}KB`
: size < 1 << 30
? `${(size / (1 << 20)).toFixed(2)}MB`
: `${(size / (1 << 30)).toFixed(2)}GB`;
? `${(size / (1 << 10)).toFixed(2)}KB`
: size < 1 << 30
? `${(size / (1 << 20)).toFixed(2)}MB`
: `${(size / (1 << 30)).toFixed(2)}GB`;
}
export function getIconHeight(icon: AllIds | 'hero') {

View File

@ -435,10 +435,9 @@ export abstract class RenderItem<E extends ERenderItemEvent = ERenderItemEvent>
ctx.globalAlpha = this.alpha;
ctx.globalCompositeOperation = this.composite;
if (this.enableCache) {
const { width, height, ctx } = this.cache;
const { width, height } = this.cache;
if (this.cacheDirty) {
const { canvas } = this.cache;
ctx.clearRect(0, 0, canvas.width, canvas.height);
this.cache.clear();
this.render(this.cache, tran);
this.cacheDirty = false;
}

View File

@ -562,12 +562,6 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = {
},
"_data": "状态栏显示项"
},
"extendToolbar": {
"_leaf": true,
"_type": "checkbox",
"_docs": "横屏底部工具栏",
"_data": "在横屏状态下是否将工具栏挪动到游戏画布下方,从而完全解放状态栏空间"
},
"flyNearStair": {
"_leaf": true,
"_type": "checkbox",
@ -586,12 +580,6 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = {
"_docs": "首次道具进行提示",
"_data": "首次获得道具是否提示"
},
"equipboxButton": {
"_leaf": true,
"_type": "checkbox",
"_docs": "状态栏装备按钮",
"_data": "状态栏的装备按钮。若此项为true则将状态栏中的楼层转换器按钮换为装备栏按钮"
},
"enableAddPoint": {
"_leaf": true,
"_type": "checkbox",
@ -610,30 +598,6 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = {
"_docs": "夹击不超伤害值",
"_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": {
"_leaf": true,
"_type": "checkbox",
@ -676,12 +640,6 @@ var data_comment_c456ea59_6018_45ef_8bcc_211a24c627dc = {
"_docs": "虚化前景层",
"_data": "是否虚化前景层;如果此项开启,则在游戏中事件层有东西(如宝石等)时虚化前景层。"
},
"autoScale": {
"_leaf": true,
"_type": "checkbox",
"_docs": "自动缩放最大化",
"_data": "是否自动缩放最大化,关闭后不再最大化"
},
}
}
}

View File

@ -583,13 +583,6 @@ actions.prototype._sys_ondown = function (x, y, px, py) {
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.push(pos);

View File

@ -2120,10 +2120,6 @@ control.prototype._doSL_load_afterGet = function (id, data) {
core.myconfirm('此存档可能存在风险,你想要播放录像么?', _replay);
return;
}
// 追逐战
Mota.r(() => {
Mota.require('@user/legacy-plugin-client').end(false);
});
// core.ui.closePanel();
core.loadData(data, function () {
core.removeFlag('__fromLoad__');
@ -2993,16 +2989,7 @@ control.prototype.checkBgm = function () {
};
///// 设置屏幕放缩 //////
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();
};
control.prototype.setDisplayScale = function (delta) {};
// ------ 状态栏,工具栏等相关 ------ //
@ -3085,33 +3072,33 @@ control.prototype.resize = function () {
const width = window.innerWidth;
const height = window.innerHeight;
if (window.innerWidth >= 600) {
// 横屏
core.domStyle.isVertical = false;
core.domStyle.availableScale = [];
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) {
if (v < maxScale) {
core.domStyle.availableScale.push(v);
}
});
if (!core.domStyle.availableScale.includes(core.domStyle.scale)) {
core.domStyle.scale = 1;
}
} else {
// 竖屏
core.domStyle.isVertical = true;
core.domStyle.scale = window.innerWidth / core._PX_;
core.domStyle.availableScale = [];
}
// if (window.innerWidth >= 600) {
// // 横屏
// core.domStyle.isVertical = false;
// core.domStyle.availableScale = [];
// 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) {
// if (v < maxScale) {
// core.domStyle.availableScale.push(v);
// }
// });
// if (!core.domStyle.availableScale.includes(core.domStyle.scale)) {
// core.domStyle.scale = 1;
// }
// } else {
// // 竖屏
// core.domStyle.isVertical = true;
// core.domStyle.scale = window.innerWidth / core._PX_;
// core.domStyle.availableScale = [];
// }
if (!core.domStyle.isVertical) {
const height = window.innerHeight;
const width = window.innerWidth;
const maxScale = Math.min(height / core._PY_, width / core._PX_);
const target = Number((Math.floor(maxScale * 4) / 4).toFixed(2));
core.domStyle.scale = target - 0.25;
}
// if (!core.domStyle.isVertical) {
// const height = window.innerHeight;
// const width = window.innerWidth;
// const maxScale = Math.min(height / core._PY_, width / core._PX_);
// const target = Number((Math.floor(maxScale * 4) / 4).toFixed(2));
// core.domStyle.scale = target - 0.25;
// }
this._doResize({});
this.setToolbarButton();

View File

@ -7,14 +7,14 @@
'use strict';
function core() {
this._WIDTH_ = 15;
this._HEIGHT_ = 15;
this._WIDTH_ = 13;
this._HEIGHT_ = 13;
this._PX_ = this._WIDTH_ * 32;
this._PY_ = this._HEIGHT_ * 32;
this._HALF_WIDTH_ = Math.floor(this._WIDTH_ / 2);
this._HALF_HEIGHT_ = Math.floor(this._HEIGHT_ / 2);
this.__SIZE__ = main.mode == 'editor' ? 15 : this._HEIGHT_;
this.__SIZE__ = main.mode == 'editor' ? 13 : this._HEIGHT_;
this.__PIXELS__ = this.__SIZE__ * 32;
this.__HALF_SIZE__ = Math.floor(this.__SIZE__ / 2);
this.material = {

View File

@ -26,7 +26,6 @@ events.prototype.startGame = function (hard, seed, route, callback) {
hard = hard || '';
if (main.mode != 'play') return;
Mota.require('@user/data-state').resetSkillLevel();
// 无动画的开始游戏
if (core.flags.startUsingCanvas || route != null) {

View File

@ -806,8 +806,6 @@ maps.prototype.generateMovableArray = function (floorId) {
for (var x = 0; x < width; ++x) {
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;
const half = core._HALF_WIDTH_;
var startX = v2
@ -829,24 +827,19 @@ maps.prototype.generateMovableArray = function (floorId) {
)
: height;
if (isLoop) {
startX = 0;
endX = core.status.maps[floorId].width;
}
for (var x = startX; x < endX; x++) {
for (var y = startY; y < endY; y++) {
array[x][y] = ['left', 'down', 'up', 'right'].filter(function (
direction
) {
return core.maps._canMoveHero_checkPoint(
x,
y,
direction,
floorId,
arrays
);
});
array[x][y] = ['left', 'down', 'up', 'right'].filter(
function (direction) {
return core.maps._canMoveHero_checkPoint(
x,
y,
direction,
floorId,
arrays
);
}
);
}
}
return array;
@ -888,11 +881,6 @@ maps.prototype._canMoveHero_checkPoint = function (
var nx = x + core.utils.scan[direction].x,
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 (
nx < 0 ||
ny < 0 ||
@ -1128,9 +1116,6 @@ maps.prototype.automaticRoute = function (destX, destY) {
// BFS找寻最短路径
var route = this._automaticRoute_bfs(startX, startY, destX, destY);
if (route[destX + ',' + destY] == null) return [];
const floor = core.status.thisMap;
const loopMaps = Mota.require('@user/data-state').MiscData.loopMaps;
// 路径数组转换
var ans = [],
nowX = destX,
@ -1140,10 +1125,6 @@ maps.prototype.automaticRoute = function (destX, destY) {
ans.push({ direction: dir, x: nowX, y: nowY });
nowX -= core.utils.scan[dir].x;
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();
return ans;
@ -1162,7 +1143,6 @@ maps.prototype._automaticRoute_bfs = function (startX, startY, destX, destY) {
queue.queue({ depth: 0, x: startX, y: startY });
var blocks = core.getMapBlocksObj();
const floor = core.status.thisMap;
const loopMaps = Mota.require('@user/data-state').MiscData.loopMaps;
while (queue.length != 0) {
var curr = queue.dequeue(),
@ -1173,22 +1153,16 @@ maps.prototype._automaticRoute_bfs = function (startX, startY, destX, destY) {
if (!core.inArray(canMoveArray[nowX][nowY], direction)) continue;
var nx = nowX + core.utils.scan[direction].x;
var ny = nowY + core.utils.scan[direction].y;
if (loopMaps.has(core.status.floorId)) {
if (nx < 0) nx = floor.width - 1;
if (nx >= floor.width) nx = 0;
if (route[nx + ',' + ny] || ny < 0 || ny >= floor.height) {
continue;
}
} else {
if (
nx < 0 ||
nx >= core.bigmap.width ||
ny < 0 ||
ny >= core.bigmap.height ||
route[nx + ',' + ny] != null
)
continue;
}
if (
nx < 0 ||
nx >= core.bigmap.width ||
ny < 0 ||
ny >= core.bigmap.height ||
route[nx + ',' + ny] != null
)
continue;
// 重点
if (nx == destX && ny == destY) {
route[nx + ',' + ny] = direction;
@ -2669,22 +2643,25 @@ maps.prototype._drawThumbnail_realDrawTempCanvas = function (
options.heroIcon =
options.heroIcon || core.status.hero.image || 'hero.png';
options.heroIcon = core.getMappedName(options.heroIcon);
var icon = core.material.icons.hero[options.heroLoc.direction];
var height = core.material.images.images[options.heroIcon].height / 4;
var width =
(core.material.images.images[options.heroIcon].width || 128) / 4;
core.drawImage(
options.ctx,
core.material.images.images[options.heroIcon],
icon.stop * width,
icon.loc * height,
width,
height,
32 * options.heroLoc.x + 32 - width,
32 * options.heroLoc.y + 32 - height,
width,
height
);
const image = core.material.images.images[options.heroIcon];
if (image) {
var icon = core.material.icons.hero[options.heroLoc.direction];
var height =
core.material.images.images[options.heroIcon].height / 4;
var width = (image.width || 128) / 4;
core.drawImage(
options.ctx,
core.material.images.images[options.heroIcon],
icon.stop * width,
icon.loc * height,
width,
height,
32 * options.heroLoc.x + 32 - width,
32 * options.heroLoc.y + 32 - height,
width,
height
);
}
}
// 缩略图:前景
this.drawFg(floorId, options);
@ -3891,7 +3868,14 @@ maps.prototype._moveBlock_doMove = function (
_run();
} else
core.maps._moveBlock_moving(blockInfo, canvases, moveInfo);
} else core.maps._moveJumpBlock_finished(blockInfo, canvases, moveInfo, animate, cb);
} else
core.maps._moveJumpBlock_finished(
blockInfo,
canvases,
moveInfo,
animate,
cb
);
}, moveInfo.per_time);
core.animateFrame.lastAsyncId = animate;

View File

@ -297,21 +297,18 @@ main.prototype.loadAsync = async function (mode, callback) {
if (main.mode === 'editor') return;
// 自动放缩最大化
let auto = Mota.require('@motajs/legacy-ui').mainSetting.getValue(
'autoScale',
true
);
const mainSetting = Mota.require('@motajs/legacy-ui').mainSetting;
const auto = mainSetting.getValue('utils.autoScale', true);
if (auto && !core.domStyle.isVertical) {
// 暂时不考虑手机端
if (auto) {
const height = window.innerHeight;
const width = window.innerWidth;
const maxScale = Math.min(height / core._PY_, width / core._PX_);
const target = Number((Math.floor(maxScale * 4) / 4).toFixed(2));
core.domStyle.scale = target - 0.25;
}
if (core.domStyle.isVertical) {
core.domStyle.scale = window.innerWidth / core._PX_;
mainSetting.setValue('screen.scale', Math.round(target * 100) - 25);
}
Mota.r(() => {
Mota.require('@user/client-modules').mainRenderer.setScale(
core.domStyle.scale

View File

@ -1,85 +0,0 @@
<!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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 MiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -3,170 +3,22 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
"main": {
"floorIds": [
"empty",
"MT0",
"MT1",
"MT2",
"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"
]
"sample0",
"sample1",
"sample2",
"MT0"
],
"floorPartitions": [],
"images": [
"IQ.png",
"arrow.png",
"atk.png",
"bg.webp",
"boom.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"
"bear.png",
"bg.jpg",
"brave.png",
"dragon.png",
"hero.png",
"winskin.png"
],
"tilesets": [
"magictower.png",
"043-Cave01.png",
"004-Mountain01.png",
"Map-Tower01.png",
"Caverna1.png",
"map-tower.png",
"winter1.png",
"snowTown.png",
"room.png"
"magictower.png"
],
"animates": [
"amazed",
@ -193,27 +45,7 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
"zone"
],
"bgms": [
"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"
"bgm.opus"
],
"sounds": [
"008-System08.opus",
@ -257,10 +89,7 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
"tree.opus",
"zone.opus"
],
"fonts": [
"normal",
"FiraCode"
],
"fonts": [],
"nameMap": {
"确定": "confirm.opus",
"取消": "cancel.opus",
@ -359,7 +188,7 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
"首饰",
"首饰"
],
"startBgm": "title.opus",
"startBgm": "bgm.opus",
"styles": {
"floorChangingStyle": " ",
"statusBarColor": [
@ -385,14 +214,14 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
"splitImages": []
},
"firstData": {
"title": "人类:开天辟地",
"name": "HumanBreak",
"version": "Ver 2.7.3.1",
"floorId": "MT0",
"title": "魔塔样板",
"name": "template",
"version": "Ver 2.B",
"floorId": "sample0",
"hero": {
"image": "hero1.png",
"image": "hero.png",
"animate": false,
"name": "原始人",
"name": "阳光",
"lv": 1,
"hpmax": 0,
"hp": 500,
@ -411,8 +240,8 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
},
"loc": {
"direction": "up",
"x": 7,
"y": 13
"x": 6,
"y": 10
},
"flags": {},
"followers": [],
@ -550,100 +379,12 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
],
"startText": [
{
"type": "hideStatusBar"
"type": "text",
"text": "欢迎使用古祠制作的 2.B 样板,本样板主要针对渲染系统进行了重构,现在我们有了更加方便强大的渲染系统,也对部分相关事件进行了重置!"
},
{
"type": "setText",
"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"
"type": "text",
"text": "这里是开场剧情,可以在编辑器全塔属性中修改,试着修改一下吧!"
}
],
"shops": [
@ -796,7 +537,7 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
"counterAttack": 0.1,
"purify": 3,
"hatred": 2,
"animateSpeed": 277.7778,
"animateSpeed": 400,
"statusCanvasRowsOnMobile": 3,
"floorChangeTime": 200,
"moveSpeed": null
@ -813,27 +554,16 @@ var data_a1e2fb4a_e986_4524_b0da_9b7ba7c0874d =
],
"flyNearStair": false,
"flyRecordPosition": true,
"steelDoorWithoutKey": true,
"itemFirstText": false,
"equipboxButton": false,
"enableAddPoint": false,
"enableNegativeDamage": true,
"betweenAttackMax": false,
"useLoop": true,
"startUsingCanvas": false,
"statusCanvas": true,
"displayEnemyDamage": true,
"displayCritical": true,
"displayExtraDamage": true,
"enableNegativeDamage": false,
"betweenAttackMax": true,
"enableGentleClick": true,
"ignoreChangeFloor": true,
"canGoDeadZone": false,
"enableMoveDirectly": true,
"enableRouteFolding": true,
"disableShopOnDamage": false,
"blurFg": true,
"extendToolbar": false,
"enableEnemyPoint": null,
"autoScale": true
"blurFg": true
}
}

View File

@ -1,219 +1,84 @@
var enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80 =
{
"greenSlime": {"name":"绿头怪","hp":100,"atk":11,"def":3,"money":0,"exp":1,"point":0,"special":[],"description":"别小看这些家伙,虽然他们只是一种极其低级的怪物,低级到普通人用手都可以打死,但数量是他们的优势。"},
"redSlime": {"name":"红头怪","hp":120,"atk":16,"def":6,"money":0,"exp":2,"point":0,"special":[],"value":10,"description":"即使是最弱的怪物,也有进化的时候,对吧?据说,红头怪便是绿头怪进化形成的。"},
"blackSlime": {"name":"青头怪","hp":170,"atk":20,"def":8,"money":0,"exp":3,"point":0,"special":[],"description":"看,这就是最弱的怪物进化出的最强的怪物之一了。他们弱吗?"},
"slimelord": {"name":"粘液王","hp":200,"atk":58,"def":24,"money":0,"exp":8,"point":0,"special":[],"description":"看上去黏糊糊的,实际也确实黏糊糊的,据说是史莱姆族的长老级人物,拥有不俗的实力。"},
"bat": {"name":"小蝙蝠","hp":60,"atk":15,"def":0,"money":0,"exp":2,"point":0,"special":[4],"description":"经常出现在山洞中,再平常不过了。但是这次它却有了攻击性。"},
"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":1200,"atk":260,"def":110,"money":1,"exp":32,"point":0,"special":[5],"description":"恐惧?或许他们并不知道恐惧是什么,他们不会害怕任何东西,即使这个东西能够威胁到自己的生命。"},
"vampire": {"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":750,"atk":200,"def":50,"money":0,"exp":21,"point":0,"special":[1],"crit":1000,"description":"机器人也会有领袖吗?这还真是第一次听说。"},
"zombie": {"name":"兽人","hp":150,"atk":43,"def":14,"money":0,"exp":6,"point":0,"special":[],"description":"野兽嘛,在远古时期,再正常不过了。"},
"zombieKnight": {"name":"兽人武士","hp":480,"atk":62,"def":30,"money":0,"exp":15,"point":0,"special":[],"description":"他们总是认为,要变得强大,杀掉其他野兽,自己才能存活下来。"},
"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":[2]},
"greenSlime": {"name":"绿头怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
"redSlime": {"name":"红头怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[16,18],"value":10},
"blackSlime": {"name":"青头怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
"slimelord": {"name":"怪王","hp":100,"atk":120,"def":0,"money":10,"exp":0,"point":0,"special":[1,9]},
"bat": {"name":"小蝙蝠","hp":100,"atk":120,"def":0,"money":2,"exp":0,"point":0,"special":[1]},
"bigBat": {"name":"大蝙蝠","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
"redBat": {"name":"红蝙蝠","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
"vampire": {"name":"冥灵魔王","hp":888,"atk":888,"def":888,"money":888,"exp":888,"point":0,"special":[6],"n":8},
"skeleton": {"name":"骷髅人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
"skeletonCaptain": {"name":"骷髅队长","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
"zombie": {"name":"兽人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
"zombieKnight": {"name":"兽人武士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
"rock": {"name":"石头人","hp":50,"atk":50,"def":0,"money":3,"exp":0,"point":0,"special":[3]},
"bluePriest": {"name":"初级法师","hp":100,"atk":120,"def":0,"money":3,"exp":0,"point":1,"special":[9]},
"redPriest": {"name":"高级法师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
"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":12000,"atk":6000,"def":4000,"money":4,"exp":300,"point":0,"special":[25],"value":200,"zoneSquare":true,"melt":30},
"swordsman": {"name":"野蛮剑士","hp":250,"atk":55,"def":27,"money":0,"exp":9,"point":0,"special":[15],"value":75,"description":"剑?这是什么东西?他们拿的只是比较锋利的骨头吧。"},
"brownWizard": {"name":"初级巫师","hp":100,"atk":120,"def":0,"money":16,"exp":0,"point":0,"special":[15],"value":100,"range":2},
"redWizard": {"name":"高级巫师","hp":1000,"atk":1200,"def":0,"money":160,"exp":0,"point":0,"special":[15],"value":200,"zoneSquare":true},
"swordsman": {"name":"双手剑士","hp":100,"atk":120,"def":0,"money":6,"exp":0,"point":0,"special":[4]},
"soldier": {"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":30000,"atk":9000,"def":3000,"money":5,"exp":600,"point":0,"special":[29],"specialHalo":[27],"iceCore":15,"haloRange":3,"fireCore":15},
"yellowKnight": {"name":"金骑士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
"redKnight": {"name":"红骑士","hp":500,"atk":200,"def":50,"money":0,"exp":0,"point":0,"special":[7]},
"darkKnight": {"name":"黑骑士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
"blueKnight": {"name":"蓝骑士","hp":40000,"atk":9000,"def":4000,"money":6,"exp":600,"point":0,"special":[4]},
"goldSlime": {"name":"黄头怪","hp":1000,"atk":50,"def":50,"money":0,"exp":18,"point":0,"special":[2]},
"poisonSkeleton": {"name":"紫骷髅","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
"poisonBat": {"name":"山间蝙蝠","hp":800,"atk":170,"def":50,"money":1,"exp":24,"point":0,"special":[5],"description":"山的高出,总会有一些恐怖的东西,就比如这只蝙蝠。"},
"skeletonPriest": {"name":"智慧骷髅","hp":4000,"atk":1200,"def":900,"money":1,"exp":75,"point":0,"special":[1,13],"value":20,"crit":500,"description":"人们说智慧可以做到任何事情,而这只骷髅却将可以变为了很容易。“我挥一挥法杖,智慧便会如泉般涌来。”,他说。"},
"blueKnight": {"name":"蓝骑士","hp":100,"atk":120,"def":0,"money":9,"exp":0,"point":0,"special":[8]},
"goldSlime": {"name":"黄头怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
"poisonSkeleton": {"name":"紫骷髅","hp":50,"atk":60,"def":70,"money":80,"exp":0,"point":0,"special":[13]},
"poisonBat": {"name":"紫蝙蝠","hp":100,"atk":120,"def":0,"money":14,"exp":0,"point":0,"special":[13]},
"skeletonPriest": {"name":"骷髅法师","hp":100,"atk":100,"def":0,"money":0,"exp":0,"point":0,"special":[18],"value":20},
"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":[]},
"demonPriest": {"name":"苍蓝法师","hp":20000,"atk":4000,"def":3000,"money":4,"exp":250,"point":0,"special":[13]},
"goldHornSlime": {"name":"尖角怪","hp":1500,"atk":366,"def":166,"money":1,"exp":35,"point":0,"special":[],"description":"一个奇怪的物种,长着两只角就了不起了吗?或许还真是!"},
"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":[]},
"redSwordsman": {"name":"山间盗贼","hp":1000,"atk":175,"def":40,"money":1,"exp":24,"point":0,"special":[4],"n":8,"description":"即使是现代法治社会,也总会有人去抢夺别人的东西,更何况远古时期呢?"},
"demonPriest": {"name":"魔神法师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
"goldHornSlime": {"name":"金角怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
"silverSlime": {"name":"银头怪","hp":100,"atk":120,"def":0,"money":15,"exp":0,"point":0,"special":[14]},
"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":[]},
"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":[]},
"octopus": {"name":"血影","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"bigImage":null},
"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":[]},
"elemental": {"name":"元素生物","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
"steelGuard": {"name":"铁守卫","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[18],"value":20},
"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":"弱小,无助,哀嚎,这就是残酷的现实。哪怕你身处极寒之中,也难有人对你伸出援手。人类总会帮助他人,但在这绝望的环境下,人类的本性便暴露无遗。这一个个精致却又无情的骷髅,便是那些在极寒之中死去的冤魂。"},
"steelGuard": {"name":"铁守卫","hp":50,"atk":50,"def":50,"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":[]},
"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":[]},
"skeletonWarrior": {"name":"骷髅士兵","hp":500,"atk":100,"def":20,"money":0,"exp":12,"point":0,"special":[1],"crit":500,"description":"看来未来的机器人并不满足与赤手空拳,他们也拿上了武器。"},
"whiteSlimeman": {"name":"水银史莱姆人","hp":750,"atk":100,"def":45,"money":0,"exp":20,"point":0,"special":[4],"description":"汞?这听起来不像是一个远古时期应该存在的名字,但是把它拆分开,叫做水银,是不是可爱了一些?"},
"slimeman": {"name":"莱姆人","hp":125,"atk":30,"def":10,"money":0,"exp":4,"point":0,"special":[4],"atkValue":2,"defValue":3,"description":"又有谁不能保证史莱姆也能进化成人型生物呢?"},
"yellowGateKeeper": {"name":"神秘卫兵","hp":375,"atk":200,"def":15,"money":1,"exp":25,"point":0,"special":[1],"crit":5000,"description":"神秘卫兵。确实很神秘,他们从不说话,只知道看着你,当你攻击他们的时候,他们会给你致命的反击。"},
"blueGateKeeper": {"name":"神秘雕像","hp":1000,"atk":275,"def":120,"money":1,"exp":38,"point":0,"special":[1],"crit":1000,"description":"大概,或许,跟神秘卫兵是同一类东西?"},
"redGateKeeper": {"name":"勇气卫兵","hp":1000,"atk":450,"def":250,"money":1,"exp":30,"point":0,"special":[1],"crit":2000,"description":"没有人知道这些卫兵是什么。他们有红通通的外表,一颗怀揣着勇气的心。但是,勇气这东西,并不是越多越好,为什么?跟他打一架,他的下场便是勇气过多的下场。"},
"skeletonWarrior": {"name":"骷髅士兵","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
"whiteSlimeman": {"name":"水银战士","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
"slimeman": {"name":"影子战士","hp":100,"atk":0,"def":0,"money":11,"exp":0,"point":0,"special":[9],"atkValue":2,"defValue":3},
"yellowGateKeeper": {"name":"初级卫兵","hp":100,"atk":120,"def":0,"money":10,"exp":0,"point":0,"special":[]},
"blueGateKeeper": {"name":"中级卫兵","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
"redGateKeeper": {"name":"高级卫兵","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
"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":[]},
"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":[]},
"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":[],"description":"法杖,人们总觉得这种东西只应该出现在虚拟的世界中。可是,智慧却成功将这件事变为了现实,而产物便是这只骷髅巫师。"},
"ironRock": {"name":"山间巨石","hp":750,"atk":150,"def":0,"money":0,"exp":20,"point":0,"special":[3],"description":"恐怖的东西,除了那只蝙蝠,还有...这个巨石。"},
"grayRock": {"name":"林间巨石","hp":100,"atk":60,"def":0,"money":0,"exp":12,"point":0,"special":[3],"description":"貌似比山洞里面的那些家伙硬了一些?哼,那又能有什么用呢?"},
"dragon": {"name":"魔龙","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"bigImage":null},
"skeletonKnight": {"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":[]},
"ironRock": {"name":"铁面人","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
"grayRock": {"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":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},
"evilPrincess": {"name":"痛苦魔女","hp":1000,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[10]},
"blademaster": {"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":[]},
"bowman": {"name":"猎人","hp":500,"atk":100,"def":50,"money":0,"exp":16,"point":0,"special":[24],"value":75,"description":"没人知道这些人怎么做出的弓,也没人知道他们怎么收集的这么多剑。而其他人唯一能做的事,便是远离他们。"},
"liteBowman": {"name":"山间猎手","hp":1200,"atk":200,"def":60,"money":1,"exp":27,"point":0,"special":[24],"description":"这箭,或许就是那些败于他弓下的那些不知好歹的莽夫的骨头吧。或许,绕开他的视野才是躲避他的攻击的最好办法。"},
"crimsonZombie": {"name":"勇气之兽","hp":1800,"atk":2000,"def":-100,"money":1,"exp":35,"point":0,"special":[],"description":"没人会愿意跟这些野兽一起吧?至少我是不想。上天赋予的勇气,却让他们更加渴望鲜血,这不是很可悲吗?"},
"watcherSlime": {"name":"邪眼史莱姆","hp":5000,"atk":1200,"def":600,"money":1,"exp":50,"point":0,"special":[17],"description":"成群结队地出现在森林中,看遍百花齐放,经历万物凋零。他们守在这森林中,将那些企图突破这里的人置于死地。"},
"mutantSlimeman": {"name":"变异史莱姆人","hp":350,"atk":70,"def":27,"money":0,"exp":13,"point":0,"special":[],"description":"据说,史莱姆人也会基因突变,这样就产生了这种变异史莱姆人。"},
"devilKnight": {"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":"“穿梭于寒风里,行走在锋芒中”,寒霜蝙蝠将这句话运用到了极致。别看那小小的身体,它足以将你拆的七零八落。在它面前,任何小把戏都会被它看得一清二楚。它那凶恶的眼神,是否在哪里见过呢?"},
"bowman": {"name":"初级弓兵","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
"watcherSlime": {"name":"邪眼怪","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
"devilKnight": {"name":"恶灵骑士","hp":150,"atk":100,"def":50,"money":0,"exp":0,"point":0,"special":[1,5,7,8]},
"grayPriest": {"name":"混沌法师","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
"greenGateKeeper": {"name":"卫兵队长","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
"ghostSoldier": {"name":"冥队长","hp":200,"atk":100,"def":50,"money":0,"exp":0,"point":0,"special":[8]},
"frostBat": {"name":"寒蝙蝠","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
"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":[]},
"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":[]},
"blueKing": {"name":"白衣武士","hp":100,"atk":120,"def":0,"money":17,"exp":0,"point":0,"special":[16]},
"E368": {"name":"绿头武装怪","hp":400,"atk":75,"def":30,"money":0,"exp":14,"point":0,"special":[]},
"E369": {"name":"红头武装怪","hp":450,"atk":85,"def":35,"money":0,"exp":17,"point":0,"special":[]},
"E370": {"name":"青头武装怪","hp":600,"atk":100,"def":50,"money":0,"exp":20,"point":0,"special":[1],"crit":400},
"E371": {"name":"武装怪王","hp":1500,"atk":230,"def":80,"money":1,"exp":30,"point":0,"special":[1],"crit":750},
"E372": {"name":"高级绿头怪","hp":280,"atk":66,"def":33,"money":0,"exp":12,"point":0,"special":[]},
"E373": {"name":"高级红头怪","hp":333,"atk":77,"def":33,"money":0,"exp":15,"point":0,"special":[1],"crit":200},
"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":[]}
"keiskeiFairy": {"name":"铃兰花妖","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
"tulipFairy": {"name":"郁金香花妖","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
"purpleBowman": {"name":"高级弓兵","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
"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},
"bearLeft": {"name":"熊出没","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"faceIds":{"down":"bearDown","left":"bearLeft","right":"bearRight","up":"bearUp"}},
"bearRight": {"name":"熊出没","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"faceIds":{"down":"bearDown","left":"bearLeft","right":"bearRight","up":"bearUp"}},
"bearUp": {"name":"熊出没","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[],"faceIds":{"down":"bearDown","left":"bearLeft","right":"bearRight","up":"bearUp"}}
}

View File

@ -1,113 +1,36 @@
main.floors.MT0=
{
"floorId": "MT0",
"title": "洞穴",
"title": "主塔 0 层",
"name": "0",
"canFlyTo": true,
"canFlyFrom": true,
"canUseQuickShop": true,
"cannotViewMap": false,
"defaultGround": "T331",
"defaultGround": "ground",
"images": [],
"ratio": 1,
"map": [
[20049,20049,20049,20049,20049,20049,20050, 91,20048,20049,20049,20049,20049,20049,20043],
[20057,20057,20057,20057,20057,20057,20058, 0,20056,20057,20057,20057,20057,20057,20040],
[20065,20065,20065,20065,20065,20065,20074, 0,20064,20065,20065,20065,20065,20065,20040],
[141,141,141,141, 0, 0, 0, 0, 0, 0, 0,141, 33, 33,20040],
[141, 34, 34,141, 0,141, 0, 0, 0,141, 0,494,482,482,20040],
[141, 33, 33,492, 0,141, 0, 0, 0,141, 0,141, 33, 33,20040],
[141, 34, 34,141, 0,141, 0, 0, 0,141, 0,141,141,141,20040],
[141,141,141,141, 92, 0, 0,141, 0, 0, 0,141, 33, 33,20040],
[141, 34, 34,141, 0,141, 0, 0, 0,141, 0,494,482,482,20040],
[141, 33, 33,492, 0,141, 0,642, 0,141, 0,141, 33, 33,20040],
[141, 34, 34,141, 0,141, 45,559, 46,141, 0,141,141,141,20040],
[141,141,141,141, 0, 0,558, 0,560, 0, 0,141, 33, 33,20040],
[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]
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
],
"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}"
}
],
"firstArrive": [],
"parallelDo": "",
"events": {
"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
]
}
},
"events": {},
"changeFloor": {},
"afterBattle": {},
"afterGetItem": {
"6,11": [
"这个可以查看bgm也可以设置bgm也可以清空设置的bgm"
],
"8,11": [
"请仔细阅读这个道具内的说明",
"注意虽然内容很多但是大部分都是“无用”信息例如对那些ui的说明基本上打开ui后就能看出来不同区域的功能的百科全书的说明基本只是对一些细节进行了说明。因此一般来说是不需要非常认真地读关于ui的信息的。",
"而对于那些新的内容,例如怪物标记等,可能需要阅读一下。",
"这里说一个非常重要的一点那就是本塔中几乎所有ui都是可以滚动的尝试用滚轮或者手指拖动进行滚动包括状态栏"
],
"7,10": [
"里面包含了所有游戏的设置,请仔细查看设置"
]
},
"afterGetItem": {},
"afterOpenDoor": {},
"cannotMove": {},
"bgmap": [
@ -116,16 +39,7 @@ main.floors.MT0=
"fgmap": [
],
"width": 15,
"height": 15,
"autoEvent": {},
"bgm": "cave.opus",
"beforeBattle": {},
"bg2map": [
],
"fg2map": [
],
"cannotMoveIn": {}
"width": 13,
"height": 13,
"autoEvent": {}
}

View File

@ -1,105 +0,0 @@
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": [
]
}

View File

@ -1,76 +0,0 @@
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": [
]
}

View File

@ -1,84 +0,0 @@
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": [
]
}

View File

@ -1,156 +0,0 @@
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": [
]
}

View File

@ -1,80 +0,0 @@
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": [
]
}

View File

@ -1,439 +0,0 @@
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": [
]
}

View File

@ -1,145 +0,0 @@
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": [
]
}

View File

@ -1,491 +0,0 @@
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": {}
}

View File

@ -1,120 +0,0 @@
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