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