Connect Semuta to native faction discards
Verify and publish checkpoint container / container (push) Successful in 10m35s

This commit is contained in:
Quintine committed 2026-10-29 03:21:32 +11:00
1 parent 59579486a4
commit 87d1887139
7 files changed
+441 -13

No files matched your search

+16 -9
View File
@@ -9533,7 +9533,7 @@ function semutaOfferSupported(
g.pendingArrivalOverlap.ambassadorEvent === continuation.entry.ambassadorEvent &&
ambassadorTerrorEntryMatches(g, continuation.entry);
if (ambassadorDiscard &&
(!bareBasicSemutaProfile(g) || g.phase !== 5 ||
(!(bareBasicSemutaProfile(g) || basicIndependentRecruitsSemutaProfile(g) && !g.pendingArrivalOverlap) || g.phase !== 5 ||
continuation.entry.stage !== 'cards' || continuation.entry.resume !== 'none' ||
!['shipment', 'movement'].includes(continuation.entry.arrivalCause ?? '') ||
!['choam', 'ixians'].includes(continuation.entry.effect ?? '') ||
@@ -9706,9 +9706,13 @@ function semutaOfferSupported(
// A discard saved before this preview expansion may already be pending
// without a reaction. Keep its original automatic continuation; newly
// staged offers and already-saved reactions retain the current rules.
const independentNativeDiscard = ambassadorDiscard || continuation.kind === 'terrorDiscard' ||
continuation.kind === 'choamSaleDiscard' || continuation.kind === 'choamKulonDiscard' ||
continuation.kind === 'choamLaLaLaDiscard' || continuation.kind === 'choamBalisetDiscard' ||
continuation.kind === 'choamGamontDiscard' || continuation.kind === 'choamJubbaDiscard';
return (g.semutaPreview === true || g.semutaRecruitsPreview === true &&
(!g.pendingTreacheryDiscard || !!g.pendingTreacheryDiscard.reaction) &&
(continuation.kind === 'ordinaryCardDiscard' &&
(independentNativeDiscard || continuation.kind === 'ordinaryCardDiscard' &&
['hajr', 'ghola', 'harvester', 'weather', 'atomics'].includes(continuation.effect) ||
continuation.kind === 'battleResolved' || continuation.kind === 'winnerMandatoryDiscard' ||
continuation.kind === 'battleCleanup' && (continuation.source === 'winner' ||
@@ -9719,7 +9723,7 @@ function semutaOfferSupported(
continuation.kind === 'ornithopterDiscard' &&
(continuation.source === 'end' || continuation.source === 'move')
? basicIndependentRecruitsSemutaProfile(g) : basicIndependentSemutaProfile(g)) &&
(continuation.kind === 'ordinaryCardDiscard' &&
(independentNativeDiscard || continuation.kind === 'ordinaryCardDiscard' &&
['hajr', 'ghola', 'harvester', 'weather', 'atomics'].includes(continuation.effect) ||
continuation.kind === 'battleResolved' || continuation.kind === 'winnerMandatoryDiscard' ||
continuation.kind === 'battleCleanup' && (continuation.source === 'winner' ||
@@ -13209,6 +13213,9 @@ function basicIndependentRecruitsSemutaProfile(g: Game): boolean {
!g.discoveryEnabled && !g.discoveries && !g.discoveryStash && !g.greatMaker &&
!g.kullPreview && !g.nexusKullPreview && !g.moritaniAssassinatePreview;
}
function semutaCardProducerEnabled(g: Game): boolean {
return g.semutaPreview === true || g.semutaRecruitsPreview === true && basicIndependentRecruitsSemutaProfile(g);
}
/** Bare native Basic starts share the original deck/roster validation. Optional
* modules retain their own explicit Semuta envelopes; saved games are not upgraded. */
function bareBasicSemutaProfile(g: Game): boolean {
@@ -37130,7 +37137,7 @@ function finishResponse(g: Game, canceled: boolean, settlement?: BureaucratSettl
}
if (pending.effect === 'kulon') {
rememberMovementDiscards(g, [{ card: used, discardedBy: choam.id, publicFace: pending.nexusEvent === undefined }]);
if (g.semutaPreview && (used.name === 'Kulon' || pending.nexusEvent) &&
if (semutaCardProducerEnabled(g) && (used.name === 'Kulon' || pending.nexusEvent) &&
!pending.storm && !pending.revival && !pending.movement &&
!pending.mentat) {
const continuation: ChoamKulonDiscardContinuation = {
@@ -37148,7 +37155,7 @@ function finishResponse(g: Game, canceled: boolean, settlement?: BureaucratSettl
}
completeChoamKulon(g, choam);
} else if (pending.effect === 'jubba') {
if (g.semutaPreview && (used.name === 'Jubba Cloak' || pending.nexusEvent) &&
if (semutaCardProducerEnabled(g) && (used.name === 'Jubba Cloak' || pending.nexusEvent) &&
pending.storm && !pending.revival && !pending.movement &&
!pending.mentat) {
const continuation: ChoamJubbaDiscardContinuation = {
@@ -37165,7 +37172,7 @@ function finishResponse(g: Game, canceled: boolean, settlement?: BureaucratSettl
}
completeChoamJubba(g, choam, pending.location!);
} else if (pending.effect === 'baliset') {
if (g.semutaPreview && (used.name === 'Baliset' || pending.nexusEvent) &&
if (semutaCardProducerEnabled(g) && (used.name === 'Baliset' || pending.nexusEvent) &&
!pending.storm && !pending.revival && !pending.mentat &&
(!pending.movement || (g.pendingChoamMove && !g.pendingChoamMove.source &&
!g.pendingChoamMove.ornithopterEvent && !g.pendingChoamMove.ornithopterRange &&
@@ -37200,7 +37207,7 @@ function finishResponse(g: Game, canceled: boolean, settlement?: BureaucratSettl
if (ambassador) offerAmbassadorRelocation(g);
}
} else if (pending.effect === 'gamont') {
if (g.semutaPreview && (used.name === 'Trip to Gamont' || pending.nexusEvent) &&
if (semutaCardProducerEnabled(g) && (used.name === 'Trip to Gamont' || pending.nexusEvent) &&
!pending.storm && !pending.revival && !pending.movement) {
const continuation: ChoamGamontDiscardContinuation = {
kind: 'choamGamontDiscard', player: choam.id,
@@ -37222,7 +37229,7 @@ function finishResponse(g: Game, canceled: boolean, settlement?: BureaucratSettl
pending.elite!, pending.noFieldEvent,
{ card: used.id, mentat: pending.mentat, nexusEvent: pending.nexusEvent });
} else {
if (g.semutaPreview && (used.name === 'La La La' || pending.nexusEvent) &&
if (semutaCardProducerEnabled(g) && (used.name === 'La La La' || pending.nexusEvent) &&
!pending.storm && !pending.movement && !pending.mentat &&
(!pending.revival || (g.pendingRevival?.kind === 'forces' &&
g.pendingRevival.player === pending.target))) {
@@ -37504,7 +37511,7 @@ function finishResponse(g: Game, canceled: boolean, settlement?: BureaucratSettl
owner.spice += sale.price;
log(g, `${owner.name} received ${sale.price} spice from the card sale.`);
delete market.sale;
if (g.semutaPreview) {
if (semutaCardProducerEnabled(g)) {
const continuation: ChoamSaleDiscardContinuation = {
kind: 'choamSaleDiscard', owner: owner.id, card: used.id,
witness: sale.witness ?? null, price: sale.price,