Mercurial > hg > early-roguelike
view arogue7/misc.c @ 306:057c5114e244
Super-Rogue: fix some out-of-range constants.
Constants K_ARROW etc., for causes of death other than monsters, are in
the 240-255 range. They were often passed to functions taking char,
which is usually signed, making the values out of range.
The function declarations have been changed to unsigned char, which is
also the type used by the scoreboard code.
author | John "Elwin" Edwards |
---|---|
date | Sat, 17 Apr 2021 15:41:12 -0400 |
parents | e52a8a7ad4c5 |
children |
line wrap: on
line source
/* * misc.c - routines dealing specifically with miscellaneous magic * * 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 <stdlib.h> #include <ctype.h> #include <string.h> #include "rogue.h" #ifdef PC7300 #include "menu.h" #endif /* * routines dealing specifically with miscellaneous magic */ /* * changeclass: * Change the player's class to the specified one. */ void changeclass(int newclass) { if (newclass == player.t_ctype) { msg("You feel more skillful."); raise_level(); } else { /* * reset his class and then use check_level to reset hit * points and the right level for his exp pts * drop exp pts by 10% */ long save; msg("You feel like a whole new person!"); /* * if he becomes a thief he has to have leather armor */ if ((newclass == C_THIEF || newclass == C_ASSASIN) && cur_armor != NULL && cur_armor->o_which != LEATHER && cur_armor->o_which != STUDDED_LEATHER ) cur_armor->o_which = STUDDED_LEATHER; /* * if he becomes a monk he can't wear armor */ if (newclass == C_MONK && cur_armor != NULL) { cur_armor->o_ac = armors[cur_armor->o_which].a_class - cur_armor->o_ac; cur_armor->o_type = MM; cur_armor->o_which = MM_PROTECT; cur_armor->o_flags &= ~(ISPROT | ISKNOW | ISMETAL); cur_misc[WEAR_CLOAK] = cur_armor; cur_armor = NULL; } /* * if he used to be a spell caster of some sort, kill the fuse */ if (player.t_ctype == C_MAGICIAN || player.t_ctype == C_RANGER) extinguish(spell_recovery); if (player.t_ctype == C_DRUID || player.t_ctype == C_RANGER) extinguish(chant_recovery); if ((player.t_ctype == C_CLERIC || player.t_ctype == C_PALADIN) && !cur_relic[HEIL_ANKH]) extinguish(prayer_recovery); /* * if he becomes a spell caster of some kind, give him a fuse */ if (newclass == C_MAGICIAN || newclass == C_RANGER) fuse(spell_recovery, NULL, SPELLTIME, AFTER); if (newclass == C_DRUID || newclass == C_RANGER) fuse(chant_recovery, NULL, SPELLTIME, AFTER); if ((newclass==C_CLERIC || newclass==C_PALADIN) && !cur_misc[HEIL_ANKH]) fuse(prayer_recovery, NULL, SPELLTIME, AFTER); /* * if he's changing from a fighter then may have to change * his sword since only fighter can use two-handed * and bastard swords */ if ((player.t_ctype == C_FIGHTER || player.t_ctype == C_RANGER || player.t_ctype == C_PALADIN) && cur_weapon != NULL && cur_weapon->o_type == WEAPON && (cur_weapon->o_which== BASWORD || cur_weapon->o_which== TWOSWORD ) && !(newclass == C_FIGHTER || newclass == C_RANGER || newclass == C_PALADIN) && !(newclass == C_ASSASIN && cur_weapon->o_which == BASWORD)) cur_weapon->o_which = SWORD; /* * if he was a thief then take out the trap_look() daemon */ if (player.t_ctype == C_THIEF || player.t_ctype == C_MONK || player.t_ctype == C_ASSASIN) kill_daemon(trap_look); /* * if he becomes a thief then add the trap_look() daemon */ if (newclass == C_THIEF || newclass == C_ASSASIN || newclass == C_MONK) start_daemon(trap_look, NULL, AFTER); char_type = player.t_ctype = newclass; save = pstats.s_hpt; max_stats.s_hpt = pstats.s_hpt = 0; max_stats.s_lvl = pstats.s_lvl = 0; max_stats.s_lvladj = pstats.s_lvladj = 0; max_stats.s_exp = pstats.s_exp -= pstats.s_exp/10; check_level(); if (pstats.s_hpt > save) /* don't add to current hits */ pstats.s_hpt = save; } } /* * Use the relic that our monster is wielding. */ void m_use_relic(struct thing *monster) { register struct object *obj; /* Make sure we really have it */ if (monster->t_using) obj = OBJPTR(monster->t_using); else { debug("Relic not set!"); monster->t_action = A_NIL; return; } /* Now let's see what we're using */ if (obj->o_type == RELIC) switch (obj->o_which) { case MING_STAFF: { static struct object missile = { MISSILE, {0,0}, "", 0, "", "0d4 " , NULL, 0, WS_MISSILE, 100, 1 }; debug("Firing Ming's staff"); sprintf(missile.o_hurldmg, "%dd4", monster->t_stats.s_lvl); do_motion(&missile, monster->t_newpos.y, monster->t_newpos.x, monster); hit_monster(unc(missile.o_pos), &missile, monster); monster->t_artifact = monster->t_artifact * 4 / 5; } when EMORI_CLOAK: debug("stunning with Emori's cloak"); do_zap(monster, obj, &monster->t_newpos, WS_PARALYZE, 0); obj->o_charges = 0; when ASMO_ROD: { char *name; switch (rnd(3)) { /* Select a function */ case 0: name = "lightning bolt"; when 1: name = "flame"; otherwise: name = "ice"; } shoot_bolt( monster, monster->t_pos, monster->t_newpos, FALSE, monster->t_index, name, roll(monster->t_stats.s_lvl,6)); monster->t_artifact /= 2; } when BRIAN_MANDOLIN: /* Make sure the defendant is still around */ if (DISTANCE(monster->t_pos.y, monster->t_pos.x, hero.y, hero.x) < 25) { if (!save(VS_MAGIC, &player, -4) && !ISWEARING(R_ALERT)) { msg("Some beautiful music enthralls you."); player.t_no_move += movement(&player) * FREEZETIME; player.t_action = A_FREEZE; monster->t_artifact = monster->t_artifact * 2 / 3; } else { msg("You wince at a sour note."); monster->t_artifact /= 3; } } when GERYON_HORN: /* Make sure the defendant is still around */ if (DISTANCE(monster->t_pos.y, monster->t_pos.x, hero.y, hero.x) < 25) { if (!ISWEARING(R_HEROISM) && !save(VS_MAGIC, &player, -4)) { turn_on(player, ISFLEE); player.t_dest = &monster->t_pos; msg("A shrill blast terrifies you."); monster->t_artifact = monster->t_artifact * 3 / 4; } else { msg("A shrill blast sends chills up your spine."); monster->t_artifact /= 3; } } otherwise: /* Unknown RELIC! */ debug("Unknown wielded relic %d", obj->o_which); } else debug("Declared relic is %d", obj->o_type); turn_off(*monster, CANSURPRISE); /* Reset the monsters actions */ monster->t_action = A_NIL; monster->t_using = NULL; } /* * add something to the contents of something else * bag: the holder of the items * item: the item to put inside */ void put_contents(struct object *bag, struct linked_list *item) { register struct linked_list *titem; register struct object *tobj; bag->o_ac++; tobj = OBJPTR(item); for (titem = bag->contents; titem != NULL; titem = next(titem)) { if ((OBJPTR(titem))->o_which == tobj->o_which) break; } if (titem == NULL) { /* if not a duplicate put at beginning */ attach(bag->contents, item); } else { item->l_prev = titem; item->l_next = titem->l_next; if (next(titem) != NULL) (titem->l_next)->l_prev = item; titem->l_next = item; } } /* * remove something from something else * bag: the holder of the items */ void take_contents(struct object *bag, struct linked_list *item) { if (bag->o_ac <= 0) { msg("Nothing to take out"); return; } bag->o_ac--; detach(bag->contents, item); if (!add_pack(item, FALSE, NULL)) put_contents(bag, item); } void do_bag(struct linked_list *item) { register struct linked_list *titem = NULL; register struct object *obj, *tobj; bool doit = TRUE; obj = OBJPTR(item); while (doit) { msg("What do you want to do? (* for a list): "); mpos = 0; switch (readchar()) { case EOF: case ESCAPE: msg (""); doit = FALSE; when '1': inventory(obj->contents, ALL); when '2': if (obj->o_ac >= MAXCONTENTS) { msg("the %s is full", m_magic[obj->o_which].mi_name); break; } switch (obj->o_which) { case MM_BEAKER: titem = get_item(pack, "put in", POTION, FALSE, FALSE); when MM_BOOK: titem = get_item(pack, "put in", SCROLL, FALSE, FALSE); } if (titem == NULL) break; detach(pack, titem); inpack--; put_contents(obj, titem); when '3': titem = get_item(obj->contents,"take out",ALL,FALSE,FALSE); if (titem == NULL) break; take_contents(obj, titem); when '4': switch (obj->o_which) { case MM_BEAKER: titem = get_item(obj->contents,"quaff",ALL,FALSE,FALSE); if (titem == NULL) break; tobj = OBJPTR(titem); obj->o_ac--; detach(obj->contents, titem); quaff(tobj->o_which, tobj->o_kind, tobj->o_flags, TRUE); if (p_know[tobj->o_which] && p_guess[tobj->o_which]) { free(p_guess[tobj->o_which]); p_guess[tobj->o_which] = NULL; } else if (!p_know[tobj->o_which] && askme && (tobj->o_flags & ISKNOW) == 0 && (tobj->o_flags & ISPOST) == 0 && p_guess[tobj->o_which] == NULL) { nameitem(titem, FALSE); } o_discard(titem); when MM_BOOK: if (on(player, ISBLIND)) { msg("You can't see to read anything"); break; } titem = get_item(obj->contents,"read",ALL,FALSE,FALSE); if (titem == NULL) break; tobj = OBJPTR(titem); obj->o_ac--; detach(obj->contents, titem); read_scroll(tobj->o_which, tobj->o_flags & (ISCURSED|ISBLESSED), TRUE); if (s_know[tobj->o_which] && s_guess[tobj->o_which]) { free(s_guess[tobj->o_which]); s_guess[tobj->o_which] = NULL; } else if (!s_know[tobj->o_which] && askme && (tobj->o_flags & ISKNOW) == 0 && (tobj->o_flags & ISPOST) == 0 && s_guess[tobj->o_which] == NULL) { nameitem(titem, FALSE); } o_discard(titem); } doit = FALSE; otherwise: wclear(hw); touchwin(hw); mvwaddstr(hw,0,0,"The following operations are available:"); mvwaddstr(hw,2,0,"[1]\tInventory\n"); wprintw(hw,"[2]\tPut something in the %s\n", m_magic[obj->o_which].mi_name); wprintw(hw,"[3]\tTake something out of the %s\n", m_magic[obj->o_which].mi_name); switch(obj->o_which) { case MM_BEAKER: waddstr(hw,"[4]\tQuaff a potion\n"); when MM_BOOK: waddstr(hw,"[4]\tRead a scroll\n"); } waddstr(hw,"[ESC]\tLeave this menu\n"); mvwaddstr(hw, lines-1, 0, spacemsg); draw(hw); wait_for (' '); clearok(cw, TRUE); touchwin(cw); } } } /* who: Kind of monster to panic (all if who is NULL) */ void do_panic(int who) { register int x,y; register struct linked_list *mon, *item; register struct thing *th; for (x = hero.x-2; x <= hero.x+2; x++) { for (y = hero.y-2; y <= hero.y+2; y++) { if (y < 1 || x < 0 || y > lines - 3 || x > cols - 1) continue; if (isalpha(mvwinch(mw, y, x))) { if ((mon = find_mons(y, x)) != NULL) { th = THINGPTR(mon); /* Is this the right kind of monster to panic? */ if (who && th->t_index != who) continue; if (who || (!on(*th, ISUNDEAD) && !save(VS_MAGIC, th, 0) && off(*th, WASTURNED))) { msg("%s %s.", prname(monster_name(th), TRUE), terse ? "panics" : "turns to run in panic"); turn_on(*th, ISFLEE); turn_on(*th, WASTURNED); turn_off(*th, CANSURPRISE); /* Disrupt what it was doing */ dsrpt_monster(th, TRUE, TRUE); /* If monster was suffocating, stop it */ if (on(*th, DIDSUFFOCATE)) { turn_off(*th, DIDSUFFOCATE); extinguish(suffocate); } /* If monster held us, stop it */ if (on(*th, DIDHOLD) && (--hold_count == 0)) turn_off(player, ISHELD); turn_off(*th, DIDHOLD); /* * if he has something he might drop it */ if ((item = th->t_pack) != NULL && (OBJPTR(item))->o_type != RELIC && rnd(100) < 50) { detach(th->t_pack, item); fall(item, FALSE); } /* It is okay to turn tail */ th->t_oldpos = th->t_pos; } runto(th, &hero); } } } } } /* * print miscellaneous magic bonuses */ char * misc_name(struct object *obj) { static char buf[LINELEN]; char buf1[LINELEN]; buf[0] = '\0'; buf1[0] = '\0'; if (!(obj->o_flags & ISKNOW)) return (m_magic[obj->o_which].mi_name); switch (obj->o_which) { case MM_BRACERS: case MM_PROTECT: strcat(buf, num(obj->o_ac, 0)); strcat(buf, " "); } switch (obj->o_which) { case MM_G_OGRE: case MM_G_DEXTERITY: case MM_JEWEL: case MM_STRANGLE: case MM_R_POWERLESS: case MM_DANCE: if (obj->o_flags & ISCURSED) strcat(buf, "cursed "); } strcat(buf, m_magic[obj->o_which].mi_name); switch (obj->o_which) { case MM_JUG: if (obj->o_ac == JUG_EMPTY) strcat(buf1, " [empty]"); else if (p_know[obj->o_ac]) sprintf(buf1, " [containing a potion of %s (%s)]", p_magic[obj->o_ac].mi_name, p_colors[obj->o_ac]); else sprintf(buf1, " [containing a%s %s liquid]", vowelstr(p_colors[obj->o_ac]), p_colors[obj->o_ac]); when MM_BEAKER: case MM_BOOK: { sprintf(buf1, " [containing %d]", obj->o_ac); } when MM_OPEN: case MM_HUNGER: sprintf(buf1, " [%d ring%s]", obj->o_charges, obj->o_charges == 1 ? "" : "s"); when MM_DRUMS: sprintf(buf1, " [%d beat%s]", obj->o_charges, obj->o_charges == 1 ? "" : "s"); when MM_DISAPPEAR: case MM_CHOKE: sprintf(buf1, " [%d pinch%s]", obj->o_charges, obj->o_charges == 1 ? "" : "es"); when MM_KEOGHTOM: sprintf(buf1, " [%d application%s]", obj->o_charges, obj->o_charges == 1 ? "" : "s"); when MM_SKILLS: sprintf(buf1, " [%s]", char_class[obj->o_ac].name); } strcat (buf, buf1); return buf; } void use_emori(void) { char selection; /* Cloak function */ int state = 0; /* Menu state */ msg("What do you want to do? (* for a list): "); do { selection = tolower(readchar()); switch (selection) { case '*': if (state != 1) { wclear(hw); touchwin(hw); mvwaddstr(hw, 2, 0, "[1] Fly\n[2] Stop flying\n"); waddstr(hw, "[3] Turn invisible\n[4] Turn Visible\n"); mvwaddstr(hw, 0, 0, "What do you want to do? "); draw(hw); state = 1; /* Now in prompt window */ } break; case ESCAPE: if (state == 1) { clearok(cw, TRUE); /* Set up for redraw */ touchwin(cw); } msg(""); after = FALSE; return; when '1': case '2': case '3': case '4': if (state == 1) { /* In prompt window */ clearok(cw, TRUE); /* Set up for redraw */ touchwin(cw); } msg(""); state = 2; /* Finished */ break; default: if (state == 1) { /* In the prompt window */ mvwaddstr(hw, 0, 0, "Please enter a selection between 1 and 4: "); draw(hw); } else { /* Normal window */ mpos = 0; msg("Please enter a selection between 1 and 4: "); } } } while (state != 2); /* We now must have a selection between 1 and 4 */ switch (selection) { case '1': /* Fly */ if (on(player, ISFLY)) { extinguish(land); /* Extinguish in case of potion */ msg("%slready flying.", terse ? "A" : "You are a"); } else { msg("You feel lighter than air!"); turn_on(player, ISFLY); } when '2': /* Stop flying */ if (off(player, ISFLY)) msg("%sot flying.", terse ? "N" : "You are n"); else { if (find_slot(land)) msg("%sot flying by the cloak.", terse ? "N" : "You are n"); else land(); } when '3': /* Turn invisible */ if (off(player, ISINVIS)) { turn_on(player, ISINVIS); msg("You have a tingling feeling all over your body"); PLAYER = IPLAYER; light(&hero); } else { extinguish(appear); /* Extinguish in case of potion */ extinguish(dust_appear);/* dust of disappearance */ msg("%slready invisible.", terse ? "A" : "You are a"); } when '4': /* Turn visible */ if (off(player, ISINVIS)) msg("%sot invisible.", terse ? "N" : "You are n"); else { if (find_slot(appear) || find_slot(dust_appear)) msg("%sot invisible by the cloak.", terse ? "N" : "You are n"); else appear(); } } } #ifdef PC7300 static menu_t Display; /* The menu structure */ static mitem_t Dispitems[MAXQUILL+1]; /* Info for each line */ static char Displines[MAXQUILL+1][LINELEN+1]; /* The lines themselves */ #endif /* * try to write a scroll with the quill of Nagrom */ void use_quill(struct object *obj) { struct linked_list *item; register int i, scroll_ability; int which_scroll, curlen, maxlen, dummy; bool nohw = FALSE; i = which_scroll = 0; scroll_ability = obj->o_charges; /* Prompt for scrolls */ msg("Which scroll are you writing? (* for list): "); which_scroll = (int) (readchar() - 'a'); if (which_scroll == (int) ESCAPE - (int) 'a') { mpos = 0; msg(""); after = FALSE; return; } if (which_scroll >= 0 && which_scroll < MAXQUILL) nohw = TRUE; else if (slow_invent) { register char c; nohw = TRUE; do { for (i=0; i<MAXQUILL; i++) { msg(""); mvwaddch(msgw, 0, 0, '['); waddch(msgw, (char) ((int) 'a' + i)); waddstr(msgw, "] A scroll of "); waddstr(msgw, s_magic[quill_scrolls[i].s_which].mi_name); waddstr(msgw, morestr); clearok(msgw, FALSE); draw(msgw); do { c = readchar(); } while (c != ' ' && c != ESCAPE); if (c == ESCAPE) break; } msg(""); mvwaddstr(msgw, 0, 0, "Which scroll are you writing? "); clearok(msgw, FALSE); draw(msgw); which_scroll = (int) (readchar() - 'a'); } while (which_scroll != (int) (ESCAPE - 'a') && (which_scroll < 0 || which_scroll >= MAXQUILL)); if (which_scroll == (int) (ESCAPE - 'a')) { mpos = 0; msg(""); after = FALSE; return; } } else { /* Now display the possible scrolls */ wclear(hw); touchwin(hw); mvwaddstr(hw, 2, 0, " Cost Scroll"); mvwaddstr(hw, 3, 0, "-----------------------------------------------"); maxlen = 47; /* Maximum width of header */ for (i=0; i<MAXQUILL; i++) { wmove(hw, i+4, 0); sprintf(prbuf, "[%c] %3d A scroll of %s", (char) ((int) 'a' + i), quill_scrolls[i].s_cost, s_magic[quill_scrolls[i].s_which].mi_name); #ifdef PC7300 /* Put it into the PC menu display */ strcpy(Displines[i], prbuf); Dispitems[i].mi_name = Displines[i]; Dispitems[i].mi_flags = 0; Dispitems[i].mi_val = i; #endif waddstr(hw, prbuf); /* Get the length of the line */ getyx(hw, dummy, curlen); if (maxlen < curlen) maxlen = curlen; } sprintf(prbuf, "[Current scroll power = %d]", scroll_ability); mvwaddstr(hw, 0, 0, prbuf); waddstr(hw, " Which scroll are you writing? "); getyx(hw, dummy, curlen); if (maxlen < curlen) maxlen = curlen; #ifdef PC7300 /* Place an end marker for the items */ Dispitems[MAXQUILL].mi_name = 0; /* Design prompts */ sprintf(prbuf, "Current scroll power is %d", scroll_ability); /* Set up the main menu structure */ Display.m_label = prbuf; Display.m_title = " Cost Scroll"; Display.m_prompt = "Select a scroll or press Cancl to continue."; Display.m_curptr = '\0'; Display.m_markptr = '\0'; Display.m_flags = M_ASISTITLE; Display.m_selcnt = 1; Display.m_items = Dispitems; Display.m_curi = 0; /* * Try to display the menu. If we don't have a local terminal, * the call will fail and we will just continue with the * normal mode. */ if (menu(&Display) >= 0) { if (Display.m_selcnt == 0) { /* Menu was cancelled */ after = FALSE; return FALSE; /* all done if abort */ } else which_scroll = (int) Display.m_curi->mi_val; goto got_scroll; } #endif /* Should we overlay? */ if (menu_overlay && MAXQUILL + 3 < lines / 2) { over_win(cw, hw, MAXQUILL + 5, maxlen + 3, 0, curlen, '\0'); } else draw(hw); } if (!nohw) { which_scroll = (int) (readchar() - 'a'); while (which_scroll < 0 || which_scroll >= MAXQUILL) { if (which_scroll == (int) ESCAPE - (int) 'a') { after = FALSE; /* Restore the screen */ touchwin(cw); if (MAXQUILL + 3 < lines / 2) clearok(cw, FALSE); else { msg(""); clearok(cw, TRUE); } return; } wmove(hw, 0, 0); wclrtoeol(hw); waddstr(hw, "Please enter one of the listed scrolls. "); getyx(hw, dummy, curlen); if (maxlen < curlen) maxlen = curlen; /* Should we overlay? */ if (menu_overlay && MAXQUILL + 3 < lines / 2) { over_win(cw, hw, MAXQUILL + 5, maxlen + 3, 0, curlen, '\0'); } else draw(hw); which_scroll = (int) (readchar() - 'a'); } } /* Now restore the screen if we have to */ if (!nohw) { touchwin(cw); if (MAXQUILL + 3 < lines / 2) clearok(cw, FALSE); else { msg(""); clearok(cw, TRUE); } } else msg(""); #ifdef PC7300 got_scroll: #endif /* We've waited our required time. */ player.t_using = NULL; player.t_action = A_NIL; if (quill_scrolls[which_scroll].s_cost > scroll_ability) { msg("Your attempt fails."); return; } obj->o_charges -= quill_scrolls[which_scroll].s_cost; item = spec_item(SCROLL, quill_scrolls[which_scroll].s_which, 0, 0); if (add_pack(item, FALSE, NULL) == FALSE) { (OBJPTR(item))->o_pos = hero; fall(item, TRUE); } } void use_mm(int which) { register struct object *obj = NULL; register struct linked_list *item = NULL; bool is_mm; is_mm = FALSE; if (which < 0) { /* A real miscellaneous magic item */ /* This is miscellaneous magic. It takes 3 movement periods to use */ if (player.t_action != C_USE) { int units; /* Number of movement units for the item */ item = get_item(pack, "use", USEABLE, FALSE, FALSE); /* * Make certain that it is a micellaneous magic item */ if (item == NULL) return; units = usage_time(item); if (units < 0) return;