Mercurial > hg > early-roguelike
view arogue7/player.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 | 2f75940cc544 |
line wrap: on
line source
/* * player.c - This file contains functions for dealing with special player * abilities * * 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. */ /* * This file contains functions for dealing with special player abilities */ #include <ctype.h> #include <string.h> #include "curses.h" #include "rogue.h" #ifdef PC7300 #include "menu.h" #endif bool pick_spell(struct spells spells[], int ability, int num_spells, int power, char *prompt, char *type); /* * affect: * cleric affecting undead */ void affect(void) { register struct linked_list *item; register struct thing *tp; register char *mname; bool see; coord new_pos; int lvl; if (!(player.t_ctype == C_CLERIC || (player.t_ctype == C_PALADIN && pstats.s_lvl > 4) || cur_relic[HEIL_ANKH] != 0)) { msg("You cannot affect undead."); return; } new_pos.y = hero.y + player.t_newpos.y; new_pos.x = hero.x + player.t_newpos.x; if (cansee(new_pos.y, new_pos.x)) see = TRUE; else see = FALSE; /* Anything there? */ if (new_pos.y < 0 || new_pos.y > lines-3 || new_pos.x < 0 || new_pos.x > cols-1 || mvwinch(mw, new_pos.y, new_pos.x) == ' ') { msg("Nothing to affect."); return; } if ((item = find_mons(new_pos.y, new_pos.x)) == NULL) { debug("Affect what @ %d,%d?", new_pos.y, new_pos.x); return; } tp = THINGPTR(item); mname = monster_name(tp); if (on(player, ISINVIS) && off(*tp, CANSEE)) { msg("%s%s cannot see you", see ? "The " : "It", see ? mname : ""); return; } if (off(*tp, TURNABLE) || on(*tp, WASTURNED)) goto annoy; turn_off(*tp, TURNABLE); lvl = pstats.s_lvl; if (player.t_ctype == C_PALADIN && cur_relic[HEIL_ANKH] == 0) { lvl -= 4; } /* Can cleric kill it? */ if (lvl >= 3 * tp->t_stats.s_lvl) { unsigned long test; /* For overflow check */ msg("You have destroyed %s%s.", see ? "the " : "it", see ? mname : ""); test = pstats.s_exp + tp->t_stats.s_exp; /* Be sure there is no overflow before increasing experience */ if (test > pstats.s_exp) pstats.s_exp = test; killed(item, FALSE, TRUE, TRUE); check_level(); return; } /* Can cleric turn it? */ if (rnd(100) + 1 > (100 * ((2 * tp->t_stats.s_lvl) - lvl)) / lvl) { unsigned long test; /* Overflow test */ /* Make the monster flee */ turn_on(*tp, WASTURNED); /* No more fleeing after this */ turn_on(*tp, ISFLEE); runto(tp, &hero); /* Disrupt it */ dsrpt_monster(tp, TRUE, TRUE); /* Let player know */ msg("You have turned %s%s.", see ? "the " : "it", see ? mname : ""); /* get points for turning monster -- but check overflow first */ test = pstats.s_exp + tp->t_stats.s_exp/2; if (test > pstats.s_exp) pstats.s_exp = test; check_level(); /* 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; return; } /* Otherwise -- no go */ annoy: if (see && tp->t_stats.s_intel > 16) msg("%s laughs at you...", prname(mname, TRUE)); else msg("You do not affect %s%s.", see ? "the " : "it", see ? mname : ""); /* Annoy monster */ if (off(*tp, ISFLEE)) runto(tp, &hero); } /* * the magic user is going to try and cast a spell */ void cast(void) { int spell_ability, which_spell, num_spells; if (player.t_ctype != C_MAGICIAN && player.t_ctype != C_RANGER) { msg("You are not permitted to cast spells."); return; } spell_ability = pstats.s_lvl * pstats.s_intel; if (player.t_ctype != C_MAGICIAN) spell_ability /= 3; if (player.t_action != C_CAST) { /* * Get the number of avilable spells */ num_spells = 0; if (pstats.s_intel >= 16) num_spells += pstats.s_intel - 15; num_spells += pstats.s_lvl; if (player.t_ctype != C_MAGICIAN) num_spells /= 3; if (num_spells > MAXSPELLS) num_spells = MAXSPELLS; if (num_spells < 1) { msg("You are not allowed to cast spells yet."); return; } if (pick_spell( magic_spells, spell_ability, num_spells, spell_power, "cast", "spell")) player.t_action = C_CAST; return; } /* We've waited our required casting time. */ which_spell = player.t_selection; player.t_selection = 0; player.t_using = NULL; player.t_action = A_NIL; if ((spell_power + magic_spells[which_spell].s_cost) > spell_ability) { msg("Your attempt fails."); return; } msg("Your spell is successful."); if (magic_spells[which_spell].s_type == TYP_POTION) quaff( magic_spells[which_spell].s_which, 0, magic_spells[which_spell].s_flag, FALSE); else if (magic_spells[which_spell].s_type == TYP_SCROLL) read_scroll( magic_spells[which_spell].s_which, magic_spells[which_spell].s_flag, FALSE); else if (magic_spells[which_spell].s_type == TYP_STICK) { if (!player_zap(magic_spells[which_spell].s_which, magic_spells[which_spell].s_flag)) { after = FALSE; return; } } spell_power += magic_spells[which_spell].s_cost; } /* * the druid asks his deity for a spell */ void chant(void) { register int num_chants, chant_ability, which_chant; which_chant = num_chants = chant_ability = 0; if (player.t_ctype != C_DRUID && player.t_ctype != C_RANGER) { msg("You are not permitted to chant."); return; } if (cur_misc[WEAR_CLOAK] != NULL && cur_misc[WEAR_CLOAK]->o_which == MM_R_POWERLESS) { msg("You can't seem to chant!"); return; } chant_ability = pstats.s_lvl * pstats.s_wisdom; if (player.t_ctype != C_DRUID) chant_ability /= 3; if (player.t_action != C_CHANT) { num_chants = 0; /* Get the number of avilable chants */ if (pstats.s_wisdom > 16) num_chants = (pstats.s_wisdom - 15) / 2; num_chants += pstats.s_lvl; if (player.t_ctype != C_DRUID) num_chants /= 3; if (num_chants > MAXCHANTS) num_chants = MAXCHANTS; if (num_chants < 1) { msg("You are not permitted to chant yet."); return; } /* Prompt for chant */ if (pick_spell( druid_spells, chant_ability, num_chants, chant_time, "sing", "chant")) player.t_action = C_CHANT; return; } /* We've waited our required chanting time. */ which_chant = player.t_selection; player.t_selection = 0; player.t_using = NULL; player.t_action = A_NIL; if (druid_spells[which_chant].s_cost + chant_time > chant_ability) { msg("Your chant fails."); return; } msg("Your chant has been granted."); if (druid_spells[which_chant].s_type == TYP_POTION) quaff( druid_spells[which_chant].s_which, 0, druid_spells[which_chant].s_flag, FALSE); else if (druid_spells[which_chant].s_type == TYP_SCROLL) read_scroll( druid_spells[which_chant].s_which, druid_spells[which_chant].s_flag, FALSE); else if (druid_spells[which_chant].s_type == TYP_STICK) { if (!player_zap(druid_spells[which_chant].s_which, druid_spells[which_chant].s_flag)) { after = FALSE; return; } } chant_time += druid_spells[which_chant].s_cost; } /* Constitution bonus */ int const_bonus(void) /* Hit point adjustment for changing levels */ { register int bonus; if (pstats.s_const > 6 && pstats.s_const <= 14) bonus = 0; else if (pstats.s_const > 14) bonus = pstats.s_const-14; else if (pstats.s_const > 3) bonus = -1; else bonus = -2; switch(player.t_ctype) { case C_FIGHTER: bonus = min(bonus, 11); when C_MAGICIAN: bonus = min(bonus, 4); when C_CLERIC: bonus = min(bonus, 4); when C_THIEF: bonus = min(bonus, 4); when C_RANGER: bonus = min(bonus, 6); when C_PALADIN: bonus = min(bonus, 8); when C_ASSASIN: bonus = min(bonus, 4); when C_MONK: bonus = min(bonus, 6); when C_DRUID: bonus = min(bonus, 4); otherwise: bonus = min(bonus, 4); } return(bonus); } /* Routines for thieves */ /* * gsense: * Sense gold */ void gsense(void) { /* Only thieves can do this */ if (player.t_ctype != C_THIEF && player.t_ctype != C_ASSASIN) { msg("You seem to have no gold sense."); return; } read_scroll(S_GFIND, 0, FALSE); } /* * the cleric asks his deity for a spell */ void pray(void) { register int num_prayers, prayer_ability, which_prayer; which_prayer = num_prayers = prayer_ability = 0; if (player.t_ctype != C_CLERIC && player.t_ctype != C_PALADIN && cur_relic[HEIL_ANKH] == 0) { msg("You are not permitted to pray."); return; } if (cur_misc[WEAR_CLOAK] != NULL && cur_misc[WEAR_CLOAK]->o_which == MM_R_POWERLESS) { msg("You can't seem to pray!"); return; } prayer_ability = pstats.s_lvl * pstats.s_wisdom; if (player.t_ctype != C_CLERIC) prayer_ability /= 3; if (cur_relic[HEIL_ANKH]) prayer_ability *= 2; if (player.t_action != C_PRAY) { num_prayers = 0; /* Get the number of avilable prayers */ if (pstats.s_wisdom > 16) num_prayers += (pstats.s_wisdom - 15) / 2; num_prayers += pstats.s_lvl; if (cur_relic[HEIL_ANKH]) num_prayers += 3; if (player.t_ctype != C_CLERIC) num_prayers /= 3; if (num_prayers > MAXPRAYERS) num_prayers = MAXPRAYERS; if (num_prayers < 1) { msg("You are not permitted to pray yet."); return; } /* Prompt for prayer */ if (pick_spell( cleric_spells, prayer_ability, num_prayers, pray_time, "offer", "prayer")) player.t_action = C_PRAY; return; } /* We've waited our required praying time. */ which_prayer = player.t_selection; player.t_selection = 0; player.t_using = NULL; player.t_action = A_NIL; if (cleric_spells[which_prayer].s_cost + pray_time > prayer_ability) { msg("Your prayer fails."); return; } msg("Your prayer has been granted."); if (cleric_spells[which_prayer].s_type == TYP_POTION) quaff( cleric_spells[which_prayer].s_which, 0, cleric_spells[which_prayer].s_flag, FALSE); else if (cleric_spells[which_prayer].s_type == TYP_SCROLL) read_scroll( cleric_spells[which_prayer].s_which, cleric_spells[which_prayer].s_flag, FALSE); else if (cleric_spells[which_prayer].s_type == TYP_STICK) { if (!player_zap(cleric_spells[which_prayer].s_which, cleric_spells[which_prayer].s_flag)) { after = FALSE; return; } } pray_time += cleric_spells[which_prayer].s_cost; } /* * steal: * Steal in direction given in delta */ void steal(void) { register struct linked_list *item; register struct thing *tp; register char *mname; coord new_pos; int thief_bonus = -50; bool isinvisible = FALSE; if (player.t_ctype != C_THIEF && player.t_ctype != C_ASSASIN) { msg("Only thieves and assassins can steal."); return; } if (on(player, ISBLIND)) { msg("You can't see anything."); return; } new_pos.y = hero.y + player.t_newpos.y; new_pos.x = hero.x + player.t_newpos.x; /* Anything there? */ if (new_pos.y < 0 || new_pos.y > lines-3 || new_pos.x < 0 || new_pos.x > cols-1 || mvwinch(mw, new_pos.y, new_pos.x) == ' ') { msg("Nothing to steal from."); return; } if ((item = find_mons(new_pos.y, new_pos.x)) == NULL) debug("Steal from what @ %d,%d?", new_pos.y, new_pos.x); tp = THINGPTR(item); if (on(*tp, ISSTONE)) { msg ("You can't steal from stone!"); return; } if ( (isinvisible = invisible(tp)) ) mname = "creature"; else mname = monster_name(tp); /* Can player steal something unnoticed? */ if (player.t_ctype == C_THIEF) thief_bonus = 10; if (player.t_ctype == C_ASSASIN) thief_bonus = 0; if (on(*tp, ISUNIQUE)) thief_bonus -= 15; if (isinvisible) thief_bonus -= 20; if (on(*tp, ISINWALL) && off(player, CANINWALL)) thief_bonus -= 50; if (on(*tp, ISHELD) || tp->t_action == A_FREEZE || rnd(100) < (thief_bonus + 2*dex_compute() + 5*pstats.s_lvl - 5*(tp->t_stats.s_lvl - 3))) { register struct linked_list *s_item, *pack_ptr; int count = 0; unsigned long test; /* Overflow check */ s_item = NULL; /* Start stolen goods out as nothing */ /* Find a good item to take */ for (pack_ptr=tp->t_pack; pack_ptr != NULL; pack_ptr=next(pack_ptr)) if ((OBJPTR(pack_ptr))->o_type != RELIC && pack_ptr != tp->t_using && /* Monster can't be using it */ rnd(++count) == 0) s_item = pack_ptr; /* * Find anything? */ if (s_item == NULL) { msg("%s apparently has nothing to steal.", prname(mname, TRUE)); return; } /* Take it from monster */ if (tp->t_pack) detach(tp->t_pack, s_item); /* Recalculate the monster's encumberance */ updpack(TRUE, tp); /* Give it to player */ if (add_pack(s_item, FALSE, NULL) == FALSE) { (OBJPTR(s_item))->o_pos = hero; fall(s_item, TRUE); } /* Get points for stealing -- but first check for overflow */ test = pstats.s_exp + tp->t_stats.s_exp/2; if (test > pstats.s_exp) pstats.s_exp = test; /* * Do adjustments if player went up a level */ check_level(); } else { msg("Your attempt fails."); /* Annoy monster (maybe) */ if (rnd(35) >= dex_compute() + thief_bonus) { /* * If this is a charmed creature, there is a chance it * will become uncharmed. */ if (on(*tp, ISCHARMED) && save(VS_MAGIC, tp, 0)) { msg("The eyes of %s turn clear.", prname(mname, FALSE)); turn_off(*tp, ISCHARMED); } if (on(*tp, CANSELL)) { turn_off(*tp, CANSELL); tp->t_action = A_NIL; tp->t_movement = 0; if (rnd(100) < 50) /* make him steal something */ turn_on(*tp, STEALMAGIC); else turn_on(*tp, STEALGOLD); if (!isinvisible) msg("%s looks insulted.", prname(mname, TRUE)); } runto(tp, &hero); } } } #ifdef PC7300 /* Use MAXSPELLS or whatever is the biggest number of spells/prayers/etc */ static menu_t Display; /* The menu structure */ static mitem_t Dispitems[MAXSPELLS+1]; /* Info for each line */ static char Displines[MAXSPELLS+1][LINELEN+1]; /* The lines themselves */ #endif /* * this routine lets the player pick the spell that they * want to cast regardless of character class * spells: spell list * ability: spell ability * num_spells: number of spells that can be cast * power: spell power * prompt: prompt for spell list * type: type of thing--> spell, prayer, chant */ bool pick_spell(struct spells spells[], int ability, int num_spells, int power, char *prompt, char *type) { bool nohw = FALSE; register int i; int curlen, maxlen, dummy, which_spell, spell_left; #ifdef PC7300 char label[LINELEN], /* For menu label */ title[LINELEN]; /* For menu title */ #endif if (cur_misc[WEAR_CLOAK] != NULL && cur_misc[WEAR_CLOAK]->o_which == MM_R_POWERLESS) { msg("You can't seem to start a %s!", type); return(FALSE); } /* Prompt for spells */ msg("Which %s are you %sing? (* for list): ", type, prompt); which_spell = (int) (readchar() - 'a'); msg(""); /* Get rid of the prompt */ if (which_spell == (int) ESCAPE - (int) 'a') { after = FALSE; return(FALSE); } if (which_spell >= 0 && which_spell < num_spells) nohw = TRUE; else if (slow_invent) { register char c; nohw = TRUE; do { for (i=0; i<num_spells; i++) { msg(""); mvwaddch(msgw, 0, 0, '['); waddch(msgw, (char) ((int) 'a' + i)); wprintw(msgw, "] A %s of ", type); if (spells[i].s_type == TYP_POTION) waddstr(msgw, p_magic[spells[i].s_which].mi_name); else if (spells[i].s_type == TYP_SCROLL) waddstr(msgw, s_magic[spells[i].s_which].mi_name); else if (spells[i].s_type == TYP_STICK) waddstr(msgw, ws_magic[spells[i].s_which].mi_name); waddstr(msgw, morestr); wclrtobot(msgw); clearok(msgw, FALSE); draw(msgw); do { c = readchar(); } while (c != ' ' && c != ESCAPE); if (c == ESCAPE) break; } msg(""); wmove(msgw, 0, 0); wprintw(msgw, "Which %s are you %sing? ", type, prompt); clearok(msgw, FALSE); draw(msgw); which_spell = (int) (readchar() - 'a'); } while (which_spell != (int) (ESCAPE - 'a') && (which_spell < 0 || which_spell >= num_spells)); if (which_spell == (int) (ESCAPE - 'a')) { mpos = 0; msg(""); after = FALSE; return(FALSE); } } else { /* Now display the possible spells */ wclear(hw); touchwin(hw); wmove(hw, 2, 0); *type = toupper(*type); wprintw(hw, " Cost %s", type); *type = tolower(*type); mvwaddstr(hw, 3, 0, "-----------------------------------------------"); maxlen = 47; /* Maximum width of header */ for (i=0; i<num_spells; i++) { sprintf(prbuf, "[%c] %3d A %s of ", (char) ((int) 'a' + i), spells[i].s_cost, type); if (spells[i].s_type == TYP_POTION) strcat(prbuf, p_magic[spells[i].s_which].mi_name); else if (spells[i].s_type == TYP_SCROLL) strcat(prbuf, s_magic[spells[i].s_which].mi_name); else if (spells[i].s_type == TYP_STICK) strcat(prbuf, ws_magic[spells[i].s_which].mi_name); mvwaddstr(hw, i+4, 0, prbuf); /* Get the length of the line */ getyx(hw, dummy, curlen); if (maxlen < curlen) maxlen = curlen; #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 } spell_left = ability - power; if (spell_left < 0) { spell_left = 0; if (spell_left < -20) power = ability + 20; } sprintf(prbuf, "[Current %s power = %d]", type, spell_left); mvwaddstr(hw, 0, 0, prbuf); wprintw(hw, " Which %s are you %sing? ", type, prompt); getyx(hw, dummy, curlen); if (maxlen < curlen) maxlen = curlen; #ifdef PC7300 /* Place an end marker for the items */ Dispitems[num_spells].mi_name = 0; /* Design prompts */ sprintf(label, "Current %s power is %d", type, spell_left); *type = toupper(*type); sprintf(title, " Cost %s", type); *type = tolower(*type); sprintf(prbuf, "Select a %s or press Cancl to continue.", type); /* Set up the main menu structure */ Display.m_label = label; Display.m_title = title; Display.m_prompt = prbuf; 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_spell = (int) Display.m_curi->mi_val; goto got_spell; } #endif /* Should we overlay? */ if (menu_overlay && num_spells + 3 < lines / 2) { over_win(cw, hw, num_spells + 5, maxlen + 3, 0, curlen, '\0'); } else draw(hw); } if (!nohw) { which_spell = (int) (readchar() - 'a'); while (which_spell < 0 || which_spell >= num_spells) { if (which_spell == (int) ESCAPE - (int) 'a') { after = FALSE; /* Restore the screen */ touchwin(cw); if (num_spells + 3 < lines / 2) clearok(cw, FALSE); else clearok(cw, TRUE); return(FALSE); } wmove(hw, 0, 0); wclrtoeol(hw); wprintw(hw, "Please enter one of the listed %ss. ", type); getyx(hw, dummy, curlen); if (maxlen < curlen) maxlen = curlen; /* Should we overlay? */ if (menu_overlay && num_spells + 3 < lines / 2) { over_win(cw, hw, num_spells + 5, maxlen + 3, 0, curlen, '\0'); } else draw(hw); which_spell = (int) (readchar() - 'a'); } } /* Now restore the screen if we have to */ if (!nohw) { touchwin(cw); if (num_spells + 3 < lines / 2) clearok(cw, FALSE); else clearok(cw, TRUE); } #ifdef PC7300 got_spell: #endif if (spells[which_spell].s_type == TYP_STICK && need_dir(STICK, spells[which_spell].s_which)) { if (!get_dir(&player.t_newpos)) { after = FALSE; return(FALSE); } } player.t_selection = which_spell; player.t_using = NULL; player.t_no_move = (which_spell/3 + 1) * movement(&player); return(TRUE); }