1 /* NetHack 3.6	role.c	$NHDT-Date: 1547086250 2019/01/10 02:10:50 $  $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.56 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985-1999. */
3 /*-Copyright (c) Robert Patrick Rankin, 2012. */
4 /* NetHack may be freely redistributed.  See license for details. */
5 
6 #include "hack.h"
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",
40       "the College of Archeology",
41       "the Tomb of the Toltec Kings",
42       PM_ARCHEOLOGIST,
43       NON_PM,
44       NON_PM,
45       PM_LORD_CARNARVON,
46       PM_STUDENT,
47       PM_MINION_OF_HUHETOTL,
48       NON_PM,
49       PM_HUMAN_MUMMY,
50       S_SNAKE,
51       S_MUMMY,
52       ART_ORB_OF_DETECTION,
53       MH_HUMAN | MH_DWARF | MH_GNOME | ROLE_MALE | ROLE_FEMALE | ROLE_LAWFUL
54           | ROLE_NEUTRAL,
55       /* Str Int Wis Dex Con Cha */
56       { 7, 10, 10, 7, 7, 7 },
57       { 20, 20, 20, 10, 20, 10 },
58       /* Init   Lower  Higher */
59       { 11, 0, 0, 8, 1, 0 }, /* Hit points */
60       { 1, 0, 0, 1, 0, 1 },
61       14, /* Energy */
62       10,
63       5,
64       0,
65       2,
66       10,
67       A_INT,
68       SPE_MAGIC_MAPPING,
69       -4 },
70     { { "Barbarian", 0 },
71       { { "Plunderer", "Plunderess" },
72         { "Pillager", 0 },
73         { "Bandit", 0 },
74         { "Brigand", 0 },
75         { "Raider", 0 },
76         { "Reaver", 0 },
77         { "Slayer", 0 },
78         { "Chieftain", "Chieftainess" },
79         { "Conqueror", "Conqueress" } },
80       "Mitra", "Crom", "Set", /* Hyborian */
81       "Bar",
82       "the Camp of the Duali Tribe",
83       "the Duali Oasis",
84       PM_BARBARIAN,
85       NON_PM,
86       NON_PM,
87       PM_PELIAS,
88       PM_CHIEFTAIN,
89       PM_THOTH_AMON,
90       PM_OGRE,
91       PM_TROLL,
92       S_OGRE,
93       S_TROLL,
94       ART_HEART_OF_AHRIMAN,
95       MH_HUMAN | MH_ORC | ROLE_MALE | ROLE_FEMALE | ROLE_NEUTRAL
96           | ROLE_CHAOTIC,
97       /* Str Int Wis Dex Con Cha */
98       { 16, 7, 7, 15, 16, 6 },
99       { 30, 6, 7, 20, 30, 7 },
100       /* Init   Lower  Higher */
101       { 14, 0, 0, 10, 2, 0 }, /* Hit points */
102       { 1, 0, 0, 1, 0, 1 },
103       10, /* Energy */
104       10,
105       14,
106       0,
107       0,
108       8,
109       A_INT,
110       SPE_HASTE_SELF,
111       -4 },
112     { { "Caveman", "Cavewoman" },
113       { { "Troglodyte", 0 },
114         { "Aborigine", 0 },
115         { "Wanderer", 0 },
116         { "Vagrant", 0 },
117         { "Wayfarer", 0 },
118         { "Roamer", 0 },
119         { "Nomad", 0 },
120         { "Rover", 0 },
121         { "Pioneer", 0 } },
122       "Anu", "_Ishtar", "Anshar", /* Babylonian */
123       "Cav",
124       "the Caves of the Ancestors",
125       "the Dragon's Lair",
126       PM_CAVEMAN,
127       PM_CAVEWOMAN,
128       PM_LITTLE_DOG,
129       PM_SHAMAN_KARNOV,
130       PM_NEANDERTHAL,
131       PM_CHROMATIC_DRAGON,
132       PM_BUGBEAR,
133       PM_HILL_GIANT,
134       S_HUMANOID,
135       S_GIANT,
136       ART_SCEPTRE_OF_MIGHT,
137       MH_HUMAN | MH_DWARF | MH_GNOME | ROLE_MALE | ROLE_FEMALE | ROLE_LAWFUL
138           | ROLE_NEUTRAL,
139       /* Str Int Wis Dex Con Cha */
140       { 10, 7, 7, 7, 8, 6 },
141       { 30, 6, 7, 20, 30, 7 },
142       /* Init   Lower  Higher */
143       { 14, 0, 0, 8, 2, 0 }, /* Hit points */
144       { 1, 0, 0, 1, 0, 1 },
145       10, /* Energy */
146       0,
147       12,
148       0,
149       1,
150       8,
151       A_INT,
152       SPE_DIG,
153       -4 },
154     { { "Healer", 0 },
155       { { "Rhizotomist", 0 },
156         { "Empiric", 0 },
157         { "Embalmer", 0 },
158         { "Dresser", 0 },
159         { "Medicus ossium", "Medica ossium" },
160         { "Herbalist", 0 },
161         { "Magister", "Magistra" },
162         { "Physician", 0 },
163         { "Chirurgeon", 0 } },
164       "_Athena", "Hermes", "Poseidon", /* Greek */
165       "Hea",
166       "the Temple of Epidaurus",
167       "the Temple of Coeus",
168       PM_HEALER,
169       NON_PM,
170       NON_PM,
171       PM_HIPPOCRATES,
172       PM_ATTENDANT,
173       PM_CYCLOPS,
174       PM_GIANT_RAT,
175       PM_SNAKE,
176       S_RODENT,
177       S_YETI,
178       ART_STAFF_OF_AESCULAPIUS,
179       MH_HUMAN | MH_GNOME | ROLE_MALE | ROLE_FEMALE | ROLE_NEUTRAL,
180       /* Str Int Wis Dex Con Cha */
181       { 7, 7, 13, 7, 11, 16 },
182       { 15, 20, 20, 15, 25, 5 },
183       /* Init   Lower  Higher */
184       { 11, 0, 0, 8, 1, 0 }, /* Hit points */
185       { 1, 4, 0, 1, 0, 2 },
186       20, /* Energy */
187       10,
188       3,
189       -3,
190       2,
191       10,
192       A_WIS,
193       SPE_CURE_SICKNESS,
194       -4 },
195     { { "Knight", 0 },
196       { { "Gallant", 0 },
197         { "Esquire", 0 },
198         { "Bachelor", 0 },
199         { "Sergeant", 0 },
200         { "Knight", 0 },
201         { "Banneret", 0 },
202         { "Chevalier", "Chevaliere" },
203         { "Seignieur", "Dame" },
204         { "Paladin", 0 } },
205       "Lugh", "_Brigit", "Manannan Mac Lir", /* Celtic */
206       "Kni",
207       "Camelot Castle",
208       "the Isle of Glass",
209       PM_KNIGHT,
210       NON_PM,
211       PM_PONY,
212       PM_KING_ARTHUR,
213       PM_PAGE,
214       PM_IXOTH,
215       PM_QUASIT,
216       PM_OCHRE_JELLY,
217       S_IMP,
218       S_JELLY,
219       ART_MAGIC_MIRROR_OF_MERLIN,
220       MH_HUMAN | ROLE_MALE | ROLE_FEMALE | ROLE_LAWFUL,
221       /* Str Int Wis Dex Con Cha */
222       { 13, 7, 14, 8, 10, 17 },
223       { 30, 15, 15, 10, 20, 10 },
224       /* Init   Lower  Higher */
225       { 14, 0, 0, 8, 2, 0 }, /* Hit points */
226       { 1, 4, 0, 1, 0, 2 },
227       10, /* Energy */
228       10,
229       8,
230       -2,
231       0,
232       9,
233       A_WIS,
234       SPE_TURN_UNDEAD,
235       -4 },
236     { { "Monk", 0 },
237       { { "Candidate", 0 },
238         { "Novice", 0 },
239         { "Initiate", 0 },
240         { "Student of Stones", 0 },
241         { "Student of Waters", 0 },
242         { "Student of Metals", 0 },
243         { "Student of Winds", 0 },
244         { "Student of Fire", 0 },
245         { "Master", 0 } },
246       "Shan Lai Ching", "Chih Sung-tzu", "Huan Ti", /* Chinese */
247       "Mon",
248       "the Monastery of Chan-Sune",
249       "the Monastery of the Earth-Lord",
250       PM_MONK,
251       NON_PM,
252       NON_PM,
253       PM_GRAND_MASTER,
254       PM_ABBOT,
255       PM_MASTER_KAEN,
256       PM_EARTH_ELEMENTAL,
257       PM_XORN,
258       S_ELEMENTAL,
259       S_XORN,
260       ART_EYES_OF_THE_OVERWORLD,
261       MH_HUMAN | ROLE_MALE | ROLE_FEMALE | ROLE_LAWFUL | ROLE_NEUTRAL
262           | ROLE_CHAOTIC,
263       /* Str Int Wis Dex Con Cha */
264       { 10, 7, 8, 8, 7, 7 },
265       { 25, 10, 20, 20, 15, 10 },
266       /* Init   Lower  Higher */
267       { 12, 0, 0, 8, 1, 0 }, /* Hit points */
268       { 2, 2, 0, 2, 0, 2 },
269       10, /* Energy */
270       10,
271       8,
272       -2,
273       2,
274       20,
275       A_WIS,
276       SPE_RESTORE_ABILITY,
277       -4 },
278     { { "Priest", "Priestess" },
279       { { "Aspirant", 0 },
280         { "Acolyte", 0 },
281         { "Adept", 0 },
282         { "Priest", "Priestess" },
283         { "Curate", 0 },
284         { "Canon", "Canoness" },
285         { "Lama", 0 },
286         { "Patriarch", "Matriarch" },
287         { "High Priest", "High Priestess" } },
288       0, 0, 0, /* deities from a randomly chosen other role will be used */
289       "Pri",
290       "the Great Temple",
291       "the Temple of Nalzok",
292       PM_PRIEST,
293       PM_PRIESTESS,
294       NON_PM,
295       PM_ARCH_PRIEST,
296       PM_ACOLYTE,
297       PM_NALZOK,
298       PM_HUMAN_ZOMBIE,
299       PM_WRAITH,
300       S_ZOMBIE,
301       S_WRAITH,
302       ART_MITRE_OF_HOLINESS,
303       MH_HUMAN | MH_ELF | ROLE_MALE | ROLE_FEMALE | ROLE_LAWFUL | ROLE_NEUTRAL
304           | ROLE_CHAOTIC,
305       /* Str Int Wis Dex Con Cha */
306       { 7, 7, 10, 7, 7, 7 },
307       { 15, 10, 30, 15, 20, 10 },
308       /* Init   Lower  Higher */
309       { 12, 0, 0, 8, 1, 0 }, /* Hit points */
310       { 4, 3, 0, 2, 0, 2 },
311       10, /* Energy */
312       0,
313       3,
314       -2,
315       2,
316       10,
317       A_WIS,
318       SPE_REMOVE_CURSE,
319       -4 },
320     /* Note:  Rogue precedes Ranger so that use of `-R' on the command line
321        retains its traditional meaning. */
322     { { "Rogue", 0 },
323       { { "Footpad", 0 },
324         { "Cutpurse", 0 },
325         { "Rogue", 0 },
326         { "Pilferer", 0 },
327         { "Robber", 0 },
328         { "Burglar", 0 },
329         { "Filcher", 0 },
330         { "Magsman", "Magswoman" },
331         { "Thief", 0 } },
332       "Issek", "Mog", "Kos", /* Nehwon */
333       "Rog",
334       "the Thieves' Guild Hall",
335       "the Assassins' Guild Hall",
336       PM_ROGUE,
337       NON_PM,
338       NON_PM,
339       PM_MASTER_OF_THIEVES,
340       PM_THUG,
341       PM_MASTER_ASSASSIN,
342       PM_LEPRECHAUN,
343       PM_GUARDIAN_NAGA,
344       S_NYMPH,
345       S_NAGA,
346       ART_MASTER_KEY_OF_THIEVERY,
347       MH_HUMAN | MH_ORC | ROLE_MALE | ROLE_FEMALE | ROLE_CHAOTIC,
348       /* Str Int Wis Dex Con Cha */
349       { 7, 7, 7, 10, 7, 6 },
350       { 20, 10, 10, 30, 20, 10 },
351       /* Init   Lower  Higher */
352       { 10, 0, 0, 8, 1, 0 }, /* Hit points */
353       { 1, 0, 0, 1, 0, 1 },
354       11, /* Energy */
355       10,
356       8,
357       0,
358       1,
359       9,
360       A_INT,
361       SPE_DETECT_TREASURE,
362       -4 },
363     { { "Ranger", 0 },
364       {
365 #if 0 /* OBSOLETE */
366         {"Edhel",   "Elleth"},
367         {"Edhel",   "Elleth"},         /* elf-maid */
368         {"Ohtar",   "Ohtie"},          /* warrior */
369         {"Kano",    "Kanie"},          /* commander (Q.) ['a] educated guess,
370                                           until further research- SAC */
371         {"Arandur"," Aranduriel"}, /* king's servant, minister (Q.) - guess */
372         {"Hir",         "Hiril"},      /* lord, lady (S.) ['ir] */
373         {"Aredhel",     "Arwen"},      /* noble elf, maiden (S.) */
374         {"Ernil",       "Elentariel"}, /* prince (S.), elf-maiden (Q.) */
375         {"Elentar",     "Elentari"},   /* Star-king, -queen (Q.) */
376         "Solonor Thelandira", "Aerdrie Faenya", "Lolth", /* Elven */
377 #endif
378         { "Tenderfoot", 0 },
379         { "Lookout", 0 },
380         { "Trailblazer", 0 },
381         { "Reconnoiterer", "Reconnoiteress" },
382         { "Scout", 0 },
383         { "Arbalester", 0 }, /* One skilled at crossbows */
384         { "Archer", 0 },
385         { "Sharpshooter", 0 },
386         { "Marksman", "Markswoman" } },
387       "Mercury", "_Venus", "Mars", /* Roman/planets */
388       "Ran",
389       "Orion's camp",
390       "the cave of the wumpus",
391       PM_RANGER,
392       NON_PM,
393       PM_LITTLE_DOG /* Orion & canis major */,
394       PM_ORION,
395       PM_HUNTER,
396       PM_SCORPIUS,
397       PM_FOREST_CENTAUR,
398       PM_SCORPION,
399       S_CENTAUR,
400       S_SPIDER,
401       ART_LONGBOW_OF_DIANA,
402       MH_HUMAN | MH_ELF | MH_GNOME | MH_ORC | ROLE_MALE | ROLE_FEMALE
403           | ROLE_NEUTRAL | ROLE_CHAOTIC,
404       /* Str Int Wis Dex Con Cha */
405       { 13, 13, 13, 9, 13, 7 },
406       { 30, 10, 10, 20, 20, 10 },
407       /* Init   Lower  Higher */
408       { 13, 0, 0, 6, 1, 0 }, /* Hit points */
409       { 1, 0, 0, 1, 0, 1 },
410       12, /* Energy */
411       10,
412       9,
413       2,
414       1,
415       10,
416       A_INT,
417       SPE_INVISIBILITY,
418       -4 },
419     { { "Samurai", 0 },
420       { { "Hatamoto", 0 },       /* Banner Knight */
421         { "Ronin", 0 },          /* no allegiance */
422         { "Ninja", "Kunoichi" }, /* secret society */
423         { "Joshu", 0 },          /* heads a castle */
424         { "Ryoshu", 0 },         /* has a territory */
425         { "Kokushu", 0 },        /* heads a province */
426         { "Daimyo", 0 },         /* a samurai lord */
427         { "Kuge", 0 },           /* Noble of the Court */
428         { "Shogun", 0 } },       /* supreme commander, warlord */
429       "_Amaterasu Omikami", "Raijin", "Susanowo", /* Japanese */
430       "Sam",
431       "the Castle of the Taro Clan",
432       "the Shogun's Castle",
433       PM_SAMURAI,
434       NON_PM,
435       PM_LITTLE_DOG,
436       PM_LORD_SATO,
437       PM_ROSHI,
438       PM_ASHIKAGA_TAKAUJI,
439       PM_WOLF,
440       PM_STALKER,
441       S_DOG,
442       S_ELEMENTAL,
443       ART_TSURUGI_OF_MURAMASA,
444       MH_HUMAN | ROLE_MALE | ROLE_FEMALE | ROLE_LAWFUL,
445       /* Str Int Wis Dex Con Cha */
446       { 10, 8, 7, 10, 17, 6 },
447       { 30, 10, 8, 30, 14, 8 },
448       /* Init   Lower  Higher */
449       { 13, 0, 0, 8, 1, 0 }, /* Hit points */
450       { 1, 0, 0, 1, 0, 1 },
451       11, /* Energy */
452       10,
453       10,
454       0,
455       0,
456       8,
457       A_INT,
458       SPE_CLAIRVOYANCE,
459       -4 },
460     { { "Tourist", 0 },
461       { { "Rambler", 0 },
462         { "Sightseer", 0 },
463         { "Excursionist", 0 },
464         { "Peregrinator", "Peregrinatrix" },
465         { "Traveler", 0 },
466         { "Journeyer", 0 },
467         { "Voyager", 0 },
468         { "Explorer", 0 },
469         { "Adventurer", 0 } },
470       "Blind Io", "_The Lady", "Offler", /* Discworld */
471       "Tou",
472       "Ankh-Morpork",
473       "the Thieves' Guild Hall",
474       PM_TOURIST,
475       NON_PM,
476       NON_PM,
477       PM_TWOFLOWER,
478       PM_GUIDE,
479       PM_MASTER_OF_THIEVES,
480       PM_GIANT_SPIDER,
481       PM_FOREST_CENTAUR,
482       S_SPIDER,
483       S_CENTAUR,
484       ART_YENDORIAN_EXPRESS_CARD,
485       MH_HUMAN | ROLE_MALE | ROLE_FEMALE | ROLE_NEUTRAL,
486       /* Str Int Wis Dex Con Cha */
487       { 7, 10, 6, 7, 7, 10 },
488       { 15, 10, 10, 15, 30, 20 },
489       /* Init   Lower  Higher */
490       { 8, 0, 0, 8, 0, 0 }, /* Hit points */
491       { 1, 0, 0, 1, 0, 1 },
492       14, /* Energy */
493       0,
494       5,
495       1,
496       2,
497       10,
498       A_INT,
499       SPE_CHARM_MONSTER,
500       -4 },
501     { { "Valkyrie", 0 },
502       { { "Stripling", 0 },
503         { "Skirmisher", 0 },
504         { "Fighter", 0 },
505         { "Man-at-arms", "Woman-at-arms" },
506         { "Warrior", 0 },
507         { "Swashbuckler", 0 },
508         { "Hero", "Heroine" },
509         { "Champion", 0 },
510         { "Lord", "Lady" } },
511       "Tyr", "Odin", "Loki", /* Norse */
512       "Val",
513       "the Shrine of Destiny",
514       "the cave of Surtur",
515       PM_VALKYRIE,
516       NON_PM,
517       NON_PM /*PM_WINTER_WOLF_CUB*/,
518       PM_NORN,
519       PM_WARRIOR,
520       PM_LORD_SURTUR,
521       PM_FIRE_ANT,
522       PM_FIRE_GIANT,
523       S_ANT,
524       S_GIANT,
525       ART_ORB_OF_FATE,
526       MH_HUMAN | MH_DWARF | ROLE_FEMALE | ROLE_LAWFUL | ROLE_NEUTRAL,
527       /* Str Int Wis Dex Con Cha */
528       { 10, 7, 7, 7, 10, 7 },
529       { 30, 6, 7, 20, 30, 7 },
530       /* Init   Lower  Higher */
531       { 14, 0, 0, 8, 2, 0 }, /* Hit points */
532       { 1, 0, 0, 1, 0, 1 },
533       10, /* Energy */
534       0,
535       10,
536       -2,
537       0,
538       9,
539       A_WIS,
540       SPE_CONE_OF_COLD,
541       -4 },
542     { { "Wizard", 0 },
543       { { "Evoker", 0 },
544         { "Conjurer", 0 },
545         { "Thaumaturge", 0 },
546         { "Magician", 0 },
547         { "Enchanter", "Enchantress" },
548         { "Sorcerer", "Sorceress" },
549         { "Necromancer", 0 },
550         { "Wizard", 0 },
551         { "Mage", 0 } },
552       "Ptah", "Thoth", "Anhur", /* Egyptian */
553       "Wiz",
554       "the Lonely Tower",
555       "the Tower of Darkness",
556       PM_WIZARD,
557       NON_PM,
558       PM_KITTEN,
559       PM_NEFERET_THE_GREEN,
560       PM_APPRENTICE,
561       PM_DARK_ONE,
562       PM_VAMPIRE_BAT,
563       PM_XORN,
564       S_BAT,
565       S_WRAITH,
566       ART_EYE_OF_THE_AETHIOPICA,
567       MH_HUMAN | MH_ELF | MH_GNOME | MH_ORC | ROLE_MALE | ROLE_FEMALE
568           | ROLE_NEUTRAL | ROLE_CHAOTIC,
569       /* Str Int Wis Dex Con Cha */
570       { 7, 10, 7, 7, 7, 7 },
571       { 10, 30, 10, 20, 20, 10 },
572       /* Init   Lower  Higher */
573       { 10, 0, 0, 8, 1, 0 }, /* Hit points */
574       { 4, 3, 0, 2, 0, 3 },
575       12, /* Energy */
576       0,
577       1,
578       0,
579       3,
580       10,
581       A_INT,
582       SPE_MAGIC_MISSILE,
583       -4 },
584     /* Array terminator */
585     { { 0, 0 } }
586 };
587 
588 /* The player's role, created at runtime from initial
589  * choices.  This may be munged in role_init().
590  */
591 struct Role urole = {
592     { "Undefined", 0 },
593     { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 },
594       { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } },
595     "L", "N", "C",
596     "Xxx", "home", "locate",
597     NON_PM, NON_PM, NON_PM, NON_PM, NON_PM, NON_PM, NON_PM, NON_PM,
598     0, 0, 0, 0,
599     /* Str Int Wis Dex Con Cha */
600     { 7, 7, 7, 7, 7, 7 },
601     { 20, 15, 15, 20, 20, 10 },
602     /* Init   Lower  Higher */
603     { 10, 0, 0, 8, 1, 0 }, /* Hit points */
604     { 2, 0, 0, 2, 0, 3 },
605     14, /* Energy */
606      0,
607     10,
608      0,
609      0,
610      4,
611     A_INT,
612      0,
613     -3
614 };
615 
616 /* Table of all races */
617 const struct Race races[] = {
618     {
619         "human",
620         "human",
621         "humanity",
622         "Hum",
623         { "man", "woman" },
624         PM_HUMAN,
625         NON_PM,
626         PM_HUMAN_MUMMY,
627         PM_HUMAN_ZOMBIE,
628         MH_HUMAN | ROLE_MALE | ROLE_FEMALE | ROLE_LAWFUL | ROLE_NEUTRAL
629             | ROLE_CHAOTIC,
630         MH_HUMAN,
631         0,
632         MH_GNOME | MH_ORC,
633         /*    Str     Int Wis Dex Con Cha */
634         { 3, 3, 3, 3, 3, 3 },
635         { STR18(100), 18, 18, 18, 18, 18 },
636         /* Init   Lower  Higher */
637         { 2, 0, 0, 2, 1, 0 }, /* Hit points */
638         { 1, 0, 2, 0, 2, 0 }  /* Energy */
639     },
640     {
641         "elf",
642         "elven",
643         "elvenkind",
644         "Elf",
645         { 0, 0 },
646         PM_ELF,
647         NON_PM,
648         PM_ELF_MUMMY,
649         PM_ELF_ZOMBIE,
650         MH_ELF | ROLE_MALE | ROLE_FEMALE | ROLE_CHAOTIC,
651         MH_ELF,
652         MH_ELF,
653         MH_ORC,
654         /*  Str    Int Wis Dex Con Cha */
655         { 3, 3, 3, 3, 3, 3 },
656         { 18, 20, 20, 18, 16, 18 },
657         /* Init   Lower  Higher */
658         { 1, 0, 0, 1, 1, 0 }, /* Hit points */
659         { 2, 0, 3, 0, 3, 0 }  /* Energy */
660     },
661     {
662         "dwarf",
663         "dwarven",
664         "dwarvenkind",
665         "Dwa",
666         { 0, 0 },
667         PM_DWARF,
668         NON_PM,
669         PM_DWARF_MUMMY,
670         PM_DWARF_ZOMBIE,
671         MH_DWARF | ROLE_MALE | ROLE_FEMALE | ROLE_LAWFUL,
672         MH_DWARF,
673         MH_DWARF | MH_GNOME,
674         MH_ORC,
675         /*    Str     Int Wis Dex Con Cha */
676         { 3, 3, 3, 3, 3, 3 },
677         { STR18(100), 16, 16, 20, 20, 16 },
678         /* Init   Lower  Higher */
679         { 4, 0, 0, 3, 2, 0 }, /* Hit points */
680         { 0, 0, 0, 0, 0, 0 }  /* Energy */
681     },
682     {
683         "gnome",
684         "gnomish",
685         "gnomehood",
686         "Gno",
687         { 0, 0 },
688         PM_GNOME,
689         NON_PM,
690         PM_GNOME_MUMMY,
691         PM_GNOME_ZOMBIE,
692         MH_GNOME | ROLE_MALE | ROLE_FEMALE | ROLE_NEUTRAL,
693         MH_GNOME,
694         MH_DWARF | MH_GNOME,
695         MH_HUMAN,
696         /*  Str    Int Wis Dex Con Cha */
697         { 3, 3, 3, 3, 3, 3 },
698         { STR18(50), 19, 18, 18, 18, 18 },
699         /* Init   Lower  Higher */
700         { 1, 0, 0, 1, 0, 0 }, /* Hit points */
701         { 2, 0, 2, 0, 2, 0 }  /* Energy */
702     },
703     {
704         "orc",
705         "orcish",
706         "orcdom",
707         "Orc",
708         { 0, 0 },
709         PM_ORC,
710         NON_PM,
711         PM_ORC_MUMMY,
712         PM_ORC_ZOMBIE,
713         MH_ORC | ROLE_MALE | ROLE_FEMALE | ROLE_CHAOTIC,
714         MH_ORC,
715         0,
716         MH_HUMAN | MH_ELF | MH_DWARF,
717         /*  Str    Int Wis Dex Con Cha */
718         { 3, 3, 3, 3, 3, 3 },
719         { STR18(50), 16, 16, 18, 18, 16 },
720         /* Init   Lower  Higher */
721         { 1, 0, 0, 1, 0, 0 }, /* Hit points */
722         { 1, 0, 1, 0, 1, 0 }  /* Energy */
723     },
724     /* Array terminator */
725     { 0, 0, 0, 0 }
726 };
727 
728 /* The player's race, created at runtime from initial
729  * choices.  This may be munged in role_init().
730  */
731 struct Race urace = {
732     "something",
733     "undefined",
734     "something",
735     "Xxx",
736     { 0, 0 },
737     NON_PM,
738     NON_PM,
739     NON_PM,
740     NON_PM,
741     0,
742     0,
743     0,
744     0,
745     /*    Str     Int Wis Dex Con Cha */
746     { 3, 3, 3, 3, 3, 3 },
747     { STR18(100), 18, 18, 18, 18, 18 },
748     /* Init   Lower  Higher */
749     { 2, 0, 0, 2, 1, 0 }, /* Hit points */
750     { 1, 0, 2, 0, 2, 0 }  /* Energy */
751 };
752 
753 /* Table of all genders */
754 const struct Gender genders[] = {
755     { "male", "he", "him", "his", "Mal", ROLE_MALE },
756     { "female", "she", "her", "her", "Fem", ROLE_FEMALE },
757     { "neuter", "it", "it", "its", "Ntr", ROLE_NEUTER }
758 };
759 
760 /* Table of all alignments */
761 const struct Align aligns[] = {
762     { "law", "lawful", "Law", ROLE_LAWFUL, A_LAWFUL },
763     { "balance", "neutral", "Neu", ROLE_NEUTRAL, A_NEUTRAL },
764     { "chaos", "chaotic", "Cha", ROLE_CHAOTIC, A_CHAOTIC },
765     { "evil", "unaligned", "Una", 0, A_NONE }
766 };
767 
768 /* Filters */
769 static struct {
770     boolean roles[SIZE(roles)];
771     short mask;
772 } rfilter;
773 
774 STATIC_DCL int NDECL(randrole_filtered);
775 STATIC_DCL char *FDECL(promptsep, (char *, int));
776 STATIC_DCL int FDECL(role_gendercount, (int));
777 STATIC_DCL int FDECL(race_alignmentcount, (int));
778 
779 /* used by str2XXX() */
780 static char NEARDATA randomstr[] = "random";
781 
782 boolean
validrole(rolenum)783 validrole(rolenum)
784 int rolenum;
785 {
786     return (boolean) (rolenum >= 0 && rolenum < SIZE(roles) - 1);
787 }
788 
789 int
randrole(for_display)790 randrole(for_display)
791 boolean for_display;
792 {
793     int res = SIZE(roles) - 1;
794 
795     if (for_display)
796         res = rn2_on_display_rng(res);
797     else
798         res = rn2(res);
799     return res;
800 }
801 
802 STATIC_OVL int
randrole_filtered()803 randrole_filtered()
804 {
805     int i, n = 0, set[SIZE(roles)];
806 
807     /* this doesn't rule out impossible combinations but attempts to
808        honor all the filter masks */
809     for (i = 0; i < SIZE(roles); ++i)
810         if (ok_role(i, ROLE_NONE, ROLE_NONE, ROLE_NONE)
811             && ok_race(i, ROLE_RANDOM, ROLE_NONE, ROLE_NONE)
812             && ok_gend(i, ROLE_NONE, ROLE_RANDOM, ROLE_NONE)
813             && ok_align(i, ROLE_NONE, ROLE_NONE, ROLE_RANDOM))
814             set[n++] = i;
815     return n ? set[rn2(n)] : randrole(FALSE);
816 }
817 
818 int
str2role(str)819 str2role(str)
820 const char *str;
821 {
822     int i, len;
823 
824     /* Is str valid? */
825     if (!str || !str[0])
826         return ROLE_NONE;
827 
828     /* Match as much of str as is provided */
829     len = strlen(str);
830     for (i = 0; roles[i].name.m; i++) {
831         /* Does it match the male name? */
832         if (!strncmpi(str, roles[i].name.m, len))
833             return i;
834         /* Or the female name? */
835         if (roles[i].name.f && !strncmpi(str, roles[i].name.f, len))
836             return i;
837         /* Or the filecode? */
838         if (!strcmpi(str, roles[i].filecode))
839             return i;
840     }
841 
842     if ((len == 1 && (*str == '*' || *str == '@'))
843         || !strncmpi(str, randomstr, len))
844         return ROLE_RANDOM;
845 
846     /* Couldn't find anything appropriate */
847     return ROLE_NONE;
848 }
849 
850 boolean
validrace(rolenum,racenum)851 validrace(rolenum, racenum)
852 int rolenum, racenum;
853 {
854     /* Assumes validrole */
855     return (boolean) (racenum >= 0 && racenum < SIZE(races) - 1
856                       && (roles[rolenum].allow & races[racenum].allow
857                           & ROLE_RACEMASK));
858 }
859 
860 int
randrace(rolenum)861 randrace(rolenum)
862 int rolenum;
863 {
864     int i, n = 0;
865 
866     /* Count the number of valid races */
867     for (i = 0; races[i].noun; i++)
868         if (roles[rolenum].allow & races[i].allow & ROLE_RACEMASK)
869             n++;
870 
871     /* Pick a random race */
872     /* Use a factor of 100 in case of bad random number generators */
873     if (n)
874         n = rn2(n * 100) / 100;
875     for (i = 0; races[i].noun; i++)
876         if (roles[rolenum].allow & races[i].allow & ROLE_RACEMASK) {
877             if (n)
878                 n--;
879             else
880                 return i;
881         }
882 
883     /* This role has no permitted races? */
884     return rn2(SIZE(races) - 1);
885 }
886 
887 int
str2race(str)888 str2race(str)
889 const char *str;
890 {
891     int i, len;
892 
893     /* Is str valid? */
894     if (!str || !str[0])
895         return ROLE_NONE;
896 
897     /* Match as much of str as is provided */
898     len = strlen(str);
899     for (i = 0; races[i].noun; i++) {
900         /* Does it match the noun? */
901         if (!strncmpi(str, races[i].noun, len))
902             return i;
903         /* Or the filecode? */
904         if (!strcmpi(str, races[i].filecode))
905             return i;
906     }
907 
908     if ((len == 1 && (*str == '*' || *str == '@'))
909         || !strncmpi(str, randomstr, len))
910         return ROLE_RANDOM;
911 
912     /* Couldn't find anything appropriate */
913     return ROLE_NONE;
914 }
915 
916 boolean
validgend(rolenum,racenum,gendnum)917 validgend(rolenum, racenum, gendnum)
918 int rolenum, racenum, gendnum;
919 {
920     /* Assumes validrole and validrace */
921     return (boolean) (gendnum >= 0 && gendnum < ROLE_GENDERS
922                       && (roles[rolenum].allow & races[racenum].allow
923                           & genders[gendnum].allow & ROLE_GENDMASK));
924 }
925 
926 int
randgend(rolenum,racenum)927 randgend(rolenum, racenum)
928 int rolenum, racenum;
929 {
930     int i, n = 0;
931 
932     /* Count the number of valid genders */
933     for (i = 0; i < ROLE_GENDERS; i++)
934         if (roles[rolenum].allow & races[racenum].allow & genders[i].allow
935             & ROLE_GENDMASK)
936             n++;
937 
938     /* Pick a random gender */
939     if (n)
940         n = rn2(n);
941     for (i = 0; i < ROLE_GENDERS; i++)
942         if (roles[rolenum].allow & races[racenum].allow & genders[i].allow
943             & ROLE_GENDMASK) {
944             if (n)
945                 n--;
946             else
947                 return i;
948         }
949 
950     /* This role/race has no permitted genders? */
951     return rn2(ROLE_GENDERS);
952 }
953 
954 int
str2gend(str)955 str2gend(str)
956 const char *str;
957 {
958     int i, len;
959 
960     /* Is str valid? */
961     if (!str || !str[0])
962         return ROLE_NONE;
963 
964     /* Match as much of str as is provided */
965     len = strlen(str);
966     for (i = 0; i < ROLE_GENDERS; i++) {
967         /* Does it match the adjective? */
968         if (!strncmpi(str, genders[i].adj, len))
969             return i;
970         /* Or the filecode? */
971         if (!strcmpi(str, genders[i].filecode))
972             return i;
973     }
974     if ((len == 1 && (*str == '*' || *str == '@'))
975         || !strncmpi(str, randomstr, len))
976         return ROLE_RANDOM;
977 
978     /* Couldn't find anything appropriate */
979     return ROLE_NONE;
980 }
981 
982 boolean
validalign(rolenum,racenum,alignnum)983 validalign(rolenum, racenum, alignnum)
984 int rolenum, racenum, alignnum;
985 {
986     /* Assumes validrole and validrace */
987     return (boolean) (alignnum >= 0 && alignnum < ROLE_ALIGNS
988                       && (roles[rolenum].allow & races[racenum].allow
989                           & aligns[alignnum].allow & ROLE_ALIGNMASK));
990 }
991 
992 int
randalign(rolenum,racenum)993 randalign(rolenum, racenum)
994 int rolenum, racenum;
995 {
996     int i, n = 0;
997 
998     /* Count the number of valid alignments */
999     for (i = 0; i < ROLE_ALIGNS; i++)
1000         if (roles[rolenum].allow & races[racenum].allow & aligns[i].allow
1001             & ROLE_ALIGNMASK)
1002             n++;
1003 
1004     /* Pick a random alignment */
1005     if (n)
1006         n = rn2(n);
1007     for (i = 0; i < ROLE_ALIGNS; i++)
1008         if (roles[rolenum].allow & races[racenum].allow & aligns[i].allow
1009             & ROLE_ALIGNMASK) {
1010             if (n)
1011                 n--;
1012             else
1013                 return i;
1014         }
1015 
1016     /* This role/race has no permitted alignments? */
1017     return rn2(ROLE_ALIGNS);
1018 }
1019 
1020 int
str2align(str)1021 str2align(str)
1022 const char *str;
1023 {
1024     int i, len;
1025 
1026     /* Is str valid? */
1027     if (!str || !str[0])
1028         return ROLE_NONE;
1029 
1030     /* Match as much of str as is provided */
1031     len = strlen(str);
1032     for (i = 0; i < ROLE_ALIGNS; i++) {
1033         /* Does it match the adjective? */
1034         if (!strncmpi(str, aligns[i].adj, len))
1035             return i;
1036         /* Or the filecode? */
1037         if (!strcmpi(str, aligns[i].filecode))
1038             return i;
1039     }
1040     if ((len == 1 && (*str == '*' || *str == '@'))
1041         || !strncmpi(str, randomstr, len))
1042         return ROLE_RANDOM;
1043 
1044     /* Couldn't find anything appropriate */
1045     return ROLE_NONE;
1046 }
1047 
1048 /* is rolenum compatible with any racenum/gendnum/alignnum constraints? */
1049 boolean
ok_role(rolenum,racenum,gendnum,alignnum)1050 ok_role(rolenum, racenum, gendnum, alignnum)
1051 int rolenum, racenum, gendnum, alignnum;
1052 {
1053     int i;
1054     short allow;
1055 
1056     if (rolenum >= 0 && rolenum < SIZE(roles) - 1) {
1057         if (rfilter.roles[rolenum])
1058             return FALSE;
1059         allow = roles[rolenum].allow;
1060         if (racenum >= 0 && racenum < SIZE(races) - 1
1061             && !(allow & races[racenum].allow & ROLE_RACEMASK))
1062             return FALSE;
1063         if (gendnum >= 0 && gendnum < ROLE_GENDERS
1064             && !(allow & genders[gendnum].allow & ROLE_GENDMASK))
1065             return FALSE;
1066         if (alignnum >= 0 && alignnum < ROLE_ALIGNS
1067             && !(allow & aligns[alignnum].allow & ROLE_ALIGNMASK))
1068             return FALSE;
1069         return TRUE;
1070     } else {
1071         /* random; check whether any selection is possible */
1072         for (i = 0; i < SIZE(roles) - 1; i++) {
1073             if (rfilter.roles[i])
1074                 continue;
1075             allow = roles[i].allow;
1076             if (racenum >= 0 && racenum < SIZE(races) - 1
1077                 && !(allow & races[racenum].allow & ROLE_RACEMASK))
1078                 continue;
1079             if (gendnum >= 0 && gendnum < ROLE_GENDERS
1080                 && !(allow & genders[gendnum].allow & ROLE_GENDMASK))
1081                 continue;
1082             if (alignnum >= 0 && alignnum < ROLE_ALIGNS
1083                 && !(allow & aligns[alignnum].allow & ROLE_ALIGNMASK))
1084                 continue;
1085             return TRUE;
1086         }
1087         return FALSE;
1088     }
1089 }
1090 
1091 /* pick a random role subject to any racenum/gendnum/alignnum constraints */
1092 /* If pickhow == PICK_RIGID a role is returned only if there is  */
1093 /* a single possibility */
1094 int
pick_role(racenum,gendnum,alignnum,pickhow)1095 pick_role(racenum, gendnum, alignnum, pickhow)
1096 int racenum, gendnum, alignnum, pickhow;
1097 {
1098     int i;
1099     int roles_ok = 0, set[SIZE(roles)];
1100 
1101     for (i = 0; i < SIZE(roles) - 1; i++) {
1102         if (ok_role(i, racenum, gendnum, alignnum)
1103             && ok_race(i, (racenum >= 0) ? racenum : ROLE_RANDOM,
1104                        gendnum, alignnum)
1105             && ok_gend(i, racenum,
1106                        (gendnum >= 0) ? gendnum : ROLE_RANDOM, alignnum)
1107             && ok_race(i, racenum,
1108                        gendnum, (alignnum >= 0) ? alignnum : ROLE_RANDOM))
1109             set[roles_ok++] = i;
1110     }
1111     if (roles_ok == 0 || (roles_ok > 1 && pickhow == PICK_RIGID))
1112         return ROLE_NONE;
1113     return set[rn2(roles_ok)];
1114 }
1115 
1116 /* is racenum compatible with any rolenum/gendnum/alignnum constraints? */
1117 boolean
ok_race(rolenum,racenum,gendnum,alignnum)1118 ok_race(rolenum, racenum, gendnum, alignnum)
1119 int rolenum, racenum, gendnum, alignnum;
1120 {
1121     int i;
1122     short allow;
1123 
1124     if (racenum >= 0 && racenum < SIZE(races) - 1) {
1125         if (rfilter.mask & races[racenum].selfmask)
1126             return FALSE;
1127         allow = races[racenum].allow;
1128         if (rolenum >= 0 && rolenum < SIZE(roles) - 1
1129             && !(allow & roles[rolenum].allow & ROLE_RACEMASK))
1130             return FALSE;
1131         if (gendnum >= 0 && gendnum < ROLE_GENDERS
1132             && !(allow & genders[gendnum].allow & ROLE_GENDMASK))
1133             return FALSE;
1134         if (alignnum >= 0 && alignnum < ROLE_ALIGNS
1135             && !(allow & aligns[alignnum].allow & ROLE_ALIGNMASK))
1136             return FALSE;
1137         return TRUE;
1138     } else {
1139         /* random; check whether any selection is possible */
1140         for (i = 0; i < SIZE(races) - 1; i++) {
1141             if (rfilter.mask & races[i].selfmask)
1142                 continue;
1143             allow = races[i].allow;
1144             if (rolenum >= 0 && rolenum < SIZE(roles) - 1
1145                 && !(allow & roles[rolenum].allow & ROLE_RACEMASK))
1146                 continue;
1147             if (gendnum >= 0 && gendnum < ROLE_GENDERS
1148                 && !(allow & genders[gendnum].allow & ROLE_GENDMASK))
1149                 continue;
1150             if (alignnum >= 0 && alignnum < ROLE_ALIGNS
1151                 && !(allow & aligns[alignnum].allow & ROLE_ALIGNMASK))
1152                 continue;
1153             return TRUE;
1154         }
1155         return FALSE;
1156     }
1157 }
1158 
1159 /* Pick a random race subject to any rolenum/gendnum/alignnum constraints.
1160    If pickhow == PICK_RIGID a race is returned only if there is
1161    a single possibility. */
1162 int
pick_race(rolenum,gendnum,alignnum,pickhow)1163 pick_race(rolenum, gendnum, alignnum, pickhow)
1164 int rolenum, gendnum, alignnum, pickhow;
1165 {
1166     int i;
1167     int races_ok = 0;
1168 
1169     for (i = 0; i < SIZE(races) - 1; i++) {
1170         if (ok_race(rolenum, i, gendnum, alignnum))
1171             races_ok++;
1172     }
1173     if (races_ok == 0 || (races_ok > 1 && pickhow == PICK_RIGID))
1174         return ROLE_NONE;
1175     races_ok = rn2(races_ok);
1176     for (i = 0; i < SIZE(races) - 1; i++) {
1177         if (ok_race(rolenum, i, gendnum, alignnum)) {
1178             if (races_ok == 0)
1179                 return i;
1180             else
1181                 races_ok--;
1182         }
1183     }
1184     return ROLE_NONE;
1185 }
1186 
1187 /* is gendnum compatible with any rolenum/racenum/alignnum constraints? */
1188 /* gender and alignment are not comparable (and also not constrainable) */
1189 boolean
ok_gend(rolenum,racenum,gendnum,alignnum)1190 ok_gend(rolenum, racenum, gendnum, alignnum)
1191 int rolenum, racenum, gendnum;
1192 int alignnum UNUSED;
1193 {
1194     int i;
1195     short allow;
1196 
1197     if (gendnum >= 0 && gendnum < ROLE_GENDERS) {
1198         if (rfilter.mask & genders[gendnum].allow)
1199             return FALSE;
1200         allow = genders[gendnum].allow;
1201         if (rolenum >= 0 && rolenum < SIZE(roles) - 1
1202             && !(allow & roles[rolenum].allow & ROLE_GENDMASK))
1203             return FALSE;
1204         if (racenum >= 0 && racenum < SIZE(races) - 1
1205             && !(allow & races[racenum].allow & ROLE_GENDMASK))
1206             return FALSE;
1207         return TRUE;
1208     } else {
1209         /* random; check whether any selection is possible */
1210         for (i = 0; i < ROLE_GENDERS; i++) {
1211             if (rfilter.mask & genders[i].allow)
1212                 continue;
1213             allow = genders[i].allow;
1214             if (rolenum >= 0 && rolenum < SIZE(roles) - 1
1215                 && !(allow & roles[rolenum].allow & ROLE_GENDMASK))
1216                 continue;
1217             if (racenum >= 0 && racenum < SIZE(races) - 1
1218                 && !(allow & races[racenum].allow & ROLE_GENDMASK))
1219                 continue;
1220             return TRUE;
1221         }
1222         return FALSE;
1223     }
1224 }
1225 
1226 /* pick a random gender subject to any rolenum/racenum/alignnum constraints */
1227 /* gender and alignment are not comparable (and also not constrainable) */
1228 /* If pickhow == PICK_RIGID a gender is returned only if there is  */
1229 /* a single possibility */
1230 int
pick_gend(rolenum,racenum,alignnum,pickhow)1231 pick_gend(rolenum, racenum, alignnum, pickhow)
1232 int rolenum, racenum, alignnum, pickhow;
1233 {
1234     int i;
1235     int gends_ok = 0;
1236 
1237     for (i = 0; i < ROLE_GENDERS; i++) {
1238         if (ok_gend(rolenum, racenum, i, alignnum))
1239             gends_ok++;
1240     }
1241     if (gends_ok == 0 || (gends_ok > 1 && pickhow == PICK_RIGID))
1242         return ROLE_NONE;
1243     gends_ok = rn2(gends_ok);
1244     for (i = 0; i < ROLE_GENDERS; i++) {
1245         if (ok_gend(rolenum, racenum, i, alignnum)) {
1246             if (gends_ok == 0)
1247                 return i;
1248             else
1249                 gends_ok--;
1250         }
1251     }
1252     return ROLE_NONE;
1253 }
1254 
1255 /* is alignnum compatible with any rolenum/racenum/gendnum constraints? */
1256 /* alignment and gender are not comparable (and also not constrainable) */
1257 boolean
ok_align(rolenum,racenum,gendnum,alignnum)1258 ok_align(rolenum, racenum, gendnum, alignnum)
1259 int rolenum, racenum;
1260 int gendnum UNUSED;
1261 int alignnum;
1262 {
1263     int i;
1264     short allow;
1265 
1266     if (alignnum >= 0 && alignnum < ROLE_ALIGNS) {
1267         if (rfilter.mask & aligns[alignnum].allow)
1268             return FALSE;
1269         allow = aligns[alignnum].allow;
1270         if (rolenum >= 0 && rolenum < SIZE(roles) - 1
1271             && !(allow & roles[rolenum].allow & ROLE_ALIGNMASK))
1272             return FALSE;
1273         if (racenum >= 0 && racenum < SIZE(races) - 1
1274             && !(allow & races[racenum].allow & ROLE_ALIGNMASK))
1275             return FALSE;
1276         return TRUE;
1277     } else {
1278         /* random; check whether any selection is possible */
1279         for (i = 0; i < ROLE_ALIGNS; i++) {
1280             if (rfilter.mask & aligns[i].allow)
1281                 return FALSE;
1282             allow = aligns[i].allow;
1283             if (rolenum >= 0 && rolenum < SIZE(roles) - 1
1284                 && !(allow & roles[rolenum].allow & ROLE_ALIGNMASK))
1285                 continue;
1286             if (racenum >= 0 && racenum < SIZE(races) - 1
1287                 && !(allow & races[racenum].allow & ROLE_ALIGNMASK))
1288                 continue;
1289             return TRUE;
1290         }
1291         return FALSE;
1292     }
1293 }
1294 
1295 /* Pick a random alignment subject to any rolenum/racenum/gendnum constraints;
1296    alignment and gender are not comparable (and also not constrainable).
1297    If pickhow == PICK_RIGID an alignment is returned only if there is
1298    a single possibility. */
1299 int
pick_align(rolenum,racenum,gendnum,pickhow)1300 pick_align(rolenum, racenum, gendnum, pickhow)
1301 int rolenum, racenum, gendnum, pickhow;
1302 {
1303     int i;
1304     int aligns_ok = 0;
1305 
1306     for (i = 0; i < ROLE_ALIGNS; i++) {
1307         if (ok_align(rolenum, racenum, gendnum, i))
1308             aligns_ok++;
1309     }
1310     if (aligns_ok == 0 || (aligns_ok > 1 && pickhow == PICK_RIGID))
1311         return ROLE_NONE;
1312     aligns_ok = rn2(aligns_ok);
1313     for (i = 0; i < ROLE_ALIGNS; i++) {
1314         if (ok_align(rolenum, racenum, gendnum, i)) {
1315             if (aligns_ok == 0)
1316                 return i;
1317             else
1318                 aligns_ok--;
1319         }
1320     }
1321     return ROLE_NONE;
1322 }
1323 
1324 void
rigid_role_checks()1325 rigid_role_checks()
1326 {
1327     int tmp;
1328 
1329     /* Some roles are limited to a single race, alignment, or gender and
1330      * calling this routine prior to XXX_player_selection() will help
1331      * prevent an extraneous prompt that actually doesn't allow
1332      * you to choose anything further. Note the use of PICK_RIGID which
1333      * causes the pick_XX() routine to return a value only if there is one
1334      * single possible selection, otherwise it returns ROLE_NONE.
1335      *
1336      */
1337     if (flags.initrole == ROLE_RANDOM) {
1338         /* If the role was explicitly specified as ROLE_RANDOM
1339          * via -uXXXX-@ or OPTIONS=role:random then choose the role
1340          * in here to narrow down later choices.
1341          */
1342         flags.initrole = pick_role(flags.initrace, flags.initgend,
1343                                    flags.initalign, PICK_RANDOM);
1344         if (flags.initrole < 0)
1345             flags.initrole = randrole_filtered();
1346     }
1347     if (flags.initrace == ROLE_RANDOM
1348         && (tmp = pick_race(flags.initrole, flags.initgend,
1349                             flags.initalign, PICK_RANDOM)) != ROLE_NONE)
1350         flags.initrace = tmp;
1351     if (flags.initalign == ROLE_RANDOM
1352         && (tmp = pick_align(flags.initrole, flags.initrace,
1353                              flags.initgend, PICK_RANDOM)) != ROLE_NONE)
1354         flags.initalign = tmp;
1355     if (flags.initgend == ROLE_RANDOM
1356         && (tmp = pick_gend(flags.initrole, flags.initrace,
1357                             flags.initalign, PICK_RANDOM)) != ROLE_NONE)
1358         flags.initgend = tmp;
1359 
1360     if (flags.initrole != ROLE_NONE) {
1361         if (flags.initrace == ROLE_NONE)
1362             flags.initrace = pick_race(flags.initrole, flags.initgend,
1363                                        flags.initalign, PICK_RIGID);
1364         if (flags.initalign == ROLE_NONE)
1365             flags.initalign = pick_align(flags.initrole, flags.initrace,
1366                                          flags.initgend, PICK_RIGID);
1367         if (flags.initgend == ROLE_NONE)
1368             flags.initgend = pick_gend(flags.initrole, flags.initrace,
1369                                        flags.initalign, PICK_RIGID);
1370     }
1371 }
1372 
1373 boolean
setrolefilter(bufp)1374 setrolefilter(bufp)
1375 const char *bufp;
1376 {
1377     int i;
1378     boolean reslt = TRUE;
1379 
1380     if ((i = str2role(bufp)) != ROLE_NONE && i != ROLE_RANDOM)
1381         rfilter.roles[i] = TRUE;
1382     else if ((i = str2race(bufp)) != ROLE_NONE && i != ROLE_RANDOM)
1383         rfilter.mask |= races[i].selfmask;
1384     else if ((i = str2gend(bufp)) != ROLE_NONE && i != ROLE_RANDOM)
1385         rfilter.mask |= genders[i].allow;
1386     else if ((i = str2align(bufp)) != ROLE_NONE && i != ROLE_RANDOM)
1387         rfilter.mask |= aligns[i].allow;
1388     else
1389         reslt = FALSE;
1390     return reslt;
1391 }
1392 
1393 boolean
gotrolefilter()1394 gotrolefilter()
1395 {
1396     int i;
1397 
1398     if (rfilter.mask)
1399         return TRUE;
1400     for (i = 0; i < SIZE(roles); ++i)
1401         if (rfilter.roles[i])
1402             return TRUE;
1403     return FALSE;
1404 }
1405 
1406 void
clearrolefilter()1407 clearrolefilter()
1408 {
1409     int i;
1410 
1411     for (i = 0; i < SIZE(roles); ++i)
1412         rfilter.roles[i] = FALSE;
1413     rfilter.mask = 0;
1414 }
1415 
1416 #define BP_ALIGN 0
1417 #define BP_GEND 1
1418 #define BP_RACE 2
1419 #define BP_ROLE 3
1420 #define NUM_BP 4
1421 
1422 STATIC_VAR char pa[NUM_BP], post_attribs;
1423 
1424 STATIC_OVL char *
promptsep(buf,num_post_attribs)1425 promptsep(buf, num_post_attribs)
1426 char *buf;
1427 int num_post_attribs;
1428 {
1429     const char *conjuct = "and ";
1430 
1431     if (num_post_attribs > 1 && post_attribs < num_post_attribs
1432         && post_attribs > 1)
1433         Strcat(buf, ",");
1434     Strcat(buf, " ");
1435     --post_attribs;
1436     if (!post_attribs && num_post_attribs > 1)
1437         Strcat(buf, conjuct);
1438     return buf;
1439 }
1440 
1441 STATIC_OVL int
role_gendercount(rolenum)1442 role_gendercount(rolenum)
1443 int rolenum;
1444 {
1445     int gendcount = 0;
1446 
1447     if (validrole(rolenum)) {
1448         if (roles[rolenum].allow & ROLE_MALE)
1449             ++gendcount;
1450         if (roles[rolenum].allow & ROLE_FEMALE)
1451             ++gendcount;
1452         if (roles[rolenum].allow & ROLE_NEUTER)
1453             ++gendcount;
1454     }
1455     return gendcount;
1456 }
1457 
1458 STATIC_OVL int
race_alignmentcount(racenum)1459 race_alignmentcount(racenum)
1460 int racenum;
1461 {
1462     int aligncount = 0;
1463 
1464     if (racenum != ROLE_NONE && racenum != ROLE_RANDOM) {
1465         if (races[racenum].allow & ROLE_CHAOTIC)
1466             ++aligncount;
1467         if (races[racenum].allow & ROLE_LAWFUL)
1468             ++aligncount;
1469         if (races[racenum].allow & ROLE_NEUTRAL)
1470             ++aligncount;
1471     }
1472     return aligncount;
1473 }
1474 
1475 char *
root_plselection_prompt(suppliedbuf,buflen,rolenum,racenum,gendnum,alignnum)1476 root_plselection_prompt(suppliedbuf, buflen, rolenum, racenum, gendnum,
1477                         alignnum)
1478 char *suppliedbuf;
1479 int buflen, rolenum, racenum, gendnum, alignnum;
1480 {
1481     int k, gendercount = 0, aligncount = 0;
1482     char buf[BUFSZ];
1483     static char err_ret[] = " character's";
1484     boolean donefirst = FALSE;
1485 
1486     if (!suppliedbuf || buflen < 1)
1487         return err_ret;
1488 
1489     /* initialize these static variables each time this is called */
1490     post_attribs = 0;
1491     for (k = 0; k < NUM_BP; ++k)
1492         pa[k] = 0;
1493     buf[0] = '\0';
1494     *suppliedbuf = '\0';
1495 
1496     /* How many alignments are allowed for the desired race? */
1497     if (racenum != ROLE_NONE && racenum != ROLE_RANDOM)
1498         aligncount = race_alignmentcount(racenum);
1499 
1500     if (alignnum != ROLE_NONE && alignnum != ROLE_RANDOM
1501         && ok_align(rolenum, racenum, gendnum, alignnum)) {
1502         /* if race specified, and multiple choice of alignments for it */
1503         if ((racenum >= 0) && (aligncount > 1)) {
1504             if (donefirst)
1505                 Strcat(buf, " ");
1506             Strcat(buf, aligns[alignnum].adj);
1507             donefirst = TRUE;
1508         } else {
1509             if (donefirst)
1510                 Strcat(buf, " ");
1511             Strcat(buf, aligns[alignnum].adj);
1512             donefirst = TRUE;
1513         }
1514     } else {
1515         /* in case we got here by failing the ok_align() test */
1516         if (alignnum != ROLE_RANDOM)
1517             alignnum = ROLE_NONE;
1518         /* if alignment not specified, but race is specified
1519            and only one choice of alignment for that race then
1520            don't include it in the later list */
1521         if ((((racenum != ROLE_NONE && racenum != ROLE_RANDOM)
1522               && ok_race(rolenum, racenum, gendnum, alignnum))
1523              && (aligncount > 1))
1524             || (racenum == ROLE_NONE || racenum == ROLE_RANDOM)) {
1525             pa[BP_ALIGN] = 1;
1526             post_attribs++;
1527         }
1528     }
1529     /* <your lawful> */
1530 
1531     /* How many genders are allowed for the desired role? */
1532     if (validrole(rolenum))
1533         gendercount = role_gendercount(rolenum);
1534 
1535     if (gendnum != ROLE_NONE && gendnum != ROLE_RANDOM) {
1536         if (validrole(rolenum)) {
1537             /* if role specified, and multiple choice of genders for it,
1538                and name of role itself does not distinguish gender */
1539             if ((rolenum != ROLE_NONE) && (gendercount > 1)
1540                 && !roles[rolenum].name.f) {
1541                 if (donefirst)
1542                     Strcat(buf, " ");
1543                 Strcat(buf, genders[gendnum].adj);
1544                 donefirst = TRUE;
1545             }
1546         } else {
1547             if (donefirst)
1548                 Strcat(buf, " ");
1549             Strcat(buf, genders[gendnum].adj);
1550             donefirst = TRUE;
1551         }
1552     } else {
1553         /* if gender not specified, but role is specified
1554                 and only one choice of gender then
1555                 don't include it in the later list */
1556         if ((validrole(rolenum) && (gendercount > 1))
1557             || !validrole(rolenum)) {
1558             pa[BP_GEND] = 1;
1559             post_attribs++;
1560         }
1561     }
1562     /* <your lawful female> */
1563 
1564     if (racenum != ROLE_NONE && racenum != ROLE_RANDOM) {
1565         if (validrole(rolenum)
1566             && ok_race(rolenum, racenum, gendnum, alignnum)) {
1567             if (donefirst)
1568                 Strcat(buf, " ");
1569             Strcat(buf, (rolenum == ROLE_NONE) ? races[racenum].noun
1570                                                : races[racenum].adj);
1571             donefirst = TRUE;
1572         } else if (!validrole(rolenum)) {
1573             if (donefirst)
1574                 Strcat(buf, " ");
1575             Strcat(buf, races[racenum].noun);
1576             donefirst = TRUE;
1577         } else {
1578             pa[BP_RACE] = 1;
1579             post_attribs++;
1580         }
1581     } else {
1582         pa[BP_RACE] = 1;
1583         post_attribs++;
1584     }
1585     /* <your lawful female gnomish> || <your lawful female gnome> */
1586 
1587     if (validrole(rolenum)) {
1588         if (donefirst)
1589             Strcat(buf, " ");
1590         if (gendnum != ROLE_NONE) {
1591             if (gendnum == 1 && roles[rolenum].name.f)
1592                 Strcat(buf, roles[rolenum].name.f);
1593             else
1594                 Strcat(buf, roles[rolenum].name.m);
1595         } else {
1596             if (roles[rolenum].name.f) {
1597                 Strcat(buf, roles[rolenum].name.m);
1598                 Strcat(buf, "/");
1599                 Strcat(buf, roles[rolenum].name.f);
1600             } else
1601                 Strcat(buf, roles[rolenum].name.m);
1602         }
1603         donefirst = TRUE;
1604     } else if (rolenum == ROLE_NONE) {
1605         pa[BP_ROLE] = 1;
1606         post_attribs++;
1607     }
1608 
1609     if ((racenum == ROLE_NONE || racenum == ROLE_RANDOM)
1610         && !validrole(rolenum)) {
1611         if (donefirst)
1612             Strcat(buf, " ");
1613         Strcat(buf, "character");
1614         donefirst = TRUE;
1615     }
1616     /* <your lawful female gnomish cavewoman> || <your lawful female gnome>
1617      *    || <your lawful female character>
1618      */
1619     if (buflen > (int) (strlen(buf) + 1)) {
1620         Strcpy(suppliedbuf, buf);
1621         return suppliedbuf;
1622     } else
1623         return err_ret;
1624 }
1625 
1626 char *
build_plselection_prompt(buf,buflen,rolenum,racenum,gendnum,alignnum)1627 build_plselection_prompt(buf, buflen, rolenum, racenum, gendnum, alignnum)
1628 char *buf;
1629 int buflen, rolenum, racenum, gendnum, alignnum;
1630 {
1631     const char *defprompt = "Shall I pick a character for you? [ynaq] ";
1632     int num_post_attribs = 0;
1633     char tmpbuf[BUFSZ], *p;
1634 
1635     if (buflen < QBUFSZ)
1636         return (char *) defprompt;
1637 
1638     Strcpy(tmpbuf, "Shall I pick ");
1639     if (racenum != ROLE_NONE || validrole(rolenum))
1640         Strcat(tmpbuf, "your ");
1641     else
1642         Strcat(tmpbuf, "a ");
1643     /* <your> */
1644 
1645     (void) root_plselection_prompt(eos(tmpbuf), buflen - strlen(tmpbuf),
1646                                    rolenum, racenum, gendnum, alignnum);
1647     /* "Shall I pick a character's role, race, gender, and alignment for you?"
1648        plus " [ynaq] (y)" is a little too long for a conventional 80 columns;
1649        also, "pick a character's <anything>" sounds a bit stilted */
1650     strsubst(tmpbuf, "pick a character", "pick character");
1651     Sprintf(buf, "%s", s_suffix(tmpbuf));
1652     /* don't bother splitting caveman/cavewoman or priest/priestess
1653        in order to apply possessive suffix to both halves, but do
1654        change "priest/priestess'" to "priest/priestess's" */
1655     if ((p = strstri(buf, "priest/priestess'")) != 0
1656         && p[sizeof "priest/priestess'" - sizeof ""] == '\0')
1657         strkitten(buf, 's');
1658 
1659     /* buf should now be:
1660      *    <your lawful female gnomish cavewoman's>
1661      * || <your lawful female gnome's>
1662      * || <your lawful female character's>
1663      *
1664      * Now append the post attributes to it
1665      */
1666     num_post_attribs = post_attribs;
1667     if (!num_post_attribs) {
1668         /* some constraints might have been mutually exclusive, in which case
1669            some prompting that would have been omitted is needed after all */
1670         if (flags.initrole == ROLE_NONE && !pa[BP_ROLE])
1671             pa[BP_ROLE] = ++post_attribs;
1672         if (flags.initrace == ROLE_NONE && !pa[BP_RACE])
1673             pa[BP_RACE] = ++post_attribs;
1674         if (flags.initalign == ROLE_NONE && !pa[BP_ALIGN])
1675             pa[BP_ALIGN] = ++post_attribs;
1676         if (flags.initgend == ROLE_NONE && !pa[BP_GEND])
1677             pa[BP_GEND] = ++post_attribs;
1678         num_post_attribs = post_attribs;
1679     }
1680     if (num_post_attribs) {
1681         if (pa[BP_RACE]) {
1682             (void) promptsep(eos(buf), num_post_attribs);
1683             Strcat(buf, "race");
1684         }
1685         if (pa[BP_ROLE]) {
1686             (void) promptsep(eos(buf), num_post_attribs);
1687             Strcat(buf, "role");
1688         }
1689         if (pa[BP_GEND]) {
1690             (void) promptsep(eos(buf), num_post_attribs);
1691             Strcat(buf, "gender");
1692         }
1693         if (pa[BP_ALIGN]) {
1694             (void) promptsep(eos(buf), num_post_attribs);
1695             Strcat(buf, "alignment");
1696         }
1697     }
1698     Strcat(buf, " for you? [ynaq] ");
1699     return buf;
1700 }
1701 
1702 #undef BP_ALIGN
1703 #undef BP_GEND
1704 #undef BP_RACE
1705 #undef BP_ROLE
1706 #undef NUM_BP
1707 
1708 void
plnamesuffix()1709 plnamesuffix()
1710 {
1711     char *sptr, *eptr;
1712     int i;
1713 
1714     /* some generic user names will be ignored in favor of prompting */
1715     if (sysopt.genericusers) {
1716         if (*sysopt.genericusers == '*') {
1717             *plname = '\0';
1718         } else {
1719             i = (int) strlen(plname);
1720             if ((sptr = strstri(sysopt.genericusers, plname)) != 0
1721                 && (sptr == sysopt.genericusers || sptr[-1] == ' ')
1722                 && (sptr[i] == ' ' || sptr[i] == '\0'))
1723                 *plname = '\0'; /* call askname() */
1724         }
1725     }
1726 
1727     do {
1728         if (!*plname)
1729             askname(); /* fill plname[] if necessary, or set defer_plname */
1730 
1731         /* Look for tokens delimited by '-' */
1732         if ((eptr = index(plname, '-')) != (char *) 0)
1733             *eptr++ = '\0';
1734         while (eptr) {
1735             /* Isolate the next token */
1736             sptr = eptr;
1737             if ((eptr = index(sptr, '-')) != (char *) 0)
1738                 *eptr++ = '\0';
1739 
1740             /* Try to match it to something */
1741             if ((i = str2role(sptr)) != ROLE_NONE)
1742                 flags.initrole = i;
1743             else if ((i = str2race(sptr)) != ROLE_NONE)
1744                 flags.initrace = i;
1745             else if ((i = str2gend(sptr)) != ROLE_NONE)
1746                 flags.initgend = i;
1747             else if ((i = str2align(sptr)) != ROLE_NONE)
1748                 flags.initalign = i;
1749         }
1750     } while (!*plname && !iflags.defer_plname);
1751 
1752     /* commas in the plname confuse the record file, convert to spaces */
1753     for (sptr = plname; *sptr; sptr++) {
1754         if (*sptr == ',')
1755             *sptr = ' ';
1756     }
1757 }
1758 
1759 /* show current settings for name, role, race, gender, and alignment
1760    in the specified window */
1761 void
role_selection_prolog(which,where)1762 role_selection_prolog(which, where)
1763 int which;
1764 winid where;
1765 {
1766     static const char NEARDATA choosing[] = " choosing now",
1767                                not_yet[] = " not yet specified",
1768                                rand_choice[] = " random";
1769     char buf[BUFSZ];
1770     int r, c, g, a, allowmask;
1771 
1772     r = flags.initrole;
1773     c = flags.initrace;
1774     g = flags.initgend;
1775     a = flags.initalign;
1776     if (r >= 0) {
1777         allowmask = roles[r].allow;
1778         if ((allowmask & ROLE_RACEMASK) == MH_HUMAN)
1779             c = 0; /* races[human] */
1780         else if (c >= 0 && !(allowmask & ROLE_RACEMASK & races[c].allow))
1781             c = ROLE_RANDOM;
1782         if ((allowmask & ROLE_GENDMASK) == ROLE_MALE)
1783             g = 0; /* role forces male (hypothetical) */
1784         else if ((allowmask & ROLE_GENDMASK) == ROLE_FEMALE)
1785             g = 1; /* role forces female (valkyrie) */
1786         if ((allowmask & ROLE_ALIGNMASK) == AM_LAWFUL)
1787             a = 0; /* aligns[lawful] */
1788         else if ((allowmask & ROLE_ALIGNMASK) == AM_NEUTRAL)
1789             a = 1; /* aligns[neutral] */
1790         else if ((allowmask & ROLE_ALIGNMASK) == AM_CHAOTIC)
1791             a = 2; /* alings[chaotic] */
1792     }
1793     if (c >= 0) {
1794         allowmask = races[c].allow;
1795         if ((allowmask & ROLE_ALIGNMASK) == AM_LAWFUL)
1796             a = 0; /* aligns[lawful] */
1797         else if ((allowmask & ROLE_ALIGNMASK) == AM_NEUTRAL)
1798             a = 1; /* aligns[neutral] */
1799         else if ((allowmask & ROLE_ALIGNMASK) == AM_CHAOTIC)
1800             a = 2; /* alings[chaotic] */
1801         /* [c never forces gender] */
1802     }
1803     /* [g and a don't constrain anything sufficiently
1804        to narrow something done to a single choice] */
1805 
1806     Sprintf(buf, "%12s ", "name:");
1807     Strcat(buf, (which == RS_NAME) ? choosing : !*plname ? not_yet : plname);
1808     putstr(where, 0, buf);
1809     Sprintf(buf, "%12s ", "role:");
1810     Strcat(buf, (which == RS_ROLE) ? choosing : (r == ROLE_NONE)
1811                                                     ? not_yet
1812                                                     : (r == ROLE_RANDOM)
1813                                                           ? rand_choice
1814                                                           : roles[r].name.m);
1815     if (r >= 0 && roles[r].name.f) {
1816         /* distinct female name [caveman/cavewoman, priest/priestess] */
1817         if (g == 1)
1818             /* female specified; replace male role name with female one */
1819             Sprintf(index(buf, ':'), ": %s", roles[r].name.f);
1820         else if (g < 0)
1821             /* gender unspecified; append slash and female role name */
1822             Sprintf(eos(buf), "/%s", roles[r].name.f);
1823     }
1824     putstr(where, 0, buf);
1825     Sprintf(buf, "%12s ", "race:");
1826     Strcat(buf, (which == RS_RACE) ? choosing : (c == ROLE_NONE)
1827                                                     ? not_yet
1828                                                     : (c == ROLE_RANDOM)
1829                                                           ? rand_choice
1830                                                           : races[c].noun);
1831     putstr(where, 0, buf);
1832     Sprintf(buf, "%12s ", "gender:");
1833     Strcat(buf, (which == RS_GENDER) ? choosing : (g == ROLE_NONE)
1834                                                       ? not_yet
1835                                                       : (g == ROLE_RANDOM)
1836                                                             ? rand_choice
1837                                                             : genders[g].adj);
1838     putstr(where, 0, buf);
1839     Sprintf(buf, "%12s ", "alignment:");
1840     Strcat(buf, (which == RS_ALGNMNT) ? choosing : (a == ROLE_NONE)
1841                                                        ? not_yet
1842                                                        : (a == ROLE_RANDOM)
1843                                                              ? rand_choice
1844                                                              : aligns[a].adj);
1845     putstr(where, 0, buf);
1846 }
1847 
1848 /* add a "pick alignment first"-type entry to the specified menu */
1849 void
role_menu_extra(which,where,preselect)1850 role_menu_extra(which, where, preselect)
1851 int which;
1852 winid where;
1853 boolean preselect;
1854 {
1855     static NEARDATA const char RS_menu_let[] = {
1856         '=',  /* name */
1857         '?',  /* role */
1858         '/',  /* race */
1859         '\"', /* gender */
1860         '[',  /* alignment */
1861     };
1862     anything any;
1863     char buf[BUFSZ];
1864     const char *what = 0, *constrainer = 0, *forcedvalue = 0;
1865     int f = 0, r, c, g, a, i, allowmask;
1866 
1867     r = flags.initrole;
1868     c = flags.initrace;
1869     switch (which) {
1870     case RS_NAME:
1871         what = "name";
1872         break;
1873     case RS_ROLE:
1874         what = "role";
1875         f = r;
1876         for (i = 0; i < SIZE(roles); ++i)
1877             if (i != f && !rfilter.roles[i])
1878                 break;
1879         if (i == SIZE(roles)) {
1880             constrainer = "filter";
1881             forcedvalue = "role";
1882         }
1883         break;
1884     case RS_RACE:
1885         what = "race";
1886         f = flags.initrace;
1887         c = ROLE_NONE; /* override player's setting */
1888         if (r >= 0) {
1889             allowmask = roles[r].allow & ROLE_RACEMASK;
1890             if (allowmask == MH_HUMAN)
1891                 c = 0; /* races[human] */
1892             if (c >= 0) {
1893                 constrainer = "role";
1894                 forcedvalue = races[c].noun;
1895             } else if (f >= 0
1896                        && (allowmask & ~rfilter.mask) == races[f].selfmask) {
1897                 /* if there is only one race choice available due to user
1898                    options disallowing others, race menu entry is disabled */
1899                 constrainer = "filter";
1900                 forcedvalue = "race";
1901             }
1902         }
1903         break;
1904     case RS_GENDER:
1905         what = "gender";
1906         f = flags.initgend;
1907         g = ROLE_NONE;
1908         if (r >= 0) {
1909             allowmask = roles[r].allow & ROLE_GENDMASK;
1910             if (allowmask == ROLE_MALE)
1911                 g = 0; /* genders[male] */
1912             else if (allowmask == ROLE_FEMALE)
1913                 g = 1; /* genders[female] */
1914             if (g >= 0) {
1915                 constrainer = "role";
1916                 forcedvalue = genders[g].adj;
1917             } else if (f >= 0
1918                        && (allowmask & ~rfilter.mask) == genders[f].allow) {
1919                 /* if there is only one gender choice available due to user
1920                    options disallowing other, gender menu entry is disabled */
1921                 constrainer = "filter";
1922                 forcedvalue = "gender";
1923             }
1924         }
1925         break;
1926     case RS_ALGNMNT:
1927         what = "alignment";
1928         f = flags.initalign;
1929         a = ROLE_NONE;
1930         if (r >= 0) {
1931             allowmask = roles[r].allow & ROLE_ALIGNMASK;
1932             if (allowmask == AM_LAWFUL)
1933                 a = 0; /* aligns[lawful] */
1934             else if (allowmask == AM_NEUTRAL)
1935                 a = 1; /* aligns[neutral] */
1936             else if (allowmask == AM_CHAOTIC)
1937                 a = 2; /* aligns[chaotic] */
1938             if (a >= 0)
1939                 constrainer = "role";
1940         }
1941         if (c >= 0 && !constrainer) {
1942             allowmask = races[c].allow & ROLE_ALIGNMASK;
1943             if (allowmask == AM_LAWFUL)
1944                 a = 0; /* aligns[lawful] */
1945             else if (allowmask == AM_NEUTRAL)
1946                 a = 1; /* aligns[neutral] */
1947             else if (allowmask == AM_CHAOTIC)
1948                 a = 2; /* aligns[chaotic] */
1949             if (a >= 0)
1950                 constrainer = "race";
1951         }
1952         if (f >= 0 && !constrainer
1953             && (ROLE_ALIGNMASK & ~rfilter.mask) == aligns[f].allow) {
1954             /* if there is only one alignment choice available due to user
1955                options disallowing others, algn menu entry is disabled */
1956             constrainer = "filter";
1957             forcedvalue = "alignment";
1958         }
1959         if (a >= 0)
1960             forcedvalue = aligns[a].adj;
1961         break;
1962     }
1963 
1964     any = zeroany; /* zero out all bits */
1965     if (constrainer) {
1966         any.a_int = 0;
1967         /* use four spaces of padding to fake a grayed out menu choice */
1968         Sprintf(buf, "%4s%s forces %s", "", constrainer, forcedvalue);
1969         add_menu(where, NO_GLYPH, &any, 0, 0, ATR_NONE, buf,
1970                  MENU_UNSELECTED);
1971     } else if (what) {
1972         any.a_int = RS_menu_arg(which);
1973         Sprintf(buf, "Pick%s %s first", (f >= 0) ? " another" : "", what);
1974         add_menu(where, NO_GLYPH, &any, RS_menu_let[which], 0, ATR_NONE, buf,
1975                  MENU_UNSELECTED);
1976     } else if (which == RS_filter) {
1977         any.a_int = RS_menu_arg(RS_filter);
1978         add_menu(where, NO_GLYPH, &any, '~', 0, ATR_NONE,
1979                  "Reset role/race/&c filtering", MENU_UNSELECTED);
1980     } else if (which == ROLE_RANDOM) {
1981         any.a_int = ROLE_RANDOM;
1982         add_menu(where, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random",
1983                  preselect ? MENU_SELECTED : MENU_UNSELECTED);
1984     } else if (which == ROLE_NONE) {
1985         any.a_int = ROLE_NONE;
1986         add_menu(where, NO_GLYPH, &any, 'q', 0, ATR_NONE, "Quit",
1987                  preselect ? MENU_SELECTED : MENU_UNSELECTED);
1988     } else {
1989         impossible("role_menu_extra: bad arg (%d)", which);
1990     }
1991 }
1992 
1993 /*
1994  *      Special setup modifications here:
1995  *
1996  *      Unfortunately, this is going to have to be done
1997  *      on each newgame or restore, because you lose the permonst mods
1998  *      across a save/restore.  :-)
1999  *
2000  *      1 - The Rogue Leader is the Tourist Nemesis.
2001  *      2 - Priests start with a random alignment - convert the leader and
2002  *          guardians here.
2003  *      3 - Priests also get their of deities from a randomly chosen role.
2004  *      4 - [obsolete] Elves can have one of two different leaders,
2005  *          but can't work it out here because it requires hacking the
2006  *          level file data (see sp_lev.c).
2007  *
2008  * This code also replaces quest_init().
2009  */
2010 void
role_init()2011 role_init()
2012 {
2013     int alignmnt;
2014     struct permonst *pm;
2015 
2016     /* Strip the role letter out of the player name.
2017      * This is included for backwards compatibility.
2018      */
2019     plnamesuffix();
2020 
2021     /* Check for a valid role.  Try flags.initrole first. */
2022     if (!validrole(flags.initrole)) {
2023         /* Try the player letter second */
2024         if ((flags.initrole = str2role(pl_character)) < 0)
2025             /* None specified; pick a random role */
2026             flags.initrole = randrole_filtered();
2027     }
2028 
2029     /* We now have a valid role index.  Copy the role name back. */
2030     /* This should become OBSOLETE */
2031     Strcpy(pl_character, roles[flags.initrole].name.m);
2032     pl_character[PL_CSIZ - 1] = '\0';
2033 
2034     /* Check for a valid race */
2035     if (!validrace(flags.initrole, flags.initrace))
2036         flags.initrace = randrace(flags.initrole);
2037 
2038     /* Check for a valid gender.  If new game, check both initgend
2039      * and female.  On restore, assume flags.female is correct. */
2040     if (flags.pantheon == -1) { /* new game */
2041         if (!validgend(flags.initrole, flags.initrace, flags.female))
2042             flags.female = !flags.female;
2043     }
2044     if (!validgend(flags.initrole, flags.initrace, flags.initgend))
2045         /* Note that there is no way to check for an unspecified gender. */
2046         flags.initgend = flags.female;
2047 
2048     /* Check for a valid alignment */
2049     if (!validalign(flags.initrole, flags.initrace, flags.initalign))
2050         /* Pick a random alignment */
2051         flags.initalign = randalign(flags.initrole, flags.initrace);
2052     alignmnt = aligns[flags.initalign].value;
2053 
2054     /* Initialize urole and urace */
2055     urole = roles[flags.initrole];
2056     urace = races[flags.initrace];
2057 
2058     /* Fix up the quest leader */
2059     if (urole.ldrnum != NON_PM) {
2060         pm = &mons[urole.ldrnum];
2061         pm->msound = MS_LEADER;
2062         pm->mflags2 |= (M2_PEACEFUL);
2063         pm->mflags3 |= M3_CLOSE;
2064         pm->maligntyp = alignmnt * 3;
2065         /* if gender is random, we choose it now instead of waiting
2066            until the leader monster is created */
2067         quest_status.ldrgend =
2068             is_neuter(pm) ? 2 : is_female(pm) ? 1 : is_male(pm)
2069                                                         ? 0
2070                                                         : (rn2(100) < 50);
2071     }
2072 
2073     /* Fix up the quest guardians */
2074     if (urole.guardnum != NON_PM) {
2075         pm = &mons[urole.guardnum];
2076         pm->mflags2 |= (M2_PEACEFUL);
2077         pm->maligntyp = alignmnt * 3;
2078     }
2079 
2080     /* Fix up the quest nemesis */
2081     if (urole.neminum != NON_PM) {
2082         pm = &mons[urole.neminum];
2083         pm->msound = MS_NEMESIS;
2084         pm->mflags2 &= ~(M2_PEACEFUL);
2085         pm->mflags2 |= (M2_NASTY | M2_STALK | M2_HOSTILE);
2086         pm->mflags3 &= ~(M3_CLOSE);
2087         pm->mflags3 |= M3_WANTSARTI | M3_WAITFORU;
2088         /* if gender is random, we choose it now instead of waiting
2089            until the nemesis monster is created */
2090         quest_status.nemgend = is_neuter(pm) ? 2 : is_female(pm) ? 1
2091                                    : is_male(pm) ? 0 : (rn2(100) < 50);
2092     }
2093 
2094     /* Fix up the god names */
2095     if (flags.pantheon == -1) {             /* new game */
2096         flags.pantheon = flags.initrole;    /* use own gods */
2097         while (!roles[flags.pantheon].lgod) /* unless they're missing */
2098             flags.pantheon = randrole(FALSE);
2099     }
2100     if (!urole.lgod) {
2101         urole.lgod = roles[flags.pantheon].lgod;
2102         urole.ngod = roles[flags.pantheon].ngod;
2103         urole.cgod = roles[flags.pantheon].cgod;
2104     }
2105     /* 0 or 1; no gods are neuter, nor is gender randomized */
2106     quest_status.godgend = !strcmpi(align_gtitle(alignmnt), "goddess");
2107 
2108 #if 0
2109 /*
2110  * Disable this fixup so that mons[] can be const.  The only
2111  * place where it actually matters for the hero is in set_uasmon()
2112  * and that can use mons[race] rather than mons[role] for this
2113  * particular property.  Despite the comment, it is checked--where
2114  * needed--via instrinsic 'Infravision' which set_uasmon() manages.
2115  */
2116     /* Fix up infravision */
2117     if (mons[urace.malenum].mflags3 & M3_INFRAVISION) {
2118         /* although an infravision intrinsic is possible, infravision
2119          * is purely a property of the physical race.  This means that we
2120          * must put the infravision flag in the player's current race
2121          * (either that or have separate permonst entries for
2122          * elven/non-elven members of each class).  The side effect is that
2123          * all NPCs of that class will have (probably bogus) infravision,
2124          * but since infravision has no effect for NPCs anyway we can
2125          * ignore this.
2126          */
2127         mons[urole.malenum].mflags3 |= M3_INFRAVISION;
2128         if (urole.femalenum != NON_PM)
2129             mons[urole.femalenum].mflags3 |= M3_INFRAVISION;
2130     }
2131 #endif /*0*/
2132 
2133     /* Artifacts are fixed in hack_artifacts() */
2134 
2135     /* Success! */
2136     return;
2137 }
2138 
2139 const char *
Hello(mtmp)2140 Hello(mtmp)
2141 struct monst *mtmp;
2142 {
2143     switch (Role_switch) {
2144     case PM_KNIGHT:
2145         return "Salutations"; /* Olde English */
2146     case PM_SAMURAI:
2147         return (mtmp && mtmp->data == &mons[PM_SHOPKEEPER])
2148                     ? "Irasshaimase"
2149                     : "Konnichi wa"; /* Japanese */
2150     case PM_TOURIST:
2151         return "Aloha"; /* Hawaiian */
2152     case PM_VALKYRIE:
2153         return
2154 #ifdef MAIL
2155                (mtmp && mtmp->data == &mons[PM_MAIL_DAEMON]) ? "Hallo" :
2156 #endif
2157                "Velkommen"; /* Norse */
2158     default:
2159         return "Hello";
2160     }
2161 }
2162 
2163 const char *
Goodbye()2164 Goodbye()
2165 {
2166     switch (Role_switch) {
2167     case PM_KNIGHT:
2168         return "Fare thee well"; /* Olde English */
2169     case PM_SAMURAI:
2170         return "Sayonara"; /* Japanese */
2171     case PM_TOURIST:
2172         return "Aloha"; /* Hawaiian */
2173     case PM_VALKYRIE:
2174         return "Farvel"; /* Norse */
2175     default:
2176         return "Goodbye";
2177     }
2178 }
2179 
2180 /* role.c */
2181