Add character emotes across bot friend and ranked matches

This commit is contained in:
2026-10-14 23:56:32 +09:00
parent c798753418
commit 493af700aa
14 changed files with 177 additions and 7 deletions
+9
View File
@@ -14,6 +14,7 @@ interface FriendConfig {
matchId: string; slot: 0 | 1; engine: MatchEngine; peer: GamePeer; clock: GameClock;
setOffset(offset: number): void; changed(): void; finished(): void;
saved?: { confirmed?: MatchCheckpoint; epoch: number };
onEmote?(id: unknown): void;
}
export function createFriendMatch(config: FriendConfig) {
const { peer, engine, clock } = config;
@@ -160,6 +161,10 @@ export function createFriendMatch(config: FriendConfig) {
if (message.type === 'ping' || message.type === 'clock:ping') { send(message.type === 'ping' ? 'pong' : 'clock:pong', { sentAt: message.sentAt, remoteAt: now() }); return; }
if (message.type === 'clock:pong') { if (Number.isFinite(message.remoteAt)) clockReplies.get(message.sentAt)?.resolve(message.remoteAt); clockReplies.delete(message.sentAt); return; }
if (message.type === 'pong') return;
if (message.type === 'emote') {
if (!recovering && engine.view().phase !== 'finished') config.onEmote?.(message.id);
return;
}
if (message.type === 'ready') { if (message.slot === config.slot) throw new Error('INVALID_SLOT'); peerReady = true; maybeStart(); return; }
if (message.type === 'resume' || message.type === 'resume:ack') {
if (!recovering) return;
@@ -205,6 +210,10 @@ export function createFriendMatch(config: FriendConfig) {
catch { abort('connection_failed'); }
},
get recovering() { return recovering; },
sendEmote(id: import('../../constants/emotes.js').EmoteId) {
if (stopped || recovering || !peer.connected || engine.view().phase === 'finished') return false;
try { send('emote', { id }); return true; } catch { return false; }
},
submitAction() { if (!recovering && !stopped) run(() => exchange!.commit(engine.view().ownAction)); },
requestRematch() { engine.requestRematch(config.slot); if (peer.connected) send('match:rematch'); config.changed(); },
snapshot: () => ({ confirmed: structuredClone(confirmed), epoch: peer.epoch, privateChoice }),