OSDN Git Service

shrink mine
[nethackexpress/trunk.git] / src / role.c
1 /*      SCCS Id: @(#)role.c     3.4     2003/01/08      */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985-1999. */
3 /* NetHack may be freely redistributed.  See license for details. */
4
5 #include "hack.h"
6
7
8 /*** Table of all roles ***/
9 /* According to AD&D, HD for some classes (ex. Wizard) should be smaller
10  * (4-sided for wizards).  But this is not AD&D, and using the AD&D
11  * rule here produces an unplayable character.  Thus I have used a minimum
12  * of an 10-sided hit die for everything.  Another AD&D change: wizards get
13  * a minimum strength of 4 since without one you can't teleport or cast
14  * spells. --KAA
15  *
16  * As the wizard has been updated (wizard patch 5 jun '96) their HD can be
17  * brought closer into line with AD&D. This forces wizards to use magic more
18  * and distance themselves from their attackers. --LSZ
19  *
20  * With the introduction of races, some hit points and energy
21  * has been reallocated for each race.  The values assigned
22  * to the roles has been reduced by the amount allocated to
23  * humans.  --KMH
24  *
25  * God names use a leading underscore to flag goddesses.
26  */
27 const struct Role roles[] = {
28 {       {"Archeologist", 0}, {
29         {"Digger",      0},
30         {"Field Worker",0},
31         {"Investigator",0},
32         {"Exhumer",     0},
33         {"Excavator",   0},
34         {"Spelunker",   0},
35         {"Speleologist",0},
36         {"Collector",   0},
37         {"Curator",     0} },
38         "Quetzalcoatl", "Camaxtli", "Huhetotl", /* Central American */
39         "Arc", "the College of Archeology", "the Tomb of the Toltec Kings",
40         PM_ARCHEOLOGIST, NON_PM, NON_PM,
41         PM_LORD_CARNARVON, PM_STUDENT, PM_MINION_OF_HUHETOTL,
42         NON_PM, PM_HUMAN_MUMMY, S_SNAKE, S_MUMMY,
43         ART_ORB_OF_DETECTION,
44         MH_HUMAN|MH_DWARF|MH_GNOME | ROLE_MALE|ROLE_FEMALE |
45           ROLE_LAWFUL|ROLE_NEUTRAL,
46         /* Str Int Wis Dex Con Cha */
47         {   7, 10, 10,  7,  7,  7 },
48         {  20, 20, 20, 10, 20, 10 },
49         /* Init   Lower  Higher */
50         { 11, 0,  0, 8,  1, 0 },        /* Hit points */
51         {  1, 0,  0, 1,  0, 1 },14,     /* Energy */
52         10, 5, 0, 2, 10, A_INT, SPE_MAGIC_MAPPING,   -4
53 },
54 {       {"Barbarian", 0}, {
55         {"Plunderer",   "Plunderess"},
56         {"Pillager",    0},
57         {"Bandit",      0},
58         {"Brigand",     0},
59         {"Raider",      0},
60         {"Reaver",      0},
61         {"Slayer",      0},
62         {"Chieftain",   "Chieftainess"},
63         {"Conqueror",   "Conqueress"} },
64         "Mitra", "Crom", "Set", /* Hyborian */
65         "Bar", "the Camp of the Duali Tribe", "the Duali Oasis",
66         PM_BARBARIAN, NON_PM, NON_PM,
67         PM_PELIAS, PM_CHIEFTAIN, PM_THOTH_AMON,
68         PM_OGRE, PM_TROLL, S_OGRE, S_TROLL,
69         ART_HEART_OF_AHRIMAN,
70         MH_HUMAN|MH_ORC | ROLE_MALE|ROLE_FEMALE |
71           ROLE_NEUTRAL|ROLE_CHAOTIC,
72         /* Str Int Wis Dex Con Cha */
73         {  16,  7,  7, 15, 16,  6 },
74         {  30,  6,  7, 20, 30,  7 },
75         /* Init   Lower  Higher */
76         { 14, 0,  0,10,  2, 0 },        /* Hit points */
77         {  1, 0,  0, 1,  0, 1 },10,     /* Energy */
78         10, 14, 0, 0,  8, A_INT, SPE_HASTE_SELF,      -4
79 },
80 {       {"Caveman", "Cavewoman"}, {
81         {"Troglodyte",  0},
82         {"Aborigine",   0},
83         {"Wanderer",    0},
84         {"Vagrant",     0},
85         {"Wayfarer",    0},
86         {"Roamer",      0},
87         {"Nomad",       0},
88         {"Rover",       0},
89         {"Pioneer",     0} },
90         "Anu", "_Ishtar", "Anshar", /* Babylonian */
91         "Cav", "the Caves of the Ancestors", "the Dragon's Lair",
92         PM_CAVEMAN, PM_CAVEWOMAN, PM_LITTLE_DOG,
93         PM_SHAMAN_KARNOV, PM_NEANDERTHAL, PM_CHROMATIC_DRAGON,
94         PM_BUGBEAR, PM_HILL_GIANT, S_HUMANOID, S_GIANT,
95         ART_SCEPTRE_OF_MIGHT,
96         MH_HUMAN|MH_DWARF|MH_GNOME | ROLE_MALE|ROLE_FEMALE |
97           ROLE_LAWFUL|ROLE_NEUTRAL,
98         /* Str Int Wis Dex Con Cha */
99         {  10,  7,  7,  7,  8,  6 },
100         {  30,  6,  7, 20, 30,  7 },
101         /* Init   Lower  Higher */
102         { 14, 0,  0, 8,  2, 0 },        /* Hit points */
103         {  1, 0,  0, 1,  0, 1 },10,     /* Energy */
104         0, 12, 0, 1,  8, A_INT, SPE_DIG,             -4
105 },
106 {       {"Healer", 0}, {
107         {"Rhizotomist",    0},
108         {"Empiric",        0},
109         {"Embalmer",       0},
110         {"Dresser",        0},
111         {"Medicus ossium", "Medica ossium"},
112         {"Herbalist",      0},
113         {"Magister",       "Magistra"},
114         {"Physician",      0},
115         {"Chirurgeon",     0} },
116         "_Athena", "Hermes", "Poseidon", /* Greek */
117         "Hea", "the Temple of Epidaurus", "the Temple of Coeus",
118         PM_HEALER, NON_PM, NON_PM,
119         PM_HIPPOCRATES, PM_ATTENDANT, PM_CYCLOPS,
120         PM_GIANT_RAT, PM_SNAKE, S_RODENT, S_YETI,
121         ART_STAFF_OF_AESCULAPIUS,
122         MH_HUMAN|MH_GNOME | ROLE_MALE|ROLE_FEMALE | ROLE_NEUTRAL,
123         /* Str Int Wis Dex Con Cha */
124         {   7,  7, 13,  7, 11, 16 },
125         {  15, 20, 20, 15, 25, 5 },
126         /* Init   Lower  Higher */
127         { 11, 0,  0, 8,  1, 0 },        /* Hit points */
128         {  1, 4,  0, 1,  0, 2 },20,     /* Energy */
129         10, 3,-3, 2, 10, A_WIS, SPE_CURE_SICKNESS,   -4
130 },
131 {       {"Knight", 0}, {
132         {"Gallant",     0},
133         {"Esquire",     0},
134         {"Bachelor",    0},
135         {"Sergeant",    0},
136         {"Knight",      0},
137         {"Banneret",    0},
138         {"Chevalier",   "Chevaliere"},
139         {"Seignieur",   "Dame"},
140         {"Paladin",     0} },
141         "Lugh", "_Brigit", "Manannan Mac Lir", /* Celtic */
142         "Kni", "Camelot Castle", "the Isle of Glass",
143         PM_KNIGHT, NON_PM, PM_PONY,
144         PM_KING_ARTHUR, PM_PAGE, PM_IXOTH,
145         PM_QUASIT, PM_OCHRE_JELLY, S_IMP, S_JELLY,
146         ART_MAGIC_MIRROR_OF_MERLIN,
147         MH_HUMAN | ROLE_MALE|ROLE_FEMALE | ROLE_LAWFUL,
148         /* Str Int Wis Dex Con Cha */
149         {  13,  7, 14,  8, 10, 17 },
150         {  30, 15, 15, 10, 20, 10 },
151         /* Init   Lower  Higher */
152         { 14, 0,  0, 8,  2, 0 },        /* Hit points */
153         {  1, 4,  0, 1,  0, 2 },10,     /* Energy */
154         10, 8,-2, 0,  9, A_WIS, SPE_TURN_UNDEAD,     -4
155 },
156 {       {"Monk", 0}, {
157         {"Candidate",         0},
158         {"Novice",            0},
159         {"Initiate",          0},
160         {"Student of Stones", 0},
161         {"Student of Waters", 0},
162         {"Student of Metals", 0},
163         {"Student of Winds",  0},
164         {"Student of Fire",   0},
165         {"Master",            0} },
166         "Shan Lai Ching", "Chih Sung-tzu", "Huan Ti", /* Chinese */
167         "Mon", "the Monastery of Chan-Sune",
168           "the Monastery of the Earth-Lord",
169         PM_MONK, NON_PM, NON_PM,
170         PM_GRAND_MASTER, PM_ABBOT, PM_MASTER_KAEN,
171         PM_EARTH_ELEMENTAL, PM_XORN, S_ELEMENTAL, S_XORN,
172         ART_EYES_OF_THE_OVERWORLD,
173         MH_HUMAN | ROLE_MALE|ROLE_FEMALE |
174           ROLE_LAWFUL|ROLE_NEUTRAL|ROLE_CHAOTIC,
175         /* Str Int Wis Dex Con Cha */
176         {  10,  7,  8,  8,  7,  7 },
177         {  25, 10, 20, 20, 15, 10 },
178         /* Init   Lower  Higher */
179         { 12, 0,  0, 8,  1, 0 },        /* Hit points */
180         {  2, 2,  0, 2,  0, 2 },10,     /* Energy */
181         10, 8,-2, 2, 20, A_WIS, SPE_RESTORE_ABILITY, -4
182 },
183 {       {"Priest", "Priestess"}, {
184         {"Aspirant",    0},
185         {"Acolyte",     0},
186         {"Adept",       0},
187         {"Priest",      "Priestess"},
188         {"Curate",      0},
189         {"Canon",       "Canoness"},
190         {"Lama",        0},
191         {"Patriarch",   "Matriarch"},
192         {"High Priest", "High Priestess"} },
193         0, 0, 0,        /* chosen randomly from among the other roles */
194         "Pri", "the Great Temple", "the Temple of Nalzok",
195         PM_PRIEST, PM_PRIESTESS, NON_PM,
196         PM_ARCH_PRIEST, PM_ACOLYTE, PM_NALZOK,
197         PM_HUMAN_ZOMBIE, PM_WRAITH, S_ZOMBIE, S_WRAITH,
198         ART_MITRE_OF_HOLINESS,
199         MH_HUMAN|MH_ELF | ROLE_MALE|ROLE_FEMALE |
200           ROLE_LAWFUL|ROLE_NEUTRAL|ROLE_CHAOTIC,
201         /* Str Int Wis Dex Con Cha */
202         {   7,  7, 10,  7,  7,  7 },
203         {  15, 10, 30, 15, 20, 10 },
204         /* Init   Lower  Higher */
205         { 12, 0,  0, 8,  1, 0 },        /* Hit points */
206         {  4, 3,  0, 2,  0, 2 },10,     /* Energy */
207         0, 3,-2, 2, 10, A_WIS, SPE_REMOVE_CURSE,    -4
208 },
209   /* Note:  Rogue precedes Ranger so that use of `-R' on the command line
210      retains its traditional meaning. */
211 {       {"Rogue", 0}, {
212         {"Footpad",     0},
213         {"Cutpurse",    0},
214         {"Rogue",       0},
215         {"Pilferer",    0},
216         {"Robber",      0},
217         {"Burglar",     0},
218         {"Filcher",     0},
219         {"Magsman",     "Magswoman"},
220         {"Thief",       0} },
221         "Issek", "Mog", "Kos", /* Nehwon */
222         "Rog", "the Thieves' Guild Hall", "the Assassins' Guild Hall",
223         PM_ROGUE, NON_PM, NON_PM,
224         PM_MASTER_OF_THIEVES, PM_THUG, PM_MASTER_ASSASSIN,
225         PM_LEPRECHAUN, PM_GUARDIAN_NAGA, S_NYMPH, S_NAGA,
226         ART_MASTER_KEY_OF_THIEVERY,
227         MH_HUMAN|MH_ORC | ROLE_MALE|ROLE_FEMALE |
228           ROLE_CHAOTIC,
229         /* Str Int Wis Dex Con Cha */
230         {   7,  7,  7, 10,  7,  6 },
231         {  20, 10, 10, 30, 20, 10 },
232         /* Init   Lower  Higher */
233         { 10, 0,  0, 8,  1, 0 },        /* Hit points */
234         {  1, 0,  0, 1,  0, 1 },11,     /* Energy */
235         10, 8, 0, 1,  9, A_INT, SPE_DETECT_TREASURE, -4
236 },
237 {       {"Ranger", 0}, {
238 #if 0   /* OBSOLETE */
239         {"Edhel",       "Elleth"},
240         {"Edhel",       "Elleth"},      /* elf-maid */
241         {"Ohtar",       "Ohtie"},       /* warrior */
242         {"Kano",                        /* commander (Q.) ['a] */
243                         "Kanie"},       /* educated guess, until further research- SAC */
244         {"Arandur",                     /* king's servant, minister (Q.) - guess */
245                         "Aranduriel"},  /* educated guess */
246         {"Hir",         "Hiril"},       /* lord, lady (S.) ['ir] */
247         {"Aredhel",     "Arwen"},       /* noble elf, maiden (S.) */
248         {"Ernil",       "Elentariel"},  /* prince (S.), elf-maiden (Q.) */
249         {"Elentar",     "Elentari"},    /* Star-king, -queen (Q.) */
250         "Solonor Thelandira", "Aerdrie Faenya", "Lolth", /* Elven */
251 #endif
252         {"Tenderfoot",    0},
253         {"Lookout",       0},
254         {"Trailblazer",   0},
255         {"Reconnoiterer", "Reconnoiteress"},
256         {"Scout",         0},
257         {"Arbalester",    0},   /* One skilled at crossbows */
258         {"Archer",        0},
259         {"Sharpshooter",  0},
260         {"Marksman",      "Markswoman"} },
261         "Mercury", "_Venus", "Mars", /* Roman/planets */
262         "Ran", "Orion's camp", "the cave of the wumpus",
263         PM_RANGER, NON_PM, PM_LITTLE_DOG /* Orion & canis major */,
264         PM_ORION, PM_HUNTER, PM_SCORPIUS,
265         PM_FOREST_CENTAUR, PM_SCORPION, S_CENTAUR, S_SPIDER,
266         ART_LONGBOW_OF_DIANA,
267         MH_HUMAN|MH_ELF|MH_GNOME|MH_ORC | ROLE_MALE|ROLE_FEMALE |
268           ROLE_NEUTRAL|ROLE_CHAOTIC,
269         /* Str Int Wis Dex Con Cha */
270         {  13, 13, 13,  9, 13,  7 },
271         {  30, 10, 10, 20, 20, 10 },
272         /* Init   Lower  Higher */
273         { 13, 0,  0, 6,  1, 0 },        /* Hit points */
274         {  1, 0,  0, 1,  0, 1 },12,     /* Energy */
275         10, 9, 2, 1, 10, A_INT, SPE_INVISIBILITY,   -4
276 },
277 {       {"Samurai", 0}, {
278         {"Hatamoto",    0},  /* Banner Knight */
279         {"Ronin",       0},  /* no allegiance */
280         {"Ninja",       "Kunoichi"},  /* secret society */
281         {"Joshu",       0},  /* heads a castle */
282         {"Ryoshu",      0},  /* has a territory */
283         {"Kokushu",     0},  /* heads a province */
284         {"Daimyo",      0},  /* a samurai lord */
285         {"Kuge",        0},  /* Noble of the Court */
286         {"Shogun",      0} },/* supreme commander, warlord */
287         "_Amaterasu Omikami", "Raijin", "Susanowo", /* Japanese */
288         "Sam", "the Castle of the Taro Clan", "the Shogun's Castle",
289         PM_SAMURAI, NON_PM, PM_LITTLE_DOG,
290         PM_LORD_SATO, PM_ROSHI, PM_ASHIKAGA_TAKAUJI,
291         PM_WOLF, PM_STALKER, S_DOG, S_ELEMENTAL,
292         ART_TSURUGI_OF_MURAMASA,
293         MH_HUMAN | ROLE_MALE|ROLE_FEMALE | ROLE_LAWFUL,
294         /* Str Int Wis Dex Con Cha */
295         {  10,  8,  7, 10, 17,  6 },
296         {  30, 10,  8, 30, 14,  8 },
297         /* Init   Lower  Higher */
298         { 13, 0,  0, 8,  1, 0 },        /* Hit points */
299         {  1, 0,  0, 1,  0, 1 },11,     /* Energy */
300         10, 10, 0, 0,  8, A_INT, SPE_CLAIRVOYANCE,    -4
301 },
302 #ifdef TOURIST
303 {       {"Tourist", 0}, {
304         {"Rambler",     0},
305         {"Sightseer",   0},
306         {"Excursionist",0},
307         {"Peregrinator","Peregrinatrix"},
308         {"Traveler",    0},
309         {"Journeyer",   0},
310         {"Voyager",     0},
311         {"Explorer",    0},
312         {"Adventurer",  0} },
313         "Blind Io", "_The Lady", "Offler", /* Discworld */
314         "Tou", "Ankh-Morpork", "the Thieves' Guild Hall",
315         PM_TOURIST, NON_PM, NON_PM,
316         PM_TWOFLOWER, PM_GUIDE, PM_MASTER_OF_THIEVES,
317         PM_GIANT_SPIDER, PM_FOREST_CENTAUR, S_SPIDER, S_CENTAUR,
318         ART_YENDORIAN_EXPRESS_CARD,
319         MH_HUMAN | ROLE_MALE|ROLE_FEMALE | ROLE_NEUTRAL,
320         /* Str Int Wis Dex Con Cha */
321         {   7, 10,  6,  7,  7, 10 },
322         {  15, 10, 10, 15, 30, 20 },
323         /* Init   Lower  Higher */
324         {  8, 0,  0, 8,  0, 0 },        /* Hit points */
325         {  1, 0,  0, 1,  0, 1 },14,     /* Energy */
326         0, 5, 1, 2, 10, A_INT, SPE_CHARM_MONSTER,   -4
327 },
328 #endif
329 {       {"Valkyrie", 0}, {
330         {"Stripling",   0},
331         {"Skirmisher",  0},
332         {"Fighter",     0},
333         {"Man-at-arms", "Woman-at-arms"},
334         {"Warrior",     0},
335         {"Swashbuckler",0},
336         {"Hero",        "Heroine"},
337         {"Champion",    0},
338         {"Lord",        "Lady"} },
339         "Tyr", "Odin", "Loki", /* Norse */
340         "Val", "the Shrine of Destiny", "the cave of Surtur",
341         PM_VALKYRIE, NON_PM, NON_PM /*PM_WINTER_WOLF_CUB*/,
342         PM_NORN, PM_WARRIOR, PM_LORD_SURTUR,
343         PM_FIRE_ANT, PM_FIRE_GIANT, S_ANT, S_GIANT,
344         ART_ORB_OF_FATE,
345         MH_HUMAN|MH_DWARF | ROLE_FEMALE | ROLE_LAWFUL|ROLE_NEUTRAL,
346         /* Str Int Wis Dex Con Cha */
347         {  10,  7,  7,  7, 10,  7 },
348         {  30,  6,  7, 20, 30,  7 },
349         /* Init   Lower  Higher */
350         { 14, 0,  0, 8,  2, 0 },        /* Hit points */
351         {  1, 0,  0, 1,  0, 1 },10,     /* Energy */
352         0, 10,-2, 0,  9, A_WIS, SPE_CONE_OF_COLD,    -4
353 },
354 {       {"Wizard", 0}, {
355         {"Evoker",      0},
356         {"Conjurer",    0},
357         {"Thaumaturge", 0},
358         {"Magician",    0},
359         {"Enchanter",   "Enchantress"},
360         {"Sorcerer",    "Sorceress"},
361         {"Necromancer", 0},
362         {"Wizard",      0},
363         {"Mage",        0} },
364         "Ptah", "Thoth", "Anhur", /* Egyptian */
365         "Wiz", "the Lonely Tower", "the Tower of Darkness",
366         PM_WIZARD, NON_PM, PM_KITTEN,
367         PM_NEFERET_THE_GREEN, PM_APPRENTICE, PM_DARK_ONE,
368         PM_VAMPIRE_BAT, PM_XORN, S_BAT, S_WRAITH,
369         ART_EYE_OF_THE_AETHIOPICA,
370         MH_HUMAN|MH_ELF|MH_GNOME|MH_ORC | ROLE_MALE|ROLE_FEMALE |
371           ROLE_NEUTRAL|ROLE_CHAOTIC,
372         /* Str Int Wis Dex Con Cha */
373         {   7, 10,  7,  7,  7,  7 },
374         {  10, 30, 10, 20, 20, 10 },
375         /* Init   Lower  Higher */
376         { 10, 0,  0, 8,  1, 0 },        /* Hit points */
377         {  4, 3,  0, 2,  0, 3 },12,     /* Energy */
378         0, 1, 0, 3, 10, A_INT, SPE_MAGIC_MISSILE,   -4
379 },
380 /* Array terminator */
381 {{0, 0}}
382 };
383
384
385 /* The player's role, created at runtime from initial
386  * choices.  This may be munged in role_init().
387  */
388 struct Role urole =
389 {       {"Undefined", 0}, { {0, 0}, {0, 0}, {0, 0},
390         {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0} },
391         "L", "N", "C", "Xxx", "home", "locate",
392         NON_PM, NON_PM, NON_PM, NON_PM, NON_PM, NON_PM,
393         NON_PM, NON_PM, 0, 0, 0, 0,
394         /* Str Int Wis Dex Con Cha */
395         {   7,  7,  7,  7,  7,  7 },
396         {  20, 15, 15, 20, 20, 10 },
397         /* Init   Lower  Higher */
398         { 10, 0,  0, 8,  1, 0 },        /* Hit points */
399         {  2, 0,  0, 2,  0, 3 },14,     /* Energy */
400         0, 10, 0, 0,  4, A_INT, 0, -3
401 };
402
403
404
405 /* Table of all races */
406 const struct Race races[] = {
407 {       "human", "human", "humanity", "Hum",
408         {"man", "woman"},
409         PM_HUMAN, NON_PM, PM_HUMAN_MUMMY, PM_HUMAN_ZOMBIE,
410         MH_HUMAN | ROLE_MALE|ROLE_FEMALE |
411           ROLE_LAWFUL|ROLE_NEUTRAL|ROLE_CHAOTIC,
412         MH_HUMAN, 0, MH_GNOME|MH_ORC,
413         /*    Str     Int Wis Dex Con Cha */
414         {      3,      3,  3,  3,  3,  3 },
415         { STR18(100), 18, 18, 18, 18, 18 },
416         /* Init   Lower  Higher */
417         {  2, 0,  0, 2,  1, 0 },        /* Hit points */
418         {  1, 0,  2, 0,  2, 0 }         /* Energy */
419 },
420 {       "elf", "elven", "elvenkind", "Elf",
421         {0, 0},
422         PM_ELF, NON_PM, PM_ELF_MUMMY, PM_ELF_ZOMBIE,
423         MH_ELF | ROLE_MALE|ROLE_FEMALE | ROLE_CHAOTIC,
424         MH_ELF, MH_ELF, MH_ORC,
425         /*  Str    Int Wis Dex Con Cha */
426         {    3,     3,  3,  3,  3,  3 },
427         {   18,    20, 20, 18, 16, 18 },
428         /* Init   Lower  Higher */
429         {  1, 0,  0, 1,  1, 0 },        /* Hit points */
430         {  2, 0,  3, 0,  3, 0 }         /* Energy */
431 },
432 {       "dwarf", "dwarven", "dwarvenkind", "Dwa",
433         {0, 0},
434         PM_DWARF, NON_PM, PM_DWARF_MUMMY, PM_DWARF_ZOMBIE,
435         MH_DWARF | ROLE_MALE|ROLE_FEMALE | ROLE_LAWFUL,
436         MH_DWARF, MH_DWARF|MH_GNOME, MH_ORC,
437         /*    Str     Int Wis Dex Con Cha */
438         {      3,      3,  3,  3,  3,  3 },
439         { STR18(100), 16, 16, 20, 20, 16 },
440         /* Init   Lower  Higher */
441         {  4, 0,  0, 3,  2, 0 },        /* Hit points */
442         {  0, 0,  0, 0,  0, 0 }         /* Energy */
443 },
444 {       "gnome", "gnomish", "gnomehood", "Gno",
445         {0, 0},
446         PM_GNOME, NON_PM, PM_GNOME_MUMMY, PM_GNOME_ZOMBIE,
447         MH_GNOME | ROLE_MALE|ROLE_FEMALE | ROLE_NEUTRAL,
448         MH_GNOME, MH_DWARF|MH_GNOME, MH_HUMAN,
449         /*  Str    Int Wis Dex Con Cha */
450         {    3,     3,  3,  3,  3,  3 },
451         {STR18(50),19, 18, 18, 18, 18 },
452         /* Init   Lower  Higher */
453         {  1, 0,  0, 1,  0, 0 },        /* Hit points */
454         {  2, 0,  2, 0,  2, 0 }         /* Energy */
455 },
456 {       "orc", "orcish", "orcdom", "Orc",
457         {0, 0},
458         PM_ORC, NON_PM, PM_ORC_MUMMY, PM_ORC_ZOMBIE,
459         MH_ORC | ROLE_MALE|ROLE_FEMALE | ROLE_CHAOTIC,
460         MH_ORC, 0, MH_HUMAN|MH_ELF|MH_DWARF,
461         /*  Str    Int Wis Dex Con Cha */
462         {   3,      3,  3,  3,  3,  3 },
463         {STR18(50),16, 16, 18, 18, 16 },
464         /* Init   Lower  Higher */
465         {  1, 0,  0, 1,  0, 0 },        /* Hit points */
466         {  1, 0,  1, 0,  1, 0 }         /* Energy */
467 },
468 /* Array terminator */
469 { 0, 0, 0, 0 }};
470
471
472 /* The player's race, created at runtime from initial
473  * choices.  This may be munged in role_init().
474  */
475 struct Race urace =
476 {       "something", "undefined", "something", "Xxx",
477         {0, 0},
478         NON_PM, NON_PM, NON_PM, NON_PM,
479         0, 0, 0, 0,
480         /*    Str     Int Wis Dex Con Cha */
481         {      3,      3,  3,  3,  3,  3 },
482         { STR18(100), 18, 18, 18, 18, 18 },
483         /* Init   Lower  Higher */
484         {  2, 0,  0, 2,  1, 0 },        /* Hit points */
485         {  1, 0,  2, 0,  2, 0 }         /* Energy */
486 };
487
488
489 /* Table of all genders */
490 const struct Gender genders[] = {
491         {"male",        "he",   "him",  "his",  "Mal",  ROLE_MALE},
492         {"female",      "she",  "her",  "her",  "Fem",  ROLE_FEMALE},
493         {"neuter",      "it",   "it",   "its",  "Ntr",  ROLE_NEUTER}
494 };
495
496
497 /* Table of all alignments */
498 const struct Align aligns[] = {
499         {"law",         "lawful",       "Law",  ROLE_LAWFUL,    A_LAWFUL},
500         {"balance",     "neutral",      "Neu",  ROLE_NEUTRAL,   A_NEUTRAL},
501         {"chaos",       "chaotic",      "Cha",  ROLE_CHAOTIC,   A_CHAOTIC},
502         {"evil",        "unaligned",    "Una",  0,              A_NONE}
503 };
504
505 STATIC_DCL char * FDECL(promptsep, (char *, int));
506 STATIC_DCL int FDECL(role_gendercount, (int));
507 STATIC_DCL int FDECL(race_alignmentcount, (int));
508
509 /* used by str2XXX() */
510 static char NEARDATA randomstr[] = "random";
511
512
513 boolean
514 validrole(rolenum)
515         int rolenum;
516 {
517         return (rolenum >= 0 && rolenum < SIZE(roles)-1);
518 }
519
520
521 int
522 randrole()
523 {
524         return (rn2(SIZE(roles)-1));
525 }
526
527
528 int
529 str2role(str)
530         char *str;
531 {
532         int i, len;
533
534         /* Is str valid? */
535         if (!str || !str[0])
536             return ROLE_NONE;
537
538         /* Match as much of str as is provided */
539         len = strlen(str);
540         for (i = 0; roles[i].name.m; i++) {
541             /* Does it match the male name? */
542             if (!strncmpi(str, roles[i].name.m, len))
543                 return i;
544             /* Or the female name? */
545             if (roles[i].name.f && !strncmpi(str, roles[i].name.f, len))
546                 return i;
547             /* Or the filecode? */
548             if (!strcmpi(str, roles[i].filecode))
549                 return i;
550         }
551
552         if ((len == 1 && (*str == '*' || *str == '@')) ||
553                 !strncmpi(str, randomstr, len))
554             return ROLE_RANDOM;
555
556         /* Couldn't find anything appropriate */
557         return ROLE_NONE;
558 }
559
560
561 boolean
562 validrace(rolenum, racenum)
563         int rolenum, racenum;
564 {
565         /* Assumes validrole */
566         return (racenum >= 0 && racenum < SIZE(races)-1 &&
567                 (roles[rolenum].allow & races[racenum].allow & ROLE_RACEMASK));
568 }
569
570
571 int
572 randrace(rolenum)
573         int rolenum;
574 {
575         int i, n = 0;
576
577         /* Count the number of valid races */
578         for (i = 0; races[i].noun; i++)
579             if (roles[rolenum].allow & races[i].allow & ROLE_RACEMASK)
580                 n++;
581
582         /* Pick a random race */
583         /* Use a factor of 100 in case of bad random number generators */
584         if (n) n = rn2(n*100)/100;
585         for (i = 0; races[i].noun; i++)
586             if (roles[rolenum].allow & races[i].allow & ROLE_RACEMASK) {
587                 if (n) n--;
588                 else return (i);
589             }
590
591         /* This role has no permitted races? */
592         return (rn2(SIZE(races)-1));
593 }
594
595
596 int
597 str2race(str)
598         char *str;
599 {
600         int i, len;
601
602         /* Is str valid? */
603         if (!str || !str[0])
604             return ROLE_NONE;
605
606         /* Match as much of str as is provided */
607         len = strlen(str);
608         for (i = 0; races[i].noun; i++) {
609             /* Does it match the noun? */
610             if (!strncmpi(str, races[i].noun, len))
611                 return i;
612             /* Or the filecode? */
613             if (!strcmpi(str, races[i].filecode))
614                 return i;
615         }
616
617         if ((len == 1 && (*str == '*' || *str == '@')) ||
618                 !strncmpi(str, randomstr, len))
619             return ROLE_RANDOM;
620
621         /* Couldn't find anything appropriate */
622         return ROLE_NONE;
623 }
624
625
626 boolean
627 validgend(rolenum, racenum, gendnum)
628         int rolenum, racenum, gendnum;
629 {
630         /* Assumes validrole and validrace */
631         return (gendnum >= 0 && gendnum < ROLE_GENDERS &&
632                 (roles[rolenum].allow & races[racenum].allow &
633                  genders[gendnum].allow & ROLE_GENDMASK));
634 }
635
636
637 int
638 randgend(rolenum, racenum)
639         int rolenum, racenum;
640 {
641         int i, n = 0;
642
643         /* Count the number of valid genders */
644         for (i = 0; i < ROLE_GENDERS; i++)
645             if (roles[rolenum].allow & races[racenum].allow &
646                         genders[i].allow & ROLE_GENDMASK)
647                 n++;
648
649         /* Pick a random gender */
650         if (n) n = rn2(n);
651         for (i = 0; i < ROLE_GENDERS; i++)
652             if (roles[rolenum].allow & races[racenum].allow &
653                         genders[i].allow & ROLE_GENDMASK) {
654                 if (n) n--;
655                 else return (i);
656             }
657
658         /* This role/race has no permitted genders? */
659         return (rn2(ROLE_GENDERS));
660 }
661
662
663 int
664 str2gend(str)
665         char *str;
666 {
667         int i, len;
668
669         /* Is str valid? */
670         if (!str || !str[0])
671             return ROLE_NONE;
672
673         /* Match as much of str as is provided */
674         len = strlen(str);
675         for (i = 0; i < ROLE_GENDERS; i++) {
676             /* Does it match the adjective? */
677             if (!strncmpi(str, genders[i].adj, len))
678                 return i;
679             /* Or the filecode? */
680             if (!strcmpi(str, genders[i].filecode))
681                 return i;
682         }
683         if ((len == 1 && (*str == '*' || *str == '@')) ||
684                 !strncmpi(str, randomstr, len))
685             return ROLE_RANDOM;
686
687         /* Couldn't find anything appropriate */
688         return ROLE_NONE;
689 }
690
691
692 boolean
693 validalign(rolenum, racenum, alignnum)
694         int rolenum, racenum, alignnum;
695 {
696         /* Assumes validrole and validrace */
697         return (alignnum >= 0 && alignnum < ROLE_ALIGNS &&
698                 (roles[rolenum].allow & races[racenum].allow &
699                  aligns[alignnum].allow & ROLE_ALIGNMASK));
700 }
701
702
703 int
704 randalign(rolenum, racenum)
705         int rolenum, racenum;
706 {
707         int i, n = 0;
708
709         /* Count the number of valid alignments */
710         for (i = 0; i < ROLE_ALIGNS; i++)
711             if (roles[rolenum].allow & races[racenum].allow &
712                         aligns[i].allow & ROLE_ALIGNMASK)
713                 n++;
714
715         /* Pick a random alignment */
716         if (n) n = rn2(n);
717         for (i = 0; i < ROLE_ALIGNS; i++)
718             if (roles[rolenum].allow & races[racenum].allow &
719                         aligns[i].allow & ROLE_ALIGNMASK) {
720                 if (n) n--;
721                 else return (i);
722             }
723
724         /* This role/race has no permitted alignments? */
725         return (rn2(ROLE_ALIGNS));
726 }
727
728
729 int
730 str2align(str)
731         char *str;
732 {
733         int i, len;
734
735         /* Is str valid? */
736         if (!str || !str[0])
737             return ROLE_NONE;
738
739         /* Match as much of str as is provided */
740         len = strlen(str);
741         for (i = 0; i < ROLE_ALIGNS; i++) {
742             /* Does it match the adjective? */
743             if (!strncmpi(str, aligns[i].adj, len))
744                 return i;
745             /* Or the filecode? */
746             if (!strcmpi(str, aligns[i].filecode))
747                 return i;
748         }
749         if ((len == 1 && (*str == '*' || *str == '@')) ||
750                 !strncmpi(str, randomstr, len))
751             return ROLE_RANDOM;
752
753         /* Couldn't find anything appropriate */
754         return ROLE_NONE;
755 }
756
757 /* is rolenum compatible with any racenum/gendnum/alignnum constraints? */
758 boolean
759 ok_role(rolenum, racenum, gendnum, alignnum)
760 int rolenum, racenum, gendnum, alignnum;
761 {
762     int i;
763     short allow;
764
765     if (rolenum >= 0 && rolenum < SIZE(roles)-1) {
766         allow = roles[rolenum].allow;
767         if (racenum >= 0 && racenum < SIZE(races)-1 &&
768                 !(allow & races[racenum].allow & ROLE_RACEMASK))
769             return FALSE;
770         if (gendnum >= 0 && gendnum < ROLE_GENDERS &&
771                 !(allow & genders[gendnum].allow & ROLE_GENDMASK))
772             return FALSE;
773         if (alignnum >= 0 && alignnum < ROLE_ALIGNS &&
774                 !(allow & aligns[alignnum].allow & ROLE_ALIGNMASK))
775             return FALSE;
776         return TRUE;
777     } else {
778         for (i = 0; i < SIZE(roles)-1; i++) {
779             allow = roles[i].allow;
780             if (racenum >= 0 && racenum < SIZE(races)-1 &&
781                     !(allow & races[racenum].allow & ROLE_RACEMASK))
782                 continue;
783             if (gendnum >= 0 && gendnum < ROLE_GENDERS &&
784                     !(allow & genders[gendnum].allow & ROLE_GENDMASK))
785                 continue;
786             if (alignnum >= 0 && alignnum < ROLE_ALIGNS &&
787                     !(allow & aligns[alignnum].allow & ROLE_ALIGNMASK))
788                 continue;
789             return TRUE;
790         }
791         return FALSE;
792     }
793 }
794
795 /* pick a random role subject to any racenum/gendnum/alignnum constraints */
796 /* If pickhow == PICK_RIGID a role is returned only if there is  */
797 /* a single possibility */
798 int
799 pick_role(racenum, gendnum, alignnum, pickhow)
800 int racenum, gendnum, alignnum, pickhow;
801 {
802     int i;
803     int roles_ok = 0;
804
805     for (i = 0; i < SIZE(roles)-1; i++) {
806         if (ok_role(i, racenum, gendnum, alignnum))
807             roles_ok++;
808     }
809     if (roles_ok == 0 || (roles_ok > 1 && pickhow == PICK_RIGID))
810         return ROLE_NONE;
811     roles_ok = rn2(roles_ok);
812     for (i = 0; i < SIZE(roles)-1; i++) {
813         if (ok_role(i, racenum, gendnum, alignnum)) {
814             if (roles_ok == 0)
815                 return i;
816             else
817                 roles_ok--;
818         }
819     }
820     return ROLE_NONE;
821 }
822
823 /* is racenum compatible with any rolenum/gendnum/alignnum constraints? */
824 boolean
825 ok_race(rolenum, racenum, gendnum, alignnum)
826 int rolenum, racenum, gendnum, alignnum;
827 {
828     int i;
829     short allow;
830
831     if (racenum >= 0 && racenum < SIZE(races)-1) {
832         allow = races[racenum].allow;
833         if (rolenum >= 0 && rolenum < SIZE(roles)-1 &&
834                 !(allow & roles[rolenum].allow & ROLE_RACEMASK))
835             return FALSE;
836         if (gendnum >= 0 && gendnum < ROLE_GENDERS &&
837                 !(allow & genders[gendnum].allow & ROLE_GENDMASK))
838             return FALSE;
839         if (alignnum >= 0 && alignnum < ROLE_ALIGNS &&
840                 !(allow & aligns[alignnum].allow & ROLE_ALIGNMASK))
841             return FALSE;
842         return TRUE;
843     } else {
844         for (i = 0; i < SIZE(races)-1; i++) {
845             allow = races[i].allow;
846             if (rolenum >= 0 && rolenum < SIZE(roles)-1 &&
847                     !(allow & roles[rolenum].allow & ROLE_RACEMASK))
848                 continue;
849             if (gendnum >= 0 && gendnum < ROLE_GENDERS &&
850                     !(allow & genders[gendnum].allow & ROLE_GENDMASK))
851                 continue;
852             if (alignnum >= 0 && alignnum < ROLE_ALIGNS &&
853                     !(allow & aligns[alignnum].allow & ROLE_ALIGNMASK))
854                 continue;
855             return TRUE;
856         }
857         return FALSE;
858     }
859 }
860
861 /* pick a random race subject to any rolenum/gendnum/alignnum constraints */
862 /* If pickhow == PICK_RIGID a race is returned only if there is  */
863 /* a single possibility */
864 int
865 pick_race(rolenum, gendnum, alignnum, pickhow)
866 int rolenum, gendnum, alignnum, pickhow;
867 {
868     int i;
869     int races_ok = 0;
870
871     for (i = 0; i < SIZE(races)-1; i++) {
872         if (ok_race(rolenum, i, gendnum, alignnum))
873             races_ok++;
874     }
875     if (races_ok == 0 || (races_ok > 1 && pickhow == PICK_RIGID))
876         return ROLE_NONE;
877     races_ok = rn2(races_ok);
878     for (i = 0; i < SIZE(races)-1; i++) {
879         if (ok_race(rolenum, i, gendnum, alignnum)) {
880             if (races_ok == 0)
881                 return i;
882             else
883                 races_ok--;
884         }
885     }
886     return ROLE_NONE;
887 }
888
889 /* is gendnum compatible with any rolenum/racenum/alignnum constraints? */
890 /* gender and alignment are not comparable (and also not constrainable) */
891 boolean
892 ok_gend(rolenum, racenum, gendnum, alignnum)
893 int rolenum, racenum, gendnum, alignnum;
894 {
895     int i;
896     short allow;
897
898     if (gendnum >= 0 && gendnum < ROLE_GENDERS) {
899         allow = genders[gendnum].allow;
900         if (rolenum >= 0 && rolenum < SIZE(roles)-1 &&
901                 !(allow & roles[rolenum].allow & ROLE_GENDMASK))
902             return FALSE;
903         if (racenum >= 0 && racenum < SIZE(races)-1 &&
904                 !(allow & races[racenum].allow & ROLE_GENDMASK))
905             return FALSE;
906         return TRUE;
907     } else {
908         for (i = 0; i < ROLE_GENDERS; i++) {
909             allow = genders[i].allow;
910             if (rolenum >= 0 && rolenum < SIZE(roles)-1 &&
911                     !(allow & roles[rolenum].allow & ROLE_GENDMASK))
912                 continue;
913             if (racenum >= 0 && racenum < SIZE(races)-1 &&
914                     !(allow & races[racenum].allow & ROLE_GENDMASK))
915                 continue;
916             return TRUE;
917         }
918         return FALSE;
919     }
920 }
921
922 /* pick a random gender subject to any rolenum/racenum/alignnum constraints */
923 /* gender and alignment are not comparable (and also not constrainable) */
924 /* If pickhow == PICK_RIGID a gender is returned only if there is  */
925 /* a single possibility */
926 int
927 pick_gend(rolenum, racenum, alignnum, pickhow)
928 int rolenum, racenum, alignnum, pickhow;
929 {
930     int i;
931     int gends_ok = 0;
932
933     for (i = 0; i < ROLE_GENDERS; i++) {
934         if (ok_gend(rolenum, racenum, i, alignnum))
935             gends_ok++;
936     }
937     if (gends_ok == 0 || (gends_ok > 1 && pickhow == PICK_RIGID))
938         return ROLE_NONE;
939     gends_ok = rn2(gends_ok);
940     for (i = 0; i < ROLE_GENDERS; i++) {
941         if (ok_gend(rolenum, racenum, i, alignnum)) {
942             if (gends_ok == 0)
943                 return i;
944             else
945                 gends_ok--;
946         }
947     }
948     return ROLE_NONE;
949 }
950
951 /* is alignnum compatible with any rolenum/racenum/gendnum constraints? */
952 /* alignment and gender are not comparable (and also not constrainable) */
953 boolean
954 ok_align(rolenum, racenum, gendnum, alignnum)
955 int rolenum, racenum, gendnum, alignnum;
956 {
957     int i;
958     short allow;
959
960     if (alignnum >= 0 && alignnum < ROLE_ALIGNS) {
961         allow = aligns[alignnum].allow;
962         if (rolenum >= 0 && rolenum < SIZE(roles)-1 &&
963                 !(allow & roles[rolenum].allow & ROLE_ALIGNMASK))
964             return FALSE;
965         if (racenum >= 0 && racenum < SIZE(races)-1 &&
966                 !(allow & races[racenum].allow & ROLE_ALIGNMASK))
967             return FALSE;
968         return TRUE;
969     } else {
970         for (i = 0; i < ROLE_ALIGNS; i++) {
971             allow = races[i].allow;
972             if (rolenum >= 0 && rolenum < SIZE(roles)-1 &&
973                     !(allow & roles[rolenum].allow & ROLE_ALIGNMASK))
974                 continue;
975             if (racenum >= 0 && racenum < SIZE(races)-1 &&
976                     !(allow & races[racenum].allow & ROLE_ALIGNMASK))
977                 continue;
978             return TRUE;
979         }
980         return FALSE;
981     }
982 }
983
984 /* pick a random alignment subject to any rolenum/racenum/gendnum constraints */
985 /* alignment and gender are not comparable (and also not constrainable) */
986 /* If pickhow == PICK_RIGID an alignment is returned only if there is  */
987 /* a single possibility */
988 int
989 pick_align(rolenum, racenum, gendnum, pickhow)
990 int rolenum, racenum, gendnum, pickhow;
991 {
992     int i;
993     int aligns_ok = 0;
994
995     for (i = 0; i < ROLE_ALIGNS; i++) {
996         if (ok_align(rolenum, racenum, gendnum, i))
997             aligns_ok++;
998     }
999     if (aligns_ok == 0 || (aligns_ok > 1 && pickhow == PICK_RIGID))
1000         return ROLE_NONE;
1001     aligns_ok = rn2(aligns_ok);
1002     for (i = 0; i < ROLE_ALIGNS; i++) {
1003         if (ok_align(rolenum, racenum, gendnum, i)) {
1004             if (aligns_ok == 0)
1005                 return i;
1006             else
1007                 aligns_ok--;
1008         }
1009     }
1010     return ROLE_NONE;
1011 }
1012
1013 void
1014 rigid_role_checks()
1015 {
1016     /* Some roles are limited to a single race, alignment, or gender and
1017      * calling this routine prior to XXX_player_selection() will help
1018      * prevent an extraneous prompt that actually doesn't allow
1019      * you to choose anything further. Note the use of PICK_RIGID which
1020      * causes the pick_XX() routine to return a value only if there is one
1021      * single possible selection, otherwise it returns ROLE_NONE.
1022      *
1023      */
1024     if (flags.initrole == ROLE_RANDOM) {
1025         /* If the role was explicitly specified as ROLE_RANDOM
1026          * via -uXXXX-@ then choose the role in here to narrow down
1027          * later choices. Pick a random role in this case.
1028          */
1029         flags.initrole = pick_role(flags.initrace, flags.initgend,
1030                                         flags.initalign, PICK_RANDOM);
1031         if (flags.initrole < 0)
1032             flags.initrole = randrole();
1033     }
1034     if (flags.initrole != ROLE_NONE) {
1035         if (flags.initrace == ROLE_NONE)
1036              flags.initrace = pick_race(flags.initrole, flags.initgend,
1037                                                 flags.initalign, PICK_RIGID);
1038         if (flags.initalign == ROLE_NONE)
1039              flags.initalign = pick_align(flags.initrole, flags.initrace,
1040                                                 flags.initgend, PICK_RIGID);
1041         if (flags.initgend == ROLE_NONE)
1042              flags.initgend = pick_gend(flags.initrole, flags.initrace,
1043                                                 flags.initalign, PICK_RIGID);
1044     }
1045 }
1046
1047 #define BP_ALIGN        0
1048 #define BP_GEND         1
1049 #define BP_RACE         2
1050 #define BP_ROLE         3
1051 #define NUM_BP          4
1052
1053 STATIC_VAR char pa[NUM_BP], post_attribs;
1054
1055 STATIC_OVL char *
1056 promptsep(buf, num_post_attribs)
1057 char *buf;
1058 int num_post_attribs;
1059 {
1060         const char *conj = "and ";
1061         if (num_post_attribs > 1
1062             && post_attribs < num_post_attribs && post_attribs > 1)
1063                 Strcat(buf, ","); 
1064         Strcat(buf, " ");
1065         --post_attribs;
1066         if (!post_attribs && num_post_attribs > 1) Strcat(buf, conj);
1067         return buf;
1068 }
1069
1070 STATIC_OVL int
1071 role_gendercount(rolenum)
1072 int rolenum;
1073 {
1074         int gendcount = 0;
1075         if (validrole(rolenum)) {
1076                 if (roles[rolenum].allow & ROLE_MALE) ++gendcount;
1077                 if (roles[rolenum].allow & ROLE_FEMALE) ++gendcount;
1078                 if (roles[rolenum].allow & ROLE_NEUTER) ++gendcount;
1079         }
1080         return gendcount;
1081 }
1082
1083 STATIC_OVL int
1084 race_alignmentcount(racenum)
1085 int racenum;
1086 {
1087         int aligncount = 0;
1088         if (racenum != ROLE_NONE && racenum != ROLE_RANDOM) {
1089                 if (races[racenum].allow & ROLE_CHAOTIC) ++aligncount;
1090                 if (races[racenum].allow & ROLE_LAWFUL) ++aligncount;
1091                 if (races[racenum].allow & ROLE_NEUTRAL) ++aligncount;
1092         }
1093         return aligncount;
1094 }
1095
1096 char *
1097 root_plselection_prompt(suppliedbuf, buflen, rolenum, racenum, gendnum, alignnum)
1098 char *suppliedbuf;
1099 int buflen, rolenum, racenum, gendnum, alignnum;
1100 {
1101         int k, gendercount = 0, aligncount = 0;
1102         char buf[BUFSZ];
1103         static char err_ret[] = " character's";
1104         boolean donefirst = FALSE;
1105
1106         if (!suppliedbuf || buflen < 1) return err_ret;
1107
1108         /* initialize these static variables each time this is called */
1109         post_attribs = 0;
1110         for (k=0; k < NUM_BP; ++k)
1111                 pa[k] = 0;
1112         buf[0] = '\0';
1113         *suppliedbuf = '\0';
1114         
1115         /* How many alignments are allowed for the desired race? */
1116         if (racenum != ROLE_NONE && racenum != ROLE_RANDOM)
1117                 aligncount = race_alignmentcount(racenum);
1118
1119         if (alignnum != ROLE_NONE && alignnum != ROLE_RANDOM) {
1120                 /* if race specified, and multiple choice of alignments for it */
1121                 if ((racenum >= 0) && (aligncount > 1)) {
1122                         if (donefirst) Strcat(buf, " ");
1123                         Strcat(buf, aligns[alignnum].adj);
1124                         donefirst = TRUE;
1125                 } else {
1126                         if (donefirst) Strcat(buf, " ");
1127                         Strcat(buf, aligns[alignnum].adj);
1128                         donefirst = TRUE;
1129                 }
1130         } else {
1131                 /* if alignment not specified, but race is specified
1132                         and only one choice of alignment for that race then
1133                         don't include it in the later list */
1134                 if ((((racenum != ROLE_NONE && racenum != ROLE_RANDOM) &&
1135                         ok_race(rolenum, racenum, gendnum, alignnum))
1136                       && (aligncount > 1))
1137                      || (racenum == ROLE_NONE || racenum == ROLE_RANDOM)) {
1138                         pa[BP_ALIGN] = 1;
1139                         post_attribs++;
1140                 }
1141         }
1142         /* <your lawful> */
1143
1144         /* How many genders are allowed for the desired role? */
1145         if (validrole(rolenum))
1146                 gendercount = role_gendercount(rolenum);
1147
1148         if (gendnum != ROLE_NONE  && gendnum != ROLE_RANDOM) {
1149                 if (validrole(rolenum)) {
1150                      /* if role specified, and multiple choice of genders for it,
1151                         and name of role itself does not distinguish gender */
1152                         if ((rolenum != ROLE_NONE) && (gendercount > 1)
1153                                                 && !roles[rolenum].name.f) {
1154                                 if (donefirst) Strcat(buf, " ");
1155                                 Strcat(buf, genders[gendnum].adj);
1156                                 donefirst = TRUE;
1157                         }
1158                 } else {
1159                         if (donefirst) Strcat(buf, " ");
1160                         Strcat(buf, genders[gendnum].adj);
1161                         donefirst = TRUE;
1162                 }
1163         } else {
1164                 /* if gender not specified, but role is specified
1165                         and only one choice of gender then
1166                         don't include it in the later list */
1167                 if ((validrole(rolenum) && (gendercount > 1)) || !validrole(rolenum)) {
1168                         pa[BP_GEND] = 1;
1169                         post_attribs++;
1170                 }
1171         }
1172         /* <your lawful female> */
1173
1174         if (racenum != ROLE_NONE && racenum != ROLE_RANDOM) {
1175                 if (validrole(rolenum) && ok_race(rolenum, racenum, gendnum, alignnum)) {
1176                         if (donefirst) Strcat(buf, " "); 
1177                         Strcat(buf, (rolenum == ROLE_NONE) ?
1178                                 races[racenum].noun :
1179                                 races[racenum].adj);
1180                         donefirst = TRUE;
1181                 } else if (!validrole(rolenum)) {
1182                         if (donefirst) Strcat(buf, " ");
1183                         Strcat(buf, races[racenum].noun);
1184                         donefirst = TRUE;
1185                 } else {
1186                         pa[BP_RACE] = 1;
1187                         post_attribs++;
1188                 }
1189         } else {
1190                 pa[BP_RACE] = 1;
1191                 post_attribs++;
1192         }
1193         /* <your lawful female gnomish> || <your lawful female gnome> */
1194
1195         if (validrole(rolenum)) {
1196                 if (donefirst) Strcat(buf, " ");
1197                 if (gendnum != ROLE_NONE) {
1198                     if (gendnum == 1  && roles[rolenum].name.f)
1199                         Strcat(buf, roles[rolenum].name.f);
1200                     else
1201                         Strcat(buf, roles[rolenum].name.m);
1202                 } else {
1203                         if (roles[rolenum].name.f) {
1204                                 Strcat(buf, roles[rolenum].name.m);
1205                                 Strcat(buf, "/");
1206                                 Strcat(buf, roles[rolenum].name.f);
1207                         } else 
1208                                 Strcat(buf, roles[rolenum].name.m);
1209                 }
1210                 donefirst = TRUE;
1211         } else if (rolenum == ROLE_NONE) {
1212                 pa[BP_ROLE] = 1;
1213                 post_attribs++;
1214         }
1215         
1216         if ((racenum == ROLE_NONE || racenum == ROLE_RANDOM) && !validrole(rolenum)) {
1217                 if (donefirst) Strcat(buf, " ");
1218                 Strcat(buf, "character");
1219                 donefirst = TRUE;
1220         }
1221         /* <your lawful female gnomish cavewoman> || <your lawful female gnome>
1222          *    || <your lawful female character>
1223          */
1224         if (buflen > (int) (strlen(buf) + 1)) {
1225                 Strcpy(suppliedbuf, buf);
1226                 return suppliedbuf;
1227         } else
1228                 return err_ret;
1229 }
1230
1231 char *
1232 build_plselection_prompt(buf, buflen, rolenum, racenum, gendnum, alignnum)
1233 char *buf;
1234 int buflen, rolenum, racenum, gendnum, alignnum;
1235 {
1236         const char *defprompt = "Shall I pick a character for you? [ynq] ";
1237         int num_post_attribs = 0;
1238         char tmpbuf[BUFSZ];
1239         
1240         if (buflen < QBUFSZ)
1241                 return (char *)defprompt;
1242
1243         Strcpy(tmpbuf, "Shall I pick ");
1244         if (racenum != ROLE_NONE || validrole(rolenum))
1245                 Strcat(tmpbuf, "your ");
1246         else {
1247                 Strcat(tmpbuf, "a ");
1248         }
1249         /* <your> */
1250
1251         (void)  root_plselection_prompt(eos(tmpbuf), buflen - strlen(tmpbuf),
1252                                         rolenum, racenum, gendnum, alignnum);
1253         Sprintf(buf, "%s", s_suffix(tmpbuf));
1254
1255         /* buf should now be:
1256          * < your lawful female gnomish cavewoman's> || <your lawful female gnome's>
1257          *    || <your lawful female character's>
1258          *
1259          * Now append the post attributes to it
1260          */
1261
1262         num_post_attribs = post_attribs;
1263         if (post_attribs) {
1264                 if (pa[BP_RACE]) {
1265                         (void) promptsep(eos(buf), num_post_attribs);
1266                         Strcat(buf, "race");
1267                 }
1268                 if (pa[BP_ROLE]) {
1269                         (void) promptsep(eos(buf), num_post_attribs);
1270                         Strcat(buf, "role");
1271                 }
1272                 if (pa[BP_GEND]) {
1273                         (void) promptsep(eos(buf), num_post_attribs);
1274                         Strcat(buf, "gender");
1275                 }
1276                 if (pa[BP_ALIGN]) {
1277                         (void) promptsep(eos(buf), num_post_attribs);
1278                         Strcat(buf, "alignment");
1279                 }
1280         }
1281         Strcat(buf, " for you? [ynq] ");
1282         return buf;
1283 }
1284
1285 #undef BP_ALIGN
1286 #undef BP_GEND
1287 #undef BP_RACE
1288 #undef BP_ROLE
1289 #undef NUM_BP
1290
1291 void
1292 plnamesuffix()
1293 {
1294         char *sptr, *eptr;
1295         int i;
1296
1297         /* Look for tokens delimited by '-' */
1298         if ((eptr = index(plname, '-')) != (char *) 0)
1299             *eptr++ = '\0';
1300         while (eptr) {
1301             /* Isolate the next token */
1302             sptr = eptr;
1303             if ((eptr = index(sptr, '-')) != (char *)0)
1304                 *eptr++ = '\0';
1305
1306             /* Try to match it to something */
1307             if ((i = str2role(sptr)) != ROLE_NONE)
1308                 flags.initrole = i;
1309             else if ((i = str2race(sptr)) != ROLE_NONE)
1310                 flags.initrace = i;
1311             else if ((i = str2gend(sptr)) != ROLE_NONE)
1312                 flags.initgend = i;
1313             else if ((i = str2align(sptr)) != ROLE_NONE)
1314                 flags.initalign = i;
1315         }
1316         if(!plname[0]) {
1317             askname();
1318             plnamesuffix();
1319         }
1320
1321         /* commas in the plname confuse the record file, convert to spaces */
1322         for (sptr = plname; *sptr; sptr++) {
1323                 if (*sptr == ',') *sptr = ' ';
1324         }
1325 }
1326
1327
1328 /*
1329  *      Special setup modifications here:
1330  *
1331  *      Unfortunately, this is going to have to be done
1332  *      on each newgame or restore, because you lose the permonst mods
1333  *      across a save/restore.  :-)
1334  *
1335  *      1 - The Rogue Leader is the Tourist Nemesis.
1336  *      2 - Priests start with a random alignment - convert the leader and
1337  *          guardians here.
1338  *      3 - Elves can have one of two different leaders, but can't work it
1339  *          out here because it requires hacking the level file data (see
1340  *          sp_lev.c).
1341  *
1342  * This code also replaces quest_init().
1343  */
1344 void
1345 role_init()
1346 {
1347         int alignmnt;
1348
1349         /* Strip the role letter out of the player name.
1350          * This is included for backwards compatibility.
1351          */
1352         plnamesuffix();
1353
1354         /* Check for a valid role.  Try flags.initrole first. */
1355         if (!validrole(flags.initrole)) {
1356             /* Try the player letter second */
1357             if ((flags.initrole = str2role(pl_character)) < 0)
1358                 /* None specified; pick a random role */
1359                 flags.initrole = randrole();
1360         }
1361
1362         /* We now have a valid role index.  Copy the role name back. */
1363         /* This should become OBSOLETE */
1364         Strcpy(pl_character, roles[flags.initrole].name.m);
1365         pl_character[PL_CSIZ-1] = '\0';
1366
1367         /* Check for a valid race */
1368         if (!validrace(flags.initrole, flags.initrace))
1369             flags.initrace = randrace(flags.initrole);
1370
1371         /* Check for a valid gender.  If new game, check both initgend
1372          * and female.  On restore, assume flags.female is correct. */
1373         if (flags.pantheon == -1) {     /* new game */
1374             if (!validgend(flags.initrole, flags.initrace, flags.female))
1375                 flags.female = !flags.female;
1376         }
1377         if (!validgend(flags.initrole, flags.initrace, flags.initgend))
1378             /* Note that there is no way to check for an unspecified gender. */
1379             flags.initgend = flags.female;
1380
1381         /* Check for a valid alignment */
1382         if (!validalign(flags.initrole, flags.initrace, flags.initalign))
1383             /* Pick a random alignment */
1384             flags.initalign = randalign(flags.initrole, flags.initrace);
1385         alignmnt = aligns[flags.initalign].value;
1386
1387         /* Initialize urole and urace */
1388         urole = roles[flags.initrole];
1389         urace = races[flags.initrace];
1390
1391         /* Fix up the quest leader */
1392         if (urole.ldrnum != NON_PM) {
1393             mons[urole.ldrnum].msound = MS_LEADER;
1394             mons[urole.ldrnum].mflags2 |= (M2_PEACEFUL);
1395             mons[urole.ldrnum].mflags3 |= M3_CLOSE;
1396             mons[urole.ldrnum].maligntyp = alignmnt * 3;
1397         }
1398
1399         /* Fix up the quest guardians */
1400         if (urole.guardnum != NON_PM) {
1401             mons[urole.guardnum].mflags2 |= (M2_PEACEFUL);
1402             mons[urole.guardnum].maligntyp = alignmnt * 3;
1403         }
1404
1405         /* Fix up the quest nemesis */
1406         if (urole.neminum != NON_PM) {
1407             mons[urole.neminum].msound = MS_NEMESIS;
1408             mons[urole.neminum].mflags2 &= ~(M2_PEACEFUL);
1409             mons[urole.neminum].mflags2 |= (M2_NASTY|M2_STALK|M2_HOSTILE);
1410             mons[urole.neminum].mflags3 |= M3_WANTSARTI | M3_WAITFORU;
1411         }
1412
1413         /* Fix up the god names */
1414         if (flags.pantheon == -1) {             /* new game */
1415             flags.pantheon = flags.initrole;    /* use own gods */
1416             while (!roles[flags.pantheon].lgod) /* unless they're missing */
1417                 flags.pantheon = randrole();
1418         }
1419         if (!urole.lgod) {
1420             urole.lgod = roles[flags.pantheon].lgod;
1421             urole.ngod = roles[flags.pantheon].ngod;
1422             urole.cgod = roles[flags.pantheon].cgod;
1423         }
1424
1425         /* Fix up infravision */
1426         if (mons[urace.malenum].mflags3 & M3_INFRAVISION) {
1427             /* although an infravision intrinsic is possible, infravision
1428              * is purely a property of the physical race.  This means that we
1429              * must put the infravision flag in the player's current race
1430              * (either that or have separate permonst entries for
1431              * elven/non-elven members of each class).  The side effect is that
1432              * all NPCs of that class will have (probably bogus) infravision,
1433              * but since infravision has no effect for NPCs anyway we can
1434              * ignore this.
1435              */
1436             mons[urole.malenum].mflags3 |= M3_INFRAVISION;
1437             if (urole.femalenum != NON_PM)
1438                 mons[urole.femalenum].mflags3 |= M3_INFRAVISION;
1439         }
1440
1441         /* Artifacts are fixed in hack_artifacts() */
1442
1443         /* Success! */
1444         return;
1445 }
1446
1447 const char *
1448 Hello(mtmp)
1449 struct monst *mtmp;
1450 {
1451         switch (Role_switch) {
1452         case PM_KNIGHT:
1453             return ("Salutations"); /* Olde English */
1454         case PM_SAMURAI:
1455             return (mtmp && mtmp->data == &mons[PM_SHOPKEEPER] ?
1456                         "Irasshaimase" : "Konnichi wa"); /* Japanese */
1457 #ifdef TOURIST
1458         case PM_TOURIST:
1459             return ("Aloha");       /* Hawaiian */
1460 #endif
1461         case PM_VALKYRIE:
1462             return (
1463 #ifdef MAIL
1464                         mtmp && mtmp->data == &mons[PM_MAIL_DAEMON] ? "Hallo" :
1465 #endif
1466                         "Velkommen");   /* Norse */
1467         default:
1468             return ("Hello");
1469         }
1470 }
1471
1472 const char *
1473 Goodbye()
1474 {
1475         switch (Role_switch) {
1476         case PM_KNIGHT:
1477             return ("Fare thee well");  /* Olde English */
1478         case PM_SAMURAI:
1479             return ("Sayonara");        /* Japanese */
1480 #ifdef TOURIST
1481         case PM_TOURIST:
1482             return ("Aloha");           /* Hawaiian */
1483 #endif
1484         case PM_VALKYRIE:
1485             return ("Farvel");          /* Norse */
1486         default:
1487             return ("Goodbye");
1488         }
1489 }
1490
1491 /* role.c */