/* File: effects.c */ /* Purpose: effects of various "objects" */ /* * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke * * This software may be copied and distributed for educational, research, and * not for profit purposes provided that this copyright and statement are * included in all such copies. */ #include "angband.h" /* * Set "p_ptr->blind", notice observable changes * * Note the use of "PU_UN_LITE" and "PU_UN_VIEW", which is needed to * memorize any terrain features which suddenly become "visible". * Note that blindness is currently the only thing which can affect * "player_can_see_bold()". */ bool set_blind(int v) { bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->blind) { msg_print("You are blind!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->blind) { msg_print("You can see again."); notice = TRUE; } } /* Use the value */ p_ptr->blind = v; /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(0, 0); /* Forget stuff */ p_ptr->update |= (PU_UN_VIEW | PU_UN_LITE); /* Update stuff */ p_ptr->update |= (PU_VIEW | PU_LITE); /* Update the monsters */ p_ptr->update |= (PU_MONSTERS); /* Redraw map */ p_ptr->redraw |= (PR_MAP); /* Redraw the "blind" */ p_ptr->redraw |= (PR_BLIND); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Set "p_ptr->confused", notice observable changes */ bool set_confused(int v) { bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->confused) { msg_print("You are confused!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->confused) { msg_print("You feel less confused now."); notice = TRUE; } } /* Use the value */ p_ptr->confused = v; /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(0, 0); /* Redraw the "confused" */ p_ptr->redraw |= (PR_CONFUSED); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Set "p_ptr->poisoned", notice observable changes */ bool set_poisoned(int v) { bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->poisoned) { msg_print("You are poisoned!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->poisoned) { msg_print("You are no longer poisoned."); notice = TRUE; } } /* Use the value */ p_ptr->poisoned = v; /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(0, 0); /* Redraw the "poisoned" */ p_ptr->redraw |= (PR_POISONED); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Set "p_ptr->afraid", notice observable changes */ bool set_afraid(int v) { bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->afraid) { msg_print("You are terrified!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->afraid) { msg_print("You feel bolder now."); notice = TRUE; } } /* Use the value */ p_ptr->afraid = v; /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(0, 0); /* Redraw the "afraid" */ p_ptr->redraw |= (PR_AFRAID); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Set "p_ptr->paralyzed", notice observable changes */ bool set_paralyzed(int v) { bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->paralyzed) { msg_print("You are paralyzed!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->paralyzed) { msg_print("You can move again."); notice = TRUE; } } /* Use the value */ p_ptr->paralyzed = v; /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(0, 0); /* Redraw the state */ p_ptr->redraw |= (PR_STATE); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Set "p_ptr->image", notice observable changes * * Note that we must redraw the map when hallucination changes. */ bool set_image(int v) { bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->image) { msg_print("You feel drugged!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->image) { msg_print("You can see clearly again."); notice = TRUE; } } /* Use the value */ p_ptr->image = v; /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(0, 0); /* Redraw map */ p_ptr->redraw |= (PR_MAP); /* Update monsters */ p_ptr->update |= (PU_MONSTERS); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Set "p_ptr->fast", notice observable changes */ bool set_fast(int v) { bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->fast) { msg_print("You feel yourself moving faster!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->fast) { msg_print("You feel yourself slow down."); notice = TRUE; } } /* Use the value */ p_ptr->fast = v; /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(0, 0); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Set "p_ptr->slow", notice observable changes */ bool set_slow(int v) { bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->slow) { msg_print("You feel yourself moving slower!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->slow) { msg_print("You feel yourself speed up."); notice = TRUE; } } /* Use the value */ p_ptr->slow = v; /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(0, 0); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Set "p_ptr->shield", notice observable changes */ bool set_shield(int v) { bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->shield) { msg_print("A mystic shield forms around your body!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->shield) { msg_print("Your mystic shield crumbles away."); notice = TRUE; } } /* Use the value */ p_ptr->shield = v; /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(0, 0); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Set "p_ptr->blessed", notice observable changes */ bool set_blessed(int v) { bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->blessed) { msg_print("You feel righteous!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->blessed) { msg_print("The prayer has expired."); notice = TRUE; } } /* Use the value */ p_ptr->blessed = v; /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(0, 0); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Set "p_ptr->hero", notice observable changes */ bool set_hero(int v) { bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->hero) { msg_print("You feel like a hero!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->hero) { msg_print("The heroism wears off."); notice = TRUE; } } /* Use the value */ p_ptr->hero = v; /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(0, 0); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Recalculate hitpoints */ p_ptr->update |= (PU_HP); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Set "p_ptr->shero", notice observable changes */ bool set_shero(int v) { bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->shero) { msg_print("You feel like a killing machine!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->shero) { msg_print("You feel less Berserk."); notice = TRUE; } } /* Use the value */ p_ptr->shero = v; /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(0, 0); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Recalculate hitpoints */ p_ptr->update |= (PU_HP); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Set "p_ptr->protevil", notice observable changes */ bool set_protevil(int v) { bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->protevil) { msg_print("You feel safe from evil!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->protevil) { msg_print("You no longer feel safe from evil."); notice = TRUE; } } /* Use the value */ p_ptr->protevil = v; /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(0, 0); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Set "p_ptr->invuln", notice observable changes */ bool set_invuln(int v) { bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->invuln) { msg_print("You feel invulnerable!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->invuln) { msg_print("You feel vulnerable once more."); notice = TRUE; } } /* Use the value */ p_ptr->invuln = v; /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(0, 0); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Set "p_ptr->tim_invis", notice observable changes */ bool set_tim_invis(int v) { bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->tim_invis) { msg_print("Your eyes feel very sensitive!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->tim_invis) { msg_print("Your eyes feel less sensitive."); notice = TRUE; } } /* Use the value */ p_ptr->tim_invis = v; /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(0, 0); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Update the monsters */ p_ptr->update |= (PU_MONSTERS); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Set "p_ptr->tim_infra", notice observable changes */ bool set_tim_infra(int v) { bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->tim_infra) { msg_print("Your eyes begin to tingle!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->tim_infra) { msg_print("Your eyes stop tingling."); notice = TRUE; } } /* Use the value */ p_ptr->tim_infra = v; /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(0, 0); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Update the monsters */ p_ptr->update |= (PU_MONSTERS); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Set "p_ptr->oppose_acid", notice observable changes */ bool set_oppose_acid(int v) { bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->oppose_acid) { msg_print("You feel resistant to acid!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->oppose_acid) { msg_print("You feel less resistant to acid."); notice = TRUE; } } /* Use the value */ p_ptr->oppose_acid = v; /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(0, 0); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Set "p_ptr->oppose_elec", notice observable changes */ bool set_oppose_elec(int v) { bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->oppose_elec) { msg_print("You feel resistant to electricity!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->oppose_elec) { msg_print("You feel less resistant to electricity."); notice = TRUE; } } /* Use the value */ p_ptr->oppose_elec = v; /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(0, 0); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Set "p_ptr->oppose_fire", notice observable changes */ bool set_oppose_fire(int v) { bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->oppose_fire) { msg_print("You feel resistant to fire!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->oppose_fire) { msg_print("You feel less resistant to fire."); notice = TRUE; } } /* Use the value */ p_ptr->oppose_fire = v; /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(0, 0); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Set "p_ptr->oppose_cold", notice observable changes */ bool set_oppose_cold(int v) { bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->oppose_cold) { msg_print("You feel resistant to cold!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->oppose_cold) { msg_print("You feel less resistant to cold."); notice = TRUE; } } /* Use the value */ p_ptr->oppose_cold = v; /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(0, 0); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Set "p_ptr->oppose_pois", notice observable changes */ bool set_oppose_pois(int v) { bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Open */ if (v) { if (!p_ptr->oppose_pois) { msg_print("You feel resistant to poison!"); notice = TRUE; } } /* Shut */ else { if (p_ptr->oppose_pois) { msg_print("You feel less resistant to poison."); notice = TRUE; } } /* Use the value */ p_ptr->oppose_pois = v; /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(0, 0); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Set "p_ptr->stun", notice observable changes * * Note the special code to only notice "range" changes. */ bool set_stun(int v) { int old_aux, new_aux; bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Knocked out */ if (p_ptr->stun > 100) { old_aux = 3; } /* Heavy stun */ else if (p_ptr->stun > 50) { old_aux = 2; } /* Stun */ else if (p_ptr->stun > 0) { old_aux = 1; } /* None */ else { old_aux = 0; } /* Knocked out */ if (v > 100) { new_aux = 3; } /* Heavy stun */ else if (v > 50) { new_aux = 2; } /* Stun */ else if (v > 0) { new_aux = 1; } /* None */ else { new_aux = 0; } /* Increase cut */ if (new_aux > old_aux) { /* Describe the state */ switch (new_aux) { /* Stun */ case 1: msg_print("You have been stunned."); break; /* Heavy stun */ case 2: msg_print("You have been heavily stunned."); break; /* Knocked out */ case 3: msg_print("You have been knocked out."); break; } /* Notice */ notice = TRUE; } /* Decrease cut */ else if (new_aux < old_aux) { /* Describe the state */ switch (new_aux) { /* None */ case 0: msg_print("You are no longer stunned."); if (disturb_state) disturb(0, 0); break; } /* Notice */ notice = TRUE; } /* Use the value */ p_ptr->stun = v; /* No change */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(0, 0); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Redraw the "stun" */ p_ptr->redraw |= (PR_STUN); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Set "p_ptr->cut", notice observable changes * * Note the special code to only notice "range" changes. */ bool set_cut(int v) { int old_aux, new_aux; bool notice = FALSE; /* Hack -- Force good values */ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v; /* Mortal wound */ if (p_ptr->cut > 1000) { old_aux = 7; } /* Deep gash */ else if (p_ptr->cut > 200) { old_aux = 6; } /* Severe cut */ else if (p_ptr->cut > 100) { old_aux = 5; } /* Nasty cut */ else if (p_ptr->cut > 50) { old_aux = 4; } /* Bad cut */ else if (p_ptr->cut > 25) { old_aux = 3; } /* Light cut */ else if (p_ptr->cut > 10) { old_aux = 2; } /* Graze */ else if (p_ptr->cut > 0) { old_aux = 1; } /* None */ else { old_aux = 0; } /* Mortal wound */ if (v > 1000) { new_aux = 7; } /* Deep gash */ else if (v > 200) { new_aux = 6; } /* Severe cut */ else if (v > 100) { new_aux = 5; } /* Nasty cut */ else if (v > 50) { new_aux = 4; } /* Bad cut */ else if (v > 25) { new_aux = 3; } /* Light cut */ else if (v > 10) { new_aux = 2; } /* Graze */ else if (v > 0) { new_aux = 1; } /* None */ else { new_aux = 0; } /* Increase cut */ if (new_aux > old_aux) { /* Describe the state */ switch (new_aux) { /* Graze */ case 1: msg_print("You have been given a graze."); break; /* Light cut */ case 2: msg_print("You have been given a light cut."); break; /* Bad cut */ case 3: msg_print("You have been given a bad cut."); break; /* Nasty cut */ case 4: msg_print("You have been given a nasty cut."); break; /* Severe cut */ case 5: msg_print("You have been given a severe cut."); break; /* Deep gash */ case 6: msg_print("You have been given a deep gash."); break; /* Mortal wound */ case 7: msg_print("You have been given a mortal wound."); break; } /* Notice */ notice = TRUE; } /* Decrease cut */ else if (new_aux < old_aux) { /* Describe the state */ switch (new_aux) { /* None */ case 0: msg_print("You are no longer bleeding."); if (disturb_state) disturb(0, 0); break; } /* Notice */ notice = TRUE; } /* Use the value */ p_ptr->cut = v; /* No change */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(0, 0); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Redraw the "cut" */ p_ptr->redraw |= (PR_CUT); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Set "p_ptr->food", notice observable changes * * The "p_ptr->food" variable can get as large as 20000, allowing the * addition of the most "filling" item, Elvish Waybread, which adds * 7500 food units, without overflowing the 32767 maximum limit. * * Perhaps we should disturb the player with various messages, * especially messages about hunger status changes. XXX XXX XXX * * Digestion of food is handled in "dungeon.c", in which, normally, * the player digests about 20 food units per 100 game turns, more * when "fast", more when "regenerating", less with "slow digestion", * but when the player is "gorged", he digests 100 food units per 10 * game turns, or a full 1000 food units per 100 game turns. * * Note that the player's speed is reduced by 10 units while gorged, * so if the player eats a single food ration (5000 food units) when * full (15000 food units), he will be gorged for (5000/100)*10 = 500 * game turns, or 500/(100/5) = 25 player turns (if nothing else is * affecting the player speed). */ bool set_food(int v) { int old_aux, new_aux; bool notice = FALSE; /* Hack -- Force good values */ v = (v > 20000) ? 20000 : (v < 0) ? 0 : v; /* Fainting / Starving */ if (p_ptr->food < PY_FOOD_FAINT) { old_aux = 0; } /* Weak */ else if (p_ptr->food < PY_FOOD_WEAK) { old_aux = 1; } /* Hungry */ else if (p_ptr->food < PY_FOOD_ALERT) { old_aux = 2; } /* Normal */ else if (p_ptr->food < PY_FOOD_FULL) { old_aux = 3; } /* Full */ else if (p_ptr->food < PY_FOOD_MAX) { old_aux = 4; } /* Gorged */ else { old_aux = 5; } /* Fainting / Starving */ if (v < PY_FOOD_FAINT) { new_aux = 0; } /* Weak */ else if (v < PY_FOOD_WEAK) { new_aux = 1; } /* Hungry */ else if (v < PY_FOOD_ALERT) { new_aux = 2; } /* Normal */ else if (v < PY_FOOD_FULL) { new_aux = 3; } /* Full */ else if (v < PY_FOOD_MAX) { new_aux = 4; } /* Gorged */ else { new_aux = 5; } /* Food increase */ if (new_aux > old_aux) { /* Describe the state */ switch (new_aux) { /* Weak */ case 1: msg_print("You are still weak."); break; /* Hungry */ case 2: msg_print("You are still hungry."); break; /* Normal */ case 3: msg_print("You are no longer hungry."); break; /* Full */ case 4: msg_print("You are full!"); break; /* Bloated */ case 5: msg_print("You have gorged yourself!"); break; } /* Change */ notice = TRUE; } /* Food decrease */ else if (new_aux < old_aux) { /* Describe the state */ switch (new_aux) { /* Fainting / Starving */ case 0: msg_print("You are getting faint from hunger!"); break; /* Weak */ case 1: msg_print("You are getting weak from hunger!"); break; /* Hungry */ case 2: msg_print("You are getting hungry."); break; /* Normal */ case 3: msg_print("You are no longer full."); break; /* Full */ case 4: msg_print("You are no longer gorged."); break; } /* Change */ notice = TRUE; } /* Use the value */ p_ptr->food = v; /* Nothing to notice */ if (!notice) return (FALSE); /* Disturb */ if (disturb_state) disturb(0, 0); /* Recalculate bonuses */ p_ptr->update |= (PU_BONUS); /* Redraw hunger */ p_ptr->redraw |= (PR_HUNGER); /* Handle stuff */ handle_stuff(); /* Result */ return (TRUE); } /* * Advance experience levels and print experience */ void check_experience(void) { int i; /* Note current level */ i = p_ptr->lev; /* Hack -- lower limit */ if (p_ptr->exp < 0) p_ptr->exp = 0; /* Hack -- lower limit */ if (p_ptr->max_exp < 0) p_ptr->max_exp = 0; /* Hack -- upper limit */ if (p_ptr->exp > PY_MAX_EXP) p_ptr->exp = PY_MAX_EXP; /* Hack -- upper limit */ if (p_ptr->max_exp > PY_MAX_EXP) p_ptr->max_exp = PY_MAX_EXP; /* Hack -- maintain "max" experience */ if (p_ptr->exp > p_ptr->max_exp) p_ptr->max_exp = p_ptr->exp; /* Redraw experience */ p_ptr->redraw |= (PR_EXP); /* Handle stuff */ handle_stuff(); /* Lose levels while possible */ while ((p_ptr->lev > 1) && (p_ptr->exp < (player_exp[p_ptr->lev-2] * p_ptr->expfact / 100L))) { /* Lose a level */ p_ptr->lev--; /* Update some stuff */ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS); /* Redraw some stuff */ p_ptr->redraw |= (PR_LEV | PR_TITLE); /* Window stuff */ p_ptr->window |= (PW_PLAYER); /* Handle stuff */ handle_stuff(); } /* Gain levels while possible */ while ((p_ptr->lev < PY_MAX_LEVEL) && (p_ptr->exp >= (player_exp[p_ptr->lev-1] * p_ptr->expfact / 100L))) { /* Gain a level */ p_ptr->lev++; /* Save the highest level */ if (p_ptr->lev > p_ptr->max_plv) p_ptr->max_plv = p_ptr->lev; /* Sound */ sound(SOUND_LEVEL); /* Message */ msg_format("Welcome to level %d.", p_ptr->lev); /* Update some stuff */ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS); /* Redraw some stuff */ p_ptr->redraw |= (PR_LEV | PR_TITLE); /* Window stuff */ p_ptr->window |= (PW_PLAYER); /* Handle stuff */ handle_stuff(); } } /* * Gain experience */ void gain_exp(s32b amount) { /* Gain some experience */ p_ptr->exp += amount; /* Slowly recover from experience drainage */ if (p_ptr->exp < p_ptr->max_exp) { /* Gain max experience (10%) */ p_ptr->max_exp += amount / 10; } /* Check Experience */ check_experience(); } /* * Lose experience */ void lose_exp(s32b amount) { /* Never drop below zero experience */ if (amount > p_ptr->exp) amount = p_ptr->exp; /* Lose some experience */ p_ptr->exp -= amount; /* Check Experience */ check_experience(); } /* * Hack -- Return the "automatic coin type" of a monster race * Used to allocate proper treasure when "Creeping coins" die * * XXX XXX XXX Note the use of actual "monster names" */ static int get_coin_type(monster_race *r_ptr) { cptr name = (r_name + r_ptr->name); /* Analyze "coin" monsters */ if (r_ptr->d_char == '$') { /* Look for textual clues */ if (strstr(name, " copper ")) return (2); if (strstr(name, " silver ")) return (5); if (strstr(name, " gold ")) return (10); if (strstr(name, " mithril ")) return (16); if (strstr(name, " adamantite ")) return (17); /* Look for textual clues */ if (strstr(name, "Copper ")) return (2); if (strstr(name, "Silver ")) return (5); if (strstr(name, "Gold ")) return (10); if (strstr(name, "Mithril ")) return (16); if (strstr(name, "Adamantite ")) return (17); } /* Assume nothing */ return (0); } /* * Handle the "death" of a monster. * * Disperse treasures centered at the monster location based on the * various flags contained in the monster flags fields. * * Check for "Quest" completion when a quest monster is killed. * * Note that only the player can induce "monster_death()" on Uniques. * Thus (for now) all Quest monsters should be Uniques. * * Note that monsters can now carry objects, and when a monster dies, * it drops all of its objects, which may disappear in crowded rooms. */ void monster_death(int m_idx) { int i, j, y, x, ny, nx; int dump_item = 0; int dump_gold = 0; int number = 0; int total = 0; s16b this_o_idx, next_o_idx = 0; monster_type *m_ptr = &m_list[m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; bool visible = (m_ptr->ml || (r_ptr->flags1 & (RF1_UNIQUE))); bool good = (r_ptr->flags1 & (RF1_DROP_GOOD)) ? TRUE : FALSE; bool great = (r_ptr->flags1 & (RF1_DROP_GREAT)) ? TRUE : FALSE; bool do_gold = (!(r_ptr->flags1 & (RF1_ONLY_ITEM))); bool do_item = (!(r_ptr->flags1 & (RF1_ONLY_GOLD))); int force_coin = get_coin_type(r_ptr); object_type forge; object_type *q_ptr; /* Get the location */ y = m_ptr->fy; x = m_ptr->fx; /* Drop objects being carried */ for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx) { object_type *o_ptr; /* Acquire object */ o_ptr = &o_list[this_o_idx]; /* Acquire next object */ next_o_idx = o_ptr->next_o_idx; /* Paranoia */ o_ptr->held_m_idx = 0; /* Get local object */ q_ptr = &forge; /* Copy the object */ object_copy(q_ptr, o_ptr); /* Delete the object */ delete_object_idx(this_o_idx); /* Drop it */ drop_near(q_ptr, -1, y, x); } /* Forget objects */ m_ptr->hold_o_idx = 0; /* Mega-Hack -- drop "winner" treasures */ if (r_ptr->flags1 & (RF1_DROP_CHOSEN)) { /* Get local object */ q_ptr = &forge; /* Mega-Hack -- Prepare to make "Grond" */ object_prep(q_ptr, lookup_kind(TV_HAFTED, SV_GROND)); /* Mega-Hack -- Mark this item as "Grond" */ q_ptr->name1 = ART_GROND; /* Mega-Hack -- Actually create "Grond" */ apply_magic(q_ptr, -1, TRUE, TRUE, TRUE); /* Drop it in the dungeon */ drop_near(q_ptr, -1, y, x); /* Get local object */ q_ptr = &forge; /* Mega-Hack -- Prepare to make "Morgoth" */ object_prep(q_ptr, lookup_kind(TV_CROWN, SV_MORGOTH)); /* Mega-Hack -- Mark this item as "Morgoth" */ q_ptr->name1 = ART_MORGOTH; /* Mega-Hack -- Actually create "Morgoth" */ apply_magic(q_ptr, -1, TRUE, TRUE, TRUE); /* Drop it in the dungeon */ drop_near(q_ptr, -1, y, x); } /* Determine how much we can drop */ if ((r_ptr->flags1 & (RF1_DROP_60)) && (rand_int(100) < 60)) number++; if ((r_ptr->flags1 & (RF1_DROP_90)) && (rand_int(100) < 90)) number++; if (r_ptr->flags1 & (RF1_DROP_1D2)) number += damroll(1, 2); if (r_ptr->flags1 & (RF1_DROP_2D2)) number += damroll(2, 2); if (r_ptr->flags1 & (RF1_DROP_3D2)) number += damroll(3, 2); if (r_ptr->flags1 & (RF1_DROP_4D2)) number += damroll(4, 2); /* Hack -- handle creeping coins */ coin_type = force_coin; /* Average dungeon and monster levels */ object_level = (dun_level + r_ptr->level) / 2; /* Drop some objects */ for (j = 0; j < number; j++) { /* Get local object */ q_ptr = &forge; /* Wipe the object */ object_wipe(q_ptr); /* Make Gold */ if (do_gold && (!do_item || (rand_int(100) < 50))) { /* Make some gold */ if (!make_gold(q_ptr)) continue; /* XXX XXX XXX */ dump_gold++; } /* Make Object */ else { /* Make an object */ if (!make_object(q_ptr, good, great)) continue; /* XXX XXX XXX */ dump_item++; } /* Drop it in the dungeon */ drop_near(q_ptr, -1, y, x); } /* Reset the object level */ object_level = dun_level; /* Reset "coin" type */ coin_type = 0; /* Take note of any dropped treasure */ if (visible && (dump_item || dump_gold)) { /* Take notes on treasure */ lore_treasure(m_idx, dump_item, dump_gold); } /* Only process "Quest Monsters" */ if (!(r_ptr->flags1 & (RF1_QUESTOR))) return; /* Hack -- Mark quests as complete */ for (i = 0; i < MAX_Q_IDX; i++) { /* Hack -- note completed quests */ if (q_list[i].level == r_ptr->level) q_list[i].level = 0; /* Count incomplete quests */ if (q_list[i].level) total++; } /* Need some stairs */ if (total) { /* Stagger around */ while (!cave_valid_bold(y, x)) { int d = 1; /* Pick a location */ scatter(&ny, &nx, y, x, d, 0); /* Stagger */ y = ny; x = nx; } /* XXX XXX XXX */ delete_object(y, x); /* Explain the stairway */ msg_print("A magical stairway appears..."); /* Create stairs down */ cave_set_feat(y, x, FEAT_MORE); /* Remember to update everything */ p_ptr->update |= (PU_VIEW | PU_LITE | PU_FLOW | PU_MONSTERS); } /* Nothing left, game over... */ else { /* Total winner */ total_winner = TRUE; /* Redraw the "title" */ p_ptr->redraw |= (PR_TITLE); /* Congratulations */ msg_print("*** CONGRATULATIONS ***"); msg_print("You have won the game!"); msg_print("You may retire (commit suicide) when you are ready."); } } /* * Decreases monsters hit points, handling monster death. * * We return TRUE if the monster has been killed (and deleted). * * We announce monster death (using an optional "death message" * if given, and a otherwise a generic killed/destroyed message). * * Only "physical attacks" can induce the "You have slain" message. * Missile and Spell attacks will induce the "dies" message, or * various "specialized" messages. Note that "You have destroyed" * and "is destroyed" are synonyms for "You have slain" and "dies". * * Hack -- unseen monsters yield "You have killed it." message. * * Added fear (DGK) and check whether to print fear messages -CWS * * Genericized name, sex, and capitilization -BEN- * * As always, the "ghost" processing is a total hack. * * Hack -- we "delay" fear messages by passing around a "fear" flag. * * XXX XXX XXX Consider decreasing monster experience over time, say, * by using "(m_exp * m_lev * (m_lev)) / (p_lev * (m_lev + n_killed))" * instead of simply "(m_exp * m_lev) / (p_lev)", to make the first * monster worth more than subsequent monsters. This would also need * to induce changes in the monster recall code. */ bool mon_take_hit(int m_idx, int dam, bool *fear, cptr note) { monster_type *m_ptr = &m_list[m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; s32b div, new_exp, new_exp_frac; /* Redraw (later) if needed */ if (health_who == m_idx) p_ptr->redraw |= (PR_HEALTH); /* Wake it up */ m_ptr->csleep = 0; /* Hurt it */ m_ptr->hp -= dam; /* It is dead now */ if (m_ptr->hp < 0) { char m_name[80]; /* Extract monster name */ monster_desc(m_name, m_ptr, 0); /* Make a sound */ sound(SOUND_KILL); /* Death by Missile/Spell attack */ if (note) { msg_format("%^s%s", m_name, note); } /* Death by physical attack -- invisible monster */ else if (!m_ptr->ml) { msg_format("You have killed %s.", m_name); } /* Death by Physical attack -- non-living monster */ else if ((r_ptr->flags3 & (RF3_DEMON)) || (r_ptr->flags3 & (RF3_UNDEAD)) || (r_ptr->flags2 & (RF2_STUPID)) || (strchr("Evg", r_ptr->d_char))) { msg_format("You have destroyed %s.", m_name); } /* Death by Physical attack -- living monster */ else { msg_format("You have slain %s.", m_name); } /* Maximum player level */ div = p_ptr->max_plv; /* Give some experience for the kill */ new_exp = ((long)r_ptr->mexp * r_ptr->level) / div; /* Handle fractional experience */ new_exp_frac = ((((long)r_ptr->mexp * r_ptr->level) % div) * 0x10000L / div) + p_ptr->exp_frac; /* Keep track of experience */ if (new_exp_frac >= 0x10000L) { new_exp++; p_ptr->exp_frac = new_exp_frac - 0x10000L; } else { p_ptr->exp_frac = new_exp_frac; } /* Gain experience */ gain_exp(new_exp); /* Generate treasure */ monster_death(m_idx); /* When the player kills a Unique, it stays dead */ if (r_ptr->flags1 & (RF1_UNIQUE)) r_ptr->max_num = 0; /* Recall even invisible uniques or winners */ if (m_ptr->ml || (r_ptr->flags1 & (RF1_UNIQUE))) { /* Count kills this life */ if (r_ptr->r_pkills < MAX_SHORT) r_ptr->r_pkills++; /* Count kills in all lives */ if (r_ptr->r_tkills < MAX_SHORT) r_ptr->r_tkills++; /* Hack -- Auto-recall */ monster_race_track(m_ptr->r_idx); } /* Delete the monster */ delete_monster_idx(m_idx); /* Not afraid */ (*fear) = FALSE; /* Monster is dead */ return (TRUE); } #ifdef ALLOW_FEAR /* Mega-Hack -- Pain cancels fear */ if (m_ptr->monfear && (dam > 0)) { int tmp = randint(dam); /* Cure a little fear */ if (tmp < m_ptr->monfear) { /* Reduce fear */ m_ptr->monfear -= tmp; } /* Cure all the fear */ else { /* Cure fear */ m_ptr->monfear = 0; /* No more fear */ (*fear) = FALSE; } } /* Sometimes a monster gets scared by damage */ if (!m_ptr->monfear && !(r_ptr->flags3 & (RF3_NO_FEAR))) { int percentage; /* Percentage of fully healthy */ percentage = (100L * m_ptr->hp) / m_ptr->maxhp; /* * Run (sometimes) if at 10% or less of max hit points, * or (usually) when hit for half its current hit points */ if (((percentage <= 10) && (rand_int(10) < percentage)) || ((dam >= m_ptr->hp) && (rand_int(100) < 80))) { /* Hack -- note fear */ (*fear) = TRUE; /* XXX XXX XXX Hack -- Add some timed fear */ m_ptr->monfear = (randint(10) + (((dam >= m_ptr->hp) && (percentage > 7)) ? 20 : ((11 - percentage) * 5))); } } #endif /* Not dead yet */ return (FALSE); } /* * Calculates current boundaries * Called below and from "do_cmd_locate()". */ void panel_bounds(void) { panel_row_min = panel_row * (SCREEN_HGT / 2); panel_row_max = panel_row_min + SCREEN_HGT - 1; panel_row_prt = panel_row_min - 1; panel_col_min = panel_col * (SCREEN_WID / 2); panel_col_max = panel_col_min + SCREEN_WID - 1; panel_col_prt = panel_col_min - 13; } /* * Given an row (y) and col (x), this routine detects when a move * off the screen has occurred and figures new borders. -RAK- * * "Update" forces a "full update" to take place. * * The map is reprinted if necessary, and "TRUE" is returned. */ void verify_panel(void) { int y = py; int x = px; int prow = panel_row; int pcol = panel_col; /* Scroll screen when 2 grids from top/bottom edge */ if ((y < panel_row_min + 2) || (y > panel_row_max - 2)) { prow = ((y - SCREEN_HGT / 4) / (SCREEN_HGT / 2)); if (prow > max_panel_rows) prow = max_panel_rows; else if (prow < 0) prow = 0; } /* Scroll screen when 4 grids from left/right edge */ if ((x < panel_col_min + 4) || (x > panel_col_max - 4)) { pcol = ((x - SCREEN_WID / 4) / (SCREEN_WID / 2)); if (pcol > max_panel_cols) pcol = max_panel_cols; else if (pcol < 0) pcol = 0; } /* Check for "no change" */ if ((prow == panel_row) && (pcol == panel_col)) return; /* Hack -- optional disturb on "panel change" */ if (disturb_panel) disturb(0, 0); /* Save the new panel info */ panel_row = prow; panel_col = pcol; /* Recalculate the boundaries */ panel_bounds(); /* Update stuff */ p_ptr->update |= (PU_MONSTERS); /* Redraw map */ p_ptr->redraw |= (PR_MAP); /* Window stuff */ p_ptr->window |= (PW_OVERHEAD); } /* * Monster health description */ cptr look_mon_desc(int m_idx) { monster_type *m_ptr = &m_list[m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; bool living = TRUE; int perc; /* Determine if the monster is "living" (vs "undead") */ if (r_ptr->flags3 & (RF3_UNDEAD)) living = FALSE; if (r_ptr->flags3 & (RF3_DEMON)) living = FALSE; if (strchr("Egv", r_ptr->d_char)) living = FALSE; /* Healthy monsters */ if (m_ptr->hp >= m_ptr->maxhp) { /* No damage */ return (living ? "unhurt" : "undamaged"); } /* Calculate a health "percentage" */ perc = 100L * m_ptr->hp / m_ptr->maxhp; if (perc >= 60) { return (living ? "somewhat wounded" : "somewhat damaged"); } if (perc >= 25) { return (living ? "wounded" : "damaged"); } if (perc >= 10) { return (living ? "badly wounded" : "badly damaged"); } return (living ? "almost dead" : "almost destroyed"); } /* * Angband sorting algorithm -- quick sort in place * * Note that the details of the data we are sorting is hidden, * and we rely on the "ang_sort_comp()" and "ang_sort_swap()" * function hooks to interact with the data, which is given as * two pointers, and which may have any user-defined form. */ void ang_sort_aux(vptr u, vptr v, int p, int q) { int z, a, b; /* Done sort */ if (p >= q) return; /* Pivot */ z = p; /* Begin */ a = p; b = q; /* Partition */ while (TRUE) { /* Slide i2 */ while (!(*ang_sort_comp)(u, v, b, z)) b--; /* Slide i1 */ while (!(*ang_sort_comp)(u, v, z, a)) a++; /* Done partition */ if (a >= b) break; /* Swap */ (*ang_sort_swap)(u, v, a, b); /* Advance */ a++, b--; } /* Recurse left side */ ang_sort_aux(u, v, p, b); /* Recurse right side */ ang_sort_aux(u, v, b+1, q); } /* * Angband sorting algorithm -- quick sort in place * * Note that the details of the data we are sorting is hidden, * and we rely on the "ang_sort_comp()" and "ang_sort_swap()" * function hooks to interact with the data, which is given as * two pointers, and which may have any user-defined form. */ void ang_sort(vptr u, vptr v, int n) { /* Sort the array */ ang_sort_aux(u, v, 0, n-1); } /*** Targetting Code ***/ /* * Determine is a monster makes a reasonable target * * The concept of "targetting" was stolen from "Morgul" (?) * * The player can target any location, or any "target-able" monster. * * Currently, a monster is "target_able" if it is visible, and if * the player can hit it with a projection, and the player is not * hallucinating. This allows use of "use closest target" macros. * * Future versions may restrict the ability to target "trappers" * and "mimics", but the semantics is a little bit weird. */ bool target_able(int m_idx) { monster_type *m_ptr = &m_list[m_idx]; /* Monster must be alive */ if (!m_ptr->r_idx) return (FALSE); /* Monster must be visible */ if (!m_ptr->ml) return (FALSE); /* Monster must be projectable */ if (!projectable(py, px, m_ptr->fy, m_ptr->fx)) return (FALSE); /* Hack -- no targeting hallucinations */ if (p_ptr->image) return (FALSE); /* XXX XXX XXX Hack -- Never target trappers */ /* if (CLEAR_ATTR && (CLEAR_CHAR)) return (FALSE); */ /* Assume okay */ return (TRUE); } /* * Update (if necessary) and verify (if possible) the target. * * We return TRUE if the target is "okay" and FALSE otherwise. */ bool target_okay(void) { /* Accept stationary targets */ if (target_who < 0) return (TRUE); /* Check moving targets */ if (target_who > 0) { /* Accept reasonable targets */ if (target_able(target_who)) { monster_type *m_ptr = &m_list[target_who]; /* Acquire monster location */ target_row = m_ptr->fy; target_col = m_ptr->fx; /* Good target */ return (TRUE); } } /* Assume no target */ return (FALSE); } /* * Sorting hook -- comp function -- by "distance to player" * * We use "u" and "v" to point to arrays of "x" and "y" positions, * and sort the arrays by double-distance to the player. */ static bool ang_sort_comp_distance(vptr u, vptr v, int a, int b) { byte *x = (byte*)(u); byte *y = (byte*)(v); int da, db, kx, ky; /* Absolute distance components */ kx = x[a]; kx -= px; kx = ABS(kx); ky = y[a]; ky -= py; ky = ABS(ky); /* Approximate Double Distance to the first point */ da = ((kx > ky) ? (kx + kx + ky) : (ky + ky + kx)); /* Absolute distance components */ kx = x[b]; kx -= px; kx = ABS(kx); ky = y[b]; ky -= py; ky = ABS(ky); /* Approximate Double Distance to the first point */ db = ((kx > ky) ? (kx + kx + ky) : (ky + ky + kx)); /* Compare the distances */ return (da <= db); } /* * Sorting hook -- swap function -- by "distance to player" * * We use "u" and "v" to point to arrays of "x" and "y" positions, * and sort the arrays by distance to the player. */ static void ang_sort_swap_distance(vptr u, vptr v, int a, int b) { byte *x = (byte*)(u); byte *y = (byte*)(v); byte temp; /* Swap "x" */ temp = x[a]; x[a] = x[b]; x[b] = temp; /* Swap "y" */ temp = y[a]; y[a] = y[b]; y[b] = temp; } /* * Hack -- help "select" a location (see below) */ static s16b target_pick(int y1, int x1, int dy, int dx) { int i, v; int x2, y2, x3, y3, x4, y4; int b_i = -1, b_v = 9999; /* Scan the locations */ for (i = 0; i < temp_n; i++) { /* Point 2 */ x2 = temp_x[i]; y2 = temp_y[i]; /* Directed distance */ x3 = (x2 - x1); y3 = (y2 - y1); /* Verify quadrant */ if (dx && (x3 * dx <= 0)) continue; if (dy && (y3 * dy <= 0)) continue; /* Absolute distance */ x4 = ABS(x3); y4 = ABS(y3); /* Verify quadrant */ if (dy && !dx && (x4 > y4)) continue; if (dx && !dy && (y4 > x4)) continue; /* Approximate Double Distance */ v = ((x4 > y4) ? (x4 + x4 + y4) : (y4 + y4 + x4)); /* XXX XXX XXX Penalize location */ /* Track best */ if ((b_i >= 0) && (v >= b_v)) continue; /* Track best */ b_i = i; b_v = v; } /* Result */ return (b_i); } /* * Hack -- determine if a given location is "interesting" */ static bool target_set_accept(int y, int x) { cave_type *c_ptr; s16b this_o_idx, next_o_idx = 0; /* Player grid is always interesting */ if ((y == py) && (x == px)) return (TRUE); /* Handle hallucination */ if (p_ptr->image) return (FALSE); /* Examine the grid */ c_ptr = &cave[y][x]; /* Visible monsters */ if (c_ptr->m_idx) { monster_type *m_ptr = &m_list[c_ptr->m_idx]; /* Visible monsters */ if (m_ptr->ml) return (TRUE); } /* Scan all objects in the grid */ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx) { object_type *o_ptr; /* Acquire object */ o_ptr = &o_list[this_o_idx]; /* Acquire next object */ next_o_idx = o_ptr->next_o_idx; /* Memorized object */ if (o_ptr->marked) return (TRUE); } /* Interesting memorized features */ if (c_ptr->info & (CAVE_MARK)) { /* Notice glyphs */ if (c_ptr->feat == FEAT_GLYPH) return (TRUE); /* Notice doors */ if (c_ptr->feat == FEAT_OPEN) return (TRUE); if (c_ptr->feat == FEAT_BROKEN) return (TRUE); /* Notice stairs */ if (c_ptr->feat == FEAT_LESS) return (TRUE); if (c_ptr->feat == FEAT_MORE) return (TRUE); /* Notice shops */ if ((c_ptr->feat >= FEAT_SHOP_HEAD) && (c_ptr->feat <= FEAT_SHOP_TAIL)) return (TRUE); /* Notice traps */ if ((c_ptr->feat >= FEAT_TRAP_HEAD) && (c_ptr->feat <= FEAT_TRAP_TAIL)) return (TRUE); /* Notice doors */ if ((c_ptr->feat >= FEAT_DOOR_HEAD) && (c_ptr->feat <= FEAT_DOOR_TAIL)) return (TRUE); /* Notice rubble */ if (c_ptr->feat == FEAT_RUBBLE) return (TRUE); /* Notice veins with treasure */ if (c_ptr->feat == FEAT_MAGMA_K) return (TRUE); if (c_ptr->feat == FEAT_QUARTZ_K) return (TRUE); } /* Nope */ return (FALSE); } /* * Prepare the "temp" array for "target_set" * * Return the number of target_able monsters in the set. */ static void target_set_prepare(int mode) { int y, x; /* Reset "temp" array */ temp_n = 0; /* Scan the current panel */ for (y = panel_row_min; y <= panel_row_max; y++) { for (x = panel_col_min; x <= panel_col_max; x++) { cave_type *c_ptr = &cave[y][x]; /* Require line of sight, unless "look" is "expanded" */ if (!expand_look && !player_has_los_bold(y, x)) continue; /* Require "interesting" contents */ if (!target_set_accept(y, x)) continue; /* Require target_able monsters for "TARGET_KILL" */ if ((mode & (TARGET_KILL)) && !target_able(c_ptr->m_idx)) continue; /* Save the location */ temp_x[temp_n] = x; temp_y[temp_n] = y; temp_n++; } } /* Set the sort hooks */ ang_sort_comp = ang_sort_comp_distance; ang_sort_swap = ang_sort_swap_distance; /* Sort the positions */ ang_sort(temp_x, temp_y, temp_n); } /* * Examine a grid, return a keypress. * * The "mode" argument contains the "TARGET_LOOK" bit flag, which * indicates that the "space" key should scan through the contents * of the grid, instead of simply returning immediately. This lets * the "look" command get complete information, without making the * "target" command annoying. * * The "info" argument contains the "commands" which should be shown * inside the "[xxx]" text. This string must never be empty, or grids * containing monsters will be displayed with an extra comma. * * Note that if a monster is in the grid, we update both the monster * recall info and the health bar info to track that monster. * * Eventually, we may allow multiple objects per grid, or objects * and terrain features in the same grid. XXX XXX XXX * * This function must handle blindness/hallucination. */ static int target_set_aux(int y, int x, int mode, cptr info) { cave_type *c_ptr = &cave[y][x]; s16b this_o_idx, next_o_idx = 0; cptr s1, s2, s3; bool boring; int feat; int query; char out_val[160]; /* Repeat forever */ while (1) { /* Paranoia */ query = ' '; /* Assume boring */ boring = TRUE; /* Default */ s1 = "You see "; s2 = ""; s3 = ""; /* Hack -- under the player */ if ((y == py) && (x == px)) { /* Description */ s1 = "You are "; /* Preposition */ s2 = "on "; } /* Hack -- hallucination */ if (p_ptr->image) { cptr name = "something strange"; /* Display a message */ sprintf(out_val, "%s%s%s%s [%s]", s1, s2, s3, name, info); prt(out_val, 0, 0); move_cursor_relative(y, x); query = inkey(); /* Stop on everything but "return" */ if ((query != '\r') && (query != '\n')) break; /* Repeat forever */ continue; } /* Actual monsters */ if (c_ptr->m_idx) { monster_type *m_ptr = &m_list[c_ptr->m_idx]; monster_race *r_ptr = &r_info[m_ptr->r_idx]; /* Visible */ if (m_ptr->ml) { bool recall = FALSE; char m_name[80]; /* Not boring */ boring = FALSE; /* Get the monster name ("a kobold") */ monster_desc(m_name, m_ptr, 0x08); /* Hack -- track this monster race */ monster_race_track(m_ptr->r_idx); /* Hack -- health bar for this monster */ health_track(c_ptr->m_idx); /* Hack -- handle stuff */ handle_stuff(); /* Interact */ while (1) { /* Recall */ if (recall) { /* Save */ Term_save(); /* Recall on screen */ screen_roff(m_ptr->r_idx); /* Hack -- Complete the prompt (again) */ Term_addstr(-1, TERM_WHITE, format(" [r,%s]", info)); /* Command */ query = inkey(); /* Restore */ Term_load(); } /* Normal */ else { /* Describe, and prompt for recall */ sprintf(out_val, "%s%s%s%s (%s) [r,%s]", s1, s2, s3, m_name, look_mon_desc(c_ptr->m_idx), info); prt(out_val, 0, 0); /* Place cursor */ move_cursor_relative(y, x); /* Command */ query = inkey(); } /* Normal commands */ if (query != 'r') break; /* Toggle recall */ recall = !recall; } /* Always stop at "normal" keys */ if ((query != '\r') && (query != '\n') && (query != ' ')) break; /* Sometimes stop at "space" key */ if ((query == ' ') && !(mode & (TARGET_LOOK))) break; /* Change the intro */ s1 = "It is "; /* Hack -- take account of gender */ if (r_ptr->flags1 & (RF1_FEMALE)) s1 = "She is "; else if (r_ptr->flags1 & (RF1_MALE)) s1 = "He is "; /* Use a preposition */ s2 = "carrying "; /* Scan all objects being carried */ for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx) { char o_name[80]; object_type *o_ptr; /* Acquire object */ o_ptr = &o_list[this_o_idx]; /* Acquire next object */ next_o_idx = o_ptr->next_o_idx; /* Obtain an object description */ object_desc(o_name, o_ptr, TRUE, 3); /* Describe the object */ sprintf(out_val, "%s%s%s%s [%s]", s1, s2, s3, o_name, info); prt(out_val, 0, 0); move_cursor_relative(y, x); query = inkey(); /* Always stop at "normal" keys */ if ((query != '\r') && (query != '\n') && (query != ' ')) break; /* Sometimes stop at "space" key */ if ((query == ' ') && !(mode & (TARGET_LOOK))) break; /* Change the intro */ s2 = "also carrying "; } /* Double break */ if (this_o_idx) break; /* Use a preposition */ s2 = "on "; } } /* Scan all objects in the grid */ for (this_o_idx = c_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx) { object_type *o_ptr; /* Acquire object */ o_ptr = &o_list[this_o_idx]; /* Acquire next object */ next_o_idx = o_ptr->next_o_idx; /* Describe it */ if (o_ptr->marked) { char o_name[80]; /* Not boring */ boring = FALSE; /* Obtain an object description */ object_desc(o_name, o_ptr, TRUE, 3); /* Describe the object */ sprintf(out_val, "%s%s%s%s [%s]", s1, s2, s3, o_name, info); prt(out_val, 0, 0); move_cursor_relative(y, x); query = inkey(); /* Always stop at "normal" keys */ if ((query != '\r') && (query != '\n') && (query != ' ')) break; /* Sometimes stop at "space" key */ if ((query == ' ') && !(mode & (TARGET_LOOK))) break; /* Change the intro */ s1 = "It is "; /* Plurals */ if (o_ptr->number != 1) s1 = "They are "; /* Preposition */ s2 = "on "; } } /* Double break */ if (this_o_idx) break; /* Feature (apply "mimic") */ feat = f_info[c_ptr->feat].mimic; /* Require knowledge about grid, or ability to see grid */ if (!(c_ptr->info & (CAVE_MARK)) && !player_can_see_bold(y,x)) { /* Forget feature */ feat = FEAT_NONE; } /* Terrain feature if needed */ if (boring || (feat > FEAT_INVIS)) { cptr name = f_name + f_info[feat].name; /* Hack -- handle unknown grids */ if (feat == FEAT_NONE) name = "unknown grid"; /* Pick a prefix */ if (*s2 && (feat >= FEAT_DOOR_HEAD)) s2 = "in "; /* Pick proper indefinite article */ s3 = (is_a_vowel(name[0])) ? "an " : "a "; /* Hack -- special introduction for store doors */ if ((feat >= FEAT_SHOP_HEAD) && (feat <= FEAT_SHOP_TAIL)) { s3 = "the entrance to the "; } /* Display a message */ sprintf(out_val, "%s%s%s%s [%s]", s1, s2, s3, name, info); prt(out_val, 0, 0); move_cursor_relative(y, x); query = inkey(); /* Always stop at "normal" keys */ if ((query != '\r') && (query != '\n') && (query != ' ')) break; } /* Stop on everything but "return" */ if ((query != '\r') && (query != '\n')) break; } /* Keep going */ return (query); } /* * Handle "target" and "look". * * Note that this code can be called from "get_aim_dir()". * * All locations must be on the current panel. Consider the use of * "panel_bounds()" to allow "off-panel" targets, perhaps by using * some form of "scrolling" the map around the cursor. XXX XXX XXX * That is, consider the possibility of "auto-scrolling" the screen * while the cursor moves around. This may require changes in the * "update_mon()" code to allow "visibility" even if off panel, and * may require dynamic recalculation of the "temp" grid set. * * Hack -- targetting/observing an "outer border grid" may induce * problems, so this is not currently allowed. * * The player can use the direction keys to move among "interesting" * grids in a heuristic manner, or the "space", "+", and "-" keys to * move through the "interesting" grids in a sequential manner, or * can enter "location" mode, and use the direction keys to move one * grid at a time in any direction. The "t" (set target) command will * only target a monster (as opposed to a location) if the monster is * target_able and the "interesting" mode is being used. * * The current grid is described using the "look" method above, and * a new command may be entered at any time, but note that if the * "TARGET_LOOK" bit flag is set (or if we are in "location" mode, * where "space" has no obvious meaning) then "space" will scan * through the description of the current grid until done, instead * of immediately jumping to the next "interesting" grid. This * allows the "target" command to retain its old semantics. * * The "*", "+", and "-" keys may always be used to jump immediately * to the next (or previous) interesting grid, in the proper mode. * * The "return" key may always be used to scan through a complete * grid description (forever). * * This command will cancel any old target, even if used from * inside the "look" command. */ bool target_set(int mode) { int i, d, m; int y = py; int x = px; bool done = FALSE; bool flag = TRUE; char query; char info[80]; cave_type *c_ptr; /* Cancel target */ target_who = 0; /* Cancel tracking */ /* health_track(0); */ /* Prepare the "temp" array */ target_set_prepare(mode); /* Start near the player */ m = 0; /* Interact */ while (!done) { /* Interesting grids */ if (flag && temp_n) { y = temp_y[m]; x = temp_x[m]; /* Access */ c_ptr = &cave[y][x]; /* Allow target */ if (target_able(c_ptr->m_idx)) { strcpy(info, "q,t,p,o,+,-,"); } /* Dis-allow target */ else { strcpy(info, "q,p,o,+,-,"); } /* Describe and Prompt */ query = target_set_aux(y, x, mode, info); /* Cancel tracking */ /* health_track(0); */ /* Assume no "direction" */ d = 0; /* Analyze */ switch (query) { case ESCAPE: case 'q': { done = TRUE; break; } case 't': case '.': case '5': case '0': { if (target_able(c_ptr->m_idx)) { health_track(c_ptr->m_idx); target_who = c_ptr->m_idx; target_row = y; target_col = x; done = TRUE; } else { bell(); } break; } case ' ': case '*': case '+': { if (++m == temp_n) { m = 0; if (!expand_list) done = TRUE; } break; } case '-': { if (m-- == 0) { m = temp_n - 1; if (!expand_list) done = TRUE; } break; } case 'p': { y = py; x = px; } case 'o': { flag = !flag; break; } case 'm': { break; } default: { d = keymap_dirs[query & 0x7F]; if (!d) bell(); break; } } /* Hack -- move around */ if (d) { /* Find a new monster */ i = target_pick(temp_y[m], temp_x[m], ddy[d], ddx[d]); /* Use that grid */ if (i >= 0) m = i; } } /* Arbitrary grids */ else { /* Access */ c_ptr = &cave[y][x]; /* Default prompt */ strcpy(info, "q,t,p,m,+,-,"); /* Describe and Prompt (enable "TARGET_LOOK") */ query = target_set_aux(y, x, mode | TARGET_LOOK, info); /* Cancel tracking */ /* health_track(0); */ /* Assume no direction */ d = 0; /* Analyze the keypress */ switch (query) { case ESCAPE: case 'q': { done = TRUE; break; } case 't': case '.': case '5': case '0': { target_who = -1; target_row = y; target_col = x; done = TRUE; break; } case ' ': case '*': case '+': case '-': { break; } case 'p': { y = py; x = px; } case 'o': { break; } case 'm': { flag = !flag; break; } default: { d = keymap_dirs[query & 0x7F]; if (!d) bell(); break; } } /* Handle "direction" */ if (d) { x += ddx[d]; y += ddy[d]; /* Hack -- Verify x */ if ((x>=cur_wid-1) || (x>panel_col_max)) x--; else if ((x<=0) || (x=cur_hgt-1) || (y>panel_row_max)) y--; else if ((y<=0) || (yconfused) { /* XXX XXX XXX */ /* Random direction */ dir = ddd[rand_int(8)]; } /* Notice confusion */ if (command_dir != dir) { /* Warn the user */ msg_print("You are confused."); } /* Save direction */ (*dp) = dir; /* A "valid" direction was entered */ return (TRUE); } /* * Request a "movement" direction (1,2,3,4,6,7,8,9) from the user, * and place it into "command_dir", unless we already have one. * * This function should be used for all "repeatable" commands, such as * run, walk, open, close, bash, disarm, spike, tunnel, etc, as well * as all commands which must reference a grid adjacent to the player, * and which may not reference the grid under the player. Note that, * for example, it is no longer possible to "disarm" or "open" chests * in the same grid as the player. * * Direction "5" is illegal and will (cleanly) abort the command. * * This function tracks and uses the "global direction", and uses * that as the "desired direction", to which "confusion" is applied. */ bool get_rep_dir(int *dp) { int dir; /* Initialize */ (*dp) = 0; /* Global direction */ dir = command_dir; /* Get a direction */ while (!dir) { char ch; /* Get a command (or Cancel) */ if (!get_com("Direction (Escape to cancel)? ", &ch)) break; /* Look up the direction */ dir = keymap_dirs[ch & 0x7F]; /* Oops */ if (!dir) bell(); } /* Prevent weirdness */ if (dir == 5) dir = 0; /* Aborted */ if (!dir) return (FALSE); /* Save desired direction */ command_dir = dir; /* Apply "confusion" */ if (p_ptr->confused) { /* Standard confusion */ if (rand_int(100) < 75) { /* Random direction */ dir = ddd[rand_int(8)]; } } /* Notice confusion */ if (command_dir != dir) { /* Warn the user */ msg_print("You are confused."); } /* Save direction */ (*dp) = dir; /* Success */ return (TRUE); }