/* * sticks.c - Functions to implement the various sticks one might find * * 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. */ /* * Functions to implement the various sticks one might find * while wandering around the dungeon. */ #include "curses.h" #include #include #include "rogue.h" void drain(int ymin, int ymax, int xmin, int xmax); /* * zap a stick and see what happens */ void do_zap(struct thing *zapper, struct object *obj, coord *direction, int which, int flags) { register struct linked_list *item = NULL; register struct thing *tp; register int y, x, bonus; struct linked_list *nitem; struct object *nobj; bool cursed, blessed, is_player; char *mname = ""; cursed = flags & ISCURSED; blessed = flags & ISBLESSED; if (obj && obj->o_type != RELIC) { /* all relics are chargeless */ if (obj->o_charges < 1) { msg(nothing); return; } obj->o_charges--; } if (which == WS_WONDER) { switch (rnd(14)) { case 0: which = WS_ELECT; when 1: which = WS_FIRE; when 2: which = WS_COLD; when 3: which = WS_POLYMORPH; when 4: which = WS_MISSILE; when 5: which = WS_SLOW_M; when 6: which = WS_TELMON; when 7: which = WS_CANCEL; when 8: which = WS_CONFMON; when 9: which = WS_DISINTEGRATE; when 10: which = WS_PETRIFY; when 11: which = WS_PARALYZE; when 12: which = WS_MDEG; when 13: which = WS_FEAR; } if(ws_magic[which].mi_curse>0 && rnd(100)<=ws_magic[which].mi_curse){ cursed = TRUE; blessed = FALSE; } } tp = NULL; switch (which) { case WS_POLYMORPH: case WS_SLOW_M: case WS_TELMON: case WS_CANCEL: case WS_CONFMON: case WS_DISINTEGRATE: case WS_PETRIFY: case WS_PARALYZE: case WS_MDEG: case WS_FEAR: y = zapper->t_pos.y; x = zapper->t_pos.x; do { y += direction->y; x += direction->x; } while (shoot_ok(winat(y, x)) && !(y == hero.y && x == hero.x)); if (y == hero.y && x == hero.x) is_player = TRUE; else if (isalpha(mvwinch(mw, y, x))) { item = find_mons(y, x); tp = THINGPTR(item); runto(tp, &hero); turn_off(*tp, CANSURPRISE); mname = monster_name(tp); is_player = FALSE; /* The monster may not like being shot at */ if ((zapper == &player) && on(*tp, ISCHARMED) && save(VS_MAGIC, tp, 0)) { msg("The eyes of %s turn clear.", prname(mname, FALSE)); turn_off(*tp, ISCHARMED); mname = monster_name(tp); } } else { /* * if monster misses player because the player dodged then lessen * the chances he will use the wand again since the player appears * to be rather dextrous */ if (zapper != &player) zapper->t_wand = zapper->t_wand * 3 / 4; } } switch (which) { case WS_LIGHT: /* * Reddy Kilowat wand. Light up the room */ blue_light(blessed, cursed); when WS_DRAIN: /* * Take away 1/2 of hero's hit points, then take it away * evenly from the monsters in the room or next to hero * if he is in a passage (but leave the monsters alone * if the stick is cursed) */ if (pstats.s_hpt < 2) { msg("You are too weak to use it."); } else if (cursed) pstats.s_hpt /= 2; else drain(hero.y-1, hero.y+1, hero.x-1, hero.x+1); when WS_POLYMORPH: { register char oldch; register struct room *rp; register struct linked_list *pitem; coord delta; if (tp == NULL) break; if (save(VS_MAGIC, tp, 0)) { msg(nothing); break; } rp = roomin(&tp->t_pos); check_residue(tp); delta.x = x; delta.y = y; detach(mlist, item); oldch = tp->t_oldch; pitem = tp->t_pack; /* save his pack */ tp->t_pack = NULL; new_monster(item,rnd(NUMMONST-NUMUNIQUE-1)+1,&delta,FALSE); if (tp->t_pack != NULL) o_free_list (tp->t_pack); tp->t_pack = pitem; if (isalpha(mvwinch(cw, y, x))) mvwaddch(cw, y, x, tp->t_type); tp->t_oldch = oldch; /* * should the room light up? */ if (on(*tp, HASFIRE)) { if (rp) { 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; if (cansee(tp->t_pos.y,tp->t_pos.x) && next(rp->r_fires) == NULL) light(&hero); } } runto(tp, &hero); msg(terse ? "A new %s!" : "You have created a new %s!", monster_name(tp)); } when WS_PETRIFY: if (tp == NULL) break; if (save(VS_MAGIC, tp, 0)) { msg(nothing); break; } check_residue(tp); turn_on(*tp, ISSTONE); turn_on(*tp, NOSTONE); turn_off(*tp, ISRUN); turn_off(*tp, ISINVIS); turn_off(*tp, CANSURPRISE); turn_off(*tp, ISDISGUISE); tp->t_action = A_NIL; tp->t_no_move = 0; msg("%s is turned to stone!",prname(mname, TRUE)); when WS_TELMON: { register int rm; register struct room *rp; if (tp == NULL) break; if (save(VS_MAGIC, tp, 0)) { msg(nothing); break; } rp = NULL; check_residue(tp); tp->t_action = A_FREEZE; /* creature is disoriented */ tp->t_no_move = 2; if (cursed) { /* Teleport monster to player */ if ((y == (hero.y + direction->y)) && (x == (hero.x + direction->x))) msg(nothing); else { tp->t_pos.y = hero.y + direction->y; tp->t_pos.x = hero.x + direction->x; } } else if (blessed) { /* Get rid of monster */ killed(item, FALSE, TRUE, TRUE); return; } else { register int i=0; do { /* Move monster to another room */ rm = rnd_room(); rnd_pos(&rooms[rm], &tp->t_pos); }until(winat(tp->t_pos.y,tp->t_pos.x)==FLOOR ||i++>500); rp = &rooms[rm]; } /* Now move the monster */ if (isalpha(mvwinch(cw, y, x))) mvwaddch(cw, y, x, tp->t_oldch); mvwaddch(mw, y, x, ' '); mvwaddch(mw, tp->t_pos.y, tp->t_pos.x, tp->t_type); if (tp->t_pos.y != y || tp->t_pos.x != x) tp->t_oldch = CCHAR( mvwinch(cw, tp->t_pos.y, tp->t_pos.x) ); /* * check to see if room that creature appears in should * light up */ if (on(*tp, HASFIRE)) { if (rp) { 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; if(cansee(tp->t_pos.y, tp->t_pos.x) && next(rp->r_fires) == NULL) light(&hero); } } } when WS_CANCEL: if (tp == NULL) break; if (save(VS_MAGIC, tp, 0)) { msg(nothing); break; } check_residue(tp); tp->t_flags[0] &= CANC0MASK; tp->t_flags[1] &= CANC1MASK; tp->t_flags[2] &= CANC2MASK; tp->t_flags[3] &= CANC3MASK; tp->t_flags[4] &= CANC4MASK; tp->t_flags[5] &= CANC5MASK; tp->t_flags[6] &= CANC6MASK; tp->t_flags[7] &= CANC7MASK; tp->t_flags[8] &= CANC8MASK; tp->t_flags[9] &= CANC9MASK; tp->t_flags[10] &= CANCAMASK; tp->t_flags[11] &= CANCBMASK; tp->t_flags[12] &= CANCCMASK; tp->t_flags[13] &= CANCDMASK; tp->t_flags[14] &= CANCEMASK; tp->t_flags[15] &= CANCFMASK; when WS_MISSILE: { int dice; static struct object bolt = { MISSILE , {0, 0}, "", 0, "", "1d4 " , NULL, 0, WS_MISSILE, 50, 1 }; if (!obj) dice = zapper->t_stats.s_lvl; if (obj->o_type == RELIC) dice = 15; else if (EQUAL(ws_type[which], "staff")) dice = 10; else dice = 6; sprintf(bolt.o_hurldmg, "%dd4", dice); do_motion(&bolt, direction->y, direction->x, zapper); if (!hit_monster(unc(bolt.o_pos), &bolt, zapper)) msg("The missile vanishes with a puff of smoke"); } when WS_HIT: { register char ch; struct object strike; /* don't want to change sticks attributes */ direction->y += hero.y; direction->x += hero.x; ch = CCHAR( winat(direction->y, direction->x) ); if (isalpha(ch)) { strike = *obj; strike.o_hplus = 6; if (EQUAL(ws_type[which], "staff")) strncpy(strike.o_damage,"3d8",sizeof(strike.o_damage)); else strncpy(strike.o_damage,"2d8",sizeof(strike.o_damage)); fight(direction, &strike, FALSE); } } when WS_SLOW_M: if (is_player) { add_slow(); break; } if (tp == NULL) break; if (cursed) { if (on(*tp, ISSLOW)) turn_off(*tp, ISSLOW); else turn_on(*tp, ISHASTE); break; } if ((on(*tp,ISUNIQUE) && save(VS_MAGIC,tp,0)) || on(*tp,NOSLOW)) { msg(nothing); break; } else if (blessed) { turn_off(*tp, ISRUN); turn_on(*tp, ISHELD); } /* * always slow in case he breaks free of HOLD */ if (on(*tp, ISHASTE)) turn_off(*tp, ISHASTE); else turn_on(*tp, ISSLOW); when WS_CHARGE: if (ws_know[WS_CHARGE] != TRUE && obj) msg("This is a wand of charging."); nitem = get_item(pack, "charge", STICK, FALSE, FALSE); if (nitem != NULL) { nobj = OBJPTR(nitem); if ((++(nobj->o_charges) == 1) && (nobj->o_which == WS_HIT)) fix_stick(nobj); if (blessed) ++(nobj->o_charges); if (EQUAL(ws_type[nobj->o_which], "staff")) { if (nobj->o_charges > 100) nobj->o_charges = 100; } else { if (nobj->o_charges > 50) nobj->o_charges = 50; } } when WS_ELECT: shoot_bolt( zapper, zapper->t_pos, *direction, TRUE, D_BOLT, "lightning bolt", roll(zapper->t_stats.s_lvl,6)); when WS_FIRE: shoot_bolt( zapper, zapper->t_pos, *direction, TRUE, D_BOLT, "flame", roll(zapper->t_stats.s_lvl,6)); when WS_COLD: shoot_bolt( zapper, zapper->t_pos, *direction, TRUE, D_BOLT, "ice", roll(zapper->t_stats.s_lvl,6)); when WS_CONFMON: if (cursed || is_player) { if (!save(VS_WAND, &player, 0)) { dsrpt_player(); confus_player(); } else { if (zapper != &player) zapper->t_wand /= 2; msg(nothing); } } else { if (tp == NULL) break; if (save(VS_MAGIC, tp, 0) || on(*tp, ISCLEAR)) msg(nothing); else turn_on (*tp, ISHUH); } when WS_PARALYZE: if (is_player || cursed) { if ((obj && obj->o_type==RELIC) || !save(VS_WAND, &player, 0)){ player.t_no_move += 2 * movement(&player) * FREEZETIME; player.t_action = A_FREEZE; msg("You can't move."); } else { if (zapper != &player) zapper->t_wand /= 2; msg(nothing); } } else { if (tp == NULL) break; bonus = 0; if (blessed) bonus = -3; if (((obj && obj->o_type==RELIC) || !save(VS_WAND,tp,bonus)) && off(*tp, NOPARALYZE)) { tp->t_no_move += 2 * movement(tp) * FREEZETIME; tp->t_action = A_FREEZE; } else { msg(nothing); } } when WS_FEAR: if (is_player) { if (!on(player, ISFLEE) || ISWEARING(R_HEROISM) || save(VS_WAND, &player, 0)) { msg(nothing); zapper->t_wand /= 2; } else { turn_on(player, ISFLEE); player.t_dest = &zapper->t_pos; msg("The sight of %s terrifies you.", prname(mname, FALSE)); } break; } if (tp == NULL) break; bonus = 0; if (blessed) bonus = -3; if(save(VS_WAND, tp,bonus) || on(*tp,ISUNDEAD) || on(*tp,NOFEAR)){ msg(nothing); break; } turn_on(*tp, ISFLEE); turn_on(*tp, WASTURNED); /* Stop it from attacking us */ dsrpt_monster(tp, TRUE, cansee(tp->t_pos.y, tp->t_pos.x)); /* If monster was suffocating, stop it */ if (on(*tp, DIDSUFFOCATE)) { turn_off(*tp, DIDSUFFOCATE); extinguish(suffocate); } /* If monster held us, stop it */ if (on(*tp, DIDHOLD) && (--hold_count == 0)) turn_off(player, ISHELD); turn_off(*tp, DIDHOLD); /* It is okay to turn tail */ tp->t_oldpos = tp->t_pos; when WS_MDEG: if (is_player) { if (save(VS_WAND, &player, 0)) { msg (nothing); zapper->t_wand /= 2; break; } pstats.s_hpt /= 2; if (pstats.s_hpt <= 0) { msg("Your life has been sucked from you -- More --"); wait_for(' '); death(zapper->t_index); } else msg("You feel a great drain on your system"); } if (tp == NULL) break; if (cursed) { tp->t_stats.s_hpt *= 2; msg("%s appears to be stronger now!", prname(mname, TRUE)); } else if (on(*tp, ISUNIQUE) && save(VS_WAND, tp, 0)) msg (nothing); else { tp->t_stats.s_hpt /= 2; msg("%s appears to be weaker now", prname(mname, TRUE)); } if (tp->t_stats.s_hpt < 1) killed(item, TRUE, TRUE, TRUE); when WS_DISINTEGRATE: if (tp == NULL) break; if (cursed) { register int m1, m2; coord mp; struct linked_list *titem; char ch; struct thing *th; if (on(*tp, ISUNIQUE)) { msg (nothing); break; } for (m1=tp->t_pos.x-1 ; m1 <= tp->t_pos.x+1 ; m1++) { for(m2=tp->t_pos.y-1 ; m2<=tp->t_pos.y+1 ; m2++) { if (m1 == hero.x && m2 == hero.y) continue; ch = CCHAR( winat(m2,m1) ); if (shoot_ok(ch)) { mp.x = m1; /* create it */ mp.y = m2; titem = new_item(sizeof(struct thing)); new_monster(titem,(short)tp->t_index,&mp,FALSE); th = THINGPTR(titem); turn_on (*th, ISMEAN); runto(th,&hero); if (on(*th, HASFIRE)) { register struct room *rp; rp = roomin(&th->t_pos); if (rp) { register struct linked_list *fire_item; fire_item = creat_item(); ldata(fire_item) = (char *) th; attach(rp->r_fires, fire_item); rp->r_flags |= HASFIRE; if (cansee(th->t_pos.y, th->t_pos.x) && next(rp->r_fires) == NULL) light(&hero); } } } } } } else { /* if its a UNIQUE it might still live */ if (on(*tp, ISUNIQUE) && save(VS_MAGIC, tp, 0)) { tp->t_stats.s_hpt /= 2; if (tp->t_stats.s_hpt < 1) { killed(item, FALSE, TRUE, TRUE); msg("You have disintegrated %s", prname(mname, FALSE)); } else { msg("%s appears wounded", prname(mname, TRUE)); } } else { msg("You have disintegrated %s", prname(mname, FALSE)); killed (item, FALSE, TRUE, TRUE); } } when WS_CURING: if (cursed) { if (!save(VS_POISON, &player, 0)) { msg("You feel extremely sick now"); pstats.s_hpt /=2; if (pstats.s_hpt == 0) death (D_POISON); } if (!save(VS_WAND, &player, 0) && !ISWEARING(R_HEALTH)) { turn_on(player, HASDISEASE); turn_on(player, HASINFEST); turn_on(player, DOROT); fuse(cure_disease, NULL, roll(HEALTIME,SICKTIME), AFTER); infest_dam++; } else msg("You fell momentarily sick"); } else { if (on(player, HASDISEASE) || on(player, HASINFEST)) { extinguish(cure_disease); turn_off(player, HASINFEST); infest_dam = 0; cure_disease(); /* this prints message */ } if (on(player, DOROT)) { msg("You feel your skin returning to normal."); turn_off(player, DOROT); } pstats.s_hpt += roll(pstats.s_lvl, blessed ? 6 : 4); if (pstats.s_hpt > max_stats.s_hpt) pstats.s_hpt = max_stats.s_hpt; msg("You begin to feel %sbetter.", blessed ? "much " : ""); } otherwise: msg("What a bizarre schtick!"); } } /* * drain: * Do drain hit points from player shtick */ void drain(int ymin, int ymax, int xmin, int xmax) { register int i, j, count; register struct thing *ick; register struct linked_list *item; /* * First count how many things we need to spread the hit points among */ count = 0; for (i = ymin; i <= ymax; i++) { if (i < 1 || i > lines - 3) continue; for (j = xmin; j <= xmax; j++) { if (j < 0 || j > cols - 1) continue; if (isalpha(mvwinch(mw, i, j))) count++; } } if (count == 0) { msg("You have a tingling feeling"); return; } count = pstats.s_hpt / count; pstats.s_hpt /= 2; /* * Now zot all of the monsters */ for (i = ymin; i <= ymax; i++) { if (i < 1 || i > lines - 3) continue; for (j = xmin; j <= xmax; j++) { if (j < 0 || j > cols - 1) continue; if (isalpha(mvwinch(mw, i, j)) && ((item = find_mons(i, j)) != NULL)) { ick = THINGPTR(item); if (on(*ick, ISUNIQUE) && save(VS_MAGIC, ick, 0)) ick->t_stats.s_hpt -= count / 2; else ick->t_stats.s_hpt -= count; if (ick->t_stats.s_hpt < 1) killed(item, cansee(i,j)&&(!on(*ick,ISINVIS)||on(player,CANSEE)), TRUE, TRUE); else { runto(ick, &hero); /* * The monster may not like being shot at. Since the * shot is not aimed directly at the monster, we will * give him a poorer save. */ if (on(*ick, ISCHARMED) && save(VS_MAGIC, ick, -2)) { msg("The eyes of %s turn clear.", prname(monster_name(ick), FALSE)); turn_off(*ick, ISCHARMED); } if (cansee(i,j) && (!on(*ick,ISINVIS)||on(player,CANSEE))) msg("%s appears wounded", prname(monster_name(ick), TRUE)); } } } } } /* * initialize a stick */ void fix_stick(struct object *cur) { if (EQUAL(ws_type[cur->o_which], "staff")) { cur->o_weight = 100; cur->o_charges = 5 + rnd(10); strncpy(cur->o_damage, "2d3", sizeof(cur->o_damage)); cur->o_hplus = 1; cur->o_dplus = 0; switch (cur->o_which) { case WS_HIT: cur->o_hplus = 3; cur->o_dplus = 3; strncpy(cur->o_damage, "2d8", sizeof(cur->o_damage)); when WS_LIGHT: cur->o_charges = 20 + rnd(10); } } else { strncpy(cur->o_damage, "1d3", sizeof(cur->o_damage)); cur->o_weight = 60; cur->o_hplus = 1; cur->o_dplus = 0; cur->o_charges = 3 + rnd(5); switch (cur->o_which) { case WS_HIT: cur->o_hplus = 3; cur->o_dplus = 3; strncpy(cur->o_damage, "1d8", sizeof(cur->o_damage)); when WS_LIGHT: cur->o_charges = 10 + rnd(10); } } strncpy(cur->o_hurldmg, "1d1", sizeof(cur->o_hurldmg)); } /* * Use the wand that our monster is wielding. */ void m_use_wand(struct thing *monster) { register struct object *obj; /* Make sure we really have it */ if (monster->t_using) obj = OBJPTR(monster->t_using); else { debug("Stick not set!"); monster->t_action = A_NIL; return; } if (obj->o_type != STICK) { debug("Stick not selected!"); monster->t_action = A_NIL; return; } /* * shoot the stick! * assume all blessed sticks are normal for now. * Note that we don't get here if the wand is cursed. */ msg("%s points a %s at you!", prname(monster_name(monster), TRUE), ws_type[obj->o_which]); do_zap(monster, obj, &monster->t_newpos, obj->o_which, 0); monster->t_wand /= 2; /* chance lowers with each use */ } /* * type: type of item, NULL means stick * which: which item */ bool need_dir(int type, int which) { if (type == STICK || type == 0) { switch (which) { case WS_LIGHT: case WS_DRAIN: case WS_CHARGE: case WS_CURING: return(FALSE); default: return(TRUE); } } else if (type == RELIC) { switch (which) { case MING_STAFF: case ASMO_ROD: case EMORI_CLOAK: return(TRUE); default: return(FALSE); } } return (FALSE); /* hope we don't get here */ } /* * let the player zap a stick and see what happens */ bool player_zap(int which, int flag) { register struct linked_list *item; register struct object *obj; obj = NULL; if (which == 0) { /* This is a stick. It takes 2 movement periods to zap it */ if (player.t_action != C_ZAP) { if ((item = get_item(pack,"zap with",ZAPPABLE,FALSE,FALSE)) == NULL) return(FALSE); obj = OBJPTR(item); if (need_dir(obj->o_type, obj->o_which)) { if (!get_dir(&player.t_newpos)) return(FALSE); } player.t_using = item; /* Remember what it is */ player.t_action = C_ZAP; /* We are quaffing */ player.t_no_move = 2 * movement(&player); return(TRUE); } item = player.t_using; /* We've waited our time, let's shoot 'em up! */ player.t_using = NULL; player.t_action = A_NIL; obj = OBJPTR(item); /* Handle relics specially here */ if (obj->o_type == RELIC) { switch (obj->o_which) { case ORCUS_WAND: msg(nothing); return(TRUE); when MING_STAFF: which = WS_MISSILE; when EMORI_CLOAK: which = WS_PARALYZE; obj->o_charges = 0; /* one zap/day(whatever that is) */ fuse(cloak_charge, obj, CLOAK_TIME, AFTER); when ASMO_ROD: switch (rnd(3)) { case 0: which = WS_ELECT; when 1: which = WS_COLD; otherwise: which = WS_FIRE; } } } else { which = obj->o_which; ws_know[which] = TRUE; flag = obj->o_flags; } } do_zap(&player, obj, &player.t_newpos, which, flag); return(TRUE); } /* * shoot_bolt fires a bolt from the given starting point in the * given direction */ void shoot_bolt(struct thing *shooter, coord start, coord dir, bool get_points, short reason, char *name, int damage) { register char dirch, ch; register bool used, change; register short y, x, bounces; coord pos; struct linked_list *target=NULL; struct { coord place; char oldch; } spotpos[BOLT_LENGTH]; switch (dir.y + dir.x) { case 0: dirch = '/'; when 1: case -1: dirch = (dir.y == 0 ? '-' : '|'); when 2: case -2: dirch = '\\'; } pos.y = start.y + dir.y; pos.x = start.x + dir.x; used = FALSE; change = FALSE; bounces = 0; /* No bounces yet */ for (y = 0; y < BOLT_LENGTH && !used; y++) { ch = CCHAR( winat(pos.y, pos.x) ); spotpos[y].place = pos; spotpos[y].oldch = CCHAR( mvwinch(cw, pos.y, pos.x) ); /* Are we at hero? */ if (ce(pos, hero)) goto at_hero; switch (ch) { case SECRETDOOR: case '|': case '-': case ' ': if (dirch == '-' || dirch == '|') { dir.y = -dir.y; dir.x = -dir.x; } else { char chx = CCHAR( mvinch(pos.y-dir.y, pos.x) ), chy = CCHAR( mvinch(pos.y, pos.x-dir.x) ); bool anychange = FALSE; /* Did we change anthing */ if (chy == WALL || chy == SECRETDOOR || chy == '-' || chy == '|') { dir.y = -dir.y; change ^= TRUE; /* Change at least one direction */ anychange = TRUE; } if (chx == WALL || chx == SECRETDOOR || chx == '-' || chx == '|') { dir.x = -dir.x; change ^= TRUE; /* Change at least one direction */ anychange = TRUE; } /* If we didn't make any change, make both changes */ if (!anychange) { dir.x = -dir.x; dir.y = -dir.y; } } /* Do we change how the bolt looks? */ if (change) { change = FALSE; if (dirch == '\\') dirch = '/'; else if (dirch == '/') dirch = '\\'; } y--; /* The bounce doesn't count as using up the bolt */ /* Make sure we aren't in an infinite bounce */ if (++bounces > BOLT_LENGTH) used = TRUE; msg("The %s bounces", name); break; default: if (isalpha(ch)) { register struct linked_list *item; register struct thing *tp; register char *mname; bool see_monster = cansee(pos.y, pos.x); item = find_mons(unc(pos)); tp = THINGPTR(item); mname = monster_name(tp); /* * If our prey shot this, let's record the fact that * he can shoot, regardless of whether he hits us. */ if ((tp->t_dest != NULL) && ce(*tp->t_dest, shooter->t_pos)) tp->t_wasshot = TRUE; if (!save(VS_BREATH, tp, -(shooter->t_stats.s_lvl/10))) { if (see_monster) { if (on(*tp, ISDISGUISE) && (tp->t_type != tp->t_disguise)) { msg("Wait! That's a %s!", mname); turn_off(*tp, ISDISGUISE); } turn_off(*tp, CANSURPRISE); msg("The %s hits %s", name, prname(mname, FALSE)); } /* Should we start to chase the shooter? */ if (shooter != &player && shooter != tp && shooter->t_index != tp->t_index && (tp->t_dest == NULL || rnd(100) < 25)) { /* * If we're intelligent enough to realize that this * is a friendly monster, we will attack the hero * instead. */ if (on(*shooter, ISFRIENDLY) && roll(3,6) < tp->t_stats.s_intel) runto(tp, &hero); /* Otherwise, let's chase the monster */ else runto(tp, &shooter->t_pos); } else if (shooter == &player) { runto(tp, &hero); /* * If the player shot a charmed monster, it may * not like being shot at. */ if (on(*tp, ISCHARMED) && save(VS_MAGIC, tp, 0)) { msg("The eyes of %s turn clear.", prname(mname, FALSE)); turn_off(*tp, ISCHARMED); mname = monster_name(tp); } } /* * Let the defender know that the attacker has * missiles! */ if (ce(*tp->t_dest, shooter->t_pos)) tp->t_wasshot = TRUE; used = TRUE; /* Hit the monster -- does it do anything? */ if ((EQUAL(name,"ice") && (on(*tp, NOCOLD) || on(*tp, ISUNDEAD))) || (EQUAL(name,"flame") && on(*tp, NOFIRE)) || (EQUAL(name,"acid") && on(*tp, NOACID)) || (EQUAL(name,"lightning bolt")&& on(*tp,NOBOLT)) || (EQUAL(name,"nerve gas") &&on(*tp,NOPARALYZE))|| (EQUAL(name,"sleeping gas") && (on(*tp, NOSLEEP) || on(*tp, ISUNDEAD))) || (EQUAL(name,"slow gas") && on(*tp,NOSLOW)) || (EQUAL(name,"fear gas") && on(*tp,NOFEAR)) || (EQUAL(name,"confusion gas") && on(*tp,ISCLEAR)) || (EQUAL(name,"chlorine gas") && on(*tp,NOGAS))) { if (see_monster) msg("The %s has no effect on %s.", name, prname(mname, FALSE)); } else { bool see_him; see_him = off(player, ISBLIND) && cansee(unc(tp->t_pos)) && (off(*tp, ISINVIS) || on(player, CANSEE)) && (off(*tp, ISSHADOW)|| on(player, CANSEE)) && (off(*tp, CANSURPRISE)||ISWEARING(R_ALERT)); /* Did a spell get disrupted? */ dsrpt_monster(tp, FALSE, see_him); /* * Check for gas with special effects */ if (EQUAL(name, "nerve gas")) { tp->t_no_move = movement(tp) * FREEZETIME; tp->t_action = A_FREEZE; } else if (EQUAL(name, "sleeping gas")) { tp->t_no_move = movement(tp) * SLEEPTIME; tp->t_action = A_FREEZE; } else if (EQUAL(name, "slow gas")) { if (on(*tp, ISHASTE)) turn_off(*tp, ISHASTE); else turn_on(*tp, ISSLOW); } else if (EQUAL(name, "fear gas")) { turn_on(*tp, ISFLEE); tp->t_dest = &hero; /* It is okay to turn tail */ tp->t_oldpos = tp->t_pos; } else if (EQUAL(name, "confusion gas")) { turn_on(*tp, ISHUH); tp->t_dest = &hero; } else if ((EQUAL(name, "lightning bolt")) && on(*tp, BOLTDIVIDE)) { if (creat_mons(tp, tp->t_index, FALSE)) { if (see_monster) msg("The %s divides %s.", name,prname(mname, FALSE)); light(&hero); } else if (see_monster) msg("The %s has no effect on %s.", name, prname(mname, FALSE)); } else { if (save(VS_BREATH, tp, -(shooter->t_stats.s_lvl/10))) damage /= 2; /* The poor fellow got killed! */ if ((tp->t_stats.s_hpt -= damage) <= 0) { if (see_monster) msg("The %s kills %s", name, prname(mname, FALSE)); else msg("You hear a faint groan in the distance"); /* * Instead of calling killed() here, we * will record that the monster was killed * and call it at the end of the routine, * after we restore what was under the bolt. * We have to do this because in the case * of a bolt that first misses the monster * and then gets it on the bounce. If we * call killed here, the 'missed' space in * spotpos puts the monster back on the * screen */ target = item; } else { /* Not dead, so just scream */ if (!see_monster) msg("You hear a scream in the distance"); } } } } else if (isalpha(show(pos.y, pos.x))) { if (see_monster) { if (terse) msg("%s misses", name); else msg("The %s whizzes past %s", name, prname(mname, FALSE)); } if (get_points) runto(tp, &hero); } } else if (pos.y == hero.y && pos.x == hero.x) { at_hero: if (!save(VS_BREATH, &player, -(shooter->t_stats.s_lvl/10))){ if (terse) msg("The %s hits you", name); else msg("You are hit by the %s", name); used = TRUE; /* * The Amulet of Yendor protects against all "breath" * * The following two if statements could be combined * into one, but it makes the compiler barf, so split * it up */ if (cur_relic[YENDOR_AMULET] || (EQUAL(name,"chlorine gas")&&on(player, NOGAS)) || (EQUAL(name,"sleeping gas")&&ISWEARING(R_ALERT))){ msg("The %s has no affect", name); } else if((EQUAL(name, "flame") && on(player, NOFIRE)) || (EQUAL(name, "ice") && on(player, NOCOLD)) || (EQUAL(name,"lightning bolt")&& on(player,NOBOLT)) || (EQUAL(name,"fear gas")&&ISWEARING(R_HEROISM))){ msg("The %s has no affect", name); } else { dsrpt_player(); /* * Check for gas with special effects */ if (EQUAL(name, "nerve gas")) { msg("The nerve gas paralyzes you."); player.t_no_move += movement(&player) * FREEZETIME; player.t_action = A_FREEZE; } else if (EQUAL(name, "sleeping gas")) { msg("The sleeping gas puts you to sleep."); player.t_no_move += movement(&player) * SLEEPTIME; player.t_action = A_FREEZE; } else if (EQUAL(name, "confusion gas")) { if (off(player, ISCLEAR)) { if (on(player, ISHUH)) lengthen(unconfuse, rnd(20)+HUHDURATION); else { turn_on(player, ISHUH); fuse(unconfuse, NULL, rnd(20)+HUHDURATION, AFTER); msg( "The confusion gas has confused you."); } } else msg("You feel dizzy for a moment, but it quickly passes."); } else if (EQUAL(name, "slow gas")) { add_slow(); } else if (EQUAL(name, "fear gas")) { turn_on(player, ISFLEE); player.t_dest = &shooter->t_pos; msg("The fear gas terrifies you."); } else { if (EQUAL(name, "acid") && cur_armor != NULL && !(cur_armor->o_flags & ISPROT) && !save(VS_BREATH, &player, -2) && cur_armor->o_ac < pstats.s_arm+1) { msg("Your armor corrodes from the acid"); cur_armor->o_ac++; } if (save(VS_BREATH, &player, -(shooter->t_stats.s_lvl/10))) damage /= 2; if ((pstats.s_hpt -= damage) <= 0) death(reason); } } } else msg("The %s whizzes by you", name); } mvwaddch(cw, pos.y, pos.x, dirch); draw(cw); } pos.y += dir.y; pos.x += dir.x; } /* Restore what was under the bolt */ for (x = y - 1; x >= 0; x--) mvwaddch(cw, spotpos[x].place.y, spotpos[x].place.x, spotpos[x].oldch); /* If we killed something, do so now. This will also blank the monster. */ if (target) killed(target, FALSE, get_points, TRUE); return; }