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