From 3987c5c16b947c22ca9f406e657e3fdbcfc8b849 Mon Sep 17 00:00:00 2001 From: Quintine <15965533+Quintine@users.noreply.github.com> Date: Thu, 29 Oct 2026 17:39:06 +1100 Subject: [PATCH] Allow travelers to pay in two-party travel deals --- components/public-deals.tsx | 8 ++-- docs/CURRENT_STATUS.md | 10 +++++ docs/PUBLIC_DEALS.md | 20 +++++++-- game/engine.ts | 2 +- game/public-deals.ts | 7 ++- game/reference.ts | 1 + .../public-deals-paid-travel-runtime.test.ts | 43 ++++++++++++++++++- tests/public-deals-travel-controls.test.tsx | 11 ++++- tests/public-deals-travel-schema.test.ts | 9 ++-- 9 files changed, 90 insertions(+), 21 deletions(-) diff --git a/components/public-deals.tsx b/components/public-deals.tsx index b3d81b6c..9b8e00ce 100644 --- a/components/public-deals.tsx +++ b/components/public-deals.tsx @@ -72,7 +72,7 @@ export function publicDealOfferAction(targets: readonly string[], terms: string, (groupTravel ? !!payment || travelKeys !== 'actor,answer,kind,minimum,territory' || typeof travel.actor !== 'string' || !targets.includes(travel.actor) : travelKeys !== 'answer,kind,minimum,territory' || - !!payment && (payment.kind !== 'immediateBribe' || payment.payer !== proposer || payment.recipient !== targets[0])) || + !!payment && payment.kind !== 'immediateBribe') || (Object.hasOwn(travel, 'dueTurn') && (!Number.isSafeInteger(travelDeadline) || (travelDeadline as number) < 1 || (currentTurn !== undefined && (!Number.isSafeInteger(currentTurn) || currentTurn < 1 || (travelDeadline as number) < currentTurn)))) || !['ship', 'move'].includes(travel.kind) || !TERRITORIES.some(territory => territory.id === travel.territory) || @@ -227,7 +227,7 @@ export function PublicDeals({ game, act, busy }: { ...(selected.length > 1 ? {actor:travelActor} : {}), ...(travelTurn > game.turn ? {dueTurn:travelTurn} : {})} : undefined; const combinedPaymentValid = !includeTravel || !payment || payment.kind === 'immediateBribe' && - payment.payer === me?.id && selected.length === 1 && payment.recipient === selected[0]; + selected.length === 1 && paymentPeopleAndAmountValid; const offer = legalSelection && paymentValid && (!includeTravel || travelDueValid) ? publicDealOfferAction(selected, terms, payment, me?.id, game.turn, travel) : null; const disabled = busy || !!game.roomControl?.paused || !!game.roomControl?.closed || !!me?.autopilot; @@ -308,8 +308,8 @@ export function PublicDeals({ game, act, busy }: { {includeTravel &&
Travel term

This term concerns one physical action during the selected turn. The sole recipient commits to travel in a two-party deal. In a group deal, choose one recipient to commit; they accept last after all other recipients. Written terms remain player-managed.

-

A two-party travel term may include an immediate payment from the proposer to the recipient. The recipient collects their share at the next Mentat opening and can use it after collection. Group travel terms cannot include a payment.

- {!combinedPaymentValid &&

For two-party travel, choose an immediate payment from the proposer to the selected recipient. Group travel cannot include payment.

} +

A two-party travel term may include an immediate payment in either direction. The payment recipient collects their share at the next Mentat opening and can use it after collection. Group travel terms cannot include a payment.

+ {!combinedPaymentValid &&

For two-party travel, choose an immediate payment between the two participants. Group travel cannot include payment.

} {selected.length > 1 &&