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