HumanBreak/src/game/game.ts

231 lines
6.7 KiB
TypeScript
Raw Normal View History

import { EventEmitter } from '../core/common/eventEmitter';
2024-05-09 23:49:53 +08:00
import type { DamageEnemy } from './enemy/damage';
2024-02-02 17:10:21 +08:00
// ----- 加载事件
interface GameLoadEvent {
2024-02-02 17:10:21 +08:00
coreLoaded: () => void;
autotileLoaded: () => void;
coreInit: () => void;
2024-05-16 22:43:24 +08:00
loaded: () => void;
materialLoaded: () => void;
2024-02-02 17:10:21 +08:00
}
class GameLoading extends EventEmitter<GameLoadEvent> {
private autotileLoaded: number = 0;
private autotileNum?: number;
private autotileListened: boolean = false;
constructor() {
super();
this.on(
'coreInit',
() => {
this.autotileNum = Object.keys(
core.material.icons.autotile
).length;
},
{ immediate: true }
);
this.on('materialLoaded', () => {
core.loader._loadMaterials_afterLoad();
});
}
addAutotileLoaded() {
2024-04-21 14:39:58 +08:00
this.autotileLoaded++;
if (this.autotileLoaded === this.autotileNum) {
this.emit('autotileLoaded');
}
2024-02-02 17:10:21 +08:00
}
/**
*
* @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();
2023-08-05 12:12:02 +08:00
export interface GameEvent {
2024-05-09 23:49:53 +08:00
/** Emitted in libs/events.js resetGame. */
2023-08-05 12:12:02 +08:00
reset: () => void;
2023-10-29 22:13:37 +08:00
/** Emitted in src/App.vue setup. */
mounted: () => void;
2024-05-09 23:49:53 +08:00
/** Emitted in plugin/game/ui.ts updateStatusBar_update */
2023-11-14 18:21:38 +08:00
statusBarUpdate: () => void;
2024-04-20 12:27:38 +08:00
/** Emitted in core/index.ts */
renderLoaded: () => void;
2024-05-09 23:49:53 +08:00
/** Emitted in libs/events.js getItem */
afterGetItem: (
itemId: AllIdsOf<'items'>,
x: number,
y: number,
isGentleClick: boolean
) => void;
2024-05-09 23:49:53 +08:00
/** Emitted in libs/events.js _openDoor_animate */
afterOpenDoor: (doorId: AllIdsOf<'animates'>, x: number, y: number) => void;
2024-05-09 23:49:53 +08:00
/** Emitted in project/functions.js afterChangeFloor */
afterChangeFloor: (floorId: FloorIds) => void;
2024-05-09 23:49:53 +08:00
/** Emitted in project/functions.js moveOneStep */
2024-04-22 23:27:23 +08:00
moveOneStep: (x: number, y: number, floorId: FloorIds) => void;
2024-05-09 23:49:53 +08:00
/** Emitted in src/game/enemy/battle.ts afterBattle */
2024-05-01 18:00:27 +08:00
afterBattle: (enemy: DamageEnemy, x?: number, y?: number) => void;
2024-05-09 23:49:53 +08:00
/** Emitted in libs/events.js changingFloor */
changingFloor: (floorId: FloorIds, heroLoc: Loc) => void;
2024-05-16 22:43:24 +08:00
2024-05-09 23:49:53 +08:00
drawHero: (
status?: Exclude<keyof MaterialIcon['hero']['down'], 'loc'>,
offset?: number,
frame?: number
) => void;
2024-05-16 22:43:24 +08:00
/** Emitted in libs/maps.js setBlock */
setBlock: (
x: number,
y: number,
floorId: FloorIds,
newBlock: AllNumbers,
oldBlock: AllNumbers
2024-05-16 22:43:24 +08:00
) => void;
2023-08-05 12:12:02 +08:00
}
export const hook = new EventEmitter<GameEvent>();
2023-11-12 19:41:07 +08:00
interface ListenerEvent {
2023-11-12 22:54:19 +08:00
// block
hoverBlock: (block: Block, ev: MouseEvent) => void;
leaveBlock: (block: Block, ev: MouseEvent, leaveGame: boolean) => void;
clickBlock: (block: Block, ev: MouseEvent) => void;
// mouse
mouseMove: (ev: MouseEvent) => void;
2023-11-12 19:41:07 +08:00
}
class GameListener extends EventEmitter<ListenerEvent> {
static num: number = 0;
num: number = GameListener.num++;
constructor() {
super();
2024-04-20 12:27:38 +08:00
if (main.replayChecking) return;
2023-11-12 22:54:19 +08:00
if (!!window.core) {
2023-11-12 19:41:07 +08:00
this.init();
2023-11-12 22:54:19 +08:00
} else {
loading.once('coreInit', () => {
this.init();
});
}
2023-11-12 19:41:07 +08:00
}
private init() {
2023-11-12 22:54:19 +08:00
// ----- block
2023-11-12 19:41:07 +08:00
let lastHoverX = -1;
let lastHoverY = -1;
2023-11-12 20:47:46 +08:00
2023-11-12 22:54:19 +08:00
const data = core.canvas.data.canvas;
const getBlockLoc = (px: number, py: number, size: number) => {
2023-11-12 20:47:46 +08:00
return [
2024-04-20 12:27:38 +08:00
Math.floor(((px * 32) / size + core.bigmap.offsetX) / 32),
Math.floor(((py * 32) / size + core.bigmap.offsetY) / 32)
2023-11-12 20:47:46 +08:00
];
};
2023-11-14 18:21:38 +08:00
// hover & leave & mouseMove
2023-11-12 22:54:19 +08:00
data.addEventListener('mousemove', e => {
2024-04-20 12:27:38 +08:00
if (
core.status.lockControl ||
!core.isPlaying() ||
!core.status.floorId
)
return;
2023-11-12 22:54:19 +08:00
this.emit('mouseMove', e);
const {
x: px,
y: py,
size
} = core.actions._getClickLoc(e.clientX, e.clientY);
const [bx, by] = getBlockLoc(px, py, size);
const blocks = core.getMapBlocksObj();
if (lastHoverX !== bx || lastHoverY !== by) {
const lastBlock = blocks[`${lastHoverX},${lastHoverY}`];
const block = blocks[`${bx},${by}`];
if (!!lastBlock) {
this.emit('leaveBlock', lastBlock, e, false);
2023-11-12 19:41:07 +08:00
}
2023-11-12 22:54:19 +08:00
if (!!block) {
this.emit('hoverBlock', block, e);
lastHoverX = bx;
lastHoverY = by;
} else {
lastHoverX = -1;
lastHoverY = -1;
}
}
});
data.addEventListener('mouseleave', e => {
2024-04-20 12:27:38 +08:00
if (
core.status.lockControl ||
!core.isPlaying() ||
!core.status.floorId
)
return;
2023-11-12 19:41:07 +08:00
const blocks = core.getMapBlocksObj();
const lastBlock = blocks[`${lastHoverX},${lastHoverY}`];
if (!!lastBlock) {
2023-11-12 22:54:19 +08:00
this.emit('leaveBlock', lastBlock, e, true);
2023-11-12 19:41:07 +08:00
}
lastHoverX = -1;
lastHoverY = -1;
});
2023-11-12 22:54:19 +08:00
// click
data.addEventListener('click', e => {
2024-04-20 12:27:38 +08:00
if (
core.status.lockControl ||
!core.isPlaying() ||
!core.status.floorId
)
return;
2023-11-12 22:54:19 +08:00
const {
x: px,
y: py,
size
} = core.actions._getClickLoc(e.clientX, e.clientY);
const [bx, by] = getBlockLoc(px, py, size);
const blocks = core.getMapBlocksObj();
const block = blocks[`${bx},${by}`];
if (!!block) {
this.emit('clickBlock', block, e);
}
});
// ----- mouse
2023-11-12 19:41:07 +08:00
}
}
export const gameListener = new GameListener();
2024-02-02 17:10:21 +08:00
declare global {
interface Main {
loading: GameLoading;
}
}