1489 lines
41 KiB
C
1489 lines
41 KiB
C
/* SCCS Id: @(#)role.c 3.3 2000/08/20 */
|
|
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985-1999. */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
#include "hack.h"
|
|
|
|
|
|
/*** Table of all roles ***/
|
|
/* According to AD&D, HD for some classes (ex. Wizard) should be smaller
|
|
* (4-sided for wizards). But this is not AD&D, and using the AD&D
|
|
* rule here produces an unplayable character. Thus I have used a minimum
|
|
* of an 10-sided hit die for everything. Another AD&D change: wizards get
|
|
* a minimum strength of 4 since without one you can't teleport or cast
|
|
* spells. --KAA
|
|
*
|
|
* As the wizard has been updated (wizard patch 5 jun '96) their HD can be
|
|
* brought closer into line with AD&D. This forces wizards to use magic more
|
|
* and distance themselves from their attackers. --LSZ
|
|
*
|
|
* With the introduction of races, some hit points and energy
|
|
* has been reallocated for each race. The values assigned
|
|
* to the roles has been reduced by the amount allocated to
|
|
* humans. --KMH
|
|
*
|
|
* God names use a leading underscore to flag goddesses.
|
|
*/
|
|
const struct Role roles[] = {
|
|
{ {"Archeologist", 0}, {
|
|
{"Digger", 0},
|
|
{"Field Worker",0},
|
|
{"Investigator",0},
|
|
{"Exhumer", 0},
|
|
{"Excavator", 0},
|
|
{"Spelunker", 0},
|
|
{"Speleologist",0},
|
|
{"Collector", 0},
|
|
{"Curator", 0} },
|
|
"Quetzalcoatl", "Camaxtli", "Huhetotl", /* Central American */
|
|
"Arc", "the College of Archeology", "the Tomb of the Toltec Kings",
|
|
PM_ARCHEOLOGIST, NON_PM, NON_PM,
|
|
PM_LORD_CARNARVON, PM_STUDENT, PM_MINION_OF_HUHETOTL,
|
|
NON_PM, PM_HUMAN_MUMMY, S_SNAKE, S_MUMMY,
|
|
ART_ORB_OF_DETECTION,
|
|
MH_HUMAN|MH_DWARF|MH_GNOME | ROLE_MALE|ROLE_FEMALE |
|
|
ROLE_LAWFUL|ROLE_NEUTRAL,
|
|
/* Str Int Wis Dex Con Cha */
|
|
{ 7, 10, 10, 7, 7, 7 },
|
|
{ 20, 20, 20, 10, 20, 10 },
|
|
/* Init Lower Higher */
|
|
{ 11, 0, 0, 8, 1, 0 }, /* Hit points */
|
|
{ 1, 0, 0, 1, 0, 1 },14, /* Energy */
|
|
10, 5, 0, 2, 10, A_INT, SPE_MAGIC_MAPPING, -4
|
|
},
|
|
{ {"Barbarian", 0}, {
|
|
{"Plunderer", "Plunderess"},
|
|
{"Pillager", 0},
|
|
{"Bandit", 0},
|
|
{"Brigand", 0},
|
|
{"Raider", 0},
|
|
{"Reaver", 0},
|
|
{"Slayer", 0},
|
|
{"Chieftain", "Chieftainess"},
|
|
{"Conqueror", "Conqueress"} },
|
|
"Mitra", "Crom", "Set", /* Hyborian */
|
|
"Bar", "the Camp of the Duali Tribe", "the Duali Oasis",
|
|
PM_BARBARIAN, NON_PM, NON_PM,
|
|
PM_PELIAS, PM_CHIEFTAIN, PM_THOTH_AMON,
|
|
PM_OGRE, PM_TROLL, S_OGRE, S_TROLL,
|
|
ART_HEART_OF_AHRIMAN,
|
|
MH_HUMAN|MH_ORC | ROLE_MALE|ROLE_FEMALE |
|
|
ROLE_NEUTRAL|ROLE_CHAOTIC,
|
|
/* Str Int Wis Dex Con Cha */
|
|
{ 16, 7, 7, 15, 16, 6 },
|
|
{ 30, 6, 7, 20, 30, 7 },
|
|
/* Init Lower Higher */
|
|
{ 14, 0, 0,10, 2, 0 }, /* Hit points */
|
|
{ 1, 0, 0, 1, 0, 1 },10, /* Energy */
|
|
10, 14, 0, 0, 8, A_INT, SPE_HASTE_SELF, -4
|
|
},
|
|
{ {"Caveman", "Cavewoman"}, {
|
|
{"Troglodyte", 0},
|
|
{"Aborigine", 0},
|
|
{"Wanderer", 0},
|
|
{"Vagrant", 0},
|
|
{"Wayfarer", 0},
|
|
{"Roamer", 0},
|
|
{"Nomad", 0},
|
|
{"Rover", 0},
|
|
{"Pioneer", 0} },
|
|
"Anu", "_Ishtar", "Anshar", /* Babylonian */
|
|
"Cav", "the Caves of the Ancestors", "the Dragon's Lair",
|
|
PM_CAVEMAN, PM_CAVEWOMAN, PM_LITTLE_DOG,
|
|
PM_SHAMAN_KARNOV, PM_NEANDERTHAL, PM_CHROMATIC_DRAGON,
|
|
PM_BUGBEAR, PM_HILL_GIANT, S_HUMANOID, S_GIANT,
|
|
ART_SCEPTRE_OF_MIGHT,
|
|
MH_HUMAN|MH_DWARF|MH_GNOME | ROLE_MALE|ROLE_FEMALE |
|
|
ROLE_LAWFUL|ROLE_NEUTRAL,
|
|
/* Str Int Wis Dex Con Cha */
|
|
{ 10, 7, 7, 7, 8, 6 },
|
|
{ 30, 6, 7, 20, 30, 7 },
|
|
/* Init Lower Higher */
|
|
{ 14, 0, 0, 8, 2, 0 }, /* Hit points */
|
|
{ 1, 0, 0, 1, 0, 1 },10, /* Energy */
|
|
0, 12, 0, 1, 8, A_INT, SPE_DIG, -4
|
|
},
|
|
{ {"Healer", 0}, {
|
|
{"Rhizotomist", 0},
|
|
{"Empiric", 0},
|
|
{"Embalmer", 0},
|
|
{"Dresser", 0},
|
|
{"Medici ossium", 0},
|
|
{"Herbalist", 0},
|
|
{"Magister", 0},
|
|
{"Physician", 0},
|
|
{"Chirurgeon", 0} },
|
|
"_Athena", "Hermes", "Poseidon", /* Greek */
|
|
"Hea", "the Temple of Epidaurus", "the Temple of Coeus",
|
|
PM_HEALER, NON_PM, NON_PM,
|
|
PM_HIPPOCRATES, PM_ATTENDANT, PM_CYCLOPS,
|
|
PM_GIANT_RAT, PM_SNAKE, S_RODENT, S_YETI,
|
|
ART_STAFF_OF_AESCULAPIUS,
|
|
MH_HUMAN|MH_GNOME | ROLE_MALE|ROLE_FEMALE | ROLE_NEUTRAL,
|
|
/* Str Int Wis Dex Con Cha */
|
|
{ 7, 7, 13, 7, 11, 16 },
|
|
{ 15, 20, 20, 15, 25, 5 },
|
|
/* Init Lower Higher */
|
|
{ 11, 0, 0, 8, 1, 0 }, /* Hit points */
|
|
{ 1, 4, 0, 1, 0, 2 },20, /* Energy */
|
|
10, 3,-3, 2, 10, A_WIS, SPE_CURE_SICKNESS, -4
|
|
},
|
|
{ {"Knight", 0}, {
|
|
{"Gallant", 0},
|
|
{"Esquire", 0},
|
|
{"Bachelor", 0},
|
|
{"Sergeant", 0},
|
|
{"Knight", 0},
|
|
{"Banneret", 0},
|
|
{"Chevalier", 0},
|
|
{"Seignieur", 0},
|
|
{"Paladin", 0} },
|
|
"Lugh", "_Brigit", "Manannan Mac Lir", /* Celtic */
|
|
"Kni", "Camelot Castle", "the Isle of Glass",
|
|
PM_KNIGHT, NON_PM, PM_PONY,
|
|
PM_KING_ARTHUR, PM_PAGE, PM_IXOTH,
|
|
PM_QUASIT, PM_OCHRE_JELLY, S_IMP, S_JELLY,
|
|
ART_MAGIC_MIRROR_OF_MERLIN,
|
|
MH_HUMAN | ROLE_MALE|ROLE_FEMALE | ROLE_LAWFUL,
|
|
/* Str Int Wis Dex Con Cha */
|
|
{ 13, 7, 14, 8, 10, 17 },
|
|
{ 30, 15, 15, 10, 20, 10 },
|
|
/* Init Lower Higher */
|
|
{ 14, 0, 0, 8, 2, 0 }, /* Hit points */
|
|
{ 1, 4, 0, 1, 0, 2 },10, /* Energy */
|
|
10, 8,-2, 0, 9, A_WIS, SPE_TURN_UNDEAD, -4
|
|
},
|
|
{ {"Monk", 0}, {
|
|
{"Candidate", 0},
|
|
{"Novice", 0},
|
|
{"Initiate", 0},
|
|
{"Student of Stones", 0},
|
|
{"Student of Waters", 0},
|
|
{"Student of Metals", 0},
|
|
{"Student of Winds", 0},
|
|
{"Student of Fire", 0},
|
|
{"Master", 0} },
|
|
"Shan Lai Ching", "Chih Sung-tzu", "Huan Ti", /* Chinese */
|
|
"Mon", "the Monastery of Chan-Sune",
|
|
"the Monastery of the Earth-Lord",
|
|
PM_MONK, NON_PM, NON_PM,
|
|
PM_GRAND_MASTER, PM_ABBOT, PM_MASTER_KAEN,
|
|
PM_EARTH_ELEMENTAL, PM_XORN, S_ELEMENTAL, S_XORN,
|
|
ART_EYES_OF_THE_OVERWORLD,
|
|
MH_HUMAN | ROLE_MALE|ROLE_FEMALE |
|
|
ROLE_LAWFUL|ROLE_NEUTRAL|ROLE_CHAOTIC,
|
|
/* Str Int Wis Dex Con Cha */
|
|
{ 10, 7, 8, 8, 7, 7 },
|
|
{ 25, 10, 20, 20, 15, 10 },
|
|
/* Init Lower Higher */
|
|
{ 12, 0, 0, 8, 1, 0 }, /* Hit points */
|
|
{ 2, 2, 0, 2, 0, 2 },10, /* Energy */
|
|
10, 8,-2, 2, 20, A_WIS, SPE_RESTORE_ABILITY, -4
|
|
},
|
|
{ {"Priest", "Priestess"}, {
|
|
{"Aspirant", 0},
|
|
{"Acolyte", 0},
|
|
{"Adept", 0},
|
|
{"Priest", "Priestess"},
|
|
{"Curate", 0},
|
|
{"Canon", "Canoness"},
|
|
{"Lama", 0},
|
|
{"Patriarch", "Matriarch"},
|
|
{"High Priest", "High Priestess"} },
|
|
0, 0, 0, /* chosen randomly from among the other roles */
|
|
"Pri", "the Great Temple", "the Temple of Nalzok",
|
|
PM_PRIEST, PM_PRIESTESS, NON_PM,
|
|
PM_ARCH_PRIEST, PM_ACOLYTE, PM_NALZOK,
|
|
PM_HUMAN_ZOMBIE, PM_WRAITH, S_ZOMBIE, S_WRAITH,
|
|
ART_MITRE_OF_HOLINESS,
|
|
MH_HUMAN|MH_ELF | ROLE_MALE|ROLE_FEMALE |
|
|
ROLE_LAWFUL|ROLE_NEUTRAL|ROLE_CHAOTIC,
|
|
/* Str Int Wis Dex Con Cha */
|
|
{ 7, 7, 10, 7, 7, 7 },
|
|
{ 15, 10, 30, 15, 20, 10 },
|
|
/* Init Lower Higher */
|
|
{ 12, 0, 0, 8, 1, 0 }, /* Hit points */
|
|
{ 4, 3, 0, 2, 0, 2 },10, /* Energy */
|
|
0, 3,-2, 2, 10, A_WIS, SPE_REMOVE_CURSE, -4
|
|
},
|
|
/* Note: Rogue precedes Ranger so that use of `-R' on the command line
|
|
retains its traditional meaning. */
|
|
{ {"Rogue", 0}, {
|
|
{"Footpad", 0},
|
|
{"Cutpurse", 0},
|
|
{"Rogue", 0},
|
|
{"Pilferer", 0},
|
|
{"Robber", 0},
|
|
{"Burglar", 0},
|
|
{"Filcher", 0},
|
|
{"Magsman", "Magswoman"},
|
|
{"Thief", 0} },
|
|
"Issek", "Mog", "Kos", /* Nehwon */
|
|
"Rog", "the Thieves' Guild Hall", "the Assassins' Guild Hall",
|
|
PM_ROGUE, NON_PM, NON_PM,
|
|
PM_MASTER_OF_THIEVES, PM_THUG, PM_MASTER_ASSASSIN,
|
|
PM_LEPRECHAUN, PM_GUARDIAN_NAGA, S_NYMPH, S_NAGA,
|
|
ART_MASTER_KEY_OF_THIEVERY,
|
|
MH_HUMAN|MH_ORC | ROLE_MALE|ROLE_FEMALE |
|
|
ROLE_CHAOTIC,
|
|
/* Str Int Wis Dex Con Cha */
|
|
{ 7, 7, 7, 10, 7, 6 },
|
|
{ 20, 10, 10, 30, 20, 10 },
|
|
/* Init Lower Higher */
|
|
{ 10, 0, 0, 8, 1, 0 }, /* Hit points */
|
|
{ 1, 0, 0, 1, 0, 1 },11, /* Energy */
|
|
10, 8, 0, 1, 9, A_INT, SPE_DETECT_TREASURE, -4
|
|
},
|
|
{ {"Ranger", 0}, {
|
|
#if 0 /* OBSOLETE */
|
|
{"Edhel", "Elleth"},
|
|
{"Edhel", "Elleth"}, /* elf-maid */
|
|
{"Ohtar", "Ohtie"}, /* warrior */
|
|
{"Kano", /* commander (Q.) ['a] */
|
|
"Kanie"}, /* educated guess, until further research- SAC */
|
|
{"Arandur", /* king's servant, minister (Q.) - guess */
|
|
"Aranduriel"}, /* educated guess */
|
|
{"Hir", "Hiril"}, /* lord, lady (S.) ['ir] */
|
|
{"Aredhel", "Arwen"}, /* noble elf, maiden (S.) */
|
|
{"Ernil", "Elentariel"}, /* prince (S.), elf-maiden (Q.) */
|
|
{"Elentar", "Elentari"}, /* Star-king, -queen (Q.) */
|
|
"Solonor Thelandira", "Aerdrie Faenya", "Lolth", /* Elven */
|
|
#endif
|
|
{"Tenderfoot", 0},
|
|
{"Lookout", 0},
|
|
{"Trailblazer", 0},
|
|
{"Reconnoiterer", "Reconnoiteress"},
|
|
{"Scout", 0},
|
|
{"Arbalester", 0}, /* One skilled at crossbows */
|
|
{"Archer", 0},
|
|
{"Sharpshooter", 0},
|
|
{"Marksman", "Markswoman"} },
|
|
"Mercury", "_Venus", "Mars", /* Roman/planets */
|
|
"Ran", "Orion's camp", "the cave of the wumpus",
|
|
PM_RANGER, NON_PM, PM_LITTLE_DOG /* Orion & canis major */,
|
|
PM_ORION, PM_HUNTER, PM_SCORPIUS,
|
|
PM_FOREST_CENTAUR, PM_SCORPION, S_CENTAUR, S_SPIDER,
|
|
ART_LONGBOW_OF_DIANA,
|
|
MH_HUMAN|MH_ELF|MH_GNOME|MH_ORC | ROLE_MALE|ROLE_FEMALE |
|
|
ROLE_NEUTRAL|ROLE_CHAOTIC,
|
|
/* Str Int Wis Dex Con Cha */
|
|
{ 13, 13, 13, 9, 13, 7 },
|
|
{ 30, 10, 10, 20, 20, 10 },
|
|
/* Init Lower Higher */
|
|
{ 13, 0, 0, 6, 1, 0 }, /* Hit points */
|
|
{ 1, 0, 0, 1, 0, 1 },12, /* Energy */
|
|
10, 9, 2, 1, 10, A_INT, SPE_INVISIBILITY, -4
|
|
},
|
|
{ {"Samurai", 0}, {
|
|
{"Hatamoto", 0}, /* Banner Knight */
|
|
{"Ronin", 0}, /* no allegiance */
|
|
{"Ninja", "Kunoichi"}, /* secret society */
|
|
{"Joshu", 0}, /* heads a castle */
|
|
{"Ryoshu", 0}, /* has a territory */
|
|
{"Kokushu", 0}, /* heads a province */
|
|
{"Daimyo", 0}, /* a samurai lord */
|
|
{"Kuge", 0}, /* Noble of the Court */
|
|
{"Shogun", 0} },/* supreme commander, warlord */
|
|
"_Amaterasu Omikami", "Raijin", "Susanowo", /* Japanese */
|
|
"Sam", "the Castle of the Taro Clan", "the Shogun's Castle",
|
|
PM_SAMURAI, NON_PM, PM_LITTLE_DOG,
|
|
PM_LORD_SATO, PM_ROSHI, PM_ASHIKAGA_TAKAUJI,
|
|
PM_WOLF, PM_STALKER, S_DOG, S_ELEMENTAL,
|
|
ART_TSURUGI_OF_MURAMASA,
|
|
MH_HUMAN | ROLE_MALE|ROLE_FEMALE | ROLE_LAWFUL,
|
|
/* Str Int Wis Dex Con Cha */
|
|
{ 10, 8, 7, 10, 17, 6 },
|
|
{ 30, 10, 8, 30, 14, 8 },
|
|
/* Init Lower Higher */
|
|
{ 13, 0, 0, 8, 1, 0 }, /* Hit points */
|
|
{ 1, 0, 0, 1, 0, 1 },11, /* Energy */
|
|
10, 10, 0, 0, 8, A_INT, SPE_CLAIRVOYANCE, -4
|
|
},
|
|
#ifdef TOURIST
|
|
{ {"Tourist", 0}, {
|
|
{"Rambler", 0},
|
|
{"Sightseer", 0},
|
|
{"Excursionist",0},
|
|
{"Peregrinator","Peregrinatrix"},
|
|
{"Traveler", 0},
|
|
{"Journeyer", 0},
|
|
{"Voyager", 0},
|
|
{"Explorer", 0},
|
|
{"Adventurer", 0} },
|
|
"Blind Io", "_The Lady", "Offler", /* Discworld */
|
|
"Tou", "Ankh-Morpork", "the Thieves' Guild Hall",
|
|
PM_TOURIST, NON_PM, NON_PM,
|
|
PM_TWOFLOWER, PM_GUIDE, PM_MASTER_OF_THIEVES,
|
|
PM_GIANT_SPIDER, PM_FOREST_CENTAUR, S_SPIDER, S_CENTAUR,
|
|
ART_YENDORIAN_EXPRESS_CARD,
|
|
MH_HUMAN | ROLE_MALE|ROLE_FEMALE | ROLE_NEUTRAL,
|
|
/* Str Int Wis Dex Con Cha */
|
|
{ 7, 10, 6, 7, 7, 10 },
|
|
{ 15, 10, 10, 15, 30, 20 },
|
|
/* Init Lower Higher */
|
|
{ 8, 0, 0, 8, 0, 0 }, /* Hit points */
|
|
{ 1, 0, 0, 1, 0, 1 },14, /* Energy */
|
|
0, 5, 1, 2, 10, A_INT, SPE_CHARM_MONSTER, -4
|
|
},
|
|
#endif
|
|
{ {"Valkyrie", 0}, {
|
|
{"Stripling", 0},
|
|
{"Skirmisher", 0},
|
|
{"Fighter", 0},
|
|
{"Man-at-arms", "Woman-at-arms"},
|
|
{"Warrior", 0},
|
|
{"Swashbuckler",0},
|
|
{"Hero", "Heroine"},
|
|
{"Champion", 0},
|
|
{"Lord", "Lady"} },
|
|
"Tyr", "Odin", "Loki", /* Norse */
|
|
"Val", "the Shrine of Destiny", "the cave of Surtur",
|
|
PM_VALKYRIE, NON_PM, NON_PM /*PM_WINTER_WOLF_CUB*/,
|
|
PM_NORN, PM_WARRIOR, PM_LORD_SURTUR,
|
|
PM_FIRE_ANT, PM_FIRE_GIANT, S_ANT, S_GIANT,
|
|
ART_ORB_OF_FATE,
|
|
MH_HUMAN|MH_DWARF | ROLE_FEMALE | ROLE_LAWFUL|ROLE_NEUTRAL,
|
|
/* Str Int Wis Dex Con Cha */
|
|
{ 10, 7, 7, 7, 10, 7 },
|
|
{ 30, 6, 7, 20, 30, 7 },
|
|
/* Init Lower Higher */
|
|
{ 14, 0, 0, 8, 2, 0 }, /* Hit points */
|
|
{ 1, 0, 0, 1, 0, 1 },10, /* Energy */
|
|
0, 10,-2, 0, 9, A_WIS, SPE_CONE_OF_COLD, -4
|
|
},
|
|
{ {"Wizard", 0}, {
|
|
{"Evoker", 0},
|
|
{"Conjurer", 0},
|
|
{"Thaumaturge", 0},
|
|
{"Magician", 0},
|
|
{"Enchanter", "Enchantress"},
|
|
{"Sorcerer", "Sorceress"},
|
|
{"Necromancer", 0},
|
|
{"Wizard", 0},
|
|
{"Mage", 0} },
|
|
"Ptah", "Thoth", "Anhur", /* Egyptian */
|
|
"Wiz", "the Lonely Tower", "the Tower of Darkness",
|
|
PM_WIZARD, NON_PM, PM_KITTEN,
|
|
PM_NEFERET_THE_GREEN, PM_APPRENTICE, PM_DARK_ONE,
|
|
PM_VAMPIRE_BAT, PM_XORN, S_BAT, S_WRAITH,
|
|
ART_EYE_OF_THE_AETHIOPICA,
|
|
MH_HUMAN|MH_ELF|MH_GNOME|MH_ORC | ROLE_MALE|ROLE_FEMALE |
|
|
ROLE_NEUTRAL|ROLE_CHAOTIC,
|
|
/* Str Int Wis Dex Con Cha */
|
|
{ 7, 10, 7, 7, 7, 7 },
|
|
{ 10, 30, 10, 20, 20, 10 },
|
|
/* Init Lower Higher */
|
|
{ 10, 0, 0, 8, 1, 0 }, /* Hit points */
|
|
{ 4, 3, 0, 2, 0, 3 },12, /* Energy */
|
|
0, 1, 0, 3, 10, A_INT, SPE_MAGIC_MISSILE, -4
|
|
},
|
|
/* Array terminator */
|
|
{{0, 0}}
|
|
};
|
|
|
|
|
|
/* The player's role, created at runtime from initial
|
|
* choices. This may be munged in role_init().
|
|
*/
|
|
struct Role urole =
|
|
{ {"Undefined", 0}, { {0, 0}, {0, 0}, {0, 0},
|
|
{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0} },
|
|
"L", "N", "C", "Xxx", "home", "locate",
|
|
NON_PM, NON_PM, NON_PM, NON_PM, NON_PM, NON_PM,
|
|
NON_PM, NON_PM, 0, 0, 0, 0,
|
|
/* Str Int Wis Dex Con Cha */
|
|
{ 7, 7, 7, 7, 7, 7 },
|
|
{ 20, 15, 15, 20, 20, 10 },
|
|
/* Init Lower Higher */
|
|
{ 10, 0, 0, 8, 1, 0 }, /* Hit points */
|
|
{ 2, 0, 0, 2, 0, 3 },14, /* Energy */
|
|
0, 10, 0, 0, 4, A_INT, 0, -3
|
|
};
|
|
|
|
|
|
|
|
/* Table of all races */
|
|
const struct Race races[] = {
|
|
{ "human", "human", "humanity", "Hum",
|
|
{"man", "woman"},
|
|
PM_HUMAN, NON_PM, PM_HUMAN_MUMMY, PM_HUMAN_ZOMBIE,
|
|
MH_HUMAN | ROLE_MALE|ROLE_FEMALE |
|
|
ROLE_LAWFUL|ROLE_NEUTRAL|ROLE_CHAOTIC,
|
|
MH_HUMAN, 0, MH_GNOME|MH_ORC,
|
|
/* Str Int Wis Dex Con Cha */
|
|
{ 3, 3, 3, 3, 3, 3 },
|
|
{ STR18(100), 18, 18, 18, 18, 18 },
|
|
/* Init Lower Higher */
|
|
{ 2, 0, 0, 2, 1, 0 }, /* Hit points */
|
|
{ 1, 0, 2, 0, 2, 0 } /* Energy */
|
|
},
|
|
{ "elf", "elven", "elvenkind", "Elf",
|
|
{0, 0},
|
|
PM_ELF, NON_PM, PM_ELF_MUMMY, PM_ELF_ZOMBIE,
|
|
MH_ELF | ROLE_MALE|ROLE_FEMALE | ROLE_CHAOTIC,
|
|
MH_ELF, MH_ELF, MH_ORC,
|
|
/* Str Int Wis Dex Con Cha */
|
|
{ 3, 3, 3, 3, 3, 3 },
|
|
{ 18, 20, 20, 18, 16, 18 },
|
|
/* Init Lower Higher */
|
|
{ 1, 0, 0, 1, 1, 0 }, /* Hit points */
|
|
{ 2, 0, 3, 0, 3, 0 } /* Energy */
|
|
},
|
|
{ "dwarf", "dwarven", "dwarvenkind", "Dwa",
|
|
{0, 0},
|
|
PM_DWARF, NON_PM, PM_DWARF_MUMMY, PM_DWARF_ZOMBIE,
|
|
MH_DWARF | ROLE_MALE|ROLE_FEMALE | ROLE_LAWFUL,
|
|
MH_DWARF, MH_DWARF|MH_GNOME, MH_ORC,
|
|
/* Str Int Wis Dex Con Cha */
|
|
{ 3, 3, 3, 3, 3, 3 },
|
|
{ STR18(100), 16, 16, 20, 20, 16 },
|
|
/* Init Lower Higher */
|
|
{ 4, 0, 0, 3, 2, 0 }, /* Hit points */
|
|
{ 0, 0, 0, 0, 0, 0 } /* Energy */
|
|
},
|
|
{ "gnome", "gnomish", "gnomehood", "Gno",
|
|
{0, 0},
|
|
PM_GNOME, NON_PM, PM_GNOME_MUMMY, PM_GNOME_ZOMBIE,
|
|
MH_GNOME | ROLE_MALE|ROLE_FEMALE | ROLE_NEUTRAL,
|
|
MH_GNOME, MH_DWARF|MH_GNOME, MH_HUMAN,
|
|
/* Str Int Wis Dex Con Cha */
|
|
{ 3, 3, 3, 3, 3, 3 },
|
|
{STR18(50),19, 18, 18, 18, 18 },
|
|
/* Init Lower Higher */
|
|
{ 1, 0, 0, 1, 0, 0 }, /* Hit points */
|
|
{ 2, 0, 2, 0, 2, 0 } /* Energy */
|
|
},
|
|
{ "orc", "orcish", "orcdom", "Orc",
|
|
{0, 0},
|
|
PM_ORC, NON_PM, PM_ORC_MUMMY, PM_ORC_ZOMBIE,
|
|
MH_ORC | ROLE_MALE|ROLE_FEMALE | ROLE_CHAOTIC,
|
|
MH_ORC, 0, MH_HUMAN|MH_ELF|MH_DWARF,
|
|
/* Str Int Wis Dex Con Cha */
|
|
{ 3, 3, 3, 3, 3, 3 },
|
|
{STR18(50),16, 16, 18, 18, 16 },
|
|
/* Init Lower Higher */
|
|
{ 1, 0, 0, 1, 0, 0 }, /* Hit points */
|
|
{ 1, 0, 1, 0, 1, 0 } /* Energy */
|
|
},
|
|
/* Array terminator */
|
|
{ 0, 0, 0, 0 }};
|
|
|
|
|
|
/* The player's race, created at runtime from initial
|
|
* choices. This may be munged in role_init().
|
|
*/
|
|
struct Race urace =
|
|
{ "something", "undefined", "something", "Xxx",
|
|
{0, 0},
|
|
NON_PM, NON_PM, NON_PM, NON_PM,
|
|
0, 0, 0, 0,
|
|
/* Str Int Wis Dex Con Cha */
|
|
{ 3, 3, 3, 3, 3, 3 },
|
|
{ STR18(100), 18, 18, 18, 18, 18 },
|
|
/* Init Lower Higher */
|
|
{ 2, 0, 0, 2, 1, 0 }, /* Hit points */
|
|
{ 1, 0, 2, 0, 2, 0 } /* Energy */
|
|
};
|
|
|
|
|
|
/* Table of all genders */
|
|
const struct Gender genders[] = {
|
|
{"male", "he", "him", "his", "Mal", ROLE_MALE},
|
|
{"female", "she", "her", "her", "Fem", ROLE_FEMALE},
|
|
{"neuter", "it", "it", "its", "Ntr", ROLE_NEUTER}
|
|
};
|
|
|
|
|
|
/* Table of all alignments */
|
|
const struct Align aligns[] = {
|
|
{"law", "lawful", "Law", ROLE_LAWFUL, A_LAWFUL},
|
|
{"balance", "neutral", "Neu", ROLE_NEUTRAL, A_NEUTRAL},
|
|
{"chaos", "chaotic", "Cha", ROLE_CHAOTIC, A_CHAOTIC},
|
|
{"evil", "unaligned", "Una", 0, A_NONE}
|
|
};
|
|
|
|
STATIC_DCL int FDECL(role_gendercount, (int));
|
|
STATIC_DCL int FDECL(race_alignmentcount, (int));
|
|
|
|
/* used by str2XXX() */
|
|
static char NEARDATA randomstr[] = "random";
|
|
|
|
|
|
boolean
|
|
validrole(rolenum)
|
|
int rolenum;
|
|
{
|
|
return (rolenum >= 0 && rolenum < SIZE(roles)-1);
|
|
}
|
|
|
|
|
|
int
|
|
randrole()
|
|
{
|
|
return (rn2(SIZE(roles)-1));
|
|
}
|
|
|
|
|
|
int
|
|
str2role(str)
|
|
char *str;
|
|
{
|
|
int i, len;
|
|
|
|
/* Is str valid? */
|
|
if (!str || !str[0])
|
|
return ROLE_NONE;
|
|
|
|
/* Match as much of str as is provided */
|
|
len = strlen(str);
|
|
for (i = 0; roles[i].name.m; i++) {
|
|
/* Does it match the male name? */
|
|
if (!strncmpi(str, roles[i].name.m, len))
|
|
return i;
|
|
/* Or the female name? */
|
|
if (roles[i].name.f && !strncmpi(str, roles[i].name.f, len))
|
|
return i;
|
|
/* Or the filecode? */
|
|
if (!strcmpi(str, roles[i].filecode))
|
|
return i;
|
|
}
|
|
|
|
if ((len == 1 && (*str == '*' || *str == '@')) ||
|
|
!strncmpi(str, randomstr, len))
|
|
return ROLE_RANDOM;
|
|
|
|
/* Couldn't find anything appropriate */
|
|
return ROLE_NONE;
|
|
}
|
|
|
|
|
|
boolean
|
|
validrace(rolenum, racenum)
|
|
int rolenum, racenum;
|
|
{
|
|
/* Assumes validrole */
|
|
return (racenum >= 0 && racenum < SIZE(races)-1 &&
|
|
(roles[rolenum].allow & races[racenum].allow & ROLE_RACEMASK));
|
|
}
|
|
|
|
|
|
int
|
|
randrace(rolenum)
|
|
int rolenum;
|
|
{
|
|
int i, n = 0;
|
|
|
|
/* Count the number of valid races */
|
|
for (i = 0; races[i].noun; i++)
|
|
if (roles[rolenum].allow & races[i].allow & ROLE_RACEMASK)
|
|
n++;
|
|
|
|
/* Pick a random race */
|
|
/* Use a factor of 100 in case of bad random number generators */
|
|
if (n) n = rn2(n*100)/100;
|
|
for (i = 0; races[i].noun; i++)
|
|
if (roles[rolenum].allow & races[i].allow & ROLE_RACEMASK) {
|
|
if (n) n--;
|
|
else return (i);
|
|
}
|
|
|
|
/* This role has no permitted races? */
|
|
return (rn2(SIZE(races)-1));
|
|
}
|
|
|
|
|
|
int
|
|
str2race(str)
|
|
char *str;
|
|
{
|
|
int i, len;
|
|
|
|
/* Is str valid? */
|
|
if (!str || !str[0])
|
|
return ROLE_NONE;
|
|
|
|
/* Match as much of str as is provided */
|
|
len = strlen(str);
|
|
for (i = 0; races[i].noun; i++) {
|
|
/* Does it match the noun? */
|
|
if (!strncmpi(str, races[i].noun, len))
|
|
return i;
|
|
/* Or the filecode? */
|
|
if (!strcmpi(str, races[i].filecode))
|
|
return i;
|
|
}
|
|
|
|
if ((len == 1 && (*str == '*' || *str == '@')) ||
|
|
!strncmpi(str, randomstr, len))
|
|
return ROLE_RANDOM;
|
|
|
|
/* Couldn't find anything appropriate */
|
|
return ROLE_NONE;
|
|
}
|
|
|
|
|
|
boolean
|
|
validgend(rolenum, racenum, gendnum)
|
|
int rolenum, racenum, gendnum;
|
|
{
|
|
/* Assumes validrole and validrace */
|
|
return (gendnum >= 0 && gendnum < ROLE_GENDERS &&
|
|
(roles[rolenum].allow & races[racenum].allow &
|
|
genders[gendnum].allow & ROLE_GENDMASK));
|
|
}
|
|
|
|
|
|
int
|
|
randgend(rolenum, racenum)
|
|
int rolenum, racenum;
|
|
{
|
|
int i, n = 0;
|
|
|
|
/* Count the number of valid genders */
|
|
for (i = 0; i < ROLE_GENDERS; i++)
|
|
if (roles[rolenum].allow & races[racenum].allow &
|
|
genders[i].allow & ROLE_GENDMASK)
|
|
n++;
|
|
|
|
/* Pick a random gender */
|
|
if (n) n = rn2(n);
|
|
for (i = 0; i < ROLE_GENDERS; i++)
|
|
if (roles[rolenum].allow & races[racenum].allow &
|
|
genders[i].allow & ROLE_GENDMASK) {
|
|
if (n) n--;
|
|
else return (i);
|
|
}
|
|
|
|
/* This role/race has no permitted genders? */
|
|
return (rn2(ROLE_GENDERS));
|
|
}
|
|
|
|
|
|
int
|
|
str2gend(str)
|
|
char *str;
|
|
{
|
|
int i, len;
|
|
|
|
/* Is str valid? */
|
|
if (!str || !str[0])
|
|
return ROLE_NONE;
|
|
|
|
/* Match as much of str as is provided */
|
|
len = strlen(str);
|
|
for (i = 0; i < ROLE_GENDERS; i++) {
|
|
/* Does it match the adjective? */
|
|
if (!strncmpi(str, genders[i].adj, len))
|
|
return i;
|
|
/* Or the filecode? */
|
|
if (!strcmpi(str, genders[i].filecode))
|
|
return i;
|
|
}
|
|
if ((len == 1 && (*str == '*' || *str == '@')) ||
|
|
!strncmpi(str, randomstr, len))
|
|
return ROLE_RANDOM;
|
|
|
|
/* Couldn't find anything appropriate */
|
|
return ROLE_NONE;
|
|
}
|
|
|
|
|
|
boolean
|
|
validalign(rolenum, racenum, alignnum)
|
|
int rolenum, racenum, alignnum;
|
|
{
|
|
/* Assumes validrole and validrace */
|
|
return (alignnum >= 0 && alignnum < ROLE_ALIGNS &&
|
|
(roles[rolenum].allow & races[racenum].allow &
|
|
aligns[alignnum].allow & ROLE_ALIGNMASK));
|
|
}
|
|
|
|
|
|
int
|
|
randalign(rolenum, racenum)
|
|
int rolenum, racenum;
|
|
{
|
|
int i, n = 0;
|
|
|
|
/* Count the number of valid alignments */
|
|
for (i = 0; i < ROLE_ALIGNS; i++)
|
|
if (roles[rolenum].allow & races[racenum].allow &
|
|
aligns[i].allow & ROLE_ALIGNMASK)
|
|
n++;
|
|
|
|
/* Pick a random alignment */
|
|
if (n) n = rn2(n);
|
|
for (i = 0; i < ROLE_ALIGNS; i++)
|
|
if (roles[rolenum].allow & races[racenum].allow &
|
|
aligns[i].allow & ROLE_ALIGNMASK) {
|
|
if (n) n--;
|
|
else return (i);
|
|
}
|
|
|
|
/* This role/race has no permitted alignments? */
|
|
return (rn2(ROLE_ALIGNS));
|
|
}
|
|
|
|
|
|
int
|
|
str2align(str)
|
|
char *str;
|
|
{
|
|
int i, len;
|
|
|
|
/* Is str valid? */
|
|
if (!str || !str[0])
|
|
return ROLE_NONE;
|
|
|
|
/* Match as much of str as is provided */
|
|
len = strlen(str);
|
|
for (i = 0; i < ROLE_ALIGNS; i++) {
|
|
/* Does it match the adjective? */
|
|
if (!strncmpi(str, aligns[i].adj, len))
|
|
return i;
|
|
/* Or the filecode? */
|
|
if (!strcmpi(str, aligns[i].filecode))
|
|
return i;
|
|
}
|
|
if ((len == 1 && (*str == '*' || *str == '@')) ||
|
|
!strncmpi(str, randomstr, len))
|
|
return ROLE_RANDOM;
|
|
|
|
/* Couldn't find anything appropriate */
|
|
return ROLE_NONE;
|
|
}
|
|
|
|
/* is rolenum compatible with any racenum/gendnum/alignnum constraints? */
|
|
boolean
|
|
ok_role(rolenum, racenum, gendnum, alignnum)
|
|
int rolenum, racenum, gendnum, alignnum;
|
|
{
|
|
int i;
|
|
short allow;
|
|
|
|
if (rolenum >= 0 && rolenum < SIZE(roles)-1) {
|
|
allow = roles[rolenum].allow;
|
|
if (racenum >= 0 && racenum < SIZE(races)-1 &&
|
|
!(allow & races[racenum].allow & ROLE_RACEMASK))
|
|
return FALSE;
|
|
if (gendnum >= 0 && gendnum < ROLE_GENDERS &&
|
|
!(allow & genders[gendnum].allow & ROLE_GENDMASK))
|
|
return FALSE;
|
|
if (alignnum >= 0 && alignnum < ROLE_ALIGNS &&
|
|
!(allow & aligns[alignnum].allow & ROLE_ALIGNMASK))
|
|
return FALSE;
|
|
return TRUE;
|
|
} else {
|
|
for (i = 0; i < SIZE(roles)-1; i++) {
|
|
allow = roles[i].allow;
|
|
if (racenum >= 0 && racenum < SIZE(races)-1 &&
|
|
!(allow & races[racenum].allow & ROLE_RACEMASK))
|
|
continue;
|
|
if (gendnum >= 0 && gendnum < ROLE_GENDERS &&
|
|
!(allow & genders[gendnum].allow & ROLE_GENDMASK))
|
|
continue;
|
|
if (alignnum >= 0 && alignnum < ROLE_ALIGNS &&
|
|
!(allow & aligns[alignnum].allow & ROLE_ALIGNMASK))
|
|
continue;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* pick a random role subject to any racenum/gendnum/alignnum constraints */
|
|
/* If pickhow == PICK_RIGID a role is returned only if there is */
|
|
/* a single possibility */
|
|
int
|
|
pick_role(racenum, gendnum, alignnum, pickhow)
|
|
int racenum, gendnum, alignnum, pickhow;
|
|
{
|
|
int i;
|
|
int roles_ok = 0;
|
|
|
|
for (i = 0; i < SIZE(roles)-1; i++) {
|
|
if (ok_role(i, racenum, gendnum, alignnum))
|
|
roles_ok++;
|
|
}
|
|
if (roles_ok == 0 || (roles_ok > 1 && pickhow == PICK_RIGID))
|
|
return ROLE_NONE;
|
|
roles_ok = rn2(roles_ok);
|
|
for (i = 0; i < SIZE(roles)-1; i++) {
|
|
if (ok_role(i, racenum, gendnum, alignnum)) {
|
|
if (roles_ok == 0)
|
|
return i;
|
|
else
|
|
roles_ok--;
|
|
}
|
|
}
|
|
return ROLE_NONE;
|
|
}
|
|
|
|
/* is racenum compatible with any rolenum/gendnum/alignnum constraints? */
|
|
boolean
|
|
ok_race(rolenum, racenum, gendnum, alignnum)
|
|
int rolenum, racenum, gendnum, alignnum;
|
|
{
|
|
int i;
|
|
short allow;
|
|
|
|
if (racenum >= 0 && racenum < SIZE(races)-1) {
|
|
allow = races[racenum].allow;
|
|
if (rolenum >= 0 && rolenum < SIZE(roles)-1 &&
|
|
!(allow & roles[rolenum].allow & ROLE_RACEMASK))
|
|
return FALSE;
|
|
if (gendnum >= 0 && gendnum < ROLE_GENDERS &&
|
|
!(allow & genders[gendnum].allow & ROLE_GENDMASK))
|
|
return FALSE;
|
|
if (alignnum >= 0 && alignnum < ROLE_ALIGNS &&
|
|
!(allow & aligns[alignnum].allow & ROLE_ALIGNMASK))
|
|
return FALSE;
|
|
return TRUE;
|
|
} else {
|
|
for (i = 0; i < SIZE(races)-1; i++) {
|
|
allow = races[i].allow;
|
|
if (rolenum >= 0 && rolenum < SIZE(roles)-1 &&
|
|
!(allow & roles[rolenum].allow & ROLE_RACEMASK))
|
|
continue;
|
|
if (gendnum >= 0 && gendnum < ROLE_GENDERS &&
|
|
!(allow & genders[gendnum].allow & ROLE_GENDMASK))
|
|
continue;
|
|
if (alignnum >= 0 && alignnum < ROLE_ALIGNS &&
|
|
!(allow & aligns[alignnum].allow & ROLE_ALIGNMASK))
|
|
continue;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* pick a random race subject to any rolenum/gendnum/alignnum constraints */
|
|
/* If pickhow == PICK_RIGID a race is returned only if there is */
|
|
/* a single possibility */
|
|
int
|
|
pick_race(rolenum, gendnum, alignnum, pickhow)
|
|
int rolenum, gendnum, alignnum, pickhow;
|
|
{
|
|
int i;
|
|
int races_ok = 0;
|
|
|
|
for (i = 0; i < SIZE(races)-1; i++) {
|
|
if (ok_race(rolenum, i, gendnum, alignnum))
|
|
races_ok++;
|
|
}
|
|
if (races_ok == 0 || (races_ok > 1 && pickhow == PICK_RIGID))
|
|
return ROLE_NONE;
|
|
races_ok = rn2(races_ok);
|
|
for (i = 0; i < SIZE(races)-1; i++) {
|
|
if (ok_race(rolenum, i, gendnum, alignnum)) {
|
|
if (races_ok == 0)
|
|
return i;
|
|
else
|
|
races_ok--;
|
|
}
|
|
}
|
|
return ROLE_NONE;
|
|
}
|
|
|
|
/* is gendnum compatible with any rolenum/racenum/alignnum constraints? */
|
|
/* gender and alignment are not comparable (and also not constrainable) */
|
|
boolean
|
|
ok_gend(rolenum, racenum, gendnum, alignnum)
|
|
int rolenum, racenum, gendnum, alignnum;
|
|
{
|
|
int i;
|
|
short allow;
|
|
|
|
if (gendnum >= 0 && gendnum < ROLE_GENDERS) {
|
|
allow = genders[gendnum].allow;
|
|
if (rolenum >= 0 && rolenum < SIZE(roles)-1 &&
|
|
!(allow & roles[rolenum].allow & ROLE_GENDMASK))
|
|
return FALSE;
|
|
if (racenum >= 0 && racenum < SIZE(races)-1 &&
|
|
!(allow & races[racenum].allow & ROLE_GENDMASK))
|
|
return FALSE;
|
|
return TRUE;
|
|
} else {
|
|
for (i = 0; i < ROLE_GENDERS; i++) {
|
|
allow = genders[i].allow;
|
|
if (rolenum >= 0 && rolenum < SIZE(roles)-1 &&
|
|
!(allow & roles[rolenum].allow & ROLE_GENDMASK))
|
|
continue;
|
|
if (racenum >= 0 && racenum < SIZE(races)-1 &&
|
|
!(allow & races[racenum].allow & ROLE_GENDMASK))
|
|
continue;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* pick a random gender subject to any rolenum/racenum/alignnum constraints */
|
|
/* gender and alignment are not comparable (and also not constrainable) */
|
|
/* If pickhow == PICK_RIGID a gender is returned only if there is */
|
|
/* a single possibility */
|
|
int
|
|
pick_gend(rolenum, racenum, alignnum, pickhow)
|
|
int rolenum, racenum, alignnum, pickhow;
|
|
{
|
|
int i;
|
|
int gends_ok = 0;
|
|
|
|
for (i = 0; i < ROLE_GENDERS; i++) {
|
|
if (ok_gend(rolenum, racenum, i, alignnum))
|
|
gends_ok++;
|
|
}
|
|
if (gends_ok == 0 || (gends_ok > 1 && pickhow == PICK_RIGID))
|
|
return ROLE_NONE;
|
|
gends_ok = rn2(gends_ok);
|
|
for (i = 0; i < ROLE_GENDERS; i++) {
|
|
if (ok_gend(rolenum, racenum, i, alignnum)) {
|
|
if (gends_ok == 0)
|
|
return i;
|
|
else
|
|
gends_ok--;
|
|
}
|
|
}
|
|
return ROLE_NONE;
|
|
}
|
|
|
|
/* is alignnum compatible with any rolenum/racenum/gendnum constraints? */
|
|
/* alignment and gender are not comparable (and also not constrainable) */
|
|
boolean
|
|
ok_align(rolenum, racenum, gendnum, alignnum)
|
|
int rolenum, racenum, gendnum, alignnum;
|
|
{
|
|
int i;
|
|
short allow;
|
|
|
|
if (alignnum >= 0 && alignnum < ROLE_ALIGNS) {
|
|
allow = aligns[alignnum].allow;
|
|
if (rolenum >= 0 && rolenum < SIZE(roles)-1 &&
|
|
!(allow & roles[rolenum].allow & ROLE_ALIGNMASK))
|
|
return FALSE;
|
|
if (racenum >= 0 && racenum < SIZE(races)-1 &&
|
|
!(allow & races[racenum].allow & ROLE_ALIGNMASK))
|
|
return FALSE;
|
|
return TRUE;
|
|
} else {
|
|
for (i = 0; i < ROLE_ALIGNS; i++) {
|
|
allow = races[i].allow;
|
|
if (rolenum >= 0 && rolenum < SIZE(roles)-1 &&
|
|
!(allow & roles[rolenum].allow & ROLE_ALIGNMASK))
|
|
continue;
|
|
if (racenum >= 0 && racenum < SIZE(races)-1 &&
|
|
!(allow & races[racenum].allow & ROLE_ALIGNMASK))
|
|
continue;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* pick a random alignment subject to any rolenum/racenum/gendnum constraints */
|
|
/* alignment and gender are not comparable (and also not constrainable) */
|
|
/* If pickhow == PICK_RIGID an alignment is returned only if there is */
|
|
/* a single possibility */
|
|
int
|
|
pick_align(rolenum, racenum, gendnum, pickhow)
|
|
int rolenum, racenum, gendnum, pickhow;
|
|
{
|
|
int i;
|
|
int aligns_ok = 0;
|
|
|
|
for (i = 0; i < ROLE_ALIGNS; i++) {
|
|
if (ok_align(rolenum, racenum, gendnum, i))
|
|
aligns_ok++;
|
|
}
|
|
if (aligns_ok == 0 || (aligns_ok > 1 && pickhow == PICK_RIGID))
|
|
return ROLE_NONE;
|
|
aligns_ok = rn2(aligns_ok);
|
|
for (i = 0; i < ROLE_ALIGNS; i++) {
|
|
if (ok_align(rolenum, racenum, gendnum, i)) {
|
|
if (aligns_ok == 0)
|
|
return i;
|
|
else
|
|
aligns_ok--;
|
|
}
|
|
}
|
|
return ROLE_NONE;
|
|
}
|
|
|
|
void
|
|
rigid_role_checks()
|
|
{
|
|
/* Some roles are limited to a single race, alignment, or gender and
|
|
* calling this routine prior to XXX_player_selection() will help
|
|
* prevent an extraneous prompt that actually doesn't allow
|
|
* you to choose anything further. Note the use of PICK_RIGID which
|
|
* causes the pick_XX() routine to return a value only if there is one
|
|
* single possible selection, otherwise it returns ROLE_NONE.
|
|
*
|
|
*/
|
|
if (flags.initrole == ROLE_RANDOM) {
|
|
/* If the role was explicitly specified as ROLE_RANDOM
|
|
* via -uXXXX-@ then choose the role in here to narrow down
|
|
* later choices. Pick a random role in this case.
|
|
*/
|
|
flags.initrole = pick_role(flags.initrace, flags.initgend,
|
|
flags.initalign, PICK_RANDOM);
|
|
if (flags.initrole < 0)
|
|
flags.initrole = randrole();
|
|
}
|
|
if (flags.initrole != ROLE_NONE) {
|
|
if (flags.initrace == ROLE_NONE)
|
|
flags.initrace = pick_race(flags.initrole, flags.initgend,
|
|
flags.initalign, PICK_RIGID);
|
|
if (flags.initalign == ROLE_NONE)
|
|
flags.initalign = pick_align(flags.initrole, flags.initrace,
|
|
flags.initgend, PICK_RIGID);
|
|
if (flags.initgend == ROLE_NONE)
|
|
flags.initgend = pick_gend(flags.initrole, flags.initrace,
|
|
flags.initalign, PICK_RIGID);
|
|
}
|
|
}
|
|
|
|
#define BP_ALIGN 0
|
|
#define BP_GEND 1
|
|
#define BP_RACE 2
|
|
#define BP_ROLE 3
|
|
#define NUM_BP 4
|
|
|
|
STATIC_VAR char pa[NUM_BP], post_attribs;
|
|
|
|
STATIC_OVL char *
|
|
promptsep(buf, num_post_attribs)
|
|
char *buf;
|
|
int num_post_attribs;
|
|
{
|
|
const char *conj = "and ";
|
|
if (num_post_attribs > 1
|
|
&& post_attribs < num_post_attribs && post_attribs > 1)
|
|
Strcat(buf, ",");
|
|
Strcat(buf, " ");
|
|
--post_attribs;
|
|
if (!post_attribs && num_post_attribs > 1) Strcat(buf, conj);
|
|
return buf;
|
|
}
|
|
|
|
STATIC_OVL int
|
|
role_gendercount(rolenum)
|
|
int rolenum;
|
|
{
|
|
int gendcount = 0;
|
|
if (validrole(rolenum)) {
|
|
if (roles[rolenum].allow & ROLE_MALE) ++gendcount;
|
|
if (roles[rolenum].allow & ROLE_FEMALE) ++gendcount;
|
|
if (roles[rolenum].allow & ROLE_NEUTER) ++gendcount;
|
|
}
|
|
return gendcount;
|
|
}
|
|
|
|
STATIC_OVL int
|
|
race_alignmentcount(racenum)
|
|
int racenum;
|
|
{
|
|
int aligncount = 0;
|
|
if (racenum != ROLE_NONE && racenum != ROLE_RANDOM) {
|
|
if (races[racenum].allow & ROLE_CHAOTIC) ++aligncount;
|
|
if (races[racenum].allow & ROLE_LAWFUL) ++aligncount;
|
|
if (races[racenum].allow & ROLE_NEUTRAL) ++aligncount;
|
|
}
|
|
return aligncount;
|
|
}
|
|
|
|
char *
|
|
root_plselection_prompt(suppliedbuf, buflen, rolenum, racenum, gendnum, alignnum)
|
|
char *suppliedbuf;
|
|
int buflen, rolenum, racenum, gendnum, alignnum;
|
|
{
|
|
int k, gendercount = 0, aligncount = 0;
|
|
char buf[BUFSZ];
|
|
char *err_ret = " character's";
|
|
boolean donefirst = FALSE;
|
|
|
|
if (!suppliedbuf || buflen < 1) return err_ret;
|
|
|
|
/* initialize these static variables each time this is called */
|
|
post_attribs = 0;
|
|
for (k=0; k < NUM_BP; ++k)
|
|
pa[k] = 0;
|
|
buf[0] = '\0';
|
|
*suppliedbuf = '\0';
|
|
|
|
/* How many alignments are allowed for the desired race? */
|
|
if (racenum != ROLE_NONE && racenum != ROLE_RANDOM)
|
|
aligncount = race_alignmentcount(racenum);
|
|
|
|
if (alignnum != ROLE_NONE && alignnum != ROLE_RANDOM) {
|
|
/* if race specified, and multiple choice of alignments for it */
|
|
if ((racenum >= 0) && (aligncount > 1)) {
|
|
if (donefirst) Strcat(buf, " ");
|
|
Strcat(buf, aligns[alignnum].adj);
|
|
donefirst = TRUE;
|
|
} else {
|
|
if (donefirst) Strcat(buf, " ");
|
|
Strcat(buf, aligns[alignnum].adj);
|
|
donefirst = TRUE;
|
|
}
|
|
} else {
|
|
/* if alignment not specified, but race is specified
|
|
and only one choice of alignment for that race then
|
|
don't include it in the later list */
|
|
if ((((racenum != ROLE_NONE && racenum != ROLE_RANDOM) &&
|
|
ok_race(rolenum, racenum, gendnum, alignnum))
|
|
&& (aligncount > 1))
|
|
|| (racenum == ROLE_NONE || racenum == ROLE_RANDOM)) {
|
|
pa[BP_ALIGN] = 1;
|
|
post_attribs++;
|
|
}
|
|
}
|
|
/* <your lawful> */
|
|
|
|
/* How many genders are allowed for the desired role? */
|
|
if (validrole(rolenum))
|
|
gendercount = role_gendercount(rolenum);
|
|
|
|
if (gendnum != ROLE_NONE && gendnum != ROLE_RANDOM) {
|
|
if (validrole(rolenum)) {
|
|
/* if role specified, and multiple choice of genders for it,
|
|
and name of role itself does not distinguish gender */
|
|
if ((rolenum != ROLE_NONE) && (gendercount > 1)
|
|
&& !roles[rolenum].name.f) {
|
|
if (donefirst) Strcat(buf, " ");
|
|
Strcat(buf, genders[gendnum].adj);
|
|
donefirst = TRUE;
|
|
}
|
|
} else {
|
|
if (donefirst) Strcat(buf, " ");
|
|
Strcat(buf, genders[gendnum].adj);
|
|
donefirst = TRUE;
|
|
}
|
|
} else {
|
|
/* if gender not specified, but role is specified
|
|
and only one choice of gender then
|
|
don't include it in the later list */
|
|
if ((validrole(rolenum) && (gendercount > 1)) || !validrole(rolenum)) {
|
|
pa[BP_GEND] = 1;
|
|
post_attribs++;
|
|
}
|
|
}
|
|
/* <your lawful female> */
|
|
|
|
if (racenum != ROLE_NONE && racenum != ROLE_RANDOM) {
|
|
if (validrole(rolenum) && ok_race(rolenum, racenum, gendnum, alignnum)) {
|
|
if (donefirst) Strcat(buf, " ");
|
|
Strcat(buf, (rolenum == ROLE_NONE) ?
|
|
races[racenum].noun :
|
|
races[racenum].adj);
|
|
donefirst = TRUE;
|
|
} else if (!validrole(rolenum)) {
|
|
if (donefirst) Strcat(buf, " ");
|
|
Strcat(buf, races[racenum].noun);
|
|
donefirst = TRUE;
|
|
} else {
|
|
pa[BP_RACE] = 1;
|
|
post_attribs++;
|
|
}
|
|
} else {
|
|
pa[BP_RACE] = 1;
|
|
post_attribs++;
|
|
}
|
|
/* <your lawful female gnomish> || <your lawful female gnome> */
|
|
|
|
if (validrole(rolenum)) {
|
|
if (donefirst) Strcat(buf, " ");
|
|
if (gendnum != ROLE_NONE) {
|
|
if (gendnum == 1 && roles[rolenum].name.f)
|
|
Strcat(buf, roles[rolenum].name.f);
|
|
else
|
|
Strcat(buf, roles[rolenum].name.m);
|
|
} else {
|
|
if (roles[rolenum].name.f) {
|
|
Strcat(buf, roles[rolenum].name.m);
|
|
Strcat(buf, "/");
|
|
Strcat(buf, roles[rolenum].name.f);
|
|
} else
|
|
Strcat(buf, roles[rolenum].name.m);
|
|
}
|
|
donefirst = TRUE;
|
|
} else if (rolenum == ROLE_NONE) {
|
|
pa[BP_ROLE] = 1;
|
|
post_attribs++;
|
|
}
|
|
|
|
if ((racenum == ROLE_NONE || racenum == ROLE_RANDOM) && !validrole(rolenum)) {
|
|
if (donefirst) Strcat(buf, " ");
|
|
Strcat(buf, "character");
|
|
donefirst = TRUE;
|
|
}
|
|
/* <your lawful female gnomish cavewoman> || <your lawful female gnome>
|
|
* || <your lawful female character>
|
|
*/
|
|
if (buflen > (int) (strlen(buf) + 1)) {
|
|
Strcpy(suppliedbuf, buf);
|
|
return suppliedbuf;
|
|
} else
|
|
return err_ret;
|
|
}
|
|
|
|
char *
|
|
build_plselection_prompt(buf, buflen, rolenum, racenum, gendnum, alignnum)
|
|
char *buf;
|
|
int buflen, rolenum, racenum, gendnum, alignnum;
|
|
{
|
|
const char *defprompt = "Shall I pick a character for you? [ynq] ";
|
|
int num_post_attribs = 0;
|
|
char tmpbuf[BUFSZ];
|
|
|
|
if (buflen < QBUFSZ)
|
|
return (char *)defprompt;
|
|
|
|
Strcpy(tmpbuf, "Shall I pick ");
|
|
if (racenum != ROLE_NONE || validrole(rolenum))
|
|
Strcat(tmpbuf, "your ");
|
|
else {
|
|
Strcat(tmpbuf, "a ");
|
|
}
|
|
/* <your> */
|
|
|
|
(void) root_plselection_prompt(eos(tmpbuf), buflen - strlen(tmpbuf),
|
|
rolenum, racenum, gendnum, alignnum);
|
|
Sprintf(buf, "%s", s_suffix(tmpbuf));
|
|
|
|
/* buf should now be:
|
|
* < your lawful female gnomish cavewoman's> || <your lawful female gnome's>
|
|
* || <your lawful female character's>
|
|
*
|
|
* Now append the post attributes to it
|
|
*/
|
|
|
|
num_post_attribs = post_attribs;
|
|
if (post_attribs) {
|
|
if (pa[BP_RACE]) {
|
|
(void) promptsep(eos(buf), num_post_attribs);
|
|
Strcat(buf, "race");
|
|
}
|
|
if (pa[BP_ROLE]) {
|
|
(void) promptsep(eos(buf), num_post_attribs);
|
|
Strcat(buf, "role");
|
|
}
|
|
if (pa[BP_GEND]) {
|
|
(void) promptsep(eos(buf), num_post_attribs);
|
|
Strcat(buf, "gender");
|
|
}
|
|
if (pa[BP_ALIGN]) {
|
|
(void) promptsep(eos(buf), num_post_attribs);
|
|
Strcat(buf, "alignment");
|
|
}
|
|
}
|
|
Strcat(buf, " for you? [ynq] ");
|
|
return buf;
|
|
}
|
|
|
|
#undef BP_ALIGN
|
|
#undef BP_GEND
|
|
#undef BP_RACE
|
|
#undef BP_ROLE
|
|
#undef NUM_BP
|
|
|
|
void
|
|
plnamesuffix()
|
|
{
|
|
char *sptr, *eptr;
|
|
int i;
|
|
|
|
/* Look for tokens delimited by '-' */
|
|
if ((eptr = index(plname, '-')) != (char *) 0)
|
|
*eptr++ = '\0';
|
|
while (eptr) {
|
|
/* Isolate the next token */
|
|
sptr = eptr;
|
|
if ((eptr = index(sptr, '-')) != (char *)0)
|
|
*eptr++ = '\0';
|
|
|
|
/* Try to match it to something */
|
|
if ((i = str2role(sptr)) != ROLE_NONE)
|
|
flags.initrole = i;
|
|
else if ((i = str2race(sptr)) != ROLE_NONE)
|
|
flags.initrace = i;
|
|
else if ((i = str2gend(sptr)) != ROLE_NONE)
|
|
flags.initgend = i;
|
|
else if ((i = str2align(sptr)) != ROLE_NONE)
|
|
flags.initalign = i;
|
|
}
|
|
if(!plname[0]) {
|
|
askname();
|
|
plnamesuffix();
|
|
}
|
|
|
|
/* commas in the plname confuse the record file, convert to spaces */
|
|
for (sptr = plname; *sptr; sptr++) {
|
|
if (*sptr == ',') *sptr = ' ';
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Special setup modifications here:
|
|
*
|
|
* Unfortunately, this is going to have to be done
|
|
* on each newgame or restore, because you lose the permonst mods
|
|
* across a save/restore. :-)
|
|
*
|
|
* 1 - The Rogue Leader is the Tourist Nemesis.
|
|
* 2 - Priests start with a random alignment - convert the leader and
|
|
* guardians here.
|
|
* 3 - Elves can have one of two different leaders, but can't work it
|
|
* out here because it requires hacking the level file data (see
|
|
* sp_lev.c).
|
|
*
|
|
* This code also replaces quest_init().
|
|
*/
|
|
void
|
|
role_init()
|
|
{
|
|
int alignmnt;
|
|
|
|
/* Strip the role letter out of the player name.
|
|
* This is included for backwards compatibility.
|
|
*/
|
|
plnamesuffix();
|
|
|
|
/* Check for a valid role. Try flags.initrole first. */
|
|
if (!validrole(flags.initrole)) {
|
|
/* Try the player letter second */
|
|
if ((flags.initrole = str2role(pl_character)) < 0)
|
|
/* None specified; pick a random role */
|
|
flags.initrole = randrole();
|
|
}
|
|
|
|
/* We now have a valid role index. Copy the role name back. */
|
|
/* This should become OBSOLETE */
|
|
Strcpy(pl_character, roles[flags.initrole].name.m);
|
|
pl_character[PL_CSIZ-1] = '\0';
|
|
|
|
/* Check for a valid race */
|
|
if (!validrace(flags.initrole, flags.initrace))
|
|
flags.initrace = randrace(flags.initrole);
|
|
|
|
/* Check for a valid gender. Try flags.igend first. */
|
|
if (!validgend(flags.initrole, flags.initrace, flags.initgend))
|
|
/* Use flags.female second. Note that there is no way
|
|
* to check for an unspecified gender.
|
|
*/
|
|
flags.initgend = flags.female;
|
|
/* Don't change flags.female; this may be a restore */
|
|
|
|
/* Check for a valid alignment */
|
|
if (!validalign(flags.initrole, flags.initrace, flags.initalign))
|
|
/* Pick a random alignment */
|
|
flags.initalign = randalign(flags.initrole, flags.initrace);
|
|
alignmnt = aligns[flags.initalign].value;
|
|
|
|
/* Initialize urole and urace */
|
|
urole = roles[flags.initrole];
|
|
urace = races[flags.initrace];
|
|
|
|
/* Fix up the quest leader */
|
|
if (urole.ldrnum != NON_PM) {
|
|
mons[urole.ldrnum].msound = MS_LEADER;
|
|
mons[urole.ldrnum].mflags2 |= (M2_PEACEFUL);
|
|
mons[urole.ldrnum].mflags3 |= M3_CLOSE;
|
|
mons[urole.ldrnum].maligntyp = alignmnt * 3;
|
|
}
|
|
|
|
/* Fix up the quest guardians */
|
|
if (urole.guardnum != NON_PM) {
|
|
mons[urole.guardnum].mflags2 |= (M2_PEACEFUL);
|
|
mons[urole.guardnum].maligntyp = alignmnt * 3;
|
|
}
|
|
|
|
/* Fix up the quest nemesis */
|
|
if (urole.neminum != NON_PM) {
|
|
mons[urole.neminum].msound = MS_NEMESIS;
|
|
mons[urole.neminum].mflags2 &= ~(M2_PEACEFUL);
|
|
mons[urole.neminum].mflags2 |= (M2_NASTY|M2_STALK|M2_HOSTILE);
|
|
mons[urole.neminum].mflags3 |= M3_WANTSARTI | M3_WAITFORU;
|
|
}
|
|
|
|
/* Fix up the god names */
|
|
if (flags.pantheon == -1) { /* new game */
|
|
flags.pantheon = flags.initrole; /* use own gods */
|
|
while (!roles[flags.pantheon].lgod) /* unless they're missing */
|
|
flags.pantheon = randrole();
|
|
}
|
|
if (!urole.lgod) {
|
|
urole.lgod = roles[flags.pantheon].lgod;
|
|
urole.ngod = roles[flags.pantheon].ngod;
|
|
urole.cgod = roles[flags.pantheon].cgod;
|
|
}
|
|
|
|
/* Fix up infravision */
|
|
if (mons[urace.malenum].mflags3 & M3_INFRAVISION) {
|
|
/* although an infravision intrinsic is possible, infravision
|
|
* is purely a property of the physical race. This means that we
|
|
* must put the infravision flag in the player's current race
|
|
* (either that or have separate permonst entries for
|
|
* elven/non-elven members of each class). The side effect is that
|
|
* all NPCs of that class will have (probably bogus) infravision,
|
|
* but since infravision has no effect for NPCs anyway we can
|
|
* ignore this.
|
|
*/
|
|
mons[urole.malenum].mflags3 |= M3_INFRAVISION;
|
|
if (urole.femalenum != NON_PM)
|
|
mons[urole.femalenum].mflags3 |= M3_INFRAVISION;
|
|
}
|
|
|
|
/* Artifacts are fixed in hack_artifacts() */
|
|
|
|
/* Success! */
|
|
return;
|
|
}
|
|
|
|
const char *
|
|
Hello(mtmp)
|
|
struct monst *mtmp;
|
|
{
|
|
switch (Role_switch) {
|
|
case PM_KNIGHT:
|
|
return ("Salutations"); /* Olde English */
|
|
case PM_SAMURAI:
|
|
return (mtmp && mtmp->data == &mons[PM_SHOPKEEPER] ?
|
|
"Irasshaimase" : "Konnichi wa"); /* Japanese */
|
|
#ifdef TOURIST
|
|
case PM_TOURIST:
|
|
return ("Aloha"); /* Hawaiian */
|
|
#endif
|
|
case PM_VALKYRIE:
|
|
return (
|
|
#ifdef MAIL
|
|
mtmp && mtmp->data == &mons[PM_MAIL_DAEMON] ? "Hallo" :
|
|
#endif
|
|
"Velkommen"); /* Norse */
|
|
default:
|
|
return ("Hello");
|
|
}
|
|
}
|
|
|
|
const char *
|
|
Goodbye()
|
|
{
|
|
switch (Role_switch) {
|
|
case PM_KNIGHT:
|
|
return ("Fare thee well"); /* Olde English */
|
|
case PM_SAMURAI:
|
|
return ("Sayonara"); /* Japanese */
|
|
#ifdef TOURIST
|
|
case PM_TOURIST:
|
|
return ("Aloha"); /* Hawaiian */
|
|
#endif
|
|
case PM_VALKYRIE:
|
|
return ("Farvel"); /* Norse */
|
|
default:
|
|
return ("Goodbye");
|
|
}
|
|
}
|
|
|
|
/* role.c */
|