Mercurial > hg > early-roguelike
diff arogue7/new_level.c @ 125:adfa37e67084
Import Advanced Rogue 7.7 from the Roguelike Restoration Project (r1490)
author | John "Elwin" Edwards |
---|---|
date | Fri, 08 May 2015 15:24:40 -0400 |
parents | |
children | f9ef86cf22b2 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/arogue7/new_level.c Fri May 08 15:24:40 2015 -0400 @@ -0,0 +1,628 @@ +/* + * 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 + +/* + * new_level: + * Dig and draw a new level + * + */ + +new_level(ltype) +LEVTYPE ltype; /* designates type of level to create */ +{ + 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; i<TERRASAVE; i++) { + if (vert) + for (j=1; j<cols-1; j++) { + if (top) { + cch = CCHAR( mvinch(i+2, j) ); + mvaddch(lines-6+i, j, cch); + } + else { + cch = CCHAR( mvinch(lines-4-i, j) ); + mvaddch(4-i, j, cch); + } + } + else + for (j=2; j<lines-3; j++) { + if (top) { + cch = CCHAR( mvinch(j, i+1) ); + mvaddch(j, cols-4+i, cch); + } + else { + cch = CCHAR( mvinch(j, cols-2-i) ); + mvaddch(j, 3-i, cch); + } + } + } + + if (vert) { + startx = deltax = 1; + if (top) { + starty = lines-4-TERRASAVE; + deltay = -1; + } + else { + starty = TERRASAVE + 2; + deltay = 1; + } + } + else { + starty = 2; + deltay = 1; + if (top) { + startx = cols-2-TERRASAVE; + deltax = -1; + } + else { + deltax = 1; + startx = TERRASAVE + 1; + } + } + + /* Check if any monsters should be saved */ + for (item = mlist; item != NULL; item = nitem) { + nitem = next(item); + tp = THINGPTR(item); + if (vert) { + if (top) { + if (tp->t_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, NULL, NULL, FALSE); +} + +/* + * Pick a room that is really there + */ + +rnd_room() +{ + 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 + */ + +put_things(ltype) +LEVTYPE ltype; /* designates type of level to create */ +{ + 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) { + register 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; j<rp->r_max.y-1; j++) + for (i=1; i<rp->r_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; + } +}