/* SCCS Id: @(#)role.c 3.3 2000/05/21 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985-1999. */ /* NetHack may be freely redistributed. See license for details. */ #include "hack.h" /*** Table of all roles ***/ /* According to AD&D, HD for some classes (ex. Wizard) should be smaller * (4-sided for wizards). But this is not AD&D, and using the AD&D * rule here produces an unplayable character. Thus I have used a minimum * of an 10-sided hit die for everything. Another AD&D change: wizards get * a minimum strength of 4 since without one you can't teleport or cast * spells. --KAA * * As the wizard has been updated (wizard patch 5 jun '96) their HD can be * brought closer into line with AD&D. This forces wizards to use magic more * and distance themselves from their attackers. --LSZ * * With the introduction of races, some hit points and energy * has been reallocated for each race. The values assigned * to the roles has been reduced by the amount allocated to * humans. --KMH * * God names use a leading underscore to flag goddesses. */ const struct Role roles[] = { { {"Archeologist", 0}, { {"Digger", 0}, {"Field Worker",0}, {"Investigator",0}, {"Exhumer", 0}, {"Excavator", 0}, {"Spelunker", 0}, {"Speleologist",0}, {"Collector", 0}, {"Curator", 0} }, "Quetzalcoatl", "Camaxtli", "Huhetotl", /* Central American */ "Arc", "the College of Archeology", "the Tomb of the Toltec Kings", PM_ARCHEOLOGIST, NON_PM, NON_PM, PM_LORD_CARNARVON, PM_STUDENT, PM_MINION_OF_HUHETOTL, NON_PM, PM_HUMAN_MUMMY, S_SNAKE, S_MUMMY, ART_ORB_OF_DETECTION, MH_HUMAN|MH_DWARF|MH_GNOME | ROLE_MALE|ROLE_FEMALE | ROLE_LAWFUL|ROLE_NEUTRAL, /* Str Int Wis Dex Con Cha */ { 7, 10, 10, 7, 7, 7 }, { 20, 20, 20, 10, 20, 10 }, /* Init Lower Higher */ { 11, 0, 0, 8, 1, 0 }, /* Hit points */ { 1, 0, 0, 1, 0, 1 },14, /* Energy */ 10, 5, 0, 2, 10, A_INT, SPE_MAGIC_MAPPING, -4 }, { {"Barbarian", 0}, { {"Plunderer", "Plunderess"}, {"Pillager", 0}, {"Bandit", 0}, {"Brigand", 0}, {"Raider", 0}, {"Reaver", 0}, {"Slayer", 0}, {"Chieftain", "Chieftainess"}, {"Conqueror", "Conqueress"} }, "Mitra", "Crom", "Set", /* Hyborian */ "Bar", "the Camp of the Duali Tribe", "the Duali Oasis", PM_BARBARIAN, NON_PM, NON_PM, PM_PELIAS, PM_CHIEFTAIN, PM_THOTH_AMON, PM_OGRE, PM_TROLL, S_OGRE, S_TROLL, ART_HEART_OF_AHRIMAN, MH_HUMAN|MH_ORC | ROLE_MALE|ROLE_FEMALE | ROLE_NEUTRAL|ROLE_CHAOTIC, /* Str Int Wis Dex Con Cha */ { 16, 7, 7, 15, 16, 6 }, { 30, 6, 7, 20, 30, 7 }, /* Init Lower Higher */ { 14, 0, 0,10, 2, 0 }, /* Hit points */ { 1, 0, 0, 1, 0, 1 },10, /* Energy */ 10, 14, 0, 0, 8, A_INT, SPE_HASTE_SELF, -4 }, { {"Caveman", "Cavewoman"}, { {"Troglodyte", 0}, {"Aborigine", 0}, {"Wanderer", 0}, {"Vagrant", 0}, {"Wayfarer", 0}, {"Roamer", 0}, {"Nomad", 0}, {"Rover", 0}, {"Pioneer", 0} }, "Anu", "_Ishtar", "Anshar", /* Babylonian */ "Cav", "the Caves of the Ancestors", "the Dragon's Lair", PM_CAVEMAN, PM_CAVEWOMAN, PM_LITTLE_DOG, PM_SHAMAN_KARNOV, PM_NEANDERTHAL, PM_CHROMATIC_DRAGON, PM_BUGBEAR, PM_HILL_GIANT, S_HUMANOID, S_GIANT, ART_SCEPTRE_OF_MIGHT, MH_HUMAN|MH_DWARF|MH_GNOME | ROLE_MALE|ROLE_FEMALE | ROLE_LAWFUL|ROLE_NEUTRAL, /* Str Int Wis Dex Con Cha */ { 10, 7, 7, 7, 8, 6 }, { 30, 6, 7, 20, 30, 7 }, /* Init Lower Higher */ { 14, 0, 0, 8, 2, 0 }, /* Hit points */ { 1, 0, 0, 1, 0, 1 },10, /* Energy */ 0, 12, 0, 1, 8, A_INT, SPE_DIG, -4 }, { {"Healer", 0}, { {"Rhizotomist", 0}, {"Empiric", 0}, {"Embalmer", 0}, {"Dresser", 0}, {"Medici ossium", 0}, {"Herbalist", 0}, {"Magister", 0}, {"Physician", 0}, {"Chirurgeon", 0} }, "_Athena", "Hermes", "Poseidon", /* Greek */ "Hea", "the Temple of Epidaurus", "the Temple of Coeus", PM_HEALER, NON_PM, NON_PM, PM_HIPPOCRATES, PM_ATTENDANT, PM_CYCLOPS, PM_GIANT_RAT, PM_SNAKE, S_RODENT, S_YETI, ART_STAFF_OF_AESCULAPIUS, MH_HUMAN|MH_GNOME | ROLE_MALE|ROLE_FEMALE | ROLE_NEUTRAL, /* Str Int Wis Dex Con Cha */ { 7, 7, 13, 7, 11, 16 }, { 15, 20, 20, 15, 25, 10 }, /* Init Lower Higher */ { 11, 0, 0, 8, 1, 0 }, /* Hit points */ { 1, 4, 0, 1, 0, 2 },20, /* Energy */ 10, 3,-3, 2, 10, A_WIS, SPE_CURE_SICKNESS, -4 }, { {"Knight", 0}, { {"Gallant", 0}, {"Esquire", 0}, {"Bachelor", 0}, {"Sergeant", 0}, {"Knight", 0}, {"Banneret", 0}, {"Chevalier", 0}, {"Seignieur", 0}, {"Paladin", 0} }, "Lugh", "_Brigit", "Manannan Mac Lir", /* Celtic */ "Kni", "Camelot Castle", "the Isle of Glass", PM_KNIGHT, NON_PM, PM_PONY, PM_KING_ARTHUR, PM_PAGE, PM_IXOTH, PM_QUASIT, PM_OCHRE_JELLY, S_IMP, S_JELLY, ART_MAGIC_MIRROR_OF_MERLIN, MH_HUMAN | ROLE_MALE|ROLE_FEMALE | ROLE_LAWFUL, /* Str Int Wis Dex Con Cha */ { 13, 7, 14, 8, 10, 17 }, { 20, 15, 15, 10, 20, 10 }, /* Init Lower Higher */ { 14, 0, 0, 8, 2, 0 }, /* Hit points */ { 1, 4, 0, 1, 0, 2 },10, /* Energy */ 10, 8,-2, 0, 9, A_WIS, SPE_TURN_UNDEAD, -4 }, { {"Monk", 0}, { {"Candidate", 0}, {"Novice", 0}, {"Initiate", 0}, {"Student of Stones", 0}, {"Student of Waters", 0}, {"Student of Metals", 0}, {"Student of Winds", 0}, {"Student of Fire", 0}, {"Master", 0} }, "Shan Lai Ching", "Chih Sung-tzu", "Huan Ti", /* Chinese */ "Mon", "the Monastery of Chan-Sune", "the Monastery of the Earth-Lord", PM_MONK, NON_PM, NON_PM, PM_GRAND_MASTER, PM_ABBOT, PM_MASTER_KAEN, PM_EARTH_ELEMENTAL, PM_XORN, S_ELEMENTAL, S_XORN, ART_EYES_OF_THE_OVERWORLD, MH_HUMAN | ROLE_MALE|ROLE_FEMALE | ROLE_LAWFUL|ROLE_NEUTRAL|ROLE_CHAOTIC, /* Str Int Wis Dex Con Cha */ { 10, 7, 8, 8, 7, 7 }, { 25, 10, 20, 20, 15, 10 }, /* Init Lower Higher */ { 12, 0, 0, 8, 1, 0 }, /* Hit points */ { 2, 2, 0, 2, 0, 2 },10, /* Energy */ 10, 8,-2, 2, 20, A_WIS, SPE_RESTORE_ABILITY, -4 }, { {"Priest", "Priestess"}, { {"Aspirant", 0}, {"Acolyte", 0}, {"Adept", 0}, {"Priest", "Priestess"}, {"Curate", 0}, {"Canon", "Canoness"}, {"Lama", 0}, {"Patriarch", "Matriarch"}, {"High Priest", "High Priestess"} }, 0, 0, 0, /* chosen randomly from among the other roles */ "Pri", "the Great Temple", "the Temple of Nalzok", PM_PRIEST, PM_PRIESTESS, NON_PM, PM_ARCH_PRIEST, PM_ACOLYTE, PM_NALZOK, PM_HUMAN_ZOMBIE, PM_WRAITH, S_ZOMBIE, S_WRAITH, ART_MITRE_OF_HOLINESS, MH_HUMAN|MH_ELF | ROLE_MALE|ROLE_FEMALE | ROLE_LAWFUL|ROLE_NEUTRAL|ROLE_CHAOTIC, /* Str Int Wis Dex Con Cha */ { 7, 7, 10, 7, 7, 7 }, { 15, 10, 30, 15, 20, 10 }, /* Init Lower Higher */ { 12, 0, 0, 8, 1, 0 }, /* Hit points */ { 4, 3, 0, 2, 0, 2 },10, /* Energy */ 0, 3,-2, 2, 10, A_WIS, SPE_REMOVE_CURSE, -4 }, /* Note: Rogue precedes Ranger so that use of `-R' on the command line retains its traditional meaning. */ { {"Rogue", 0}, { {"Footpad", 0}, {"Cutpurse", 0}, {"Rogue", 0}, {"Pilferer", 0}, {"Robber", 0}, {"Burglar", 0}, {"Filcher", 0}, {"Magsman", "Magswoman"}, {"Thief", 0} }, "Issek", "Mog", "Kos", /* Nehwon */ "Rog", "the Thieves' Guild Hall", "the Assassins' Guild Hall", PM_ROGUE, NON_PM, NON_PM, PM_MASTER_OF_THIEVES, PM_THUG, PM_MASTER_ASSASSIN, PM_LEPRECHAUN, PM_GUARDIAN_NAGA, S_NYMPH, S_NAGA, ART_MASTER_KEY_OF_THIEVERY, MH_HUMAN|MH_ORC | ROLE_MALE|ROLE_FEMALE | ROLE_CHAOTIC, /* Str Int Wis Dex Con Cha */ { 7, 7, 7, 10, 7, 6 }, { 20, 10, 10, 30, 20, 10 }, /* Init Lower Higher */ { 10, 0, 0, 8, 1, 0 }, /* Hit points */ { 1, 0, 0, 1, 0, 1 },11, /* Energy */ 10, 8, 0, 1, 9, A_INT, SPE_DETECT_TREASURE, -4 }, { {"Ranger", 0}, { #if 0 /* OBSOLETE */ {"Edhel", "Elleth"}, {"Edhel", "Elleth"}, /* elf-maid */ {"Ohtar", "Ohtie"}, /* warrior */ {"Kano", /* commander (Q.) ['a] */ "Kanie"}, /* educated guess, until further research- SAC */ {"Arandur", /* king's servant, minister (Q.) - guess */ "Aranduriel"}, /* educated guess */ {"Hir", "Hiril"}, /* lord, lady (S.) ['ir] */ {"Aredhel", "Arwen"}, /* noble elf, maiden (S.) */ {"Ernil", "Elentariel"}, /* prince (S.), elf-maiden (Q.) */ {"Elentar", "Elentari"}, /* Star-king, -queen (Q.) */ "Solonor Thelandira", "Aerdrie Faenya", "Lolth", /* Elven */ #endif {"Tenderfoot", 0}, {"Lookout", 0}, {"Trailblazer", 0}, {"Reconnoiterer", "Reconnoiteress"}, {"Scout", 0}, {"Arbalester", 0}, /* One skilled at crossbows */ {"Archer", 0}, {"Sharpshooter", 0}, {"Marksman", "Markswoman"} }, "Mercury", "_Venus", "Mars", /* Roman/planets */ "Ran", "Orion's camp", "the cave of the wumpus", PM_RANGER, NON_PM, PM_LITTLE_DOG /* Orion & canis major */, PM_ORION, PM_HUNTER, PM_SCORPIUS, PM_FOREST_CENTAUR, PM_SCORPION, S_CENTAUR, S_SPIDER, ART_LONGBOW_OF_DIANA, MH_HUMAN|MH_ELF|MH_GNOME|MH_ORC | ROLE_MALE|ROLE_FEMALE | ROLE_NEUTRAL|ROLE_CHAOTIC, /* Str Int Wis Dex Con Cha */ { 13, 13, 13, 9, 13, 7 }, { 30, 10, 10, 20, 20, 10 }, /* Init Lower Higher */ { 13, 0, 0, 6, 1, 0 }, /* Hit points */ { 1, 0, 0, 1, 0, 1 },12, /* Energy */ 10, 9, 2, 1, 10, A_INT, SPE_INVISIBILITY, -4 }, { {"Samurai", 0}, { {"Hatamoto", 0}, /* Banner Knight */ {"Ronin", 0}, /* no allegiance */ {"Ninja", 0}, /* secret society */ {"Joshu", 0}, /* heads a castle */ {"Ryoshu", 0}, /* has a territory */ {"Kokushu", 0}, /* heads a province */ {"Daimyo", 0}, /* a samurai lord */ {"Kuge", 0}, /* Noble of the Court */ {"Shogun", 0} },/* supreme commander, warlord */ "_Amaterasu Omikami", "Raijin", "Susanowo", /* Japanese */ "Sam", "the Castle of the Taro Clan", "the Shogun's Castle", PM_SAMURAI, NON_PM, PM_LITTLE_DOG, PM_LORD_SATO, PM_ROSHI, PM_ASHIKAGA_TAKAUJI, PM_WOLF, PM_STALKER, S_DOG, S_ELEMENTAL, ART_TSURUGI_OF_MURAMASA, MH_HUMAN | ROLE_MALE|ROLE_FEMALE | ROLE_LAWFUL, /* Str Int Wis Dex Con Cha */ { 10, 8, 7, 10, 17, 6 }, { 30, 10, 10, 30, 14, 10 }, /* Init Lower Higher */ { 13, 0, 0, 8, 1, 0 }, /* Hit points */ { 1, 0, 0, 1, 0, 1 },11, /* Energy */ 10, 10, 0, 0, 8, A_INT, SPE_CLAIRVOYANCE, -4 }, #ifdef TOURIST { {"Tourist", 0}, { {"Rambler", 0}, {"Sightseer", 0}, {"Excursionist",0}, {"Peregrinator","Peregrinatrix"}, {"Traveler", 0}, {"Journeyer", 0}, {"Voyager", 0}, {"Explorer", 0}, {"Adventurer", 0} }, "Blind Io", "_The Lady", "Offler", /* Discworld */ "Tou", "Ankh-Morpork", "the Thieves' Guild Hall", PM_TOURIST, NON_PM, NON_PM, PM_TWOFLOWER, PM_GUIDE, PM_MASTER_OF_THIEVES, PM_GIANT_SPIDER, PM_FOREST_CENTAUR, S_SPIDER, S_CENTAUR, ART_YENDORIAN_EXPRESS_CARD, MH_HUMAN | ROLE_MALE|ROLE_FEMALE | ROLE_NEUTRAL, /* Str Int Wis Dex Con Cha */ { 7, 10, 6, 7, 7, 10 }, { 15, 10, 10, 15, 30, 20 }, /* Init Lower Higher */ { 8, 0, 0, 8, 0, 0 }, /* Hit points */ { 1, 0, 0, 1, 0, 1 },14, /* Energy */ 0, 5, 1, 2, 10, A_INT, SPE_CHARM_MONSTER, -4 }, #endif { {"Valkyrie", 0}, { {"Stripling", 0}, {"Skirmisher", 0}, {"Fighter", 0}, {"Man-at-arms", "Woman-at-arms"}, {"Warrior", 0}, {"Swashbuckler",0}, {"Hero", "Heroine"}, {"Champion", 0}, {"Lord", "Lady"} }, "Tyr", "Odin", "Loki", /* Norse */ "Val", "the Shrine of Destiny", "the cave of Surtur", PM_VALKYRIE, NON_PM, NON_PM /*PM_WINTER_WOLF_CUB*/, PM_NORN, PM_WARRIOR, PM_LORD_SURTUR, PM_FIRE_ANT, PM_FIRE_GIANT, S_ANT, S_GIANT, ART_ORB_OF_FATE, MH_HUMAN|MH_DWARF | ROLE_FEMALE | ROLE_LAWFUL|ROLE_NEUTRAL, /* Str Int Wis Dex Con Cha */ { 10, 7, 7, 7, 10, 7 }, { 30, 6, 7, 20, 30, 7 }, /* Init Lower Higher */ { 14, 0, 0, 8, 2, 0 }, /* Hit points */ { 1, 0, 0, 1, 0, 1 },10, /* Energy */ 0, 10,-2, 0, 9, A_WIS, SPE_CONE_OF_COLD, -4 }, { {"Wizard", 0}, { {"Evoker", 0}, {"Conjurer", 0}, {"Thaumaturge", 0}, {"Magician", 0}, {"Enchanter", "Enchantress"}, {"Sorcerer", "Sorceress"}, {"Necromancer", 0}, {"Wizard", 0}, {"Mage", 0} }, "Ptah", "Thoth", "Anhur", /* Egyptian */ "Wiz", "the Tower of the Balance", "the Tower of Darkness", PM_WIZARD, NON_PM, PM_KITTEN, PM_WIZARD_OF_BALANCE, PM_APPRENTICE, PM_DARK_ONE, PM_VAMPIRE_BAT, PM_XORN, S_BAT, S_WRAITH, ART_EYE_OF_THE_AETHIOPICA, MH_HUMAN|MH_ELF|MH_GNOME|MH_ORC | ROLE_MALE|ROLE_FEMALE | ROLE_NEUTRAL|ROLE_CHAOTIC, /* Str Int Wis Dex Con Cha */ { 7, 10, 7, 7, 7, 7 }, { 10, 30, 10, 20, 20, 10 }, /* Init Lower Higher */ { 10, 0, 0, 8, 1, 0 }, /* Hit points */ { 4, 3, 0, 2, 0, 3 },12, /* Energy */ 0, 1, 0, 3, 10, A_INT, SPE_MAGIC_MISSILE, -4 }, /* Array terminator */ {{0, 0}} }; /* The player's role, created at runtime from initial * choices. This may be munged in role_init(). */ struct Role urole = { {"Undefined", 0}, { {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0} }, "L", "N", "C", "Xxx", "home", "locate", NON_PM, NON_PM, NON_PM, NON_PM, NON_PM, NON_PM, NON_PM, NON_PM, 0, 0, 0, 0, /* Str Int Wis Dex Con Cha */ { 7, 7, 7, 7, 7, 7 }, { 20, 15, 15, 20, 20, 10 }, /* Init Lower Higher */ { 10, 0, 0, 8, 1, 0 }, /* Hit points */ { 2, 0, 0, 2, 0, 3 },14, /* Energy */ 0, 10, 0, 0, 4, A_INT, 0, -3 }; /* Table of all races */ const struct Race races[] = { { "human", "human", "humanity", "Hum", {"man", "woman"}, PM_HUMAN, NON_PM, PM_HUMAN_MUMMY, PM_HUMAN_ZOMBIE, MH_HUMAN | ROLE_MALE|ROLE_FEMALE | ROLE_LAWFUL|ROLE_NEUTRAL|ROLE_CHAOTIC, MH_HUMAN, 0, MH_GNOME|MH_ORC, /* Str Int Wis Dex Con Cha */ { 3, 3, 3, 3, 3, 3 }, { STR18(100), 18, 18, 18, 18, 18 }, /* Init Lower Higher */ { 2, 0, 0, 2, 1, 0 }, /* Hit points */ { 1, 0, 2, 0, 2, 0 } /* Energy */ }, { "elf", "elven", "elvenkind", "Elf", {0, 0}, PM_ELF, NON_PM, PM_ELF_MUMMY, PM_ELF_ZOMBIE, MH_ELF | ROLE_MALE|ROLE_FEMALE | ROLE_CHAOTIC, MH_ELF, MH_ELF, MH_ORC, /* Str Int Wis Dex Con Cha */ { 3, 3, 3, 3, 3, 3 }, { 18, 20, 20, 18, 16, 18 }, /* Init Lower Higher */ { 1, 0, 0, 1, 1, 0 }, /* Hit points */ { 2, 0, 3, 0, 3, 0 } /* Energy */ }, { "dwarf", "dwarven", "dwarvenkind", "Dwa", {0, 0}, PM_DWARF, NON_PM, PM_DWARF_MUMMY, PM_DWARF_ZOMBIE, MH_DWARF | ROLE_MALE|ROLE_FEMALE | ROLE_LAWFUL, MH_DWARF, MH_DWARF|MH_GNOME, MH_ORC, /* Str Int Wis Dex Con Cha */ { 3, 3, 3, 3, 3, 3 }, { STR18(100), 16, 16, 20, 20, 16 }, /* Init Lower Higher */ { 4, 0, 0, 3, 2, 0 }, /* Hit points */ { 0, 0, 0, 0, 0, 0 } /* Energy */ }, { "gnome", "gnomish", "gnomehood", "Gno", {0, 0}, PM_GNOME, NON_PM, PM_GNOME_MUMMY, PM_GNOME_ZOMBIE, MH_GNOME | ROLE_MALE|ROLE_FEMALE | ROLE_NEUTRAL, MH_GNOME, MH_DWARF|MH_GNOME, MH_HUMAN, /* Str Int Wis Dex Con Cha */ { 3, 3, 3, 3, 3, 3 }, {STR18(50),19, 18, 18, 18, 18 }, /* Init Lower Higher */ { 1, 0, 0, 1, 0, 0 }, /* Hit points */ { 2, 0, 2, 0, 2, 0 } /* Energy */ }, { "orc", "orcish", "orcdom", "Orc", {0, 0}, PM_ORC, NON_PM, PM_ORC_MUMMY, PM_ORC_ZOMBIE, MH_ORC | ROLE_MALE|ROLE_FEMALE | ROLE_CHAOTIC, MH_ORC, 0, MH_HUMAN|MH_ELF|MH_DWARF, /* Str Int Wis Dex Con Cha */ { 3, 3, 3, 3, 3, 3 }, {STR18(50),16, 16, 18, 18, 16 }, /* Init Lower Higher */ { 1, 0, 0, 1, 0, 0 }, /* Hit points */ { 1, 0, 1, 0, 1, 0 } /* Energy */ }, /* Array terminator */ { 0, 0, 0, 0 }}; /* The player's race, created at runtime from initial * choices. This may be munged in role_init(). */ struct Race urace = { "something", "undefined", "something", "Xxx", {0, 0}, NON_PM, NON_PM, NON_PM, NON_PM, 0, 0, 0, 0, /* Str Int Wis Dex Con Cha */ { 3, 3, 3, 3, 3, 3 }, { STR18(100), 18, 18, 18, 18, 18 }, /* Init Lower Higher */ { 2, 0, 0, 2, 1, 0 }, /* Hit points */ { 1, 0, 2, 0, 2, 0 } /* Energy */ }; /* Table of all genders */ const struct Gender genders[] = { {"male", "he", "him", "his", "Mal", ROLE_MALE}, {"female", "she", "her", "her", "Fem", ROLE_FEMALE}, {"neuter", "it", "it", "its", "Ntr", ROLE_NEUTER} }; /* Table of all alignments */ const struct Align aligns[] = { {"law", "lawful", "Law", ROLE_LAWFUL, A_LAWFUL}, {"balance", "neutral", "Neu", ROLE_NEUTRAL, A_NEUTRAL}, {"chaos", "chaotic", "Cha", ROLE_CHAOTIC, A_CHAOTIC}, {"evil", "unaligned", "Una", 0, A_NONE} }; /* used by str2XXX() */ static char NEARDATA randomstr[] = "random"; boolean validrole(rolenum) int rolenum; { return (rolenum >= 0 && rolenum < SIZE(roles)-1); } int randrole() { return (rn2(SIZE(roles)-1)); } int str2role(str) char *str; { int i, len; /* Is str valid? */ if (!str || !str[0]) return ROLE_NONE; /* Match as much of str as is provided */ len = strlen(str); for (i = 0; roles[i].name.m; i++) { /* Does it match the male name? */ if (!strncmpi(str, roles[i].name.m, len)) return i; /* Or the female name? */ if (roles[i].name.f && !strncmpi(str, roles[i].name.f, len)) return i; /* Or the filecode? */ if (!strcmpi(str, roles[i].filecode)) return i; } if ((len == 1 && (*str == '*' || *str == '@')) || !strncmpi(str, randomstr, len)) return ROLE_RANDOM; /* Couldn't find anything appropriate */ return ROLE_NONE; } boolean validrace(rolenum, racenum) int rolenum, racenum; { /* Assumes validrole */ return (racenum >= 0 && racenum < SIZE(races)-1 && (roles[rolenum].allow & races[racenum].allow & ROLE_RACEMASK)); } int randrace(rolenum) int rolenum; { int i, n = 0; /* Count the number of valid races */ for (i = 0; races[i].noun; i++) if (roles[rolenum].allow & races[i].allow & ROLE_RACEMASK) n++; /* Pick a random race */ /* Use a factor of 100 in case of bad random number generators */ if (n) n = rn2(n*100)/100; for (i = 0; races[i].noun; i++) if (roles[rolenum].allow & races[i].allow & ROLE_RACEMASK) { if (n) n--; else return (i); } /* This role has no permitted races? */ return (rn2(SIZE(races)-1)); } int str2race(str) char *str; { int i, len; /* Is str valid? */ if (!str || !str[0]) return ROLE_NONE; /* Match as much of str as is provided */ len = strlen(str); for (i = 0; races[i].noun; i++) { /* Does it match the noun? */ if (!strncmpi(str, races[i].noun, len)) return i; /* Or the filecode? */ if (!strcmpi(str, races[i].filecode)) return i; } if ((len == 1 && (*str == '*' || *str == '@')) || !strncmpi(str, randomstr, len)) return ROLE_RANDOM; /* Couldn't find anything appropriate */ return ROLE_NONE; } boolean validgend(rolenum, racenum, gendnum) int rolenum, racenum, gendnum; { /* Assumes validrole and validrace */ return (gendnum >= 0 && gendnum < ROLE_GENDERS && (roles[rolenum].allow & races[racenum].allow & genders[gendnum].allow & ROLE_GENDMASK)); } int randgend(rolenum, racenum) int rolenum, racenum; { int i, n = 0; /* Count the number of valid genders */ for (i = 0; i < ROLE_GENDERS; i++) if (roles[rolenum].allow & races[racenum].allow & genders[i].allow & ROLE_GENDMASK) n++; /* Pick a random gender */ if (n) n = rn2(n); for (i = 0; i < ROLE_GENDERS; i++) if (roles[rolenum].allow & races[racenum].allow & genders[i].allow & ROLE_GENDMASK) { if (n) n--; else return (i); } /* This role/race has no permitted genders? */ return (rn2(ROLE_GENDERS)); } int str2gend(str) char *str; { int i, len; /* Is str valid? */ if (!str || !str[0]) return ROLE_NONE; /* Match as much of str as is provided */ len = strlen(str); for (i = 0; i < ROLE_GENDERS; i++) { /* Does it match the adjective? */ if (!strncmpi(str, genders[i].adj, len)) return i; /* Or the filecode? */ if (!strcmpi(str, genders[i].filecode)) return i; } if ((len == 1 && (*str == '*' || *str == '@')) || !strncmpi(str, randomstr, len)) return ROLE_RANDOM; /* Couldn't find anything appropriate */ return ROLE_NONE; } boolean validalign(rolenum, racenum, alignnum) int rolenum, racenum, alignnum; { /* Assumes validrole and validrace */ return (alignnum >= 0 && alignnum < ROLE_ALIGNS && (roles[rolenum].allow & races[racenum].allow & aligns[alignnum].allow & ROLE_ALIGNMASK)); } int randalign(rolenum, racenum) int rolenum, racenum; { int i, n = 0; /* Count the number of valid alignments */ for (i = 0; i < ROLE_ALIGNS; i++) if (roles[rolenum].allow & races[racenum].allow & aligns[i].allow & ROLE_ALIGNMASK) n++; /* Pick a random alignment */ if (n) n = rn2(n); for (i = 0; i < ROLE_ALIGNS; i++) if (roles[rolenum].allow & races[racenum].allow & aligns[i].allow & ROLE_ALIGNMASK) { if (n) n--; else return (i); } /* This role/race has no permitted alignments? */ return (rn2(ROLE_ALIGNS)); } int str2align(str) char *str; { int i, len; /* Is str valid? */ if (!str || !str[0]) return ROLE_NONE; /* Match as much of str as is provided */ len = strlen(str); for (i = 0; i < ROLE_ALIGNS; i++) { /* Does it match the adjective? */ if (!strncmpi(str, aligns[i].adj, len)) return i; /* Or the filecode? */ if (!strcmpi(str, aligns[i].filecode)) return i; } if ((len == 1 && (*str == '*' || *str == '@')) || !strncmpi(str, randomstr, len)) return ROLE_RANDOM; /* Couldn't find anything appropriate */ return ROLE_NONE; } /* is rolenum compatible with any racenum/gendnum/alignnum constraints? */ boolean ok_role(rolenum, racenum, gendnum, alignnum) int rolenum, racenum, gendnum, alignnum; { int i; short allow; if (rolenum >= 0 && rolenum < SIZE(roles)-1) { allow = roles[rolenum].allow; if (racenum >= 0 && racenum < SIZE(races)-1 && !(allow & races[racenum].allow & ROLE_RACEMASK)) return FALSE; if (gendnum >= 0 && gendnum < ROLE_GENDERS && !(allow & genders[gendnum].allow & ROLE_GENDMASK)) return FALSE; if (alignnum >= 0 && alignnum < ROLE_ALIGNS && !(allow & aligns[alignnum].allow & ROLE_ALIGNMASK)) return FALSE; return TRUE; } else { for (i = 0; i < SIZE(roles)-1; i++) { allow = roles[i].allow; if (racenum >= 0 && racenum < SIZE(races)-1 && !(allow & races[racenum].allow & ROLE_RACEMASK)) continue; if (gendnum >= 0 && gendnum < ROLE_GENDERS && !(allow & genders[gendnum].allow & ROLE_GENDMASK)) continue; if (alignnum >= 0 && alignnum < ROLE_ALIGNS && !(allow & aligns[alignnum].allow & ROLE_ALIGNMASK)) continue; return TRUE; } return FALSE; } } /* pick a random role subject to any racenum/gendnum/alignnum constraints */ int pick_role(racenum, gendnum, alignnum) int racenum, gendnum, alignnum; { int i; int roles_ok = 0; for (i = 0; i < SIZE(roles)-1; i++) { if (ok_role(i, racenum, gendnum, alignnum)) roles_ok++; } if (roles_ok == 0) return ROLE_NONE; roles_ok = rn2(roles_ok); for (i = 0; i < SIZE(roles)-1; i++) { if (ok_role(i, racenum, gendnum, alignnum)) { if (roles_ok == 0) return i; else roles_ok--; } } return ROLE_NONE; } /* is racenum compatible with any rolenum/gendnum/alignnum constraints? */ boolean ok_race(rolenum, racenum, gendnum, alignnum) int rolenum, racenum, gendnum, alignnum; { int i; short allow; if (racenum >= 0 && racenum < SIZE(races)-1) { allow = races[racenum].allow; if (rolenum >= 0 && rolenum < SIZE(roles)-1 && !(allow & roles[rolenum].allow & ROLE_RACEMASK)) return FALSE; if (gendnum >= 0 && gendnum < ROLE_GENDERS && !(allow & genders[gendnum].allow & ROLE_GENDMASK)) return FALSE; if (alignnum >= 0 && alignnum < ROLE_ALIGNS && !(allow & aligns[alignnum].allow & ROLE_ALIGNMASK)) return FALSE; return TRUE; } else { for (i = 0; i < SIZE(races)-1; i++) { allow = races[i].allow; if (rolenum >= 0 && rolenum < SIZE(roles)-1 && !(allow & roles[rolenum].allow & ROLE_RACEMASK)) continue; if (gendnum >= 0 && gendnum < ROLE_GENDERS && !(allow & genders[gendnum].allow & ROLE_GENDMASK)) continue; if (alignnum >= 0 && alignnum < ROLE_ALIGNS && !(allow & aligns[alignnum].allow & ROLE_ALIGNMASK)) continue; return TRUE; } return FALSE; } } /* pick a random race subject to any rolenum/gendnum/alignnum constraints */ int pick_race(rolenum, gendnum, alignnum) int rolenum, gendnum, alignnum; { int i; int races_ok = 0; for (i = 0; i < SIZE(races)-1; i++) { if (ok_race(rolenum, i, gendnum, alignnum)) races_ok++; } if (races_ok == 0) return ROLE_NONE; races_ok = rn2(races_ok); for (i = 0; i < SIZE(races)-1; i++) { if (ok_race(rolenum, i, gendnum, alignnum)) { if (races_ok == 0) return i; else races_ok--; } } return ROLE_NONE; } /* is gendnum compatible with any rolenum/racenum/alignnum constraints? */ /* gender and alignment are not comparable (and also not constrainable) */ boolean ok_gend(rolenum, racenum, gendnum, alignnum) int rolenum, racenum, gendnum, alignnum; { int i; short allow; if (gendnum >= 0 && gendnum < ROLE_GENDERS) { allow = genders[gendnum].allow; if (rolenum >= 0 && rolenum < SIZE(roles)-1 && !(allow & roles[rolenum].allow & ROLE_GENDMASK)) return FALSE; if (racenum >= 0 && racenum < SIZE(races)-1 && !(allow & races[racenum].allow & ROLE_GENDMASK)) return FALSE; return TRUE; } else { for (i = 0; i < ROLE_GENDERS; i++) { allow = genders[i].allow; if (rolenum >= 0 && rolenum < SIZE(roles)-1 && !(allow & roles[rolenum].allow & ROLE_GENDMASK)) continue; if (racenum >= 0 && racenum < SIZE(races)-1 && !(allow & races[racenum].allow & ROLE_GENDMASK)) continue; return TRUE; } return FALSE; } } /* pick a random gender subject to any rolenum/racenum/alignnum constraints */ /* gender and alignment are not comparable (and also not constrainable) */ int pick_gend(rolenum, racenum, alignnum) int rolenum, racenum, alignnum; { int i; int gends_ok = 0; for (i = 0; i < ROLE_GENDERS; i++) { if (ok_gend(rolenum, racenum, i, alignnum)) gends_ok++; } if (gends_ok == 0) return ROLE_NONE; gends_ok = rn2(gends_ok); for (i = 0; i < ROLE_GENDERS; i++) { if (ok_gend(rolenum, racenum, i, alignnum)) { if (gends_ok == 0) return i; else gends_ok--; } } return ROLE_NONE; } /* is alignnum compatible with any rolenum/racenum/gendnum constraints? */ /* alignment and gender are not comparable (and also not constrainable) */ boolean ok_align(rolenum, racenum, gendnum, alignnum) int rolenum, racenum, gendnum, alignnum; { int i; short allow; if (alignnum >= 0 && alignnum < ROLE_ALIGNS) { allow = aligns[alignnum].allow; if (rolenum >= 0 && rolenum < SIZE(roles)-1 && !(allow & roles[rolenum].allow & ROLE_ALIGNMASK)) return FALSE; if (racenum >= 0 && racenum < SIZE(races)-1 && !(allow & races[racenum].allow & ROLE_ALIGNMASK)) return FALSE; return TRUE; } else { for (i = 0; i < ROLE_ALIGNS; i++) { allow = races[i].allow; if (rolenum >= 0 && rolenum < SIZE(roles)-1 && !(allow & roles[rolenum].allow & ROLE_ALIGNMASK)) continue; if (racenum >= 0 && racenum < SIZE(races)-1 && !(allow & races[racenum].allow & ROLE_ALIGNMASK)) continue; return TRUE; } return FALSE; } } /* pick a random alignment subject to any rolenum/racenum/gendnum constraints */ /* alignment and gender are not comparable (and also not constrainable) */ int pick_align(rolenum, racenum, gendnum) int rolenum, racenum, gendnum; { int i; int aligns_ok = 0; for (i = 0; i < ROLE_ALIGNS; i++) { if (ok_align(rolenum, racenum, gendnum, i)) aligns_ok++; } if (aligns_ok == 0) return ROLE_NONE; aligns_ok = rn2(aligns_ok); for (i = 0; i < ROLE_ALIGNS; i++) { if (ok_align(rolenum, racenum, gendnum, i)) { if (aligns_ok == 0) return i; else aligns_ok--; } } return ROLE_NONE; } void plnamesuffix() { char *sptr, *eptr; int i; /* Look for tokens delimited by '-' */ if ((eptr = index(plname, '-')) != (char *) 0) *eptr++ = '\0'; while (eptr) { /* Isolate the next token */ sptr = eptr; if ((eptr = index(sptr, '-')) != (char *)0) *eptr++ = '\0'; /* Try to match it to something */ if ((i = str2role(sptr)) != ROLE_NONE) flags.initrole = i; else if ((i = str2race(sptr)) != ROLE_NONE) flags.initrace = i; else if ((i = str2gend(sptr)) != ROLE_NONE) flags.initgend = i; else if ((i = str2align(sptr)) != ROLE_NONE) flags.initalign = i; } if(!plname[0]) { askname(); plnamesuffix(); } /* commas in the plname confuse the record file, convert to spaces */ for (sptr = plname; *sptr; sptr++) { if (*sptr == ',') *sptr = ' '; } } /* * Special setup modifications here: * * Unfortunately, this is going to have to be done * on each newgame or restore, because you lose the permonst mods * across a save/restore. :-) * * 1 - The Rogue Leader is the Tourist Nemesis. * 2 - Priests start with a random alignment - convert the leader and * guardians here. * 3 - Elves can have one of two different leaders, but can't work it * out here because it requires hacking the level file data (see * sp_lev.c). * * This code also replaces quest_init(). */ void role_init() { int alignmnt; /* Strip the role letter out of the player name. * This is included for backwards compatibility. */ plnamesuffix(); /* Check for a valid role. Try flags.initrole first. */ if (!validrole(flags.initrole)) { /* Try the player letter second */ if ((flags.initrole = str2role(pl_character)) < 0) /* None specified; pick a random role */ flags.initrole = randrole(); } /* We now have a valid role index. Copy the role name back. */ /* This should become OBSOLETE */ Strcpy(pl_character, roles[flags.initrole].name.m); pl_character[PL_CSIZ-1] = '\0'; /* Check for a valid race */ if (!validrace(flags.initrole, flags.initrace)) flags.initrace = randrace(flags.initrole); /* Check for a valid gender. Try flags.igend first. */ if (!validgend(flags.initrole, flags.initrace, flags.initgend)) /* Use flags.female second. Note that there is no way * to check for an unspecified gender. */ flags.initgend = flags.female; /* Don't change flags.female; this may be a restore */ /* Check for a valid alignment */ if (!validalign(flags.initrole, flags.initrace, flags.initalign)) /* Pick a random alignment */ flags.initalign = randalign(flags.initrole, flags.initrace); alignmnt = aligns[flags.initalign].value; /* Initialize urole and urace */ urole = roles[flags.initrole]; urace = races[flags.initrace]; /* Fix up the quest leader */ if (urole.ldrnum != NON_PM) { mons[urole.ldrnum].msound = MS_LEADER; mons[urole.ldrnum].mflags2 |= (M2_PEACEFUL); mons[urole.ldrnum].mflags3 |= M3_CLOSE; mons[urole.ldrnum].maligntyp = alignmnt * 3; } /* Fix up the quest guardians */ if (urole.guardnum != NON_PM) { mons[urole.guardnum].mflags2 |= (M2_PEACEFUL); mons[urole.guardnum].maligntyp = alignmnt * 3; } /* Fix up the quest nemesis */ if (urole.neminum != NON_PM) { mons[urole.neminum].msound = MS_NEMESIS; mons[urole.neminum].mflags2 &= ~(M2_PEACEFUL); mons[urole.neminum].mflags2 |= (M2_NASTY|M2_STALK|M2_HOSTILE); mons[urole.neminum].mflags3 |= M3_WANTSARTI | M3_WAITFORU; } /* Fix up the god names */ if (flags.pantheon == -1) { /* new game */ flags.pantheon = flags.initrole; /* use own gods */ while (!roles[flags.pantheon].lgod) /* unless they're missing */ flags.pantheon = randrole(); } if (!urole.lgod) { urole.lgod = roles[flags.pantheon].lgod; urole.ngod = roles[flags.pantheon].ngod; urole.cgod = roles[flags.pantheon].cgod; } /* Fix up infravision */ if (mons[urace.malenum].mflags3 & M3_INFRAVISION) { /* although an infravision intrinsic is possible, infravision * is purely a property of the physical race. This means that we * must put the infravision flag in the player's current race * (either that or have separate permonst entries for * elven/non-elven members of each class). The side effect is that * all NPCs of that class will have (probably bogus) infravision, * but since infravision has no effect for NPCs anyway we can * ignore this. */ mons[urole.malenum].mflags3 |= M3_INFRAVISION; if (urole.femalenum != NON_PM) mons[urole.femalenum].mflags3 |= M3_INFRAVISION; } /* Artifacts are fixed in hack_artifacts() */ /* Success! */ return; } const char * Hello(mtmp) struct monst *mtmp; { switch (Role_switch) { case PM_KNIGHT: return ("Salutations"); /* Olde English */ case PM_SAMURAI: return (mtmp && mtmp->data == &mons[PM_SHOPKEEPER] ? "Irasshaimase" : "Konnichi wa"); /* Japanese */ #ifdef TOURIST case PM_TOURIST: return ("Aloha"); /* Hawaiian */ #endif case PM_VALKYRIE: return ( #ifdef MAIL mtmp && mtmp->data == &mons[PM_MAIL_DAEMON] ? "Hallo" : #endif "Velkommen"); /* Norse */ default: return ("Hello"); } } const char * Goodbye() { switch (Role_switch) { case PM_KNIGHT: return ("Fare thee well"); /* Olde English */ case PM_SAMURAI: return ("Sayonara"); /* Japanese */ #ifdef TOURIST case PM_TOURIST: return ("Aloha"); /* Hawaiian */ #endif case PM_VALKYRIE: return ("Farvel"); /* Norse */ default: return ("Goodbye"); } } /* role.c */