diff --git a/public/libs/events.js b/public/libs/events.js index 0bbc104..7f3a5cc 100644 --- a/public/libs/events.js +++ b/public/libs/events.js @@ -370,7 +370,76 @@ events.prototype.doSystemEvent = function (type, data, callback) { ////// 触发(x,y)点的事件 ////// events.prototype.trigger = function (x, y, callback) { - // see src/plugin/game/loopMap.js + var _executeCallback = function () { + // 因为trigger之后还有可能触发其他同步脚本(比如阻激夹域检测) + // 所以这里强制callback被异步触发 + if (callback) { + setTimeout(callback, 1); // +1是为了录像检测系统 + } + return; + }; + if (core.status.gameOver) return _executeCallback(); + if (core.status.event.id == 'action') { + core.insertAction( + { + type: 'function', + function: + 'function () { core.events._trigger_inAction(' + + x + + ',' + + y + + '); }', + async: true + }, + void 0, + void 0, + void 0, + true + ); + return _executeCallback(); + } + if (core.status.event.id) return _executeCallback(); + + let block = core.getBlock(x, y); + + if (block == null) return _executeCallback(); + + // 执行该点的脚本 + if (block.event.script) { + core.clearRouteFolding(); + try { + eval(block.event.script); + } catch (ee) { + console.error(ee); + } + } + + // 碰触事件 + if (block.event.event) { + core.clearRouteFolding(); + core.insertAction(block.event.event, block.x, block.y); + // 不再执行该点的系统事件 + return _executeCallback(); + } + + if (block.event.trigger && block.event.trigger !== 'null') { + var noPass = block.event.noPass, + trigger = block.event.trigger; + if (noPass) core.clearAutomaticRouteNode(x, y); + + // 转换楼层能否穿透 + if ( + trigger == 'changeFloor' && + !noPass && + this._trigger_ignoreChangeFloor(block) && + !loop + ) + return _executeCallback(); + // @ts-ignore + core.status.automaticRoute.moveDirectly = false; + this.doSystemEvent(trigger, block); + } + return _executeCallback(); }; events.prototype._trigger_inAction = function (x, y) { @@ -687,7 +756,16 @@ events.prototype._getNextItem = function (direction, noRoute) { }; events.prototype._sys_changeFloor = function (data, callback) { - // see src/plugin/game/loopMap.js + data = data.event.data; + let heroLoc = {}; + if (data.loc) heroLoc = { x: data.loc[0], y: data.loc[1] }; + if (data.direction) heroLoc.direction = data.direction; + // @ts-ignore + if (core.status.event.id != 'action') core.status.event.id = null; + core.changeFloor(data.floorId, data.stair, heroLoc, data.time, function () { + core.replay(); + if (callback) callback(); + }); }; ////// 楼层切换 ////// diff --git a/public/libs/maps.js b/public/libs/maps.js index ddf6620..281ba7a 100644 --- a/public/libs/maps.js +++ b/public/libs/maps.js @@ -715,8 +715,59 @@ maps.prototype.getMapBlocksObj = function (floorId, noCache) { }; ////// 将背景前景层变成二维数组的形式 ////// + maps.prototype._getBgFgMapArray = function (name, floorId, noCache) { - // see src/plugin/game/loopMap.js + floorId = floorId || core.status.floorId; + if (!floorId) return []; + var width = core.floors[floorId].width; + var height = core.floors[floorId].height; + + // @ts-ignore + if (!noCache && core.status[name + 'maps'][floorId]) + // @ts-ignore + return core.status[name + 'maps'][floorId]; + + var arr = + main.mode == 'editor' && + // @ts-ignore + !(window.editor && editor.uievent && editor.uievent.isOpen) + ? // @ts-ignore + core.cloneArray(editor[name + 'map']) + : null; + if (arr == null) + // @ts-ignore + arr = core.cloneArray(core.floors[floorId][name + 'map'] || []); + + for (var y = 0; y < height; ++y) { + if (arr[y] == null) arr[y] = Array(width).fill(0); + } + // @ts-ignore + (core.getFlag('__' + name + 'v__', {})[floorId] || []).forEach( + // @ts-ignore + function (one) { + arr[one[1]][one[0]] = one[2] || 0; + } + ); + // @ts-ignore + (core.getFlag('__' + name + 'd__', {})[floorId] || []).forEach( + // @ts-ignore + function (one) { + arr[one[1]][one[0]] = 0; + } + ); + if (main.mode == 'editor') { + for (var x = 0; x < width; x++) { + for (var y = 0; y < height; y++) { + // @ts-ignore + arr[y][x] = arr[y][x].idnum || arr[y][x] || 0; + } + } + } + // @ts-ignore + if (core.status[name + 'maps']) + // @ts-ignore + core.status[name + 'maps'][floorId] = arr; + return arr; }; maps.prototype.getBgMapArray = function (floorId) { diff --git a/public/project/functions.js b/public/project/functions.js index 92152dd..f345a57 100644 --- a/public/project/functions.js +++ b/public/project/functions.js @@ -120,8 +120,6 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = { // 正在切换楼层过程中执行的操作;此函数的执行时间是“屏幕完全变黑“的那一刻 // floorId为要切换到的楼层ID;heroLoc表示勇士切换到的位置 - const { checkLoopMap } = Mota.Plugin.require('loopMap_g'); - // ---------- 此时还没有进行切换,当前floorId还是原来的 ---------- // var currentId = core.status.floorId || null; // 获得当前的floorId,可能为null var fromLoad = core.hasFlag('__fromLoad__'); // 是否是读档造成的切换 @@ -178,7 +176,6 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = { if (weather) core.setWeather(weather[0], weather[1]); else core.setWeather(); - checkLoopMap(); core.updateDamage(); // ...可以新增一些其他内容,比如创建个画布在右上角显示什么内容等等 @@ -385,8 +382,6 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = { // 2, 将楼层属性中的cannotMoveDirectly这个开关勾上,即禁止在该层楼使用瞬移。 // 3. 将flag:cannotMoveDirectly置为true,即可使用flag控制在某段剧情范围内禁止瞬移。 - const { checkLoopMap } = Mota.Plugin.require('loopMap_g'); - // 增加步数 core.status.hero.steps++; // 更新跟随者状态,并绘制 @@ -433,8 +428,6 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = { ); } - checkLoopMap(); - // 如需强行终止行走可以在这里条件判定: // core.stopAutomaticRoute(); Mota.require('var', 'hook').emit( diff --git a/src/core/render/item.ts b/src/core/render/item.ts index fa2d5f4..69408ba 100644 --- a/src/core/render/item.ts +++ b/src/core/render/item.ts @@ -109,6 +109,12 @@ interface IRenderTickerSupport { * @returns 是否删除成功,比如对应ticker不存在,就是删除失败 */ removeTicker(id: number, callEnd?: boolean): boolean; + + /** + * 检查是否包含一个委托函数 + * @param id 函数id + */ + hasTicker(id: number): boolean; } export interface ERenderItemEvent { @@ -373,9 +379,14 @@ export abstract class RenderItem RenderItem.ticker.remove(delegation.fn); window.clearTimeout(delegation.timeout); if (callEnd) delegation.endFn?.(); + RenderItem.tickerMap.delete(id); return true; } + hasTicker(id: number): boolean { + return RenderItem.tickerMap.has(id); + } + /** * 隐藏这个元素 */ diff --git a/src/core/render/preset/hero.ts b/src/core/render/preset/hero.ts index e36d93b..60d2e12 100644 --- a/src/core/render/preset/hero.ts +++ b/src/core/render/preset/hero.ts @@ -197,25 +197,26 @@ export class HeroRenderer * 勇士移动定时器 */ private moveTick(time: number) { - if (!this.moving) return; if (!this.renderable) return; - const progress = (time - this.lastStepTime) / this.speed; + if (this.moving) { + const progress = (time - this.lastStepTime) / this.speed; - const { x: dx, y: dy } = this.stepDelta; - const { x, y } = core.status.hero.loc; - if (progress >= 1) { - this.renderable.x = x + dx; - this.renderable.y = y + dy; - this.emit('stepEnd'); - } else { - const rx = dx * progress + x; - const ry = dy * progress + y; - this.renderable.x = rx; - this.renderable.y = ry; + const { x: dx, y: dy } = this.stepDelta; + const { x, y } = core.status.hero.loc; + if (progress >= 1) { + this.renderable.x = x + dx; + this.renderable.y = y + dy; + this.emit('stepEnd'); + } else { + const rx = dx * progress + x; + const ry = dy * progress + y; + this.renderable.x = rx; + this.renderable.y = ry; + } + this.layer.update(this.layer); } this.emit('moveTick', this.renderable.x, this.renderable.y); - this.layer.update(this.layer); } /** diff --git a/src/game/index.ts b/src/game/index.ts index 436ccf3..65d5ae8 100644 --- a/src/game/index.ts +++ b/src/game/index.ts @@ -34,7 +34,8 @@ Mota.register('var', 'loading', loading); // ----- 模块注册 Mota.register('module', 'Mechanism', { BluePalace: miscMechanism.BluePalace, - NightSpecial: miscMechanism.NightSpecial + NightSpecial: miscMechanism.NightSpecial, + MiscData: miscMechanism.MiscData }); Mota.register('module', 'State', { ItemState, diff --git a/src/game/mechanism/misc.ts b/src/game/mechanism/misc.ts index 318e18a..3857dde 100644 --- a/src/game/mechanism/misc.ts +++ b/src/game/mechanism/misc.ts @@ -2,6 +2,14 @@ import { backDir, has } from '@/plugin/game/utils'; import { loading } from '../game'; import type { LayerDoorAnimate } from '@/core/render/preset/floor'; +/** + * 一些零散机制的数据 + */ +export namespace MiscData { + /** 循环式地图 */ + export const loopMaps: Set = new Set(['tower6']); +} + /** * 永夜/极昼 */ diff --git a/src/game/state/move.ts b/src/game/state/move.ts index dffa606..74d2200 100644 --- a/src/game/state/move.ts +++ b/src/game/state/move.ts @@ -1,5 +1,5 @@ import EventEmitter from 'eventemitter3'; -import { backDir, toDir } from './utils'; +import { backDir, checkCanMoveExtended, toDir } from './utils'; import { loading } from '../game'; import type { RenderAdapter } from '@/core/render/adapter'; import type { HeroRenderer } from '@/core/render/preset/hero'; @@ -8,10 +8,11 @@ import type { HeroKeyMover } from '@/core/main/action/move'; import type { FloorLayer, Layer, + LayerGroup, LayerMovingRenderable } from '@/core/render/preset/layer'; import type { LayerFloorBinder } from '@/core/render/preset/floor'; -import { BluePalace } from '../mechanism/misc'; +import { BluePalace, MiscData } from '../mechanism/misc'; interface MoveStepDir { type: 'dir'; @@ -425,7 +426,9 @@ const enum HeroMoveCode { /** 不能移动,同时当前格有CannotOut,或目标格有CannotIn,不会触发前面一格的触发器 */ CannotMove, /** 进入传送门 */ - Portal + Portal, + /** 循环式地图 */ + Loop } export class HeroMover extends ObjectMoverBase { @@ -478,8 +481,10 @@ export class HeroMover extends ObjectMoverBase { protected async onMoveStart(controller: IMoveController): Promise { this.beforeMoveSpeed = this.moveSpeed; const adapter = HeroMover.adapter; - if (!adapter) return; + const viewport = HeroMover.viewport; + if (!adapter || !viewport) return; await adapter.all('readyMove'); + viewport.sync('startMove'); adapter.sync('startAnimate'); } @@ -487,8 +492,10 @@ export class HeroMover extends ObjectMoverBase { this.moveSpeed = this.beforeMoveSpeed; this.onSetMoveSpeed(this.moveSpeed, controller); const adapter = HeroMover.adapter; - if (!adapter) return; + const viewport = HeroMover.viewport; + if (!adapter || !viewport) return; await adapter.all('endMove'); + viewport.sync('endMove'); adapter.sync('endAnimate'); core.clearContinueAutomaticRoute(); core.stopAutomaticRoute(); @@ -536,6 +543,25 @@ export class HeroMover extends ObjectMoverBase { if (!canMove) return HeroMoveCode.CannotMove; else return HeroMoveCode.Hit; } + + 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') { + await Promise.all([ + this.renderHeroLoop(), + this.moveAnimate(nx, ny, showDir, dir) + ]); + return HeroMoveCode.Loop; + } else if (x === width - 1 && dir === 'right') { + await Promise.all([ + this.renderHeroLoop(), + this.moveAnimate(nx, ny, showDir, dir) + ]); + return HeroMoveCode.Loop; + } + } } // 可以移动,显示移动动画 @@ -572,13 +598,21 @@ export class HeroMover extends ObjectMoverBase { } // 本次移动正常完成 - if (code === HeroMoveCode.Step || code === HeroMoveCode.Portal) { + 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); @@ -644,6 +678,21 @@ export class HeroMover extends ObjectMoverBase { * @param dir 移动方向 */ 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); @@ -777,6 +826,64 @@ export class HeroMover extends ObjectMoverBase { return Promise.all(promises); } + + private renderHeroLoop() { + const adapter = HeroMover.adapter; + const viewport = HeroMover.viewport; + if (!adapter || !viewport) return; + const MotaRenderer = Mota.require('module', '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(res => { + layer.delegateTicker( + () => { + const progress = (Date.now() - startTime) / this.moveSpeed; + const dx = delta * progress; + loopHero.x = dx + from; + layer.update(layer); + console.log( + loopHero.x, + loopHero.y, + renderable.x, + renderable.y + ); + }, + this.moveSpeed, + () => { + layer.moving.delete(loopHero); + layer.requestUpdateMoving(); + viewport.all('setPosition', x === 0 ? width - 1 : 0, y); + res(); + } + ); + }); + } } interface HeroMoveCollection { diff --git a/src/game/state/utils.ts b/src/game/state/utils.ts index d658847..e2ce87b 100644 --- a/src/game/state/utils.ts +++ b/src/game/state/utils.ts @@ -33,3 +33,75 @@ export function backDir(dir: Dir2): Dir2; export function backDir(dir: Dir2): Dir2 { return backDirMap[dir]; } + +export function locInMap(x: number, y: number, floorId: FloorIds) { + const { width, height } = core.status.maps[floorId]; + return x >= 0 && y >= 0 && x < width && y < height; +} + +/** + * 广义检查能否移动,指定两个点以及行走方向,返回能否执行上述移动。 + * 可以传入地图之外的点,视为可以随意移动。仅检查 cannotIn 和 cannotOut,不检查 noPass + * @param fx 出点横坐标 + * @param fy 出点纵坐标 + * @param tx 入点横坐标 + * @param ty 入点纵坐标 + * @param dir 行走方向 + * @param fromFloorId 出点楼层id + * @param toFloorId 入点楼层id + */ +export function checkCanMoveExtended( + fx: number, + fy: number, + tx: number, + ty: number, + dir: Dir, + fromFloorId: FloorIds = core.status.floorId, + toFloorId: FloorIds = core.status.floorId +) { + const fromInMap = locInMap(fx, fy, fromFloorId); + const toInMap = locInMap(tx, ty, toFloorId); + const map = maps_90f36752_8815_4be8_b32b_d7fad1d0542e; + + if (fromInMap) { + const cannotMove = core.floors[fromFloorId].cannotMove; + const fromIndex: LocString = `${fx},${fy}`; + // 检查当前点是否有不可出 + if (cannotMove[fromIndex]?.includes(dir)) return false; + const blocks = getBlockForLoc(fx, fy, fromFloorId); + const can = blocks.some(v => { + if (v === 0) return false; + const out = map[v as Exclude]?.cannotOut; + return out?.includes(dir); + }); + if (can) return false; + } + if (toInMap) { + const cannotMoveIn = core.floors[toFloorId].cannotMoveIn; + const toIndex: LocString = `${tx},${ty}`; + const back = backDir(dir); + // 检查目标点是否有不可入 + if (cannotMoveIn[toIndex]?.includes(back)) return false; + const blocks = getBlockForLoc(tx, ty, toFloorId); + const can = blocks.some(v => { + if (v === 0) return false; + const out = map[v as Exclude]?.cannotIn; + return out?.includes(back); + }); + if (can) return false; + } + + return true; +} + +function getBlockForLoc(x: number, y: number, floorId: FloorIds) { + const map = core.status.maps[floorId]; + const floor = core.floors[floorId]; + return [ + floor.bgmap[y][x], + floor.bg2map[y][x], + map.map[y][x], + floor.bgmap[y][x], + floor.bg2map[y][x] + ]; +} diff --git a/src/game/system.ts b/src/game/system.ts index 97e756d..028e8d8 100644 --- a/src/game/system.ts +++ b/src/game/system.ts @@ -106,13 +106,13 @@ interface ModuleInterface { Mechanism: { BluePalace: typeof misc.BluePalace; NightSpecial: typeof misc.NightSpecial; + MiscData: typeof misc.MiscData; }; Effect: { Portal: typeof portal; }; Render: { texture: typeof texture; - main: MotaRenderer; MotaRenderer: typeof MotaRenderer; Container: typeof Container; Sprite: typeof Sprite; @@ -161,7 +161,6 @@ interface PluginInterface { frag_r: typeof import('../plugin/fx/frag'); // 游戏进程定义的插件 utils_g: typeof import('../plugin/game/utils'); - loopMap_g: typeof import('../plugin/game/loopMap'); shop_g: typeof import('../plugin/game/shop'); replay_g: typeof import('../plugin/game/replay'); skillTree_g: typeof import('../plugin/game/skillTree'); diff --git a/src/plugin/game/index.ts b/src/plugin/game/index.ts index 97ca1b2..f3bed0b 100644 --- a/src/plugin/game/index.ts +++ b/src/plugin/game/index.ts @@ -3,7 +3,6 @@ import * as fiveLayer from './fiveLayer'; import * as itemDetail from './fx/itemDetail'; import * as replay from './replay'; import * as ui from './ui'; -import * as loopMap from './loopMap'; import * as removeMap from './removeMap'; import * as shop from './shop'; import * as skill from './skill'; @@ -17,7 +16,6 @@ import * as fallback from './fallback'; import './hook'; Mota.Plugin.register('utils_g', utils); -Mota.Plugin.register('loopMap_g', loopMap, loopMap.init); Mota.Plugin.register('shop_g', shop); Mota.Plugin.register('replay_g', replay, replay.init); Mota.Plugin.register('skillTree_g', skillTree); diff --git a/src/plugin/game/loopMap.ts b/src/plugin/game/loopMap.ts deleted file mode 100644 index dea4bd9..0000000 --- a/src/plugin/game/loopMap.ts +++ /dev/null @@ -1,256 +0,0 @@ -import { slide } from './utils'; - -const list = ['tower6']; - -/** - * 设置循环地图的偏移量 - * @param offset 横向偏移量 - */ -function setLoopMap(offset: number, floorId: FloorIds) { - const floor = core.status.maps[floorId]; - if (offset < 9) { - moveMap(floor.width - 17, floorId); - } - if (offset > floor.width - 9) { - moveMap(17 - floor.width, floorId); - } -} - -/** - * 当勇士移动时自动设置循环地图 - */ -function autoSetLoopMap(floorId: FloorIds) { - setLoopMap(core.status.hero.loc.x, floorId); -} - -export function checkLoopMap() { - if (isLoopMap(core.status.floorId)) { - autoSetLoopMap(core.status.floorId); - } -} - -/** - * 移动地图 - */ -function moveMap(delta: number, floorId: FloorIds) { - core.extractBlocks(floorId); - const floor = core.status.maps[floorId]; - core.setHeroLoc('x', core.status.hero.loc.x + delta); - flags[`loop_${floorId}`] += delta; - flags[`loop_${floorId}`] %= floor.width; - const origin = floor.blocks.slice(); - for (let i = 0; i < origin.length; i++) { - core.removeBlockByIndex(0, floorId); - core.removeGlobalAnimate(origin[i].x, origin[i].y); - } - origin.forEach(v => { - let to = v.x + delta; - if (to >= floor.width) to -= floor.width; - if (to < 0) to += floor.width; - core.setBlock(v.id, to, v.y, floorId, true); - core.setMapBlockDisabled(floorId, to, v.y, false); - }); - core.drawMap(); - core.drawHero(); -} - -function isLoopMap(floorId: FloorIds) { - return list.includes(floorId); -} - -export function init() { - events.prototype._sys_changeFloor = function ( - data: any, - callback: () => void - ) { - data = data.event.data; - let heroLoc: Partial = {}; - if (isLoopMap(data.floorId)) { - const floor = core.status.maps[data.floorId as FloorIds] as Floor; - flags[`loop_${data.floorId}`] ??= 0; - let tx = data.loc[0] + flags[`loop_${data.floorId}`]; - tx %= floor.width; - if (tx < 0) tx += floor.width; - heroLoc = { - x: tx, - y: data.loc[1] - }; - } else if (data.loc) heroLoc = { x: data.loc[0], y: data.loc[1] }; - if (data.direction) heroLoc.direction = data.direction; - // @ts-ignore - if (core.status.event.id != 'action') core.status.event.id = null; - core.changeFloor( - data.floorId, - data.stair, - heroLoc, - data.time, - function () { - core.replay(); - if (callback) callback(); - } - ); - }; - - events.prototype.trigger = function ( - x: number, - y: number, - callback: () => void - ) { - var _executeCallback = function () { - // 因为trigger之后还有可能触发其他同步脚本(比如阻激夹域检测) - // 所以这里强制callback被异步触发 - if (callback) { - setTimeout(callback, 1); // +1是为了录像检测系统 - } - return; - }; - if (core.status.gameOver) return _executeCallback(); - if (core.status.event.id == 'action') { - core.insertAction( - { - type: 'function', - function: - 'function () { core.events._trigger_inAction(' + - x + - ',' + - y + - '); }', - async: true - }, - void 0, - void 0, - void 0, - true - ); - return _executeCallback(); - } - if (core.status.event.id) return _executeCallback(); - - let block = core.getBlock(x, y); - const id = core.status.floorId; - const loop = isLoopMap(id); - if (loop && flags[`loop_${id}`] !== 0) { - if (block && block.event.trigger === 'changeFloor') { - delete block.event.trigger; - // @ts-ignore - core.maps._addInfo(block); - } else { - const floor = core.status.maps[id]; - let tx = x - flags[`loop_${id}`]; - tx %= floor.width; - if (tx < 0) tx += floor.width; - const c = core.floors[id].changeFloor[`${tx},${y}`]; - if (c) { - const b: DeepPartial = { event: {}, x: tx, y }; - b.event!.data = c; - b.event!.trigger = 'changeFloor'; - block = b as Block; - } - } - } - - if (block == null) return _executeCallback(); - - // 执行该点的脚本 - if (block.event.script) { - core.clearRouteFolding(); - try { - eval(block.event.script); - } catch (ee) { - console.error(ee); - } - } - - // 碰触事件 - if (block.event.event) { - core.clearRouteFolding(); - core.insertAction(block.event.event, block.x, block.y); - // 不再执行该点的系统事件 - return _executeCallback(); - } - - if (block.event.trigger && block.event.trigger !== 'null') { - var noPass = block.event.noPass, - trigger = block.event.trigger; - if (noPass) core.clearAutomaticRouteNode(x, y); - - // 转换楼层能否穿透 - if ( - trigger == 'changeFloor' && - !noPass && - this._trigger_ignoreChangeFloor(block) && - !loop - ) - return _executeCallback(); - // @ts-ignore - core.status.automaticRoute.moveDirectly = false; - this.doSystemEvent(trigger, block); - } - return _executeCallback(); - }; - - maps.prototype._getBgFgMapArray = function ( - name: string, - floorId: FloorIds, - noCache: boolean = false - ) { - floorId = floorId || core.status.floorId; - if (!floorId) return []; - var width = core.floors[floorId].width; - var height = core.floors[floorId].height; - - // @ts-ignore - if (!noCache && core.status[name + 'maps'][floorId]) - // @ts-ignore - return core.status[name + 'maps'][floorId]; - - var arr: number[][] = - main.mode == 'editor' && - // @ts-ignore - !(window.editor && editor.uievent && editor.uievent.isOpen) - ? // @ts-ignore - core.cloneArray(editor[name + 'map']) - : null; - if (arr == null) - // @ts-ignore - arr = core.cloneArray(core.floors[floorId][name + 'map'] || []); - - if (isLoopMap(floorId) && window.flags) { - flags[`loop_${floorId}`] ??= 0; - arr.forEach(v => { - slide(v, flags[`loop_${floorId}`] % width); - }); - } - - for (var y = 0; y < height; ++y) { - if (arr[y] == null) arr[y] = Array(width).fill(0); - } - // @ts-ignore - (core.getFlag('__' + name + 'v__', {})[floorId] || []).forEach( - // @ts-ignore - function (one) { - arr[one[1]][one[0]] = one[2] || 0; - } - ); - // @ts-ignore - (core.getFlag('__' + name + 'd__', {})[floorId] || []).forEach( - // @ts-ignore - function (one) { - arr[one[1]][one[0]] = 0; - } - ); - if (main.mode == 'editor') { - for (var x = 0; x < width; x++) { - for (var y = 0; y < height; y++) { - // @ts-ignore - arr[y][x] = arr[y][x].idnum || arr[y][x] || 0; - } - } - } - // @ts-ignore - if (core.status[name + 'maps']) - // @ts-ignore - core.status[name + 'maps'][floorId] = arr; - return arr; - }; -} diff --git a/src/plugin/index.ts b/src/plugin/index.ts index 409caf8..11c0206 100644 --- a/src/plugin/index.ts +++ b/src/plugin/index.ts @@ -6,6 +6,7 @@ import * as frag from './fx/frag'; import * as use from './use'; import * as gameCanvas from './fx/gameCanvas'; import * as animateController from './animateController'; +import './loopMap'; Mota.Plugin.register('fly_r', fly); Mota.Plugin.register('chase_r', chase); diff --git a/src/plugin/loopMap.ts b/src/plugin/loopMap.ts new file mode 100644 index 0000000..6077e33 --- /dev/null +++ b/src/plugin/loopMap.ts @@ -0,0 +1,104 @@ +import { Container } from '@/core/render/container'; +import { FloorDamageExtends } from '@/core/render/preset/damage'; +import { LayerGroupFloorBinder } from '@/core/render/preset/floor'; +import { HeroRenderer } from '@/core/render/preset/hero'; +import { FloorLayer, LayerGroup } from '@/core/render/preset/layer'; +import { FloorViewport } from '@/core/render/preset/viewport'; +import { MotaRenderer } from '@/core/render/render'; +import { Transform } from '@/core/render/transform'; +import { FloorItemDetail } from '@/plugin/fx/itemDetail'; + +const loopMaps = Mota.require('module', 'Mechanism').MiscData.loopMaps; + +let loopLayer: LayerGroup; +let show: boolean = false; +/** 循环式地图中,更新视角的委托ticker */ +let delegation: number = -1; + +const hook = Mota.require('var', 'hook'); +hook.on('changingFloor', (floorId, heroLoc) => { + enableLoopMapElement(floorId); +}); + +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.extends(damage); + group.extends(detail); + + loopLayer = group; + group.setZIndex(20); + group.id = 'layer-loop'; +} + +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(transform, 0, 0); + const camera = loopLayer.camera; + if (x1 > 0) { + // 这个是计算循环地图应该显示在哪 + const [, y2] = Transform.transformed(transform, x1 - testPos, 0); + camera.reset(); + camera.translate(core._PX_ - testPos, y2); + loopLayer.pos(transform.x - core._PX_, 0); + loopLayer.show(); + loopLayer.update(loopLayer); + } else { + const [x2, y2] = Transform.transformed(transform, testPos, 0); + if (x2 < core._PX_) { + // 这个不用做其他运算,可以直接显示 + 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); +} diff --git a/src/types/core.d.ts b/src/types/core.d.ts index de82f9b..78c2af8 100644 --- a/src/types/core.d.ts +++ b/src/types/core.d.ts @@ -1422,6 +1422,8 @@ interface MapDataOf { faceIds?: Record; animate?: number; autotileConnection?: (AllIds | AllNumbers)[]; + cannotOut?: Dir[]; + cannotIn?: Dir[]; } /** diff --git a/src/types/map.d.ts b/src/types/map.d.ts index 4dd069a..47c585a 100644 --- a/src/types/map.d.ts +++ b/src/types/map.d.ts @@ -251,11 +251,21 @@ interface ResolvedFloor extends FloorBase { */ bgmap: number[][]; + /** + * 背景2层 + */ + bg2map: number[][]; + /** * 前景层 */ fgmap: number[][]; + /** + * 前景2层 + */ + fg2map: number[][]; + /** * 楼层切换 */