diff --git a/src/core/render/preset/hero.ts b/src/core/render/preset/hero.ts index 8f6321e..29432e7 100644 --- a/src/core/render/preset/hero.ts +++ b/src/core/render/preset/hero.ts @@ -13,6 +13,7 @@ type HeroMovingStatus = 'stop' | 'moving' | 'moving-as'; interface HeroRenderEvent { stepEnd: []; moveTick: [x: number, y: number]; + append: [renderable: LayerMovingRenderable[]]; } export class HeroRenderer @@ -40,6 +41,7 @@ export class HeroRenderer /** 勇士移动速度 */ speed: number = 100; /** 当前勇士朝向 */ + // todo: 删了这个属性 dir: Dir = 'down'; /** 勇士移动定时器id */ @@ -47,7 +49,7 @@ export class HeroRenderer /** 上一次帧数切换的时间 */ private lastFrameTime: number = 0; /** 当前的移动方向 */ - private moveDir: Move2 = 'down'; + moveDir: Move2 = 'down'; /** 上一步走到格子上的时间 */ private lastStepTime: number = 0; /** 执行当前步移动的Promise */ @@ -58,7 +60,7 @@ export class HeroRenderer * 这一步的移动方向,与{@link moveDir}不同的是,在这一步走完之前,它都不会变, * 当这一步走完之后,才会将其设置为{@link moveDir}的值 */ - private stepDir: Dir2 = 'down'; + stepDir: Dir2 = 'down'; /** 每步的格子增量 */ private stepDelta: Loc = { x: 0, y: 1 }; /** 动画显示的方向,用于适配后退 */ @@ -354,7 +356,10 @@ export class HeroRenderer } onMovingUpdate(layer: Layer, renderable: LayerMovingRenderable[]): void { - if (this.renderable) renderable.push(this.renderable); + if (this.renderable) { + renderable.push(this.renderable); + this.emit('append', renderable); + } } } diff --git a/src/game/mechanism/misc.ts b/src/game/mechanism/misc.ts index 7ea2e0c..41ebf88 100644 --- a/src/game/mechanism/misc.ts +++ b/src/game/mechanism/misc.ts @@ -1,4 +1,4 @@ -import { has } from '@/plugin/game/utils'; +import { backDir, has } from '@/plugin/game/utils'; import { loading } from '../game'; export namespace BluePalace { @@ -90,6 +90,16 @@ export namespace BluePalace { toDir: Dir; } + export interface PortalTo { + x: number; + y: number; + dir: Dir; + } + + type PortalMap = Map>>>; + + export const portalMap: PortalMap = new Map(); + export const portals: Partial> = { MT75: [ { fx: 7, fy: 7, dir: 'left', tx: 9, ty: 9, toDir: 'down' }, @@ -101,7 +111,103 @@ export namespace BluePalace { }; loading.once('coreInit', initPortals); + function generatePortalMap() { + const delta: Record = { + // 方向:[正向, 逆向]<进出> + 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>>(); + 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; + 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(); + console.log(portalMap); } } diff --git a/src/plugin/game/fallback.ts b/src/plugin/game/fallback.ts index aa47462..bf9eb24 100644 --- a/src/plugin/game/fallback.ts +++ b/src/plugin/game/fallback.ts @@ -2,6 +2,8 @@ import type { RenderAdapter } from '@/core/render/adapter'; import type { LayerGroupAnimate } from '@/core/render/preset/animate'; import type { LayerDoorAnimate } from '@/core/render/preset/floor'; import type { HeroRenderer } from '@/core/render/preset/hero'; +import type { LayerMovingRenderable } from '@/core/render/preset/layer'; +import { BluePalace } from '@/game/mechanism/misc'; interface Adapters { 'hero-adapter'?: RenderAdapter; @@ -33,6 +35,11 @@ export function init() { let stepDir: Dir; let moveEnding: Promise; + /** 传送门信息,下一步传送到哪 */ + let portalData: BluePalace.PortalTo | undefined; + /** 下一步是否步入传送门 */ + let portal: boolean = false; + const pressedArrow: Set = new Set(); Mota.r(() => { const gameKey = Mota.require('var', 'gameKey'); @@ -141,7 +148,7 @@ export function init() { if (stopChian || core.status.lockControl) break; stepDir = moveDir; if (!checkCanMoveStatus(callback)) break; - + if (portal) renderHeroSwap(); await adapter.all('move', moveDir); onMoveEnd(false, callback); } @@ -153,8 +160,9 @@ export function init() { function checkCanMoveStatus(callback?: () => void) { core.setHeroLoc('direction', stepDir); const { noPass, canMove } = checkCanMove(); + checkPortal(); - if (noPass || !canMove) { + if (!portal && (noPass || !canMove)) { onCannotMove(canMove, callback); if (moving) endMove(); return false; @@ -199,7 +207,13 @@ export function init() { } function onMoveEnd(noPass: boolean, callback?: () => void) { - if (!noPass) { + if (portal && portalData) { + const { x, y, dir } = portalData; + core.setHeroLoc('x', x); + core.setHeroLoc('y', y); + core.setHeroLoc('direction', dir); + portal = false; + } else if (!noPass) { const { nx, ny } = getNextLoc(); core.setHeroLoc('x', nx, true); core.setHeroLoc('y', ny, true); @@ -214,6 +228,130 @@ export function init() { callback?.(); } + // ----- 移动 - 传送门 + function checkPortal() { + const map = BluePalace.portalMap.get(core.status.floorId); + if (!map) { + portal = false; + portalData = void 0; + return; + } + const width = core.status.thisMap.width; + const { x, y, direction } = core.status.hero.loc; + const index = x + y * width; + const data = map?.get(index); + if (!data) { + portal = false; + portalData = void 0; + return; + } + const to = data[direction]; + if (to) { + portal = true; + portalData = to; + } + } + + const end: Record = { + left: [-1, 0], + right: [1, 0], + down: [0, 1], + up: [0, -1] + }; + + /** + * 对勇士进行切割渲染,分成两个renderable进行渲染 + */ + function renderHeroSwap() { + if (!portal || !portalData) return; + const list = adapters['hero-adapter']?.items; + if (!list) return; + const { x: tx, y: ty, dir: toDir } = portalData; + const { x, y, direction } = core.status.hero.loc; + const [dx, dy] = end[direction]; + const [tdx] = end[toDir]; + const checkX = x + dx; + const checkY = y + dy; + + list.forEach(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.updateMovingRenderable(); + const append = (r: LayerMovingRenderable[]) => { + r.push(renderable); + }; + v.on('append', append); + v.on('moveTick', function func() { + const progress = + heroDir === 'left' || heroDir === 'right' + ? 1 - Math.abs(checkX - v.renderable!.x) + : 1 - Math.abs(checkY - v.renderable!.y); + if (progress >= 1 || !portal) { + v.renderable!.render = originFrom; + v.off('moveTick', func); + v.off('append', append); + return; + } + 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; + } + }); + } + }); + }); + } + // ----- 勇士移动相关 Mota.r(() => { diff --git a/src/plugin/game/utils.ts b/src/plugin/game/utils.ts index 0635705..deb23d5 100644 --- a/src/plugin/game/utils.ts +++ b/src/plugin/game/utils.ts @@ -28,6 +28,8 @@ const backDirMap: Record = { rightdown: 'leftup' }; +export function backDir(dir: Dir): Dir; +export function backDir(dir: Dir2): Dir2; export function backDir(dir: Dir2): Dir2 { return backDirMap[dir]; }