mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-11-27 13:42:58 +08:00
feat: 勇士渲染兼容
This commit is contained in:
parent
7a019f02f4
commit
037746999d
@ -46,6 +46,7 @@ export function createRender() {
|
||||
Font.setDefaults(DEFAULT_FONT);
|
||||
}
|
||||
|
||||
export * from './commonIns';
|
||||
export * from './components';
|
||||
export * from './elements';
|
||||
export * from './fx';
|
||||
|
||||
@ -6,6 +6,7 @@ import {
|
||||
IHeroState,
|
||||
IHeroStateHooks,
|
||||
IMapLayer,
|
||||
nextFaceDirection,
|
||||
state
|
||||
} from '@user/data-state';
|
||||
import { IMapRenderer, IMapRendererTicker, IMovingBlock } from '../types';
|
||||
@ -59,14 +60,14 @@ export class MapHeroRenderer implements IMapHeroRenderer {
|
||||
|
||||
/** 勇士钩子 */
|
||||
readonly controller: IHookController<IHeroStateHooks>;
|
||||
/** 每个朝向的贴图对象 */
|
||||
/** 勇士每个朝向的贴图对象 */
|
||||
readonly textureMap: Map<FaceDirection, IMaterialFramedData> = new Map();
|
||||
/** 勇士渲染实体,与 `entities[0]` 同引用 */
|
||||
readonly heroEntity: HeroRenderEntity;
|
||||
|
||||
/**
|
||||
* 渲染实体,索引 0 表示勇士,后续索引依次表示跟随的跟随者。
|
||||
* 整体是一个状态机,而且下一个跟随者只与上一个跟随者有关,下一个跟随者移动的方向就是上一个跟随者移动前指向的方向。
|
||||
* 整体是一个状态机,而且下一个跟随者只与上一个跟随者有关,下一个跟随者移动的方向就是上一个跟随者上一步移动后指向的方向。
|
||||
*/
|
||||
readonly entities: HeroRenderEntity[] = [];
|
||||
|
||||
@ -194,7 +195,10 @@ export class MapHeroRenderer implements IMapHeroRenderer {
|
||||
}
|
||||
|
||||
setPosition(x: number, y: number): void {
|
||||
this.heroEntity.block.setPos(x, y);
|
||||
this.entities.forEach(v => {
|
||||
v.block.setPos(x, y);
|
||||
v.nextDirection = FaceDirection.Unknown;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -220,12 +224,12 @@ export class MapHeroRenderer implements IMapHeroRenderer {
|
||||
entity.promise = entity.promise.then(async () => {
|
||||
entity.moving = true;
|
||||
entity.animating = true;
|
||||
entity.nextDirection = entity.direction;
|
||||
entity.direction = direction;
|
||||
if (nextTex) block.setTexture(nextTex);
|
||||
await block.lineTo(tx, ty, time);
|
||||
entity.moving = false;
|
||||
entity.animating = false;
|
||||
entity.nextDirection = entity.direction;
|
||||
});
|
||||
}
|
||||
|
||||
@ -406,8 +410,8 @@ export class MapHeroRenderer implements IMapHeroRenderer {
|
||||
);
|
||||
if (!tile) continue;
|
||||
moving.block.setTexture(tile);
|
||||
moving.nextDirection = moving.direction;
|
||||
moving.direction = last.nextDirection;
|
||||
moving.nextDirection = moving.direction;
|
||||
}
|
||||
this.entities.splice(index, 1);
|
||||
}
|
||||
@ -426,6 +430,17 @@ export class MapHeroRenderer implements IMapHeroRenderer {
|
||||
this.heroEntity.animateDirection = direction;
|
||||
}
|
||||
|
||||
turn(direction?: FaceDirection): void {
|
||||
const next = isNil(direction)
|
||||
? nextFaceDirection(this.heroEntity.direction)
|
||||
: direction;
|
||||
const tex = this.textureMap.get(next);
|
||||
if (tex) {
|
||||
this.heroEntity.block.setTexture(tex);
|
||||
this.heroEntity.direction = next;
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.controller.unload();
|
||||
}
|
||||
@ -447,6 +462,10 @@ class MapHeroHook implements Partial<IHeroStateHooks> {
|
||||
this.hero.setPosition(x, y);
|
||||
}
|
||||
|
||||
onTurnHero(direction: FaceDirection): void {
|
||||
this.hero.turn(direction);
|
||||
}
|
||||
|
||||
onStartMove(): void {
|
||||
this.hero.startMove();
|
||||
}
|
||||
|
||||
@ -88,6 +88,12 @@ export interface IMapHeroRenderer {
|
||||
*/
|
||||
setHeroAnimateDirection(direction: HeroAnimateDirection): void;
|
||||
|
||||
/**
|
||||
* 设置勇士朝向
|
||||
* @param direction 勇士朝向,不填表示顺时针旋转
|
||||
*/
|
||||
turn(direction?: FaceDirection): void;
|
||||
|
||||
/**
|
||||
* 摧毁这个勇士渲染拓展,释放相关资源
|
||||
*/
|
||||
|
||||
@ -50,3 +50,133 @@ export function degradeFace(
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定朝向旋转后的朝向
|
||||
* @param dir 当前朝向
|
||||
* @param anticlockwise 是否逆时针旋转,默认顺时针
|
||||
* @param face8 是否使用八朝向。为 `false` 时,旋转为九十度旋转,即 上->右->下->左,左上->右上->右下->左下。
|
||||
* 为 `true` 时,旋转为四十五度旋转,即 上->右上->右->右下->下->左下->左->左上。逆时针反过来旋转。
|
||||
*/
|
||||
export function nextFaceDirection(
|
||||
dir: FaceDirection,
|
||||
anticlockwise: boolean = false,
|
||||
face8: boolean = false
|
||||
): FaceDirection {
|
||||
if (face8) {
|
||||
if (anticlockwise) {
|
||||
switch (dir) {
|
||||
case FaceDirection.Left:
|
||||
return FaceDirection.LeftDown;
|
||||
case FaceDirection.LeftDown:
|
||||
return FaceDirection.Down;
|
||||
case FaceDirection.Down:
|
||||
return FaceDirection.RightDown;
|
||||
case FaceDirection.RightDown:
|
||||
return FaceDirection.Right;
|
||||
case FaceDirection.Right:
|
||||
return FaceDirection.RightUp;
|
||||
case FaceDirection.RightUp:
|
||||
return FaceDirection.Up;
|
||||
case FaceDirection.Up:
|
||||
return FaceDirection.LeftUp;
|
||||
case FaceDirection.LeftUp:
|
||||
return FaceDirection.Left;
|
||||
case FaceDirection.Unknown:
|
||||
return FaceDirection.Unknown;
|
||||
}
|
||||
} else {
|
||||
switch (dir) {
|
||||
case FaceDirection.Left:
|
||||
return FaceDirection.LeftUp;
|
||||
case FaceDirection.LeftUp:
|
||||
return FaceDirection.Up;
|
||||
case FaceDirection.Up:
|
||||
return FaceDirection.RightUp;
|
||||
case FaceDirection.RightUp:
|
||||
return FaceDirection.Right;
|
||||
case FaceDirection.Right:
|
||||
return FaceDirection.RightDown;
|
||||
case FaceDirection.RightDown:
|
||||
return FaceDirection.Down;
|
||||
case FaceDirection.Down:
|
||||
return FaceDirection.LeftDown;
|
||||
case FaceDirection.LeftDown:
|
||||
return FaceDirection.Left;
|
||||
case FaceDirection.Unknown:
|
||||
return FaceDirection.Unknown;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (anticlockwise) {
|
||||
switch (dir) {
|
||||
case FaceDirection.Left:
|
||||
return FaceDirection.Down;
|
||||
case FaceDirection.Down:
|
||||
return FaceDirection.Right;
|
||||
case FaceDirection.Right:
|
||||
return FaceDirection.Up;
|
||||
case FaceDirection.Up:
|
||||
return FaceDirection.Left;
|
||||
case FaceDirection.LeftUp:
|
||||
return FaceDirection.LeftDown;
|
||||
case FaceDirection.LeftDown:
|
||||
return FaceDirection.RightDown;
|
||||
case FaceDirection.RightDown:
|
||||
return FaceDirection.RightUp;
|
||||
case FaceDirection.RightUp:
|
||||
return FaceDirection.LeftUp;
|
||||
case FaceDirection.Unknown:
|
||||
return FaceDirection.Unknown;
|
||||
}
|
||||
} else {
|
||||
switch (dir) {
|
||||
case FaceDirection.Left:
|
||||
return FaceDirection.Up;
|
||||
case FaceDirection.Up:
|
||||
return FaceDirection.Right;
|
||||
case FaceDirection.Right:
|
||||
return FaceDirection.Down;
|
||||
case FaceDirection.Down:
|
||||
return FaceDirection.Left;
|
||||
case FaceDirection.LeftUp:
|
||||
return FaceDirection.RightUp;
|
||||
case FaceDirection.RightUp:
|
||||
return FaceDirection.RightDown;
|
||||
case FaceDirection.RightDown:
|
||||
return FaceDirection.LeftDown;
|
||||
case FaceDirection.LeftDown:
|
||||
return FaceDirection.LeftUp;
|
||||
case FaceDirection.Unknown:
|
||||
return FaceDirection.Unknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据朝向字符串获取朝向枚举值
|
||||
* @param dir 朝向字符串
|
||||
*/
|
||||
export function fromDirectionString(dir: Dir2): FaceDirection {
|
||||
switch (dir) {
|
||||
case 'left':
|
||||
return FaceDirection.Left;
|
||||
case 'right':
|
||||
return FaceDirection.Right;
|
||||
case 'up':
|
||||
return FaceDirection.Up;
|
||||
case 'down':
|
||||
return FaceDirection.Down;
|
||||
case 'leftup':
|
||||
return FaceDirection.LeftUp;
|
||||
case 'rightup':
|
||||
return FaceDirection.RightUp;
|
||||
case 'leftdown':
|
||||
return FaceDirection.LeftDown;
|
||||
case 'rightdown':
|
||||
return FaceDirection.RightDown;
|
||||
default:
|
||||
return FaceDirection.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Hookable, HookController, IHookController } from '@motajs/common';
|
||||
import { IHeroFollower, IHeroState, IHeroStateHooks } from './types';
|
||||
import { FaceDirection, getFaceMovement } from '../common';
|
||||
import { FaceDirection, getFaceMovement, nextFaceDirection } from '../common';
|
||||
import { isNil } from 'lodash-es';
|
||||
|
||||
export class HeroState extends Hookable<IHeroStateHooks> implements IHeroState {
|
||||
x: number = 0;
|
||||
@ -28,6 +29,16 @@ export class HeroState extends Hookable<IHeroStateHooks> implements IHeroState {
|
||||
});
|
||||
}
|
||||
|
||||
turn(direction?: FaceDirection): void {
|
||||
const next = isNil(direction)
|
||||
? nextFaceDirection(this.direction)
|
||||
: direction;
|
||||
this.direction = next;
|
||||
this.forEachHook(hook => {
|
||||
hook.onTurnHero?.(next);
|
||||
});
|
||||
}
|
||||
|
||||
startMove(): void {
|
||||
this.moving = true;
|
||||
this.forEachHook(hook => {
|
||||
|
||||
@ -26,6 +26,12 @@ export interface IHeroStateHooks extends IHookBase {
|
||||
*/
|
||||
onSetPosition(x: number, y: number): void;
|
||||
|
||||
/**
|
||||
* 当设置勇士朝向时触发
|
||||
* @param direction 勇士朝向
|
||||
*/
|
||||
onTurnHero(direction: FaceDirection): void;
|
||||
|
||||
/**
|
||||
* 当勇士开始移动时触发
|
||||
*/
|
||||
@ -119,6 +125,12 @@ export interface IHeroState extends IHookable<IHeroStateHooks> {
|
||||
*/
|
||||
setPosition(x: number, y: number): void;
|
||||
|
||||
/**
|
||||
* 设置勇士朝向
|
||||
* @param direction 勇士朝向,不填表示顺时针旋转
|
||||
*/
|
||||
turn(direction?: FaceDirection): void;
|
||||
|
||||
/**
|
||||
* 开始勇士移动,在移动前必须先调用此方法将勇士切换为移动状态
|
||||
*/
|
||||
|
||||
@ -1,25 +1,27 @@
|
||||
import type { RenderAdapter } from '@motajs/render';
|
||||
import type { TimingFn } from 'mutate-animate';
|
||||
import { BlockMover, heroMoveCollection, MoveStep } from '@user/data-state';
|
||||
import {
|
||||
BlockMover,
|
||||
fromDirectionString,
|
||||
heroMoveCollection,
|
||||
MoveStep,
|
||||
state
|
||||
} from '@user/data-state';
|
||||
import { hook, loading } from '@user/data-base';
|
||||
import { Patch, PatchClass } from '@motajs/legacy-common';
|
||||
import type {
|
||||
HeroRenderer,
|
||||
LayerDoorAnimate,
|
||||
LayerGroupAnimate,
|
||||
Layer,
|
||||
FloorViewport,
|
||||
LayerFloorBinder,
|
||||
LayerGroup
|
||||
} from '@user/client-modules';
|
||||
import { isNil } from 'lodash-es';
|
||||
|
||||
// 向后兼容用,会充当两个版本间过渡的作用
|
||||
|
||||
interface Adapters {
|
||||
'hero-adapter'?: RenderAdapter<HeroRenderer>;
|
||||
'door-animate'?: RenderAdapter<LayerDoorAnimate>;
|
||||
animate?: RenderAdapter<LayerGroupAnimate>;
|
||||
layer?: RenderAdapter<Layer>;
|
||||
viewport?: RenderAdapter<FloorViewport>;
|
||||
}
|
||||
|
||||
@ -30,16 +32,12 @@ export function initFallback() {
|
||||
|
||||
if (!main.replayChecking && main.mode === 'play') {
|
||||
const Adapter = Mota.require('@motajs/render').RenderAdapter;
|
||||
const hero = Adapter.get<HeroRenderer>('hero-adapter');
|
||||
const doorAnimate = Adapter.get<LayerDoorAnimate>('door-animate');
|
||||
const animate = Adapter.get<LayerGroupAnimate>('animate');
|
||||
const layer = Adapter.get<Layer>('layer');
|
||||
const viewport = Adapter.get<FloorViewport>('viewport');
|
||||
|
||||
adapters['hero-adapter'] = hero;
|
||||
adapters['door-animate'] = doorAnimate;
|
||||
adapters['animate'] = animate;
|
||||
adapters['layer'] = layer;
|
||||
adapters['viewport'] = viewport;
|
||||
}
|
||||
|
||||
@ -74,7 +72,7 @@ export function initFallback() {
|
||||
/**
|
||||
* 生成跳跃函数
|
||||
*/
|
||||
function generateJumpFn(dx: number, dy: number): TimingFn<3> {
|
||||
function generateJumpFn(dx: number, dy: number): TimingFn<2> {
|
||||
const distance = Math.hypot(dx, dy);
|
||||
const peak = 3 + distance;
|
||||
|
||||
@ -82,7 +80,7 @@ export function initFallback() {
|
||||
const x = dx * progress;
|
||||
const y = progress * dy + (progress ** 2 - progress) * peak;
|
||||
|
||||
return [x, y, Math.ceil(y)];
|
||||
return [x, y];
|
||||
};
|
||||
}
|
||||
|
||||
@ -112,9 +110,7 @@ export function initFallback() {
|
||||
|
||||
patch.add('_moveAction_moving', () => {});
|
||||
|
||||
patch2.add(
|
||||
'_action_moveAction',
|
||||
function (data: any, x: number, y: number, prefix: any) {
|
||||
patch2.add('_action_moveAction', function () {
|
||||
if (core.canMoveHero()) {
|
||||
const nx = core.nextX(),
|
||||
ny = core.nextY();
|
||||
@ -135,8 +131,7 @@ export function initFallback() {
|
||||
}
|
||||
}
|
||||
core.doAction();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
patch2.add(
|
||||
'eventMoveHero',
|
||||
@ -176,29 +171,28 @@ export function initFallback() {
|
||||
|
||||
patch.add(
|
||||
'setHeroLoc',
|
||||
function (
|
||||
name: 'x' | 'y' | 'direction',
|
||||
value: number | Dir,
|
||||
noGather?: boolean
|
||||
) {
|
||||
function (name: 'x' | 'y' | 'direction', value: number | Dir) {
|
||||
if (!core.status.hero) return;
|
||||
// @ts-ignore
|
||||
core.status.hero.loc[name] = value;
|
||||
if ((name === 'x' || name === 'y') && !noGather) {
|
||||
core.control.gatherFollowers();
|
||||
}
|
||||
if (name === 'direction') {
|
||||
adapters['hero-adapter']?.sync('turn', value);
|
||||
adapters['hero-adapter']?.sync('setAnimateDir', value);
|
||||
const dir = fromDirectionString(value as Dir);
|
||||
state.hero.turn(dir);
|
||||
setHeroDirection(value as Dir);
|
||||
} else if (name === 'x') {
|
||||
// 为了防止逆天样板出问题
|
||||
core.bigmap.posX = value as number;
|
||||
adapters['hero-adapter']?.sync('setHeroLoc', value);
|
||||
state.hero.setPosition(
|
||||
value as number,
|
||||
core.status.hero.loc.y
|
||||
);
|
||||
} else {
|
||||
// 为了防止逆天样板出问题
|
||||
core.bigmap.posY = value as number;
|
||||
adapters['hero-adapter']?.sync('setHeroLoc', void 0, value);
|
||||
state.hero.setPosition(
|
||||
core.status.hero.loc.x,
|
||||
value as number
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
@ -243,10 +237,8 @@ export function initFallback() {
|
||||
);
|
||||
|
||||
patch2.add('setHeroIcon', function (name: ImageIds) {
|
||||
const img = core.material.images.images[name];
|
||||
if (!img) return;
|
||||
core.status.hero.image = name;
|
||||
adapters['hero-adapter']?.sync('setImage', img);
|
||||
state.hero.setImage(name);
|
||||
});
|
||||
|
||||
patch.add('isMoving', function () {
|
||||
@ -279,10 +271,10 @@ export function initFallback() {
|
||||
// 找寻自动寻路路线
|
||||
const moveStep = core.automaticRoute(destX, destY);
|
||||
if (
|
||||
moveStep.length == 0 &&
|
||||
(destX != core.status.hero.loc.x ||
|
||||
destY != core.status.hero.loc.y ||
|
||||
stepPostfix.length == 0)
|
||||
moveStep.length === 0 &&
|
||||
(destX !== core.status.hero.loc.x ||
|
||||
destY !== core.status.hero.loc.y ||
|
||||
stepPostfix.length === 0)
|
||||
)
|
||||
return;
|
||||
moveStep.push(...stepPostfix);
|
||||
@ -375,10 +367,10 @@ export function initFallback() {
|
||||
id = id || '';
|
||||
if (
|
||||
// @ts-ignore
|
||||
(core.material.icons.animates[id] == null &&
|
||||
(isNil(core.material.icons.animates[id]) &&
|
||||
// @ts-ignore
|
||||
core.material.icons.npc48[id] == null) ||
|
||||
core.getBlock(x, y) != null
|
||||
isNil(core.material.icons.npc48[id])) ||
|
||||
!isNil(core.getBlock(x, y))
|
||||
) {
|
||||
if (callback) callback();
|
||||
return;
|
||||
@ -438,10 +430,10 @@ export function initFallback() {
|
||||
if (
|
||||
core.isReplaying() ||
|
||||
!core.material.animates[name] ||
|
||||
x == null ||
|
||||
y == null
|
||||
isNil(x) ||
|
||||
isNil(y)
|
||||
) {
|
||||
if (callback) callback();
|
||||
callback?.();
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -547,26 +539,25 @@ export function initFallback() {
|
||||
const dy = ey - sy;
|
||||
|
||||
const fn = generateJumpFn(dx, dy);
|
||||
|
||||
const list = adapters.layer?.items ?? [];
|
||||
const items = [...list].filter(v => {
|
||||
if (v.layer !== 'event') return false;
|
||||
const ex = v.getExtends('floor-binder') as LayerFloorBinder;
|
||||
if (!ex) return false;
|
||||
return ex.getFloor() === core.status.floorId;
|
||||
});
|
||||
const width = core.status.thisMap.width;
|
||||
const index = sx + sy * width;
|
||||
|
||||
const promise = Promise.all(
|
||||
items.map(v => {
|
||||
return v.moveAs(index, ex, ey, fn, time, keep);
|
||||
})
|
||||
// 先使用 mainMapRenderer 妥协
|
||||
const { mainMapRenderer: renderer } = Mota.require(
|
||||
'@user/client-modules'
|
||||
);
|
||||
|
||||
core.updateStatusBar();
|
||||
if (renderer.layerState !== state.layer) {
|
||||
callback?.();
|
||||
return;
|
||||
}
|
||||
const layer = state.layer.getLayerByAlias('event');
|
||||
if (!layer) {
|
||||
callback?.();
|
||||
return;
|
||||
}
|
||||
core.removeBlock(sx, sy);
|
||||
await promise;
|
||||
const moving = renderer.addMovingBlock(layer, block.id, sx, sy);
|
||||
core.updateStatusBar();
|
||||
await moving.moveRelative(fn, time);
|
||||
moving.destroy();
|
||||
|
||||
if (keep) {
|
||||
core.setBlock(block.id, ex, ey);
|
||||
}
|
||||
@ -586,30 +577,15 @@ export function initFallback() {
|
||||
) {
|
||||
if (heroMover.moving) return;
|
||||
|
||||
const sx = core.getHeroLoc('x');
|
||||
const sy = core.getHeroLoc('y');
|
||||
adapters.viewport?.all('mutateTo', ex, ey, time);
|
||||
|
||||
const locked = core.status.lockControl;
|
||||
core.lockControl();
|
||||
const list = adapters['hero-adapter']?.items ?? [];
|
||||
const items = [...list];
|
||||
|
||||
time /= core.status.replay.speed;
|
||||
if (core.status.replay.speed === 24) time = 1;
|
||||
const fn = generateJumpFn(ex - sx, ey - sy);
|
||||
await Promise.all(
|
||||
items.map(v => {
|
||||
if (!v.renderable) return Promise.reject();
|
||||
return v.layer.moveRenderable(
|
||||
v.renderable,
|
||||
sx,
|
||||
sy,
|
||||
fn,
|
||||
time
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
await state.hero.jumpHero(ex, ey, time);
|
||||
|
||||
if (!locked) core.unlockControl();
|
||||
core.setHeroLoc('x', ex);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user