Add Truthtrance weapon and defense types
Verify and publish checkpoint container / container (push) Failing after 11m16s
Verify and publish checkpoint container / container (push) Failing after 11m16s
This commit is contained in:
1 parent
89589878fc
commit
6eb417ff32
7 files changed
+117
-12
No files matched your search
+1
-1
@@ -3208,7 +3208,7 @@ export const RULE_TOPICS: RuleTopic[] = [
|
||||
steps: [
|
||||
'Declare Truthtrance, choose Verified current facts, then Hand size or primary card role. Select all cards or a primary role, a comparison and a whole count of zero or more. Review the question before asking.',
|
||||
'Count only physical cards currently in the target’s hand, including reserved cards still held there. Decks, discards, separate caches and cards already on the table do not count. Every held physical copy counts once.',
|
||||
'Primary roles are the cards’ default roles. Weirding Way is a weapon; Chemistry is a defense. Alternate battle roles do not change these counts. Worthless cards and Cheap Heroes or Heroines are separate categories. Bene Gesserit and CHOAM powers do not turn held Worthless cards into another category.',
|
||||
'Primary roles are the cards’ default roles. Choose projectile or poison weapons, or projectile or poison defenses, to count those primary types. A dual-type card counts once in each matching type. Weirding Way is primarily a weapon; Chemistry is primarily a defense. Alternate battle roles do not change these counts. Worthless cards and Cheap Heroes or Heroines are separate categories. Bene Gesserit and CHOAM powers do not turn held Worthless cards into another category.',
|
||||
'Richese cards use their printed roles: Stone Burner and Mirror Weapon are weapons; Portable Snooper is a defense. Other Special cards belong to the Special category. A classification question does not make an unfinished card effect available.',
|
||||
'Only the target receives the verified answer before answering. Other players see the question and its final aggregate Yes/No answer, without a list of matching cards. An exact comparison answered Yes necessarily confirms that count. AND/OR combinations publish only their combined result.',
|
||||
'These are current facts, not promises to keep cards or to use them in battle. All four AI levels answer from their own verified result. Broader freeform questions and future-action commitments remain unfinished.',
|
||||
|
||||
@@ -4,11 +4,17 @@ import {
|
||||
printedCardCategory,
|
||||
type CardCategory,
|
||||
} from './card-category';
|
||||
import { defenseTypes, weaponTypes } from './battle-cards';
|
||||
|
||||
export const HAND_INVENTORY_TYPES = [
|
||||
'projectileWeapon', 'poisonWeapon', 'projectileDefense', 'poisonDefense',
|
||||
] as const;
|
||||
type HandInventoryType = (typeof HAND_INVENTORY_TYPES)[number];
|
||||
|
||||
/** A current custody question, not a promise to retain cards or use them. */
|
||||
export type HandInventoryFact = {
|
||||
kind: 'handInventory';
|
||||
category: 'all' | CardCategory;
|
||||
category: 'all' | CardCategory | HandInventoryType;
|
||||
compare: 'eq' | 'gte' | 'lte';
|
||||
value: number;
|
||||
};
|
||||
@@ -23,6 +29,10 @@ export const HAND_INVENTORY_LABELS: Record<
|
||||
worthless: 'Worthless cards',
|
||||
hero: 'Cheap Heroes and Heroines',
|
||||
special: 'Special cards without a primary battle role',
|
||||
projectileWeapon: 'Projectile weapons · primary role',
|
||||
poisonWeapon: 'Poison weapons · primary role',
|
||||
projectileDefense: 'Projectile defenses · primary role',
|
||||
poisonDefense: 'Poison defenses · primary role',
|
||||
};
|
||||
|
||||
export function parseHandInventoryFact(
|
||||
@@ -31,7 +41,8 @@ export function parseHandInventoryFact(
|
||||
if (
|
||||
v.kind !== 'handInventory' ||
|
||||
(v.category !== 'all' &&
|
||||
!CARD_CATEGORIES.includes(v.category as CardCategory))
|
||||
!CARD_CATEGORIES.includes(v.category as CardCategory) &&
|
||||
!HAND_INVENTORY_TYPES.includes(v.category as HandInventoryType))
|
||||
)
|
||||
throw new Error('Choose all held cards or a known primary card role.');
|
||||
if (v.compare !== 'eq' && v.compare !== 'gte' && v.compare !== 'lte')
|
||||
@@ -56,11 +67,15 @@ export function handInventoryFactMatches(
|
||||
hand: readonly Card[],
|
||||
fact: HandInventoryFact,
|
||||
): boolean {
|
||||
const count =
|
||||
fact.category === 'all'
|
||||
? hand.length
|
||||
: hand.filter((card) => printedCardCategory(card) === fact.category)
|
||||
.length;
|
||||
const category = fact.category;
|
||||
const count = category === 'all' ? hand.length : hand.filter((card) => {
|
||||
const primary = printedCardCategory(card);
|
||||
if (category === 'projectileWeapon' || category === 'poisonWeapon')
|
||||
return primary === 'weapon' && weaponTypes(card).includes(category === 'projectileWeapon' ? 'projectile' : 'poison');
|
||||
if (category === 'projectileDefense' || category === 'poisonDefense')
|
||||
return primary === 'defense' && defenseTypes(card).includes(category === 'projectileDefense' ? 'shield' : 'snooper');
|
||||
return primary === category;
|
||||
}).length;
|
||||
return fact.compare === 'eq'
|
||||
? count === fact.value
|
||||
: fact.compare === 'gte'
|
||||
@@ -83,6 +98,10 @@ export function handInventoryFactText(fact: HandInventoryFact): string {
|
||||
worthless: `Worthless ${cards}`,
|
||||
hero: `Cheap Hero or Cheap Heroine ${cards}`,
|
||||
special: `Special ${cards} without a primary battle role`,
|
||||
projectileWeapon: `${cards} whose primary role is a projectile weapon`,
|
||||
poisonWeapon: `${cards} whose primary role is a poison weapon`,
|
||||
projectileDefense: `${cards} whose primary role is a projectile defense`,
|
||||
poisonDefense: `${cards} whose primary role is a poison defense`,
|
||||
};
|
||||
return `you currently hold ${comparison} ${fact.value} ${description[fact.category]}`;
|
||||
}
|
||||
Reference in new issue
Block a user