65 lines
4.2 KiB
TypeScript
65 lines
4.2 KiB
TypeScript
'use client';
|
|
|
|
import type { Action, GameView } from '@/game/engine';
|
|
import { bidClaimText, bidPromiseModeSupported } from '@/game/bid-promises';
|
|
import { Button } from './ui/button';
|
|
|
|
type BidPromiseProps = { game: GameView; busy: boolean; send: (action: Action) => void };
|
|
|
|
/** The server's current guide owns bid feasibility and funding. This leaf only
|
|
* checks that its personalized event and public action opportunity stay live. */
|
|
export function BidPromiseGuideButton(props: BidPromiseProps) {
|
|
const game = props.game, guide = game.bidPromiseGuide, lot = game.bidPromiseLot;
|
|
if (!guide || !lot) return null;
|
|
const actor = game.me, turn = lot.turn, number = lot.lot;
|
|
const action = guide.action, event = action.bidPromiseEvent, signature = JSON.stringify(action);
|
|
const canAct = () => {
|
|
const current = props.game, me = current.players.find(player => player.id === current.me);
|
|
return !props.busy && current === game && bidPromiseModeSupported(current) && current.status === 'playing' && current.phase === 3 &&
|
|
current.me === actor && current.auction?.active === actor &&
|
|
current.turn === turn && current.bidPromiseLot?.turn === turn && current.bidPromiseLot.lot === number &&
|
|
current.bidPromiseGuide === guide && current.bidPromiseGuide.action === action && JSON.stringify(action) === signature &&
|
|
typeof event === 'string' && (action.type === 'bid' || action.type === 'passBid') &&
|
|
(current.bidPromises.some(promise => promise.event === event && promise.player === actor &&
|
|
promise.turn === turn && promise.lot === number && !promise.fulfilled && !promise.released) ||
|
|
current.mixedBidPromises.some(promise => promise.event === event && promise.player === actor &&
|
|
promise.turn === turn && promise.lot === number && !promise.fulfilled && !promise.released)) &&
|
|
!!me && !me.autopilot && !current.truthtrance && !current.response && !current.decision &&
|
|
!current.phaseOpening && !current.automaticContinuationPending && !current.roomControl?.paused && !current.roomControl?.closed;
|
|
};
|
|
return <Button className="game-action min-h-11 whitespace-normal" data-testid="bid-promise-guide"
|
|
disabled={!canAct()} onClick={() => { if (canAct()) props.send(action); }}>
|
|
{action.type === 'bid'
|
|
? `Bid ${typeof action.amount === 'number' ? action.amount : 'the suggested amount'} spice while honoring your answers`
|
|
: 'Pass this bid while honoring your answers'}
|
|
</Button>;
|
|
}
|
|
|
|
export function BidPromises(props: BidPromiseProps) {
|
|
const own = props.game.bidPromises.filter(promise => promise.player === props.game.me);
|
|
if (!own.length) return null;
|
|
const waiting = own.some(promise => !promise.fulfilled && !promise.released &&
|
|
props.game.bidPromiseLot?.turn === props.game.turn && promise.turn === props.game.turn && promise.lot === props.game.bidPromiseLot.lot);
|
|
return <section className="notice space-y-3" aria-label="Your Truthtrance bid promises" id="bid-promise-guidance">
|
|
<h3>Your bid promises</h3>
|
|
<ul>{own.map(promise => <li key={promise.event}>
|
|
Turn {promise.turn}, ordinary lot {promise.lot}: You answered {promise.answer ? 'Yes' : 'No'} to {bidClaimText(promise)}.
|
|
{promise.released ? ' Released: this commitment is no longer possible.'
|
|
: promise.fulfilled ? ' Fulfilled: this commitment is complete.' : ' This answer is binding for this lot.'}
|
|
</li>)}</ul>
|
|
<p>
|
|
Yes requires one qualifying bid from your answer until this lot closes, while it remains possible.
|
|
No forbids every later bid at or above that minimum through the close of this lot.
|
|
A qualifying declaration fulfills this answer even if another player wins.
|
|
If you win, the original payment rules still apply.
|
|
</p>
|
|
<p className="fine">
|
|
<a href="/rules#truthtrance-bid">Bid promise rules</a>.{' '}
|
|
These commitments concern the current ordinary bank lot. Optional modules and native special auctions are excluded.
|
|
Ordinary bids keep their existing funding and payment rules.
|
|
</p>
|
|
{waiting && props.game.auction?.active !== props.game.me && <p>Your next bid opportunity is still ahead; this promise remains visible while others bid.</p>}
|
|
{waiting && <BidPromiseGuideButton {...props} />}
|
|
</section>;
|
|
}
|