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