Mercurial > hg > early-roguelike
view xrogue/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 | 2236ef808bcb |
children |
line wrap: on
line source
/* player.c - functions for dealing with special player abilities XRogue: Expeditions into the Dungeons of Doom Copyright (C) 1991 Robert Pietkivitch All rights reserved. Based on "Advanced Rogue" Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka and AT&T All rights reserved. See the file LICENSE.TXT for full copyright and licensing information. */ #include <ctype.h> #include <string.h> #include <curses.h> #include "rogue.h" bool pick_spell(struct spells spells[], int ability, int num_spells, int power, const char *prompt, const 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)) == 0) { 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 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 - 5; if (player.t_ctype != C_CLERIC) prayer_ability /= 2; if (cur_relic[HEIL_ANKH]) prayer_ability += 75; 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 - 16; num_prayers += pstats.s_lvl; if (cur_relic[HEIL_ANKH]) num_prayers += pstats.s_wisdom - 18; if (player.t_ctype != C_CLERIC) num_prayers /= 2; 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_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; } /* * the magician is going to try and cast a spell */ void cast(void) { register 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; } if (cur_misc[WEAR_CLOAK] != NULL && cur_misc[WEAR_CLOAK]->o_which == MM_R_POWERLESS) { msg("You can't seem to cast spells!"); return; } spell_ability = pstats.s_lvl * pstats.s_intel - 5; if (player.t_ctype != C_MAGICIAN) spell_ability /= 2; 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 - 16; num_spells += pstats.s_lvl; if (player.t_ctype != C_MAGICIAN) num_spells /= 2; if (num_spells > MAXSPELLS) num_spells = MAXSPELLS; if (num_spells < 1) { msg("You are not allowed to cast spells yet."); return; } /* prompt for spell */ 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_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_MONK) { 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 - 5; if (player.t_ctype != C_DRUID) chant_ability /= 2; 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 - 16; num_chants += pstats.s_lvl; if (player.t_ctype != C_DRUID) num_chants /= 2; 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_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 > 9 && pstats.s_const < 18) bonus = 0; else if (pstats.s_const >= 18 && pstats.s_const < 20) bonus = 1; else if (pstats.s_const >= 20 && pstats.s_const < 26) bonus = 2; else if (pstats.s_const >= 26 && pstats.s_const < 36) bonus = 3; else if (pstats.s_const >= 36) bonus = 4; else if (pstats.s_const > 7) bonus = -1; else bonus = -2; switch(player.t_ctype) { case C_FIGHTER: bonus = min(bonus, 11); when C_RANGER: bonus = min(bonus, 9); when C_PALADIN: bonus = min(bonus, 9); when C_MAGICIAN: bonus = min(bonus, 8); when C_CLERIC: bonus = min(bonus, 8); when C_THIEF: bonus = min(bonus, 10); when C_ASSASSIN: bonus = min(bonus, 10); when C_DRUID: bonus = min(bonus, 8); when C_MONK: bonus = min(bonus, 9); otherwise: bonus = min(bonus, 7); } return(bonus); } /* * Give away slime-molds to monsters. If monster is friendly, * it will give you a "regular" food ration in return. You have * to give a slime-mold to Alteran (a unique monster) in order to * get the special "Card of Alteran" quest item. There's no other * way to get this artifact and remain alive. */ void give(struct thing *th) { /* * Find any monster within one space of you */ struct linked_list *ll; struct object *lb; register int x,y; register struct linked_list *mon = NULL; bool gotone = FALSE; if (levtype != POSTLEV) { /* no monsters at trading post */ for (x = hero.x-1; x <= hero.x+1; x++) { for (y = hero.y-1; y <= hero.y+1; 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) { gotone = TRUE; /* found a monster to give away to */ th = THINGPTR(mon); } } } } } if (gotone) { if ((ll=get_item(pack, "give away", ALL, FALSE, FALSE)) != NULL) { lb = OBJPTR(ll); mpos = 0; switch(lb->o_type) { case FOOD: switch (lb->o_which) { case E_SLIMEMOLD: /* only slime-molds for now */ if (on(*th, CANSELL)) { /* quartermaster */ msg("%s laughs at you. "); return; } if ((on(*th, ISFRIENDLY) || off(*th, ISMEAN)) && off(*th, ISUNIQUE) && off(*th, CANSELL)) { turn_on(*th, ISRUN); /* we want him awake */ msg("%s accepts and promptly eats your gift of food. --More--", prname(monster_name(th), TRUE)); wait_for(' '); del_pack(ll); /* delete slime-mold */ /* and add a food ration */ create_obj(FALSE, FOOD, E_RATION); msg("%s gives you food in return and nods off to sleep. ", prname(monster_name(th), TRUE)); turn_off(*th, ISRUN); /* put him to sleep */ return; } else if (on(*th, CARRYCARD) && on(*th, ISUNIQUE)) { /* Now you get the Card of Alteran */ msg("%s gives you a strange rectangular card. --More--", prname(monster_name(th), TRUE)); wait_for(' '); del_pack(ll); /* get rid of slime-mold */ create_obj(FALSE, RELIC, ALTERAN_CARD); msg("%s bids you farewell. ", prname(monster_name(th), TRUE)); killed(mon, FALSE, FALSE, FALSE); return; } else if (on(*th, ISUNIQUE) && off(*th, ISMEAN)) { /* Dragons */ msg("%s is set free by your generosity. ", prname(monster_name(th), TRUE)); del_pack(ll); /* get rid of it */ /* just let him roam around */ turn_on(*th, ISRUN); if (on(*th, ISFLEE)) turn_off(*th, ISFLEE); runto(th, &hero); th->t_action = A_NIL; return; } else if (on(*th, ISRUN) && off(*th, ISUNIQUE)) { /* if NOT sleeping and not a unique */ switch (rnd(2)) { case 0: msg("%s ignores you. ", prname(monster_name(th), TRUE)); when 1: { msg("%s nips at your hand. ", prname(monster_name(th), TRUE)); if (rnd(100) < 10) { del_pack(ll); /* delete it */ if (off(*th, ISMEAN)) { msg("The slime-mold makes %s sleepy. ", prname(monster_name(th), TRUE)); /* put him to sleep */ turn_off(*th, ISRUN); return; } else { switch (rnd(2)) { case 0: msg("%s's eyes roll back. ", prname(monster_name(th), TRUE)); when 1: msg("%s becomes wanderlust. ", prname(monster_name(th), TRUE)); } /* just let him roam around */ turn_on(*th, ISRUN); if (on(*th, ISFLEE)) turn_off(*th, ISFLEE); runto(th, &hero); th->t_action = A_NIL; return; } } } } } else { msg("%s's mouth waters. ", prname(monster_name(th), TRUE)); /* this wakes him up */ if (off(*th, ISUNIQUE)) turn_on(*th, ISRUN); return; } otherwise: switch (rnd(3)) { /* mention food (hint hint) */ case 0: msg("You cannot give away the %s! ", foods[lb->o_which].mi_name); when 1: msg("The %s looks rancid! ", foods[lb->o_which].mi_name); when 2: msg("You change your mind. "); } return; } otherwise: switch (rnd(3)) { /* do not mention other items */ case 0: msg("You feel foolish. "); when 1: msg("You change your mind. "); when 2: msg("%s ignores you. ", prname(monster_name(th), TRUE)); } return; } } } else msg("Your efforts are futile. "); return; } /* * Frighten a monster. Useful for the 'good' characters. */ void fright(struct thing *th) { /* * Find any monster within one space of you */ register int x,y; register struct linked_list *mon; bool gotone = FALSE; if (levtype != POSTLEV) { /* no monsters at trading post */ for (x = hero.x-1; x <= hero.x+1; x++) { for (y = hero.y-1; y <= hero.y+1; 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) { gotone = TRUE; /* found a monster to give away to */ th = THINGPTR(mon); } } } } } if (gotone) { /* If 'good' character or is wearing a ring of fear */ if (player.t_ctype == C_RANGER || player.t_ctype == C_PALADIN || player.t_ctype == C_MONK || ISWEARING(R_FEAR) != 0) { player.t_action = A_NIL; player.t_no_move = movement(&player); switch (player.t_ctype) { case C_FIGHTER: /* loss of strength */ pstats.s_str--; if (pstats.s_str < 3) pstats.s_str = 3; when C_RANGER: /* loss of charisma */ case C_PALADIN: pstats.s_charisma--; if (pstats.s_charisma < 3) pstats.s_charisma = 3; when C_CLERIC: /* loss of wisdom */ case C_DRUID: pstats.s_wisdom--; if (pstats.s_wisdom < 3) pstats.s_wisdom = 3; when C_MAGICIAN: /* loss of wisdom intelligence */ pstats.s_intel--; if (pstats.s_intel < 3) pstats.s_intel = 3; when C_THIEF: /* loss of dexterity */ case C_ASSASSIN: pstats.s_dext--; if (pstats.s_dext < 3) pstats.s_dext = 3; when C_MONK: /* loss of constitution */ pstats.s_const--; if (pstats.s_const < 3) pstats.s_const = 3; otherwise: /* this msg can induce great fear */ msg("You miss. "); } /* Cause a panic. Good thru level 16. */ if (level < 17) { msg("You wave your arms and yell! "); do_panic(th->t_index); pstats.s_hpt -= (pstats.s_hpt/2)+1; if (pstats.s_hpt < 25) msg("You heart quivers... "); if (pstats.s_hpt < 1) { msg("Your heart stops!! --More--"); wait_for(' '); pstats.s_hpt = -1; death(D_FRIGHT); } return; } else { /* He can't do it after level 16 */ switch (rnd(20)) { case 0: case 2: msg("You stamp your foot!! "); when 4: case 8: msg("%s laughs at you! ",prname(monster_name(th),TRUE)); when 10: case 12: msg("You forget what you are doing? "); otherwise: msg(nothing); } return; } } else { switch (rnd(25)) { case 0: case 2: case 4: msg("You motion angrily! "); when 6: case 8: case 10: msg("You can't frighten anything. "); when 12: case 14: case 16: msg("Your puff up your face. "); otherwise: msg(nothing); } return; } } else { msg("There is nothing to fear but fear itself. "); return; } } /* Routines for thieves */ /* * gsense: Sense gold */ void gsense(void) { /* Thief & assassin can do this, but fighter & ranger can later */ if (player.t_ctype == C_THIEF || player.t_ctype == C_ASSASSIN || ((player.t_ctype == C_FIGHTER || player.t_ctype == C_RANGER) && pstats.s_lvl >= 12)) { read_scroll(S_GFIND, 0, FALSE); } else msg("You seem to have no gold sense."); return; } /* * xsense: Sense traps */ void xsense(void) { /* Only thief can do this, but assassin, fighter, & monk can later */ if (player.t_ctype == C_THIEF || ((player.t_ctype == C_ASSASSIN || player.t_ctype == C_FIGHTER || player.t_ctype == C_MONK) && pstats.s_lvl >= 14)) { read_scroll(S_FINDTRAPS, 0, FALSE); } else msg("You seem not to be able to sense traps."); return; } /* * 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; /* let the fighter steal after level 15 */ if (player.t_ctype == C_FIGHTER && pstats.s_lvl < 15) { msg(nothing); return; } else if (player.t_ctype != C_THIEF && player.t_ctype != C_ASSASSIN && player.t_ctype != C_FIGHTER) { 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 (on(*tp, ISFLEE)) { msg("You can't get your hand in anywhere! "); return; } isinvisible = invisible(tp); if (isinvisible) mname = "creature"; else mname = monster_name(tp); /* Can player steal something unnoticed? */ if (player.t_ctype == C_THIEF) thief_bonus = 9; if (player.t_ctype == C_ASSASSIN) thief_bonus = 6; if (player.t_ctype == C_FIGHTER) thief_bonus = 3; 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 */