feat: botfights v1 — full fighting game with Kaplay engine
- Vue 3 + Vite + Tailwind 4 frontend with synthwave aesthetic - Hono backend on port 9100 with SQLite/Drizzle - Procedural pixel-art sprite generator (48x48, 8 animation states) - Kaplay fight scene with punch/kick/special/knockback/KO animations - 12 mock bots across 6 tiers with Elo rating system - 9 challenge types, 10 fight arenas with modifiers - Fight replay with staggered battle log and ~1 min timing - Sprite preview page at /sprites Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
type Listener = (event: FightEvent) => void
|
||||
|
||||
export interface FightEvent {
|
||||
fightId: string
|
||||
type: string
|
||||
data: Record<string, unknown>
|
||||
timestamp: string
|
||||
}
|
||||
|
||||
class EventBus {
|
||||
private listeners = new Map<string, Set<Listener>>()
|
||||
private globalListeners = new Set<Listener>()
|
||||
|
||||
on(fightId: string, listener: Listener) {
|
||||
if (!this.listeners.has(fightId)) {
|
||||
this.listeners.set(fightId, new Set())
|
||||
}
|
||||
this.listeners.get(fightId)!.add(listener)
|
||||
return () => this.off(fightId, listener)
|
||||
}
|
||||
|
||||
onAll(listener: Listener) {
|
||||
this.globalListeners.add(listener)
|
||||
return () => this.globalListeners.delete(listener)
|
||||
}
|
||||
|
||||
off(fightId: string, listener: Listener) {
|
||||
this.listeners.get(fightId)?.delete(listener)
|
||||
}
|
||||
|
||||
emit(event: FightEvent) {
|
||||
this.listeners.get(event.fightId)?.forEach(fn => fn(event))
|
||||
this.globalListeners.forEach(fn => fn(event))
|
||||
}
|
||||
|
||||
cleanup(fightId: string) {
|
||||
this.listeners.delete(fightId)
|
||||
}
|
||||
}
|
||||
|
||||
export const fightEvents = new EventBus()
|
||||
Reference in New Issue
Block a user