/* * Code to fill the dungeon with objects. */ #include "obj.h" #include "vars.h" #include "dice.h" #include "vault.h" #include "pline.h" #include "structs.h" #include "objtypes.h" #include "obj-ring.h" #include "digdungeon.h" #include "obj-weapon.h" #include "obj-armour.h" #include "filldungeon.h" static RANDCHOICE choose_type[] = { { 30, OC_SCROLL }, { 30, OC_POTION }, { 5, OC_WAND }, { 10, OC_WEAPON }, { 10, OC_ARMOUR }, { 4, OC_RING } }; static RANDCHOICE choose_scroll[] = { { 4, OBJ_SCROLL_OF_CHARGING }, { 5, OBJ_SCROLL_OF_ENCHANT_ITEM }, { 8, OBJ_SCROLL_OF_HOLD_OTHERS }, { 15, OBJ_SCROLL_OF_IDENTIFY }, { 3, OBJ_SCROLL_OF_INVULNERABILITY }, { 10, OBJ_SCROLL_OF_LOCATE_MONSTERS }, { 10, OBJ_SCROLL_OF_LOCATE_OBJECTS }, { 5, OBJ_SCROLL_OF_MAGIC_MAPPING }, { 4, OBJ_SCROLL_OF_PHASING }, { 6, OBJ_SCROLL_OF_TELEPORTATION }, { 9, OBJ_SCROLL_OF_REMOVE_CURSE }, { 10, OBJ_SCROLL_OF_SUMMON_MONSTER }, { 10, OBJ_SCROLL_OF_EXPLOSION } }; static RANDCHOICE choose_potion[] = { { 15, OBJ_POTION_OF_AMNESIA }, { 10, OBJ_POTION_OF_CLAIRVOYANCE }, { 15, OBJ_POTION_OF_CONFUSION }, { 5, OBJ_POTION_OF_EXTRA_HEALING }, { 9, OBJ_POTION_OF_FIRE_RESISTANCE }, { 7, OBJ_POTION_OF_GAIN_STRENGTH }, { 6, OBJ_POTION_OF_GIANT_STRENGTH }, { 10, OBJ_POTION_OF_HEALING }, { 5, OBJ_POTION_OF_HEROISM }, { 10, OBJ_POTION_OF_INFRAVISION }, { 5, OBJ_POTION_OF_INVISIBILITY }, { 9, OBJ_POTION_OF_LEVITATION }, { 8, OBJ_POTION_OF_MEMORY }, { 10, OBJ_POTION_OF_PERCEPTION }, { 8, OBJ_POTION_OF_RESTORATION }, { 10, OBJ_POTION_OF_SEE_INVISIBLE }, { 15, OBJ_POTION_OF_SLEEP }, { 5, OBJ_POTION_OF_SPEED }, { 8, OBJ_POTION_OF_WATER_BREATHING } }; static RANDCHOICE choose_wand[] = { { 10, OBJ_WAND_OF_CANCELLATION }, { 10, OBJ_WAND_OF_DARKNESS }, { 3, OBJ_WAND_OF_DEATH }, { 7, OBJ_WAND_OF_DIGGING }, { 6, OBJ_WAND_OF_DISINTEGRATION }, { 10, OBJ_WAND_OF_FIRE_BALL }, { 10, OBJ_WAND_OF_FIRE_BOLT }, { 10, OBJ_WAND_OF_ICE_BOLT }, { 10, OBJ_WAND_OF_LIGHTNING }, { 10, OBJ_WAND_OF_MAGIC_MISSILE }, { 15, OBJ_WAND_OF_MAKE_INVISIBLE }, { 4, OBJ_WAND_OF_POLYMORPH }, { 10, OBJ_WAND_OF_STRIKING }, { 10, OBJ_WAND_OF_TELEPORTATION }, { 5, OBJ_WAND_OF_UNDEAD_TURNING }, { 3, OBJ_WAND_OF_WISHING } }; static RANDCHOICE choose_ring[] = { { 15, OBJ_RING_OF_FIRE_RESISTANCE }, { 10, OBJ_RING_OF_PROTECTION }, { 7, OBJ_RING_OF_REGENERATION }, { 15, OBJ_RING_OF_SEE_INVISIBLE }, { 4, OBJ_RING_OF_SLOW_DIGESTION }, { 10, OBJ_RING_OF_STEALTH }, { 10, OBJ_RING_OF_TELEPORT_CONTROL }, { 7, OBJ_RING_OF_VAMPIRIC_REGENERATION }, { 10, OBJ_RING_OF_WATER_BREATHING } }; static RANDCHOICE choose_weapon[] = { { 3, OBJ_DAGGER }, { 4, OBJ_CLUB }, { 2, OBJ_MACE }, { 1, OBJ_FLAIL }, { 1, OBJ_MORNINGSTAR }, { 1, OBJ_SHORT_SWORD }, { 1, OBJ_LONG_SWORD }, { 1, OBJ_TWO_HANDED_SWORD } }; static RANDCHOICE choose_armour[] = { { 4, OBJ_LEATHER_ARMOUR }, { 3, OBJ_STUDDED_LEATHER_ARMOUR }, { 2, OBJ_RING_MAIL }, { 1, OBJ_SCALE_MAIL }, { 1, OBJ_CHAIN_MAIL }, { 1, OBJ_BANDED_MAIL }, { 1, OBJ_SPLINT_MAIL }, { 1, OBJ_PLATE_MAIL } }; /* * Location test function for placing the Crown. */ static int ckcrown(LOC *lc) { return(!(lc->flags & LF_NOCROWN) && ckavoid(lc)); } OBJ *obj_gen(int type) { OBJ *o; o = obj_make(type); switch (objtypes[type].class) { case OC_SCROLL: case OC_POTION: case OC_WAND: break; case OC_RING: // Ring plusses/curses are handled in new_ring() break; case OC_WEAPON: { int plush; int plusd; int sign; plush = 0; plusd = 0; if (onein(3)) { sign = rnd(2) ? 1 : -1; do if (rnd(2)) plush += sign * roll("2d10-1"); else plusd += sign * roll("2d10-1"); while (rnd(2)); } weapon_set_plusses(o,plush,plusd); (*objtypes[type].ops->setcursed)(o,sign<0); } break; case OC_ARMOUR: { int plus; int sign; plus = 0; if (onein(3)) { sign = rnd(2) ? 1 : -1; do plus += sign * roll("2d10-1"); while (rnd(2)); } armour_set_plusses(o,plus); (*objtypes[type].ops->setcursed)(o,sign<0); } break; } return(o); } /* * Generate a random object and place it on a dungeon level, given by * level number. */ static void random_object_on_level(int ln) { int class; int type; OBJ *o; class = RANDCHOOSE(choose_type); switch (class) { case OC_SCROLL: type = RANDCHOOSE(choose_scroll); break; case OC_POTION: type = RANDCHOOSE(choose_potion); break; case OC_WAND: type = RANDCHOOSE(choose_wand); break; case OC_WEAPON: type = RANDCHOOSE(choose_weapon); break; case OC_ARMOUR: type = RANDCHOOSE(choose_armour); break; case OC_RING: type = RANDCHOOSE(choose_ring); break; default: panic("impossible object class"); break; } o = obj_gen(type); add_obj_to_inv(o,&randomloc(&levels[ln],&ckroutine)->objs); } /* * Create a trap on a given level. * * Note that this doesn't check that the location doesn't already have * a trap. See the comment on filldungeon for discussion. */ static void create_trap(LEVEL *lv) { LOC *l; l = randomloc(lv,&ckroutine); l->trap = TRAP_KIND_ARROW; } /* * Fill the dungeon with objects. This means: * * - Blank maps. We create one per level of the main dungeon. With * the maps created in the player's inventory, this is enough to map * the whole dungeon. * * - The Crown of Yendor. * * - Scarabs of the elemental planes, on the bottom four levels of the * dungeon, with a chance of a duplicate. * * - Gold scattered around. * * - Various objects. * * - Traps. See below. * * The initial player OBJ_LEATHER_ARMOUR and OBJ_DAGGER are not done * here; those are created as part of creating the player-avatar * monster, over in mon-@-you.c. * * We create an average of two traps per level, with a bias for * creating them deeper in the dungeon; we want an expected count of * one trap on level 1, increasing linearly up to an expected count of * three traps on the deepest level. We could create a fixed number * of traps, with only the levels they are on varying, but that would * be too predictable ("okay, I've found all but two of the traps in * this dungeon, so...."). Instead, we do a loop like * "while (chance) create trap" for each level, with the chance * computed based on level number to get the desired expected number * of traps for each level. For probability p in the while(), the * expected number of traps is 0*(1-p) + 1*p*(1-p) + 2*p*p*(1-p) + * 3*p*p*p*(1-p) + ...; if we let this number be E, then it can be * written as E = ((1 - p) * 0) + (p * (1 + E)): chance 1-p of none, * plus chance p of 1 plus another E more. Solving, p = E / (1 + E). * If E were always an integer, we could express this as simply * !onein(E) in the while(), but E is not an integer for most levels. * * Because create_trap() doesn't check that the location it chooses * doesn't already have a trap, there is a reduction in the expected * number of traps per level due to the second and later traps * potentially picking the same cell as an earlier trap. We don't do * anything to alleviate this; the reduction is slight enough I don't * think it's worth doing anything about. */ void filldungeon(void) { int ln; LOC *lc; OBJ *g; int scarabs[4]; int i; int j; int t; char dr[16]; double e; double p; // Maps and gold in the main dungeon for (ln=DUNGEON_LEVELS-1;ln>=0;ln--) { add_obj_to_inv(obj_make(OBJ_MAP),&randomloc(&levels[ln],&ckroutine)->objs); sprintf(&dr[0],"%dd10",levels[ln].levelno); for (i=roll("2d6-1");i>0;i--) { g = obj_make(OBJ_GOLD); g->number = roll(&dr[0]); add_obj_to_inv(g,&randomloc(&levels[ln],&ckroutine)->objs); } } // Gold in vaults fillvaults(); // The Crown of Yendor avoidloc = gate_hell; lc = randomloc(&levels[L_HELL],&ckcrown); add_obj_to_inv(obj_make(OBJ_CROWN),&lc->objs); // The scarabs scarabs[0] = OBJ_SCARAB_OF_EARTH; scarabs[1] = OBJ_SCARAB_OF_AIR; scarabs[2] = OBJ_SCARAB_OF_FIRE; scarabs[3] = OBJ_SCARAB_OF_WATER; for (i=4-1;i>=0;i--) { j = rnd(4); t = scarabs[i]; scarabs[i] = scarabs[j]; scarabs[j] = t; } for (i=4-1;i>=0;i--) { add_obj_to_inv(obj_make(scarabs[i]),&randomloc(&levels[DUNGEON_LEVELS-1-i],&ckroutine)->objs); } // Possible duplicate scarab if (! rnd(5)) { add_obj_to_inv(obj_make(scarabs[1+rnd(3)]),&randomloc(&levels[DUNGEON_LEVELS-4+rnd(2)],&ckroutine)->objs); } // Various other objects for (ln=DUNGEON_LEVELS-1;ln>=0;ln--) { for (j=roll("d4+d3+d2");j>0;j--) random_object_on_level(ln); while (! rnd(2)) random_object_on_level(ln); } for (ln=DUNGEON_LEVELS-1;ln>=0;ln--) { e = ((2.0 * ln) / (DUNGEON_LEVELS - 1)) + 1; p = e / (1 + e); while (frnd() < p) { create_trap(&levels[ln]); } } }