/* * new_level.c - Dig and draw a new level * * Advanced Rogue * Copyright (C) 1984, 1985, 1986 Michael Morgan, Ken Dalka and AT&T * All rights reserved. * * Based on "Rogue: Exploring the Dungeons of Doom" * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman * All rights reserved. * * See the file LICENSE.TXT for full copyright and licensing information. */ #include "curses.h" #include "rogue.h" #define TERRASAVE 3 void put_things(LEVTYPE ltype); /* * new_level: * Dig and draw a new level * ltype: designates type of level to create */ void new_level(LEVTYPE ltype) { register int rm, i, cnt; register char ch; register struct linked_list *item; register struct thing *tp; register struct object *obj; int waslit = 0; /* Was the previous outside level lit? */ int starty, startx, deltay, deltax; bool fresh=TRUE, vert, top; struct room *rp; struct linked_list *nitem, *savmonst=NULL, *savitems=NULL; coord stairs; if (wizard) { msg("Turns: %d", turns); /* Number of turns for last level */ mpos = 0; } /* Start player off right */ turn_off(player, ISHELD); turn_off(player, ISFLEE); extinguish(suffocate); hold_count = 0; trap_tries = 0; /* Are we just entering a dungeon? If so, how big is it? */ if (ltype != OUTSIDE && nfloors < 0) nfloors = HARDER+10 + rnd(11); if (level > max_level) max_level = level; /* Are we starting a new outside level? */ if (ltype == OUTSIDE) { register int i, j; /* Save some information prior to clearing the screen */ if (level == -1 || mvinch(hero.y, hero.x) == '-') vert = TRUE; else vert = FALSE; if (level == -1) { fresh = TRUE; starty = 2; startx = 1; deltay = deltax = 1; level = 0; /* Restore the level */ } else { /* Copy several lines of the terrain to the other end */ char cch; /* Copy character */ /* Was the area dark (not magically lit)? */ if (!(rooms[0].r_flags & ISDARK)) waslit = 1; fresh = FALSE; if ((vert && hero.y == 1) || (!vert && hero.x == 0)) top = TRUE; else top = FALSE; for (i=0; it_pos.y < TERRASAVE + 2) tp->t_pos.y += lines - 5 - TERRASAVE; else continue; } else { if (tp->t_pos.y > lines - 4 - TERRASAVE) tp->t_pos.y += 5 + TERRASAVE - lines; else continue; } } else { if (top) { if (tp->t_pos.x < TERRASAVE + 1) tp->t_pos.x += cols - 2 - TERRASAVE; else continue; } else { if (tp->t_pos.x > cols - 2 - TERRASAVE) tp->t_pos.x += 2 + TERRASAVE - cols; else continue; } } /* * If the monster is busy chasing another monster, don't save * it */ if (tp->t_dest && tp->t_dest != &hero) continue; detach(mlist, item); attach(savmonst, item); } /* Check if any treasure should be saved */ for (item = lvl_obj; item != NULL; item = nitem) { nitem = next(item); obj = OBJPTR(item); if (vert) { if (top) { if (obj->o_pos.y < TERRASAVE + 2) obj->o_pos.y += lines - 5 - TERRASAVE; else continue; } else { if (obj->o_pos.y > lines - 4 - TERRASAVE) obj->o_pos.y += 5 + TERRASAVE - lines; else continue; } } else { if (top) { if (obj->o_pos.x < TERRASAVE + 1) obj->o_pos.x += cols - 2 - TERRASAVE; else continue; } else { if (obj->o_pos.x > cols - 2 - TERRASAVE) obj->o_pos.x += 2 + TERRASAVE - cols; else continue; } } detach(lvl_obj, item); attach(savitems, item); } } } wclear(cw); wclear(mw); if (fresh) clear(); /* * check to see if he missed a UNIQUE, If he did then put it back * in the monster table for next time */ for (item = mlist; item != NULL; item = next(item)) { tp = THINGPTR(item); if (on(*tp, ISUNIQUE)) monsters[tp->t_index].m_normal = TRUE; } /* * Free up the monsters on the last level */ t_free_list(monst_dead); t_free_list(mlist); o_free_list(lvl_obj); /* Free up previous objects (if any) */ for (rp = rooms; rp < &rooms[MAXROOMS]; rp++) r_free_list(rp->r_exit); /* Free up the exit lists */ levtype = ltype; foods_this_level = 0; /* food for hero this level */ if (ltype == POSTLEV || ltype == STARTLEV) { if (ltype == POSTLEV) do_post(FALSE); /* Trading post */ else do_post(TRUE); /* Equippage */ levtype = ltype = POSTLEV; } else if (ltype == MAZELEV) { do_maze(); no_food++; put_things(ltype); /* Place objects (if any) */ } else if (ltype == OUTSIDE) { init_terrain(); do_terrain(starty, startx, deltay, deltax, (bool) (fresh || !vert)); no_food++; put_things(ltype); /* Should we magically light this area? */ if (waslit) rooms[0].r_flags &= ~ISDARK; } else { do_rooms(); /* Draw rooms */ do_passages(); /* Draw passages */ no_food++; put_things(ltype); /* Place objects (if any) */ } /* * Place the staircase down. Only a small chance for an outside stairway. */ if (ltype != OUTSIDE || roll(1, 4) == 4) { cnt = 0; do { rm = rnd_room(); rnd_pos(&rooms[rm], &stairs); } until (mvinch(stairs.y, stairs.x) == FLOOR || cnt++ > 5000); addch(STAIRS); } /* * maybe add a trading post */ if (level > 5 && rnd(11) == 7 && ltype == NORMLEV) { cnt = 0; do { rm = rnd_room(); if (rooms[rm].r_flags & ISTREAS) continue; rnd_pos(&rooms[rm], &stairs); } until (winat(stairs.y, stairs.x) == FLOOR || cnt++ > 5000); addch(POST); } if (ltype != POSTLEV) { /* Add monsters that fell through */ nitem = tlist; while (nitem != NULL) { item = nitem; nitem = next(item); /* because detach and attach mess up ptrs */ tp = THINGPTR(item); cnt = 0; do { rm = rnd_room(); rnd_pos(&rooms[rm], &tp->t_pos); } until (cnt++ > 5000 || winat(tp->t_pos.y, tp->t_pos.x) == FLOOR); mvwaddch(mw, tp->t_pos.y, tp->t_pos.x, tp->t_type); tp->t_oldch = CCHAR( mvwinch(cw, tp->t_pos.y, tp->t_pos.x) ); /* * If it has a fire, mark it */ if (on(*tp, HASFIRE)) { register struct linked_list *fire_item; fire_item = creat_item(); ldata(fire_item) = (char *) tp; attach(rooms[rm].r_fires, fire_item); rooms[rm].r_flags |= HASFIRE; } turn_off(*tp,ISELSEWHERE); detach(tlist, item); attach(mlist, item); } } /* Restore any saved monsters */ for (item = savmonst; item != NULL; item = nitem) { nitem = next(item); tp = THINGPTR(item); mvwaddch(mw, tp->t_pos.y, tp->t_pos.x, tp->t_type); tp->t_oldch = CCHAR( mvwinch(cw, tp->t_pos.y, tp->t_pos.x) ); /* * If it has a fire, mark it */ if (on(*tp, HASFIRE)) { register struct linked_list *fire_item; fire_item = creat_item(); ldata(fire_item) = (char *) tp; attach(rooms[rm].r_fires, fire_item); rooms[rm].r_flags |= HASFIRE; } detach(savmonst, item); attach(mlist, item); } /* Restore any saved objects */ for(item = savitems; item != NULL; item = nitem) { nitem = next(item); obj = OBJPTR(item); mvaddch(obj->o_pos.y, obj->o_pos.x, obj->o_type); detach(savitems, item); attach(lvl_obj, item); } /* * Place the traps (except for trading post) */ ntraps = 0; /* No traps yet */ if (levtype == NORMLEV) { if (rnd(10) < vlevel) { ntraps = rnd(vlevel/4)+1; if (ntraps > MAXTRAPS) ntraps = MAXTRAPS; i = ntraps; while (i--) { cnt = 0; do { rm = rnd_room(); if (rooms[rm].r_flags & ISTREAS) continue; rnd_pos(&rooms[rm], &stairs); } until (winat(stairs.y, stairs.x) == FLOOR || cnt++ > 5000); traps[i].tr_flags = 0; /* If we are at the bottom, we can't set a trap door */ if (level >= nfloors) ch = (char) rnd(7) + 1; else ch = (char) rnd(8); switch((int) ch) { case 0: ch = TRAPDOOR; when 1: ch = BEARTRAP; when 2: ch = SLEEPTRAP; when 3: ch = ARROWTRAP; when 4: ch = TELTRAP; when 5: ch = DARTTRAP; when 6: ch = POOL; traps[i].tr_flags = ISFOUND; when 7: ch = MAZETRAP; } addch(ch); traps[i].tr_type = ch; traps[i].tr_show = FLOOR; traps[i].tr_pos = stairs; } } } if (fresh) { /* A whole new picture */ /* * try to find a room for the hero. The objective here is to: * * --> don't put him in a treasure room * --> don't put him on an object * --> try not to put him next to the stairs */ cnt = 5000; do { rm = rnd_room(); if (rooms[rm].r_flags & ISTREAS) continue; rnd_pos(&rooms[rm], &hero); } until( cnt-- == 0 || (winat(hero.y, hero.x) == FLOOR && DISTANCE(hero.y, hero.x, stairs.y, stairs.x) > cnt/10)); } else { /* We're extending into an adjacent outside plane */ rm = 0; if (vert) { if (hero.y == 1) hero.y = lines - 3 - TERRASAVE; /* Top to bottom */ else hero.y = TERRASAVE + 1; /* Bottom to top */ } else { if (hero.x == 0) hero.x = cols - 1 - TERRASAVE; /* Right to left */ else hero.x = TERRASAVE; /* Left to right */ } } oldrp = &rooms[rm]; /* Set the current room */ player.t_oldpos = player.t_pos; /* Set the current position */ if (ISWEARING(R_AGGR) || (cur_misc[WEAR_JEWEL] != NULL && cur_misc[WEAR_JEWEL]->o_which == MM_JEWEL)) aggravate(TRUE, TRUE); /* * If player is moving up or above his deepest point, wake up any * non-uniques */ else if (level < cur_max) aggravate(FALSE, FALSE); light(&hero); wmove(cw, hero.y, hero.x); waddch(cw, PLAYER); if (level > cur_max) cur_max = level; status(TRUE); /* Do we sense any food on this level? */ if (cur_relic[SURTUR_RING]) quaff(P_FFIND, 0, 0, FALSE); } /* * Pick a room that is really there */ int rnd_room(void) { register int rm; if (levtype != NORMLEV) rm = 0; else do { rm = rnd(MAXROOMS); } while (rooms[rm].r_flags & ISGONE); return rm; } /* * put_things: * put potions and scrolls on this level * ltype: designates type of level to create */ void put_things(LEVTYPE ltype) { register int i, rm, cnt; register struct object *cur; register struct linked_list *item, *nextitem, *exitptr; int length, width; coord tp, *exit; /* * The only way to get new stuff is to go down into the dungeon. */ if (level <= cur_max) return; /* * There is a chance that there is a treasure room on this level */ if (ltype != MAZELEV && rnd(HARDER) < level - 10) { int j; register struct room *rp; /* Count the number of free spaces */ i = 0; /* 0 tries */ do { rp = &rooms[rnd_room()]; width = rp->r_max.y - 2; length = rp->r_max.x - 2; } until ((width*length >= MAXTREAS) || (i++ > MAXROOMS*4)); /* Mark the room as a treasure room */ rp->r_flags |= ISTREAS; /* Make all the doors secret doors */ for (exitptr = rp->r_exit; exitptr; exitptr = next(exitptr)) { exit = DOORPTR(exitptr); move(exit->y, exit->x); addch(SECRETDOOR); } /* * check to see if there are any monsters in room already */ for (item = mlist; item != NULL; item = nextitem) { register struct thing *tp; tp = THINGPTR(item); nextitem = next(item); if (rp == roomin(&tp->t_pos)) { /* * Don't let nice creatures be generated in a treasure * room. */ if ((player.t_ctype==C_PALADIN || player.t_ctype==C_RANGER) && off(*tp, ISMEAN)) { int index; if (on(*tp, ISUNIQUE)) index = tp->t_index; else index = -1; /* Get rid of the monster */ killed(item, FALSE, FALSE, FALSE); /* Restore uniques back in the table */ if (index != -1) monsters[index].m_normal = TRUE; continue; } turn_on(*tp, ISMEAN); turn_on(*tp, ISGUARDIAN); } } /* Put in the monsters and treasures */ for (j=1; jr_max.y-1; j++) for (i=1; ir_max.x-1; i++) { coord trp; trp.y = rp->r_pos.y+j; trp.x = rp->r_pos.x+i; /* Monsters */ if ((rnd(100) < (MAXTREAS*100)/(width*length)) && (mvwinch(mw, rp->r_pos.y+j, rp->r_pos.x+i) == ' ')) { register struct thing *tp; /* * Put it there and leave it asleep. Wake the monsters * when the player enters the room. Hopefully, all bases * are covered as far as the ways to get in. This way * cpu time is not wasted on the awake monsters that * can't get to the player anyway. * try not to put any UNIQUEs in a treasure room. * note that they may have put put in already by the * non-treasure room code. * also, try not to put ISMEAN monsters in a treasure * room as these are supposed to be non-hostile until * attacked. It also makes life simpler for the ranger * and paladin. */ while (TRUE) { item = new_item(sizeof *tp); /* Make a monster */ tp = THINGPTR(item); new_monster(item,randmonster(FALSE, TRUE),&trp,TRUE); if (on(*tp, HASFIRE)) { register struct linked_list *fire_item; fire_item = creat_item(); ldata(fire_item) = (char *) tp; attach(rp->r_fires, fire_item); rp->r_flags |= HASFIRE; } /* * only picky for these classes */ if (player.t_ctype!=C_RANGER&&player.t_ctype!=C_PALADIN) break; if (on(*tp, ISMEAN)) break; killed (item, FALSE, FALSE, FALSE); } if (on(*tp, ISUNIQUE)) { /* just in case */ carry_obj(tp, monsters[tp->t_index].m_carry); tp->t_no_move = movement(tp); } turn_on(*tp, ISGUARDIAN); } /* Treasures */ if ((rnd(100) < (MAXTREAS*100)/(width*length)) && (mvinch(rp->r_pos.y+j, rp->r_pos.x+i) == FLOOR)) { item = new_thing(ALL, TRUE); attach(lvl_obj, item); cur = OBJPTR(item); mvaddch(trp.y, trp.x, cur->o_type); cur->o_pos = trp; } } } /* * Do MAXOBJ attempts to put things on a level * put more things in a maze to entice player to navigate them */ for (i = 0; i < MAXOBJ; i++) if (ltype == MAZELEV || rnd(100) < 45) { /* * Pick a new object and link it in the list */ item = new_thing(ALL, TRUE); attach(lvl_obj, item); cur = OBJPTR(item); /* * Put it somewhere */ cnt = 0; do { rm = rnd_room(); rnd_pos(&rooms[rm], &tp); } until (winat(tp.y, tp.x) == FLOOR || cnt++ > 500); mvaddch(tp.y, tp.x, cur->o_type); cur->o_pos = tp; } }