HumanBreak/packages-user/data-base/src/game.ts

269 lines
8.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { EventEmitter } from 'eventemitter3';
import { IDamageEnemy, IEnemyCollection } from '@motajs/types';
// ----- 加载事件
interface GameLoadEvent {
/** 当核心脚本加载完毕时触发 */
coreLoaded: [];
/** 当自动元件加载完毕后触发 */
autotileLoaded: [];
/** 当核心类初始化完毕后触发 */
coreInit: [];
/** 当所有启动必要资源加载完毕后触发 */
loaded: [];
/** 当客户端(渲染端)和数据端都挂载完毕后触发 */
registered: [];
/** 当数据端挂载完毕后触发 */
dataRegistered: [];
/** 当客户端(渲染端)挂载完毕后触发 */
clientRegistered: [];
}
class GameLoading extends EventEmitter<GameLoadEvent> {
private autotileLoaded: number = 0;
private autotileNum?: number;
private autotileListened: boolean = false;
loaded: boolean = false;
constructor() {
super();
this.once('coreInit', () => {
this.autotileNum = Object.keys(core.material.icons.autotile).length;
});
this.once('loaded', () => {
this.loaded = true;
});
}
addAutotileLoaded() {
this.autotileLoaded++;
if (this.autotileLoaded === this.autotileNum) {
this.emit('autotileLoaded');
}
}
/**
* 当自动元件加载完毕时
* @param autotiles 自动元件数组
*/
onAutotileLoaded(
autotiles: Partial<Record<AllIdsOf<'autotile'>, HTMLImageElement>>
) {
if (this.autotileListened) return;
this.autotileListened = true;
this.on('autotileLoaded', () => {
const keys = Object.keys(
core.material.icons.autotile
) as AllIdsOf<'autotile'>[];
keys.forEach(v => {
core.material.images.autotile[v] = autotiles[v]!;
});
setTimeout(() => {
core.maps._makeAutotileEdges();
});
});
}
}
export const loading = new GameLoading();
main.loading = loading;
let clientRegistered = false;
let dataRegistered = false;
function checkRegistered() {
if (main.replayChecking || main.mode === 'editor') {
clientRegistered = true;
}
if (clientRegistered && dataRegistered) {
loading.emit('registered');
}
}
loading.once('clientRegistered', () => {
clientRegistered = true;
checkRegistered();
});
loading.once('dataRegistered', () => {
dataRegistered = true;
checkRegistered();
});
export interface GameEvent {
/** 当游戏初始化时触发Emitted in libs/events.js resetGame. */
reset: [];
/** 当游戏挂载完毕后触发Emitted in src/App.vue setup. */
mounted: [];
/** 当状态栏更新时触发Emitted in plugin/game/ui.ts updateStatusBar_update */
statusBarUpdate: [];
/** 当客户端渲染端加载完毕后触发Emitted in core/index.ts */
renderLoaded: [];
/** 当捡拾道具后触发Emitted in libs/events.js getItem */
afterGetItem: [
itemId: AllIdsOf<'items'>,
x: number,
y: number,
isGentleClick: boolean
];
/** 当开门后触发Emitted in libs/events.js _openDoor_animate */
afterOpenDoor: [doorId: AllIdsOf<'animates'>, x: number, y: number];
/** 当楼层切换后触发Emitted in project/functions.js afterChangeFloor */
afterChangeFloor: [floorId: FloorIds];
/** 勇士每移动一步时触发Emitted in project/functions.js moveOneStep */
moveOneStep: [x: number, y: number, floorId: FloorIds];
/** 战斗后触发Emitted in src/game/enemy/battle.ts afterBattle */
afterBattle: [enemy: IDamageEnemy, x?: number, y?: number];
/** 楼层切换中触发具体时刻是楼层切换的正中间刚刚执行完切换Emitted in libs/events.js changingFloor */
changingFloor: [floorId: FloorIds, heroLoc: Loc];
/** 当某一个图块被设置时触发Emitted in libs/maps.js setBlock */
setBlock: [
x: number,
y: number,
floorId: FloorIds,
newBlock: AllNumbers,
oldBlock: AllNumbers
];
/** 当怪物信息被解析时触发Emitted in game/enemy/damage.ts */
enemyExtract: [col: IEnemyCollection];
/** 当从游戏中回到游戏标题界面时触发Emitted in lib/events.js restart */
restart: [];
/** 当设置背景或前景图块时触发Emitted in lib/maps.js setBgFgBlock */
setBgFgBlock: [
name: 'bg' | 'fg' | 'bg2' | 'fg2',
number: AllNumbers,
x: number,
y: number,
floorId: FloorIds
];
/** 当录像播放在暂停和播放状态间切换时触发Emitted in lib/control.js */
replayStatus: [replaying: boolean];
/** 当加载存档时触发Emitted in project/functions.js */
loadData: [];
}
export const hook = new EventEmitter<GameEvent>();
interface ListenerEvent {
// block
hoverBlock: [block: Block, ev: MouseEvent];
leaveBlock: [block: Block, ev: MouseEvent, leaveGame: boolean];
clickBlock: [block: Block, ev: MouseEvent];
// mouse
mouseMove: [ev: MouseEvent];
}
class GameListener extends EventEmitter<ListenerEvent> {
static num: number = 0;
num: number = GameListener.num++;
mouseX: number = -1;
mouseY: number = -1;
constructor() {
super();
if (main.replayChecking) return;
if (!!window.core) {
this.init();
} else {
loading.once('coreInit', () => {
this.init();
});
}
}
private init() {
// ----- block
const data = core.canvas.data.canvas;
const getBlockLoc = (px: number, py: number, size: number) => {
return [
Math.floor(((px * 32) / size + core.bigmap.offsetX) / 32),
Math.floor(((py * 32) / size + core.bigmap.offsetY) / 32)
];
};
// hover & leave & mouseMove
data.addEventListener('mousemove', e => {
if (
core.status.lockControl ||
!core.isPlaying() ||
!core.status.floorId
)
return;
this.emit('mouseMove', e);
const {
x: px,
y: py,
size
} = core.actions._getClickLoc(e.offsetX, e.offsetY);
const [bx, by] = getBlockLoc(px, py, size);
const blocks = core.getMapBlocksObj();
if (this.mouseX !== bx || this.mouseY !== by) {
const lastBlock = blocks[`${this.mouseX},${this.mouseY}`];
const block = blocks[`${bx},${by}`];
if (!!lastBlock) {
this.emit('leaveBlock', lastBlock, e, false);
}
if (!!block) {
this.emit('hoverBlock', block, e);
this.mouseX = bx;
this.mouseY = by;
} else {
this.mouseX = -1;
this.mouseY = -1;
}
}
});
data.addEventListener('mouseleave', e => {
if (
core.status.lockControl ||
!core.isPlaying() ||
!core.status.floorId
)
return;
const blocks = core.getMapBlocksObj();
const lastBlock = blocks[`${this.mouseX},${this.mouseY}`];
if (!!lastBlock) {
this.emit('leaveBlock', lastBlock, e, true);
}
this.mouseX = -1;
this.mouseY = -1;
});
// click
data.addEventListener('click', e => {
if (
core.status.lockControl ||
!core.isPlaying() ||
!core.status.floorId
)
return;
const {
x: px,
y: py,
size
} = core.actions._getClickLoc(e.offsetX, e.offsetY);
const [bx, by] = getBlockLoc(px, py, size);
const blocks = core.getMapBlocksObj();
const block = blocks[`${bx},${by}`];
if (!!block) {
this.emit('clickBlock', block, e);
}
});
// ----- mouse
}
}
/** @deprecated */
export const gameListener = new GameListener();
declare global {
interface Main {
loading: GameLoading;
}
}