Play liveChoose your opponent
Join
games
What shoal-games changes in this game
Branched from main at 1b3bc26 by @shoal shoal · 1 commit ahead, 0 behind main
Continue
Contribute back
36 lines · 2.6 KB Raw Permalink History
1// Narrow, reproducible fixes to the pinned upstream, applied in memory by Vite.
2// Keep the upstream checkout untouched and fail loudly if its source no longer matches.
3import type { Plugin } from 'vite';
4
5export function upstreamFixes(): Plugin {
6 return {
7 name: 'shoal-sanguosha-rules-fixes',
8 enforce: 'pre',
9 transform(code, id) {
10 if (id.endsWith('/src/engine/rules/身份局.ts')) {
11 const before = 'if (aliveRenegade) return { gameOver: true, winner: aliveRenegade.index };';
12 if (!code.includes(before)) throw new Error('The pinned identity rule changed; review the Shoal rule fix.');
13 return code.replace(before, 'if (aliveRenegade && state.players.filter((p) => p.alive).length === 1) return { gameOver: true, winner: aliveRenegade.index };');
14 }
15 if (id.endsWith('/src/engine/core/skill.ts')) {
16 const before = ' if (state.pendingSlots.size === 1) {';
17 if (!code.includes(before)) throw new Error('The pinned pending-slot lookup changed; review the extra-phase fix.');
18 // A previous turn's paused execute can await the next player's extra phase. The new
19 // player's play slot must still be paused/resolved by dispatch, even with two map entries.
20 return code.replace(before, ' if (direct && !direct.isPaused && !direct.isBlocking) return direct;\n' + before);
21 }
22 if (id.endsWith('/src/engine/view/buildView.ts')) {
23 const before = 'atom: slot.atom,\n prompt: { type: \'confirm\', title: \'等待回应\', cancelLabel: \'\' },';
24 if (!code.includes(before)) throw new Error('The pinned observer view changed; review the private-prompt fix.');
25 code = code.replace(before, "atom: { type: slot.atom.type, target, ...('requestType' in slot.atom ? { requestType: slot.atom.requestType } : {}) },\n prompt: { type: 'confirm', title: '等待回应', cancelLabel: '' },");
26 const priority = 'const ownOrBroadcastSlot = ownSlot && !ownSlot.isPaused ? ownSlot : broadcastSlot;';
27 if (!code.includes(priority)) throw new Error('The pinned pending priority changed; review the broadcast fix.');
28 // Match findPendingSlot: a blocking response outranks a nonblocking play window.
29 // Extra play phases (界当先) otherwise leave the active player unable to answer/pass
30 // a broadcast, while every attempted play is rejected by hasBlockingPending.
31 return code.replace(priority, 'const ownOrBroadcastSlot = ownSlot && !ownSlot.isPaused && ownSlot.isBlocking ? ownSlot : broadcastSlot ?? (ownSlot && !ownSlot.isPaused ? ownSlot : undefined);');
32 }
33 return null;
34 },
35 };
36}