Connect provisional Basic Guild and Karama allied No-Field prices
This commit is contained in:
1 parent
2ede6b55ea
commit
8d55b8f3f7
11 files changed
+107
-17
No files matched your search
+5
-3
@@ -82,7 +82,7 @@ import {
|
||||
canUseAsKaramaRole,
|
||||
shrineTruthtranceBotAction,
|
||||
} from './shrine';
|
||||
import { reserveShipmentCost } from './shipment-price';
|
||||
import { reserveShipmentCost, alliedNoFieldShipmentPricing } from './shipment-price';
|
||||
import { quoteSmugglerShipment } from './smuggler-shipment';
|
||||
import { quoteSmugglerNoField } from './smuggler-no-field';
|
||||
import { spiceBankerBattleMaximum, spiceBankerModeSupported } from './spice-banker';
|
||||
@@ -153,15 +153,17 @@ const variation = (g: GameView, salt: string) => {
|
||||
};
|
||||
function alliedNoFieldOffer(g: GameView): Action | null {
|
||||
const me = g.players.find((p) => p.id === g.me)!;
|
||||
const ally = g.players.find(p => p.id === me.ally);
|
||||
const level = rank(g);
|
||||
const alliedNoField = g.richeseNoField;
|
||||
if (
|
||||
alliedNoField?.owner === me.id &&
|
||||
alliedNoField.canOfferAlly &&
|
||||
!alliedNoField.allyDeclined &&
|
||||
alliedNoField.private
|
||||
alliedNoField.private && ally
|
||||
) {
|
||||
const inventory = alliedNoField.private;
|
||||
const pricing = alliedNoFieldShipmentPricing(g, ally);
|
||||
const oldMarker = inventory.deployed;
|
||||
const oldValue =
|
||||
inventory.tokens.find((token) => token.id === oldMarker?.tokenId)
|
||||
@@ -202,7 +204,7 @@ function alliedNoFieldOffer(g: GameView): Action | null {
|
||||
.sort((a, b) => b.score - a.score);
|
||||
if (token) {
|
||||
const target = targets.find(
|
||||
({ t }) => (t.type === 'stronghold' ? 1 : 2) <= (me.spice ?? 0),
|
||||
({ t }) => (t.type === 'stronghold' ? pricing.stronghold : pricing.other) <= (me.spice ?? 0),
|
||||
);
|
||||
if (target)
|
||||
return {
|
||||
|
||||
+10
-3
@@ -469,6 +469,7 @@ import {
|
||||
guildShipmentCost,
|
||||
guildShipmentIncome,
|
||||
shipmentPaymentBounds,
|
||||
alliedNoFieldShipmentPricing,
|
||||
} from './shipment-price';
|
||||
import {
|
||||
TERROR_DEFINITIONS,
|
||||
@@ -3941,7 +3942,7 @@ function noFieldAllyOfferBlock(
|
||||
return 'No-Field inventory is not initialized.';
|
||||
if (ally.faction === 'fremen')
|
||||
return 'Fremen reserves are on the planet; this ability ships off-planet reserves.';
|
||||
if (ally.faction === 'guild' || g.karamaShipping?.player === ally.id)
|
||||
if (g.advanced && (ally.faction === 'guild' || g.karamaShipping?.player === ally.id))
|
||||
return 'Allied No-Field pricing with Guild or Karama discounts awaits a ruling.';
|
||||
if (
|
||||
g.richeseAllyBlocked?.turn === g.turn &&
|
||||
@@ -4000,7 +4001,8 @@ function alliedNoFieldQuote(
|
||||
false,
|
||||
advisors,
|
||||
);
|
||||
const cost = territory(offer.territory).type === 'stronghold' ? 1 : 2;
|
||||
const pricing = alliedNoFieldShipmentPricing(g, recipient);
|
||||
const cost = territory(offer.territory).type === 'stronghold' ? pricing.stronghold : pricing.other;
|
||||
requireRule(
|
||||
offer.payer !== 'both' || cost === 2,
|
||||
'An equal split requires the two-spice shipment price.',
|
||||
@@ -25665,7 +25667,8 @@ function commitShipment(g: Game, shipment: PendingShipment, settlement?: {recipi
|
||||
ally: p.ally,
|
||||
cost,
|
||||
allyPayment,
|
||||
bankOnly: g.karamaShipping?.player === p.id,
|
||||
bankOnly: g.karamaShipping?.player === p.id ||
|
||||
!!(shipment.alliedNoField && alliedNoFieldShipmentPricing(g, p).bankOnly),
|
||||
});
|
||||
if (!settlement?.recipient && guild && guildPayment > 0)
|
||||
g.response = guildPaymentResponse(g, guild.id, shipmentIncomeContributions(g, p, cost, allyPayment),
|
||||
@@ -32764,6 +32767,8 @@ export function viewGame(state: Game, id: string) {
|
||||
if (!offer || ![offer.owner, offer.recipient].includes(id))
|
||||
return null;
|
||||
const { event, owner, recipient, territory, sector, payer } = offer;
|
||||
const recipientSeat = g.players.find(player => player.id === recipient);
|
||||
const pricingPolicy = recipientSeat ? alliedNoFieldShipmentPricing(g, recipientSeat).policy : null;
|
||||
try {
|
||||
const {
|
||||
cost,
|
||||
@@ -32789,6 +32794,7 @@ export function viewGame(state: Game, id: string) {
|
||||
eliteMin,
|
||||
eliteMax,
|
||||
blocked: null as string | null,
|
||||
...(pricingPolicy ? { pricingPolicy } : {}),
|
||||
};
|
||||
} catch (error) {
|
||||
if (!(error instanceof RuleError)) throw error;
|
||||
@@ -32807,6 +32813,7 @@ export function viewGame(state: Game, id: string) {
|
||||
eliteMin: 0,
|
||||
eliteMax: 0,
|
||||
blocked: error.message,
|
||||
...(pricingPolicy ? { pricingPolicy } : {}),
|
||||
};
|
||||
}
|
||||
})(),
|
||||
|
||||
+3
-3
@@ -1016,7 +1016,7 @@ export const RULE_TOPICS: RuleTopic[] = [
|
||||
'A normal No-Field shipment pays the one-force price. The concealed token moves like a force. Before Battle, Richese may reveal it voluntarily; a token involved in battle is revealed with the Battle Plan. Revelation brings the indicated forces from reserves, limited by what remains there.',
|
||||
'The revealed token stays face up until another token is placed. Revealing it does not clear the previous-shipment restriction. An allied No-Field shipment reveals immediately and uses the ally’s reserves; an existing token must be revealed before another shipment.',
|
||||
'Before an eligible ally uses its shipment, the game holds an offer opportunity for Richese. Richese may pass or privately propose a token, destination and payment. The ally accepts the exact offer and chooses its permitted regular/elite mix, or declines without payment. Success consumes the ally’s shipment, not Richese’s shipment or either player’s movement.',
|
||||
'Allied shipment costs one spice into a stronghold or two elsewhere. Either ally may pay the full price; a two-spice shipment may instead cost each ally one. Resolve prevention before payment or revealing an existing Richese marker; a successful shipment reveals that old marker first, then immediately reveals and materializes the allied token. Fremen reserves are on the planet, so they are not eligible for this off-planet method. Guild recipients and active Karama shipment discounts remain unavailable while their pricing is unresolved.',
|
||||
'Ordinary allied shipment costs one spice into a stronghold or two elsewhere, funded by either ally or one each at cost two. Basic Guild recipients and already-active Karama rates now use a visibly provisional one-spice price, paid entirely to bank even if Richese funds it; their Advanced counterparts remain guarded. Prevention precedes payment and the old-marker/new-token reveals. Fremen reserves are on the planet and remain ineligible.',
|
||||
'Storm and worm exposure reveal the token and destroy its forces. A zero token still had one-force presence before revelation. Trip to Gamont forces a reveal, then returns one force if present. Revealing a token already in a territory does not trigger Terror a second time.',
|
||||
'Development tables support your own concealed shipment, movement of a marker alone or with a physical group, voluntary reveal and one-force board presence. Physical reserves remain unchanged until reveal. Karama can prevent the No-Field shipment, with Guild shipment prevention and payment handled separately. Price and chosen allied funding are shown before shipment.',
|
||||
'The map shows a concealed marker without its denomination to everyone. Richese privately sees its token values, last-use restriction and reveal control. Movement selection keeps the marker separate from physical forces. No hidden denomination or physical token identity is included in another player’s projected board state.',
|
||||
@@ -1024,7 +1024,7 @@ export const RULE_TOPICS: RuleTopic[] = [
|
||||
'Basic ordinary mixed battles have a provisional private pool: physical forces already here plus the token value capped by current reserves. The Battle Wheel labels that interpretation explicitly. Both plans seal before materialization, then native casualties and aftermath use actual forces. This first version does not adjudicate the FAQ mixed dial cap.',
|
||||
'Concealed collection, occupancy and control use one-force presence. Exposed storm sectors and unprotected worm destruction reveal and remove the resulting actual forces. Storm shelter and other sectors remain separate. Trip to Gamont at a location with a marker reveals it, even if ordinary forces also occupy that sector, then returns one actual force if present. It is still used if zero forces remain. Canceling the CHOAM power leaves the marker concealed.',
|
||||
'All four AI profiles choose from their own private token inventory, pay only the one-force shipment price, move the marker explicitly and reveal positive tokens when useful. They retain zero-token presence and use only public information to assess opponents. They also offer safe positive tokens from their own inventory during the allied opportunity, fund them with their own spice and accept or decline private offers using their own force and payment information. A human Richese player’s offer window holds an AI ally’s shipment. Pending responses, decisions and Truthtrance retain priority.',
|
||||
'Advanced mixed-force battles, combined Ecaz Occupy and entire-plan special Karama inspection against a No-Field remain guarded. Guild-recipient and active-Karama allied pricing, Homeworld custody, broader Truthtrance and interrupted compositions remain unfinished. The Basic mixed-pool prototype is not a completed or publisher-verified formula. Richese starts remain disabled.',
|
||||
'Advanced mixed-force battles, combined Ecaz Occupy, Advanced Guild/Karama allied prices and entire-plan special Karama inspection against a No-Field remain guarded. Homeworld custody, broader Truthtrance and interrupted compositions remain unfinished. Basic mixed-pool and discounted-price prototypes are not publisher-adjudicated formulas. Richese starts remain disabled.',
|
||||
],
|
||||
related: [
|
||||
'richese-cards',
|
||||
@@ -1038,7 +1038,7 @@ export const RULE_TOPICS: RuleTopic[] = [
|
||||
area: 'Implementation',
|
||||
status: 'Partial',
|
||||
detail:
|
||||
'Own shipment, movement, voluntary reveal, effective presence, collection, storm/unprotected-worm destruction, Gamont reveal/return and marker-only battle integration work in development fixtures. Basic mixed physical/marker battle has a connected provisional pool, native reveal and aftermath. Allied offers, typed recipient consent, full/split funding, prevention and immediate reveal are integrated. Advanced/combined mixed battles, whole-plan inspection, Guild/Karama allied pricing and broader combinations remain guarded or unfinished.',
|
||||
'Own shipment, movement, reveal, effective presence, collection, hazards, Gamont and marker-only battles work in development fixtures. Basic mixed battles have a provisional private pool and native aftermath. Allied consent, typed forces, full/split funding, prevention and immediate reveal are integrated; Basic Guild/active-Karama offers have a shared provisional one-spice/bank quote. Advanced/combined mixed battles, Advanced discounted allied prices, whole-plan inspection and wider combinations remain separate.',
|
||||
},
|
||||
{
|
||||
area: 'Player controls',
|
||||
|
||||
@@ -24,6 +24,24 @@ export function guildShipmentCost(
|
||||
return halfRate ? Math.ceil(normal / 2) : normal;
|
||||
}
|
||||
|
||||
const ordinaryAlliedNoFieldPricing = Object.freeze({
|
||||
stronghold: 1, other: 2, bankOnly: false, policy: null,
|
||||
});
|
||||
const basicDiscountedAlliedNoFieldPricing = Object.freeze({
|
||||
stronghold: 1, other: 1, bankOnly: true,
|
||||
policy: 'Provisional Basic pricing: a Guild recipient or already-active Karama rate uses the rounded one-force Guild fee (1 spice), paid entirely to the bank. This price/payee precedence is not an adjudicated publisher ruling.',
|
||||
});
|
||||
|
||||
/** Basic first version; Advanced keeps its separate unresolved-entry guard. */
|
||||
export function alliedNoFieldShipmentPricing(
|
||||
context: { advanced: boolean; karamaShipping?: { player: string } | null },
|
||||
recipient: { id: string; faction: FactionId },
|
||||
) {
|
||||
return !context.advanced &&
|
||||
(recipient.faction === 'guild' || context.karamaShipping?.player === recipient.id)
|
||||
? basicDiscountedAlliedNoFieldPricing : ordinaryAlliedNoFieldPricing;
|
||||
}
|
||||
|
||||
/** A pledge is already escrowed. Selecting a share never spends a donor's
|
||||
* unpledged balance or enlarges the contribution they authorized.
|
||||
*/
|
||||
|
||||
Reference in new issue
Block a user