343 lines
13 KiB
C
343 lines
13 KiB
C
#include "tarot.h"
|
|
#include "i18n.h"
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
|
|
/* Card meanings are condensed from A. E. Waite, The Pictorial Key to the
|
|
* Tarot (1911), Part III §3 "The Greater Arcana and their Divinatory
|
|
* Meanings" (public domain). Waite gives several alternative readings
|
|
* for some cards; these keep his own wording, trimmed to the core
|
|
* clauses. */
|
|
typedef struct {
|
|
const char *name;
|
|
const char *image_file;
|
|
const char *upright;
|
|
const char *reversed;
|
|
} TarotCardInfo;
|
|
|
|
static const TarotCardInfo k_cards[TAROT_DECK_SIZE] = {
|
|
[CARD_FOOL] = {
|
|
"The Fool", "RWS_Tarot_00_Fool.jpeg",
|
|
"Folly, mania, extravagance, intoxication, delirium, frenzy.",
|
|
"Negligence, absence, carelessness, apathy, nullity, vanity."
|
|
},
|
|
[CARD_MAGICIAN] = {
|
|
"The Magician", "RWS_Tarot_01_Magician.jpeg",
|
|
"Skill, diplomacy, address, subtlety; self-confidence, will.",
|
|
"Physician, Magus, mental disease, disgrace, disquiet."
|
|
},
|
|
[CARD_HIGH_PRIESTESS] = {
|
|
"The High Priestess", "RWS_Tarot_02_High_Priestess.jpeg",
|
|
"Secrets, mystery, the future as yet unrevealed; silence, tenacity, wisdom, science.",
|
|
"Passion, moral or physical ardour, conceit, surface knowledge."
|
|
},
|
|
[CARD_EMPRESS] = {
|
|
"The Empress", "RWS_Tarot_03_Empress.jpeg",
|
|
"Fruitfulness, action, initiative, length of days; also difficulty, doubt, ignorance.",
|
|
"Light, truth, the unravelling of involved matters, public rejoicings; vacillation."
|
|
},
|
|
[CARD_EMPEROR] = {
|
|
"The Emperor", "RWS_Tarot_04_Emperor.jpeg",
|
|
"Stability, power, protection, realization; aid, reason, conviction, authority and will.",
|
|
"Benevolence, compassion, credit; also confusion to enemies, obstruction, immaturity."
|
|
},
|
|
[CARD_HIEROPHANT] = {
|
|
"The Hierophant", "RWS_Tarot_05_Hierophant.jpeg",
|
|
"Marriage, alliance, captivity, servitude; by another account, mercy, goodness, inspiration.",
|
|
"Society, good understanding, concord, over-kindness, weakness."
|
|
},
|
|
[CARD_LOVERS] = {
|
|
"The Lovers", "RWS_Tarot_06_Lovers.jpeg",
|
|
"Attraction, love, beauty, trials overcome.",
|
|
"Failure, foolish designs; marriage frustrated, contrarieties of all kinds."
|
|
},
|
|
[CARD_CHARIOT] = {
|
|
"The Chariot", "RWS_Tarot_07_Chariot.jpeg",
|
|
"Succour, providence; also war, triumph, presumption, vengeance, trouble.",
|
|
"Riot, quarrel, dispute, litigation, defeat."
|
|
},
|
|
[CARD_STRENGTH] = {
|
|
"Strength", "RWS_Tarot_08_Strength.jpeg",
|
|
"Power, energy, action, courage, magnanimity; complete success and honours.",
|
|
"Despotism, abuse of power, weakness, discord, sometimes even disgrace."
|
|
},
|
|
[CARD_HERMIT] = {
|
|
"The Hermit", "RWS_Tarot_09_Hermit.jpeg",
|
|
"Prudence, circumspection; also, and especially, treason, dissimulation, roguery, corruption.",
|
|
"Concealment, disguise, policy, fear, unreasoned caution."
|
|
},
|
|
[CARD_WHEEL_OF_FORTUNE] = {
|
|
"Wheel of Fortune", "RWS_Tarot_10_Wheel_of_Fortune.jpeg",
|
|
"Destiny, fortune, success, elevation, luck, felicity.",
|
|
"Increase, abundance, superfluity."
|
|
},
|
|
[CARD_JUSTICE] = {
|
|
"Justice", "RWS_Tarot_11_Justice.jpeg",
|
|
"Equity, rightness, probity, executive; triumph of the deserving side in law.",
|
|
"Law in all its departments, legal complications, bigotry, bias, excessive severity."
|
|
},
|
|
[CARD_HANGED_MAN] = {
|
|
"The Hanged Man", "RWS_Tarot_12_Hanged_Man.jpeg",
|
|
"Wisdom, circumspection, discernment, trials, sacrifice, intuition, divination, prophecy.",
|
|
"Selfishness, the crowd, body politic."
|
|
},
|
|
[CARD_DEATH] = {
|
|
"Death", "RWS_Tarot_13_Death.jpeg",
|
|
"End, mortality, destruction, corruption; loss of a benefactor, or many contrarieties.",
|
|
"Inertia, sleep, lethargy, petrifaction, somnambulism; hope destroyed."
|
|
},
|
|
[CARD_TEMPERANCE] = {
|
|
"Temperance", "RWS_Tarot_14_Temperance.jpeg",
|
|
"Economy, moderation, frugality, management, accommodation.",
|
|
"Things connected with churches, religions, sects, the priesthood; disunion, competing interests."
|
|
},
|
|
[CARD_DEVIL] = {
|
|
"The Devil", "RWS_Tarot_15_Devil.jpeg",
|
|
"Ravage, violence, vehemence, extraordinary efforts, force, fatality.",
|
|
"Evil fatality, weakness, pettiness, blindness."
|
|
},
|
|
[CARD_TOWER] = {
|
|
"The Tower", "RWS_Tarot_16_Tower.jpeg",
|
|
"Misery, distress, indigence, adversity, calamity, disgrace, deception, ruin; unforeseen catastrophe.",
|
|
"The same in a lesser degree; also oppression, imprisonment, tyranny."
|
|
},
|
|
[CARD_STAR] = {
|
|
"The Star", "RWS_Tarot_17_Star.jpeg",
|
|
"Loss, theft, privation, abandonment; another reading says hope and bright prospects.",
|
|
"Arrogance, haughtiness, impotence."
|
|
},
|
|
[CARD_MOON] = {
|
|
"The Moon", "RWS_Tarot_18_Moon.jpeg",
|
|
"Hidden enemies, danger, calumny, darkness, terror, deception, occult forces, error.",
|
|
"Instability, inconstancy, silence, lesser degrees of deception and error."
|
|
},
|
|
[CARD_SUN] = {
|
|
"The Sun", "RWS_Tarot_19_Sun.jpeg",
|
|
"Material happiness, fortunate marriage, contentment.",
|
|
"The same in a lesser sense."
|
|
},
|
|
[CARD_JUDGEMENT] = {
|
|
"Judgement", "RWS_Tarot_20_Judgement.jpeg",
|
|
"Change of position, renewal, outcome; another account specifies total loss through lawsuit.",
|
|
"Weakness, pusillanimity, simplicity; also deliberation, decision, sentence."
|
|
},
|
|
[CARD_WORLD] = {
|
|
"The World", "RWS_Tarot_21_World.jpeg",
|
|
"Assured success, recompense, voyage, route, emigration, flight, change of place.",
|
|
"Inertia, fixity, stagnation, permanence."
|
|
},
|
|
};
|
|
|
|
/* Position names and one-line descriptions follow Waite's own account
|
|
* of "An Ancient Celtic Method of Divination" (Part III §7), in his
|
|
* original drawing order. Waite addresses the querent as "him"
|
|
* throughout; that wording is kept verbatim as the GENDER_MALE variant
|
|
* below, and GENDER_FEMALE/GENDER_UNSPECIFIED are original paraphrases
|
|
* written for this project (not Waite's own text) so the reading can
|
|
* address the person it's actually for. Each array is indexed by
|
|
* Gender (tarot.h): [GENDER_UNSPECIFIED], [GENDER_MALE], [GENDER_FEMALE]. */
|
|
typedef struct {
|
|
const char *name[3];
|
|
const char *description[3];
|
|
} PositionInfo;
|
|
|
|
static const PositionInfo k_positions[TAROT_SPREAD_SIZE] = {
|
|
[POSITION_PRESENT] = {
|
|
{ "The Present", "The Present", "The Present" },
|
|
{
|
|
"This covers them: the general influence affecting the matter.",
|
|
"This covers him: the general influence affecting the matter.",
|
|
"This covers her: the general influence affecting the matter.",
|
|
}
|
|
},
|
|
[POSITION_CHALLENGE] = {
|
|
{ "The Challenge", "The Challenge", "The Challenge" },
|
|
{
|
|
"This crosses them: the nature of the obstacle in the matter.",
|
|
"This crosses him: the nature of the obstacle in the matter.",
|
|
"This crosses her: the nature of the obstacle in the matter.",
|
|
}
|
|
},
|
|
[POSITION_CROWN] = {
|
|
{ "The Crown", "The Crown", "The Crown" },
|
|
{
|
|
"This crowns them: the aim or ideal, the best that can be achieved.",
|
|
"This crowns him: the aim or ideal, the best that can be achieved.",
|
|
"This crowns her: the aim or ideal, the best that can be achieved.",
|
|
}
|
|
},
|
|
[POSITION_FOUNDATION] = {
|
|
{ "The Foundation", "The Foundation", "The Foundation" },
|
|
{
|
|
"This is beneath them: the basis of the matter, already actual.",
|
|
"This is beneath him: the basis of the matter, already actual.",
|
|
"This is beneath her: the basis of the matter, already actual.",
|
|
}
|
|
},
|
|
[POSITION_RECENT_PAST] = {
|
|
{ "The Recent Past", "The Recent Past", "The Recent Past" },
|
|
{
|
|
"This is behind them: the influence that is just passing away.",
|
|
"This is behind him: the influence that is just passing away.",
|
|
"This is behind her: the influence that is just passing away.",
|
|
}
|
|
},
|
|
[POSITION_NEAR_FUTURE] = {
|
|
{ "The Near Future", "The Near Future", "The Near Future" },
|
|
{
|
|
"This is before them: the influence now coming into action.",
|
|
"This is before him: the influence now coming into action.",
|
|
"This is before her: the influence now coming into action.",
|
|
}
|
|
},
|
|
[POSITION_ATTITUDE] = {
|
|
{ "Themself", "Himself", "Herself" },
|
|
{
|
|
"Their position or attitude in the circumstances.",
|
|
"His position or attitude in the circumstances.",
|
|
"Her position or attitude in the circumstances.",
|
|
}
|
|
},
|
|
[POSITION_ENVIRONMENT] = {
|
|
{ "Their House", "His House", "Her House" },
|
|
{
|
|
"Their environment and the tendencies at work therein.",
|
|
"His environment and the tendencies at work therein.",
|
|
"Her environment and the tendencies at work therein.",
|
|
}
|
|
},
|
|
[POSITION_HOPES_AND_FEARS] = {
|
|
{ "Hopes and Fears", "Hopes and Fears", "Hopes and Fears" },
|
|
{
|
|
"Their hopes or fears in the matter.",
|
|
"His hopes or fears in the matter.",
|
|
"Her hopes or fears in the matter.",
|
|
}
|
|
},
|
|
[POSITION_OUTCOME] = {
|
|
{ "The Outcome", "The Outcome", "The Outcome" },
|
|
{
|
|
"What will come: the final result of the matter.",
|
|
"What will come: the final result of the matter.",
|
|
"What will come: the final result of the matter.",
|
|
}
|
|
},
|
|
};
|
|
|
|
/* Slugs used to build i18n.c lookup keys ("card.<slug>.name", etc.) -
|
|
* translation files key off these, not the enum names, so renaming a C
|
|
* enumerator never silently breaks a .lang file. */
|
|
static const char *const k_card_slug[TAROT_DECK_SIZE] = {
|
|
[CARD_FOOL] = "fool",
|
|
[CARD_MAGICIAN] = "magician",
|
|
[CARD_HIGH_PRIESTESS] = "high_priestess",
|
|
[CARD_EMPRESS] = "empress",
|
|
[CARD_EMPEROR] = "emperor",
|
|
[CARD_HIEROPHANT] = "hierophant",
|
|
[CARD_LOVERS] = "lovers",
|
|
[CARD_CHARIOT] = "chariot",
|
|
[CARD_STRENGTH] = "strength",
|
|
[CARD_HERMIT] = "hermit",
|
|
[CARD_WHEEL_OF_FORTUNE] = "wheel_of_fortune",
|
|
[CARD_JUSTICE] = "justice",
|
|
[CARD_HANGED_MAN] = "hanged_man",
|
|
[CARD_DEATH] = "death",
|
|
[CARD_TEMPERANCE] = "temperance",
|
|
[CARD_DEVIL] = "devil",
|
|
[CARD_TOWER] = "tower",
|
|
[CARD_STAR] = "star",
|
|
[CARD_MOON] = "moon",
|
|
[CARD_SUN] = "sun",
|
|
[CARD_JUDGEMENT] = "judgement",
|
|
[CARD_WORLD] = "world",
|
|
};
|
|
|
|
static const char *const k_position_slug[TAROT_SPREAD_SIZE] = {
|
|
[POSITION_PRESENT] = "present",
|
|
[POSITION_CHALLENGE] = "challenge",
|
|
[POSITION_CROWN] = "crown",
|
|
[POSITION_FOUNDATION] = "foundation",
|
|
[POSITION_RECENT_PAST] = "recent_past",
|
|
[POSITION_NEAR_FUTURE] = "near_future",
|
|
[POSITION_ATTITUDE] = "attitude",
|
|
[POSITION_ENVIRONMENT] = "environment",
|
|
[POSITION_HOPES_AND_FEARS] = "hopes_and_fears",
|
|
[POSITION_OUTCOME] = "outcome",
|
|
};
|
|
|
|
static const char *card_field(const char *slug, const char *field, const char *fallback) {
|
|
char key[64];
|
|
strcpy(key, "card.");
|
|
strcat(key, slug);
|
|
strcat(key, ".");
|
|
strcat(key, field);
|
|
return i18n_get(key, fallback);
|
|
}
|
|
|
|
/* Suffix appended to the i18n key for the male/female variants - the
|
|
* unspecified/neutral variant uses the plain, unsuffixed key so it
|
|
* matches the field name callers already know (e.g. "position.
|
|
* attitude.name"), same convention as card_field()'s upright/reversed
|
|
* field names above. */
|
|
static const char *const k_gender_suffix[3] = {
|
|
[GENDER_UNSPECIFIED] = "",
|
|
[GENDER_MALE] = ".male",
|
|
[GENDER_FEMALE] = ".female",
|
|
};
|
|
|
|
static const char *const k_gender_slug[3] = {
|
|
[GENDER_UNSPECIFIED] = "unspecified",
|
|
[GENDER_MALE] = "male",
|
|
[GENDER_FEMALE] = "female",
|
|
};
|
|
|
|
static const char *position_field(const char *slug, const char *field, Gender gender,
|
|
const char *fallback) {
|
|
char neutral_key[80];
|
|
strcpy(neutral_key, "position.");
|
|
strcat(neutral_key, slug);
|
|
strcat(neutral_key, ".");
|
|
strcat(neutral_key, field);
|
|
if (gender == GENDER_UNSPECIFIED) return i18n_get(neutral_key, fallback);
|
|
|
|
/* Not every position's text actually varies by gender (e.g. "The
|
|
* Outcome" has no pronoun) - the .lang files only define a ".male"/
|
|
* ".female" key where the wording differs. So look up the neutral key
|
|
* first and use *that* (translated, if a catalog is loaded) as the
|
|
* fallback for the gendered key, rather than jumping straight past a
|
|
* loaded translation to the hardcoded English `fallback` whenever the
|
|
* gendered key happens to be absent. */
|
|
const char *neutral_text = i18n_get(neutral_key, fallback);
|
|
char key[88];
|
|
strcpy(key, neutral_key);
|
|
strcat(key, k_gender_suffix[gender]);
|
|
return i18n_get(key, neutral_text);
|
|
}
|
|
|
|
const char *tarot_card_name(TarotCard card) {
|
|
return card_field(k_card_slug[card], "name", k_cards[card].name);
|
|
}
|
|
|
|
const char *tarot_card_image_file(TarotCard card) {
|
|
/* Not translated - it's a filename, not display text. */
|
|
return k_cards[card].image_file;
|
|
}
|
|
|
|
const char *tarot_card_meaning(TarotCard card, bool reversed) {
|
|
return card_field(k_card_slug[card], reversed ? "reversed" : "upright",
|
|
reversed ? k_cards[card].reversed : k_cards[card].upright);
|
|
}
|
|
|
|
const char *tarot_position_name(CelticCrossPosition position, Gender gender) {
|
|
return position_field(k_position_slug[position], "name", gender, k_positions[position].name[gender]);
|
|
}
|
|
|
|
const char *tarot_position_description(CelticCrossPosition position, Gender gender) {
|
|
return position_field(k_position_slug[position], "desc", gender,
|
|
k_positions[position].description[gender]);
|
|
}
|
|
|
|
const char *tarot_card_slug(TarotCard card) { return k_card_slug[card]; }
|
|
const char *tarot_position_slug(CelticCrossPosition position) { return k_position_slug[position]; }
|
|
const char *tarot_gender_slug(Gender gender) { return k_gender_slug[gender]; }
|